source: git/src/readval.c @ 5723f6d

stereo-2025
Last change on this file since 5723f6d was 501dd27, checked in by Olly Betts <olly@…>, 11 months ago

Clean up diagnostic reporting machinery

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