source: git/src/readval.c @ a72ed95

RELEASE/1.2debug-cidebug-ci-sanitisersstereowalls-data
Last change on this file since a72ed95 was 66f5fc4, checked in by Olly Betts <olly@…>, 9 years ago

src/datain.c,src/readval.c,tests/badinc.out,tests/badinc2.out,
tests/badinc3.out: Include column number when a *include file isn't
found.

  • Property mode set to 100644
File size: 14.3 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,2014,2015 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               /* TRANSLATORS: Here "station" is a survey station, not a train station. */
206               compile_error(-/*Character “%c” not allowed in station name (use *SET NAMES to set allowed characters)*/7, ch);
207            }
208            LONGJMP(file.jbSkipLine);
209         }
210         return (prefix *)NULL;
211      }
212
213      name[i++] = '\0';
214
215      back_ptr = ptr;
216      ptr = ptr->down;
217      if (ptr == NULL) {
218         /* Special case first time around at each level */
219         name = osrealloc(name, i);
220         ptr = osnew(prefix);
221         ptr->ident = name;
222         name = NULL;
223         ptr->right = ptr->down = NULL;
224         ptr->pos = NULL;
225         ptr->shape = 0;
226         ptr->stn = NULL;
227         ptr->up = back_ptr;
228         ptr->filename = file.filename;
229         ptr->line = file.line;
230         ptr->min_export = ptr->max_export = 0;
231         ptr->sflags = BIT(SFLAGS_SURVEY);
232         if (fSuspectTypo && !fImplicitPrefix)
233            ptr->sflags |= BIT(SFLAGS_SUSPECTTYPO);
234         back_ptr->down = ptr;
235         fNew = fTrue;
236      } else {
237         /* Use caching to speed up adding an increasing sequence to a
238          * large survey */
239         static prefix *cached_survey = NULL, *cached_station = NULL;
240         prefix *ptrPrev = NULL;
241         int cmp = 1; /* result of strcmp ( -ve for <, 0 for =, +ve for > ) */
242         if (cached_survey == back_ptr) {
243            cmp = strcmp(cached_station->ident, name);
244            if (cmp <= 0) ptr = cached_station;
245         }
246         while (ptr && (cmp = strcmp(ptr->ident, name))<0) {
247            ptrPrev = ptr;
248            ptr = ptr->right;
249         }
250         if (cmp) {
251            /* ie we got to one that was higher, or the end */
252            prefix *newptr;
253            name = osrealloc(name, i);
254            newptr = osnew(prefix);
255            newptr->ident = name;
256            name = NULL;
257            if (ptrPrev == NULL)
258               back_ptr->down = newptr;
259            else
260               ptrPrev->right = newptr;
261            newptr->right = ptr;
262            newptr->down = NULL;
263            newptr->pos = NULL;
264            newptr->shape = 0;
265            newptr->stn = NULL;
266            newptr->up = back_ptr;
267            newptr->filename = file.filename;
268            newptr->line = file.line;
269            newptr->min_export = newptr->max_export = 0;
270            newptr->sflags = BIT(SFLAGS_SURVEY);
271            if (fSuspectTypo && !fImplicitPrefix)
272               newptr->sflags |= BIT(SFLAGS_SUSPECTTYPO);
273            ptr = newptr;
274            fNew = fTrue;
275         }
276         cached_survey = back_ptr;
277         cached_station = ptr;
278      }
279      depth++;
280      f_optional = fFalse; /* disallow after first level */
281   } while (isSep(ch));
282   if (name) osfree(name);
283
284   /* don't warn about a station that is referred to twice */
285   if (!fNew) ptr->sflags &= ~BIT(SFLAGS_SUSPECTTYPO);
286
287   if (fNew) {
288      /* fNew means SFLAGS_SURVEY is currently set */
289      SVX_ASSERT(TSTBIT(ptr->sflags, SFLAGS_SURVEY));
290      if (!fSurvey) {
291         ptr->sflags &= ~BIT(SFLAGS_SURVEY);
292         if (TSTBIT(pcs->infer, INFER_EXPORTS)) ptr->min_export = USHRT_MAX;
293      }
294   } else {
295      /* check that the same name isn't being used for a survey and station */
296      if (fSurvey ^ TSTBIT(ptr->sflags, SFLAGS_SURVEY)) {
297         /* TRANSLATORS: Here "station" is a survey station, not a train station.
298          *
299          * Here "survey" is a "cave map" rather than list of questions - it should be
300          * translated to the terminology that cavers using the language would use.
301          */
302         compile_error(/*“%s” can’t be both a station and a survey*/27,
303                       sprint_prefix(ptr));
304      }
305      if (!fSurvey && TSTBIT(pcs->infer, INFER_EXPORTS)) ptr->min_export = USHRT_MAX;
306   }
307
308   /* check the export level */
309#if 0
310   printf("R min %d max %d depth %d pfx %s\n",
311          ptr->min_export, ptr->max_export, depth, sprint_prefix(ptr));
312#endif
313   if (ptr->min_export == 0 || ptr->min_export == USHRT_MAX) {
314      if (depth > ptr->max_export) ptr->max_export = depth;
315   } else if (ptr->max_export < depth) {
316      prefix *survey = ptr;
317      char *s;
318      const char *p;
319      int level;
320      for (level = ptr->max_export + 1; level; level--) {
321         survey = survey->up;
322         SVX_ASSERT(survey);
323      }
324      s = osstrdup(sprint_prefix(survey));
325      p = sprint_prefix(ptr);
326      if (survey->filename) {
327         compile_error_pfx(survey,
328                           /*Station “%s” not exported from survey “%s”*/26,
329                           p, s);
330      } else {
331         compile_error(/*Station “%s” not exported from survey “%s”*/26, p, s);
332      }
333      osfree(s);
334#if 0
335      printf(" *** pfx %s warning not exported enough depth %d "
336             "ptr->max_export %d\n", sprint_prefix(ptr),
337             depth, ptr->max_export);
338#endif
339   }
340   if (!fImplicitPrefix && (pfx_flags & PFX_WARN_SEPARATOR)) {
341      compile_warning(/*Separator in survey name*/392);
342   }
343   return ptr;
344}
345
346/* if numeric expr is omitted: if f_optional return HUGE_REAL, else longjmp */
347static real
348read_number(bool f_optional)
349{
350   bool fPositive, fDigits = fFalse;
351   real n = (real)0.0;
352   filepos fp;
353   int ch_old;
354
355   get_pos(&fp);
356   ch_old = ch;
357   fPositive = !isMinus(ch);
358   if (isSign(ch)) nextch();
359
360   while (isdigit(ch)) {
361      n = n * (real)10.0 + (char)(ch - '0');
362      nextch();
363      fDigits = fTrue;
364   }
365
366   if (isDecimal(ch)) {
367      real mult = (real)1.0;
368      nextch();
369      while (isdigit(ch)) {
370         mult *= (real).1;
371         n += (char)(ch - '0') * mult;
372         fDigits = fTrue;
373         nextch();
374      }
375   }
376
377   /* !'fRead' => !fDigits so fDigits => 'fRead' */
378   if (fDigits) return (fPositive ? n : -n);
379
380   /* didn't read a valid number.  If it's optional, reset filepos & return */
381   set_pos(&fp);
382   if (f_optional) {
383      return HUGE_REAL;
384   }
385
386   if (isOmit(ch_old)) {
387      compile_error(-/*Field may not be omitted*/8);
388   } else {
389      compile_error_token(-/*Expecting numeric field, found “%s”*/9);
390   }
391   LONGJMP(file.jbSkipLine);
392   return 0.0; /* for brain-fried compilers */
393}
394
395extern real
396read_numeric(bool f_optional, int *p_n_readings)
397{
398   size_t n_readings = 0;
399   real tot = (real)0.0;
400
401   skipblanks();
402   if (!isOpen(ch)) {
403      real r = read_number(f_optional);
404      if (p_n_readings) *p_n_readings = (r == HUGE_REAL ? 0 : 1);
405      return r;
406   }
407   nextch();
408
409   skipblanks();
410   do {
411      tot += read_number(fFalse);
412      ++n_readings;
413      skipblanks();
414   } while (!isClose(ch));
415   nextch();
416
417   if (p_n_readings) *p_n_readings = n_readings;
418   /* FIXME: special averaging for bearings ... */
419   /* And for percentage gradient */
420   return tot / n_readings;
421}
422
423/* read numeric expr or omit (return HUGE_REAL); else longjmp */
424extern real
425read_numeric_or_omit(int *p_n_readings)
426{
427   real v = read_numeric(fTrue, p_n_readings);
428   if (v == HUGE_REAL) {
429      if (!isOmit(ch)) {
430         compile_error_token(-/*Expecting numeric field, found “%s”*/9);
431         LONGJMP(file.jbSkipLine);
432         return 0.0; /* for brain-fried compilers */
433      }
434      nextch();
435   }
436   return v;
437}
438
439/* Don't skip blanks, variable error code */
440static unsigned int
441read_uint_internal(int errmsg, const filepos *fp)
442{
443   unsigned int n = 0;
444   if (!isdigit(ch)) {
445      if (fp) set_pos(fp);
446      compile_error_token(-errmsg);
447      LONGJMP(file.jbSkipLine);
448   }
449   while (isdigit(ch)) {
450      n = n * 10 + (char)(ch - '0');
451      nextch();
452   }
453   return n;
454}
455
456extern unsigned int
457read_uint(void)
458{
459   skipblanks();
460   return read_uint_internal(/*Expecting numeric field, found “%s”*/9, NULL);
461}
462
463extern void
464read_string(char **pstr, int *plen)
465{
466   s_zero(pstr);
467
468   skipblanks();
469   if (ch == '\"') {
470      /* String quoted in "" */
471      nextch();
472      while (1) {
473         if (isEol(ch)) {
474            compile_error(-/*Missing \"*/69);
475            LONGJMP(file.jbSkipLine);
476         }
477
478         if (ch == '\"') break;
479
480         s_catchar(pstr, plen, ch);
481         nextch();
482      }
483      nextch();
484   } else {
485      /* Unquoted string */
486      while (1) {
487         if (isEol(ch) || isComm(ch)) {
488            if (!*pstr || !(*pstr)[0]) {
489               compile_error(-/*Expecting string field*/121);
490               LONGJMP(file.jbSkipLine);
491            }
492            return;
493         }
494
495         if (isBlank(ch)) break;
496
497         s_catchar(pstr, plen, ch);
498         nextch();
499      }
500   }
501}
502
503extern void
504read_date(int *py, int *pm, int *pd)
505{
506   int y = 0, m = 0, d = 0;
507   filepos fp;
508
509   skipblanks();
510
511   get_pos(&fp);
512   y = read_uint_internal(/*Expecting date, found “%s”*/198, &fp);
513   /* Two digit year is 19xx. */
514   if (y < 100) y += 1900;
515   if (y < 1900 || y > 2078) {
516      compile_warning(/*Invalid year (< 1900 or > 2078)*/58);
517      LONGJMP(file.jbSkipLine);
518      return; /* for brain-fried compilers */
519   }
520   if (ch == '.') {
521      nextch();
522      m = read_uint_internal(/*Expecting date, found “%s”*/198, &fp);
523      if (m < 1 || m > 12) {
524         compile_warning(/*Invalid month*/86);
525         LONGJMP(file.jbSkipLine);
526         return; /* for brain-fried compilers */
527      }
528      if (ch == '.') {
529         nextch();
530         d = read_uint_internal(/*Expecting date, found “%s”*/198, &fp);
531         if (d < 1 || d > last_day(y, m)) {
532            /* TRANSLATORS: e.g. 31st of April, or 32nd of any month */
533            compile_warning(/*Invalid day of the month*/87);
534            LONGJMP(file.jbSkipLine);
535            return; /* for brain-fried compilers */
536         }
537      }
538   }
539   if (py) *py = y;
540   if (pm) *pm = m;
541   if (pd) *pd = d;
542}
Note: See TracBrowser for help on using the repository browser.