source: git/src/readval.c@ 26bdac6

main stereo-2025 warn-only-for-hanging-survey
Last change on this file since 26bdac6 was 725d3b1, checked in by Olly Betts <olly@…>, 2 years ago

Add support for processing Walls format survey data

  • Property mode set to 100644
File size: 27.4 KB
Line 
1/* readval.c
2 * Routines to read a prefix or number from the current input file
3 * Copyright (C) 1991-2024 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#include <config.h>
21
22#include <limits.h>
23#include <stddef.h> /* for offsetof */
24
25#include "cavern.h"
26#include "commands.h" /* For match_tok(), etc */
27#include "date.h"
28#include "debug.h"
29#include "filename.h"
30#include "message.h"
31#include "readval.h"
32#include "datain.h"
33#include "netbits.h"
34#include "osalloc.h"
35#include "str.h"
36
37#ifdef HAVE_SETJMP_H
38# define LONGJMP(JB) longjmp((JB), 1)
39#else
40# define LONGJMP(JB) exit(1)
41#endif
42
43int root_depr_count = 0;
44
45static prefix *
46new_anon_station(void)
47{
48 prefix *name = osnew(prefix);
49 name->pos = NULL;
50 name->ident = NULL;
51 name->shape = 0;
52 name->stn = NULL;
53 name->up = pcs->Prefix;
54 name->down = NULL;
55 name->filename = file.filename;
56 name->line = file.line;
57 name->min_export = name->max_export = 0;
58 name->sflags = BIT(SFLAGS_ANON);
59 /* Keep linked list of anon stations for node stats. */
60 name->right = anon_list;
61 anon_list = name;
62 return name;
63}
64
65/* if prefix is omitted: if PFX_OPT set return NULL, otherwise use longjmp */
66extern prefix *
67read_prefix(unsigned pfx_flags)
68{
69 bool f_optional = !!(pfx_flags & PFX_OPT);
70 bool fSurvey = !!(pfx_flags & PFX_SURVEY);
71 bool fSuspectTypo = !!(pfx_flags & PFX_SUSPECT_TYPO);
72 prefix *back_ptr, *ptr;
73 char *name;
74 size_t name_len = 32;
75 size_t i;
76 bool fNew;
77 bool fImplicitPrefix = true;
78 int depth = -1;
79 filepos here;
80 filepos fp_firstsep;
81
82 skipblanks();
83 get_pos(&here);
84#ifndef NO_DEPRECATED
85 if (isRoot(ch)) {
86 if (!(pfx_flags & PFX_ALLOW_ROOT)) {
87 compile_diagnostic(DIAG_ERR|DIAG_COL, /*ROOT is deprecated*/25);
88 LONGJMP(file.jbSkipLine);
89 }
90 if (root_depr_count < 5) {
91 compile_diagnostic(DIAG_WARN|DIAG_COL, /*ROOT is deprecated*/25);
92 if (++root_depr_count == 5)
93 compile_diagnostic(DIAG_INFO, /*Further uses of this deprecated feature will not be reported*/95);
94 }
95 nextch();
96 ptr = root;
97 if (!isNames(ch)) {
98 if (!isSep(ch)) return ptr;
99 /* Allow optional SEPARATOR after ROOT */
100 get_pos(&fp_firstsep);
101 nextch();
102 }
103 fImplicitPrefix = false;
104#else
105 if (0) {
106#endif
107 } else {
108 if ((pfx_flags & PFX_ANON) &&
109 (isSep(ch) || (pcs->dash_for_anon_wall_station && ch == '-'))) {
110 int first_ch = ch;
111 nextch();
112 if (isBlank(ch) || isEol(ch)) {
113 if (!isSep(first_ch))
114 goto anon_wall_station;
115 /* A single separator alone ('.' by default) is an anonymous
116 * station which is on a point inside the passage and implies
117 * the leg to it is a splay.
118 */
119 if (TSTBIT(pcs->flags, FLAGS_ANON_ONE_END)) {
120 set_pos(&here);
121 compile_diagnostic(DIAG_ERR|DIAG_WORD, /*Can't have a leg between two anonymous stations*/3);
122 LONGJMP(file.jbSkipLine);
123 }
124 pcs->flags |= BIT(FLAGS_ANON_ONE_END) | BIT(FLAGS_IMPLICIT_SPLAY);
125 return new_anon_station();
126 }
127 if (isSep(first_ch) && ch == first_ch) {
128 nextch();
129 if (isBlank(ch) || isEol(ch)) {
130 /* A double separator ('..' by default) is an anonymous station
131 * which is on the wall and implies the leg to it is a splay.
132 */
133 prefix * pfx;
134anon_wall_station:
135 if (TSTBIT(pcs->flags, FLAGS_ANON_ONE_END)) {
136 set_pos(&here);
137 compile_diagnostic(DIAG_ERR|DIAG_WORD, /*Can't have a leg between two anonymous stations*/3);
138 LONGJMP(file.jbSkipLine);
139 }
140 pcs->flags |= BIT(FLAGS_ANON_ONE_END) | BIT(FLAGS_IMPLICIT_SPLAY);
141 pfx = new_anon_station();
142 pfx->sflags |= BIT(SFLAGS_WALL);
143 return pfx;
144 }
145 if (ch == first_ch) {
146 nextch();
147 if (isBlank(ch) || isEol(ch)) {
148 /* A triple separator ('...' by default) is an anonymous
149 * station, but otherwise not handled specially (e.g. for
150 * a single leg down an unexplored side passage to a station
151 * which isn't refindable).
152 */
153 if (TSTBIT(pcs->flags, FLAGS_ANON_ONE_END)) {
154 set_pos(&here);
155 compile_diagnostic(DIAG_ERR|DIAG_WORD, /*Can't have a leg between two anonymous stations*/3);
156 LONGJMP(file.jbSkipLine);
157 }
158 pcs->flags |= BIT(FLAGS_ANON_ONE_END);
159 return new_anon_station();
160 }
161 }
162 }
163 set_pos(&here);
164 }
165 ptr = pcs->Prefix;
166 }
167
168 i = 0;
169 name = NULL;
170 do {
171 fNew = false;
172 if (name == NULL) {
173 /* Need a new name buffer */
174 name = osmalloc(name_len);
175 }
176 /* i==0 iff this is the first pass */
177 if (i) {
178 i = 0;
179 nextch();
180 }
181 while (isNames(ch)) {
182 if (i < pcs->Truncate) {
183 /* truncate name */
184 name[i++] = (pcs->Case == LOWER ? tolower(ch) :
185 (pcs->Case == OFF ? ch : toupper(ch)));
186 if (i >= name_len) {
187 name_len = name_len + name_len;
188 name = osrealloc(name, name_len);
189 }
190 }
191 nextch();
192 }
193 if (isSep(ch)) {
194 fImplicitPrefix = false;
195 get_pos(&fp_firstsep);
196 }
197 if (i == 0) {
198 osfree(name);
199 if (!f_optional) {
200 if (isEol(ch)) {
201 if (fSurvey) {
202 compile_diagnostic(DIAG_ERR|DIAG_COL, /*Expecting survey name*/89);
203 } else {
204 compile_diagnostic(DIAG_ERR|DIAG_COL, /*Expecting station name*/28);
205 }
206 } else {
207 /* TRANSLATORS: Here "station" is a survey station, not a train station. */
208 compile_diagnostic(DIAG_ERR|DIAG_COL, /*Character “%c” not allowed in station name (use *SET NAMES to set allowed characters)*/7, ch);
209 }
210 LONGJMP(file.jbSkipLine);
211 }
212 return (prefix *)NULL;
213 }
214
215 name[i++] = '\0';
216
217 back_ptr = ptr;
218 ptr = ptr->down;
219 if (ptr == NULL) {
220 /* Special case first time around at each level */
221 name = osrealloc(name, i);
222 ptr = osnew(prefix);
223 ptr->ident = name;
224 name = NULL;
225 ptr->right = ptr->down = NULL;
226 ptr->pos = NULL;
227 ptr->shape = 0;
228 ptr->stn = NULL;
229 ptr->up = back_ptr;
230 ptr->filename = file.filename;
231 ptr->line = file.line;
232 ptr->min_export = ptr->max_export = 0;
233 ptr->sflags = BIT(SFLAGS_SURVEY);
234 if (fSuspectTypo && !fImplicitPrefix)
235 ptr->sflags |= BIT(SFLAGS_SUSPECTTYPO);
236 back_ptr->down = ptr;
237 fNew = true;
238 } else {
239 /* Use caching to speed up adding an increasing sequence to a
240 * large survey */
241 static prefix *cached_survey = NULL, *cached_station = NULL;
242 prefix *ptrPrev = NULL;
243 int cmp = 1; /* result of strcmp ( -ve for <, 0 for =, +ve for > ) */
244 if (cached_survey == back_ptr) {
245 cmp = strcmp(cached_station->ident, name);
246 if (cmp <= 0) ptr = cached_station;
247 }
248 while (ptr && (cmp = strcmp(ptr->ident, name)) < 0) {
249 ptrPrev = ptr;
250 ptr = ptr->right;
251 }
252 if (cmp) {
253 /* ie we got to one that was higher, or the end */
254 prefix *newptr;
255 name = osrealloc(name, i);
256 newptr = osnew(prefix);
257 newptr->ident = name;
258 name = NULL;
259 if (ptrPrev == NULL)
260 back_ptr->down = newptr;
261 else
262 ptrPrev->right = newptr;
263 newptr->right = ptr;
264 newptr->down = NULL;
265 newptr->pos = NULL;
266 newptr->shape = 0;
267 newptr->stn = NULL;
268 newptr->up = back_ptr;
269 newptr->filename = file.filename;
270 newptr->line = file.line;
271 newptr->min_export = newptr->max_export = 0;
272 newptr->sflags = BIT(SFLAGS_SURVEY);
273 if (fSuspectTypo && !fImplicitPrefix)
274 newptr->sflags |= BIT(SFLAGS_SUSPECTTYPO);
275 ptr = newptr;
276 fNew = true;
277 }
278 cached_survey = back_ptr;
279 cached_station = ptr;
280 }
281 depth++;
282 f_optional = false; /* disallow after first level */
283 if (isSep(ch)) {
284 get_pos(&fp_firstsep);
285 if (!TSTBIT(ptr->sflags, SFLAGS_SURVEY)) {
286 /* TRANSLATORS: Here "station" is a survey station, not a train station.
287 *
288 * Here "survey" is a "cave map" rather than list of questions - it should be
289 * translated to the terminology that cavers using the language would use.
290 */
291 compile_diagnostic(DIAG_ERR|DIAG_FROM(here), /*“%s” can’t be both a station and a survey*/27,
292 sprint_prefix(ptr));
293 }
294 }
295 } while (isSep(ch));
296 if (name) osfree(name);
297
298 /* don't warn about a station that is referred to twice */
299 if (!fNew) ptr->sflags &= ~BIT(SFLAGS_SUSPECTTYPO);
300
301 if (fNew) {
302 /* fNew means SFLAGS_SURVEY is currently set */
303 SVX_ASSERT(TSTBIT(ptr->sflags, SFLAGS_SURVEY));
304 if (!fSurvey) {
305 ptr->sflags &= ~BIT(SFLAGS_SURVEY);
306 if (TSTBIT(pcs->infer, INFER_EXPORTS)) ptr->min_export = USHRT_MAX;
307 }
308 } else {
309 /* check that the same name isn't being used for a survey and station */
310 if (fSurvey ^ TSTBIT(ptr->sflags, SFLAGS_SURVEY)) {
311 /* TRANSLATORS: Here "station" is a survey station, not a train station.
312 *
313 * Here "survey" is a "cave map" rather than list of questions - it should be
314 * translated to the terminology that cavers using the language would use.
315 */
316 compile_diagnostic(DIAG_ERR|DIAG_FROM(here), /*“%s” can’t be both a station and a survey*/27,
317 sprint_prefix(ptr));
318 }
319 if (!fSurvey && TSTBIT(pcs->infer, INFER_EXPORTS)) ptr->min_export = USHRT_MAX;
320 }
321
322 /* check the export level */
323#if 0
324 printf("R min %d max %d depth %d pfx %s\n",
325 ptr->min_export, ptr->max_export, depth, sprint_prefix(ptr));
326#endif
327 if (ptr->min_export == 0 || ptr->min_export == USHRT_MAX) {
328 if (depth > ptr->max_export) ptr->max_export = depth;
329 } else if (ptr->max_export < depth) {
330 prefix *survey = ptr;
331 char *s;
332 const char *p;
333 int level;
334 for (level = ptr->max_export + 1; level; level--) {
335 survey = survey->up;
336 SVX_ASSERT(survey);
337 }
338 s = osstrdup(sprint_prefix(survey));
339 p = sprint_prefix(ptr);
340 if (survey->filename) {
341 compile_diagnostic_pfx(DIAG_ERR, survey,
342 /*Station “%s” not exported from survey “%s”*/26,
343 p, s);
344 } else {
345 compile_diagnostic(DIAG_ERR, /*Station “%s” not exported from survey “%s”*/26, p, s);
346 }
347 osfree(s);
348#if 0
349 printf(" *** pfx %s warning not exported enough depth %d "
350 "ptr->max_export %d\n", sprint_prefix(ptr),
351 depth, ptr->max_export);
352#endif
353 }
354 if (!fImplicitPrefix && (pfx_flags & PFX_WARN_SEPARATOR)) {
355 filepos fp_tmp;
356 get_pos(&fp_tmp);
357 set_pos(&fp_firstsep);
358 compile_diagnostic(DIAG_WARN|DIAG_COL, /*Separator in survey name*/392);
359 set_pos(&fp_tmp);
360 }
361 return ptr;
362}
363
364char *
365read_walls_prefix(void)
366{
367 string name = S_INIT;
368 skipblanks();
369 if (!isNames(ch))
370 return NULL;
371 do {
372 s_catchar(&name, ch);
373 nextch();
374 } while (isNames(ch));
375 return s_steal(&name);
376}
377
378prefix *
379read_walls_station(char * const walls_prefix[3], bool anon_allowed)
380{
381// bool f_optional = false; //!!(pfx_flags & PFX_OPT);
382// bool fSuspectTypo = false; //!!(pfx_flags & PFX_SUSPECT_TYPO);
383// prefix *back_ptr, *ptr;
384 string component = S_INIT;
385// size_t i;
386// bool fNew;
387// bool fImplicitPrefix = true;
388// int depth = -1;
389// filepos fp_firstsep;
390
391 filepos fp;
392 get_pos(&fp);
393
394 skipblanks();
395 if (anon_allowed && ch == '-') {
396 // - or -- is an anonymous wall point in a shot, but in #Fix they seem
397 // to just be treated as ordinary station names.
398 // FIXME: Issue warning for such a useless station?
399 //
400 // Not yet checked, but you can presumably use - and -- as a prefix
401 // (FIXME check this).
402 nextch();
403 int dashes = 1;
404 if (ch == '-') {
405 ++dashes;
406 nextch();
407 }
408 if (!isNames(ch) && ch != ':') {
409 // An anonymous station implies the leg it is on is a splay.
410 if (TSTBIT(pcs->flags, FLAGS_ANON_ONE_END)) {
411 set_pos(&fp);
412 // Walls also rejects this case.
413 compile_diagnostic(DIAG_ERR|DIAG_TOKEN, /*Can't have a leg between two anonymous stations*/3);
414 LONGJMP(file.jbSkipLine);
415 }
416 pcs->flags |= BIT(FLAGS_ANON_ONE_END) | BIT(FLAGS_IMPLICIT_SPLAY);
417 prefix *pfx = new_anon_station();
418 pfx->sflags |= BIT(SFLAGS_WALL);
419 return pfx;
420 }
421 s_catn(&component, dashes, '-');
422 }
423
424 char *w_prefix[3] = { NULL, NULL, NULL };
425 int explicit_prefix_levels = 0;
426 while (true) {
427 while (isNames(ch)) {
428 s_catchar(&component, ch);
429 nextch();
430 }
431 //printf("component = '%s'\n", s_str(&component));
432 if (ch == ':') {
433 nextch();
434
435 if (++explicit_prefix_levels > 3) {
436 // FIXME Make this a proper error
437 printf("too many prefix levels\n");
438 s_free(&component);
439 for (int i = 0; i < 3; ++i) osfree(w_prefix[i]);
440 LONGJMP(file.jbSkipLine);
441 }
442
443 if (!s_empty(&component)) {
444 // printf("w_prefix[%d] = '%s'\n", explicit_prefix_levels - 1, s_str(&component));
445 w_prefix[explicit_prefix_levels - 1] = s_steal(&component);
446 }
447
448 continue;
449 }
450
451 // printf("explicit_prefix_levels=%d %s:%s:%s\n", explicit_prefix_levels, w_prefix[0], w_prefix[1], w_prefix[2]);
452
453 // component is the station name itself.
454 if (s_empty(&component)) {
455 compile_diagnostic(DIAG_ERR|DIAG_COL, /*Expecting station name*/28);
456 s_free(&component);
457 for (int i = 0; i < 3; ++i) osfree(w_prefix[i]);
458 LONGJMP(file.jbSkipLine);
459 }
460 int len = s_len(&component);
461 char *p = s_steal(&component);
462 // Apply case treatment.
463 switch (pcs->Case) {
464 case LOWER:
465 for (int i = 0; i < len; ++i)
466 p[i] = tolower((unsigned char)p[i]);
467 break;
468 case UPPER:
469 for (int i = 0; i < len; ++i)
470 p[i] = toupper((unsigned char)p[i]);
471 break;
472 case OFF:
473 // Avoid unhandled enum warning.
474 break;
475 }
476
477 prefix *ptr = root;
478 for (int i = 0; i < 4; ++i) {
479 const char *name;
480 int sflag = BIT(SFLAGS_SURVEY);
481 if (i == 3) {
482 name = p;
483 sflag = 0;
484 } else {
485 if (i < 3 - explicit_prefix_levels) {
486 name = walls_prefix[i];
487 // printf("using walls_prefix[%d] = '%s'\n", 2 - i, name);
488 } else {
489 name = w_prefix[i - (3 - explicit_prefix_levels)]; // FIXME: Could steal wprefix[i].
490 // printf("using w_prefix[%d] = '%s'\n", i - (3 - explicit_prefix_levels), name);
491 }
492
493 if (name == NULL) {
494 // FIXME: This means :X::Y is treated as the same as
495 // ::X:Y but is that right? Walls docs don't really
496 // say. Need to test (and is they're different then
497 // probably use a character not valid in Walls station
498 // names for the empty prefix level (e.g. space or
499 // `#`).
500 //
501 // Also, does Walls allow :::X as a station and
502 // ::X:Y which would mean X is a station and survey?
503 // If so, we probably want to keep every empty level.
504 continue;
505 }
506 }
507 prefix *back_ptr = ptr;
508 ptr = ptr->down;
509 if (ptr == NULL) {
510 /* Special case first time around at each level */
511 ptr = osnew(prefix);
512 ptr->ident = (i < 3 ? osstrdup(name) : name);
513 name = NULL;
514 ptr->right = ptr->down = NULL;
515 ptr->pos = NULL;
516 ptr->shape = 0;
517 ptr->stn = NULL;
518 ptr->up = back_ptr;
519 ptr->filename = file.filename; // FIXME: Or location of #Prefix, etc for it?
520 ptr->line = file.line; // FIXME: Or location of #Prefix, etc for it?
521 ptr->min_export = ptr->max_export = 0;
522 ptr->sflags = sflag;
523 back_ptr->down = ptr;
524 } else {
525 /* Use caching to speed up adding an increasing sequence to a
526 * large survey */
527 static prefix *cached_survey = NULL, *cached_station = NULL;
528 prefix *ptrPrev = NULL;
529 int cmp = 1; /* result of strcmp ( -ve for <, 0 for =, +ve for > ) */
530 if (cached_survey == back_ptr) {
531 cmp = strcmp(cached_station->ident, name);
532 if (cmp <= 0) ptr = cached_station;
533 }
534 while (ptr && (cmp = strcmp(ptr->ident, name))<0) {
535 ptrPrev = ptr;
536 ptr = ptr->right;
537 }
538 if (cmp) {
539 /* ie we got to one that was higher, or the end */
540 prefix *newptr;
541 newptr = osnew(prefix);
542 newptr->ident = (i < 3 ? osstrdup(name) : name);
543 name = NULL;
544 if (ptrPrev == NULL)
545 back_ptr->down = newptr;
546 else
547 ptrPrev->right = newptr;
548 newptr->right = ptr;
549 newptr->down = NULL;
550 newptr->pos = NULL;
551 newptr->shape = 0;
552 newptr->stn = NULL;
553 newptr->up = back_ptr;
554 newptr->filename = file.filename; // FIXME
555 newptr->line = file.line;
556 newptr->min_export = newptr->max_export = 0;
557 newptr->sflags = sflag;
558 ptr = newptr;
559 } else {
560 if ((ptr->sflags & sflag) == 0) {
561 // FIXME diagnostic?
562 }
563 }
564 cached_survey = back_ptr;
565 cached_station = ptr;
566 }
567 if (name == p) osfree(p);
568 }
569
570 // fprint_prefix(stdout, ptr); fputnl(stdout);
571
572 for (int i = 0; i < 3; ++i) osfree(w_prefix[i]);
573
574 return ptr;
575 }
576}
577
578/* if numeric expr is omitted: if f_optional return HUGE_REAL, else longjmp */
579real
580read_number(bool f_optional, bool f_unsigned)
581{
582 bool fPositive = true, fDigits = false;
583 real n = (real)0.0;
584 filepos fp;
585 int ch_old;
586
587 get_pos(&fp);
588 ch_old = ch;
589 if (!f_unsigned) {
590 fPositive = !isMinus(ch);
591 if (isSign(ch)) nextch();
592 }
593
594 while (isdigit(ch)) {
595 n = n * (real)10.0 + (char)(ch - '0');
596 nextch();
597 fDigits = true;
598 }
599
600 if (isDecimal(ch)) {
601 real mult = (real)1.0;
602 nextch();
603 while (isdigit(ch)) {
604 mult *= (real).1;
605 n += (char)(ch - '0') * mult;
606 fDigits = true;
607 nextch();
608 }
609 }
610
611 /* !'fRead' => !fDigits so fDigits => 'fRead' */
612 if (fDigits) return (fPositive ? n : -n);
613
614 /* didn't read a valid number. If it's optional, reset filepos & return */
615 set_pos(&fp);
616 if (f_optional) {
617 return HUGE_REAL;
618 }
619
620 if (isOmit(ch_old)) {
621 compile_diagnostic(DIAG_ERR|DIAG_COL, /*Field may not be omitted*/8);
622 } else {
623 compile_diagnostic_token_show(DIAG_ERR, /*Expecting numeric field, found “%s”*/9);
624 }
625 LONGJMP(file.jbSkipLine);
626 return 0.0; /* for brain-fried compilers */
627}
628
629real
630read_quadrant(bool f_optional)
631{
632 enum {
633 POINT_N = 0,
634 POINT_E = 1,
635 POINT_S = 2,
636 POINT_W = 3,
637 POINT_NONE = -1
638 };
639 static const sztok pointtab[] = {
640 {"E", POINT_E },
641 {"N", POINT_N },
642 {"S", POINT_S },
643 {"W", POINT_W },
644 {NULL, POINT_NONE }
645 };
646 static const sztok pointewtab[] = {
647 {"E", POINT_E },
648 {"W", POINT_W },
649 {NULL, POINT_NONE }
650 };
651 if (f_optional && isOmit(ch)) {
652 return HUGE_REAL;
653 }
654 const int quad = 90;
655 filepos fp;
656 get_pos(&fp);
657 get_token_no_blanks();
658 int first_point = match_tok(pointtab, TABSIZE(pointtab));
659 if (first_point == POINT_NONE) {
660 set_pos(&fp);
661 if (isOmit(ch)) {
662 compile_diagnostic(DIAG_ERR|DIAG_COL, /*Field may not be omitted*/8);
663 }
664 compile_diagnostic_token_show(DIAG_ERR, /*Expecting quadrant bearing, found “%s”*/483);
665 LONGJMP(file.jbSkipLine);
666 return 0.0; /* for brain-fried compilers */
667 }
668 real r = read_number(true, true);
669 if (r == HUGE_REAL) {
670 if (isSign(ch) || isDecimal(ch)) {
671 /* Give better errors for S-0E, N+10W, N.E, etc. */
672 set_pos(&fp);
673 compile_diagnostic_token_show(DIAG_ERR, /*Expecting quadrant bearing, found “%s”*/483);
674 LONGJMP(file.jbSkipLine);
675 return 0.0; /* for brain-fried compilers */
676 }
677 /* N, S, E or W. */
678 return first_point * quad;
679 }
680 if (first_point == POINT_E || first_point == POINT_W) {
681 set_pos(&fp);
682 compile_diagnostic_token_show(DIAG_ERR, /*Expecting quadrant bearing, found “%s”*/483);
683 LONGJMP(file.jbSkipLine);
684 return 0.0; /* for brain-fried compilers */
685 }
686
687 get_token_no_blanks();
688 int second_point = match_tok(pointewtab, TABSIZE(pointewtab));
689 if (second_point == POINT_NONE) {
690 set_pos(&fp);
691 compile_diagnostic_token_show(DIAG_ERR, /*Expecting quadrant bearing, found “%s”*/483);
692 LONGJMP(file.jbSkipLine);
693 return 0.0; /* for brain-fried compilers */
694 }
695
696 if (r > quad) {
697 set_pos(&fp);
698 compile_diagnostic_token_show(DIAG_ERR|DIAG_COL, /*Suspicious compass reading*/59);
699 LONGJMP(file.jbSkipLine);
700 return 0.0; /* for brain-fried compilers */
701 }
702
703 if (first_point == POINT_N) {
704 if (second_point == POINT_W) {
705 r = quad * 4 - r;
706 }
707 } else {
708 if (second_point == POINT_W) {
709 r += quad * 2;
710 } else {
711 r = quad * 2 - r;
712 }
713 }
714 return r;
715}
716
717extern real
718read_numeric(bool f_optional)
719{
720 skipblanks();
721 return read_number(f_optional, false);
722}
723
724extern real
725read_numeric_multi(bool f_optional, bool f_quadrants, int *p_n_readings)
726{
727 size_t n_readings = 0;
728 real tot = (real)0.0;
729
730 skipblanks();
731 if (!isOpen(ch)) {
732 real r = 0;
733 if (!f_quadrants)
734 r = read_number(f_optional, false);
735 else
736 r = read_quadrant(f_optional);
737 if (p_n_readings) *p_n_readings = (r == HUGE_REAL ? 0 : 1);
738 return r;
739 }
740 nextch();
741
742 skipblanks();
743 do {
744 if (!f_quadrants)
745 tot += read_number(false, false);
746 else
747 tot += read_quadrant(false);
748 ++n_readings;
749 skipblanks();
750 } while (!isClose(ch));
751 nextch();
752
753 if (p_n_readings) *p_n_readings = n_readings;
754 /* FIXME: special averaging for bearings ... */
755 /* And for percentage gradient */
756 return tot / n_readings;
757}
758
759/* read numeric expr or omit (return HUGE_REAL); else longjmp */
760extern real
761read_bearing_multi_or_omit(bool f_quadrants, int *p_n_readings)
762{
763 real v;
764 v = read_numeric_multi(true, f_quadrants, p_n_readings);
765 if (v == HUGE_REAL) {
766 if (!isOmit(ch)) {
767 compile_diagnostic_token_show(DIAG_ERR, /*Expecting numeric field, found “%s”*/9);
768 LONGJMP(file.jbSkipLine);
769 return 0.0; /* for brain-fried compilers */
770 }
771 nextch();
772 }
773 return v;
774}
775
776/* Don't skip blanks, variable error code */
777static unsigned int
778read_uint_internal(int errmsg, const filepos *fp)
779{
780 unsigned int n = 0;
781 if (!isdigit(ch)) {
782 if (fp) set_pos(fp);
783 compile_diagnostic_token_show(DIAG_ERR, errmsg);
784 LONGJMP(file.jbSkipLine);
785 }
786 while (isdigit(ch)) {
787 n = n * 10 + (char)(ch - '0');
788 nextch();
789 }
790 return n;
791}
792
793extern unsigned int
794read_uint(void)
795{
796 skipblanks();
797 return read_uint_internal(/*Expecting numeric field, found “%s”*/9, NULL);
798}
799
800extern int
801read_int(int min_val, int max_val)
802{
803 skipblanks();
804 unsigned n = 0;
805 filepos fp;
806
807 get_pos(&fp);
808 bool negated = isMinus(ch);
809 unsigned limit;
810 if (negated) {
811 limit = (unsigned)(min_val == INT_MIN ? INT_MIN : -min_val);
812 } else {
813 limit = (unsigned)max_val;
814 }
815 if (isSign(ch)) nextch();
816
817 if (!isdigit(ch)) {
818bad_value:
819 set_pos(&fp);
820 /* TRANSLATORS: The first %d will be replaced by the (inclusive) lower
821 * bound and the second by the (inclusive) upper bound, for example:
822 * Expecting integer in range -60 to 60
823 */
824 compile_diagnostic(DIAG_ERR|DIAG_NUM, /*Expecting integer in range %d to %d*/489);
825 LONGJMP(file.jbSkipLine);
826 }
827
828 while (isdigit(ch)) {
829 unsigned old_n = n;
830 n = n * 10 + (char)(ch - '0');
831 if (n > limit || n < old_n) {
832 goto bad_value;
833 }
834 nextch();
835 }
836 if (isDecimal(ch)) goto bad_value;
837
838 if (negated) {
839 if (n > (unsigned)INT_MAX) {
840 // Avoid unportable casting.
841 return INT_MIN;
842 }
843 return -(int)n;
844 }
845 return (int)n;
846}
847
848extern void
849read_string(string *pstr)
850{
851 s_clear(pstr);
852
853 skipblanks();
854 if (ch == '\"') {
855 /* String quoted in "" */
856 nextch();
857 while (1) {
858 if (isEol(ch)) {
859 compile_diagnostic(DIAG_ERR|DIAG_COL, /*Missing \"*/69);
860 LONGJMP(file.jbSkipLine);
861 }
862
863 if (ch == '\"') break;
864
865 s_catchar(pstr, ch);
866 nextch();
867 }
868 nextch();
869 } else {
870 /* Unquoted string */
871 while (1) {
872 if (isEol(ch) || isComm(ch)) {
873 if (s_empty(pstr)) {
874 compile_diagnostic(DIAG_ERR|DIAG_COL, /*Expecting string field*/121);
875 LONGJMP(file.jbSkipLine);
876 }
877 return;
878 }
879
880 if (isBlank(ch)) break;
881
882 s_catchar(pstr, ch);
883 nextch();
884 }
885 }
886}
887
888extern void
889read_date(int *py, int *pm, int *pd)
890{
891 unsigned int y = 0, m = 0, d = 0;
892 filepos fp_date;
893
894 skipblanks();
895
896 get_pos(&fp_date);
897 y = read_uint_internal(/*Expecting date, found “%s”*/198, &fp_date);
898 /* Two digit year is 19xx. */
899 if (y < 100) {
900 filepos fp_save;
901 get_pos(&fp_save);
902 y += 1900;
903 set_pos(&fp_date);
904 /* TRANSLATORS: %d will be replaced by the assumed year, e.g. 1918 */
905 compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Assuming 2 digit year is %d*/76, y);
906 set_pos(&fp_save);
907 }
908 if (y < 1900 || y > 2078) {
909 set_pos(&fp_date);
910 compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Invalid year (< 1900 or > 2078)*/58);
911 LONGJMP(file.jbSkipLine);
912 return; /* for brain-fried compilers */
913 }
914 if (ch == '.') {
915 filepos fp;
916 nextch();
917 get_pos(&fp);
918 m = read_uint_internal(/*Expecting date, found “%s”*/198, &fp_date);
919 if (m < 1 || m > 12) {
920 set_pos(&fp);
921 compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Invalid month*/86);
922 LONGJMP(file.jbSkipLine);
923 return; /* for brain-fried compilers */
924 }
925 if (ch == '.') {
926 nextch();
927 get_pos(&fp);
928 d = read_uint_internal(/*Expecting date, found “%s”*/198, &fp_date);
929 if (d < 1 || d > last_day(y, m)) {
930 set_pos(&fp);
931 /* TRANSLATORS: e.g. 31st of April, or 32nd of any month */
932 compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Invalid day of the month*/87);
933 LONGJMP(file.jbSkipLine);
934 return; /* for brain-fried compilers */
935 }
936 }
937 }
938 if (py) *py = y;
939 if (pm) *pm = m;
940 if (pd) *pd = d;
941}
942
943extern void
944read_walls_srv_date(int *py, int *pm, int *pd)
945{
946 skipblanks();
947
948 filepos fp_date;
949 get_pos(&fp_date);
950 unsigned y = read_uint_internal(/*Expecting date, found “%s”*/198, &fp_date);
951 int separator = -2;
952 if (ch == '-' || ch == '/') {
953 separator = ch;
954 nextch();
955 }
956 filepos fp_month;
957 get_pos(&fp_month);
958 unsigned m = read_uint_internal(/*Expecting date, found “%s”*/198, &fp_date);
959 if (ch == separator) {
960 nextch();
961 }
962 filepos fp_day;
963 get_pos(&fp_day);
964 unsigned d = read_uint_internal(/*Expecting date, found “%s”*/198, &fp_date);
965
966 filepos fp_year;
967 if (y < 100) {
968 // Walls recommends ISO 8601 date format (yyyy-mm-dd and seemingly the
969 // non-standard variant yyyy/mm/dd), but also accepts "some date formats
970 // common in the U.S. (mm/dd/yy, mm-dd-yyyy, etc.)"
971 unsigned tmp = y;
972 y = d;
973 fp_year = fp_day;
974 d = m;
975 fp_day = fp_month;
976 m = tmp;
977 fp_month = fp_date;
978
979 if (y < 100) {
980 // FIXME: Are all 2 digit years 19xx?
981 y += 1900;
982
983 filepos fp_save;
984 get_pos(&fp_save);
985 set_pos(&fp_year);
986 /* TRANSLATORS: %d will be replaced by the assumed year, e.g. 1918 */
987 compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Assuming 2 digit year is %d*/76, y);
988 set_pos(&fp_save);
989 }
990 } else {
991 if (y < 1900 || y > 2078) {
992 set_pos(&fp_date);
993 compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Invalid year (< 1900 or > 2078)*/58);
994 LONGJMP(file.jbSkipLine);
995 return; /* for brain-fried compilers */
996 }
997 fp_year = fp_date;
998 }
999
1000 if (m < 1 || m > 12) {
1001 set_pos(&fp_month);
1002 compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Invalid month*/86);
1003 LONGJMP(file.jbSkipLine);
1004 return; /* for brain-fried compilers */
1005 }
1006
1007 if (d < 1 || d > last_day(y, m)) {
1008 set_pos(&fp_day);
1009 /* TRANSLATORS: e.g. 31st of April, or 32nd of any month */
1010 compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Invalid day of the month*/87);
1011 LONGJMP(file.jbSkipLine);
1012 return; /* for brain-fried compilers */
1013 }
1014
1015 if (py) *py = y;
1016 if (pm) *pm = m;
1017 if (pd) *pd = d;
1018}
Note: See TracBrowser for help on using the repository browser.