source: git/src/readval.c @ 91b80d0

stereo-2025
Last change on this file since 91b80d0 was fdd4ed76, checked in by Olly Betts <olly@…>, 7 months ago

Move jbSkipLine out of struct parse

We only need one jmp_buf so it adds needless bloat to struct parse
(e.g. 200 bytes on x86-64 glibc linux).

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