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 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
;
84 if (!(pfx_flags
& PFX_ALLOW_ROOT
)) {
85 compile_error(-/*ROOT is deprecated*/25);
86 LONGJMP(file
.jbSkipLine
);
88 if (root_depr_count
< 5) {
89 compile_warning(-/*ROOT is deprecated*/25);
90 if (++root_depr_count
== 5)
91 compile_warning(/*Further uses of this deprecated feature will not be reported*/95);
96 if (!isSep(ch
)) return ptr
;
97 /* Allow optional SEPARATOR after ROOT */
100 fImplicitPrefix
= fFalse
;
105 if ((pfx_flags
& PFX_ANON
) &&
106 (isSep(ch
) || (pcs
->dash_for_anon_wall_station
&& ch
== '-'))) {
111 if (isBlank(ch
) || isEol(ch
)) {
112 if (!isSep(first_ch
))
113 goto anon_wall_station
;
114 /* A single separator alone ('.' by default) is an anonymous
115 * station which is on a point inside the passage and implies
116 * the leg to it is a splay.
118 if (TSTBIT(pcs
->flags
, FLAGS_ANON_ONE_END
)) {
119 compile_error(-/*Can't have a leg between two anonymous stations*/3);
120 LONGJMP(file
.jbSkipLine
);
122 pcs
->flags
|= BIT(FLAGS_ANON_ONE_END
) | BIT(FLAGS_IMPLICIT_SPLAY
);
123 return new_anon_station();
125 if (isSep(first_ch
) && ch
== first_ch
) {
127 if (isBlank(ch
) || isEol(ch
)) {
128 /* A double separator ('..' by default) is an anonymous station
129 * which is on the wall and implies the leg to it is a splay.
133 if (TSTBIT(pcs
->flags
, FLAGS_ANON_ONE_END
)) {
134 compile_error(-/*Can't have a leg between two anonymous stations*/3);
135 LONGJMP(file
.jbSkipLine
);
137 pcs
->flags
|= BIT(FLAGS_ANON_ONE_END
) | BIT(FLAGS_IMPLICIT_SPLAY
);
138 pfx
= new_anon_station();
139 pfx
->sflags
|= BIT(SFLAGS_WALL
);
142 if (ch
== first_ch
) {
144 if (isBlank(ch
) || isEol(ch
)) {
145 /* A triple separator ('...' by default) is an anonymous
146 * station, but otherwise not handled specially (e.g. for
147 * a single leg down an unexplored side passage to a station
148 * which isn't refindable).
150 if (TSTBIT(pcs
->flags
, FLAGS_ANON_ONE_END
)) {
151 compile_error(-/*Can't have a leg between two anonymous stations*/3);
152 LONGJMP(file
.jbSkipLine
);
154 pcs
->flags
|= BIT(FLAGS_ANON_ONE_END
);
155 return new_anon_station();
169 /* Need a new name buffer */
170 name
= osmalloc(name_len
);
172 /* i==0 iff this is the first pass */
177 while (isNames(ch
)) {
178 if (i
< pcs
->Truncate
) {
180 name
[i
++] = (pcs
->Case
== LOWER
? tolower(ch
) :
181 (pcs
->Case
== OFF
? ch
: toupper(ch
)));
183 name_len
= name_len
+ name_len
;
184 name
= osrealloc(name
, name_len
);
189 if (isSep(ch
)) fImplicitPrefix
= fFalse
;
195 compile_error(-/*Expecting survey name*/89);
197 compile_error(-/*Expecting station name*/28);
200 /* TRANSLATORS: Here "station" is a survey station, not a train station. */
201 compile_error(-/*Character “%c” not allowed in station name (use *SET NAMES to set allowed characters)*/7, ch
);
203 LONGJMP(file
.jbSkipLine
);
205 return (prefix
*)NULL
;
213 /* Special case first time around at each level */
214 name
= osrealloc(name
, i
);
218 ptr
->right
= ptr
->down
= NULL
;
223 ptr
->filename
= file
.filename
;
224 ptr
->line
= file
.line
;
225 ptr
->min_export
= ptr
->max_export
= 0;
226 ptr
->sflags
= BIT(SFLAGS_SURVEY
);
227 if (fSuspectTypo
&& !fImplicitPrefix
)
228 ptr
->sflags
|= BIT(SFLAGS_SUSPECTTYPO
);
229 back_ptr
->down
= ptr
;
232 /* Use caching to speed up adding an increasing sequence to a
234 static prefix
*cached_survey
= NULL
, *cached_station
= NULL
;
235 prefix
*ptrPrev
= NULL
;
236 int cmp
= 1; /* result of strcmp ( -ve for <, 0 for =, +ve for > ) */
237 if (cached_survey
== back_ptr
) {
238 cmp
= strcmp(cached_station
->ident
, name
);
239 if (cmp
<= 0) ptr
= cached_station
;
241 while (ptr
&& (cmp
= strcmp(ptr
->ident
, name
))<0) {
246 /* ie we got to one that was higher, or the end */
248 name
= osrealloc(name
, i
);
249 newptr
= osnew(prefix
);
250 newptr
->ident
= name
;
253 back_ptr
->down
= newptr
;
255 ptrPrev
->right
= newptr
;
261 newptr
->up
= back_ptr
;
262 newptr
->filename
= file
.filename
;
263 newptr
->line
= file
.line
;
264 newptr
->min_export
= newptr
->max_export
= 0;
265 newptr
->sflags
= BIT(SFLAGS_SURVEY
);
266 if (fSuspectTypo
&& !fImplicitPrefix
)
267 newptr
->sflags
|= BIT(SFLAGS_SUSPECTTYPO
);
271 cached_survey
= back_ptr
;
272 cached_station
= ptr
;
275 f_optional
= fFalse
; /* disallow after first level */
277 if (name
) osfree(name
);
279 /* don't warn about a station that is referred to twice */
280 if (!fNew
) ptr
->sflags
&= ~BIT(SFLAGS_SUSPECTTYPO
);
283 /* fNew means SFLAGS_SURVEY is currently set */
284 SVX_ASSERT(TSTBIT(ptr
->sflags
, SFLAGS_SURVEY
));
286 ptr
->sflags
&= ~BIT(SFLAGS_SURVEY
);
287 if (TSTBIT(pcs
->infer
, INFER_EXPORTS
)) ptr
->min_export
= USHRT_MAX
;
290 /* check that the same name isn't being used for a survey and station */
291 if (fSurvey
^ TSTBIT(ptr
->sflags
, SFLAGS_SURVEY
)) {
292 /* TRANSLATORS: Here "station" is a survey station, not a train station.
294 * Here "survey" is a "cave map" rather than list of questions - it should be
295 * translated to the terminology that cavers using the language would use.
297 compile_error(/*“%s” can’t be both a station and a survey*/27,
300 if (!fSurvey
&& TSTBIT(pcs
->infer
, INFER_EXPORTS
)) ptr
->min_export
= USHRT_MAX
;
303 /* check the export level */
305 printf("R min %d max %d depth %d pfx %s\n",
306 ptr
->min_export
, ptr
->max_export
, depth
, sprint_prefix(ptr
));
308 if (ptr
->min_export
== 0 || ptr
->min_export
== USHRT_MAX
) {
309 if (depth
> ptr
->max_export
) ptr
->max_export
= depth
;
310 } else if (ptr
->max_export
< depth
) {
311 prefix
*survey
= ptr
;
315 for (level
= ptr
->max_export
+ 1; level
; level
--) {
319 s
= osstrdup(sprint_prefix(survey
));
320 p
= sprint_prefix(ptr
);
321 if (survey
->filename
) {
322 compile_error_pfx(survey
,
323 /*Station “%s” not exported from survey “%s”*/26,
326 compile_error(/*Station “%s” not exported from survey “%s”*/26, p
, s
);
330 printf(" *** pfx %s warning not exported enough depth %d "
331 "ptr->max_export %d\n", sprint_prefix(ptr
),
332 depth
, ptr
->max_export
);
335 if (!fImplicitPrefix
&& (pfx_flags
& PFX_WARN_SEPARATOR
)) {
336 compile_warning(/*Separator in survey name*/392);
341 /* if numeric expr is omitted: if f_optional return HUGE_REAL, else longjmp */
343 read_number(bool f_optional
)
345 bool fPositive
, fDigits
= fFalse
;
352 fPositive
= !isMinus(ch
);
353 if (isSign(ch
)) nextch();
355 while (isdigit(ch
)) {
356 n
= n
* (real
)10.0 + (char)(ch
- '0');
362 real mult
= (real
)1.0;
364 while (isdigit(ch
)) {
366 n
+= (char)(ch
- '0') * mult
;
372 /* !'fRead' => !fDigits so fDigits => 'fRead' */
373 if (fDigits
) return (fPositive
? n
: -n
);
375 /* didn't read a valid number. If it's optional, reset filepos & return */
381 if (isOmit(ch_old
)) {
382 compile_error(-/*Field may not be omitted*/8);
384 compile_error_token(-/*Expecting numeric field, found “%s”*/9);
386 LONGJMP(file
.jbSkipLine
);
387 return 0.0; /* for brain-fried compilers */
391 read_numeric(bool f_optional
)
394 return read_number(f_optional
);
398 read_numeric_multi(bool f_optional
, int *p_n_readings
)
400 size_t n_readings
= 0;
401 real tot
= (real
)0.0;
405 real r
= read_number(f_optional
);
406 if (p_n_readings
) *p_n_readings
= (r
== HUGE_REAL
? 0 : 1);
413 tot
+= read_number(fFalse
);
416 } while (!isClose(ch
));
419 if (p_n_readings
) *p_n_readings
= n_readings
;
420 /* FIXME: special averaging for bearings ... */
421 /* And for percentage gradient */
422 return tot
/ n_readings
;
425 /* read numeric expr or omit (return HUGE_REAL); else longjmp */
427 read_numeric_multi_or_omit(int *p_n_readings
)
429 real v
= read_numeric_multi(fTrue
, p_n_readings
);
430 if (v
== HUGE_REAL
) {
432 compile_error_token(-/*Expecting numeric field, found “%s”*/9);
433 LONGJMP(file
.jbSkipLine
);
434 return 0.0; /* for brain-fried compilers */
441 /* Don't skip blanks, variable error code */
443 read_uint_internal(int errmsg
, const filepos
*fp
)
448 compile_error_token(-errmsg
);
449 LONGJMP(file
.jbSkipLine
);
451 while (isdigit(ch
)) {
452 n
= n
* 10 + (char)(ch
- '0');
462 return read_uint_internal(/*Expecting numeric field, found “%s”*/9, NULL
);
466 read_string(char **pstr
, int *plen
)
472 /* String quoted in "" */
476 compile_error(-/*Missing \"*/69);
477 LONGJMP(file
.jbSkipLine
);
480 if (ch
== '\"') break;
482 s_catchar(pstr
, plen
, ch
);
487 /* Unquoted string */
489 if (isEol(ch
) || isComm(ch
)) {
490 if (!*pstr
|| !(*pstr
)[0]) {
491 compile_error(-/*Expecting string field*/121);
492 LONGJMP(file
.jbSkipLine
);
497 if (isBlank(ch
)) break;
499 s_catchar(pstr
, plen
, ch
);
506 read_date(int *py
, int *pm
, int *pd
)
508 int y
= 0, m
= 0, d
= 0;
514 y
= read_uint_internal(/*Expecting date, found “%s”*/198, &fp
);
515 /* Two digit year is 19xx. */
516 if (y
< 100) y
+= 1900;
517 if (y
< 1900 || y
> 2078) {
518 compile_warning(/*Invalid year (< 1900 or > 2078)*/58);
519 LONGJMP(file
.jbSkipLine
);
520 return; /* for brain-fried compilers */
524 m
= read_uint_internal(/*Expecting date, found “%s”*/198, &fp
);
525 if (m
< 1 || m
> 12) {
526 compile_warning(/*Invalid month*/86);
527 LONGJMP(file
.jbSkipLine
);
528 return; /* for brain-fried compilers */
532 d
= read_uint_internal(/*Expecting date, found “%s”*/198, &fp
);
533 if (d
< 1 || d
> last_day(y
, m
)) {
534 /* TRANSLATORS: e.g. 31st of April, or 32nd of any month */
535 compile_warning(/*Invalid day of the month*/87);
536 LONGJMP(file
.jbSkipLine
);
537 return; /* for brain-fried compilers */