source: git/src/readval.c @ 2acdae1

debug-ci-sanitisersfaster-cavernloglog-selectwalls-datawalls-data-hanging-as-warningwarn-only-for-hanging-survey
Last change on this file since 2acdae1 was 1000d9d, checked in by Olly Betts <olly@…>, 7 months ago

Avoid triggering ubsan unsigned-integer-overflow

This isn't undefined, but it's suspicious in most situations so
useful to be able to enable to find bugs.

  • Property mode set to 100644
File size: 19.0 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
[a420b49]20#ifdef HAVE_CONFIG_H
21# include <config.h>
22#endif
[d1b1380]23
[c00c74a9]24#include <limits.h>
[be97baf]25#include <stddef.h> /* for offsetof */
[c00c74a9]26
[a420b49]27#include "cavern.h"
[5a0ab6a]28#include "commands.h" /* For match_tok(), etc */
[1ee204e]29#include "date.h"
[dd5b1a8]30#include "debug.h"
[a1706d1]31#include "filename.h"
32#include "message.h"
[d1b1380]33#include "readval.h"
34#include "datain.h"
[fb2e93c]35#include "netbits.h"
[8cf2e04]36#include "osalloc.h"
[76bb277a]37#include "str.h"
[d1b1380]38
[7d5f3c0]39#ifdef HAVE_SETJMP_H
[647407d]40# define LONGJMP(JB) longjmp((JB), 1)
41#else
42# define LONGJMP(JB) exit(1)
43#endif
44
[c86cc71]45int root_depr_count = 0;
46
[710ecc1]47static prefix *
[a2c33ae]48new_anon_station(void)
[710ecc1]49{
[a2c33ae]50    prefix *name = osnew(prefix);
51    name->pos = NULL;
52    name->ident = NULL;
53    name->shape = 0;
54    name->stn = NULL;
55    name->up = pcs->Prefix;
56    name->down = NULL;
57    name->filename = file.filename;
58    name->line = file.line;
[5fc783b]59    name->min_export = name->max_export = 0;
[a2c33ae]60    name->sflags = BIT(SFLAGS_ANON);
61    /* Keep linked list of anon stations for node stats. */
62    name->right = anon_list;
63    anon_list = name;
64    return name;
[710ecc1]65}
66
[c458cf7]67/* if prefix is omitted: if PFX_OPT set return NULL, otherwise use longjmp */
68extern prefix *
69read_prefix(unsigned pfx_flags)
[a420b49]70{
[c458cf7]71   bool f_optional = !!(pfx_flags & PFX_OPT);
72   bool fSurvey = !!(pfx_flags & PFX_SURVEY);
73   bool fSuspectTypo = !!(pfx_flags & PFX_SUSPECT_TYPO);
[4a6a094]74   prefix *back_ptr, *ptr;
[a420b49]75   char *name;
76   size_t name_len = 32;
[eb18f4d]77   size_t i;
[647407d]78   bool fNew;
[63d4f07]79   bool fImplicitPrefix = true;
[932f7e9]80   int depth = -1;
[5b4085b]81   filepos fp_firstsep;
[a420b49]82
83   skipblanks();
[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)
[cab0f26]93            compile_diagnostic(DIAG_WARN, /*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         filepos here;
112         get_pos(&here);
113         nextch();
114         if (isBlank(ch) || isEol(ch)) {
[dcbcae0]115            if (!isSep(first_ch))
116               goto anon_wall_station;
117            /* A single separator alone ('.' by default) is an anonymous
118             * station which is on a point inside the passage and implies
119             * the leg to it is a splay.
120             */
121            if (TSTBIT(pcs->flags, FLAGS_ANON_ONE_END)) {
[9f55538]122               set_pos(&here);
[d0be687d]123               compile_diagnostic(DIAG_ERR|DIAG_WORD, /*Can't have a leg between two anonymous stations*/3);
[dcbcae0]124               LONGJMP(file.jbSkipLine);
[710ecc1]125            }
[dcbcae0]126            pcs->flags |= BIT(FLAGS_ANON_ONE_END) | BIT(FLAGS_IMPLICIT_SPLAY);
[a2c33ae]127            return new_anon_station();
[710ecc1]128         }
[dcbcae0]129         if (isSep(first_ch) && ch == first_ch) {
130            nextch();
131            if (isBlank(ch) || isEol(ch)) {
132               /* A double separator ('..' by default) is an anonymous station
133                * which is on the wall and implies the leg to it is a splay.
134                */
135               prefix * pfx;
136anon_wall_station:
137               if (TSTBIT(pcs->flags, FLAGS_ANON_ONE_END)) {
[9f55538]138                  set_pos(&here);
[d0be687d]139                  compile_diagnostic(DIAG_ERR|DIAG_WORD, /*Can't have a leg between two anonymous stations*/3);
[dcbcae0]140                  LONGJMP(file.jbSkipLine);
141               }
[da65891]142               pcs->flags |= BIT(FLAGS_ANON_ONE_END) | BIT(FLAGS_IMPLICIT_SPLAY);
[dcbcae0]143               pfx = new_anon_station();
144               pfx->sflags |= BIT(SFLAGS_WALL);
145               return pfx;
146            }
147            if (ch == first_ch) {
148               nextch();
149               if (isBlank(ch) || isEol(ch)) {
150                  /* A triple separator ('...' by default) is an anonymous
151                   * station, but otherwise not handled specially (e.g. for
152                   * a single leg down an unexplored side passage to a station
153                   * which isn't refindable).
154                   */
155                  if (TSTBIT(pcs->flags, FLAGS_ANON_ONE_END)) {
[9f55538]156                     set_pos(&here);
[d0be687d]157                     compile_diagnostic(DIAG_ERR|DIAG_WORD, /*Can't have a leg between two anonymous stations*/3);
[dcbcae0]158                     LONGJMP(file.jbSkipLine);
159                  }
160                  pcs->flags |= BIT(FLAGS_ANON_ONE_END);
161                  return new_anon_station();
162               }
163            }
164         }
[710ecc1]165         set_pos(&here);
166      }
[a420b49]167      ptr = pcs->Prefix;
168   }
169
170   i = 0;
[be97baf]171   name = NULL;
[a420b49]172   do {
[63d4f07]173      fNew = false;
[be97baf]174      if (name == NULL) {
175         /* Need a new name buffer */
176         name = osmalloc(name_len);
177      }
[a420b49]178      /* i==0 iff this is the first pass */
179      if (i) {
180         i = 0;
181         nextch();
182      }
183      while (isNames(ch)) {
184         if (i < pcs->Truncate) {
185            /* truncate name */
[1157304]186            name[i++] = (pcs->Case == LOWER ? tolower(ch) :
187                         (pcs->Case == OFF ? ch : toupper(ch)));
[a420b49]188            if (i >= name_len) {
189               name_len = name_len + name_len;
190               name = osrealloc(name, name_len);
191            }
[8cf2e04]192         }
[a420b49]193         nextch();
[8cf2e04]194      }
[5b4085b]195      if (isSep(ch)) {
[63d4f07]196         fImplicitPrefix = false;
[5b4085b]197         get_pos(&fp_firstsep);
198      }
[a420b49]199      if (i == 0) {
200         osfree(name);
[21c226e]201         if (!f_optional) {
[ee1d81f]202            if (isEol(ch)) {
[84c60fc]203               if (fSurvey) {
[cab0f26]204                  compile_diagnostic(DIAG_ERR|DIAG_COL, /*Expecting survey name*/89);
[84c60fc]205               } else {
[cab0f26]206                  compile_diagnostic(DIAG_ERR|DIAG_COL, /*Expecting station name*/28);
[84c60fc]207               }
[ee1d81f]208            } else {
[736f7df]209               /* TRANSLATORS: Here "station" is a survey station, not a train station. */
[cab0f26]210               compile_diagnostic(DIAG_ERR|DIAG_COL, /*Character “%c” not allowed in station name (use *SET NAMES to set allowed characters)*/7, ch);
[ee1d81f]211            }
[647407d]212            LONGJMP(file.jbSkipLine);
213         }
[a420b49]214         return (prefix *)NULL;
[d1b1380]215      }
[a420b49]216
[76bb277a]217      name[i++] = '\0';
[cb3d1e2]218
[a420b49]219      back_ptr = ptr;
220      ptr = ptr->down;
221      if (ptr == NULL) {
222         /* Special case first time around at each level */
[be97baf]223         name = osrealloc(name, i);
[a420b49]224         ptr = osnew(prefix);
225         ptr->ident = name;
[be97baf]226         name = NULL;
[a420b49]227         ptr->right = ptr->down = NULL;
228         ptr->pos = NULL;
[6adb88c]229         ptr->shape = 0;
[a420b49]230         ptr->stn = NULL;
231         ptr->up = back_ptr;
[d333899]232         ptr->filename = file.filename;
233         ptr->line = file.line;
[932f7e9]234         ptr->min_export = ptr->max_export = 0;
[95c3272]235         ptr->sflags = BIT(SFLAGS_SURVEY);
236         if (fSuspectTypo && !fImplicitPrefix)
237            ptr->sflags |= BIT(SFLAGS_SUSPECTTYPO);
[a420b49]238         back_ptr->down = ptr;
[63d4f07]239         fNew = true;
[a420b49]240      } else {
[be97baf]241         /* Use caching to speed up adding an increasing sequence to a
242          * large survey */
243         static prefix *cached_survey = NULL, *cached_station = NULL;
[a420b49]244         prefix *ptrPrev = NULL;
[76bb277a]245         int cmp = 1; /* result of strcmp ( -ve for <, 0 for =, +ve for > ) */
[be97baf]246         if (cached_survey == back_ptr) {
247            cmp = strcmp(cached_station->ident, name);
248            if (cmp <= 0) ptr = cached_station;
249         }
250         while (ptr && (cmp = strcmp(ptr->ident, name))<0) {
[a420b49]251            ptrPrev = ptr;
252            ptr = ptr->right;
253         }
254         if (cmp) {
255            /* ie we got to one that was higher, or the end */
[be97baf]256            prefix *newptr;
257            name = osrealloc(name, i);
258            newptr = osnew(prefix);
[647407d]259            newptr->ident = name;
[be97baf]260            name = NULL;
[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;
[6adb88c]268            newptr->shape = 0;
[647407d]269            newptr->stn = NULL;
270            newptr->up = back_ptr;
[d333899]271            newptr->filename = file.filename;
272            newptr->line = file.line;
[421b7d2]273            newptr->min_export = newptr->max_export = 0;
[95c3272]274            newptr->sflags = BIT(SFLAGS_SURVEY);
275            if (fSuspectTypo && !fImplicitPrefix)
276               newptr->sflags |= BIT(SFLAGS_SUSPECTTYPO);
[647407d]277            ptr = newptr;
[63d4f07]278            fNew = true;
[a420b49]279         }
[be97baf]280         cached_survey = back_ptr;
281         cached_station = ptr;
[d1b1380]282      }
[fb2e93c]283      depth++;
[63d4f07]284      f_optional = false; /* disallow after first level */
[5b4085b]285      if (isSep(ch)) get_pos(&fp_firstsep);
[a420b49]286   } while (isSep(ch));
[be97baf]287   if (name) osfree(name);
288
[d40f787]289   /* don't warn about a station that is referred to twice */
[95c3272]290   if (!fNew) ptr->sflags &= ~BIT(SFLAGS_SUSPECTTYPO);
[421b7d2]291
[6430f0e]292   if (fNew) {
[95c3272]293      /* fNew means SFLAGS_SURVEY is currently set */
[4c07c51]294      SVX_ASSERT(TSTBIT(ptr->sflags, SFLAGS_SURVEY));
[c00c74a9]295      if (!fSurvey) {
296         ptr->sflags &= ~BIT(SFLAGS_SURVEY);
[27b8b59]297         if (TSTBIT(pcs->infer, INFER_EXPORTS)) ptr->min_export = USHRT_MAX;
[c00c74a9]298      }
[6430f0e]299   } else {
300      /* check that the same name isn't being used for a survey and station */
[95c3272]301      if (fSurvey ^ TSTBIT(ptr->sflags, SFLAGS_SURVEY)) {
[0b8c321]302         /* TRANSLATORS: Here "station" is a survey station, not a train station.
303          *
304          * Here "survey" is a "cave map" rather than list of questions - it should be
305          * translated to the terminology that cavers using the language would use.
306          */
[cab0f26]307         compile_diagnostic(DIAG_ERR, /*“%s” can’t be both a station and a survey*/27,
308                            sprint_prefix(ptr));
[6430f0e]309      }
[27b8b59]310      if (!fSurvey && TSTBIT(pcs->infer, INFER_EXPORTS)) ptr->min_export = USHRT_MAX;
[051a72b]311   }
312
[932f7e9]313   /* check the export level */
314#if 0
315   printf("R min %d max %d depth %d pfx %s\n",
316          ptr->min_export, ptr->max_export, depth, sprint_prefix(ptr));
317#endif
[c00c74a9]318   if (ptr->min_export == 0 || ptr->min_export == USHRT_MAX) {
[932f7e9]319      if (depth > ptr->max_export) ptr->max_export = depth;
320   } else if (ptr->max_export < depth) {
[dd5b1a8]321      prefix *survey = ptr;
322      char *s;
[93e3492]323      const char *p;
[dd5b1a8]324      int level;
325      for (level = ptr->max_export + 1; level; level--) {
326         survey = survey->up;
[4c07c51]327         SVX_ASSERT(survey);
[dd5b1a8]328      }
329      s = osstrdup(sprint_prefix(survey));
[93e3492]330      p = sprint_prefix(ptr);
[016068a]331      if (survey->filename) {
[cab0f26]332         compile_diagnostic_pfx(DIAG_ERR, survey,
333                                /*Station “%s” not exported from survey “%s”*/26,
334                                p, s);
[93e3492]335      } else {
[cab0f26]336         compile_diagnostic(DIAG_ERR, /*Station “%s” not exported from survey “%s”*/26, p, s);
[016068a]337      }
[dd5b1a8]338      osfree(s);
[932f7e9]339#if 0
340      printf(" *** pfx %s warning not exported enough depth %d "
[421b7d2]341             "ptr->max_export %d\n", sprint_prefix(ptr),
[932f7e9]342             depth, ptr->max_export);
343#endif
344   }
[0fa7aac]345   if (!fImplicitPrefix && (pfx_flags & PFX_WARN_SEPARATOR)) {
[5b4085b]346      filepos fp_tmp;
347      get_pos(&fp_tmp);
348      set_pos(&fp_firstsep);
349      compile_diagnostic(DIAG_WARN|DIAG_COL, /*Separator in survey name*/392);
350      set_pos(&fp_tmp);
[0fa7aac]351   }
[a420b49]352   return ptr;
[d1b1380]353}
354
[21c226e]355/* if numeric expr is omitted: if f_optional return HUGE_REAL, else longjmp */
356static real
[5a0ab6a]357read_number(bool f_optional, bool f_unsigned)
[a420b49]358{
[63d4f07]359   bool fPositive = true, fDigits = false;
[a420b49]360   real n = (real)0.0;
[c80bd34]361   filepos fp;
362   int ch_old;
[a420b49]363
[c80bd34]364   get_pos(&fp);
365   ch_old = ch;
[5a0ab6a]366   if (!f_unsigned) {
367      fPositive = !isMinus(ch);
368      if (isSign(ch)) nextch();
369   }
[a420b49]370
371   while (isdigit(ch)) {
[6974a34]372      n = n * (real)10.0 + (char)(ch - '0');
[d1b1380]373      nextch();
[63d4f07]374      fDigits = true;
[a420b49]375   }
376
377   if (isDecimal(ch)) {
378      real mult = (real)1.0;
379      nextch();
380      while (isdigit(ch)) {
381         mult *= (real).1;
382         n += (char)(ch - '0') * mult;
[63d4f07]383         fDigits = true;
[a420b49]384         nextch();
385      }
386   }
387
388   /* !'fRead' => !fDigits so fDigits => 'fRead' */
389   if (fDigits) return (fPositive ? n : -n);
390
391   /* didn't read a valid number.  If it's optional, reset filepos & return */
[4fb15a1]392   set_pos(&fp);
[21c226e]393   if (f_optional) {
[a420b49]394      return HUGE_REAL;
395   }
396
[c80bd34]397   if (isOmit(ch_old)) {
[cab0f26]398      compile_diagnostic(DIAG_ERR|DIAG_COL, /*Field may not be omitted*/8);
[647407d]399   } else {
[cab0f26]400      compile_diagnostic_token_show(DIAG_ERR, /*Expecting numeric field, found “%s”*/9);
[647407d]401   }
402   LONGJMP(file.jbSkipLine);
[a420b49]403   return 0.0; /* for brain-fried compilers */
[d1b1380]404}
[647407d]405
[5a0ab6a]406static real
[4ca3219]407read_quadrant(bool f_optional)
408{
[5a0ab6a]409   enum {
410      POINT_N = 0,
411      POINT_E = 1,
412      POINT_S = 2,
413      POINT_W = 3,
414      POINT_NONE = -1
415   };
416   static const sztok pointtab[] = {
417        {"E", POINT_E },
418        {"N", POINT_N },
419        {"S", POINT_S },
420        {"W", POINT_W },
421        {NULL, POINT_NONE }
422   };
423   static const sztok pointewtab[] = {
424        {"E", POINT_E },
425        {"W", POINT_W },
426        {NULL, POINT_NONE }
427   };
428   if (f_optional && isOmit(ch)) {
429      return HUGE_REAL;
430   }
[9d9bee9]431   const int quad = 90;
432   filepos fp;
433   get_pos(&fp);
[5a0ab6a]434   get_token_no_blanks();
435   int first_point = match_tok(pointtab, TABSIZE(pointtab));
436   if (first_point == POINT_NONE) {
437      set_pos(&fp);
438      if (isOmit(ch)) {
439         compile_diagnostic(DIAG_ERR|DIAG_COL, /*Field may not be omitted*/8);
440      }
441      compile_diagnostic_token_show(DIAG_ERR, /*Expecting quadrant bearing, found “%s”*/483);
442      LONGJMP(file.jbSkipLine);
443      return 0.0; /* for brain-fried compilers */
[9d9bee9]444   }
[63d4f07]445   real r = read_number(true, true);
[5a0ab6a]446   if (r == HUGE_REAL) {
447      if (isSign(ch) || isDecimal(ch)) {
448         /* Give better errors for S-0E, N+10W, N.E, etc. */
[9d9bee9]449         set_pos(&fp);
[728874f9]450         compile_diagnostic_token_show(DIAG_ERR, /*Expecting quadrant bearing, found “%s”*/483);
[9d9bee9]451         LONGJMP(file.jbSkipLine);
452         return 0.0; /* for brain-fried compilers */
453      }
[5a0ab6a]454      /* N, S, E or W. */
455      return first_point * quad;
456   }
457   if (first_point == POINT_E || first_point == POINT_W) {
[9d9bee9]458      set_pos(&fp);
[728874f9]459      compile_diagnostic_token_show(DIAG_ERR, /*Expecting quadrant bearing, found “%s”*/483);
[9d9bee9]460      LONGJMP(file.jbSkipLine);
461      return 0.0; /* for brain-fried compilers */
[4ca3219]462   }
[5a0ab6a]463
[eb429bf]464   get_token_no_blanks();
465   int second_point = match_tok(pointewtab, TABSIZE(pointewtab));
466   if (second_point == POINT_NONE) {
[5a0ab6a]467      set_pos(&fp);
468      compile_diagnostic_token_show(DIAG_ERR, /*Expecting quadrant bearing, found “%s”*/483);
469      LONGJMP(file.jbSkipLine);
470      return 0.0; /* for brain-fried compilers */
[4ca3219]471   }
[5a0ab6a]472
[eb429bf]473   if (r > quad) {
[5a0ab6a]474      set_pos(&fp);
[eb429bf]475      compile_diagnostic_token_show(DIAG_ERR|DIAG_COL, /*Suspicious compass reading*/59);
[5a0ab6a]476      LONGJMP(file.jbSkipLine);
477      return 0.0; /* for brain-fried compilers */
[4ca3219]478   }
[5a0ab6a]479
480   if (first_point == POINT_N) {
481      if (second_point == POINT_W) {
482         r = quad * 4 - r;
483      }
484   } else {
485      if (second_point == POINT_W) {
486         r += quad * 2;
487      } else {
488         r = quad * 2 - r;
489      }
490   }
491   return r;
[4ca3219]492}
493
[21c226e]494extern real
[d3241e6]495read_numeric(bool f_optional)
496{
497   skipblanks();
[63d4f07]498   return read_number(f_optional, false);
[d3241e6]499}
500
501extern real
[61513b57]502read_numeric_multi(bool f_optional, bool f_quadrants, int *p_n_readings)
[21c226e]503{
504   size_t n_readings = 0;
505   real tot = (real)0.0;
506
507   skipblanks();
508   if (!isOpen(ch)) {
[4699c40]509      real r = 0;
[61513b57]510      if (!f_quadrants)
[63d4f07]511          r = read_number(f_optional, false);
[61513b57]512      else
[5a0ab6a]513          r = read_quadrant(f_optional);
[21c226e]514      if (p_n_readings) *p_n_readings = (r == HUGE_REAL ? 0 : 1);
515      return r;
516   }
517   nextch();
518
519   skipblanks();
520   do {
[9d9bee9]521      if (!f_quadrants)
[63d4f07]522         tot += read_number(false, false);
[61513b57]523      else
[63d4f07]524         tot += read_quadrant(false);
[21c226e]525      ++n_readings;
526      skipblanks();
527   } while (!isClose(ch));
528   nextch();
529
530   if (p_n_readings) *p_n_readings = n_readings;
531   /* FIXME: special averaging for bearings ... */
532   /* And for percentage gradient */
533   return tot / n_readings;
534}
535
[2aa2f3f]536/* read numeric expr or omit (return HUGE_REAL); else longjmp */
537extern real
[00b10c1]538read_bearing_multi_or_omit(bool f_quadrants, int *p_n_readings)
[2aa2f3f]539{
[9d9bee9]540   real v;
[63d4f07]541   v = read_numeric_multi(true, f_quadrants, p_n_readings);
[9d9bee9]542   if (v == HUGE_REAL) {
543      if (!isOmit(ch)) {
544         compile_diagnostic_token_show(DIAG_ERR, /*Expecting numeric field, found “%s”*/9);
545         LONGJMP(file.jbSkipLine);
546         return 0.0; /* for brain-fried compilers */
[2aa2f3f]547      }
548      nextch();
549   }
550   return v;
551}
552
[950a829]553/* Don't skip blanks, variable error code */
554static unsigned int
[75e67ab]555read_uint_internal(int errmsg, const filepos *fp)
[647407d]556{
[421b7d2]557   unsigned int n = 0;
[647407d]558   if (!isdigit(ch)) {
[029cc3f]559      if (fp) set_pos(fp);
[cab0f26]560      compile_diagnostic_token_show(DIAG_ERR, errmsg);
[647407d]561      LONGJMP(file.jbSkipLine);
562   }
563   while (isdigit(ch)) {
564      n = n * 10 + (char)(ch - '0');
565      nextch();
566   }
567   return n;
568}
[76bb277a]569
[950a829]570extern unsigned int
571read_uint(void)
572{
573   skipblanks();
[0804fbe]574   return read_uint_internal(/*Expecting numeric field, found “%s”*/9, NULL);
[950a829]575}
576
[5b9b9d4]577extern int
578read_int(int min_val, int max_val)
579{
580    skipblanks();
581    unsigned n = 0;
582    filepos fp;
583
584    get_pos(&fp);
[1000d9d]585    bool negated = isMinus(ch);
586    unsigned limit;
587    if (negated) {
588        limit = (unsigned)(min_val == INT_MIN ? INT_MIN : -min_val);
589    } else {
590        limit = (unsigned)max_val;
591    }
[5b9b9d4]592    if (isSign(ch)) nextch();
593
594    if (!isdigit(ch)) {
595bad_value:
596        set_pos(&fp);
597        /* TRANSLATORS: The first %d will be replaced by the (inclusive) lower
598         * bound and the second by the (inclusive) upper bound, for example:
599         * Expecting integer in range -60 to 60
600         */
601        compile_diagnostic(DIAG_ERR|DIAG_NUM, /*Expecting integer in range %d to %d*/489);
602        LONGJMP(file.jbSkipLine);
603    }
604
605    while (isdigit(ch)) {
606        unsigned old_n = n;
607        n = n * 10 + (char)(ch - '0');
608        if (n > limit || n < old_n) {
609            goto bad_value;
610        }
611        nextch();
612    }
613    if (isDecimal(ch)) goto bad_value;
614
615    if (negated) {
616        if (n > (unsigned)INT_MAX) {
617            // Avoid unportable casting.
618            return INT_MIN;
619        }
620        return -(int)n;
621    }
622    return (int)n;
623}
624
[76bb277a]625extern void
[0532954]626read_string(string *pstr)
[76bb277a]627{
[0532954]628   s_clear(pstr);
[76bb277a]629
630   skipblanks();
631   if (ch == '\"') {
[1157304]632      /* String quoted in "" */
[76bb277a]633      nextch();
[457a390]634      while (1) {
635         if (isEol(ch)) {
[cab0f26]636            compile_diagnostic(DIAG_ERR|DIAG_COL, /*Missing \"*/69);
[4fb15a1]637            LONGJMP(file.jbSkipLine);
[457a390]638         }
639
640         if (ch == '\"') break;
641
[0532954]642         s_catchar(pstr, ch);
[457a390]643         nextch();
644      }
[66f5fc4]645      nextch();
[457a390]646   } else {
[1157304]647      /* Unquoted string */
[457a390]648      while (1) {
649         if (isEol(ch) || isComm(ch)) {
[0532954]650            if (s_empty(pstr)) {
[cab0f26]651               compile_diagnostic(DIAG_ERR|DIAG_COL, /*Expecting string field*/121);
[6f61f83]652               LONGJMP(file.jbSkipLine);
[b97d134]653            }
[457a390]654            return;
[76bb277a]655         }
656
[457a390]657         if (isBlank(ch)) break;
[76bb277a]658
[0532954]659         s_catchar(pstr, ch);
[457a390]660         nextch();
661      }
[76bb277a]662   }
663}
[950a829]664
665extern void
[b5a3219]666read_date(int *py, int *pm, int *pd)
[950a829]667{
[734f5f4]668   unsigned int y = 0, m = 0, d = 0;
[1d74718]669   filepos fp_date;
[950a829]670
671   skipblanks();
[029cc3f]672
[1d74718]673   get_pos(&fp_date);
674   y = read_uint_internal(/*Expecting date, found “%s”*/198, &fp_date);
[e0c7cd1]675   /* Two digit year is 19xx. */
[734f5f4]676   if (y < 100) {
677      filepos fp_save;
678      get_pos(&fp_save);
679      y += 1900;
680      set_pos(&fp_date);
681      /* TRANSLATORS: %d will be replaced by the assumed year, e.g. 1918 */
682      compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Assuming 2 digit year is %d*/76, y);
683      set_pos(&fp_save);
684   }
[1ee204e]685   if (y < 1900 || y > 2078) {
[1d74718]686      set_pos(&fp_date);
[c270761]687      compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Invalid year (< 1900 or > 2078)*/58);
[e0c7cd1]688      LONGJMP(file.jbSkipLine);
689      return; /* for brain-fried compilers */
690   }
[950a829]691   if (ch == '.') {
[1d74718]692      filepos fp;
[950a829]693      nextch();
[1d74718]694      get_pos(&fp);
695      m = read_uint_internal(/*Expecting date, found “%s”*/198, &fp_date);
[e0c7cd1]696      if (m < 1 || m > 12) {
[1d74718]697         set_pos(&fp);
[c270761]698         compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Invalid month*/86);
[e0c7cd1]699         LONGJMP(file.jbSkipLine);
700         return; /* for brain-fried compilers */
[79d84d9]701      }
[950a829]702      if (ch == '.') {
703         nextch();
[1d74718]704         get_pos(&fp);
705         d = read_uint_internal(/*Expecting date, found “%s”*/198, &fp_date);
[e0c7cd1]706         if (d < 1 || d > last_day(y, m)) {
[1d74718]707            set_pos(&fp);
[736f7df]708            /* TRANSLATORS: e.g. 31st of April, or 32nd of any month */
[c270761]709            compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Invalid day of the month*/87);
[e0c7cd1]710            LONGJMP(file.jbSkipLine);
711            return; /* for brain-fried compilers */
712         }
[950a829]713      }
714   }
[b5a3219]715   if (py) *py = y;
716   if (pm) *pm = m;
717   if (pd) *pd = d;
[950a829]718}
Note: See TracBrowser for help on using the repository browser.