source: git/src/readval.c@ 53496ab3

RELEASE/1.2 debug-ci debug-ci-sanitisers faster-cavernlog log-select main stereo stereo-2025 walls-data walls-data-hanging-as-warning warn-only-for-hanging-survey
Last change on this file since 53496ab3 was da65891, checked in by Olly Betts <olly@…>, 13 years ago

NEWS,src/readval.c,tests/cmd_alias.out: Fix handling of anonymous
wall stations ('..' by default) to implicitly set the SPLAY leg flag,
as was intended.

  • Property mode set to 100644
File size: 14.0 KB
Line 
1/* readval.c
2 * Routines to read a prefix or number from the current input file
3 * Copyright (C) 1991-2003,2005,2006,2010,2011,2012,2013 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#ifdef HAVE_CONFIG_H
21# include <config.h>
22#endif
23
24#include <limits.h>
25#include <stddef.h> /* for offsetof */
26
27#include "cavern.h"
28#include "date.h"
29#include "debug.h"
30#include "filename.h"
31#include "message.h"
32#include "readval.h"
33#include "datain.h"
34#include "netbits.h"
35#include "osalloc.h"
36#include "str.h"
37
38#ifdef HAVE_SETJMP_H
39# define LONGJMP(JB) longjmp((JB), 1)
40#else
41# define LONGJMP(JB) exit(1)
42#endif
43
44int root_depr_count = 0;
45
46static prefix *
47new_anon_station(void)
48{
49 prefix *name = osnew(prefix);
50 name->pos = NULL;
51 name->ident = NULL;
52 name->shape = 0;
53 name->stn = NULL;
54 name->up = pcs->Prefix;
55 name->down = NULL;
56 name->filename = file.filename;
57 name->line = file.line;
58 if (TSTBIT(pcs->infer, INFER_EXPORTS)) {
59 name->min_export = USHRT_MAX;
60 } else {
61 name->min_export = 0;
62 }
63 name->max_export = 0;
64 name->sflags = BIT(SFLAGS_ANON);
65 /* Keep linked list of anon stations for node stats. */
66 name->right = anon_list;
67 anon_list = name;
68 return name;
69}
70
71/* if prefix is omitted: if PFX_OPT set return NULL, otherwise use longjmp */
72extern prefix *
73read_prefix(unsigned pfx_flags)
74{
75 bool f_optional = !!(pfx_flags & PFX_OPT);
76 bool fSurvey = !!(pfx_flags & PFX_SURVEY);
77 bool fSuspectTypo = !!(pfx_flags & PFX_SUSPECT_TYPO);
78 prefix *back_ptr, *ptr;
79 char *name;
80 size_t name_len = 32;
81 size_t i;
82 bool fNew;
83 bool fImplicitPrefix = fTrue;
84 int depth = -1;
85
86 skipblanks();
87#ifndef NO_DEPRECATED
88 if (isRoot(ch)) {
89 if (!(pfx_flags & PFX_ALLOW_ROOT)) {
90 compile_error(-/*ROOT is deprecated*/25);
91 LONGJMP(file.jbSkipLine);
92 }
93 if (root_depr_count < 5) {
94 compile_warning(-/*ROOT is deprecated*/25);
95 if (++root_depr_count == 5)
96 compile_warning(/*Further uses of this deprecated feature will not be reported*/95);
97 }
98 nextch();
99 ptr = root;
100 if (!isNames(ch)) {
101 if (!isSep(ch)) return ptr;
102 /* Allow optional SEPARATOR after ROOT */
103 nextch();
104 }
105 fImplicitPrefix = fFalse;
106#else
107 if (0) {
108#endif
109 } else {
110 if ((pfx_flags & PFX_ANON) &&
111 (isSep(ch) || (pcs->dash_for_anon_wall_station && ch == '-'))) {
112 int first_ch = ch;
113 filepos here;
114 get_pos(&here);
115 nextch();
116 if (isBlank(ch) || isEol(ch)) {
117 if (!isSep(first_ch))
118 goto anon_wall_station;
119 /* A single separator alone ('.' by default) is an anonymous
120 * station which is on a point inside the passage and implies
121 * the leg to it is a splay.
122 */
123 if (TSTBIT(pcs->flags, FLAGS_ANON_ONE_END)) {
124 compile_error(-/*Can't have a leg between two anonymous stations*/3);
125 LONGJMP(file.jbSkipLine);
126 }
127 pcs->flags |= BIT(FLAGS_ANON_ONE_END) | BIT(FLAGS_IMPLICIT_SPLAY);
128 return new_anon_station();
129 }
130 if (isSep(first_ch) && ch == first_ch) {
131 nextch();
132 if (isBlank(ch) || isEol(ch)) {
133 /* A double separator ('..' by default) is an anonymous station
134 * which is on the wall and implies the leg to it is a splay.
135 */
136 prefix * pfx;
137anon_wall_station:
138 if (TSTBIT(pcs->flags, FLAGS_ANON_ONE_END)) {
139 compile_error(-/*Can't have a leg between two anonymous stations*/3);
140 LONGJMP(file.jbSkipLine);
141 }
142 pcs->flags |= BIT(FLAGS_ANON_ONE_END) | BIT(FLAGS_IMPLICIT_SPLAY);
143 pfx = new_anon_station();
144 pfx->sflags |= BIT(SFLAGS_WALL);
145 return pfx;
146 }
147 if (ch == first_ch) {
148 nextch();
149 if (isBlank(ch) || isEol(ch)) {
150 /* A triple separator ('...' by default) is an anonymous
151 * station, but otherwise not handled specially (e.g. for
152 * a single leg down an unexplored side passage to a station
153 * which isn't refindable).
154 */
155 if (TSTBIT(pcs->flags, FLAGS_ANON_ONE_END)) {
156 compile_error(-/*Can't have a leg between two anonymous stations*/3);
157 LONGJMP(file.jbSkipLine);
158 }
159 pcs->flags |= BIT(FLAGS_ANON_ONE_END);
160 return new_anon_station();
161 }
162 }
163 }
164 set_pos(&here);
165 }
166 ptr = pcs->Prefix;
167 }
168
169 i = 0;
170 name = NULL;
171 do {
172 fNew = fFalse;
173 if (name == NULL) {
174 /* Need a new name buffer */
175 name = osmalloc(name_len);
176 }
177 /* i==0 iff this is the first pass */
178 if (i) {
179 i = 0;
180 nextch();
181 }
182 while (isNames(ch)) {
183 if (i < pcs->Truncate) {
184 /* truncate name */
185 name[i++] = (pcs->Case == LOWER ? tolower(ch) :
186 (pcs->Case == OFF ? ch : toupper(ch)));
187 if (i >= name_len) {
188 name_len = name_len + name_len;
189 name = osrealloc(name, name_len);
190 }
191 }
192 nextch();
193 }
194 if (isSep(ch)) fImplicitPrefix = fFalse;
195 if (i == 0) {
196 osfree(name);
197 if (!f_optional) {
198 if (isEol(ch)) {
199 if (fSurvey) {
200 compile_error(-/*Expecting survey name*/89);
201 } else {
202 compile_error(-/*Expecting station name*/28);
203 }
204 } else {
205 compile_error(-/*Character “%c” not allowed in station name (use *SET NAMES to set allowed characters)*/7, ch);
206 }
207 LONGJMP(file.jbSkipLine);
208 }
209 return (prefix *)NULL;
210 }
211
212 name[i++] = '\0';
213
214 back_ptr = ptr;
215 ptr = ptr->down;
216 if (ptr == NULL) {
217 /* Special case first time around at each level */
218 name = osrealloc(name, i);
219 ptr = osnew(prefix);
220 ptr->ident = name;
221 name = NULL;
222 ptr->right = ptr->down = NULL;
223 ptr->pos = NULL;
224 ptr->shape = 0;
225 ptr->stn = NULL;
226 ptr->up = back_ptr;
227 ptr->filename = file.filename;
228 ptr->line = file.line;
229 ptr->min_export = ptr->max_export = 0;
230 ptr->sflags = BIT(SFLAGS_SURVEY);
231 if (fSuspectTypo && !fImplicitPrefix)
232 ptr->sflags |= BIT(SFLAGS_SUSPECTTYPO);
233 back_ptr->down = ptr;
234 fNew = fTrue;
235 } else {
236 /* Use caching to speed up adding an increasing sequence to a
237 * large survey */
238 static prefix *cached_survey = NULL, *cached_station = NULL;
239 prefix *ptrPrev = NULL;
240 int cmp = 1; /* result of strcmp ( -ve for <, 0 for =, +ve for > ) */
241 if (cached_survey == back_ptr) {
242 cmp = strcmp(cached_station->ident, name);
243 if (cmp <= 0) ptr = cached_station;
244 }
245 while (ptr && (cmp = strcmp(ptr->ident, name))<0) {
246 ptrPrev = ptr;
247 ptr = ptr->right;
248 }
249 if (cmp) {
250 /* ie we got to one that was higher, or the end */
251 prefix *newptr;
252 name = osrealloc(name, i);
253 newptr = osnew(prefix);
254 newptr->ident = name;
255 name = NULL;
256 if (ptrPrev == NULL)
257 back_ptr->down = newptr;
258 else
259 ptrPrev->right = newptr;
260 newptr->right = ptr;
261 newptr->down = NULL;
262 newptr->pos = NULL;
263 newptr->shape = 0;
264 newptr->stn = NULL;
265 newptr->up = back_ptr;
266 newptr->filename = file.filename;
267 newptr->line = file.line;
268 newptr->min_export = newptr->max_export = 0;
269 newptr->sflags = BIT(SFLAGS_SURVEY);
270 if (fSuspectTypo && !fImplicitPrefix)
271 newptr->sflags |= BIT(SFLAGS_SUSPECTTYPO);
272 ptr = newptr;
273 fNew = fTrue;
274 }
275 cached_survey = back_ptr;
276 cached_station = ptr;
277 }
278 depth++;
279 f_optional = fFalse; /* disallow after first level */
280 } while (isSep(ch));
281 if (name) osfree(name);
282
283 /* don't warn about a station that is referred to twice */
284 if (!fNew) ptr->sflags &= ~BIT(SFLAGS_SUSPECTTYPO);
285
286 if (fNew) {
287 /* fNew means SFLAGS_SURVEY is currently set */
288 SVX_ASSERT(TSTBIT(ptr->sflags, SFLAGS_SURVEY));
289 if (!fSurvey) {
290 ptr->sflags &= ~BIT(SFLAGS_SURVEY);
291 if (TSTBIT(pcs->infer, INFER_EXPORTS)) ptr->min_export = USHRT_MAX;
292 }
293 } else {
294 /* check that the same name isn't being used for a survey and station */
295 if (fSurvey ^ TSTBIT(ptr->sflags, SFLAGS_SURVEY)) {
296 compile_error(/*“%s” can’t be both a station and a survey*/27,
297 sprint_prefix(ptr));
298 }
299 if (!fSurvey && TSTBIT(pcs->infer, INFER_EXPORTS)) ptr->min_export = USHRT_MAX;
300 }
301
302 /* check the export level */
303#if 0
304 printf("R min %d max %d depth %d pfx %s\n",
305 ptr->min_export, ptr->max_export, depth, sprint_prefix(ptr));
306#endif
307 if (ptr->min_export == 0 || ptr->min_export == USHRT_MAX) {
308 if (depth > ptr->max_export) ptr->max_export = depth;
309 } else if (ptr->max_export < depth) {
310 const char *filename_store = file.filename;
311 unsigned int line_store = file.line;
312 prefix *survey = ptr;
313 char *s;
314 int level;
315 for (level = ptr->max_export + 1; level; level--) {
316 survey = survey->up;
317 SVX_ASSERT(survey);
318 }
319 s = osstrdup(sprint_prefix(survey));
320 if (survey->filename) {
321 file.filename = survey->filename;
322 file.line = survey->line;
323 }
324 compile_error(/*Station “%s” not exported from survey “%s”*/26,
325 sprint_prefix(ptr), s);
326 if (survey->filename) {
327 file.filename = filename_store;
328 file.line = line_store;
329 }
330 osfree(s);
331#if 0
332 printf(" *** pfx %s warning not exported enough depth %d "
333 "ptr->max_export %d\n", sprint_prefix(ptr),
334 depth, ptr->max_export);
335#endif
336 }
337 if (!fImplicitPrefix && (pfx_flags & PFX_WARN_SEPARATOR)) {
338 compile_warning(/*Separator in survey name*/392);
339 }
340 return ptr;
341}
342
343/* if numeric expr is omitted: if f_optional return HUGE_REAL, else longjmp */
344static real
345read_number(bool f_optional)
346{
347 bool fPositive, fDigits = fFalse;
348 real n = (real)0.0;
349 filepos fp;
350 int ch_old;
351
352 get_pos(&fp);
353 ch_old = ch;
354 fPositive = !isMinus(ch);
355 if (isSign(ch)) nextch();
356
357 while (isdigit(ch)) {
358 n = n * (real)10.0 + (char)(ch - '0');
359 nextch();
360 fDigits = fTrue;
361 }
362
363 if (isDecimal(ch)) {
364 real mult = (real)1.0;
365 nextch();
366 while (isdigit(ch)) {
367 mult *= (real).1;
368 n += (char)(ch - '0') * mult;
369 fDigits = fTrue;
370 nextch();
371 }
372 }
373
374 /* !'fRead' => !fDigits so fDigits => 'fRead' */
375 if (fDigits) return (fPositive ? n : -n);
376
377 /* didn't read a valid number. If it's optional, reset filepos & return */
378 set_pos(&fp);
379 if (f_optional) {
380 return HUGE_REAL;
381 }
382
383 if (isOmit(ch_old)) {
384 compile_error(-/*Field may not be omitted*/8);
385 } else {
386 compile_error_token(-/*Expecting numeric field, found “%s”*/9);
387 }
388 LONGJMP(file.jbSkipLine);
389 return 0.0; /* for brain-fried compilers */
390}
391
392extern real
393read_numeric(bool f_optional, int *p_n_readings)
394{
395 size_t n_readings = 0;
396 real tot = (real)0.0;
397
398 skipblanks();
399 if (!isOpen(ch)) {
400 real r = read_number(f_optional);
401 if (p_n_readings) *p_n_readings = (r == HUGE_REAL ? 0 : 1);
402 return r;
403 }
404 nextch();
405
406 skipblanks();
407 do {
408 tot += read_number(fFalse);
409 ++n_readings;
410 skipblanks();
411 } while (!isClose(ch));
412 nextch();
413
414 if (p_n_readings) *p_n_readings = n_readings;
415 /* FIXME: special averaging for bearings ... */
416 /* And for percentage gradient */
417 return tot / n_readings;
418}
419
420/* read numeric expr or omit (return HUGE_REAL); else longjmp */
421extern real
422read_numeric_or_omit(int *p_n_readings)
423{
424 real v = read_numeric(fTrue, p_n_readings);
425 if (v == HUGE_REAL) {
426 if (!isOmit(ch)) {
427 compile_error_token(-/*Expecting numeric field, found “%s”*/9);
428 LONGJMP(file.jbSkipLine);
429 return 0.0; /* for brain-fried compilers */
430 }
431 nextch();
432 }
433 return v;
434}
435
436/* Don't skip blanks, variable error code */
437static unsigned int
438read_uint_internal(int errmsg, const filepos *fp)
439{
440 unsigned int n = 0;
441 if (!isdigit(ch)) {
442 if (fp) set_pos(fp);
443 compile_error_token(-errmsg);
444 LONGJMP(file.jbSkipLine);
445 }
446 while (isdigit(ch)) {
447 n = n * 10 + (char)(ch - '0');
448 nextch();
449 }
450 return n;
451}
452
453extern unsigned int
454read_uint(void)
455{
456 skipblanks();
457 return read_uint_internal(/*Expecting numeric field, found “%s”*/9, NULL);
458}
459
460extern void
461read_string(char **pstr, int *plen)
462{
463 s_zero(pstr);
464
465 skipblanks();
466 if (ch == '\"') {
467 /* String quoted in "" */
468 nextch();
469 while (1) {
470 if (isEol(ch)) {
471 compile_error(-/*Missing \"*/69);
472 LONGJMP(file.jbSkipLine);
473 }
474
475 if (ch == '\"') break;
476
477 s_catchar(pstr, plen, ch);
478 nextch();
479 }
480 } else {
481 /* Unquoted string */
482 while (1) {
483 if (isEol(ch) || isComm(ch)) {
484 if (!*pstr || !(*pstr)[0]) {
485 compile_error(-/*Expecting string field*/121);
486 LONGJMP(file.jbSkipLine);
487 }
488 return;
489 }
490
491 if (isBlank(ch)) break;
492
493 s_catchar(pstr, plen, ch);
494 nextch();
495 }
496 }
497
498 nextch();
499}
500
501extern void
502read_date(int *py, int *pm, int *pd)
503{
504 int y = 0, m = 0, d = 0;
505 filepos fp;
506
507 skipblanks();
508
509 get_pos(&fp);
510 y = read_uint_internal(/*Expecting date, found “%s”*/198, &fp);
511 /* Two digit year is 19xx. */
512 if (y < 100) y += 1900;
513 if (y < 1900 || y > 2078) {
514 compile_warning(/*Invalid year (< 1900 or > 2078)*/58);
515 LONGJMP(file.jbSkipLine);
516 return; /* for brain-fried compilers */
517 }
518 if (ch == '.') {
519 nextch();
520 m = read_uint_internal(/*Expecting date, found “%s”*/198, &fp);
521 if (m < 1 || m > 12) {
522 compile_warning(/*Invalid month*/86);
523 LONGJMP(file.jbSkipLine);
524 return; /* for brain-fried compilers */
525 }
526 if (ch == '.') {
527 nextch();
528 d = read_uint_internal(/*Expecting date, found “%s”*/198, &fp);
529 if (d < 1 || d > last_day(y, m)) {
530 compile_warning(/*Invalid day of the month*/87);
531 LONGJMP(file.jbSkipLine);
532 return; /* for brain-fried compilers */
533 }
534 }
535 }
536 if (py) *py = y;
537 if (pm) *pm = m;
538 if (pd) *pd = d;
539}
Note: See TracBrowser for help on using the repository browser.