| 1 | /* readval.c
|
|---|
| 2 | * Routines to read a prefix or number from the current input file
|
|---|
| 3 | * Copyright (C) 1991-2003,2005,2006,2010,2011,2012,2013 Olly Betts
|
|---|
| 4 | *
|
|---|
| 5 | * This program is free software; you can redistribute it and/or modify
|
|---|
| 6 | * it under the terms of the GNU General Public License as published by
|
|---|
| 7 | * the Free Software Foundation; either version 2 of the License, or
|
|---|
| 8 | * (at your option) any later version.
|
|---|
| 9 | *
|
|---|
| 10 | * This program is distributed in the hope that it will be useful,
|
|---|
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|---|
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|---|
| 13 | * GNU General Public License for more details.
|
|---|
| 14 | *
|
|---|
| 15 | * You should have received a copy of the GNU General Public License
|
|---|
| 16 | * along with this program; if not, write to the Free Software
|
|---|
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|---|
| 18 | */
|
|---|
| 19 |
|
|---|
| 20 | #ifdef HAVE_CONFIG_H
|
|---|
| 21 | # include <config.h>
|
|---|
| 22 | #endif
|
|---|
| 23 |
|
|---|
| 24 | #include <limits.h>
|
|---|
| 25 | #include <stddef.h> /* for offsetof */
|
|---|
| 26 |
|
|---|
| 27 | #include "cavern.h"
|
|---|
| 28 | #include "date.h"
|
|---|
| 29 | #include "debug.h"
|
|---|
| 30 | #include "filename.h"
|
|---|
| 31 | #include "message.h"
|
|---|
| 32 | #include "readval.h"
|
|---|
| 33 | #include "datain.h"
|
|---|
| 34 | #include "netbits.h"
|
|---|
| 35 | #include "osalloc.h"
|
|---|
| 36 | #include "str.h"
|
|---|
| 37 |
|
|---|
| 38 | #ifdef HAVE_SETJMP_H
|
|---|
| 39 | # define LONGJMP(JB) longjmp((JB), 1)
|
|---|
| 40 | #else
|
|---|
| 41 | # define LONGJMP(JB) exit(1)
|
|---|
| 42 | #endif
|
|---|
| 43 |
|
|---|
| 44 | int root_depr_count = 0;
|
|---|
| 45 |
|
|---|
| 46 | static prefix *
|
|---|
| 47 | new_anon_station(void)
|
|---|
| 48 | {
|
|---|
| 49 | prefix *name = osnew(prefix);
|
|---|
| 50 | name->pos = NULL;
|
|---|
| 51 | name->ident = NULL;
|
|---|
| 52 | name->shape = 0;
|
|---|
| 53 | name->stn = NULL;
|
|---|
| 54 | name->up = pcs->Prefix;
|
|---|
| 55 | name->down = NULL;
|
|---|
| 56 | name->filename = file.filename;
|
|---|
| 57 | name->line = file.line;
|
|---|
| 58 | if (TSTBIT(pcs->infer, INFER_EXPORTS)) {
|
|---|
| 59 | name->min_export = USHRT_MAX;
|
|---|
| 60 | } else {
|
|---|
| 61 | name->min_export = 0;
|
|---|
| 62 | }
|
|---|
| 63 | name->max_export = 0;
|
|---|
| 64 | name->sflags = BIT(SFLAGS_ANON);
|
|---|
| 65 | /* Keep linked list of anon stations for node stats. */
|
|---|
| 66 | name->right = anon_list;
|
|---|
| 67 | anon_list = name;
|
|---|
| 68 | return name;
|
|---|
| 69 | }
|
|---|
| 70 |
|
|---|
| 71 | /* if prefix is omitted: if PFX_OPT set return NULL, otherwise use longjmp */
|
|---|
| 72 | extern prefix *
|
|---|
| 73 | read_prefix(unsigned pfx_flags)
|
|---|
| 74 | {
|
|---|
| 75 | bool f_optional = !!(pfx_flags & PFX_OPT);
|
|---|
| 76 | bool fSurvey = !!(pfx_flags & PFX_SURVEY);
|
|---|
| 77 | bool fSuspectTypo = !!(pfx_flags & PFX_SUSPECT_TYPO);
|
|---|
| 78 | prefix *back_ptr, *ptr;
|
|---|
| 79 | char *name;
|
|---|
| 80 | size_t name_len = 32;
|
|---|
| 81 | size_t i;
|
|---|
| 82 | bool fNew;
|
|---|
| 83 | bool fImplicitPrefix = fTrue;
|
|---|
| 84 | int depth = -1;
|
|---|
| 85 |
|
|---|
| 86 | skipblanks();
|
|---|
| 87 | #ifndef NO_DEPRECATED
|
|---|
| 88 | if (isRoot(ch)) {
|
|---|
| 89 | if (!(pfx_flags & PFX_ALLOW_ROOT)) {
|
|---|
| 90 | compile_error(-/*ROOT is deprecated*/25);
|
|---|
| 91 | LONGJMP(file.jbSkipLine);
|
|---|
| 92 | }
|
|---|
| 93 | if (root_depr_count < 5) {
|
|---|
| 94 | compile_warning(-/*ROOT is deprecated*/25);
|
|---|
| 95 | if (++root_depr_count == 5)
|
|---|
| 96 | compile_warning(/*Further uses of this deprecated feature will not be reported*/95);
|
|---|
| 97 | }
|
|---|
| 98 | nextch();
|
|---|
| 99 | ptr = root;
|
|---|
| 100 | if (!isNames(ch)) {
|
|---|
| 101 | if (!isSep(ch)) return ptr;
|
|---|
| 102 | /* Allow optional SEPARATOR after ROOT */
|
|---|
| 103 | nextch();
|
|---|
| 104 | }
|
|---|
| 105 | fImplicitPrefix = fFalse;
|
|---|
| 106 | #else
|
|---|
| 107 | if (0) {
|
|---|
| 108 | #endif
|
|---|
| 109 | } else {
|
|---|
| 110 | if ((pfx_flags & PFX_ANON) &&
|
|---|
| 111 | (isSep(ch) || (pcs->dash_for_anon_wall_station && ch == '-'))) {
|
|---|
| 112 | int first_ch = ch;
|
|---|
| 113 | filepos here;
|
|---|
| 114 | get_pos(&here);
|
|---|
| 115 | nextch();
|
|---|
| 116 | if (isBlank(ch) || isEol(ch)) {
|
|---|
| 117 | if (!isSep(first_ch))
|
|---|
| 118 | goto anon_wall_station;
|
|---|
| 119 | /* A single separator alone ('.' by default) is an anonymous
|
|---|
| 120 | * station which is on a point inside the passage and implies
|
|---|
| 121 | * the leg to it is a splay.
|
|---|
| 122 | */
|
|---|
| 123 | if (TSTBIT(pcs->flags, FLAGS_ANON_ONE_END)) {
|
|---|
| 124 | compile_error(-/*Can't have a leg between two anonymous stations*/3);
|
|---|
| 125 | LONGJMP(file.jbSkipLine);
|
|---|
| 126 | }
|
|---|
| 127 | pcs->flags |= BIT(FLAGS_ANON_ONE_END) | BIT(FLAGS_IMPLICIT_SPLAY);
|
|---|
| 128 | return new_anon_station();
|
|---|
| 129 | }
|
|---|
| 130 | if (isSep(first_ch) && ch == first_ch) {
|
|---|
| 131 | nextch();
|
|---|
| 132 | if (isBlank(ch) || isEol(ch)) {
|
|---|
| 133 | /* A double separator ('..' by default) is an anonymous station
|
|---|
| 134 | * which is on the wall and implies the leg to it is a splay.
|
|---|
| 135 | */
|
|---|
| 136 | prefix * pfx;
|
|---|
| 137 | anon_wall_station:
|
|---|
| 138 | if (TSTBIT(pcs->flags, FLAGS_ANON_ONE_END)) {
|
|---|
| 139 | compile_error(-/*Can't have a leg between two anonymous stations*/3);
|
|---|
| 140 | LONGJMP(file.jbSkipLine);
|
|---|
| 141 | }
|
|---|
| 142 | pcs->flags |= BIT(FLAGS_ANON_ONE_END) | BIT(FLAGS_IMPLICIT_SPLAY);
|
|---|
| 143 | pfx = new_anon_station();
|
|---|
| 144 | pfx->sflags |= BIT(SFLAGS_WALL);
|
|---|
| 145 | return pfx;
|
|---|
| 146 | }
|
|---|
| 147 | if (ch == first_ch) {
|
|---|
| 148 | nextch();
|
|---|
| 149 | if (isBlank(ch) || isEol(ch)) {
|
|---|
| 150 | /* A triple separator ('...' by default) is an anonymous
|
|---|
| 151 | * station, but otherwise not handled specially (e.g. for
|
|---|
| 152 | * a single leg down an unexplored side passage to a station
|
|---|
| 153 | * which isn't refindable).
|
|---|
| 154 | */
|
|---|
| 155 | if (TSTBIT(pcs->flags, FLAGS_ANON_ONE_END)) {
|
|---|
| 156 | compile_error(-/*Can't have a leg between two anonymous stations*/3);
|
|---|
| 157 | LONGJMP(file.jbSkipLine);
|
|---|
| 158 | }
|
|---|
| 159 | pcs->flags |= BIT(FLAGS_ANON_ONE_END);
|
|---|
| 160 | return new_anon_station();
|
|---|
| 161 | }
|
|---|
| 162 | }
|
|---|
| 163 | }
|
|---|
| 164 | set_pos(&here);
|
|---|
| 165 | }
|
|---|
| 166 | ptr = pcs->Prefix;
|
|---|
| 167 | }
|
|---|
| 168 |
|
|---|
| 169 | i = 0;
|
|---|
| 170 | name = NULL;
|
|---|
| 171 | do {
|
|---|
| 172 | fNew = fFalse;
|
|---|
| 173 | if (name == NULL) {
|
|---|
| 174 | /* Need a new name buffer */
|
|---|
| 175 | name = osmalloc(name_len);
|
|---|
| 176 | }
|
|---|
| 177 | /* i==0 iff this is the first pass */
|
|---|
| 178 | if (i) {
|
|---|
| 179 | i = 0;
|
|---|
| 180 | nextch();
|
|---|
| 181 | }
|
|---|
| 182 | while (isNames(ch)) {
|
|---|
| 183 | if (i < pcs->Truncate) {
|
|---|
| 184 | /* truncate name */
|
|---|
| 185 | name[i++] = (pcs->Case == LOWER ? tolower(ch) :
|
|---|
| 186 | (pcs->Case == OFF ? ch : toupper(ch)));
|
|---|
| 187 | if (i >= name_len) {
|
|---|
| 188 | name_len = name_len + name_len;
|
|---|
| 189 | name = osrealloc(name, name_len);
|
|---|
| 190 | }
|
|---|
| 191 | }
|
|---|
| 192 | nextch();
|
|---|
| 193 | }
|
|---|
| 194 | if (isSep(ch)) fImplicitPrefix = fFalse;
|
|---|
| 195 | if (i == 0) {
|
|---|
| 196 | osfree(name);
|
|---|
| 197 | if (!f_optional) {
|
|---|
| 198 | if (isEol(ch)) {
|
|---|
| 199 | if (fSurvey) {
|
|---|
| 200 | compile_error(-/*Expecting survey name*/89);
|
|---|
| 201 | } else {
|
|---|
| 202 | compile_error(-/*Expecting station name*/28);
|
|---|
| 203 | }
|
|---|
| 204 | } else {
|
|---|
| 205 | compile_error(-/*Character “%c” not allowed in station name (use *SET NAMES to set allowed characters)*/7, ch);
|
|---|
| 206 | }
|
|---|
| 207 | LONGJMP(file.jbSkipLine);
|
|---|
| 208 | }
|
|---|
| 209 | return (prefix *)NULL;
|
|---|
| 210 | }
|
|---|
| 211 |
|
|---|
| 212 | name[i++] = '\0';
|
|---|
| 213 |
|
|---|
| 214 | back_ptr = ptr;
|
|---|
| 215 | ptr = ptr->down;
|
|---|
| 216 | if (ptr == NULL) {
|
|---|
| 217 | /* Special case first time around at each level */
|
|---|
| 218 | name = osrealloc(name, i);
|
|---|
| 219 | ptr = osnew(prefix);
|
|---|
| 220 | ptr->ident = name;
|
|---|
| 221 | name = NULL;
|
|---|
| 222 | ptr->right = ptr->down = NULL;
|
|---|
| 223 | ptr->pos = NULL;
|
|---|
| 224 | ptr->shape = 0;
|
|---|
| 225 | ptr->stn = NULL;
|
|---|
| 226 | ptr->up = back_ptr;
|
|---|
| 227 | ptr->filename = file.filename;
|
|---|
| 228 | ptr->line = file.line;
|
|---|
| 229 | ptr->min_export = ptr->max_export = 0;
|
|---|
| 230 | ptr->sflags = BIT(SFLAGS_SURVEY);
|
|---|
| 231 | if (fSuspectTypo && !fImplicitPrefix)
|
|---|
| 232 | ptr->sflags |= BIT(SFLAGS_SUSPECTTYPO);
|
|---|
| 233 | back_ptr->down = ptr;
|
|---|
| 234 | fNew = fTrue;
|
|---|
| 235 | } else {
|
|---|
| 236 | /* Use caching to speed up adding an increasing sequence to a
|
|---|
| 237 | * large survey */
|
|---|
| 238 | static prefix *cached_survey = NULL, *cached_station = NULL;
|
|---|
| 239 | prefix *ptrPrev = NULL;
|
|---|
| 240 | int cmp = 1; /* result of strcmp ( -ve for <, 0 for =, +ve for > ) */
|
|---|
| 241 | if (cached_survey == back_ptr) {
|
|---|
| 242 | cmp = strcmp(cached_station->ident, name);
|
|---|
| 243 | if (cmp <= 0) ptr = cached_station;
|
|---|
| 244 | }
|
|---|
| 245 | while (ptr && (cmp = strcmp(ptr->ident, name))<0) {
|
|---|
| 246 | ptrPrev = ptr;
|
|---|
| 247 | ptr = ptr->right;
|
|---|
| 248 | }
|
|---|
| 249 | if (cmp) {
|
|---|
| 250 | /* ie we got to one that was higher, or the end */
|
|---|
| 251 | prefix *newptr;
|
|---|
| 252 | name = osrealloc(name, i);
|
|---|
| 253 | newptr = osnew(prefix);
|
|---|
| 254 | newptr->ident = name;
|
|---|
| 255 | name = NULL;
|
|---|
| 256 | if (ptrPrev == NULL)
|
|---|
| 257 | back_ptr->down = newptr;
|
|---|
| 258 | else
|
|---|
| 259 | ptrPrev->right = newptr;
|
|---|
| 260 | newptr->right = ptr;
|
|---|
| 261 | newptr->down = NULL;
|
|---|
| 262 | newptr->pos = NULL;
|
|---|
| 263 | newptr->shape = 0;
|
|---|
| 264 | newptr->stn = NULL;
|
|---|
| 265 | newptr->up = back_ptr;
|
|---|
| 266 | newptr->filename = file.filename;
|
|---|
| 267 | newptr->line = file.line;
|
|---|
| 268 | newptr->min_export = newptr->max_export = 0;
|
|---|
| 269 | newptr->sflags = BIT(SFLAGS_SURVEY);
|
|---|
| 270 | if (fSuspectTypo && !fImplicitPrefix)
|
|---|
| 271 | newptr->sflags |= BIT(SFLAGS_SUSPECTTYPO);
|
|---|
| 272 | ptr = newptr;
|
|---|
| 273 | fNew = fTrue;
|
|---|
| 274 | }
|
|---|
| 275 | cached_survey = back_ptr;
|
|---|
| 276 | cached_station = ptr;
|
|---|
| 277 | }
|
|---|
| 278 | depth++;
|
|---|
| 279 | f_optional = fFalse; /* disallow after first level */
|
|---|
| 280 | } while (isSep(ch));
|
|---|
| 281 | if (name) osfree(name);
|
|---|
| 282 |
|
|---|
| 283 | /* don't warn about a station that is referred to twice */
|
|---|
| 284 | if (!fNew) ptr->sflags &= ~BIT(SFLAGS_SUSPECTTYPO);
|
|---|
| 285 |
|
|---|
| 286 | if (fNew) {
|
|---|
| 287 | /* fNew means SFLAGS_SURVEY is currently set */
|
|---|
| 288 | SVX_ASSERT(TSTBIT(ptr->sflags, SFLAGS_SURVEY));
|
|---|
| 289 | if (!fSurvey) {
|
|---|
| 290 | ptr->sflags &= ~BIT(SFLAGS_SURVEY);
|
|---|
| 291 | if (TSTBIT(pcs->infer, INFER_EXPORTS)) ptr->min_export = USHRT_MAX;
|
|---|
| 292 | }
|
|---|
| 293 | } else {
|
|---|
| 294 | /* check that the same name isn't being used for a survey and station */
|
|---|
| 295 | if (fSurvey ^ TSTBIT(ptr->sflags, SFLAGS_SURVEY)) {
|
|---|
| 296 | compile_error(/*“%s” can’t be both a station and a survey*/27,
|
|---|
| 297 | sprint_prefix(ptr));
|
|---|
| 298 | }
|
|---|
| 299 | if (!fSurvey && TSTBIT(pcs->infer, INFER_EXPORTS)) ptr->min_export = USHRT_MAX;
|
|---|
| 300 | }
|
|---|
| 301 |
|
|---|
| 302 | /* check the export level */
|
|---|
| 303 | #if 0
|
|---|
| 304 | printf("R min %d max %d depth %d pfx %s\n",
|
|---|
| 305 | ptr->min_export, ptr->max_export, depth, sprint_prefix(ptr));
|
|---|
| 306 | #endif
|
|---|
| 307 | if (ptr->min_export == 0 || ptr->min_export == USHRT_MAX) {
|
|---|
| 308 | if (depth > ptr->max_export) ptr->max_export = depth;
|
|---|
| 309 | } else if (ptr->max_export < depth) {
|
|---|
| 310 | const char *filename_store = file.filename;
|
|---|
| 311 | unsigned int line_store = file.line;
|
|---|
| 312 | prefix *survey = ptr;
|
|---|
| 313 | char *s;
|
|---|
| 314 | int level;
|
|---|
| 315 | for (level = ptr->max_export + 1; level; level--) {
|
|---|
| 316 | survey = survey->up;
|
|---|
| 317 | SVX_ASSERT(survey);
|
|---|
| 318 | }
|
|---|
| 319 | s = osstrdup(sprint_prefix(survey));
|
|---|
| 320 | if (survey->filename) {
|
|---|
| 321 | file.filename = survey->filename;
|
|---|
| 322 | file.line = survey->line;
|
|---|
| 323 | }
|
|---|
| 324 | compile_error(/*Station “%s” not exported from survey “%s”*/26,
|
|---|
| 325 | sprint_prefix(ptr), s);
|
|---|
| 326 | if (survey->filename) {
|
|---|
| 327 | file.filename = filename_store;
|
|---|
| 328 | file.line = line_store;
|
|---|
| 329 | }
|
|---|
| 330 | osfree(s);
|
|---|
| 331 | #if 0
|
|---|
| 332 | printf(" *** pfx %s warning not exported enough depth %d "
|
|---|
| 333 | "ptr->max_export %d\n", sprint_prefix(ptr),
|
|---|
| 334 | depth, ptr->max_export);
|
|---|
| 335 | #endif
|
|---|
| 336 | }
|
|---|
| 337 | if (!fImplicitPrefix && (pfx_flags & PFX_WARN_SEPARATOR)) {
|
|---|
| 338 | compile_warning(/*Separator in survey name*/392);
|
|---|
| 339 | }
|
|---|
| 340 | return ptr;
|
|---|
| 341 | }
|
|---|
| 342 |
|
|---|
| 343 | /* if numeric expr is omitted: if f_optional return HUGE_REAL, else longjmp */
|
|---|
| 344 | static real
|
|---|
| 345 | read_number(bool f_optional)
|
|---|
| 346 | {
|
|---|
| 347 | bool fPositive, fDigits = fFalse;
|
|---|
| 348 | real n = (real)0.0;
|
|---|
| 349 | filepos fp;
|
|---|
| 350 | int ch_old;
|
|---|
| 351 |
|
|---|
| 352 | get_pos(&fp);
|
|---|
| 353 | ch_old = ch;
|
|---|
| 354 | fPositive = !isMinus(ch);
|
|---|
| 355 | if (isSign(ch)) nextch();
|
|---|
| 356 |
|
|---|
| 357 | while (isdigit(ch)) {
|
|---|
| 358 | n = n * (real)10.0 + (char)(ch - '0');
|
|---|
| 359 | nextch();
|
|---|
| 360 | fDigits = fTrue;
|
|---|
| 361 | }
|
|---|
| 362 |
|
|---|
| 363 | if (isDecimal(ch)) {
|
|---|
| 364 | real mult = (real)1.0;
|
|---|
| 365 | nextch();
|
|---|
| 366 | while (isdigit(ch)) {
|
|---|
| 367 | mult *= (real).1;
|
|---|
| 368 | n += (char)(ch - '0') * mult;
|
|---|
| 369 | fDigits = fTrue;
|
|---|
| 370 | nextch();
|
|---|
| 371 | }
|
|---|
| 372 | }
|
|---|
| 373 |
|
|---|
| 374 | /* !'fRead' => !fDigits so fDigits => 'fRead' */
|
|---|
| 375 | if (fDigits) return (fPositive ? n : -n);
|
|---|
| 376 |
|
|---|
| 377 | /* didn't read a valid number. If it's optional, reset filepos & return */
|
|---|
| 378 | set_pos(&fp);
|
|---|
| 379 | if (f_optional) {
|
|---|
| 380 | return HUGE_REAL;
|
|---|
| 381 | }
|
|---|
| 382 |
|
|---|
| 383 | if (isOmit(ch_old)) {
|
|---|
| 384 | compile_error(-/*Field may not be omitted*/8);
|
|---|
| 385 | } else {
|
|---|
| 386 | compile_error_token(-/*Expecting numeric field, found “%s”*/9);
|
|---|
| 387 | }
|
|---|
| 388 | LONGJMP(file.jbSkipLine);
|
|---|
| 389 | return 0.0; /* for brain-fried compilers */
|
|---|
| 390 | }
|
|---|
| 391 |
|
|---|
| 392 | extern real
|
|---|
| 393 | read_numeric(bool f_optional, int *p_n_readings)
|
|---|
| 394 | {
|
|---|
| 395 | size_t n_readings = 0;
|
|---|
| 396 | real tot = (real)0.0;
|
|---|
| 397 |
|
|---|
| 398 | skipblanks();
|
|---|
| 399 | if (!isOpen(ch)) {
|
|---|
| 400 | real r = read_number(f_optional);
|
|---|
| 401 | if (p_n_readings) *p_n_readings = (r == HUGE_REAL ? 0 : 1);
|
|---|
| 402 | return r;
|
|---|
| 403 | }
|
|---|
| 404 | nextch();
|
|---|
| 405 |
|
|---|
| 406 | skipblanks();
|
|---|
| 407 | do {
|
|---|
| 408 | tot += read_number(fFalse);
|
|---|
| 409 | ++n_readings;
|
|---|
| 410 | skipblanks();
|
|---|
| 411 | } while (!isClose(ch));
|
|---|
| 412 | nextch();
|
|---|
| 413 |
|
|---|
| 414 | if (p_n_readings) *p_n_readings = n_readings;
|
|---|
| 415 | /* FIXME: special averaging for bearings ... */
|
|---|
| 416 | /* And for percentage gradient */
|
|---|
| 417 | return tot / n_readings;
|
|---|
| 418 | }
|
|---|
| 419 |
|
|---|
| 420 | /* read numeric expr or omit (return HUGE_REAL); else longjmp */
|
|---|
| 421 | extern real
|
|---|
| 422 | read_numeric_or_omit(int *p_n_readings)
|
|---|
| 423 | {
|
|---|
| 424 | real v = read_numeric(fTrue, p_n_readings);
|
|---|
| 425 | if (v == HUGE_REAL) {
|
|---|
| 426 | if (!isOmit(ch)) {
|
|---|
| 427 | compile_error_token(-/*Expecting numeric field, found “%s”*/9);
|
|---|
| 428 | LONGJMP(file.jbSkipLine);
|
|---|
| 429 | return 0.0; /* for brain-fried compilers */
|
|---|
| 430 | }
|
|---|
| 431 | nextch();
|
|---|
| 432 | }
|
|---|
| 433 | return v;
|
|---|
| 434 | }
|
|---|
| 435 |
|
|---|
| 436 | /* Don't skip blanks, variable error code */
|
|---|
| 437 | static unsigned int
|
|---|
| 438 | read_uint_internal(int errmsg, const filepos *fp)
|
|---|
| 439 | {
|
|---|
| 440 | unsigned int n = 0;
|
|---|
| 441 | if (!isdigit(ch)) {
|
|---|
| 442 | if (fp) set_pos(fp);
|
|---|
| 443 | compile_error_token(-errmsg);
|
|---|
| 444 | LONGJMP(file.jbSkipLine);
|
|---|
| 445 | }
|
|---|
| 446 | while (isdigit(ch)) {
|
|---|
| 447 | n = n * 10 + (char)(ch - '0');
|
|---|
| 448 | nextch();
|
|---|
| 449 | }
|
|---|
| 450 | return n;
|
|---|
| 451 | }
|
|---|
| 452 |
|
|---|
| 453 | extern unsigned int
|
|---|
| 454 | read_uint(void)
|
|---|
| 455 | {
|
|---|
| 456 | skipblanks();
|
|---|
| 457 | return read_uint_internal(/*Expecting numeric field, found “%s”*/9, NULL);
|
|---|
| 458 | }
|
|---|
| 459 |
|
|---|
| 460 | extern void
|
|---|
| 461 | read_string(char **pstr, int *plen)
|
|---|
| 462 | {
|
|---|
| 463 | s_zero(pstr);
|
|---|
| 464 |
|
|---|
| 465 | skipblanks();
|
|---|
| 466 | if (ch == '\"') {
|
|---|
| 467 | /* String quoted in "" */
|
|---|
| 468 | nextch();
|
|---|
| 469 | while (1) {
|
|---|
| 470 | if (isEol(ch)) {
|
|---|
| 471 | compile_error(-/*Missing \"*/69);
|
|---|
| 472 | LONGJMP(file.jbSkipLine);
|
|---|
| 473 | }
|
|---|
| 474 |
|
|---|
| 475 | if (ch == '\"') break;
|
|---|
| 476 |
|
|---|
| 477 | s_catchar(pstr, plen, ch);
|
|---|
| 478 | nextch();
|
|---|
| 479 | }
|
|---|
| 480 | } else {
|
|---|
| 481 | /* Unquoted string */
|
|---|
| 482 | while (1) {
|
|---|
| 483 | if (isEol(ch) || isComm(ch)) {
|
|---|
| 484 | if (!*pstr || !(*pstr)[0]) {
|
|---|
| 485 | compile_error(-/*Expecting string field*/121);
|
|---|
| 486 | LONGJMP(file.jbSkipLine);
|
|---|
| 487 | }
|
|---|
| 488 | return;
|
|---|
| 489 | }
|
|---|
| 490 |
|
|---|
| 491 | if (isBlank(ch)) break;
|
|---|
| 492 |
|
|---|
| 493 | s_catchar(pstr, plen, ch);
|
|---|
| 494 | nextch();
|
|---|
| 495 | }
|
|---|
| 496 | }
|
|---|
| 497 |
|
|---|
| 498 | nextch();
|
|---|
| 499 | }
|
|---|
| 500 |
|
|---|
| 501 | extern void
|
|---|
| 502 | read_date(int *py, int *pm, int *pd)
|
|---|
| 503 | {
|
|---|
| 504 | int y = 0, m = 0, d = 0;
|
|---|
| 505 | filepos fp;
|
|---|
| 506 |
|
|---|
| 507 | skipblanks();
|
|---|
| 508 |
|
|---|
| 509 | get_pos(&fp);
|
|---|
| 510 | y = read_uint_internal(/*Expecting date, found “%s”*/198, &fp);
|
|---|
| 511 | /* Two digit year is 19xx. */
|
|---|
| 512 | if (y < 100) y += 1900;
|
|---|
| 513 | if (y < 1900 || y > 2078) {
|
|---|
| 514 | compile_warning(/*Invalid year (< 1900 or > 2078)*/58);
|
|---|
| 515 | LONGJMP(file.jbSkipLine);
|
|---|
| 516 | return; /* for brain-fried compilers */
|
|---|
| 517 | }
|
|---|
| 518 | if (ch == '.') {
|
|---|
| 519 | nextch();
|
|---|
| 520 | m = read_uint_internal(/*Expecting date, found “%s”*/198, &fp);
|
|---|
| 521 | if (m < 1 || m > 12) {
|
|---|
| 522 | compile_warning(/*Invalid month*/86);
|
|---|
| 523 | LONGJMP(file.jbSkipLine);
|
|---|
| 524 | return; /* for brain-fried compilers */
|
|---|
| 525 | }
|
|---|
| 526 | if (ch == '.') {
|
|---|
| 527 | nextch();
|
|---|
| 528 | d = read_uint_internal(/*Expecting date, found “%s”*/198, &fp);
|
|---|
| 529 | if (d < 1 || d > last_day(y, m)) {
|
|---|
| 530 | compile_warning(/*Invalid day of the month*/87);
|
|---|
| 531 | LONGJMP(file.jbSkipLine);
|
|---|
| 532 | return; /* for brain-fried compilers */
|
|---|
| 533 | }
|
|---|
| 534 | }
|
|---|
| 535 | }
|
|---|
| 536 | if (py) *py = y;
|
|---|
| 537 | if (pm) *pm = m;
|
|---|
| 538 | if (pd) *pd = d;
|
|---|
| 539 | }
|
|---|