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 */
38 # define LONGJMP(JB) longjmp((JB), 1)
40 # define LONGJMP(JB) exit(1)
43 int root_depr_count
= 0;
46 new_anon_station(void)
48 prefix
*name
= osnew(prefix
);
53 name
->up
= pcs
->Prefix
;
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
;
65 /* if prefix is omitted: if PFX_OPT set return NULL, otherwise use longjmp */
67 read_prefix(unsigned pfx_flags
)
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
;
77 bool fImplicitPrefix
= true;
86 if (!(pfx_flags
& PFX_ALLOW_ROOT
)) {
87 compile_diagnostic(DIAG_ERR
|DIAG_COL
, /*ROOT is deprecated*/25);
88 LONGJMP(file
.jbSkipLine
);
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);
98 if (!isSep(ch
)) return ptr
;
99 /* Allow optional SEPARATOR after ROOT */
100 get_pos(&fp_firstsep
);
103 fImplicitPrefix
= false;
108 if ((pfx_flags
& PFX_ANON
) &&
109 (isSep(ch
) || (pcs
->dash_for_anon_wall_station
&& ch
== '-'))) {
112 if (isBlank(ch
) || isComm(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.
119 if (TSTBIT(pcs
->flags
, FLAGS_ANON_ONE_END
)) {
121 compile_diagnostic(DIAG_ERR
|DIAG_WORD
, /*Can't have a leg between two anonymous stations*/3);
122 LONGJMP(file
.jbSkipLine
);
124 pcs
->flags
|= BIT(FLAGS_ANON_ONE_END
) | BIT(FLAGS_IMPLICIT_SPLAY
);
125 return new_anon_station();
127 if (isSep(first_ch
) && ch
== first_ch
) {
129 if (isBlank(ch
) || isComm(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.
135 if (TSTBIT(pcs
->flags
, FLAGS_ANON_ONE_END
)) {
137 compile_diagnostic(DIAG_ERR
|DIAG_WORD
, /*Can't have a leg between two anonymous stations*/3);
138 LONGJMP(file
.jbSkipLine
);
140 pcs
->flags
|= BIT(FLAGS_ANON_ONE_END
) | BIT(FLAGS_IMPLICIT_SPLAY
);
141 pfx
= new_anon_station();
142 pfx
->sflags
|= BIT(SFLAGS_WALL
);
145 if (ch
== first_ch
) {
147 if (isBlank(ch
) || isComm(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).
153 if (TSTBIT(pcs
->flags
, FLAGS_ANON_ONE_END
)) {
155 compile_diagnostic(DIAG_ERR
|DIAG_WORD
, /*Can't have a leg between two anonymous stations*/3);
156 LONGJMP(file
.jbSkipLine
);
158 pcs
->flags
|= BIT(FLAGS_ANON_ONE_END
);
159 return new_anon_station();
173 /* Need a new name buffer */
174 name
= osmalloc(name_len
);
176 /* i==0 iff this is the first pass */
181 while (isNames(ch
)) {
182 if (i
< pcs
->Truncate
) {
184 name
[i
++] = (pcs
->Case
== LOWER
? tolower(ch
) :
185 (pcs
->Case
== OFF
? ch
: toupper(ch
)));
187 name_len
= name_len
+ name_len
;
188 name
= osrealloc(name
, name_len
);
194 fImplicitPrefix
= false;
195 get_pos(&fp_firstsep
);
202 compile_diagnostic(DIAG_ERR
|DIAG_COL
, /*Expecting survey name*/89);
204 compile_diagnostic(DIAG_ERR
|DIAG_COL
, /*Expecting station name*/28);
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
);
210 LONGJMP(file
.jbSkipLine
);
212 return (prefix
*)NULL
;
220 /* Special case first time around at each level */
221 name
= osrealloc(name
, i
);
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 ptr
->sflags
= BIT(SFLAGS_SURVEY
);
234 if (fSuspectTypo
&& !fImplicitPrefix
)
235 ptr
->sflags
|= BIT(SFLAGS_SUSPECTTYPO
);
236 back_ptr
->down
= ptr
;
239 /* Use caching to speed up adding an increasing sequence to a
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
;
248 while (ptr
&& (cmp
= strcmp(ptr
->ident
, name
)) < 0) {
253 /* ie we got to one that was higher, or the end */
255 name
= osrealloc(name
, i
);
256 newptr
= osnew(prefix
);
257 newptr
->ident
= name
;
260 back_ptr
->down
= newptr
;
262 ptrPrev
->right
= newptr
;
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
);
278 cached_survey
= back_ptr
;
279 cached_station
= ptr
;
282 f_optional
= false; /* disallow after first level */
284 get_pos(&fp_firstsep
);
285 if (!TSTBIT(ptr
->sflags
, SFLAGS_SURVEY
)) {
286 /* TRANSLATORS: Here "station" is a survey station, not a train station.
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.
291 compile_diagnostic(DIAG_ERR
|DIAG_FROM(here
), /*“%s” can’t be both a station and a survey*/27,
296 if (name
) osfree(name
);
298 /* don't warn about a station that is referred to twice */
299 if (!fNew
) ptr
->sflags
&= ~BIT(SFLAGS_SUSPECTTYPO
);
302 /* fNew means SFLAGS_SURVEY is currently set */
303 SVX_ASSERT(TSTBIT(ptr
->sflags
, SFLAGS_SURVEY
));
305 ptr
->sflags
&= ~BIT(SFLAGS_SURVEY
);
306 if (TSTBIT(pcs
->infer
, INFER_EXPORTS
)) ptr
->min_export
= USHRT_MAX
;
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.
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.
316 compile_diagnostic(DIAG_ERR
|DIAG_FROM(here
), /*“%s” can’t be both a station and a survey*/27,
319 if (!fSurvey
&& TSTBIT(pcs
->infer
, INFER_EXPORTS
)) ptr
->min_export
= USHRT_MAX
;
322 /* check the export level */
324 printf("R min %d max %d depth %d pfx %s\n",
325 ptr
->min_export
, ptr
->max_export
, depth
, sprint_prefix(ptr
));
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
;
334 for (level
= ptr
->max_export
+ 1; level
; level
--) {
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,
345 compile_diagnostic(DIAG_ERR
, /*Station “%s” not exported from survey “%s”*/26, p
, s
);
349 printf(" *** pfx %s warning not exported enough depth %d "
350 "ptr->max_export %d\n", sprint_prefix(ptr
),
351 depth
, ptr
->max_export
);
354 if (!fImplicitPrefix
&& (pfx_flags
& PFX_WARN_SEPARATOR
)) {
357 set_pos(&fp_firstsep
);
358 compile_diagnostic(DIAG_WARN
|DIAG_COL
, /*Separator in survey name*/392);
365 read_walls_prefix(void)
367 string name
= S_INIT
;
372 s_catchar(&name
, ch
);
374 } while (isNames(ch
));
375 return s_steal(&name
);
379 read_walls_station(char * const walls_prefix
[3], bool anon_allowed
)
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
;
387 // bool fImplicitPrefix = true;
389 // filepos fp_firstsep;
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?
400 // Not yet checked, but you can presumably use - and -- as a prefix
401 // (FIXME check this).
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
)) {
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
);
416 pcs
->flags
|= BIT(FLAGS_ANON_ONE_END
) | BIT(FLAGS_IMPLICIT_SPLAY
);
417 prefix
*pfx
= new_anon_station();
418 pfx
->sflags
|= BIT(SFLAGS_WALL
);
421 s_catn(&component
, dashes
, '-');
424 char *w_prefix
[3] = { NULL
, NULL
, NULL
};
425 int explicit_prefix_levels
= 0;
427 while (isNames(ch
)) {
428 s_catchar(&component
, ch
);
431 //printf("component = '%s'\n", s_str(&component));
435 if (++explicit_prefix_levels
> 3) {
436 // FIXME Make this a proper error
437 printf("too many prefix levels\n");
439 for (int i
= 0; i
< 3; ++i
) osfree(w_prefix
[i
]);
440 LONGJMP(file
.jbSkipLine
);
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
);
451 // printf("explicit_prefix_levels=%d %s:%s:%s\n", explicit_prefix_levels, w_prefix[0], w_prefix[1], w_prefix[2]);
453 // component is the station name itself.
454 if (s_empty(&component
)) {
455 if (explicit_prefix_levels
== 0) {
456 compile_diagnostic(DIAG_ERR
|DIAG_COL
, /*Expecting station name*/28);
458 for (int i
= 0; i
< 3; ++i
) osfree(w_prefix
[i
]);
459 LONGJMP(file
.jbSkipLine
);
461 // Walls allows an empty station name if there's an explicit prefix.
462 // This seems unlikely to be intended, so warn about it.
463 compile_diagnostic(DIAG_WARN
|DIAG_COL
, /*Expecting station name*/28);
464 // Use a name with a space in so it can't collide with a real
465 // Walls station name.
466 s_cat(&component
, "empty name");
468 int len
= s_len(&component
);
469 char *p
= s_steal(&component
);
470 // Apply case treatment.
473 for (int i
= 0; i
< len
; ++i
)
474 p
[i
] = tolower((unsigned char)p
[i
]);
477 for (int i
= 0; i
< len
; ++i
)
478 p
[i
] = toupper((unsigned char)p
[i
]);
481 // Avoid unhandled enum warning.
486 for (int i
= 0; i
< 4; ++i
) {
488 int sflag
= BIT(SFLAGS_SURVEY
);
491 // Mark stations used with explicit prefix as exported.
492 sflag
= explicit_prefix_levels
? BIT(SFLAGS_EXPORTED
) : 0;
494 if (i
< 3 - explicit_prefix_levels
) {
495 name
= walls_prefix
[i
];
496 // printf("using walls_prefix[%d] = '%s'\n", 2 - i, name);
498 name
= w_prefix
[i
- (3 - explicit_prefix_levels
)]; // FIXME: Could steal wprefix[i].
499 // printf("using w_prefix[%d] = '%s'\n", i - (3 - explicit_prefix_levels), name);
503 // FIXME: This means :X::Y is treated as the same as
504 // ::X:Y but is that right? Walls docs don't really
505 // say. Need to test (and is they're different then
506 // probably use a character not valid in Walls station
507 // names for the empty prefix level (e.g. space or
510 // Also, does Walls allow :::X as a station and
511 // ::X:Y which would mean X is a station and survey?
512 // If so, we probably want to keep every empty level.
516 prefix
*back_ptr
= ptr
;
519 /* Special case first time around at each level */
521 ptr
->ident
= (i
< 3 ? osstrdup(name
) : name
);
523 ptr
->right
= ptr
->down
= NULL
;
528 ptr
->filename
= file
.filename
; // FIXME: Or location of #Prefix, etc for it?
529 ptr
->line
= file
.line
; // FIXME: Or location of #Prefix, etc for it?
530 ptr
->min_export
= ptr
->max_export
= 0;
532 back_ptr
->down
= ptr
;
534 /* Use caching to speed up adding an increasing sequence to a
536 static prefix
*cached_survey
= NULL
, *cached_station
= NULL
;
537 prefix
*ptrPrev
= NULL
;
538 int cmp
= 1; /* result of strcmp ( -ve for <, 0 for =, +ve for > ) */
539 if (cached_survey
== back_ptr
) {
540 cmp
= strcmp(cached_station
->ident
, name
);
541 if (cmp
<= 0) ptr
= cached_station
;
543 while (ptr
&& (cmp
= strcmp(ptr
->ident
, name
))<0) {
548 /* ie we got to one that was higher, or the end */
550 newptr
= osnew(prefix
);
551 newptr
->ident
= (i
< 3 ? osstrdup(name
) : name
);
554 back_ptr
->down
= newptr
;
556 ptrPrev
->right
= newptr
;
562 newptr
->up
= back_ptr
;
563 newptr
->filename
= file
.filename
; // FIXME
564 newptr
->line
= file
.line
;
565 newptr
->min_export
= newptr
->max_export
= 0;
566 newptr
->sflags
= sflag
;
569 ptr
->sflags
|= sflag
;
571 cached_survey
= back_ptr
;
572 cached_station
= ptr
;
574 if (name
== p
) osfree(p
);
577 // fprint_prefix(stdout, ptr); fputnl(stdout);
579 for (int i
= 0; i
< 3; ++i
) osfree(w_prefix
[i
]);
585 /* if numeric expr is omitted: if f_optional return HUGE_REAL, else longjmp */
587 read_number(bool f_optional
, bool f_unsigned
)
589 bool fPositive
= true, fDigits
= false;
597 fPositive
= !isMinus(ch
);
598 if (isSign(ch
)) nextch();
601 while (isdigit(ch
)) {
602 n
= n
* (real
)10.0 + (char)(ch
- '0');
608 real mult
= (real
)1.0;
610 while (isdigit(ch
)) {
612 n
+= (char)(ch
- '0') * mult
;
618 /* !'fRead' => !fDigits so fDigits => 'fRead' */
619 if (fDigits
) return (fPositive
? n
: -n
);
621 /* didn't read a valid number. If it's optional, reset filepos & return */
627 if (isOmit(ch_old
)) {
628 compile_diagnostic(DIAG_ERR
|DIAG_COL
, /*Field may not be omitted*/8);
630 compile_diagnostic_token_show(DIAG_ERR
, /*Expecting numeric field, found “%s”*/9);
632 LONGJMP(file
.jbSkipLine
);
633 return 0.0; /* for brain-fried compilers */
637 read_quadrant(bool f_optional
)
646 static const sztok pointtab
[] = {
653 static const sztok pointewtab
[] = {
658 if (f_optional
&& isOmit(ch
)) {
664 get_token_no_blanks();
665 int first_point
= match_tok(pointtab
, TABSIZE(pointtab
));
666 if (first_point
== POINT_NONE
) {
669 compile_diagnostic(DIAG_ERR
|DIAG_COL
, /*Field may not be omitted*/8);
671 compile_diagnostic_token_show(DIAG_ERR
, /*Expecting quadrant bearing, found “%s”*/483);
672 LONGJMP(file
.jbSkipLine
);
673 return 0.0; /* for brain-fried compilers */
675 real r
= read_number(true, true);
676 if (r
== HUGE_REAL
) {
677 if (isSign(ch
) || isDecimal(ch
)) {
678 /* Give better errors for S-0E, N+10W, N.E, etc. */
680 compile_diagnostic_token_show(DIAG_ERR
, /*Expecting quadrant bearing, found “%s”*/483);
681 LONGJMP(file
.jbSkipLine
);
682 return 0.0; /* for brain-fried compilers */
685 return first_point
* quad
;
687 if (first_point
== POINT_E
|| first_point
== POINT_W
) {
689 compile_diagnostic_token_show(DIAG_ERR
, /*Expecting quadrant bearing, found “%s”*/483);
690 LONGJMP(file
.jbSkipLine
);
691 return 0.0; /* for brain-fried compilers */
694 get_token_no_blanks();
695 int second_point
= match_tok(pointewtab
, TABSIZE(pointewtab
));
696 if (second_point
== POINT_NONE
) {
698 compile_diagnostic_token_show(DIAG_ERR
, /*Expecting quadrant bearing, found “%s”*/483);
699 LONGJMP(file
.jbSkipLine
);
700 return 0.0; /* for brain-fried compilers */
705 compile_diagnostic_token_show(DIAG_ERR
|DIAG_COL
, /*Suspicious compass reading*/59);
706 LONGJMP(file
.jbSkipLine
);
707 return 0.0; /* for brain-fried compilers */
710 if (first_point
== POINT_N
) {
711 if (second_point
== POINT_W
) {
715 if (second_point
== POINT_W
) {
725 read_numeric(bool f_optional
)
728 return read_number(f_optional
, false);
732 read_numeric_multi(bool f_optional
, bool f_quadrants
, int *p_n_readings
)
734 size_t n_readings
= 0;
735 real tot
= (real
)0.0;
741 r
= read_number(f_optional
, false);
743 r
= read_quadrant(f_optional
);
744 if (p_n_readings
) *p_n_readings
= (r
== HUGE_REAL
? 0 : 1);
752 tot
+= read_number(false, false);
754 tot
+= read_quadrant(false);
757 } while (!isClose(ch
));
760 if (p_n_readings
) *p_n_readings
= n_readings
;
761 /* FIXME: special averaging for bearings ... */
762 /* And for percentage gradient */
763 return tot
/ n_readings
;
766 /* read numeric expr or omit (return HUGE_REAL); else longjmp */
768 read_bearing_multi_or_omit(bool f_quadrants
, int *p_n_readings
)
771 v
= read_numeric_multi(true, f_quadrants
, p_n_readings
);
772 if (v
== HUGE_REAL
) {
774 compile_diagnostic_token_show(DIAG_ERR
, /*Expecting numeric field, found “%s”*/9);
775 LONGJMP(file
.jbSkipLine
);
776 return 0.0; /* for brain-fried compilers */
783 /* Don't skip blanks, variable error code */
785 read_uint_internal(int errmsg
, const filepos
*fp
)
790 compile_diagnostic_token_show(DIAG_ERR
, errmsg
);
791 LONGJMP(file
.jbSkipLine
);
793 while (isdigit(ch
)) {
794 n
= n
* 10 + (char)(ch
- '0');
804 return read_uint_internal(/*Expecting numeric field, found “%s”*/9, NULL
);
808 read_int(int min_val
, int max_val
)
815 bool negated
= isMinus(ch
);
818 limit
= (unsigned)(min_val
== INT_MIN
? INT_MIN
: -min_val
);
820 limit
= (unsigned)max_val
;
822 if (isSign(ch
)) nextch();
827 /* TRANSLATORS: The first %d will be replaced by the (inclusive) lower
828 * bound and the second by the (inclusive) upper bound, for example:
829 * Expecting integer in range -60 to 60
831 compile_diagnostic(DIAG_ERR
|DIAG_NUM
, /*Expecting integer in range %d to %d*/489);
832 LONGJMP(file
.jbSkipLine
);
835 while (isdigit(ch
)) {
837 n
= n
* 10 + (char)(ch
- '0');
838 if (n
> limit
|| n
< old_n
) {
843 if (isDecimal(ch
)) goto bad_value
;
846 if (n
> (unsigned)INT_MAX
) {
847 // Avoid unportable casting.
856 read_string(string
*pstr
)
862 /* String quoted in "" */
866 compile_diagnostic(DIAG_ERR
|DIAG_COL
, /*Missing \"*/69);
867 LONGJMP(file
.jbSkipLine
);
870 if (ch
== '\"') break;
877 /* Unquoted string */
879 if (isEol(ch
) || isComm(ch
)) {
881 compile_diagnostic(DIAG_ERR
|DIAG_COL
, /*Expecting string field*/121);
882 LONGJMP(file
.jbSkipLine
);
887 if (isBlank(ch
)) break;
896 read_date(int *py
, int *pm
, int *pd
)
898 unsigned int y
= 0, m
= 0, d
= 0;
904 y
= read_uint_internal(/*Expecting date, found “%s”*/198, &fp_date
);
905 /* Two digit year is 19xx. */
911 /* TRANSLATORS: %d will be replaced by the assumed year, e.g. 1918 */
912 compile_diagnostic(DIAG_WARN
|DIAG_UINT
, /*Assuming 2 digit year is %d*/76, y
);
915 if (y
< 1900 || y
> 2078) {
917 compile_diagnostic(DIAG_WARN
|DIAG_UINT
, /*Invalid year (< 1900 or > 2078)*/58);
918 LONGJMP(file
.jbSkipLine
);
919 return; /* for brain-fried compilers */
925 m
= read_uint_internal(/*Expecting date, found “%s”*/198, &fp_date
);
926 if (m
< 1 || m
> 12) {
928 compile_diagnostic(DIAG_WARN
|DIAG_UINT
, /*Invalid month*/86);
929 LONGJMP(file
.jbSkipLine
);
930 return; /* for brain-fried compilers */
935 d
= read_uint_internal(/*Expecting date, found “%s”*/198, &fp_date
);
936 if (d
< 1 || d
> last_day(y
, m
)) {
938 /* TRANSLATORS: e.g. 31st of April, or 32nd of any month */
939 compile_diagnostic(DIAG_WARN
|DIAG_UINT
, /*Invalid day of the month*/87);
940 LONGJMP(file
.jbSkipLine
);
941 return; /* for brain-fried compilers */
951 read_walls_srv_date(int *py
, int *pm
, int *pd
)
957 unsigned y
= read_uint_internal(/*Expecting date, found “%s”*/198, &fp_date
);
959 if (ch
== '-' || ch
== '/') {
965 unsigned m
= read_uint_internal(/*Expecting date, found “%s”*/198, &fp_date
);
966 if (ch
== separator
) {
971 unsigned d
= read_uint_internal(/*Expecting date, found “%s”*/198, &fp_date
);
975 // Walls recommends ISO 8601 date format (yyyy-mm-dd and seemingly the
976 // non-standard variant yyyy/mm/dd), but also accepts "some date formats
977 // common in the U.S. (mm/dd/yy, mm-dd-yyyy, etc.)"
987 // FIXME: Are all 2 digit years 19xx?
993 /* TRANSLATORS: %d will be replaced by the assumed year, e.g. 1918 */
994 compile_diagnostic(DIAG_WARN
|DIAG_UINT
, /*Assuming 2 digit year is %d*/76, y
);
998 if (y
< 1900 || y
> 2078) {
1000 compile_diagnostic(DIAG_WARN
|DIAG_UINT
, /*Invalid year (< 1900 or > 2078)*/58);
1001 LONGJMP(file
.jbSkipLine
);
1002 return; /* for brain-fried compilers */
1007 if (m
< 1 || m
> 12) {
1009 compile_diagnostic(DIAG_WARN
|DIAG_UINT
, /*Invalid month*/86);
1010 LONGJMP(file
.jbSkipLine
);
1011 return; /* for brain-fried compilers */
1014 if (d
< 1 || d
> last_day(y
, m
)) {
1016 /* TRANSLATORS: e.g. 31st of April, or 32nd of any month */
1017 compile_diagnostic(DIAG_WARN
|DIAG_UINT
, /*Invalid day of the month*/87);
1018 LONGJMP(file
.jbSkipLine
);
1019 return; /* for brain-fried compilers */