2 * Routines to read a prefix or number from the current input file
3 * Copyright (C) 1991-2003,2005,2006,2010,2011,2012,2013,2014,2015,2016,2018 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
25 #include <stddef.h> /* for offsetof */
39 # define LONGJMP(JB) longjmp((JB), 1)
41 # define LONGJMP(JB) exit(1)
44 int root_depr_count
= 0;
47 new_anon_station(void)
49 prefix
*name
= osnew(prefix
);
54 name
->up
= pcs
->Prefix
;
56 name
->filename
= file
.filename
;
57 name
->line
= file
.line
;
58 name
->min_export
= name
->max_export
= 0;
59 name
->sflags
= BIT(SFLAGS_ANON
);
60 /* Keep linked list of anon stations for node stats. */
61 name
->right
= anon_list
;
66 /* if prefix is omitted: if PFX_OPT set return NULL, otherwise use longjmp */
68 read_prefix(unsigned pfx_flags
)
70 bool f_optional
= !!(pfx_flags
& PFX_OPT
);
71 bool fSurvey
= !!(pfx_flags
& PFX_SURVEY
);
72 bool fSuspectTypo
= !!(pfx_flags
& PFX_SUSPECT_TYPO
);
73 prefix
*back_ptr
, *ptr
;
78 bool fImplicitPrefix
= fTrue
;
85 if (!(pfx_flags
& PFX_ALLOW_ROOT
)) {
86 compile_diagnostic(DIAG_ERR
|DIAG_COL
, /*ROOT is deprecated*/25);
87 LONGJMP(file
.jbSkipLine
);
89 if (root_depr_count
< 5) {
90 compile_diagnostic(DIAG_WARN
|DIAG_COL
, /*ROOT is deprecated*/25);
91 if (++root_depr_count
== 5)
92 compile_diagnostic(DIAG_WARN
, /*Further uses of this deprecated feature will not be reported*/95);
97 if (!isSep(ch
)) return ptr
;
98 /* Allow optional SEPARATOR after ROOT */
99 get_pos(&fp_firstsep
);
102 fImplicitPrefix
= fFalse
;
107 if ((pfx_flags
& PFX_ANON
) &&
108 (isSep(ch
) || (pcs
->dash_for_anon_wall_station
&& ch
== '-'))) {
113 if (isBlank(ch
) || isEol(ch
)) {
114 if (!isSep(first_ch
))
115 goto anon_wall_station
;
116 /* A single separator alone ('.' by default) is an anonymous
117 * station which is on a point inside the passage and implies
118 * the leg to it is a splay.
120 if (TSTBIT(pcs
->flags
, FLAGS_ANON_ONE_END
)) {
122 compile_diagnostic(DIAG_ERR
|DIAG_TOKEN
, /*Can't have a leg between two anonymous stations*/3);
123 LONGJMP(file
.jbSkipLine
);
125 pcs
->flags
|= BIT(FLAGS_ANON_ONE_END
) | BIT(FLAGS_IMPLICIT_SPLAY
);
126 return new_anon_station();
128 if (isSep(first_ch
) && ch
== first_ch
) {
130 if (isBlank(ch
) || isEol(ch
)) {
131 /* A double separator ('..' by default) is an anonymous station
132 * which is on the wall and implies the leg to it is a splay.
136 if (TSTBIT(pcs
->flags
, FLAGS_ANON_ONE_END
)) {
138 compile_diagnostic(DIAG_ERR
|DIAG_TOKEN
, /*Can't have a leg between two anonymous stations*/3);
139 LONGJMP(file
.jbSkipLine
);
141 pcs
->flags
|= BIT(FLAGS_ANON_ONE_END
) | BIT(FLAGS_IMPLICIT_SPLAY
);
142 pfx
= new_anon_station();
143 pfx
->sflags
|= BIT(SFLAGS_WALL
);
146 if (ch
== first_ch
) {
148 if (isBlank(ch
) || isEol(ch
)) {
149 /* A triple separator ('...' by default) is an anonymous
150 * station, but otherwise not handled specially (e.g. for
151 * a single leg down an unexplored side passage to a station
152 * which isn't refindable).
154 if (TSTBIT(pcs
->flags
, FLAGS_ANON_ONE_END
)) {
156 compile_diagnostic(DIAG_ERR
|DIAG_TOKEN
, /*Can't have a leg between two anonymous stations*/3);
157 LONGJMP(file
.jbSkipLine
);
159 pcs
->flags
|= BIT(FLAGS_ANON_ONE_END
);
160 return new_anon_station();
174 /* Need a new name buffer */
175 name
= osmalloc(name_len
);
177 /* i==0 iff this is the first pass */
182 while (isNames(ch
)) {
183 if (i
< pcs
->Truncate
) {
185 name
[i
++] = (pcs
->Case
== LOWER
? tolower(ch
) :
186 (pcs
->Case
== OFF
? ch
: toupper(ch
)));
188 name_len
= name_len
+ name_len
;
189 name
= osrealloc(name
, name_len
);
195 fImplicitPrefix
= fFalse
;
196 get_pos(&fp_firstsep
);
203 compile_diagnostic(DIAG_ERR
|DIAG_COL
, /*Expecting survey name*/89);
205 compile_diagnostic(DIAG_ERR
|DIAG_COL
, /*Expecting station name*/28);
208 /* TRANSLATORS: Here "station" is a survey station, not a train station. */
209 compile_diagnostic(DIAG_ERR
|DIAG_COL
, /*Character “%c” not allowed in station name (use *SET NAMES to set allowed characters)*/7, ch
);
211 LONGJMP(file
.jbSkipLine
);
213 return (prefix
*)NULL
;
221 /* Special case first time around at each level */
222 name
= osrealloc(name
, i
);
226 ptr
->right
= ptr
->down
= NULL
;
231 ptr
->filename
= file
.filename
;
232 ptr
->line
= file
.line
;
233 ptr
->min_export
= ptr
->max_export
= 0;
234 ptr
->sflags
= BIT(SFLAGS_SURVEY
);
235 if (fSuspectTypo
&& !fImplicitPrefix
)
236 ptr
->sflags
|= BIT(SFLAGS_SUSPECTTYPO
);
237 back_ptr
->down
= ptr
;
240 /* Use caching to speed up adding an increasing sequence to a
242 static prefix
*cached_survey
= NULL
, *cached_station
= NULL
;
243 prefix
*ptrPrev
= NULL
;
244 int cmp
= 1; /* result of strcmp ( -ve for <, 0 for =, +ve for > ) */
245 if (cached_survey
== back_ptr
) {
246 cmp
= strcmp(cached_station
->ident
, name
);
247 if (cmp
<= 0) ptr
= cached_station
;
249 while (ptr
&& (cmp
= strcmp(ptr
->ident
, name
))<0) {
254 /* ie we got to one that was higher, or the end */
256 name
= osrealloc(name
, i
);
257 newptr
= osnew(prefix
);
258 newptr
->ident
= name
;
261 back_ptr
->down
= newptr
;
263 ptrPrev
->right
= newptr
;
269 newptr
->up
= back_ptr
;
270 newptr
->filename
= file
.filename
;
271 newptr
->line
= file
.line
;
272 newptr
->min_export
= newptr
->max_export
= 0;
273 newptr
->sflags
= BIT(SFLAGS_SURVEY
);
274 if (fSuspectTypo
&& !fImplicitPrefix
)
275 newptr
->sflags
|= BIT(SFLAGS_SUSPECTTYPO
);
279 cached_survey
= back_ptr
;
280 cached_station
= ptr
;
283 f_optional
= fFalse
; /* disallow after first level */
284 if (isSep(ch
)) get_pos(&fp_firstsep
);
286 if (name
) osfree(name
);
288 /* don't warn about a station that is referred to twice */
289 if (!fNew
) ptr
->sflags
&= ~BIT(SFLAGS_SUSPECTTYPO
);
292 /* fNew means SFLAGS_SURVEY is currently set */
293 SVX_ASSERT(TSTBIT(ptr
->sflags
, SFLAGS_SURVEY
));
295 ptr
->sflags
&= ~BIT(SFLAGS_SURVEY
);
296 if (TSTBIT(pcs
->infer
, INFER_EXPORTS
)) ptr
->min_export
= USHRT_MAX
;
299 /* check that the same name isn't being used for a survey and station */
300 if (fSurvey
^ TSTBIT(ptr
->sflags
, SFLAGS_SURVEY
)) {
301 /* TRANSLATORS: Here "station" is a survey station, not a train station.
303 * Here "survey" is a "cave map" rather than list of questions - it should be
304 * translated to the terminology that cavers using the language would use.
306 compile_diagnostic(DIAG_ERR
, /*“%s” can’t be both a station and a survey*/27,
309 if (!fSurvey
&& TSTBIT(pcs
->infer
, INFER_EXPORTS
)) ptr
->min_export
= USHRT_MAX
;
312 /* check the export level */
314 printf("R min %d max %d depth %d pfx %s\n",
315 ptr
->min_export
, ptr
->max_export
, depth
, sprint_prefix(ptr
));
317 if (ptr
->min_export
== 0 || ptr
->min_export
== USHRT_MAX
) {
318 if (depth
> ptr
->max_export
) ptr
->max_export
= depth
;
319 } else if (ptr
->max_export
< depth
) {
320 prefix
*survey
= ptr
;
324 for (level
= ptr
->max_export
+ 1; level
; level
--) {
328 s
= osstrdup(sprint_prefix(survey
));
329 p
= sprint_prefix(ptr
);
330 if (survey
->filename
) {
331 compile_diagnostic_pfx(DIAG_ERR
, survey
,
332 /*Station “%s” not exported from survey “%s”*/26,
335 compile_diagnostic(DIAG_ERR
, /*Station “%s” not exported from survey “%s”*/26, p
, s
);
339 printf(" *** pfx %s warning not exported enough depth %d "
340 "ptr->max_export %d\n", sprint_prefix(ptr
),
341 depth
, ptr
->max_export
);
344 if (!fImplicitPrefix
&& (pfx_flags
& PFX_WARN_SEPARATOR
)) {
347 set_pos(&fp_firstsep
);
348 compile_diagnostic(DIAG_WARN
|DIAG_COL
, /*Separator in survey name*/392);
354 /* if numeric expr is omitted: if f_optional return HUGE_REAL, else longjmp */
356 read_number(bool f_optional
)
358 bool fPositive
, fDigits
= fFalse
;
365 fPositive
= !isMinus(ch
);
366 if (isSign(ch
)) nextch();
368 while (isdigit(ch
)) {
369 n
= n
* (real
)10.0 + (char)(ch
- '0');
375 real mult
= (real
)1.0;
377 while (isdigit(ch
)) {
379 n
+= (char)(ch
- '0') * mult
;
385 /* !'fRead' => !fDigits so fDigits => 'fRead' */
386 if (fDigits
) return (fPositive
? n
: -n
);
388 /* didn't read a valid number. If it's optional, reset filepos & return */
394 if (isOmit(ch_old
)) {
395 compile_diagnostic(DIAG_ERR
|DIAG_COL
, /*Field may not be omitted*/8);
397 compile_diagnostic_token_show(DIAG_ERR
, /*Expecting numeric field, found “%s”*/9);
399 LONGJMP(file
.jbSkipLine
);
400 return 0.0; /* for brain-fried compilers */
404 read_numeric(bool f_optional
)
407 return read_number(f_optional
);
411 read_numeric_multi(bool f_optional
, int *p_n_readings
)
413 size_t n_readings
= 0;
414 real tot
= (real
)0.0;
418 real r
= read_number(f_optional
);
419 if (p_n_readings
) *p_n_readings
= (r
== HUGE_REAL
? 0 : 1);
426 tot
+= read_number(fFalse
);
429 } while (!isClose(ch
));
432 if (p_n_readings
) *p_n_readings
= n_readings
;
433 /* FIXME: special averaging for bearings ... */
434 /* And for percentage gradient */
435 return tot
/ n_readings
;
438 /* read numeric expr or omit (return HUGE_REAL); else longjmp */
440 read_numeric_multi_or_omit(int *p_n_readings
)
442 real v
= read_numeric_multi(fTrue
, p_n_readings
);
443 if (v
== HUGE_REAL
) {
445 compile_diagnostic_token_show(DIAG_ERR
, /*Expecting numeric field, found “%s”*/9);
446 LONGJMP(file
.jbSkipLine
);
447 return 0.0; /* for brain-fried compilers */
454 /* Don't skip blanks, variable error code */
456 read_uint_internal(int errmsg
, const filepos
*fp
)
461 compile_diagnostic_token_show(DIAG_ERR
, errmsg
);
462 LONGJMP(file
.jbSkipLine
);
464 while (isdigit(ch
)) {
465 n
= n
* 10 + (char)(ch
- '0');
475 return read_uint_internal(/*Expecting numeric field, found “%s”*/9, NULL
);
479 read_string(char **pstr
, int *plen
)
485 /* String quoted in "" */
489 compile_diagnostic(DIAG_ERR
|DIAG_COL
, /*Missing \"*/69);
490 LONGJMP(file
.jbSkipLine
);
493 if (ch
== '\"') break;
495 s_catchar(pstr
, plen
, ch
);
500 /* Unquoted string */
502 if (isEol(ch
) || isComm(ch
)) {
503 if (!*pstr
|| !(*pstr
)[0]) {
504 compile_diagnostic(DIAG_ERR
|DIAG_COL
, /*Expecting string field*/121);
505 LONGJMP(file
.jbSkipLine
);
510 if (isBlank(ch
)) break;
512 s_catchar(pstr
, plen
, ch
);
519 read_date(int *py
, int *pm
, int *pd
)
521 unsigned int y
= 0, m
= 0, d
= 0;
527 y
= read_uint_internal(/*Expecting date, found “%s”*/198, &fp_date
);
528 /* Two digit year is 19xx. */
534 /* TRANSLATORS: %d will be replaced by the assumed year, e.g. 1918 */
535 compile_diagnostic(DIAG_WARN
|DIAG_UINT
, /*Assuming 2 digit year is %d*/76, y
);
538 if (y
< 1900 || y
> 2078) {
540 compile_diagnostic(DIAG_WARN
|DIAG_UINT
, /*Invalid year (< 1900 or > 2078)*/58);
541 LONGJMP(file
.jbSkipLine
);
542 return; /* for brain-fried compilers */
548 m
= read_uint_internal(/*Expecting date, found “%s”*/198, &fp_date
);
549 if (m
< 1 || m
> 12) {
551 compile_diagnostic(DIAG_WARN
|DIAG_UINT
, /*Invalid month*/86);
552 LONGJMP(file
.jbSkipLine
);
553 return; /* for brain-fried compilers */
558 d
= read_uint_internal(/*Expecting date, found “%s”*/198, &fp_date
);
559 if (d
< 1 || d
> last_day(y
, m
)) {
561 /* TRANSLATORS: e.g. 31st of April, or 32nd of any month */
562 compile_diagnostic(DIAG_WARN
|DIAG_UINT
, /*Invalid day of the month*/87);
563 LONGJMP(file
.jbSkipLine
);
564 return; /* for brain-fried compilers */