2 * Routines to read a prefix or number from the current input file
3 * Copyright (C) 1991-2024 Olly Betts
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.
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.
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
23 #include <stddef.h> /* for offsetof */
26 #include "commands.h" /* For match_tok(), etc */
37 int root_depr_count
= 0;
40 new_anon_station(void)
42 prefix
*name
= osnew(prefix
);
47 name
->up
= pcs
->Prefix
;
49 name
->filename
= file
.filename
;
50 name
->line
= file
.line
;
51 name
->min_export
= name
->max_export
= 0;
52 name
->sflags
= BIT(SFLAGS_ANON
);
53 /* Keep linked list of anon stations for node stats. */
54 name
->right
= anon_list
;
59 static char *id
= NULL
;
60 static size_t id_len
= 0;
62 /* if prefix is omitted: if PFX_OPT set return NULL, otherwise use longjmp */
64 read_prefix(unsigned pfx_flags
)
66 bool f_optional
= !!(pfx_flags
& PFX_OPT
);
67 bool fSurvey
= !!(pfx_flags
& PFX_SURVEY
);
68 bool fSuspectTypo
= !!(pfx_flags
& PFX_SUSPECT_TYPO
);
69 prefix
*back_ptr
, *ptr
;
72 bool fImplicitPrefix
= true;
81 if (!(pfx_flags
& PFX_ALLOW_ROOT
)) {
82 compile_diagnostic(DIAG_ERR
|DIAG_COL
, /*ROOT is deprecated*/25);
83 longjmp(jbSkipLine
, 1);
85 if (root_depr_count
< 5) {
86 compile_diagnostic(DIAG_WARN
|DIAG_COL
, /*ROOT is deprecated*/25);
87 if (++root_depr_count
== 5)
88 compile_diagnostic(DIAG_INFO
, /*Further uses of this deprecated feature will not be reported*/95);
93 if (!isSep(ch
)) return ptr
;
94 /* Allow optional SEPARATOR after ROOT */
95 get_pos(&fp_firstsep
);
98 fImplicitPrefix
= false;
103 if ((pfx_flags
& PFX_ANON
) &&
104 (isSep(ch
) || (pcs
->dash_for_anon_wall_station
&& ch
== '-'))) {
107 if (isBlank(ch
) || isComm(ch
) || isEol(ch
)) {
108 if (!isSep(first_ch
))
109 goto anon_wall_station
;
110 /* A single separator alone ('.' by default) is an anonymous
111 * station which is on a point inside the passage and implies
112 * the leg to it is a splay.
114 if (TSTBIT(pcs
->flags
, FLAGS_ANON_ONE_END
)) {
116 compile_diagnostic(DIAG_ERR
|DIAG_WORD
, /*Can't have a leg between two anonymous stations*/3);
117 longjmp(jbSkipLine
, 1);
119 pcs
->flags
|= BIT(FLAGS_ANON_ONE_END
) | BIT(FLAGS_IMPLICIT_SPLAY
);
120 return new_anon_station();
122 if (isSep(first_ch
) && ch
== first_ch
) {
124 if (isBlank(ch
) || isComm(ch
) || isEol(ch
)) {
125 /* A double separator ('..' by default) is an anonymous station
126 * which is on the wall and implies the leg to it is a splay.
130 if (TSTBIT(pcs
->flags
, FLAGS_ANON_ONE_END
)) {
132 compile_diagnostic(DIAG_ERR
|DIAG_WORD
, /*Can't have a leg between two anonymous stations*/3);
133 longjmp(jbSkipLine
, 1);
135 pcs
->flags
|= BIT(FLAGS_ANON_ONE_END
) | BIT(FLAGS_IMPLICIT_SPLAY
);
136 pfx
= new_anon_station();
137 pfx
->sflags
|= BIT(SFLAGS_WALL
);
140 if (ch
== first_ch
) {
142 if (isBlank(ch
) || isComm(ch
) || isEol(ch
)) {
143 /* A triple separator ('...' by default) is an anonymous
144 * station, but otherwise not handled specially (e.g. for
145 * a single leg down an unexplored side passage to a station
146 * which isn't refindable).
148 if (TSTBIT(pcs
->flags
, FLAGS_ANON_ONE_END
)) {
150 compile_diagnostic(DIAG_ERR
|DIAG_WORD
, /*Can't have a leg between two anonymous stations*/3);
151 longjmp(jbSkipLine
, 1);
153 pcs
->flags
|= BIT(FLAGS_ANON_ONE_END
);
154 return new_anon_station();
167 /* Allocate buffer the first time */
169 id
= osmalloc(id_len
);
171 /* i==0 iff this is the first pass */
176 while (isNames(ch
)) {
177 if (i
< pcs
->Truncate
) {
179 id
[i
++] = (pcs
->Case
== LOWER
? tolower(ch
) :
180 (pcs
->Case
== OFF
? ch
: toupper(ch
)));
183 id
= osrealloc(id
, id_len
);
189 fImplicitPrefix
= false;
190 get_pos(&fp_firstsep
);
196 compile_diagnostic(DIAG_ERR
|DIAG_COL
, /*Expecting survey name*/89);
198 compile_diagnostic(DIAG_ERR
|DIAG_COL
, /*Expecting station name*/28);
201 /* TRANSLATORS: Here "station" is a survey station, not a train station. */
202 compile_diagnostic(DIAG_ERR
|DIAG_COL
, /*Character “%c” not allowed in station name (use *SET NAMES to set allowed characters)*/7, ch
);
204 longjmp(jbSkipLine
, 1);
206 return (prefix
*)NULL
;
214 /* Special case first time around at each level */
216 ptr
->sflags
= BIT(SFLAGS_SURVEY
);
217 if (i
<= sizeof(ptr
->ident
.i
)) {
218 memcpy(ptr
->ident
.i
, id
, i
);
219 ptr
->sflags
|= BIT(SFLAGS_IDENT_INLINE
);
221 char *new_id
= osmalloc(i
);
222 memcpy(new_id
, id
, i
);
223 ptr
->ident
.p
= new_id
;
225 ptr
->right
= ptr
->down
= NULL
;
230 ptr
->filename
= file
.filename
;
231 ptr
->line
= file
.line
;
232 ptr
->min_export
= ptr
->max_export
= 0;
233 if (fSuspectTypo
&& !fImplicitPrefix
)
234 ptr
->sflags
|= BIT(SFLAGS_SUSPECTTYPO
);
235 back_ptr
->down
= ptr
;
238 /* Use caching to speed up adding an increasing sequence to a
240 static prefix
*cached_survey
= NULL
, *cached_station
= NULL
;
241 prefix
*ptrPrev
= NULL
;
242 int cmp
= 1; /* result of strcmp ( -ve for <, 0 for =, +ve for > ) */
243 if (cached_survey
== back_ptr
) {
244 cmp
= strcmp(prefix_ident(cached_station
), id
);
245 if (cmp
<= 0) ptr
= cached_station
;
247 while (ptr
&& (cmp
= strcmp(prefix_ident(ptr
), id
)) < 0) {
252 /* ie we got to one that was higher, or the end */
253 prefix
*newptr
= osnew(prefix
);
254 newptr
->sflags
= BIT(SFLAGS_SURVEY
);
255 if (strlen(id
) < sizeof(newptr
->ident
.i
)) {
256 memcpy(newptr
->ident
.i
, id
, i
);
257 newptr
->sflags
|= BIT(SFLAGS_IDENT_INLINE
);
259 char *new_id
= osmalloc(i
);
260 memcpy(new_id
, id
, i
);
261 newptr
->ident
.p
= new_id
;
264 back_ptr
->down
= newptr
;
266 ptrPrev
->right
= newptr
;
272 newptr
->up
= back_ptr
;
273 newptr
->filename
= file
.filename
;
274 newptr
->line
= file
.line
;
275 newptr
->min_export
= newptr
->max_export
= 0;
276 if (fSuspectTypo
&& !fImplicitPrefix
)
277 newptr
->sflags
|= BIT(SFLAGS_SUSPECTTYPO
);
281 cached_survey
= back_ptr
;
282 cached_station
= ptr
;
285 f_optional
= false; /* disallow after first level */
287 get_pos(&fp_firstsep
);
288 if (!TSTBIT(ptr
->sflags
, SFLAGS_SURVEY
)) {
289 /* TRANSLATORS: Here "station" is a survey station, not a train station.
291 * Here "survey" is a "cave map" rather than list of questions - it should be
292 * translated to the terminology that cavers using the language would use.
294 compile_diagnostic(DIAG_ERR
|DIAG_FROM(here
), /*“%s” can’t be both a station and a survey*/27,
300 /* don't warn about a station that is referred to twice */
301 if (!fNew
) ptr
->sflags
&= ~BIT(SFLAGS_SUSPECTTYPO
);
304 /* fNew means SFLAGS_SURVEY is currently set */
305 SVX_ASSERT(TSTBIT(ptr
->sflags
, SFLAGS_SURVEY
));
307 ptr
->sflags
&= ~BIT(SFLAGS_SURVEY
);
308 if (TSTBIT(pcs
->infer
, INFER_EXPORTS
)) ptr
->min_export
= USHRT_MAX
;
311 /* check that the same name isn't being used for a survey and station */
312 if (fSurvey
^ TSTBIT(ptr
->sflags
, SFLAGS_SURVEY
)) {
313 /* TRANSLATORS: Here "station" is a survey station, not a train station.
315 * Here "survey" is a "cave map" rather than list of questions - it should be
316 * translated to the terminology that cavers using the language would use.
318 compile_diagnostic(DIAG_ERR
|DIAG_FROM(here
), /*“%s” can’t be both a station and a survey*/27,
321 if (!fSurvey
&& TSTBIT(pcs
->infer
, INFER_EXPORTS
)) ptr
->min_export
= USHRT_MAX
;
324 /* check the export level */
326 printf("R min %d max %d depth %d pfx %s\n",
327 ptr
->min_export
, ptr
->max_export
, depth
, sprint_prefix(ptr
));
329 if (ptr
->min_export
== 0 || ptr
->min_export
== USHRT_MAX
) {
330 if (depth
> ptr
->max_export
) ptr
->max_export
= depth
;
331 } else if (ptr
->max_export
< depth
) {
332 prefix
*survey
= ptr
;
336 for (level
= ptr
->max_export
+ 1; level
; level
--) {
340 s
= osstrdup(sprint_prefix(survey
));
341 p
= sprint_prefix(ptr
);
342 if (survey
->filename
) {
343 compile_diagnostic_pfx(DIAG_ERR
, survey
,
344 /*Station “%s” not exported from survey “%s”*/26,
347 compile_diagnostic(DIAG_ERR
, /*Station “%s” not exported from survey “%s”*/26, p
, s
);
351 printf(" *** pfx %s warning not exported enough depth %d "
352 "ptr->max_export %d\n", sprint_prefix(ptr
),
353 depth
, ptr
->max_export
);
356 if (!fImplicitPrefix
&& (pfx_flags
& PFX_WARN_SEPARATOR
)) {
359 set_pos(&fp_firstsep
);
360 compile_diagnostic(DIAG_WARN
|DIAG_COL
, /*Separator in survey name*/392);
367 read_walls_prefix(void)
369 string name
= S_INIT
;
374 s_appendch(&name
, ch
);
376 } while (isNames(ch
));
377 return s_steal(&name
);
381 read_walls_station(char * const walls_prefix
[3], bool anon_allowed
, bool *p_new
)
383 if (p_new
) *p_new
= false;
384 // bool f_optional = false; //!!(pfx_flags & PFX_OPT);
385 // bool fSuspectTypo = false; //!!(pfx_flags & PFX_SUSPECT_TYPO);
386 // prefix *back_ptr, *ptr;
387 string component
= S_INIT
;
390 // bool fImplicitPrefix = true;
392 // filepos fp_firstsep;
398 if (anon_allowed
&& ch
== '-') {
399 // - or -- is an anonymous wall point in a shot, but in #Fix they seem
400 // to just be treated as ordinary station names.
401 // FIXME: Issue warning for such a useless station?
403 // Not yet checked, but you can presumably use - and -- as a prefix
404 // (FIXME check this).
411 if (!isNames(ch
) && ch
!= ':') {
412 // An anonymous station implies the leg it is on is a splay.
413 if (TSTBIT(pcs
->flags
, FLAGS_ANON_ONE_END
)) {
415 // Walls also rejects this case.
416 compile_diagnostic(DIAG_ERR
|DIAG_TOKEN
, /*Can't have a leg between two anonymous stations*/3);
417 longjmp(jbSkipLine
, 1);
419 pcs
->flags
|= BIT(FLAGS_ANON_ONE_END
) | BIT(FLAGS_IMPLICIT_SPLAY
);
420 prefix
*pfx
= new_anon_station();
421 pfx
->sflags
|= BIT(SFLAGS_WALL
);
422 // An anonymous station is always new.
423 if (p_new
) *p_new
= true;
426 s_appendn(&component
, dashes
, '-');
429 char *w_prefix
[3] = { NULL
, NULL
, NULL
};
430 int explicit_prefix_levels
= 0;
432 while (isNames(ch
)) {
433 s_appendch(&component
, ch
);
436 //printf("component = '%s'\n", s_str(&component));
440 if (++explicit_prefix_levels
> 3) {
441 // FIXME Make this a proper error
442 printf("too many prefix levels\n");
444 for (int i
= 0; i
< 3; ++i
) osfree(w_prefix
[i
]);
445 longjmp(jbSkipLine
, 1);
448 if (!s_empty(&component
)) {
449 // printf("w_prefix[%d] = '%s'\n", explicit_prefix_levels - 1, s_str(&component));
450 w_prefix
[explicit_prefix_levels
- 1] = s_steal(&component
);
456 // printf("explicit_prefix_levels=%d %s:%s:%s\n", explicit_prefix_levels, w_prefix[0], w_prefix[1], w_prefix[2]);
458 // component is the station name itself.
459 if (s_empty(&component
)) {
460 if (explicit_prefix_levels
== 0) {
461 compile_diagnostic(DIAG_ERR
|DIAG_COL
, /*Expecting station name*/28);
463 for (int i
= 0; i
< 3; ++i
) osfree(w_prefix
[i
]);
464 longjmp(jbSkipLine
, 1);
466 // Walls allows an empty station name if there's an explicit prefix.
467 // This seems unlikely to be intended, so warn about it.
468 compile_diagnostic(DIAG_WARN
|DIAG_COL
, /*Expecting station name*/28);
469 // Use a name with a space in so it can't collide with a real
470 // Walls station name.
471 s_append(&component
, "empty name");
473 int len
= s_len(&component
);
474 char *p
= s_steal(&component
);
475 // Apply case treatment.
478 for (int i
= 0; i
< len
; ++i
)
479 p
[i
] = tolower((unsigned char)p
[i
]);
482 for (int i
= 0; i
< len
; ++i
)
483 p
[i
] = toupper((unsigned char)p
[i
]);
486 // Avoid unhandled enum warning.
491 for (int i
= 0; i
< 4; ++i
) {
493 int sflag
= BIT(SFLAGS_SURVEY
);
498 if (i
< 3 - explicit_prefix_levels
) {
499 name
= walls_prefix
[i
];
500 // printf("using walls_prefix[%d] = '%s'\n", 2 - i, name);
502 name
= w_prefix
[i
- (3 - explicit_prefix_levels
)]; // FIXME: Could steal wprefix[i].
503 // printf("using w_prefix[%d] = '%s'\n", i - (3 - explicit_prefix_levels), name);
507 // FIXME: This means :X::Y is treated as the same as
508 // ::X:Y but is that right? Walls docs don't really
509 // say. Need to test (and is they're different then
510 // probably use a character not valid in Walls station
511 // names for the empty prefix level (e.g. space or
514 // Also, does Walls allow :::X as a station and
515 // ::X:Y which would mean X is a station and survey?
516 // If so, we probably want to keep every empty level.
520 prefix
*back_ptr
= ptr
;
523 /* Special case first time around at each level */
524 /* No need to check if we're at the station level - if the
525 * prefix is new the station must be. */
526 if (p_new
) *p_new
= true;
529 if (strlen(name
) < sizeof(ptr
->ident
.i
)) {
530 strcpy(ptr
->ident
.i
, name
);
531 ptr
->sflags
|= BIT(SFLAGS_IDENT_INLINE
);
532 if (i
>= 3) osfree(name
);
534 ptr
->ident
.p
= (i
< 3 ? osstrdup(name
) : name
);
537 ptr
->right
= ptr
->down
= NULL
;
542 ptr
->filename
= file
.filename
; // FIXME: Or location of #Prefix, etc for it?
543 ptr
->line
= file
.line
; // FIXME: Or location of #Prefix, etc for it?
544 ptr
->min_export
= ptr
->max_export
= 0;
545 back_ptr
->down
= ptr
;
547 /* Use caching to speed up adding an increasing sequence to a
549 static prefix
*cached_survey
= NULL
, *cached_station
= NULL
;
550 prefix
*ptrPrev
= NULL
;
551 int cmp
= 1; /* result of strcmp ( -ve for <, 0 for =, +ve for > ) */
552 if (cached_survey
== back_ptr
) {
553 cmp
= strcmp(prefix_ident(cached_station
), name
);
554 if (cmp
<= 0) ptr
= cached_station
;
556 while (ptr
&& (cmp
= strcmp(prefix_ident(ptr
), name
))<0) {
561 /* ie we got to one that was higher, or the end */
562 if (p_new
) *p_new
= true;
563 prefix
*newptr
= osnew(prefix
);
564 newptr
->sflags
= sflag
;
565 if (strlen(name
) < sizeof(newptr
->ident
.i
)) {
566 strcpy(newptr
->ident
.i
, name
);
567 newptr
->sflags
|= BIT(SFLAGS_IDENT_INLINE
);
568 if (i
>= 3) osfree(name
);
570 newptr
->ident
.p
= (i
< 3 ? osstrdup(name
) : name
);
574 back_ptr
->down
= newptr
;
576 ptrPrev
->right
= newptr
;
582 newptr
->up
= back_ptr
;
583 newptr
->filename
= file
.filename
; // FIXME
584 newptr
->line
= file
.line
;
585 newptr
->min_export
= newptr
->max_export
= 0;
588 ptr
->sflags
|= sflag
;
590 if (!TSTBIT(ptr
->sflags
, SFLAGS_SURVEY
)) {
591 ptr
->min_export
= USHRT_MAX
;
593 cached_survey
= back_ptr
;
594 cached_station
= ptr
;
596 if (name
== p
) osfree(p
);
599 // Do the equivalent of "*infer exports" for Walls stations with an
601 if (ptr
->min_export
== 0 || ptr
->min_export
== USHRT_MAX
) {
602 if (explicit_prefix_levels
> ptr
->max_export
)
603 ptr
->max_export
= explicit_prefix_levels
;
606 for (int i
= 0; i
< 3; ++i
) osfree(w_prefix
[i
]);
612 /* if numeric expr is omitted: if f_optional return HUGE_REAL, else longjmp */
614 read_number(bool f_optional
, bool f_unsigned
)
616 bool fPositive
= true, fDigits
= false;
624 fPositive
= !isMinus(ch
);
625 if (isSign(ch
)) nextch();
628 while (isdigit(ch
)) {
629 n
= n
* (real
)10.0 + (char)(ch
- '0');
635 real mult
= (real
)1.0;
637 while (isdigit(ch
)) {
639 n
+= (char)(ch
- '0') * mult
;
645 /* !'fRead' => !fDigits so fDigits => 'fRead' */
646 if (fDigits
) return (fPositive
? n
: -n
);
648 /* didn't read a valid number. If it's optional, reset filepos & return */
654 if (isOmit(ch_old
)) {
655 compile_diagnostic(DIAG_ERR
|DIAG_COL
, /*Field may not be omitted*/8);
657 compile_diagnostic_token_show(DIAG_ERR
, /*Expecting numeric field, found “%s”*/9);
659 longjmp(jbSkipLine
, 1);
663 read_quadrant(bool f_optional
)
672 static const sztok pointtab
[] = {
679 static const sztok pointewtab
[] = {
684 if (f_optional
&& isOmit(ch
)) {
690 get_token_legacy_no_blanks();
691 int first_point
= match_tok(pointtab
, TABSIZE(pointtab
));
692 if (first_point
== POINT_NONE
) {
695 compile_diagnostic(DIAG_ERR
|DIAG_COL
, /*Field may not be omitted*/8);
697 compile_diagnostic_token_show(DIAG_ERR
, /*Expecting quadrant bearing, found “%s”*/483);
698 longjmp(jbSkipLine
, 1);
700 real r
= read_number(true, true);
701 if (r
== HUGE_REAL
) {
702 if (isSign(ch
) || isDecimal(ch
)) {
703 /* Give better errors for S-0E, N+10W, N.E, etc. */
705 compile_diagnostic_token_show(DIAG_ERR
, /*Expecting quadrant bearing, found “%s”*/483);
706 longjmp(jbSkipLine
, 1);
709 return first_point
* quad
;
711 if (first_point
== POINT_E
|| first_point
== POINT_W
) {
713 compile_diagnostic_token_show(DIAG_ERR
, /*Expecting quadrant bearing, found “%s”*/483);
714 longjmp(jbSkipLine
, 1);
717 get_token_legacy_no_blanks();
718 int second_point
= match_tok(pointewtab
, TABSIZE(pointewtab
));
719 if (second_point
== POINT_NONE
) {
721 compile_diagnostic_token_show(DIAG_ERR
, /*Expecting quadrant bearing, found “%s”*/483);
722 longjmp(jbSkipLine
, 1);
727 compile_diagnostic_token_show(DIAG_ERR
, /*Suspicious compass reading*/59);
728 longjmp(jbSkipLine
, 1);
731 if (first_point
== POINT_N
) {
732 if (second_point
== POINT_W
) {
736 if (second_point
== POINT_W
) {
746 read_numeric(bool f_optional
)
749 return read_number(f_optional
, false);
753 read_numeric_multi(bool f_optional
, bool f_quadrants
, int *p_n_readings
)
755 size_t n_readings
= 0;
756 real tot
= (real
)0.0;
762 r
= read_number(f_optional
, false);
764 r
= read_quadrant(f_optional
);
766 do_legacy_token_warning();
768 if (p_n_readings
) *p_n_readings
= (r
== HUGE_REAL
? 0 : 1);
776 tot
+= read_number(false, false);
778 tot
+= read_quadrant(false);
779 do_legacy_token_warning();
783 } while (!isClose(ch
));
786 if (p_n_readings
) *p_n_readings
= n_readings
;
787 /* FIXME: special averaging for bearings ... */
788 /* And for percentage gradient */
789 return tot
/ n_readings
;
792 /* read numeric expr or omit (return HUGE_REAL); else longjmp */
794 read_bearing_multi_or_omit(bool f_quadrants
, int *p_n_readings
)
797 v
= read_numeric_multi(true, f_quadrants
, p_n_readings
);
798 if (v
== HUGE_REAL
) {
800 compile_diagnostic_token_show(DIAG_ERR
, /*Expecting numeric field, found “%s”*/9);
801 longjmp(jbSkipLine
, 1);
808 /* Don't skip blanks, variable error code */
810 read_uint_raw(int errmsg
, const filepos
*fp
)
815 compile_diagnostic_token_show(DIAG_ERR
, errmsg
);
816 longjmp(jbSkipLine
, 1);
818 while (isdigit(ch
)) {
819 n
= n
* 10 + (char)(ch
- '0');
829 return read_uint_raw(/*Expecting numeric field, found “%s”*/9, NULL
);
833 read_int(int min_val
, int max_val
)
840 bool negated
= isMinus(ch
);
843 limit
= (unsigned)(min_val
== INT_MIN
? INT_MIN
: -min_val
);
845 limit
= (unsigned)max_val
;
847 if (isSign(ch
)) nextch();
852 /* TRANSLATORS: The first %d will be replaced by the (inclusive) lower
853 * bound and the second by the (inclusive) upper bound, for example:
854 * Expecting integer in range -60 to 60
856 compile_diagnostic(DIAG_ERR
|DIAG_NUM
, /*Expecting integer in range %d to %d*/489);
857 longjmp(jbSkipLine
, 1);
860 while (isdigit(ch
)) {
862 n
= n
* 10 + (char)(ch
- '0');
863 if (n
> limit
|| n
< old_n
) {
868 if (isDecimal(ch
)) goto bad_value
;
871 if (n
> (unsigned)INT_MAX
) {
872 // Avoid unportable casting.
881 read_string(string
*pstr
)
887 /* String quoted in "" */
891 compile_diagnostic(DIAG_ERR
|DIAG_COL
, /*Missing \"*/69);
892 longjmp(jbSkipLine
, 1);
895 if (ch
== '\"') break;
897 s_appendch(pstr
, ch
);
902 /* Unquoted string */
904 if (isEol(ch
) || isComm(ch
)) {
906 compile_diagnostic(DIAG_ERR
|DIAG_COL
, /*Expecting string field*/121);
907 longjmp(jbSkipLine
, 1);
912 if (isBlank(ch
)) break;
914 s_appendch(pstr
, ch
);
921 read_walls_srv_date(int *py
, int *pm
, int *pd
)
927 unsigned y
= read_uint_raw(/*Expecting date, found “%s”*/198, &fp_date
);
929 if (ch
== '-' || ch
== '/') {
935 unsigned m
= read_uint_raw(/*Expecting date, found “%s”*/198, &fp_date
);
936 if (ch
== separator
) {
941 unsigned d
= read_uint_raw(/*Expecting date, found “%s”*/198, &fp_date
);
945 // Walls recommends ISO 8601 date format (yyyy-mm-dd and seemingly the
946 // non-standard variant yyyy/mm/dd), but also accepts "some date formats
947 // common in the U.S. (mm/dd/yy, mm-dd-yyyy, etc.)"
957 // FIXME: Are all 2 digit years 19xx?
963 /* TRANSLATORS: %d will be replaced by the assumed year, e.g. 1918 */
964 compile_diagnostic(DIAG_WARN
|DIAG_UINT
, /*Assuming 2 digit year is %d*/76, y
);
968 if (y
< 1900 || y
> 2078) {
970 compile_diagnostic(DIAG_WARN
|DIAG_UINT
, /*Invalid year (< 1900 or > 2078)*/58);
971 longjmp(jbSkipLine
, 1);
976 if (m
< 1 || m
> 12) {
978 compile_diagnostic(DIAG_WARN
|DIAG_UINT
, /*Invalid month*/86);
979 longjmp(jbSkipLine
, 1);
982 if (d
< 1 || d
> (unsigned)last_day(y
, m
)) {
984 /* TRANSLATORS: e.g. 31st of April, or 32nd of any month */
985 compile_diagnostic(DIAG_WARN
|DIAG_UINT
, /*Invalid day of the month*/87);
986 longjmp(jbSkipLine
, 1);