source: git/src/readval.c @ 8a7804fb

main
Last change on this file since 8a7804fb was 0b99107, checked in by Olly Betts <olly@…>, 4 weeks ago

Eliminate old FSF addresses

Update GPL/LGPL licence files and boilerplate to direct people who
didn't receive the licence text to the FSF website, as the current
versions of the FSF licence texts now do, rather than giving a postal
address.

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