source: git/src/readval.c @ c6c4996

Last change on this file since c6c4996 was f15c53d9, checked in by Olly Betts <olly@…>, 4 months ago

Implement parsing of *copyright

  • Property mode set to 100644
File size: 27.2 KB
RevLine 
[6974a34]1/* readval.c
[0395657]2 * Routines to read a prefix or number from the current input file
[62be3ee]3 * Copyright (C) 1991-2025 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
[c86cc71]37int root_depr_count = 0;
38
[710ecc1]39static prefix *
[a2c33ae]40new_anon_station(void)
[710ecc1]41{
[a2c33ae]42    prefix *name = osnew(prefix);
43    name->pos = NULL;
[ba84079]44    name->ident.p = NULL;
[a2c33ae]45    name->stn = NULL;
46    name->up = pcs->Prefix;
47    name->down = NULL;
48    name->filename = file.filename;
49    name->line = file.line;
[5fc783b]50    name->min_export = name->max_export = 0;
[a2c33ae]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;
[710ecc1]56}
57
[ba84079]58static char *id = NULL;
59static size_t id_len = 0;
60
[c458cf7]61/* if prefix is omitted: if PFX_OPT set return NULL, otherwise use longjmp */
62extern prefix *
63read_prefix(unsigned pfx_flags)
[a420b49]64{
[c458cf7]65   bool f_optional = !!(pfx_flags & PFX_OPT);
66   bool fSurvey = !!(pfx_flags & PFX_SURVEY);
67   bool fSuspectTypo = !!(pfx_flags & PFX_SUSPECT_TYPO);
[4a6a094]68   prefix *back_ptr, *ptr;
[eb18f4d]69   size_t i;
[647407d]70   bool fNew;
[63d4f07]71   bool fImplicitPrefix = true;
[932f7e9]72   int depth = -1;
[d3fa693]73   filepos here;
[5b4085b]74   filepos fp_firstsep;
[a420b49]75
76   skipblanks();
[d3fa693]77   get_pos(&here);
[7f1ab95]78#ifndef NO_DEPRECATED
[a420b49]79   if (isRoot(ch)) {
[c458cf7]80      if (!(pfx_flags & PFX_ALLOW_ROOT)) {
[cab0f26]81         compile_diagnostic(DIAG_ERR|DIAG_COL, /*ROOT is deprecated*/25);
[fdd4ed76]82         longjmp(jbSkipLine, 1);
[84c60fc]83      }
[c86cc71]84      if (root_depr_count < 5) {
[cab0f26]85         compile_diagnostic(DIAG_WARN|DIAG_COL, /*ROOT is deprecated*/25);
[c86cc71]86         if (++root_depr_count == 5)
[64544daf]87            compile_diagnostic(DIAG_INFO, /*Further uses of this deprecated feature will not be reported*/95);
[c86cc71]88      }
[d1b1380]89      nextch();
[a420b49]90      ptr = root;
91      if (!isNames(ch)) {
92         if (!isSep(ch)) return ptr;
[27b8b59]93         /* Allow optional SEPARATOR after ROOT */
[5b4085b]94         get_pos(&fp_firstsep);
[a420b49]95         nextch();
96      }
[63d4f07]97      fImplicitPrefix = false;
[710ecc1]98#else
99   if (0) {
100#endif
[a420b49]101   } else {
[dcbcae0]102      if ((pfx_flags & PFX_ANON) &&
103          (isSep(ch) || (pcs->dash_for_anon_wall_station && ch == '-'))) {
104         int first_ch = ch;
[710ecc1]105         nextch();
[1cb22b5]106         if (isBlank(ch) || isComm(ch) || isEol(ch)) {
[dcbcae0]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)) {
[9f55538]114               set_pos(&here);
[7962c9d]115               compile_diagnostic(DIAG_ERR|DIAG_WORD, /*Can't have a leg between two anonymous stations*/47);
[fdd4ed76]116               longjmp(jbSkipLine, 1);
[710ecc1]117            }
[dcbcae0]118            pcs->flags |= BIT(FLAGS_ANON_ONE_END) | BIT(FLAGS_IMPLICIT_SPLAY);
[a2c33ae]119            return new_anon_station();
[710ecc1]120         }
[dcbcae0]121         if (isSep(first_ch) && ch == first_ch) {
122            nextch();
[1cb22b5]123            if (isBlank(ch) || isComm(ch) || isEol(ch)) {
[dcbcae0]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)) {
[9f55538]130                  set_pos(&here);
[7962c9d]131                  compile_diagnostic(DIAG_ERR|DIAG_WORD, /*Can't have a leg between two anonymous stations*/47);
[fdd4ed76]132                  longjmp(jbSkipLine, 1);
[dcbcae0]133               }
[da65891]134               pcs->flags |= BIT(FLAGS_ANON_ONE_END) | BIT(FLAGS_IMPLICIT_SPLAY);
[dcbcae0]135               pfx = new_anon_station();
136               pfx->sflags |= BIT(SFLAGS_WALL);
137               return pfx;
138            }
139            if (ch == first_ch) {
140               nextch();
[1cb22b5]141               if (isBlank(ch) || isComm(ch) || isEol(ch)) {
[dcbcae0]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)) {
[9f55538]148                     set_pos(&here);
[7962c9d]149                     compile_diagnostic(DIAG_ERR|DIAG_WORD, /*Can't have a leg between two anonymous stations*/47);
[fdd4ed76]150                     longjmp(jbSkipLine, 1);
[dcbcae0]151                  }
152                  pcs->flags |= BIT(FLAGS_ANON_ONE_END);
153                  return new_anon_station();
154               }
155            }
156         }
[710ecc1]157         set_pos(&here);
158      }
[a420b49]159      ptr = pcs->Prefix;
160   }
161
162   i = 0;
163   do {
[63d4f07]164      fNew = false;
[ba84079]165      if (id == NULL) {
166         /* Allocate buffer the first time */
167         id_len = 256;
168         id = osmalloc(id_len);
[be97baf]169      }
[a420b49]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 */
[ba84079]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);
[a420b49]183            }
[8cf2e04]184         }
[a420b49]185         nextch();
[8cf2e04]186      }
[5b4085b]187      if (isSep(ch)) {
[63d4f07]188         fImplicitPrefix = false;
[5b4085b]189         get_pos(&fp_firstsep);
190      }
[a420b49]191      if (i == 0) {
[21c226e]192         if (!f_optional) {
[ee1d81f]193            if (isEol(ch)) {
[84c60fc]194               if (fSurvey) {
[cab0f26]195                  compile_diagnostic(DIAG_ERR|DIAG_COL, /*Expecting survey name*/89);
[84c60fc]196               } else {
[cab0f26]197                  compile_diagnostic(DIAG_ERR|DIAG_COL, /*Expecting station name*/28);
[84c60fc]198               }
[ee1d81f]199            } else {
[736f7df]200               /* TRANSLATORS: Here "station" is a survey station, not a train station. */
[7962c9d]201               compile_diagnostic(DIAG_ERR|DIAG_COL, /*Character “%c” not allowed in station name (use *SET NAMES to set allowed characters)*/110, ch);
[ee1d81f]202            }
[fdd4ed76]203            longjmp(jbSkipLine, 1);
[647407d]204         }
[a420b49]205         return (prefix *)NULL;
[d1b1380]206      }
[a420b49]207
[ba84079]208      id[i++] = '\0';
[cb3d1e2]209
[a420b49]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);
[ba84079]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         }
[a420b49]224         ptr->right = ptr->down = NULL;
225         ptr->pos = NULL;
226         ptr->stn = NULL;
227         ptr->up = back_ptr;
[d333899]228         ptr->filename = file.filename;
229         ptr->line = file.line;
[932f7e9]230         ptr->min_export = ptr->max_export = 0;
[95c3272]231         if (fSuspectTypo && !fImplicitPrefix)
232            ptr->sflags |= BIT(SFLAGS_SUSPECTTYPO);
[a420b49]233         back_ptr->down = ptr;
[63d4f07]234         fNew = true;
[a420b49]235      } else {
[be97baf]236         /* Use caching to speed up adding an increasing sequence to a
237          * large survey */
238         static prefix *cached_survey = NULL, *cached_station = NULL;
[a420b49]239         prefix *ptrPrev = NULL;
[76bb277a]240         int cmp = 1; /* result of strcmp ( -ve for <, 0 for =, +ve for > ) */
[be97baf]241         if (cached_survey == back_ptr) {
[ba84079]242            cmp = strcmp(prefix_ident(cached_station), id);
[be97baf]243            if (cmp <= 0) ptr = cached_station;
244         }
[ba84079]245         while (ptr && (cmp = strcmp(prefix_ident(ptr), id)) < 0) {
[a420b49]246            ptrPrev = ptr;
247            ptr = ptr->right;
248         }
249         if (cmp) {
250            /* ie we got to one that was higher, or the end */
[ba84079]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            }
[a420b49]261            if (ptrPrev == NULL)
[647407d]262               back_ptr->down = newptr;
[a420b49]263            else
[647407d]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;
[d333899]270            newptr->filename = file.filename;
271            newptr->line = file.line;
[421b7d2]272            newptr->min_export = newptr->max_export = 0;
[95c3272]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
[d40f787]297   /* don't warn about a station that is referred to twice */
[95c3272]298   if (!fNew) ptr->sflags &= ~BIT(SFLAGS_SUSPECTTYPO);
[421b7d2]299
[6430f0e]300   if (fNew) {
[95c3272]301      /* fNew means SFLAGS_SURVEY is currently set */
[4c07c51]302      SVX_ASSERT(TSTBIT(ptr->sflags, SFLAGS_SURVEY));
[c00c74a9]303      if (!fSurvey) {
304         ptr->sflags &= ~BIT(SFLAGS_SURVEY);
[27b8b59]305         if (TSTBIT(pcs->infer, INFER_EXPORTS)) ptr->min_export = USHRT_MAX;
[c00c74a9]306      }
[6430f0e]307   } else {
308      /* check that the same name isn't being used for a survey and station */
[95c3272]309      if (fSurvey ^ TSTBIT(ptr->sflags, SFLAGS_SURVEY)) {
[0b8c321]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          */
[d3fa693]315         compile_diagnostic(DIAG_ERR|DIAG_FROM(here), /*“%s” can’t be both a station and a survey*/27,
[cab0f26]316                            sprint_prefix(ptr));
[6430f0e]317      }
[27b8b59]318      if (!fSurvey && TSTBIT(pcs->infer, INFER_EXPORTS)) ptr->min_export = USHRT_MAX;
[051a72b]319   }
320
[932f7e9]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
[c00c74a9]326   if (ptr->min_export == 0 || ptr->min_export == USHRT_MAX) {
[932f7e9]327      if (depth > ptr->max_export) ptr->max_export = depth;
328   } else if (ptr->max_export < depth) {
[dd5b1a8]329      prefix *survey = ptr;
330      char *s;
[93e3492]331      const char *p;
[dd5b1a8]332      int level;
333      for (level = ptr->max_export + 1; level; level--) {
334         survey = survey->up;
[4c07c51]335         SVX_ASSERT(survey);
[dd5b1a8]336      }
337      s = osstrdup(sprint_prefix(survey));
[93e3492]338      p = sprint_prefix(ptr);
[016068a]339      if (survey->filename) {
[cab0f26]340         compile_diagnostic_pfx(DIAG_ERR, survey,
341                                /*Station “%s” not exported from survey “%s”*/26,
342                                p, s);
[93e3492]343      } else {
[cab0f26]344         compile_diagnostic(DIAG_ERR, /*Station “%s” not exported from survey “%s”*/26, p, s);
[016068a]345      }
[ae917b96]346      free(s);
[932f7e9]347#if 0
348      printf(" *** pfx %s warning not exported enough depth %d "
[421b7d2]349             "ptr->max_export %d\n", sprint_prefix(ptr),
[932f7e9]350             depth, ptr->max_export);
351#endif
352   }
[0fa7aac]353   if (!fImplicitPrefix && (pfx_flags & PFX_WARN_SEPARATOR)) {
[5b4085b]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);
[0fa7aac]359   }
[a420b49]360   return ptr;
[d1b1380]361}
362
[725d3b1]363char *
364read_walls_prefix(void)
365{
366    string name = S_INIT;
367    skipblanks();
368    if (!isNames(ch))
369        return NULL;
370    do {
[94d9f64]371        s_appendch(&name, ch);
[725d3b1]372        nextch();
373    } while (isNames(ch));
374    return s_steal(&name);
375}
376
377prefix *
[202e357]378read_walls_station(char * const walls_prefix[3], bool anon_allowed, bool *p_new)
[725d3b1]379{
[202e357]380    if (p_new) *p_new = false;
[725d3b1]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.
[7962c9d]413                compile_diagnostic(DIAG_ERR|DIAG_TOKEN, /*Can't have a leg between two anonymous stations*/47);
[fdd4ed76]414                longjmp(jbSkipLine, 1);
[725d3b1]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);
[202e357]419            // An anonymous station is always new.
420            if (p_new) *p_new = true;
[725d3b1]421            return pfx;
422        }
[94d9f64]423        s_appendn(&component, dashes, '-');
[725d3b1]424    }
425
426    char *w_prefix[3] = { NULL, NULL, NULL };
427    int explicit_prefix_levels = 0;
428    while (true) {
429        while (isNames(ch)) {
[94d9f64]430            s_appendch(&component, ch);
[725d3b1]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);
[ae917b96]441                for (int i = 0; i < 3; ++i) free(w_prefix[i]);
[fdd4ed76]442                longjmp(jbSkipLine, 1);
[725d3b1]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)) {
[1f34f48]457            if (explicit_prefix_levels == 0) {
458                compile_diagnostic(DIAG_ERR|DIAG_COL, /*Expecting station name*/28);
459                s_free(&component);
[ae917b96]460                for (int i = 0; i < 3; ++i) free(w_prefix[i]);
[fdd4ed76]461                longjmp(jbSkipLine, 1);
[1f34f48]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.
[94d9f64]468            s_append(&component, "empty name");
[725d3b1]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) {
[ba84079]489            char *name;
[725d3b1]490            int sflag = BIT(SFLAGS_SURVEY);
491            if (i == 3) {
492                name = p;
[1bb4ba1]493                sflag = 0;
[725d3b1]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 */
[202e357]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;
[725d3b1]524                ptr = osnew(prefix);
[ba84079]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);
[ae917b96]529                    if (i >= 3) free(name);
[ba84079]530                } else {
531                    ptr->ident.p = (i < 3 ? osstrdup(name) : name);
532                }
[725d3b1]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) {
[ba84079]549                    cmp = strcmp(prefix_ident(cached_station), name);
[725d3b1]550                    if (cmp <= 0) ptr = cached_station;
551                }
[ba84079]552                while (ptr && (cmp = strcmp(prefix_ident(ptr), name))<0) {
[725d3b1]553                    ptrPrev = ptr;
554                    ptr = ptr->right;
555                }
556                if (cmp) {
557                    /* ie we got to one that was higher, or the end */
[202e357]558                    if (p_new) *p_new = true;
559                    prefix *newptr = osnew(prefix);
[ba84079]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);
[ae917b96]564                        if (i >= 3) free(name);
[ba84079]565                    } else {
566                        newptr->ident.p = (i < 3 ? osstrdup(name) : name);
567                    }
[725d3b1]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 {
[116b485]583                    ptr->sflags |= sflag;
[725d3b1]584                }
[1bb4ba1]585                if (!TSTBIT(ptr->sflags, SFLAGS_SURVEY)) {
586                    ptr->min_export = USHRT_MAX;
587                }
[725d3b1]588                cached_survey = back_ptr;
589                cached_station = ptr;
590            }
[ae917b96]591            if (name == p) free(p);
[725d3b1]592        }
593
[1bb4ba1]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        }
[725d3b1]600
[ae917b96]601        for (int i = 0; i < 3; ++i) free(w_prefix[i]);
[725d3b1]602
603        return ptr;
604    }
605}
606
[21c226e]607/* if numeric expr is omitted: if f_optional return HUGE_REAL, else longjmp */
[725d3b1]608real
[5a0ab6a]609read_number(bool f_optional, bool f_unsigned)
[a420b49]610{
[63d4f07]611   bool fPositive = true, fDigits = false;
[a420b49]612   real n = (real)0.0;
[c80bd34]613   filepos fp;
614   int ch_old;
[a420b49]615
[c80bd34]616   get_pos(&fp);
617   ch_old = ch;
[5a0ab6a]618   if (!f_unsigned) {
619      fPositive = !isMinus(ch);
620      if (isSign(ch)) nextch();
621   }
[a420b49]622
623   while (isdigit(ch)) {
[6974a34]624      n = n * (real)10.0 + (char)(ch - '0');
[d1b1380]625      nextch();
[63d4f07]626      fDigits = true;
[a420b49]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;
[63d4f07]635         fDigits = true;
[a420b49]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 */
[4fb15a1]644   set_pos(&fp);
[21c226e]645   if (f_optional) {
[a420b49]646      return HUGE_REAL;
647   }
648
[c80bd34]649   if (isOmit(ch_old)) {
[7962c9d]650      compile_diagnostic(DIAG_ERR|DIAG_COL, /*Field may not be omitted*/114);
[647407d]651   } else {
[cab0f26]652      compile_diagnostic_token_show(DIAG_ERR, /*Expecting numeric field, found “%s”*/9);
[647407d]653   }
[fdd4ed76]654   longjmp(jbSkipLine, 1);
[d1b1380]655}
[647407d]656
[725d3b1]657real
[4ca3219]658read_quadrant(bool f_optional)
659{
[5a0ab6a]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   }
[9d9bee9]682   const int quad = 90;
683   filepos fp;
684   get_pos(&fp);
[e1cbc0d]685   get_token_legacy_no_blanks();
[5a0ab6a]686   int first_point = match_tok(pointtab, TABSIZE(pointtab));
687   if (first_point == POINT_NONE) {
688      set_pos(&fp);
689      if (isOmit(ch)) {
[7962c9d]690         compile_diagnostic(DIAG_ERR|DIAG_COL, /*Field may not be omitted*/114);
[5a0ab6a]691      }
692      compile_diagnostic_token_show(DIAG_ERR, /*Expecting quadrant bearing, found “%s”*/483);
[fdd4ed76]693      longjmp(jbSkipLine, 1);
[9d9bee9]694   }
[63d4f07]695   real r = read_number(true, true);
[5a0ab6a]696   if (r == HUGE_REAL) {
697      if (isSign(ch) || isDecimal(ch)) {
698         /* Give better errors for S-0E, N+10W, N.E, etc. */
[9d9bee9]699         set_pos(&fp);
[728874f9]700         compile_diagnostic_token_show(DIAG_ERR, /*Expecting quadrant bearing, found “%s”*/483);
[fdd4ed76]701         longjmp(jbSkipLine, 1);
[9d9bee9]702      }
[5a0ab6a]703      /* N, S, E or W. */
704      return first_point * quad;
705   }
706   if (first_point == POINT_E || first_point == POINT_W) {
[9d9bee9]707      set_pos(&fp);
[728874f9]708      compile_diagnostic_token_show(DIAG_ERR, /*Expecting quadrant bearing, found “%s”*/483);
[fdd4ed76]709      longjmp(jbSkipLine, 1);
[4ca3219]710   }
[5a0ab6a]711
[e1cbc0d]712   get_token_legacy_no_blanks();
[eb429bf]713   int second_point = match_tok(pointewtab, TABSIZE(pointewtab));
714   if (second_point == POINT_NONE) {
[5a0ab6a]715      set_pos(&fp);
716      compile_diagnostic_token_show(DIAG_ERR, /*Expecting quadrant bearing, found “%s”*/483);
[fdd4ed76]717      longjmp(jbSkipLine, 1);
[4ca3219]718   }
[5a0ab6a]719
[eb429bf]720   if (r > quad) {
[5a0ab6a]721      set_pos(&fp);
[501dd27]722      compile_diagnostic_token_show(DIAG_ERR, /*Suspicious compass reading*/59);
[fdd4ed76]723      longjmp(jbSkipLine, 1);
[4ca3219]724   }
[5a0ab6a]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;
[4ca3219]738}
739
[21c226e]740extern real
[d3241e6]741read_numeric(bool f_optional)
742{
743   skipblanks();
[63d4f07]744   return read_number(f_optional, false);
[d3241e6]745}
746
747extern real
[61513b57]748read_numeric_multi(bool f_optional, bool f_quadrants, int *p_n_readings)
[21c226e]749{
750   size_t n_readings = 0;
751   real tot = (real)0.0;
752
753   skipblanks();
754   if (!isOpen(ch)) {
[4699c40]755      real r = 0;
[05b9de76]756      if (!f_quadrants) {
[63d4f07]757          r = read_number(f_optional, false);
[05b9de76]758      } else {
[5a0ab6a]759          r = read_quadrant(f_optional);
[05b9de76]760          if (r != HUGE_REAL)
761              do_legacy_token_warning();
762      }
[21c226e]763      if (p_n_readings) *p_n_readings = (r == HUGE_REAL ? 0 : 1);
764      return r;
765   }
766   nextch();
767
768   skipblanks();
769   do {
[05b9de76]770      if (!f_quadrants) {
[63d4f07]771         tot += read_number(false, false);
[05b9de76]772      } else {
[63d4f07]773         tot += read_quadrant(false);
[05b9de76]774         do_legacy_token_warning();
775      }
[21c226e]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
[2aa2f3f]787/* read numeric expr or omit (return HUGE_REAL); else longjmp */
788extern real
[00b10c1]789read_bearing_multi_or_omit(bool f_quadrants, int *p_n_readings)
[2aa2f3f]790{
[9d9bee9]791   real v;
[63d4f07]792   v = read_numeric_multi(true, f_quadrants, p_n_readings);
[9d9bee9]793   if (v == HUGE_REAL) {
794      if (!isOmit(ch)) {
795         compile_diagnostic_token_show(DIAG_ERR, /*Expecting numeric field, found “%s”*/9);
[fdd4ed76]796         longjmp(jbSkipLine, 1);
[2aa2f3f]797      }
798      nextch();
799   }
800   return v;
801}
802
[950a829]803/* Don't skip blanks, variable error code */
[c7c0f93]804unsigned int
[f15c53d9]805read_uint_raw(int diag_type, int errmsg, const filepos *fp)
[647407d]806{
[421b7d2]807   unsigned int n = 0;
[647407d]808   if (!isdigit(ch)) {
[029cc3f]809      if (fp) set_pos(fp);
[f15c53d9]810      if ((diag_type & DIAG_UINT)) {
811          compile_diagnostic(diag_type, errmsg);
812      } else {
813          compile_diagnostic_token_show(diag_type, errmsg);
814      }
[fdd4ed76]815      longjmp(jbSkipLine, 1);
[647407d]816   }
817   while (isdigit(ch)) {
818      n = n * 10 + (char)(ch - '0');
819      nextch();
820   }
821   return n;
822}
[76bb277a]823
[950a829]824extern unsigned int
825read_uint(void)
826{
827   skipblanks();
[f15c53d9]828   return read_uint_raw(DIAG_ERR, /*Expecting numeric field, found “%s”*/9, NULL);
[950a829]829}
830
[5b9b9d4]831extern int
832read_int(int min_val, int max_val)
833{
834    skipblanks();
835    unsigned n = 0;
836    filepos fp;
837
838    get_pos(&fp);
[1000d9d]839    bool negated = isMinus(ch);
840    unsigned limit;
841    if (negated) {
842        limit = (unsigned)(min_val == INT_MIN ? INT_MIN : -min_val);
843    } else {
844        limit = (unsigned)max_val;
845    }
[5b9b9d4]846    if (isSign(ch)) nextch();
847
848    if (!isdigit(ch)) {
849bad_value:
850        set_pos(&fp);
851        /* TRANSLATORS: The first %d will be replaced by the (inclusive) lower
852         * bound and the second by the (inclusive) upper bound, for example:
853         * Expecting integer in range -60 to 60
854         */
855        compile_diagnostic(DIAG_ERR|DIAG_NUM, /*Expecting integer in range %d to %d*/489);
[fdd4ed76]856        longjmp(jbSkipLine, 1);
[5b9b9d4]857    }
858
859    while (isdigit(ch)) {
860        unsigned old_n = n;
861        n = n * 10 + (char)(ch - '0');
862        if (n > limit || n < old_n) {
863            goto bad_value;
864        }
865        nextch();
866    }
867    if (isDecimal(ch)) goto bad_value;
868
869    if (negated) {
870        if (n > (unsigned)INT_MAX) {
871            // Avoid unportable casting.
872            return INT_MIN;
873        }
874        return -(int)n;
875    }
876    return (int)n;
877}
878
[62be3ee]879static bool
880read_string_(string *pstr, int diag_type)
[76bb277a]881{
[0532954]882   s_clear(pstr);
[76bb277a]883
884   skipblanks();
885   if (ch == '\"') {
[1157304]886      /* String quoted in "" */
[76bb277a]887      nextch();
[457a390]888      while (1) {
889         if (isEol(ch)) {
[62be3ee]890            compile_diagnostic(diag_type|DIAG_COL, /*Missing \"*/69);
891            if (diag_type == DIAG_ERR) longjmp(jbSkipLine, 1);
892            return false;
[457a390]893         }
894
895         if (ch == '\"') break;
896
[94d9f64]897         s_appendch(pstr, ch);
[457a390]898         nextch();
899      }
[66f5fc4]900      nextch();
[457a390]901   } else {
[1157304]902      /* Unquoted string */
[457a390]903      while (1) {
904         if (isEol(ch) || isComm(ch)) {
[0532954]905            if (s_empty(pstr)) {
[62be3ee]906               compile_diagnostic(diag_type|DIAG_COL, /*Expecting string field*/121);
907                if (diag_type == DIAG_ERR) longjmp(jbSkipLine, 1);
908                return false;
[b97d134]909            }
[62be3ee]910            return true;
[76bb277a]911         }
912
[457a390]913         if (isBlank(ch)) break;
[76bb277a]914
[94d9f64]915         s_appendch(pstr, ch);
[457a390]916         nextch();
917      }
[76bb277a]918   }
[62be3ee]919   return true;
920}
921
922extern void
923read_string(string *pstr)
924{
925    (void)read_string_(pstr, DIAG_ERR);
926}
927
928extern bool
929read_string_warning(string *pstr)
930{
931    return read_string_(pstr, DIAG_WARN);
[76bb277a]932}
[950a829]933
[725d3b1]934extern void
935read_walls_srv_date(int *py, int *pm, int *pd)
936{
937    skipblanks();
938
939    filepos fp_date;
940    get_pos(&fp_date);
[f15c53d9]941    unsigned y = read_uint_raw(DIAG_ERR, /*Expecting date, found “%s”*/198, &fp_date);
[725d3b1]942    int separator = -2;
943    if (ch == '-' || ch == '/') {
944        separator = ch;
945        nextch();
946    }
947    filepos fp_month;
948    get_pos(&fp_month);
[f15c53d9]949    unsigned m = read_uint_raw(DIAG_ERR, /*Expecting date, found “%s”*/198, &fp_date);
[725d3b1]950    if (ch == separator) {
951        nextch();
952    }
953    filepos fp_day;
954    get_pos(&fp_day);
[f15c53d9]955    unsigned d = read_uint_raw(DIAG_ERR, /*Expecting date, found “%s”*/198, &fp_date);
[725d3b1]956
957    filepos fp_year;
958    if (y < 100) {
959        // Walls recommends ISO 8601 date format (yyyy-mm-dd and seemingly the
960        // non-standard variant yyyy/mm/dd), but also accepts "some date formats
961        // common in the U.S. (mm/dd/yy, mm-dd-yyyy, etc.)"
962        unsigned tmp = y;
963        y = d;
964        fp_year = fp_day;
965        d = m;
966        fp_day = fp_month;
967        m = tmp;
968        fp_month = fp_date;
969
970        if (y < 100) {
971            // FIXME: Are all 2 digit years 19xx?
972            y += 1900;
973
974            filepos fp_save;
975            get_pos(&fp_save);
976            set_pos(&fp_year);
977            /* TRANSLATORS: %d will be replaced by the assumed year, e.g. 1918 */
978            compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Assuming 2 digit year is %d*/76, y);
979            set_pos(&fp_save);
980        }
981    } else {
982        if (y < 1900 || y > 2078) {
983            set_pos(&fp_date);
984            compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Invalid year (< 1900 or > 2078)*/58);
[fdd4ed76]985            longjmp(jbSkipLine, 1);
[725d3b1]986        }
987        fp_year = fp_date;
988    }
989
990    if (m < 1 || m > 12) {
991        set_pos(&fp_month);
992        compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Invalid month*/86);
[fdd4ed76]993        longjmp(jbSkipLine, 1);
[725d3b1]994    }
995
[c7c0f93]996    if (d < 1 || d > (unsigned)last_day(y, m)) {
[725d3b1]997        set_pos(&fp_day);
998        /* TRANSLATORS: e.g. 31st of April, or 32nd of any month */
999        compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Invalid day of the month*/87);
[fdd4ed76]1000        longjmp(jbSkipLine, 1);
[725d3b1]1001    }
1002
1003    if (py) *py = y;
1004    if (pm) *pm = m;
1005    if (pd) *pd = d;
1006}
Note: See TracBrowser for help on using the repository browser.