source: git/src/readval.c @ be4f785

stereo-2025
Last change on this file since be4f785 was c77682a, checked in by Olly Betts <olly@…>, 4 months ago

Stop reporting node stats

These are kind of interesting, but since the advent of surveying with
Disto-X and similar devices which make it quick to shoot multiple
splay legs from each station the number of larger order nodes has
increased and this information is now quite verbose and any utility
it had has substantially declined.

If they're still wanted they could make a reappearance in the future in
aven, with splays excluded when counting the number of legs at each
station.

Fixes #86, reported by Wookey.

  • Property mode set to 100644
File size: 26.7 KB
Line 
1/* readval.c
2 * Routines to read a prefix or number from the current input file
3 * Copyright (C) 1991-2024 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#include <config.h>
21
22#include <limits.h>
23#include <stddef.h> /* for offsetof */
24
25#include "cavern.h"
26#include "commands.h" /* For match_tok(), etc */
27#include "date.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
37int root_depr_count = 0;
38
39static prefix *
40new_anon_station(void)
41{
42    prefix *name = osnew(prefix);
43    name->pos = NULL;
44    name->ident.p = NULL;
45    name->stn = NULL;
46    name->up = pcs->Prefix;
47    name->down = NULL;
48    name->filename = file.filename;
49    name->line = file.line;
50    name->min_export = name->max_export = 0;
51    name->sflags = BIT(SFLAGS_ANON);
52    /* Keep linked list of anon stations for node stats. */
53    name->right = anon_list;
54    anon_list = name;
55    return name;
56}
57
58static char *id = NULL;
59static size_t id_len = 0;
60
61/* if prefix is omitted: if PFX_OPT set return NULL, otherwise use longjmp */
62extern prefix *
63read_prefix(unsigned pfx_flags)
64{
65   bool f_optional = !!(pfx_flags & PFX_OPT);
66   bool fSurvey = !!(pfx_flags & PFX_SURVEY);
67   bool fSuspectTypo = !!(pfx_flags & PFX_SUSPECT_TYPO);
68   prefix *back_ptr, *ptr;
69   size_t i;
70   bool fNew;
71   bool fImplicitPrefix = true;
72   int depth = -1;
73   filepos here;
74   filepos fp_firstsep;
75
76   skipblanks();
77   get_pos(&here);
78#ifndef NO_DEPRECATED
79   if (isRoot(ch)) {
80      if (!(pfx_flags & PFX_ALLOW_ROOT)) {
81         compile_diagnostic(DIAG_ERR|DIAG_COL, /*ROOT is deprecated*/25);
82         longjmp(jbSkipLine, 1);
83      }
84      if (root_depr_count < 5) {
85         compile_diagnostic(DIAG_WARN|DIAG_COL, /*ROOT is deprecated*/25);
86         if (++root_depr_count == 5)
87            compile_diagnostic(DIAG_INFO, /*Further uses of this deprecated feature will not be reported*/95);
88      }
89      nextch();
90      ptr = root;
91      if (!isNames(ch)) {
92         if (!isSep(ch)) return ptr;
93         /* Allow optional SEPARATOR after ROOT */
94         get_pos(&fp_firstsep);
95         nextch();
96      }
97      fImplicitPrefix = false;
98#else
99   if (0) {
100#endif
101   } else {
102      if ((pfx_flags & PFX_ANON) &&
103          (isSep(ch) || (pcs->dash_for_anon_wall_station && ch == '-'))) {
104         int first_ch = ch;
105         nextch();
106         if (isBlank(ch) || isComm(ch) || isEol(ch)) {
107            if (!isSep(first_ch))
108               goto anon_wall_station;
109            /* A single separator alone ('.' by default) is an anonymous
110             * station which is on a point inside the passage and implies
111             * the leg to it is a splay.
112             */
113            if (TSTBIT(pcs->flags, FLAGS_ANON_ONE_END)) {
114               set_pos(&here);
115               compile_diagnostic(DIAG_ERR|DIAG_WORD, /*Can't have a leg between two anonymous stations*/3);
116               longjmp(jbSkipLine, 1);
117            }
118            pcs->flags |= BIT(FLAGS_ANON_ONE_END) | BIT(FLAGS_IMPLICIT_SPLAY);
119            return new_anon_station();
120         }
121         if (isSep(first_ch) && ch == first_ch) {
122            nextch();
123            if (isBlank(ch) || isComm(ch) || isEol(ch)) {
124               /* A double separator ('..' by default) is an anonymous station
125                * which is on the wall and implies the leg to it is a splay.
126                */
127               prefix * pfx;
128anon_wall_station:
129               if (TSTBIT(pcs->flags, FLAGS_ANON_ONE_END)) {
130                  set_pos(&here);
131                  compile_diagnostic(DIAG_ERR|DIAG_WORD, /*Can't have a leg between two anonymous stations*/3);
132                  longjmp(jbSkipLine, 1);
133               }
134               pcs->flags |= BIT(FLAGS_ANON_ONE_END) | BIT(FLAGS_IMPLICIT_SPLAY);
135               pfx = new_anon_station();
136               pfx->sflags |= BIT(SFLAGS_WALL);
137               return pfx;
138            }
139            if (ch == first_ch) {
140               nextch();
141               if (isBlank(ch) || isComm(ch) || isEol(ch)) {
142                  /* A triple separator ('...' by default) is an anonymous
143                   * station, but otherwise not handled specially (e.g. for
144                   * a single leg down an unexplored side passage to a station
145                   * which isn't refindable).
146                   */
147                  if (TSTBIT(pcs->flags, FLAGS_ANON_ONE_END)) {
148                     set_pos(&here);
149                     compile_diagnostic(DIAG_ERR|DIAG_WORD, /*Can't have a leg between two anonymous stations*/3);
150                     longjmp(jbSkipLine, 1);
151                  }
152                  pcs->flags |= BIT(FLAGS_ANON_ONE_END);
153                  return new_anon_station();
154               }
155            }
156         }
157         set_pos(&here);
158      }
159      ptr = pcs->Prefix;
160   }
161
162   i = 0;
163   do {
164      fNew = false;
165      if (id == NULL) {
166         /* Allocate buffer the first time */
167         id_len = 256;
168         id = osmalloc(id_len);
169      }
170      /* i==0 iff this is the first pass */
171      if (i) {
172         i = 0;
173         nextch();
174      }
175      while (isNames(ch)) {
176         if (i < pcs->Truncate) {
177            /* truncate name */
178            id[i++] = (pcs->Case == LOWER ? tolower(ch) :
179                       (pcs->Case == OFF ? ch : toupper(ch)));
180            if (i >= id_len) {
181               id_len *= 2;
182               id = osrealloc(id, id_len);
183            }
184         }
185         nextch();
186      }
187      if (isSep(ch)) {
188         fImplicitPrefix = false;
189         get_pos(&fp_firstsep);
190      }
191      if (i == 0) {
192         if (!f_optional) {
193            if (isEol(ch)) {
194               if (fSurvey) {
195                  compile_diagnostic(DIAG_ERR|DIAG_COL, /*Expecting survey name*/89);
196               } else {
197                  compile_diagnostic(DIAG_ERR|DIAG_COL, /*Expecting station name*/28);
198               }
199            } else {
200               /* TRANSLATORS: Here "station" is a survey station, not a train station. */
201               compile_diagnostic(DIAG_ERR|DIAG_COL, /*Character “%c” not allowed in station name (use *SET NAMES to set allowed characters)*/7, ch);
202            }
203            longjmp(jbSkipLine, 1);
204         }
205         return (prefix *)NULL;
206      }
207
208      id[i++] = '\0';
209
210      back_ptr = ptr;
211      ptr = ptr->down;
212      if (ptr == NULL) {
213         /* Special case first time around at each level */
214         ptr = osnew(prefix);
215         ptr->sflags = BIT(SFLAGS_SURVEY);
216         if (i <= sizeof(ptr->ident.i)) {
217             memcpy(ptr->ident.i, id, i);
218             ptr->sflags |= BIT(SFLAGS_IDENT_INLINE);
219         } else {
220             char *new_id = osmalloc(i);
221             memcpy(new_id, id, i);
222             ptr->ident.p = new_id;
223         }
224         ptr->right = ptr->down = NULL;
225         ptr->pos = NULL;
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         if (fSuspectTypo && !fImplicitPrefix)
232            ptr->sflags |= BIT(SFLAGS_SUSPECTTYPO);
233         back_ptr->down = ptr;
234         fNew = true;
235      } else {
236         /* Use caching to speed up adding an increasing sequence to a
237          * large survey */
238         static prefix *cached_survey = NULL, *cached_station = NULL;
239         prefix *ptrPrev = NULL;
240         int cmp = 1; /* result of strcmp ( -ve for <, 0 for =, +ve for > ) */
241         if (cached_survey == back_ptr) {
242            cmp = strcmp(prefix_ident(cached_station), id);
243            if (cmp <= 0) ptr = cached_station;
244         }
245         while (ptr && (cmp = strcmp(prefix_ident(ptr), id)) < 0) {
246            ptrPrev = ptr;
247            ptr = ptr->right;
248         }
249         if (cmp) {
250            /* ie we got to one that was higher, or the end */
251            prefix *newptr = osnew(prefix);
252            newptr->sflags = BIT(SFLAGS_SURVEY);
253            if (strlen(id) < sizeof(newptr->ident.i)) {
254                memcpy(newptr->ident.i, id, i);
255                newptr->sflags |= BIT(SFLAGS_IDENT_INLINE);
256            } else {
257                char *new_id = osmalloc(i);
258                memcpy(new_id, id, i);
259                newptr->ident.p = new_id;
260            }
261            if (ptrPrev == NULL)
262               back_ptr->down = newptr;
263            else
264               ptrPrev->right = newptr;
265            newptr->right = ptr;
266            newptr->down = NULL;
267            newptr->pos = NULL;
268            newptr->stn = NULL;
269            newptr->up = back_ptr;
270            newptr->filename = file.filename;
271            newptr->line = file.line;
272            newptr->min_export = newptr->max_export = 0;
273            if (fSuspectTypo && !fImplicitPrefix)
274               newptr->sflags |= BIT(SFLAGS_SUSPECTTYPO);
275            ptr = newptr;
276            fNew = true;
277         }
278         cached_survey = back_ptr;
279         cached_station = ptr;
280      }
281      depth++;
282      f_optional = false; /* disallow after first level */
283      if (isSep(ch)) {
284         get_pos(&fp_firstsep);
285         if (!TSTBIT(ptr->sflags, SFLAGS_SURVEY)) {
286            /* TRANSLATORS: Here "station" is a survey station, not a train station.
287             *
288             * Here "survey" is a "cave map" rather than list of questions - it should be
289             * translated to the terminology that cavers using the language would use.
290             */
291            compile_diagnostic(DIAG_ERR|DIAG_FROM(here), /*“%s” can’t be both a station and a survey*/27,
292                               sprint_prefix(ptr));
293         }
294      }
295   } while (isSep(ch));
296
297   /* don't warn about a station that is referred to twice */
298   if (!fNew) ptr->sflags &= ~BIT(SFLAGS_SUSPECTTYPO);
299
300   if (fNew) {
301      /* fNew means SFLAGS_SURVEY is currently set */
302      SVX_ASSERT(TSTBIT(ptr->sflags, SFLAGS_SURVEY));
303      if (!fSurvey) {
304         ptr->sflags &= ~BIT(SFLAGS_SURVEY);
305         if (TSTBIT(pcs->infer, INFER_EXPORTS)) ptr->min_export = USHRT_MAX;
306      }
307   } else {
308      /* check that the same name isn't being used for a survey and station */
309      if (fSurvey ^ TSTBIT(ptr->sflags, SFLAGS_SURVEY)) {
310         /* TRANSLATORS: Here "station" is a survey station, not a train station.
311          *
312          * Here "survey" is a "cave map" rather than list of questions - it should be
313          * translated to the terminology that cavers using the language would use.
314          */
315         compile_diagnostic(DIAG_ERR|DIAG_FROM(here), /*“%s” can’t be both a station and a survey*/27,
316                            sprint_prefix(ptr));
317      }
318      if (!fSurvey && TSTBIT(pcs->infer, INFER_EXPORTS)) ptr->min_export = USHRT_MAX;
319   }
320
321   /* check the export level */
322#if 0
323   printf("R min %d max %d depth %d pfx %s\n",
324          ptr->min_export, ptr->max_export, depth, sprint_prefix(ptr));
325#endif
326   if (ptr->min_export == 0 || ptr->min_export == USHRT_MAX) {
327      if (depth > ptr->max_export) ptr->max_export = depth;
328   } else if (ptr->max_export < depth) {
329      prefix *survey = ptr;
330      char *s;
331      const char *p;
332      int level;
333      for (level = ptr->max_export + 1; level; level--) {
334         survey = survey->up;
335         SVX_ASSERT(survey);
336      }
337      s = osstrdup(sprint_prefix(survey));
338      p = sprint_prefix(ptr);
339      if (survey->filename) {
340         compile_diagnostic_pfx(DIAG_ERR, survey,
341                                /*Station “%s” not exported from survey “%s”*/26,
342                                p, s);
343      } else {
344         compile_diagnostic(DIAG_ERR, /*Station “%s” not exported from survey “%s”*/26, p, s);
345      }
346      osfree(s);
347#if 0
348      printf(" *** pfx %s warning not exported enough depth %d "
349             "ptr->max_export %d\n", sprint_prefix(ptr),
350             depth, ptr->max_export);
351#endif
352   }
353   if (!fImplicitPrefix && (pfx_flags & PFX_WARN_SEPARATOR)) {
354      filepos fp_tmp;
355      get_pos(&fp_tmp);
356      set_pos(&fp_firstsep);
357      compile_diagnostic(DIAG_WARN|DIAG_COL, /*Separator in survey name*/392);
358      set_pos(&fp_tmp);
359   }
360   return ptr;
361}
362
363char *
364read_walls_prefix(void)
365{
366    string name = S_INIT;
367    skipblanks();
368    if (!isNames(ch))
369        return NULL;
370    do {
371        s_appendch(&name, ch);
372        nextch();
373    } while (isNames(ch));
374    return s_steal(&name);
375}
376
377prefix *
378read_walls_station(char * const walls_prefix[3], bool anon_allowed, bool *p_new)
379{
380    if (p_new) *p_new = false;
381//    bool f_optional = false; //!!(pfx_flags & PFX_OPT);
382//    bool fSuspectTypo = false; //!!(pfx_flags & PFX_SUSPECT_TYPO);
383//    prefix *back_ptr, *ptr;
384    string component = S_INIT;
385//    size_t i;
386//    bool fNew;
387//    bool fImplicitPrefix = true;
388//    int depth = -1;
389//    filepos fp_firstsep;
390
391    filepos fp;
392    get_pos(&fp);
393
394    skipblanks();
395    if (anon_allowed && ch == '-') {
396        // - or -- is an anonymous wall point in a shot, but in #Fix they seem
397        // to just be treated as ordinary station names.
398        // FIXME: Issue warning for such a useless station?
399        //
400        // Not yet checked, but you can presumably use - and -- as a prefix
401        // (FIXME check this).
402        nextch();
403        int dashes = 1;
404        if (ch == '-') {
405            ++dashes;
406            nextch();
407        }
408        if (!isNames(ch) && ch != ':') {
409            // An anonymous station implies the leg it is on is a splay.
410            if (TSTBIT(pcs->flags, FLAGS_ANON_ONE_END)) {
411                set_pos(&fp);
412                // Walls also rejects this case.
413                compile_diagnostic(DIAG_ERR|DIAG_TOKEN, /*Can't have a leg between two anonymous stations*/3);
414                longjmp(jbSkipLine, 1);
415            }
416            pcs->flags |= BIT(FLAGS_ANON_ONE_END) | BIT(FLAGS_IMPLICIT_SPLAY);
417            prefix *pfx = new_anon_station();
418            pfx->sflags |= BIT(SFLAGS_WALL);
419            // An anonymous station is always new.
420            if (p_new) *p_new = true;
421            return pfx;
422        }
423        s_appendn(&component, dashes, '-');
424    }
425
426    char *w_prefix[3] = { NULL, NULL, NULL };
427    int explicit_prefix_levels = 0;
428    while (true) {
429        while (isNames(ch)) {
430            s_appendch(&component, ch);
431            nextch();
432        }
433        //printf("component = '%s'\n", s_str(&component));
434        if (ch == ':') {
435            nextch();
436
437            if (++explicit_prefix_levels > 3) {
438                // FIXME Make this a proper error
439                printf("too many prefix levels\n");
440                s_free(&component);
441                for (int i = 0; i < 3; ++i) osfree(w_prefix[i]);
442                longjmp(jbSkipLine, 1);
443            }
444
445            if (!s_empty(&component)) {
446                // printf("w_prefix[%d] = '%s'\n", explicit_prefix_levels - 1, s_str(&component));
447                w_prefix[explicit_prefix_levels - 1] = s_steal(&component);
448            }
449
450            continue;
451        }
452
453        // printf("explicit_prefix_levels=%d %s:%s:%s\n", explicit_prefix_levels, w_prefix[0], w_prefix[1], w_prefix[2]);
454
455        // component is the station name itself.
456        if (s_empty(&component)) {
457            if (explicit_prefix_levels == 0) {
458                compile_diagnostic(DIAG_ERR|DIAG_COL, /*Expecting station name*/28);
459                s_free(&component);
460                for (int i = 0; i < 3; ++i) osfree(w_prefix[i]);
461                longjmp(jbSkipLine, 1);
462            }
463            // Walls allows an empty station name if there's an explicit prefix.
464            // This seems unlikely to be intended, so warn about it.
465            compile_diagnostic(DIAG_WARN|DIAG_COL, /*Expecting station name*/28);
466            // Use a name with a space in so it can't collide with a real
467            // Walls station name.
468            s_append(&component, "empty name");
469        }
470        int len = s_len(&component);
471        char *p = s_steal(&component);
472        // Apply case treatment.
473        switch (pcs->Case) {
474          case LOWER:
475            for (int i = 0; i < len; ++i)
476                p[i] = tolower((unsigned char)p[i]);
477            break;
478          case UPPER:
479            for (int i = 0; i < len; ++i)
480                p[i] = toupper((unsigned char)p[i]);
481            break;
482          case OFF:
483            // Avoid unhandled enum warning.
484            break;
485        }
486
487        prefix *ptr = root;
488        for (int i = 0; i < 4; ++i) {
489            char *name;
490            int sflag = BIT(SFLAGS_SURVEY);
491            if (i == 3) {
492                name = p;
493                sflag = 0;
494            } else {
495                if (i < 3 - explicit_prefix_levels) {
496                    name = walls_prefix[i];
497                    // printf("using walls_prefix[%d] = '%s'\n", 2 - i, name);
498                } else {
499                    name = w_prefix[i - (3 - explicit_prefix_levels)]; // FIXME: Could steal wprefix[i].
500                    // printf("using w_prefix[%d] = '%s'\n", i - (3 - explicit_prefix_levels), name);
501                }
502
503                if (name == NULL) {
504                    // FIXME: This means :X::Y is treated as the same as
505                    // ::X:Y but is that right?  Walls docs don't really
506                    // say.  Need to test (and is they're different then
507                    // probably use a character not valid in Walls station
508                    // names for the empty prefix level (e.g. space or
509                    // `#`).
510                    //
511                    // Also, does Walls allow :::X as a station and
512                    // ::X:Y which would mean X is a station and survey?
513                    // If so, we probably want to keep every empty level.
514                    continue;
515                }
516            }
517            prefix *back_ptr = ptr;
518            ptr = ptr->down;
519            if (ptr == NULL) {
520                /* Special case first time around at each level */
521                /* No need to check if we're at the station level - if the
522                 * prefix is new the station must be. */
523                if (p_new) *p_new = true;
524                ptr = osnew(prefix);
525                ptr->sflags = sflag;
526                if (strlen(name) < sizeof(ptr->ident.i)) {
527                    strcpy(ptr->ident.i, name);
528                    ptr->sflags |= BIT(SFLAGS_IDENT_INLINE);
529                    if (i >= 3) osfree(name);
530                } else {
531                    ptr->ident.p = (i < 3 ? osstrdup(name) : name);
532                }
533                name = NULL;
534                ptr->right = ptr->down = NULL;
535                ptr->pos = NULL;
536                ptr->stn = NULL;
537                ptr->up = back_ptr;
538                ptr->filename = file.filename; // FIXME: Or location of #Prefix, etc for it?
539                ptr->line = file.line; // FIXME: Or location of #Prefix, etc for it?
540                ptr->min_export = ptr->max_export = 0;
541                back_ptr->down = ptr;
542            } else {
543                /* Use caching to speed up adding an increasing sequence to a
544                 * large survey */
545                static prefix *cached_survey = NULL, *cached_station = NULL;
546                prefix *ptrPrev = NULL;
547                int cmp = 1; /* result of strcmp ( -ve for <, 0 for =, +ve for > ) */
548                if (cached_survey == back_ptr) {
549                    cmp = strcmp(prefix_ident(cached_station), name);
550                    if (cmp <= 0) ptr = cached_station;
551                }
552                while (ptr && (cmp = strcmp(prefix_ident(ptr), name))<0) {
553                    ptrPrev = ptr;
554                    ptr = ptr->right;
555                }
556                if (cmp) {
557                    /* ie we got to one that was higher, or the end */
558                    if (p_new) *p_new = true;
559                    prefix *newptr = osnew(prefix);
560                    newptr->sflags = sflag;
561                    if (strlen(name) < sizeof(newptr->ident.i)) {
562                        strcpy(newptr->ident.i, name);
563                        newptr->sflags |= BIT(SFLAGS_IDENT_INLINE);
564                        if (i >= 3) osfree(name);
565                    } else {
566                        newptr->ident.p = (i < 3 ? osstrdup(name) : name);
567                    }
568                    name = NULL;
569                    if (ptrPrev == NULL)
570                        back_ptr->down = newptr;
571                    else
572                        ptrPrev->right = newptr;
573                    newptr->right = ptr;
574                    newptr->down = NULL;
575                    newptr->pos = NULL;
576                    newptr->stn = NULL;
577                    newptr->up = back_ptr;
578                    newptr->filename = file.filename; // FIXME
579                    newptr->line = file.line;
580                    newptr->min_export = newptr->max_export = 0;
581                    ptr = newptr;
582                } else {
583                    ptr->sflags |= sflag;
584                }
585                if (!TSTBIT(ptr->sflags, SFLAGS_SURVEY)) {
586                    ptr->min_export = USHRT_MAX;
587                }
588                cached_survey = back_ptr;
589                cached_station = ptr;
590            }
591            if (name == p) osfree(p);
592        }
593
594        // Do the equivalent of "*infer exports" for Walls stations with an
595        // explicit prefix.
596        if (ptr->min_export == 0 || ptr->min_export == USHRT_MAX) {
597            if (explicit_prefix_levels > ptr->max_export)
598                ptr->max_export = explicit_prefix_levels;
599        }
600
601        for (int i = 0; i < 3; ++i) osfree(w_prefix[i]);
602
603        return ptr;
604    }
605}
606
607/* if numeric expr is omitted: if f_optional return HUGE_REAL, else longjmp */
608real
609read_number(bool f_optional, bool f_unsigned)
610{
611   bool fPositive = true, fDigits = false;
612   real n = (real)0.0;
613   filepos fp;
614   int ch_old;
615
616   get_pos(&fp);
617   ch_old = ch;
618   if (!f_unsigned) {
619      fPositive = !isMinus(ch);
620      if (isSign(ch)) nextch();
621   }
622
623   while (isdigit(ch)) {
624      n = n * (real)10.0 + (char)(ch - '0');
625      nextch();
626      fDigits = true;
627   }
628
629   if (isDecimal(ch)) {
630      real mult = (real)1.0;
631      nextch();
632      while (isdigit(ch)) {
633         mult *= (real).1;
634         n += (char)(ch - '0') * mult;
635         fDigits = true;
636         nextch();
637      }
638   }
639
640   /* !'fRead' => !fDigits so fDigits => 'fRead' */
641   if (fDigits) return (fPositive ? n : -n);
642
643   /* didn't read a valid number.  If it's optional, reset filepos & return */
644   set_pos(&fp);
645   if (f_optional) {
646      return HUGE_REAL;
647   }
648
649   if (isOmit(ch_old)) {
650      compile_diagnostic(DIAG_ERR|DIAG_COL, /*Field may not be omitted*/8);
651   } else {
652      compile_diagnostic_token_show(DIAG_ERR, /*Expecting numeric field, found “%s”*/9);
653   }
654   longjmp(jbSkipLine, 1);
655}
656
657real
658read_quadrant(bool f_optional)
659{
660   enum {
661      POINT_N = 0,
662      POINT_E = 1,
663      POINT_S = 2,
664      POINT_W = 3,
665      POINT_NONE = -1
666   };
667   static const sztok pointtab[] = {
668        {"E", POINT_E },
669        {"N", POINT_N },
670        {"S", POINT_S },
671        {"W", POINT_W },
672        {NULL, POINT_NONE }
673   };
674   static const sztok pointewtab[] = {
675        {"E", POINT_E },
676        {"W", POINT_W },
677        {NULL, POINT_NONE }
678   };
679   if (f_optional && isOmit(ch)) {
680      return HUGE_REAL;
681   }
682   const int quad = 90;
683   filepos fp;
684   get_pos(&fp);
685   get_token_legacy_no_blanks();
686   int first_point = match_tok(pointtab, TABSIZE(pointtab));
687   if (first_point == POINT_NONE) {
688      set_pos(&fp);
689      if (isOmit(ch)) {
690         compile_diagnostic(DIAG_ERR|DIAG_COL, /*Field may not be omitted*/8);
691      }
692      compile_diagnostic_token_show(DIAG_ERR, /*Expecting quadrant bearing, found “%s”*/483);
693      longjmp(jbSkipLine, 1);
694   }
695   real r = read_number(true, true);
696   if (r == HUGE_REAL) {
697      if (isSign(ch) || isDecimal(ch)) {
698         /* Give better errors for S-0E, N+10W, N.E, etc. */
699         set_pos(&fp);
700         compile_diagnostic_token_show(DIAG_ERR, /*Expecting quadrant bearing, found “%s”*/483);
701         longjmp(jbSkipLine, 1);
702      }
703      /* N, S, E or W. */
704      return first_point * quad;
705   }
706   if (first_point == POINT_E || first_point == POINT_W) {
707      set_pos(&fp);
708      compile_diagnostic_token_show(DIAG_ERR, /*Expecting quadrant bearing, found “%s”*/483);
709      longjmp(jbSkipLine, 1);
710   }
711
712   get_token_legacy_no_blanks();
713   int second_point = match_tok(pointewtab, TABSIZE(pointewtab));
714   if (second_point == POINT_NONE) {
715      set_pos(&fp);
716      compile_diagnostic_token_show(DIAG_ERR, /*Expecting quadrant bearing, found “%s”*/483);
717      longjmp(jbSkipLine, 1);
718   }
719
720   if (r > quad) {
721      set_pos(&fp);
722      compile_diagnostic_token_show(DIAG_ERR, /*Suspicious compass reading*/59);
723      longjmp(jbSkipLine, 1);
724   }
725
726   if (first_point == POINT_N) {
727      if (second_point == POINT_W) {
728         r = quad * 4 - r;
729      }
730   } else {
731      if (second_point == POINT_W) {
732         r += quad * 2;
733      } else {
734         r = quad * 2 - r;
735      }
736   }
737   return r;
738}
739
740extern real
741read_numeric(bool f_optional)
742{
743   skipblanks();
744   return read_number(f_optional, false);
745}
746
747extern real
748read_numeric_multi(bool f_optional, bool f_quadrants, int *p_n_readings)
749{
750   size_t n_readings = 0;
751   real tot = (real)0.0;
752
753   skipblanks();
754   if (!isOpen(ch)) {
755      real r = 0;
756      if (!f_quadrants) {
757          r = read_number(f_optional, false);
758      } else {
759          r = read_quadrant(f_optional);
760          if (r != HUGE_REAL)
761              do_legacy_token_warning();
762      }
763      if (p_n_readings) *p_n_readings = (r == HUGE_REAL ? 0 : 1);
764      return r;
765   }
766   nextch();
767
768   skipblanks();
769   do {
770      if (!f_quadrants) {
771         tot += read_number(false, false);
772      } else {
773         tot += read_quadrant(false);
774         do_legacy_token_warning();
775      }
776      ++n_readings;
777      skipblanks();
778   } while (!isClose(ch));
779   nextch();
780
781   if (p_n_readings) *p_n_readings = n_readings;
782   /* FIXME: special averaging for bearings ... */
783   /* And for percentage gradient */
784   return tot / n_readings;
785}
786
787/* read numeric expr or omit (return HUGE_REAL); else longjmp */
788extern real
789read_bearing_multi_or_omit(bool f_quadrants, int *p_n_readings)
790{
791   real v;
792   v = read_numeric_multi(true, f_quadrants, p_n_readings);
793   if (v == HUGE_REAL) {
794      if (!isOmit(ch)) {
795         compile_diagnostic_token_show(DIAG_ERR, /*Expecting numeric field, found “%s”*/9);
796         longjmp(jbSkipLine, 1);
797      }
798      nextch();
799   }
800   return v;
801}
802
803/* Don't skip blanks, variable error code */
804unsigned int
805read_uint_raw(int errmsg, const filepos *fp)
806{
807   unsigned int n = 0;
808   if (!isdigit(ch)) {
809      if (fp) set_pos(fp);
810      compile_diagnostic_token_show(DIAG_ERR, errmsg);
811      longjmp(jbSkipLine, 1);
812   }
813   while (isdigit(ch)) {
814      n = n * 10 + (char)(ch - '0');
815      nextch();
816   }
817   return n;
818}
819
820extern unsigned int
821read_uint(void)
822{
823   skipblanks();
824   return read_uint_raw(/*Expecting numeric field, found “%s”*/9, NULL);
825}
826
827extern int
828read_int(int min_val, int max_val)
829{
830    skipblanks();
831    unsigned n = 0;
832    filepos fp;
833
834    get_pos(&fp);
835    bool negated = isMinus(ch);
836    unsigned limit;
837    if (negated) {
838        limit = (unsigned)(min_val == INT_MIN ? INT_MIN : -min_val);
839    } else {
840        limit = (unsigned)max_val;
841    }
842    if (isSign(ch)) nextch();
843
844    if (!isdigit(ch)) {
845bad_value:
846        set_pos(&fp);
847        /* TRANSLATORS: The first %d will be replaced by the (inclusive) lower
848         * bound and the second by the (inclusive) upper bound, for example:
849         * Expecting integer in range -60 to 60
850         */
851        compile_diagnostic(DIAG_ERR|DIAG_NUM, /*Expecting integer in range %d to %d*/489);
852        longjmp(jbSkipLine, 1);
853    }
854
855    while (isdigit(ch)) {
856        unsigned old_n = n;
857        n = n * 10 + (char)(ch - '0');
858        if (n > limit || n < old_n) {
859            goto bad_value;
860        }
861        nextch();
862    }
863    if (isDecimal(ch)) goto bad_value;
864
865    if (negated) {
866        if (n > (unsigned)INT_MAX) {
867            // Avoid unportable casting.
868            return INT_MIN;
869        }
870        return -(int)n;
871    }
872    return (int)n;
873}
874
875extern void
876read_string(string *pstr)
877{
878   s_clear(pstr);
879
880   skipblanks();
881   if (ch == '\"') {
882      /* String quoted in "" */
883      nextch();
884      while (1) {
885         if (isEol(ch)) {
886            compile_diagnostic(DIAG_ERR|DIAG_COL, /*Missing \"*/69);
887            longjmp(jbSkipLine, 1);
888         }
889
890         if (ch == '\"') break;
891
892         s_appendch(pstr, ch);
893         nextch();
894      }
895      nextch();
896   } else {
897      /* Unquoted string */
898      while (1) {
899         if (isEol(ch) || isComm(ch)) {
900            if (s_empty(pstr)) {
901               compile_diagnostic(DIAG_ERR|DIAG_COL, /*Expecting string field*/121);
902               longjmp(jbSkipLine, 1);
903            }
904            return;
905         }
906
907         if (isBlank(ch)) break;
908
909         s_appendch(pstr, ch);
910         nextch();
911      }
912   }
913}
914
915extern void
916read_walls_srv_date(int *py, int *pm, int *pd)
917{
918    skipblanks();
919
920    filepos fp_date;
921    get_pos(&fp_date);
922    unsigned y = read_uint_raw(/*Expecting date, found “%s”*/198, &fp_date);
923    int separator = -2;
924    if (ch == '-' || ch == '/') {
925        separator = ch;
926        nextch();
927    }
928    filepos fp_month;
929    get_pos(&fp_month);
930    unsigned m = read_uint_raw(/*Expecting date, found “%s”*/198, &fp_date);
931    if (ch == separator) {
932        nextch();
933    }
934    filepos fp_day;
935    get_pos(&fp_day);
936    unsigned d = read_uint_raw(/*Expecting date, found “%s”*/198, &fp_date);
937
938    filepos fp_year;
939    if (y < 100) {
940        // Walls recommends ISO 8601 date format (yyyy-mm-dd and seemingly the
941        // non-standard variant yyyy/mm/dd), but also accepts "some date formats
942        // common in the U.S. (mm/dd/yy, mm-dd-yyyy, etc.)"
943        unsigned tmp = y;
944        y = d;
945        fp_year = fp_day;
946        d = m;
947        fp_day = fp_month;
948        m = tmp;
949        fp_month = fp_date;
950
951        if (y < 100) {
952            // FIXME: Are all 2 digit years 19xx?
953            y += 1900;
954
955            filepos fp_save;
956            get_pos(&fp_save);
957            set_pos(&fp_year);
958            /* TRANSLATORS: %d will be replaced by the assumed year, e.g. 1918 */
959            compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Assuming 2 digit year is %d*/76, y);
960            set_pos(&fp_save);
961        }
962    } else {
963        if (y < 1900 || y > 2078) {
964            set_pos(&fp_date);
965            compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Invalid year (< 1900 or > 2078)*/58);
966            longjmp(jbSkipLine, 1);
967        }
968        fp_year = fp_date;
969    }
970
971    if (m < 1 || m > 12) {
972        set_pos(&fp_month);
973        compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Invalid month*/86);
974        longjmp(jbSkipLine, 1);
975    }
976
977    if (d < 1 || d > (unsigned)last_day(y, m)) {
978        set_pos(&fp_day);
979        /* TRANSLATORS: e.g. 31st of April, or 32nd of any month */
980        compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Invalid day of the month*/87);
981        longjmp(jbSkipLine, 1);
982    }
983
984    if (py) *py = y;
985    if (pm) *pm = m;
986    if (pd) *pd = d;
987}
Note: See TracBrowser for help on using the repository browser.