source: git/src/readval.c @ 8676839

stereo-2025
Last change on this file since 8676839 was 09bedfa, checked in by Olly Betts <olly@…>, 7 months ago

Assume compiler knows longjmp() won't return

Remove return statements added after longjmp() to try to avoid
compiler warnings which were added more than 27 years ago. Modern
compilers should be smart enough not to need these.

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