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

RELEASE/1.2debug-cidebug-ci-sanitisersstereowalls-datawalls-data-hanging-as-warning
Last change on this file since 53496ab3 was da65891, checked in by Olly Betts <olly@…>, 11 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
RevLine 
[6974a34]1/* readval.c
[0395657]2 * Routines to read a prefix or number from the current input file
[710ecc1]3 * Copyright (C) 1991-2003,2005,2006,2010,2011,2012,2013 Olly Betts
[846746e]4 *
[89231c4]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.
[846746e]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
[89231c4]12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
[846746e]14 *
[89231c4]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
[d333899]17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
[d1b1380]18 */
19
[a420b49]20#ifdef HAVE_CONFIG_H
21# include <config.h>
22#endif
[d1b1380]23
[c00c74a9]24#include <limits.h>
[be97baf]25#include <stddef.h> /* for offsetof */
[c00c74a9]26
[a420b49]27#include "cavern.h"
[1ee204e]28#include "date.h"
[dd5b1a8]29#include "debug.h"
[a1706d1]30#include "filename.h"
31#include "message.h"
[d1b1380]32#include "readval.h"
33#include "datain.h"
[fb2e93c]34#include "netbits.h"
[8cf2e04]35#include "osalloc.h"
[76bb277a]36#include "str.h"
[d1b1380]37
[7d5f3c0]38#ifdef HAVE_SETJMP_H
[647407d]39# define LONGJMP(JB) longjmp((JB), 1)
40#else
41# define LONGJMP(JB) exit(1)
42#endif
43
[c86cc71]44int root_depr_count = 0;
45
[710ecc1]46static prefix *
[a2c33ae]47new_anon_station(void)
[710ecc1]48{
[a2c33ae]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;
[710ecc1]69}
70
[c458cf7]71/* if prefix is omitted: if PFX_OPT set return NULL, otherwise use longjmp */
72extern prefix *
73read_prefix(unsigned pfx_flags)
[a420b49]74{
[c458cf7]75   bool f_optional = !!(pfx_flags & PFX_OPT);
76   bool fSurvey = !!(pfx_flags & PFX_SURVEY);
77   bool fSuspectTypo = !!(pfx_flags & PFX_SUSPECT_TYPO);
[4a6a094]78   prefix *back_ptr, *ptr;
[a420b49]79   char *name;
80   size_t name_len = 32;
[eb18f4d]81   size_t i;
[647407d]82   bool fNew;
[d6274ba]83   bool fImplicitPrefix = fTrue;
[932f7e9]84   int depth = -1;
[a420b49]85
86   skipblanks();
[7f1ab95]87#ifndef NO_DEPRECATED
[a420b49]88   if (isRoot(ch)) {
[c458cf7]89      if (!(pfx_flags & PFX_ALLOW_ROOT)) {
[da96015]90         compile_error(-/*ROOT is deprecated*/25);
[421b7d2]91         LONGJMP(file.jbSkipLine);
[84c60fc]92      }
[c86cc71]93      if (root_depr_count < 5) {
[da96015]94         compile_warning(-/*ROOT is deprecated*/25);
[c86cc71]95         if (++root_depr_count == 5)
[c40038a]96            compile_warning(/*Further uses of this deprecated feature will not be reported*/95);
[c86cc71]97      }
[d1b1380]98      nextch();
[a420b49]99      ptr = root;
100      if (!isNames(ch)) {
101         if (!isSep(ch)) return ptr;
[27b8b59]102         /* Allow optional SEPARATOR after ROOT */
[a420b49]103         nextch();
104      }
[647407d]105      fImplicitPrefix = fFalse;
[710ecc1]106#else
107   if (0) {
108#endif
[a420b49]109   } else {
[dcbcae0]110      if ((pfx_flags & PFX_ANON) &&
111          (isSep(ch) || (pcs->dash_for_anon_wall_station && ch == '-'))) {
112         int first_ch = ch;
[710ecc1]113         filepos here;
114         get_pos(&here);
115         nextch();
116         if (isBlank(ch) || isEol(ch)) {
[dcbcae0]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)) {
[710ecc1]124               compile_error(-/*Can't have a leg between two anonymous stations*/3);
[dcbcae0]125               LONGJMP(file.jbSkipLine);
[710ecc1]126            }
[dcbcae0]127            pcs->flags |= BIT(FLAGS_ANON_ONE_END) | BIT(FLAGS_IMPLICIT_SPLAY);
[a2c33ae]128            return new_anon_station();
[710ecc1]129         }
[dcbcae0]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               }
[da65891]142               pcs->flags |= BIT(FLAGS_ANON_ONE_END) | BIT(FLAGS_IMPLICIT_SPLAY);
[dcbcae0]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         }
[710ecc1]164         set_pos(&here);
165      }
[a420b49]166      ptr = pcs->Prefix;
167   }
168
169   i = 0;
[be97baf]170   name = NULL;
[a420b49]171   do {
[647407d]172      fNew = fFalse;
[be97baf]173      if (name == NULL) {
174         /* Need a new name buffer */
175         name = osmalloc(name_len);
176      }
[a420b49]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 */
[1157304]185            name[i++] = (pcs->Case == LOWER ? tolower(ch) :
186                         (pcs->Case == OFF ? ch : toupper(ch)));
[a420b49]187            if (i >= name_len) {
188               name_len = name_len + name_len;
189               name = osrealloc(name, name_len);
190            }
[8cf2e04]191         }
[a420b49]192         nextch();
[8cf2e04]193      }
[335f37a]194      if (isSep(ch)) fImplicitPrefix = fFalse;
[a420b49]195      if (i == 0) {
196         osfree(name);
[21c226e]197         if (!f_optional) {
[ee1d81f]198            if (isEol(ch)) {
[84c60fc]199               if (fSurvey) {
[4fb15a1]200                  compile_error(-/*Expecting survey name*/89);
[84c60fc]201               } else {
[4fb15a1]202                  compile_error(-/*Expecting station name*/28);
[84c60fc]203               }
[ee1d81f]204            } else {
[4fb15a1]205               compile_error(-/*Character “%c” not allowed in station name (use *SET NAMES to set allowed characters)*/7, ch);
[ee1d81f]206            }
[647407d]207            LONGJMP(file.jbSkipLine);
208         }
[a420b49]209         return (prefix *)NULL;
[d1b1380]210      }
[a420b49]211
[76bb277a]212      name[i++] = '\0';
[cb3d1e2]213
[a420b49]214      back_ptr = ptr;
215      ptr = ptr->down;
216      if (ptr == NULL) {
217         /* Special case first time around at each level */
[be97baf]218         name = osrealloc(name, i);
[a420b49]219         ptr = osnew(prefix);
220         ptr->ident = name;
[be97baf]221         name = NULL;
[a420b49]222         ptr->right = ptr->down = NULL;
223         ptr->pos = NULL;
[6adb88c]224         ptr->shape = 0;
[a420b49]225         ptr->stn = NULL;
226         ptr->up = back_ptr;
[d333899]227         ptr->filename = file.filename;
228         ptr->line = file.line;
[932f7e9]229         ptr->min_export = ptr->max_export = 0;
[95c3272]230         ptr->sflags = BIT(SFLAGS_SURVEY);
231         if (fSuspectTypo && !fImplicitPrefix)
232            ptr->sflags |= BIT(SFLAGS_SUSPECTTYPO);
[a420b49]233         back_ptr->down = ptr;
[647407d]234         fNew = fTrue;
[a420b49]235      } else {
[be97baf]236         /* Use caching to speed up adding an increasing sequence to a
237          * large survey */
238         static prefix *cached_survey = NULL, *cached_station = NULL;
[a420b49]239         prefix *ptrPrev = NULL;
[76bb277a]240         int cmp = 1; /* result of strcmp ( -ve for <, 0 for =, +ve for > ) */
[be97baf]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) {
[a420b49]246            ptrPrev = ptr;
247            ptr = ptr->right;
248         }
249         if (cmp) {
250            /* ie we got to one that was higher, or the end */
[be97baf]251            prefix *newptr;
252            name = osrealloc(name, i);
253            newptr = osnew(prefix);
[647407d]254            newptr->ident = name;
[be97baf]255            name = NULL;
[a420b49]256            if (ptrPrev == NULL)
[647407d]257               back_ptr->down = newptr;
[a420b49]258            else
[647407d]259               ptrPrev->right = newptr;
260            newptr->right = ptr;
261            newptr->down = NULL;
262            newptr->pos = NULL;
[6adb88c]263            newptr->shape = 0;
[647407d]264            newptr->stn = NULL;
265            newptr->up = back_ptr;
[d333899]266            newptr->filename = file.filename;
267            newptr->line = file.line;
[421b7d2]268            newptr->min_export = newptr->max_export = 0;
[95c3272]269            newptr->sflags = BIT(SFLAGS_SURVEY);
270            if (fSuspectTypo && !fImplicitPrefix)
271               newptr->sflags |= BIT(SFLAGS_SUSPECTTYPO);
[647407d]272            ptr = newptr;
273            fNew = fTrue;
[a420b49]274         }
[be97baf]275         cached_survey = back_ptr;
276         cached_station = ptr;
[d1b1380]277      }
[fb2e93c]278      depth++;
[21c226e]279      f_optional = fFalse; /* disallow after first level */
[a420b49]280   } while (isSep(ch));
[be97baf]281   if (name) osfree(name);
282
[d40f787]283   /* don't warn about a station that is referred to twice */
[95c3272]284   if (!fNew) ptr->sflags &= ~BIT(SFLAGS_SUSPECTTYPO);
[421b7d2]285
[6430f0e]286   if (fNew) {
[95c3272]287      /* fNew means SFLAGS_SURVEY is currently set */
[4c07c51]288      SVX_ASSERT(TSTBIT(ptr->sflags, SFLAGS_SURVEY));
[c00c74a9]289      if (!fSurvey) {
290         ptr->sflags &= ~BIT(SFLAGS_SURVEY);
[27b8b59]291         if (TSTBIT(pcs->infer, INFER_EXPORTS)) ptr->min_export = USHRT_MAX;
[c00c74a9]292      }
[6430f0e]293   } else {
294      /* check that the same name isn't being used for a survey and station */
[95c3272]295      if (fSurvey ^ TSTBIT(ptr->sflags, SFLAGS_SURVEY)) {
[ee7511a]296         compile_error(/*“%s” can’t be both a station and a survey*/27,
[6430f0e]297                       sprint_prefix(ptr));
298      }
[27b8b59]299      if (!fSurvey && TSTBIT(pcs->infer, INFER_EXPORTS)) ptr->min_export = USHRT_MAX;
[051a72b]300   }
301
[932f7e9]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
[c00c74a9]307   if (ptr->min_export == 0 || ptr->min_export == USHRT_MAX) {
[932f7e9]308      if (depth > ptr->max_export) ptr->max_export = depth;
309   } else if (ptr->max_export < depth) {
[016068a]310      const char *filename_store = file.filename;
311      unsigned int line_store = file.line;
[dd5b1a8]312      prefix *survey = ptr;
313      char *s;
314      int level;
315      for (level = ptr->max_export + 1; level; level--) {
316         survey = survey->up;
[4c07c51]317         SVX_ASSERT(survey);
[dd5b1a8]318      }
319      s = osstrdup(sprint_prefix(survey));
[016068a]320      if (survey->filename) {
321         file.filename = survey->filename;
322         file.line = survey->line;
323      }
[0804fbe]324      compile_error(/*Station “%s” not exported from survey “%s”*/26,
[dd5b1a8]325                    sprint_prefix(ptr), s);
[016068a]326      if (survey->filename) {
327         file.filename = filename_store;
328         file.line = line_store;
329      }
[dd5b1a8]330      osfree(s);
[932f7e9]331#if 0
332      printf(" *** pfx %s warning not exported enough depth %d "
[421b7d2]333             "ptr->max_export %d\n", sprint_prefix(ptr),
[932f7e9]334             depth, ptr->max_export);
335#endif
336   }
[0fa7aac]337   if (!fImplicitPrefix && (pfx_flags & PFX_WARN_SEPARATOR)) {
338      compile_warning(/*Separator in survey name*/392);
339   }
[a420b49]340   return ptr;
[d1b1380]341}
342
[21c226e]343/* if numeric expr is omitted: if f_optional return HUGE_REAL, else longjmp */
344static real
345read_number(bool f_optional)
[a420b49]346{
347   bool fPositive, fDigits = fFalse;
348   real n = (real)0.0;
[c80bd34]349   filepos fp;
350   int ch_old;
[a420b49]351
[c80bd34]352   get_pos(&fp);
353   ch_old = ch;
[a420b49]354   fPositive = !isMinus(ch);
355   if (isSign(ch)) nextch();
356
357   while (isdigit(ch)) {
[6974a34]358      n = n * (real)10.0 + (char)(ch - '0');
[d1b1380]359      nextch();
[a420b49]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 */
[4fb15a1]378   set_pos(&fp);
[21c226e]379   if (f_optional) {
[a420b49]380      return HUGE_REAL;
381   }
382
[c80bd34]383   if (isOmit(ch_old)) {
[4fb15a1]384      compile_error(-/*Field may not be omitted*/8);
[647407d]385   } else {
[da96015]386      compile_error_token(-/*Expecting numeric field, found “%s”*/9);
[647407d]387   }
388   LONGJMP(file.jbSkipLine);
[a420b49]389   return 0.0; /* for brain-fried compilers */
[d1b1380]390}
[647407d]391
[21c226e]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
[2aa2f3f]420/* read numeric expr or omit (return HUGE_REAL); else longjmp */
421extern real
[21c226e]422read_numeric_or_omit(int *p_n_readings)
[2aa2f3f]423{
[21c226e]424   real v = read_numeric(fTrue, p_n_readings);
[2aa2f3f]425   if (v == HUGE_REAL) {
426      if (!isOmit(ch)) {
[da96015]427         compile_error_token(-/*Expecting numeric field, found “%s”*/9);
[2aa2f3f]428         LONGJMP(file.jbSkipLine);
429         return 0.0; /* for brain-fried compilers */
430      }
431      nextch();
432   }
433   return v;
434}
435
[950a829]436/* Don't skip blanks, variable error code */
437static unsigned int
[75e67ab]438read_uint_internal(int errmsg, const filepos *fp)
[647407d]439{
[421b7d2]440   unsigned int n = 0;
[647407d]441   if (!isdigit(ch)) {
[029cc3f]442      if (fp) set_pos(fp);
[da96015]443      compile_error_token(-errmsg);
[647407d]444      LONGJMP(file.jbSkipLine);
445   }
446   while (isdigit(ch)) {
447      n = n * 10 + (char)(ch - '0');
448      nextch();
449   }
450   return n;
451}
[76bb277a]452
[950a829]453extern unsigned int
454read_uint(void)
455{
456   skipblanks();
[0804fbe]457   return read_uint_internal(/*Expecting numeric field, found “%s”*/9, NULL);
[950a829]458}
459
[76bb277a]460extern void
461read_string(char **pstr, int *plen)
462{
463   s_zero(pstr);
464
465   skipblanks();
466   if (ch == '\"') {
[1157304]467      /* String quoted in "" */
[76bb277a]468      nextch();
[457a390]469      while (1) {
470         if (isEol(ch)) {
[4fb15a1]471            compile_error(-/*Missing \"*/69);
472            LONGJMP(file.jbSkipLine);
[457a390]473         }
474
475         if (ch == '\"') break;
476
477         s_catchar(pstr, plen, ch);
478         nextch();
479      }
480   } else {
[1157304]481      /* Unquoted string */
[457a390]482      while (1) {
483         if (isEol(ch) || isComm(ch)) {
[b97d134]484            if (!*pstr || !(*pstr)[0]) {
[4fb15a1]485               compile_error(-/*Expecting string field*/121);
[6f61f83]486               LONGJMP(file.jbSkipLine);
[b97d134]487            }
[457a390]488            return;
[76bb277a]489         }
490
[457a390]491         if (isBlank(ch)) break;
[76bb277a]492
[457a390]493         s_catchar(pstr, plen, ch);
494         nextch();
495      }
[76bb277a]496   }
[421b7d2]497
[76bb277a]498   nextch();
499}
[950a829]500
501extern void
[b5a3219]502read_date(int *py, int *pm, int *pd)
[950a829]503{
504   int y = 0, m = 0, d = 0;
[029cc3f]505   filepos fp;
[950a829]506
507   skipblanks();
[029cc3f]508
509   get_pos(&fp);
[0804fbe]510   y = read_uint_internal(/*Expecting date, found “%s”*/198, &fp);
[e0c7cd1]511   /* Two digit year is 19xx. */
512   if (y < 100) y += 1900;
[1ee204e]513   if (y < 1900 || y > 2078) {
[ce8f81c]514      compile_warning(/*Invalid year (< 1900 or > 2078)*/58);
[e0c7cd1]515      LONGJMP(file.jbSkipLine);
516      return; /* for brain-fried compilers */
517   }
[950a829]518   if (ch == '.') {
519      nextch();
[0804fbe]520      m = read_uint_internal(/*Expecting date, found “%s”*/198, &fp);
[e0c7cd1]521      if (m < 1 || m > 12) {
[ce8f81c]522         compile_warning(/*Invalid month*/86);
[e0c7cd1]523         LONGJMP(file.jbSkipLine);
524         return; /* for brain-fried compilers */
[79d84d9]525      }
[950a829]526      if (ch == '.') {
527         nextch();
[0804fbe]528         d = read_uint_internal(/*Expecting date, found “%s”*/198, &fp);
[e0c7cd1]529         if (d < 1 || d > last_day(y, m)) {
[ce8f81c]530            compile_warning(/*Invalid day of the month*/87);
[e0c7cd1]531            LONGJMP(file.jbSkipLine);
532            return; /* for brain-fried compilers */
533         }
[950a829]534      }
535   }
[b5a3219]536   if (py) *py = y;
537   if (pm) *pm = m;
538   if (pd) *pd = d;
[950a829]539}
Note: See TracBrowser for help on using the repository browser.