source: git/src/readval.c @ 728f877

RELEASE/1.0
Last change on this file since 728f877 was 728f877, checked in by Olly Betts <olly@…>, 13 years ago

Backport change from 1.2.0:
src/readval.c: Fix comment typo.

git-svn-id: file:///home/survex-svn/survex/branches/1.0@3720 4b37db11-9a0c-4f06-9ece-9ab7cdaee568

  • Property mode set to 100644
File size: 11.8 KB
Line 
1/* readval.c
2 * Routines to read a prefix or number from the current input file
3 * Copyright (C) 1991-2002 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#ifdef CHASM3DX
135         if (fUseNewFormat) {
136            ptr = osnew(prefix);
137         } else {
138            /* only allocate the part of the structure we need... */
139            ptr = (prefix *)osmalloc(offsetof(prefix, twig_link));
140         }
141#else
142         ptr = osnew(prefix);
143#endif
144         ptr->ident = name;
145         name = NULL;
146         ptr->right = ptr->down = NULL;
147         ptr->pos = NULL;
148         ptr->shape = 0;
149         ptr->stn = NULL;
150         ptr->up = back_ptr;
151         ptr->filename = NULL;
152         ptr->min_export = ptr->max_export = 0;
153         ptr->sflags = BIT(SFLAGS_SURVEY);
154         if (fSuspectTypo && !fImplicitPrefix)
155            ptr->sflags |= BIT(SFLAGS_SUSPECTTYPO);
156         back_ptr->down = ptr;
157         fNew = fTrue;
158#ifdef CHASM3DX
159         if (fUseNewFormat) {
160            create_twig(ptr, file.filename);
161         }
162#endif
163      } else {
164         /* Use caching to speed up adding an increasing sequence to a
165          * large survey */
166         static prefix *cached_survey = NULL, *cached_station = NULL;
167         prefix *ptrPrev = NULL;
168         int cmp = 1; /* result of strcmp ( -ve for <, 0 for =, +ve for > ) */
169         if (cached_survey == back_ptr) {
170            cmp = strcmp(cached_station->ident, name);
171            if (cmp <= 0) ptr = cached_station;
172         }
173         while (ptr && (cmp = strcmp(ptr->ident, name))<0) {
174            ptrPrev = ptr;
175            ptr = ptr->right;
176         }
177         if (cmp) {
178            /* ie we got to one that was higher, or the end */
179            prefix *newptr;
180            name = osrealloc(name, i);
181#ifdef CHASM3DX
182            if (fUseNewFormat) {
183               newptr = osnew(prefix);
184            } else {
185               /* only allocate the part of the structure we need... */
186               newptr = (prefix *)osmalloc(offsetof(prefix, twig_link));
187            }
188#else
189            newptr = osnew(prefix);
190#endif
191            newptr->ident = name;
192            name = NULL;
193            if (ptrPrev == NULL)
194               back_ptr->down = newptr;
195            else
196               ptrPrev->right = newptr;
197            newptr->right = ptr;
198            newptr->down = NULL;
199            newptr->pos = NULL;
200            newptr->shape = 0;
201            newptr->stn = NULL;
202            newptr->up = back_ptr;
203            newptr->filename = NULL;
204            newptr->min_export = newptr->max_export = 0;
205            newptr->sflags = BIT(SFLAGS_SURVEY);
206            if (fSuspectTypo && !fImplicitPrefix)
207               newptr->sflags |= BIT(SFLAGS_SUSPECTTYPO);
208            ptr = newptr;
209            fNew = fTrue;
210#ifdef CHASM3DX
211            if (fUseNewFormat) {
212               create_twig(ptr, file.filename);
213            }
214#endif
215         }
216         cached_survey = back_ptr;
217         cached_station = ptr;
218      }
219      depth++;
220      f_optional = fFalse; /* disallow after first level */
221   } while (isSep(ch));
222   if (name) osfree(name);
223
224   /* don't warn about a station that is referred to twice */
225   if (!fNew) ptr->sflags &= ~BIT(SFLAGS_SUSPECTTYPO);
226
227   if (fNew) {
228      /* fNew means SFLAGS_SURVEY is currently set */
229      SVX_ASSERT(TSTBIT(ptr->sflags, SFLAGS_SURVEY));
230      if (!fSurvey) {
231         ptr->sflags &= ~BIT(SFLAGS_SURVEY);
232         if (TSTBIT(pcs->infer, INFER_EXPORTS)) ptr->min_export = USHRT_MAX;
233      }
234   } else {
235      /* check that the same name isn't being used for a survey and station */
236      if (fSurvey ^ TSTBIT(ptr->sflags, SFLAGS_SURVEY)) {
237         compile_error(/*`%s' can't be both a station and a survey*/27,
238                       sprint_prefix(ptr));
239      }
240      if (!fSurvey && TSTBIT(pcs->infer, INFER_EXPORTS)) ptr->min_export = USHRT_MAX;
241   }
242
243   /* check the export level */
244#if 0
245   printf("R min %d max %d depth %d pfx %s\n",
246          ptr->min_export, ptr->max_export, depth, sprint_prefix(ptr));
247#endif
248   if (ptr->min_export == 0 || ptr->min_export == USHRT_MAX) {
249      if (depth > ptr->max_export) ptr->max_export = depth;
250   } else if (ptr->max_export < depth) {
251      const char *filename_store = file.filename;
252      unsigned int line_store = file.line;
253      prefix *survey = ptr;
254      char *s;
255      int level;
256      for (level = ptr->max_export + 1; level; level--) {
257         survey = survey->up;
258         SVX_ASSERT(survey);
259      }
260      s = osstrdup(sprint_prefix(survey));
261      if (survey->filename) {
262         file.filename = survey->filename;
263         file.line = survey->line;
264      }
265      compile_error(/*Station `%s' not exported from survey `%s'*/26,
266                    sprint_prefix(ptr), s);
267      if (survey->filename) {
268         file.filename = filename_store;
269         file.line = line_store;
270      }
271      osfree(s);
272#if 0
273      printf(" *** pfx %s warning not exported enough depth %d "
274             "ptr->max_export %d\n", sprint_prefix(ptr),
275             depth, ptr->max_export);
276#endif
277   }
278   return ptr;
279}
280
281/* if prefix is omitted: if f_optional return NULL, otherwise use longjmp */
282extern prefix *
283read_prefix_survey(bool f_optional, bool fAllowRoot)
284{
285   return read_prefix_(f_optional, fTrue, fFalse, fAllowRoot);
286}
287
288/* if prefix is omitted: if f_optional return NULL, otherwise use longjmp */
289extern prefix *
290read_prefix_stn(bool f_optional, bool fAllowRoot)
291{
292   return read_prefix_(f_optional, fFalse, fFalse, fAllowRoot);
293}
294
295/* if prefix is omitted: if f_optional return NULL, otherwise use longjmp */
296/* Same as read_prefix_stn but implicit checks are made */
297extern prefix *
298read_prefix_stn_check_implicit(bool f_optional, bool fAllowRoot)
299{
300   return read_prefix_(f_optional, fFalse, fTrue, fAllowRoot);
301}
302
303/* if numeric expr is omitted: if f_optional return HUGE_REAL, else longjmp */
304static real
305read_number(bool f_optional)
306{
307   bool fPositive, fDigits = fFalse;
308   real n = (real)0.0;
309   filepos fp;
310   int ch_old;
311
312   get_pos(&fp);
313   ch_old = ch;
314   fPositive = !isMinus(ch);
315   if (isSign(ch)) nextch();
316
317   while (isdigit(ch)) {
318      n = n * (real)10.0 + (char)(ch - '0');
319      nextch();
320      fDigits = fTrue;
321   }
322
323   if (isDecimal(ch)) {
324      real mult = (real)1.0;
325      nextch();
326      while (isdigit(ch)) {
327         mult *= (real).1;
328         n += (char)(ch - '0') * mult;
329         fDigits = fTrue;
330         nextch();
331      }
332   }
333
334   /* !'fRead' => !fDigits so fDigits => 'fRead' */
335   if (fDigits) return (fPositive ? n : -n);
336
337   /* didn't read a valid number.  If it's optional, reset filepos & return */
338   if (f_optional) {
339      set_pos(&fp);
340      return HUGE_REAL;
341   }
342
343   if (isOmit(ch_old)) {
344      compile_error(/*Field may not be omitted*/8);
345   } else {
346      compile_error_token(/*Expecting numeric field, found `%s'*/9);
347   }
348   LONGJMP(file.jbSkipLine);
349   return 0.0; /* for brain-fried compilers */
350}
351
352extern real
353read_numeric(bool f_optional, int *p_n_readings)
354{
355   size_t n_readings = 0;
356   real tot = (real)0.0;
357
358   skipblanks();
359   if (!isOpen(ch)) {
360      real r = read_number(f_optional);
361      if (p_n_readings) *p_n_readings = (r == HUGE_REAL ? 0 : 1);
362      return r;
363   }
364   nextch();
365
366   skipblanks();
367   do {
368      tot += read_number(fFalse);
369      ++n_readings;
370      skipblanks();
371   } while (!isClose(ch));
372   nextch();
373
374   if (p_n_readings) *p_n_readings = n_readings;
375   /* FIXME: special averaging for bearings ... */
376   /* And for percentage gradient */
377   return tot / n_readings;
378}
379
380/* read numeric expr or omit (return HUGE_REAL); else longjmp */
381extern real
382read_numeric_or_omit(int *p_n_readings)
383{
384   real v = read_numeric(fTrue, p_n_readings);
385   if (v == HUGE_REAL) {
386      if (!isOmit(ch)) {
387         compile_error_token(/*Expecting numeric field, found `%s'*/9);
388         LONGJMP(file.jbSkipLine);
389         return 0.0; /* for brain-fried compilers */
390      }
391      nextch();
392   }
393   return v;
394}
395
396/* Don't skip blanks, variable error code */
397static unsigned int
398read_uint_internal(int errmsg, filepos *fp)
399{
400   unsigned int n = 0;
401   if (!isdigit(ch)) {
402      if (fp) set_pos(fp);
403      compile_error_token(errmsg);
404      LONGJMP(file.jbSkipLine);
405   }
406   while (isdigit(ch)) {
407      n = n * 10 + (char)(ch - '0');
408      nextch();
409   }
410   return n;
411}
412
413extern unsigned int
414read_uint(void)
415{
416   skipblanks();
417   return read_uint_internal(/*Expecting numeric field, found `%s'*/9, NULL);
418}
419
420extern void
421read_string(char **pstr, int *plen)
422{
423   s_zero(pstr);
424
425   skipblanks();
426   if (ch == '\"') {
427      /* String quoted in "" */
428      nextch();
429      while (1) {
430         if (isEol(ch)) {
431            compile_error_skip(/*Missing &quot;*/69);
432            return;
433         }
434
435         if (ch == '\"') break;
436
437         s_catchar(pstr, plen, ch);
438         nextch();
439      }
440   } else {
441      /* Unquoted string */
442      while (1) {
443         if (isEol(ch) || isComm(ch)) {
444            if (!*pstr || !(*pstr)[0]) {
445               compile_error_skip(/*Expecting string field*/121);
446            }
447            return;
448         }
449
450         if (isBlank(ch)) break;
451
452         s_catchar(pstr, plen, ch);
453         nextch();
454      }
455   }
456
457   nextch();
458}
459
460extern void
461read_date(void)
462{
463   int y = 0, m = 0, d = 0;
464   filepos fp;
465
466   skipblanks();
467
468   get_pos(&fp);
469   y = read_uint_internal(/*Expecting date, found `%s'*/198, &fp);
470   if (ch == '.') {
471      nextch();
472      m = read_uint_internal(/*Expecting date, found `%s'*/198, &fp);
473      if (ch == '.') {
474         nextch();
475         d = read_uint_internal(/*Expecting date, found `%s'*/198, &fp);
476      }
477   }
478}
Note: See TracBrowser for help on using the repository browser.