source: git/trunk/src/readval.c @ 7bb8184

Last change on this file since 7bb8184 was 7bb8184, checked in by Olly Betts <olly@…>, 13 years ago

Retagging 1.2.0

git-svn-id: file:///home/survex-svn/survex/tags/1.2.0@3664 4b37db11-9a0c-4f06-9ece-9ab7cdaee568

  • Property mode set to 100644
File size: 11.9 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 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
46/* if prefix is omitted: if f_optional return NULL, otherwise use longjmp */
47static prefix *
48read_prefix_(bool f_optional, bool fSurvey, bool fSuspectTypo, bool fAllowRoot)
49{
50   prefix *back_ptr, *ptr;
51   char *name;
52   size_t name_len = 32;
53   size_t i;
54   bool fNew;
55   bool fImplicitPrefix = fTrue;
56   int depth = -1;
57
58   skipblanks();
59#ifndef NO_DEPRECATED
60   if (isRoot(ch)) {
61      if (!fAllowRoot) {
62         compile_error_skip(/*ROOT is deprecated*/25);
63         LONGJMP(file.jbSkipLine);
64      }
65      if (root_depr_count < 5) {
66         compile_warning(/*ROOT is deprecated*/25);
67         if (++root_depr_count == 5)
68            compile_warning(/*Further uses of this deprecated feature will not be reported*/95);
69      }
70      nextch();
71      ptr = root;
72      if (!isNames(ch)) {
73         if (!isSep(ch)) return ptr;
74         /* Allow optional SEPARATOR after ROOT */
75         nextch();
76      }
77      fImplicitPrefix = fFalse;
78   } else {
79      ptr = pcs->Prefix;
80   }
81#else
82   ptr = pcs->Prefix;
83#endif
84
85   i = 0;
86   name = NULL;
87   do {
88      fNew = fFalse;
89      if (isSep(ch)) fImplicitPrefix = fFalse;
90      if (name == NULL) {
91         /* Need a new name buffer */
92         name = osmalloc(name_len);
93      }
94      /* i==0 iff this is the first pass */
95      if (i) {
96         i = 0;
97         nextch();
98      }
99      while (isNames(ch)) {
100         if (i < pcs->Truncate) {
101            /* truncate name */
102            name[i++] = (pcs->Case == LOWER ? tolower(ch) :
103                         (pcs->Case == OFF ? ch : toupper(ch)));
104            if (i >= name_len) {
105               name_len = name_len + name_len;
106               name = osrealloc(name, name_len);
107            }
108         }
109         nextch();
110      }
111      if (i == 0) {
112         osfree(name);
113         if (!f_optional) {
114            if (isEol(ch)) {
115               if (fSurvey) {
116                  compile_error_skip(/*Expecting survey name*/89);
117               } else {
118                  compile_error_skip(/*Expecting station name*/28);
119               }
120            } else {
121               compile_error_skip(/*Character `%c' not allowed in station name (use *SET NAMES to set allowed characters)*/7, ch);
122            }
123            LONGJMP(file.jbSkipLine);
124         }
125         return (prefix *)NULL;
126      }
127
128      name[i++] = '\0';
129
130      back_ptr = ptr;
131      ptr = ptr->down;
132      if (ptr == NULL) {
133         /* Special case first time around at each level */
134         name = osrealloc(name, i);
135         ptr = osnew(prefix);
136         ptr->ident = name;
137         name = NULL;
138         ptr->right = ptr->down = NULL;
139         ptr->pos = NULL;
140         ptr->shape = 0;
141         ptr->stn = NULL;
142         ptr->up = back_ptr;
143         ptr->filename = file.filename;
144         ptr->line = file.line;
145         ptr->min_export = ptr->max_export = 0;
146         ptr->sflags = BIT(SFLAGS_SURVEY);
147         if (fSuspectTypo && !fImplicitPrefix)
148            ptr->sflags |= BIT(SFLAGS_SUSPECTTYPO);
149         back_ptr->down = ptr;
150         fNew = fTrue;
151      } else {
152         /* Use caching to speed up adding an increasing sequence to a
153          * large survey */
154         static prefix *cached_survey = NULL, *cached_station = NULL;
155         prefix *ptrPrev = NULL;
156         int cmp = 1; /* result of strcmp ( -ve for <, 0 for =, +ve for > ) */
157         if (cached_survey == back_ptr) {
158            cmp = strcmp(cached_station->ident, name);
159            if (cmp <= 0) ptr = cached_station;
160         }
161         while (ptr && (cmp = strcmp(ptr->ident, name))<0) {
162            ptrPrev = ptr;
163            ptr = ptr->right;
164         }
165         if (cmp) {
166            /* ie we got to one that was higher, or the end */
167            prefix *newptr;
168            name = osrealloc(name, i);
169            newptr = osnew(prefix);
170            newptr->ident = name;
171            name = NULL;
172            if (ptrPrev == NULL)
173               back_ptr->down = newptr;
174            else
175               ptrPrev->right = newptr;
176            newptr->right = ptr;
177            newptr->down = NULL;
178            newptr->pos = NULL;
179            newptr->shape = 0;
180            newptr->stn = NULL;
181            newptr->up = back_ptr;
182            newptr->filename = file.filename;
183            newptr->line = file.line;
184            newptr->min_export = newptr->max_export = 0;
185            newptr->sflags = BIT(SFLAGS_SURVEY);
186            if (fSuspectTypo && !fImplicitPrefix)
187               newptr->sflags |= BIT(SFLAGS_SUSPECTTYPO);
188            ptr = newptr;
189            fNew = fTrue;
190         }
191         cached_survey = back_ptr;
192         cached_station = ptr;
193      }
194      depth++;
195      f_optional = fFalse; /* disallow after first level */
196   } while (isSep(ch));
197   if (name) osfree(name);
198
199   /* don't warn about a station that is referred to twice */
200   if (!fNew) ptr->sflags &= ~BIT(SFLAGS_SUSPECTTYPO);
201
202   if (fNew) {
203      /* fNew means SFLAGS_SURVEY is currently set */
204      SVX_ASSERT(TSTBIT(ptr->sflags, SFLAGS_SURVEY));
205      if (!fSurvey) {
206         ptr->sflags &= ~BIT(SFLAGS_SURVEY);
207         if (TSTBIT(pcs->infer, INFER_EXPORTS)) ptr->min_export = USHRT_MAX;
208      }
209   } else {
210      /* check that the same name isn't being used for a survey and station */
211      if (fSurvey ^ TSTBIT(ptr->sflags, SFLAGS_SURVEY)) {
212         compile_error(/*`%s' can't be both a station and a survey*/27,
213                       sprint_prefix(ptr));
214      }
215      if (!fSurvey && TSTBIT(pcs->infer, INFER_EXPORTS)) ptr->min_export = USHRT_MAX;
216   }
217
218   /* check the export level */
219#if 0
220   printf("R min %d max %d depth %d pfx %s\n",
221          ptr->min_export, ptr->max_export, depth, sprint_prefix(ptr));
222#endif
223   if (ptr->min_export == 0 || ptr->min_export == USHRT_MAX) {
224      if (depth > ptr->max_export) ptr->max_export = depth;
225   } else if (ptr->max_export < depth) {
226      const char *filename_store = file.filename;
227      unsigned int line_store = file.line;
228      prefix *survey = ptr;
229      char *s;
230      int level;
231      for (level = ptr->max_export + 1; level; level--) {
232         survey = survey->up;
233         SVX_ASSERT(survey);
234      }
235      s = osstrdup(sprint_prefix(survey));
236      if (survey->filename) {
237         file.filename = survey->filename;
238         file.line = survey->line;
239      }
240      compile_error(/*Station `%s' not exported from survey `%s'*/26,
241                    sprint_prefix(ptr), s);
242      if (survey->filename) {
243         file.filename = filename_store;
244         file.line = line_store;
245      }
246      osfree(s);
247#if 0
248      printf(" *** pfx %s warning not exported enough depth %d "
249             "ptr->max_export %d\n", sprint_prefix(ptr),
250             depth, ptr->max_export);
251#endif
252   }
253   return ptr;
254}
255
256/* if prefix is omitted: if f_optional return NULL, otherwise use longjmp */
257extern prefix *
258read_prefix_survey(bool f_optional, bool fAllowRoot)
259{
260   return read_prefix_(f_optional, fTrue, fFalse, fAllowRoot);
261}
262
263/* if prefix is omitted: if f_optional return NULL, otherwise use longjmp */
264extern prefix *
265read_prefix_stn(bool f_optional, bool fAllowRoot)
266{
267   return read_prefix_(f_optional, fFalse, fFalse, fAllowRoot);
268}
269
270/* if prefix is omitted: if f_optional return NULL, otherwise use longjmp */
271/* Same as read_prefix_stn but implicit checks are made */
272extern prefix *
273read_prefix_stn_check_implicit(bool f_optional, bool fAllowRoot)
274{
275   return read_prefix_(f_optional, fFalse, fTrue, fAllowRoot);
276}
277
278/* if numeric expr is omitted: if f_optional return HUGE_REAL, else longjmp */
279static real
280read_number(bool f_optional)
281{
282   bool fPositive, fDigits = fFalse;
283   real n = (real)0.0;
284   filepos fp;
285   int ch_old;
286
287   get_pos(&fp);
288   ch_old = ch;
289   fPositive = !isMinus(ch);
290   if (isSign(ch)) nextch();
291
292   while (isdigit(ch)) {
293      n = n * (real)10.0 + (char)(ch - '0');
294      nextch();
295      fDigits = fTrue;
296   }
297
298   if (isDecimal(ch)) {
299      real mult = (real)1.0;
300      nextch();
301      while (isdigit(ch)) {
302         mult *= (real).1;
303         n += (char)(ch - '0') * mult;
304         fDigits = fTrue;
305         nextch();
306      }
307   }
308
309   /* !'fRead' => !fDigits so fDigits => 'fRead' */
310   if (fDigits) return (fPositive ? n : -n);
311
312   /* didn't read a valid number.  If it's optional, reset filepos & return */
313   if (f_optional) {
314      set_pos(&fp);
315      return HUGE_REAL;
316   }
317
318   if (isOmit(ch_old)) {
319      compile_error(/*Field may not be omitted*/8);
320   } else {
321      compile_error_token(/*Expecting numeric field, found `%s'*/9);
322   }
323   LONGJMP(file.jbSkipLine);
324   return 0.0; /* for brain-fried compilers */
325}
326
327extern real
328read_numeric(bool f_optional, int *p_n_readings)
329{
330   size_t n_readings = 0;
331   real tot = (real)0.0;
332
333   skipblanks();
334   if (!isOpen(ch)) {
335      real r = read_number(f_optional);
336      if (p_n_readings) *p_n_readings = (r == HUGE_REAL ? 0 : 1);
337      return r;
338   }
339   nextch();
340
341   skipblanks();
342   do {
343      tot += read_number(fFalse);
344      ++n_readings;
345      skipblanks();
346   } while (!isClose(ch));
347   nextch();
348
349   if (p_n_readings) *p_n_readings = n_readings;
350   /* FIXME: special averaging for bearings ... */
351   /* And for percentage gradient */
352   return tot / n_readings;
353}
354
355/* read numeric expr or omit (return HUGE_REAL); else longjmp */
356extern real
357read_numeric_or_omit(int *p_n_readings)
358{
359   real v = read_numeric(fTrue, p_n_readings);
360   if (v == HUGE_REAL) {
361      if (!isOmit(ch)) {
362         compile_error_token(/*Expecting numeric field, found `%s'*/9);
363         LONGJMP(file.jbSkipLine);
364         return 0.0; /* for brain-fried compilers */
365      }
366      nextch();
367   }
368   return v;
369}
370
371/* Don't skip blanks, variable error code */
372static unsigned int
373read_uint_internal(int errmsg, filepos *fp)
374{
375   unsigned int n = 0;
376   if (!isdigit(ch)) {
377      if (fp) set_pos(fp);
378      compile_error_token(errmsg);
379      LONGJMP(file.jbSkipLine);
380   }
381   while (isdigit(ch)) {
382      n = n * 10 + (char)(ch - '0');
383      nextch();
384   }
385   return n;
386}
387
388extern unsigned int
389read_uint(void)
390{
391   skipblanks();
392   return read_uint_internal(/*Expecting numeric field, found `%s'*/9, NULL);
393}
394
395extern void
396read_string(char **pstr, int *plen)
397{
398   s_zero(pstr);
399
400   skipblanks();
401   if (ch == '\"') {
402      /* String quoted in "" */
403      nextch();
404      while (1) {
405         if (isEol(ch)) {
406            compile_error_skip(/*Missing &quot;*/69);
407            return;
408         }
409
410         if (ch == '\"') break;
411
412         s_catchar(pstr, plen, ch);
413         nextch();
414      }
415   } else {
416      /* Unquoted string */
417      while (1) {
418         if (isEol(ch) || isComm(ch)) {
419            if (!*pstr || !(*pstr)[0]) {
420               compile_error_skip(/*Expecting string field*/121);
421            }
422            return;
423         }
424
425         if (isBlank(ch)) break;
426
427         s_catchar(pstr, plen, ch);
428         nextch();
429      }
430   }
431
432   nextch();
433}
434
435extern void
436read_date(int *py, int *pm, int *pd)
437{
438   int y = 0, m = 0, d = 0;
439   filepos fp;
440
441   skipblanks();
442
443   get_pos(&fp);
444   y = read_uint_internal(/*Expecting date, found `%s'*/198, &fp);
445   /* Two digit year is 19xx. */
446   if (y < 100) y += 1900;
447   if (y < 1900 || y > 2078) {
448      compile_error_skip(/*Invalid year (< 1900 or > 2078)*/58);
449      LONGJMP(file.jbSkipLine);
450      return; /* for brain-fried compilers */
451   }
452   if (ch == '.') {
453      nextch();
454      m = read_uint_internal(/*Expecting date, found `%s'*/198, &fp);
455      if (m < 1 || m > 12) {
456         compile_error_skip(/*Invalid month*/86);
457         LONGJMP(file.jbSkipLine);
458         return; /* for brain-fried compilers */
459      }
460      if (ch == '.') {
461         nextch();
462         d = read_uint_internal(/*Expecting date, found `%s'*/198, &fp);
463         if (d < 1 || d > last_day(y, m)) {
464            compile_error_skip(/*Invalid day of the month*/87);
465            LONGJMP(file.jbSkipLine);
466            return; /* for brain-fried compilers */
467         }
468      }
469   }
470   if (py) *py = y;
471   if (pm) *pm = m;
472   if (pd) *pd = d;
473}
Note: See TracBrowser for help on using the repository browser.