source: git/src/readval.c @ 9fcc81a

RELEASE/1.2debug-cidebug-ci-sanitiserswalls-datawalls-data-hanging-as-warning
Last change on this file since 9fcc81a was 0fa4371, checked in by Olly Betts <olly@…>, 5 years ago

cavern: Fix segfault for *include ""

This isn't valid, but now reports "file not found" instead.

  • Property mode set to 100644
File size: 15.5 KB
RevLine 
[6974a34]1/* readval.c
[0395657]2 * Routines to read a prefix or number from the current input file
[0fa4371]3 * Copyright (C) 1991-2003,2005,2006,2010,2011,2012,2013,2014,2015,2016,2018,2019 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"
[1ee204e]28#include "date.h"
[dd5b1a8]29#include "debug.h"
[a1706d1]30#include "filename.h"
31#include "message.h"
[d1b1380]32#include "readval.h"
33#include "datain.h"
[fb2e93c]34#include "netbits.h"
[8cf2e04]35#include "osalloc.h"
[76bb277a]36#include "str.h"
[d1b1380]37
[7d5f3c0]38#ifdef HAVE_SETJMP_H
[647407d]39# define LONGJMP(JB) longjmp((JB), 1)
40#else
41# define LONGJMP(JB) exit(1)
42#endif
43
[c86cc71]44int root_depr_count = 0;
45
[710ecc1]46static prefix *
[a2c33ae]47new_anon_station(void)
[710ecc1]48{
[a2c33ae]49    prefix *name = osnew(prefix);
50    name->pos = NULL;
51    name->ident = NULL;
52    name->shape = 0;
53    name->stn = NULL;
54    name->up = pcs->Prefix;
55    name->down = NULL;
56    name->filename = file.filename;
57    name->line = file.line;
[5fc783b]58    name->min_export = name->max_export = 0;
[a2c33ae]59    name->sflags = BIT(SFLAGS_ANON);
60    /* Keep linked list of anon stations for node stats. */
61    name->right = anon_list;
62    anon_list = name;
63    return name;
[710ecc1]64}
65
[c458cf7]66/* if prefix is omitted: if PFX_OPT set return NULL, otherwise use longjmp */
67extern prefix *
68read_prefix(unsigned pfx_flags)
[a420b49]69{
[c458cf7]70   bool f_optional = !!(pfx_flags & PFX_OPT);
71   bool fSurvey = !!(pfx_flags & PFX_SURVEY);
72   bool fSuspectTypo = !!(pfx_flags & PFX_SUSPECT_TYPO);
[4a6a094]73   prefix *back_ptr, *ptr;
[a420b49]74   char *name;
75   size_t name_len = 32;
[eb18f4d]76   size_t i;
[647407d]77   bool fNew;
[d6274ba]78   bool fImplicitPrefix = fTrue;
[932f7e9]79   int depth = -1;
[5b4085b]80   filepos fp_firstsep;
[a420b49]81
82   skipblanks();
[7f1ab95]83#ifndef NO_DEPRECATED
[a420b49]84   if (isRoot(ch)) {
[c458cf7]85      if (!(pfx_flags & PFX_ALLOW_ROOT)) {
[cab0f26]86         compile_diagnostic(DIAG_ERR|DIAG_COL, /*ROOT is deprecated*/25);
[421b7d2]87         LONGJMP(file.jbSkipLine);
[84c60fc]88      }
[c86cc71]89      if (root_depr_count < 5) {
[cab0f26]90         compile_diagnostic(DIAG_WARN|DIAG_COL, /*ROOT is deprecated*/25);
[c86cc71]91         if (++root_depr_count == 5)
[cab0f26]92            compile_diagnostic(DIAG_WARN, /*Further uses of this deprecated feature will not be reported*/95);
[c86cc71]93      }
[d1b1380]94      nextch();
[a420b49]95      ptr = root;
96      if (!isNames(ch)) {
97         if (!isSep(ch)) return ptr;
[27b8b59]98         /* Allow optional SEPARATOR after ROOT */
[5b4085b]99         get_pos(&fp_firstsep);
[a420b49]100         nextch();
101      }
[647407d]102      fImplicitPrefix = fFalse;
[710ecc1]103#else
104   if (0) {
105#endif
[a420b49]106   } else {
[dcbcae0]107      if ((pfx_flags & PFX_ANON) &&
108          (isSep(ch) || (pcs->dash_for_anon_wall_station && ch == '-'))) {
109         int first_ch = ch;
[710ecc1]110         filepos here;
111         get_pos(&here);
112         nextch();
113         if (isBlank(ch) || isEol(ch)) {
[dcbcae0]114            if (!isSep(first_ch))
115               goto anon_wall_station;
116            /* A single separator alone ('.' by default) is an anonymous
117             * station which is on a point inside the passage and implies
118             * the leg to it is a splay.
119             */
120            if (TSTBIT(pcs->flags, FLAGS_ANON_ONE_END)) {
[9f55538]121               set_pos(&here);
[1d74718]122               compile_diagnostic(DIAG_ERR|DIAG_TOKEN, /*Can't have a leg between two anonymous stations*/3);
[dcbcae0]123               LONGJMP(file.jbSkipLine);
[710ecc1]124            }
[dcbcae0]125            pcs->flags |= BIT(FLAGS_ANON_ONE_END) | BIT(FLAGS_IMPLICIT_SPLAY);
[a2c33ae]126            return new_anon_station();
[710ecc1]127         }
[dcbcae0]128         if (isSep(first_ch) && ch == first_ch) {
129            nextch();
130            if (isBlank(ch) || isEol(ch)) {
131               /* A double separator ('..' by default) is an anonymous station
132                * which is on the wall and implies the leg to it is a splay.
133                */
134               prefix * pfx;
135anon_wall_station:
136               if (TSTBIT(pcs->flags, FLAGS_ANON_ONE_END)) {
[9f55538]137                  set_pos(&here);
[1d74718]138                  compile_diagnostic(DIAG_ERR|DIAG_TOKEN, /*Can't have a leg between two anonymous stations*/3);
[dcbcae0]139                  LONGJMP(file.jbSkipLine);
140               }
[da65891]141               pcs->flags |= BIT(FLAGS_ANON_ONE_END) | BIT(FLAGS_IMPLICIT_SPLAY);
[dcbcae0]142               pfx = new_anon_station();
143               pfx->sflags |= BIT(SFLAGS_WALL);
144               return pfx;
145            }
146            if (ch == first_ch) {
147               nextch();
148               if (isBlank(ch) || isEol(ch)) {
149                  /* A triple separator ('...' by default) is an anonymous
150                   * station, but otherwise not handled specially (e.g. for
151                   * a single leg down an unexplored side passage to a station
152                   * which isn't refindable).
153                   */
154                  if (TSTBIT(pcs->flags, FLAGS_ANON_ONE_END)) {
[9f55538]155                     set_pos(&here);
[1d74718]156                     compile_diagnostic(DIAG_ERR|DIAG_TOKEN, /*Can't have a leg between two anonymous stations*/3);
[dcbcae0]157                     LONGJMP(file.jbSkipLine);
158                  }
159                  pcs->flags |= BIT(FLAGS_ANON_ONE_END);
160                  return new_anon_station();
161               }
162            }
163         }
[710ecc1]164         set_pos(&here);
165      }
[a420b49]166      ptr = pcs->Prefix;
167   }
168
169   i = 0;
[be97baf]170   name = NULL;
[a420b49]171   do {
[647407d]172      fNew = fFalse;
[be97baf]173      if (name == NULL) {
174         /* Need a new name buffer */
175         name = osmalloc(name_len);
176      }
[a420b49]177      /* i==0 iff this is the first pass */
178      if (i) {
179         i = 0;
180         nextch();
181      }
182      while (isNames(ch)) {
183         if (i < pcs->Truncate) {
184            /* truncate name */
[1157304]185            name[i++] = (pcs->Case == LOWER ? tolower(ch) :
186                         (pcs->Case == OFF ? ch : toupper(ch)));
[a420b49]187            if (i >= name_len) {
188               name_len = name_len + name_len;
189               name = osrealloc(name, name_len);
190            }
[8cf2e04]191         }
[a420b49]192         nextch();
[8cf2e04]193      }
[5b4085b]194      if (isSep(ch)) {
195         fImplicitPrefix = fFalse;
196         get_pos(&fp_firstsep);
197      }
[a420b49]198      if (i == 0) {
199         osfree(name);
[21c226e]200         if (!f_optional) {
[ee1d81f]201            if (isEol(ch)) {
[84c60fc]202               if (fSurvey) {
[cab0f26]203                  compile_diagnostic(DIAG_ERR|DIAG_COL, /*Expecting survey name*/89);
[84c60fc]204               } else {
[cab0f26]205                  compile_diagnostic(DIAG_ERR|DIAG_COL, /*Expecting station name*/28);
[84c60fc]206               }
[ee1d81f]207            } else {
[736f7df]208               /* TRANSLATORS: Here "station" is a survey station, not a train station. */
[cab0f26]209               compile_diagnostic(DIAG_ERR|DIAG_COL, /*Character “%c” not allowed in station name (use *SET NAMES to set allowed characters)*/7, ch);
[ee1d81f]210            }
[647407d]211            LONGJMP(file.jbSkipLine);
212         }
[a420b49]213         return (prefix *)NULL;
[d1b1380]214      }
[a420b49]215
[76bb277a]216      name[i++] = '\0';
[cb3d1e2]217
[a420b49]218      back_ptr = ptr;
219      ptr = ptr->down;
220      if (ptr == NULL) {
221         /* Special case first time around at each level */
[be97baf]222         name = osrealloc(name, i);
[a420b49]223         ptr = osnew(prefix);
224         ptr->ident = name;
[be97baf]225         name = NULL;
[a420b49]226         ptr->right = ptr->down = NULL;
227         ptr->pos = NULL;
[6adb88c]228         ptr->shape = 0;
[a420b49]229         ptr->stn = NULL;
230         ptr->up = back_ptr;
[d333899]231         ptr->filename = file.filename;
232         ptr->line = file.line;
[932f7e9]233         ptr->min_export = ptr->max_export = 0;
[95c3272]234         ptr->sflags = BIT(SFLAGS_SURVEY);
235         if (fSuspectTypo && !fImplicitPrefix)
236            ptr->sflags |= BIT(SFLAGS_SUSPECTTYPO);
[a420b49]237         back_ptr->down = ptr;
[647407d]238         fNew = fTrue;
[a420b49]239      } else {
[be97baf]240         /* Use caching to speed up adding an increasing sequence to a
241          * large survey */
242         static prefix *cached_survey = NULL, *cached_station = NULL;
[a420b49]243         prefix *ptrPrev = NULL;
[76bb277a]244         int cmp = 1; /* result of strcmp ( -ve for <, 0 for =, +ve for > ) */
[be97baf]245         if (cached_survey == back_ptr) {
246            cmp = strcmp(cached_station->ident, name);
247            if (cmp <= 0) ptr = cached_station;
248         }
249         while (ptr && (cmp = strcmp(ptr->ident, name))<0) {
[a420b49]250            ptrPrev = ptr;
251            ptr = ptr->right;
252         }
253         if (cmp) {
254            /* ie we got to one that was higher, or the end */
[be97baf]255            prefix *newptr;
256            name = osrealloc(name, i);
257            newptr = osnew(prefix);
[647407d]258            newptr->ident = name;
[be97baf]259            name = NULL;
[a420b49]260            if (ptrPrev == NULL)
[647407d]261               back_ptr->down = newptr;
[a420b49]262            else
[647407d]263               ptrPrev->right = newptr;
264            newptr->right = ptr;
265            newptr->down = NULL;
266            newptr->pos = NULL;
[6adb88c]267            newptr->shape = 0;
[647407d]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            newptr->sflags = BIT(SFLAGS_SURVEY);
274            if (fSuspectTypo && !fImplicitPrefix)
275               newptr->sflags |= BIT(SFLAGS_SUSPECTTYPO);
[647407d]276            ptr = newptr;
277            fNew = fTrue;
[a420b49]278         }
[be97baf]279         cached_survey = back_ptr;
280         cached_station = ptr;
[d1b1380]281      }
[fb2e93c]282      depth++;
[21c226e]283      f_optional = fFalse; /* disallow after first level */
[5b4085b]284      if (isSep(ch)) get_pos(&fp_firstsep);
[a420b49]285   } while (isSep(ch));
[be97baf]286   if (name) osfree(name);
287
[d40f787]288   /* don't warn about a station that is referred to twice */
[95c3272]289   if (!fNew) ptr->sflags &= ~BIT(SFLAGS_SUSPECTTYPO);
[421b7d2]290
[6430f0e]291   if (fNew) {
[95c3272]292      /* fNew means SFLAGS_SURVEY is currently set */
[4c07c51]293      SVX_ASSERT(TSTBIT(ptr->sflags, SFLAGS_SURVEY));
[c00c74a9]294      if (!fSurvey) {
295         ptr->sflags &= ~BIT(SFLAGS_SURVEY);
[27b8b59]296         if (TSTBIT(pcs->infer, INFER_EXPORTS)) ptr->min_export = USHRT_MAX;
[c00c74a9]297      }
[6430f0e]298   } else {
299      /* check that the same name isn't being used for a survey and station */
[95c3272]300      if (fSurvey ^ TSTBIT(ptr->sflags, SFLAGS_SURVEY)) {
[0b8c321]301         /* TRANSLATORS: Here "station" is a survey station, not a train station.
302          *
303          * Here "survey" is a "cave map" rather than list of questions - it should be
304          * translated to the terminology that cavers using the language would use.
305          */
[cab0f26]306         compile_diagnostic(DIAG_ERR, /*“%s” can’t be both a station and a survey*/27,
307                            sprint_prefix(ptr));
[6430f0e]308      }
[27b8b59]309      if (!fSurvey && TSTBIT(pcs->infer, INFER_EXPORTS)) ptr->min_export = USHRT_MAX;
[051a72b]310   }
311
[932f7e9]312   /* check the export level */
313#if 0
314   printf("R min %d max %d depth %d pfx %s\n",
315          ptr->min_export, ptr->max_export, depth, sprint_prefix(ptr));
316#endif
[c00c74a9]317   if (ptr->min_export == 0 || ptr->min_export == USHRT_MAX) {
[932f7e9]318      if (depth > ptr->max_export) ptr->max_export = depth;
319   } else if (ptr->max_export < depth) {
[dd5b1a8]320      prefix *survey = ptr;
321      char *s;
[93e3492]322      const char *p;
[dd5b1a8]323      int level;
324      for (level = ptr->max_export + 1; level; level--) {
325         survey = survey->up;
[4c07c51]326         SVX_ASSERT(survey);
[dd5b1a8]327      }
328      s = osstrdup(sprint_prefix(survey));
[93e3492]329      p = sprint_prefix(ptr);
[016068a]330      if (survey->filename) {
[cab0f26]331         compile_diagnostic_pfx(DIAG_ERR, survey,
332                                /*Station “%s” not exported from survey “%s”*/26,
333                                p, s);
[93e3492]334      } else {
[cab0f26]335         compile_diagnostic(DIAG_ERR, /*Station “%s” not exported from survey “%s”*/26, p, s);
[016068a]336      }
[dd5b1a8]337      osfree(s);
[932f7e9]338#if 0
339      printf(" *** pfx %s warning not exported enough depth %d "
[421b7d2]340             "ptr->max_export %d\n", sprint_prefix(ptr),
[932f7e9]341             depth, ptr->max_export);
342#endif
343   }
[0fa7aac]344   if (!fImplicitPrefix && (pfx_flags & PFX_WARN_SEPARATOR)) {
[5b4085b]345      filepos fp_tmp;
346      get_pos(&fp_tmp);
347      set_pos(&fp_firstsep);
348      compile_diagnostic(DIAG_WARN|DIAG_COL, /*Separator in survey name*/392);
349      set_pos(&fp_tmp);
[0fa7aac]350   }
[a420b49]351   return ptr;
[d1b1380]352}
353
[21c226e]354/* if numeric expr is omitted: if f_optional return HUGE_REAL, else longjmp */
355static real
356read_number(bool f_optional)
[a420b49]357{
358   bool fPositive, fDigits = fFalse;
359   real n = (real)0.0;
[c80bd34]360   filepos fp;
361   int ch_old;
[a420b49]362
[c80bd34]363   get_pos(&fp);
364   ch_old = ch;
[a420b49]365   fPositive = !isMinus(ch);
366   if (isSign(ch)) nextch();
367
368   while (isdigit(ch)) {
[6974a34]369      n = n * (real)10.0 + (char)(ch - '0');
[d1b1380]370      nextch();
[a420b49]371      fDigits = fTrue;
372   }
373
374   if (isDecimal(ch)) {
375      real mult = (real)1.0;
376      nextch();
377      while (isdigit(ch)) {
378         mult *= (real).1;
379         n += (char)(ch - '0') * mult;
380         fDigits = fTrue;
381         nextch();
382      }
383   }
384
385   /* !'fRead' => !fDigits so fDigits => 'fRead' */
386   if (fDigits) return (fPositive ? n : -n);
387
388   /* didn't read a valid number.  If it's optional, reset filepos & return */
[4fb15a1]389   set_pos(&fp);
[21c226e]390   if (f_optional) {
[a420b49]391      return HUGE_REAL;
392   }
393
[c80bd34]394   if (isOmit(ch_old)) {
[cab0f26]395      compile_diagnostic(DIAG_ERR|DIAG_COL, /*Field may not be omitted*/8);
[647407d]396   } else {
[cab0f26]397      compile_diagnostic_token_show(DIAG_ERR, /*Expecting numeric field, found “%s”*/9);
[647407d]398   }
399   LONGJMP(file.jbSkipLine);
[a420b49]400   return 0.0; /* for brain-fried compilers */
[d1b1380]401}
[647407d]402
[21c226e]403extern real
[d3241e6]404read_numeric(bool f_optional)
405{
406   skipblanks();
407   return read_number(f_optional);
408}
409
410extern real
411read_numeric_multi(bool f_optional, int *p_n_readings)
[21c226e]412{
413   size_t n_readings = 0;
414   real tot = (real)0.0;
415
416   skipblanks();
417   if (!isOpen(ch)) {
418      real r = read_number(f_optional);
419      if (p_n_readings) *p_n_readings = (r == HUGE_REAL ? 0 : 1);
420      return r;
421   }
422   nextch();
423
424   skipblanks();
425   do {
426      tot += read_number(fFalse);
427      ++n_readings;
428      skipblanks();
429   } while (!isClose(ch));
430   nextch();
431
432   if (p_n_readings) *p_n_readings = n_readings;
433   /* FIXME: special averaging for bearings ... */
434   /* And for percentage gradient */
435   return tot / n_readings;
436}
437
[2aa2f3f]438/* read numeric expr or omit (return HUGE_REAL); else longjmp */
439extern real
[d3241e6]440read_numeric_multi_or_omit(int *p_n_readings)
[2aa2f3f]441{
[d3241e6]442   real v = read_numeric_multi(fTrue, p_n_readings);
[2aa2f3f]443   if (v == HUGE_REAL) {
444      if (!isOmit(ch)) {
[cab0f26]445         compile_diagnostic_token_show(DIAG_ERR, /*Expecting numeric field, found “%s”*/9);
[2aa2f3f]446         LONGJMP(file.jbSkipLine);
447         return 0.0; /* for brain-fried compilers */
448      }
449      nextch();
450   }
451   return v;
452}
453
[950a829]454/* Don't skip blanks, variable error code */
455static unsigned int
[75e67ab]456read_uint_internal(int errmsg, const filepos *fp)
[647407d]457{
[421b7d2]458   unsigned int n = 0;
[647407d]459   if (!isdigit(ch)) {
[029cc3f]460      if (fp) set_pos(fp);
[cab0f26]461      compile_diagnostic_token_show(DIAG_ERR, errmsg);
[647407d]462      LONGJMP(file.jbSkipLine);
463   }
464   while (isdigit(ch)) {
465      n = n * 10 + (char)(ch - '0');
466      nextch();
467   }
468   return n;
469}
[76bb277a]470
[950a829]471extern unsigned int
472read_uint(void)
473{
474   skipblanks();
[0804fbe]475   return read_uint_internal(/*Expecting numeric field, found “%s”*/9, NULL);
[950a829]476}
477
[76bb277a]478extern void
479read_string(char **pstr, int *plen)
480{
481   s_zero(pstr);
482
483   skipblanks();
484   if (ch == '\"') {
[1157304]485      /* String quoted in "" */
[76bb277a]486      nextch();
[457a390]487      while (1) {
488         if (isEol(ch)) {
[cab0f26]489            compile_diagnostic(DIAG_ERR|DIAG_COL, /*Missing \"*/69);
[4fb15a1]490            LONGJMP(file.jbSkipLine);
[457a390]491         }
492
493         if (ch == '\"') break;
494
495         s_catchar(pstr, plen, ch);
496         nextch();
497      }
[0fa4371]498      if (!*pstr) {
499         /* Return empty string for "", not NULL. */
500         s_catchar(pstr, plen, '\0');
501      }
[66f5fc4]502      nextch();
[457a390]503   } else {
[1157304]504      /* Unquoted string */
[457a390]505      while (1) {
506         if (isEol(ch) || isComm(ch)) {
[b97d134]507            if (!*pstr || !(*pstr)[0]) {
[cab0f26]508               compile_diagnostic(DIAG_ERR|DIAG_COL, /*Expecting string field*/121);
[6f61f83]509               LONGJMP(file.jbSkipLine);
[b97d134]510            }
[457a390]511            return;
[76bb277a]512         }
513
[457a390]514         if (isBlank(ch)) break;
[76bb277a]515
[457a390]516         s_catchar(pstr, plen, ch);
517         nextch();
518      }
[76bb277a]519   }
520}
[950a829]521
522extern void
[b5a3219]523read_date(int *py, int *pm, int *pd)
[950a829]524{
[734f5f4]525   unsigned int y = 0, m = 0, d = 0;
[1d74718]526   filepos fp_date;
[950a829]527
528   skipblanks();
[029cc3f]529
[1d74718]530   get_pos(&fp_date);
531   y = read_uint_internal(/*Expecting date, found “%s”*/198, &fp_date);
[e0c7cd1]532   /* Two digit year is 19xx. */
[734f5f4]533   if (y < 100) {
534      filepos fp_save;
535      get_pos(&fp_save);
536      y += 1900;
537      set_pos(&fp_date);
538      /* TRANSLATORS: %d will be replaced by the assumed year, e.g. 1918 */
539      compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Assuming 2 digit year is %d*/76, y);
540      set_pos(&fp_save);
541   }
[1ee204e]542   if (y < 1900 || y > 2078) {
[1d74718]543      set_pos(&fp_date);
[c270761]544      compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Invalid year (< 1900 or > 2078)*/58);
[e0c7cd1]545      LONGJMP(file.jbSkipLine);
546      return; /* for brain-fried compilers */
547   }
[950a829]548   if (ch == '.') {
[1d74718]549      filepos fp;
[950a829]550      nextch();
[1d74718]551      get_pos(&fp);
552      m = read_uint_internal(/*Expecting date, found “%s”*/198, &fp_date);
[e0c7cd1]553      if (m < 1 || m > 12) {
[1d74718]554         set_pos(&fp);
[c270761]555         compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Invalid month*/86);
[e0c7cd1]556         LONGJMP(file.jbSkipLine);
557         return; /* for brain-fried compilers */
[79d84d9]558      }
[950a829]559      if (ch == '.') {
560         nextch();
[1d74718]561         get_pos(&fp);
562         d = read_uint_internal(/*Expecting date, found “%s”*/198, &fp_date);
[e0c7cd1]563         if (d < 1 || d > last_day(y, m)) {
[1d74718]564            set_pos(&fp);
[736f7df]565            /* TRANSLATORS: e.g. 31st of April, or 32nd of any month */
[c270761]566            compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Invalid day of the month*/87);
[e0c7cd1]567            LONGJMP(file.jbSkipLine);
568            return; /* for brain-fried compilers */
569         }
[950a829]570      }
571   }
[b5a3219]572   if (py) *py = y;
573   if (pm) *pm = m;
574   if (pd) *pd = d;
[950a829]575}
Note: See TracBrowser for help on using the repository browser.