source: git/src/readval.c @ d333899

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

Give an error if a cross-section is specified for a station which doesn't
exist.

git-svn-id: file:///home/survex-svn/survex/branches/survex-1_1@3239 4b37db11-9a0c-4f06-9ece-9ab7cdaee568

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