source: git/src/readval.c@ c046853

main
Last change on this file since c046853 was 1445470, checked in by Olly Betts <olly@…>, 6 weeks ago

Add more TRANSLATORS comments

  • Property mode set to 100644
File size: 27.8 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");
[99c26d5]472 static bool marked_space_as_used = false;
473 if (!marked_space_as_used) {
474 marked_space_as_used = true;
475 update_separator_map_for_foreign_name(" ");
476 update_output_separator();
477 }
[725d3b1]478 }
479 int len = s_len(&component);
480 char *p = s_steal(&component);
481 // Apply case treatment.
482 switch (pcs->Case) {
483 case LOWER:
484 for (int i = 0; i < len; ++i)
485 p[i] = tolower((unsigned char)p[i]);
486 break;
487 case UPPER:
488 for (int i = 0; i < len; ++i)
489 p[i] = toupper((unsigned char)p[i]);
490 break;
491 case OFF:
492 // Avoid unhandled enum warning.
493 break;
494 }
495
496 prefix *ptr = root;
497 for (int i = 0; i < 4; ++i) {
[ba84079]498 char *name;
[725d3b1]499 int sflag = BIT(SFLAGS_SURVEY);
500 if (i == 3) {
501 name = p;
[1bb4ba1]502 sflag = 0;
[725d3b1]503 } else {
504 if (i < 3 - explicit_prefix_levels) {
505 name = walls_prefix[i];
506 // printf("using walls_prefix[%d] = '%s'\n", 2 - i, name);
507 } else {
508 name = w_prefix[i - (3 - explicit_prefix_levels)]; // FIXME: Could steal wprefix[i].
509 // printf("using w_prefix[%d] = '%s'\n", i - (3 - explicit_prefix_levels), name);
510 }
511
512 if (name == NULL) {
513 // FIXME: This means :X::Y is treated as the same as
514 // ::X:Y but is that right? Walls docs don't really
515 // say. Need to test (and is they're different then
516 // probably use a character not valid in Walls station
517 // names for the empty prefix level (e.g. space or
518 // `#`).
519 //
520 // Also, does Walls allow :::X as a station and
521 // ::X:Y which would mean X is a station and survey?
522 // If so, we probably want to keep every empty level.
523 continue;
524 }
525 }
526 prefix *back_ptr = ptr;
527 ptr = ptr->down;
528 if (ptr == NULL) {
529 /* Special case first time around at each level */
[202e357]530 /* No need to check if we're at the station level - if the
531 * prefix is new the station must be. */
532 if (p_new) *p_new = true;
[725d3b1]533 ptr = osnew(prefix);
[ba84079]534 ptr->sflags = sflag;
535 if (strlen(name) < sizeof(ptr->ident.i)) {
536 strcpy(ptr->ident.i, name);
537 ptr->sflags |= BIT(SFLAGS_IDENT_INLINE);
[ae917b96]538 if (i >= 3) free(name);
[ba84079]539 } else {
540 ptr->ident.p = (i < 3 ? osstrdup(name) : name);
541 }
[725d3b1]542 name = NULL;
543 ptr->right = ptr->down = NULL;
544 ptr->pos = NULL;
545 ptr->stn = NULL;
546 ptr->up = back_ptr;
[98b705d]547 // FIXME: Or location of #Prefix, etc for it?
548 ptr->filename = file.filename;
549 ptr->line = file.line;
550 ptr->column = fp.offset - file.lpos;
551
[725d3b1]552 ptr->min_export = ptr->max_export = 0;
553 back_ptr->down = ptr;
554 } else {
555 /* Use caching to speed up adding an increasing sequence to a
556 * large survey */
557 static prefix *cached_survey = NULL, *cached_station = NULL;
558 prefix *ptrPrev = NULL;
559 int cmp = 1; /* result of strcmp ( -ve for <, 0 for =, +ve for > ) */
560 if (cached_survey == back_ptr) {
[ba84079]561 cmp = strcmp(prefix_ident(cached_station), name);
[725d3b1]562 if (cmp <= 0) ptr = cached_station;
563 }
[ba84079]564 while (ptr && (cmp = strcmp(prefix_ident(ptr), name))<0) {
[725d3b1]565 ptrPrev = ptr;
566 ptr = ptr->right;
567 }
568 if (cmp) {
569 /* ie we got to one that was higher, or the end */
[202e357]570 if (p_new) *p_new = true;
571 prefix *newptr = osnew(prefix);
[ba84079]572 newptr->sflags = sflag;
573 if (strlen(name) < sizeof(newptr->ident.i)) {
574 strcpy(newptr->ident.i, name);
575 newptr->sflags |= BIT(SFLAGS_IDENT_INLINE);
[ae917b96]576 if (i >= 3) free(name);
[ba84079]577 } else {
578 newptr->ident.p = (i < 3 ? osstrdup(name) : name);
579 }
[725d3b1]580 name = NULL;
581 if (ptrPrev == NULL)
582 back_ptr->down = newptr;
583 else
584 ptrPrev->right = newptr;
585 newptr->right = ptr;
586 newptr->down = NULL;
587 newptr->pos = NULL;
588 newptr->stn = NULL;
589 newptr->up = back_ptr;
[98b705d]590 // FIXME: Or location of #Prefix, etc for it?
591 newptr->filename = file.filename;
[725d3b1]592 newptr->line = file.line;
[98b705d]593 newptr->column = fp.offset - file.lpos;
594
[725d3b1]595 newptr->min_export = newptr->max_export = 0;
596 ptr = newptr;
597 } else {
[116b485]598 ptr->sflags |= sflag;
[725d3b1]599 }
[1bb4ba1]600 if (!TSTBIT(ptr->sflags, SFLAGS_SURVEY)) {
601 ptr->min_export = USHRT_MAX;
602 }
[725d3b1]603 cached_survey = back_ptr;
604 cached_station = ptr;
605 }
[ae917b96]606 if (name == p) free(p);
[725d3b1]607 }
608
[1bb4ba1]609 // Do the equivalent of "*infer exports" for Walls stations with an
610 // explicit prefix.
611 if (ptr->min_export == 0 || ptr->min_export == USHRT_MAX) {
612 if (explicit_prefix_levels > ptr->max_export)
613 ptr->max_export = explicit_prefix_levels;
614 }
[725d3b1]615
[ae917b96]616 for (int i = 0; i < 3; ++i) free(w_prefix[i]);
[725d3b1]617
618 return ptr;
619 }
620}
621
[21c226e]622/* if numeric expr is omitted: if f_optional return HUGE_REAL, else longjmp */
[725d3b1]623real
[e096a93]624read_number_or_int(bool f_optional, bool f_unsigned, bool* pf_decimal_point)
[a420b49]625{
[e096a93]626 if (pf_decimal_point) *pf_decimal_point = false;
[63d4f07]627 bool fPositive = true, fDigits = false;
[a420b49]628 real n = (real)0.0;
[c80bd34]629 filepos fp;
630 int ch_old;
[a420b49]631
[c80bd34]632 get_pos(&fp);
633 ch_old = ch;
[5a0ab6a]634 if (!f_unsigned) {
635 fPositive = !isMinus(ch);
636 if (isSign(ch)) nextch();
637 }
[a420b49]638
639 while (isdigit(ch)) {
[6974a34]640 n = n * (real)10.0 + (char)(ch - '0');
[d1b1380]641 nextch();
[63d4f07]642 fDigits = true;
[a420b49]643 }
644
645 if (isDecimal(ch)) {
[e096a93]646 if (pf_decimal_point) *pf_decimal_point = true;
[a420b49]647 real mult = (real)1.0;
648 nextch();
649 while (isdigit(ch)) {
650 mult *= (real).1;
651 n += (char)(ch - '0') * mult;
[63d4f07]652 fDigits = true;
[a420b49]653 nextch();
654 }
655 }
656
657 /* !'fRead' => !fDigits so fDigits => 'fRead' */
658 if (fDigits) return (fPositive ? n : -n);
659
660 /* didn't read a valid number. If it's optional, reset filepos & return */
[4fb15a1]661 set_pos(&fp);
[21c226e]662 if (f_optional) {
[a420b49]663 return HUGE_REAL;
664 }
665
[c80bd34]666 if (isOmit(ch_old)) {
[7962c9d]667 compile_diagnostic(DIAG_ERR|DIAG_COL, /*Field may not be omitted*/114);
[647407d]668 } else {
[cab0f26]669 compile_diagnostic_token_show(DIAG_ERR, /*Expecting numeric field, found “%s”*/9);
[647407d]670 }
[fdd4ed76]671 longjmp(jbSkipLine, 1);
[d1b1380]672}
[647407d]673
[725d3b1]674real
[4ca3219]675read_quadrant(bool f_optional)
676{
[5a0ab6a]677 enum {
678 POINT_N = 0,
679 POINT_E = 1,
680 POINT_S = 2,
681 POINT_W = 3,
682 POINT_NONE = -1
683 };
684 static const sztok pointtab[] = {
685 {"E", POINT_E },
686 {"N", POINT_N },
687 {"S", POINT_S },
688 {"W", POINT_W },
689 {NULL, POINT_NONE }
690 };
691 static const sztok pointewtab[] = {
692 {"E", POINT_E },
693 {"W", POINT_W },
694 {NULL, POINT_NONE }
695 };
696 if (f_optional && isOmit(ch)) {
697 return HUGE_REAL;
698 }
[9d9bee9]699 const int quad = 90;
700 filepos fp;
701 get_pos(&fp);
[e1cbc0d]702 get_token_legacy_no_blanks();
[5a0ab6a]703 int first_point = match_tok(pointtab, TABSIZE(pointtab));
704 if (first_point == POINT_NONE) {
705 set_pos(&fp);
706 if (isOmit(ch)) {
[7962c9d]707 compile_diagnostic(DIAG_ERR|DIAG_COL, /*Field may not be omitted*/114);
[5a0ab6a]708 }
[1445470]709 // TRANSLATORS: Quadrant bearings have the format e.g. S34E or N65.5W.
710 // They're enabled by `*units bearing quadrants` and supported in
711 // Walls format data.
[5a0ab6a]712 compile_diagnostic_token_show(DIAG_ERR, /*Expecting quadrant bearing, found “%s”*/483);
[fdd4ed76]713 longjmp(jbSkipLine, 1);
[9d9bee9]714 }
[63d4f07]715 real r = read_number(true, true);
[5a0ab6a]716 if (r == HUGE_REAL) {
717 if (isSign(ch) || isDecimal(ch)) {
718 /* Give better errors for S-0E, N+10W, N.E, etc. */
[9d9bee9]719 set_pos(&fp);
[728874f9]720 compile_diagnostic_token_show(DIAG_ERR, /*Expecting quadrant bearing, found “%s”*/483);
[fdd4ed76]721 longjmp(jbSkipLine, 1);
[9d9bee9]722 }
[5a0ab6a]723 /* N, S, E or W. */
724 return first_point * quad;
725 }
726 if (first_point == POINT_E || first_point == POINT_W) {
[9d9bee9]727 set_pos(&fp);
[728874f9]728 compile_diagnostic_token_show(DIAG_ERR, /*Expecting quadrant bearing, found “%s”*/483);
[fdd4ed76]729 longjmp(jbSkipLine, 1);
[4ca3219]730 }
[5a0ab6a]731
[e1cbc0d]732 get_token_legacy_no_blanks();
[eb429bf]733 int second_point = match_tok(pointewtab, TABSIZE(pointewtab));
734 if (second_point == POINT_NONE) {
[5a0ab6a]735 set_pos(&fp);
736 compile_diagnostic_token_show(DIAG_ERR, /*Expecting quadrant bearing, found “%s”*/483);
[fdd4ed76]737 longjmp(jbSkipLine, 1);
[4ca3219]738 }
[5a0ab6a]739
[eb429bf]740 if (r > quad) {
[5a0ab6a]741 set_pos(&fp);
[501dd27]742 compile_diagnostic_token_show(DIAG_ERR, /*Suspicious compass reading*/59);
[fdd4ed76]743 longjmp(jbSkipLine, 1);
[4ca3219]744 }
[5a0ab6a]745
746 if (first_point == POINT_N) {
747 if (second_point == POINT_W) {
748 r = quad * 4 - r;
749 }
750 } else {
751 if (second_point == POINT_W) {
752 r += quad * 2;
753 } else {
754 r = quad * 2 - r;
755 }
756 }
757 return r;
[4ca3219]758}
759
[21c226e]760extern real
[d3241e6]761read_numeric(bool f_optional)
762{
763 skipblanks();
[63d4f07]764 return read_number(f_optional, false);
[d3241e6]765}
766
767extern real
[61513b57]768read_numeric_multi(bool f_optional, bool f_quadrants, int *p_n_readings)
[21c226e]769{
770 size_t n_readings = 0;
771 real tot = (real)0.0;
772
773 skipblanks();
774 if (!isOpen(ch)) {
[4699c40]775 real r = 0;
[05b9de76]776 if (!f_quadrants) {
[63d4f07]777 r = read_number(f_optional, false);
[05b9de76]778 } else {
[5a0ab6a]779 r = read_quadrant(f_optional);
[05b9de76]780 if (r != HUGE_REAL)
781 do_legacy_token_warning();
782 }
[21c226e]783 if (p_n_readings) *p_n_readings = (r == HUGE_REAL ? 0 : 1);
784 return r;
785 }
786 nextch();
787
788 skipblanks();
789 do {
[05b9de76]790 if (!f_quadrants) {
[63d4f07]791 tot += read_number(false, false);
[05b9de76]792 } else {
[63d4f07]793 tot += read_quadrant(false);
[05b9de76]794 do_legacy_token_warning();
795 }
[21c226e]796 ++n_readings;
797 skipblanks();
798 } while (!isClose(ch));
799 nextch();
800
801 if (p_n_readings) *p_n_readings = n_readings;
802 /* FIXME: special averaging for bearings ... */
803 /* And for percentage gradient */
804 return tot / n_readings;
805}
806
[2aa2f3f]807/* read numeric expr or omit (return HUGE_REAL); else longjmp */
808extern real
[00b10c1]809read_bearing_multi_or_omit(bool f_quadrants, int *p_n_readings)
[2aa2f3f]810{
[9d9bee9]811 real v;
[63d4f07]812 v = read_numeric_multi(true, f_quadrants, p_n_readings);
[9d9bee9]813 if (v == HUGE_REAL) {
814 if (!isOmit(ch)) {
815 compile_diagnostic_token_show(DIAG_ERR, /*Expecting numeric field, found “%s”*/9);
[fdd4ed76]816 longjmp(jbSkipLine, 1);
[2aa2f3f]817 }
818 nextch();
819 }
820 return v;
821}
822
[950a829]823/* Don't skip blanks, variable error code */
[c7c0f93]824unsigned int
[f15c53d9]825read_uint_raw(int diag_type, int errmsg, const filepos *fp)
[647407d]826{
[421b7d2]827 unsigned int n = 0;
[647407d]828 if (!isdigit(ch)) {
[029cc3f]829 if (fp) set_pos(fp);
[f15c53d9]830 if ((diag_type & DIAG_UINT)) {
831 compile_diagnostic(diag_type, errmsg);
832 } else {
833 compile_diagnostic_token_show(diag_type, errmsg);
834 }
[fdd4ed76]835 longjmp(jbSkipLine, 1);
[647407d]836 }
837 while (isdigit(ch)) {
838 n = n * 10 + (char)(ch - '0');
839 nextch();
840 }
841 return n;
842}
[76bb277a]843
[950a829]844extern unsigned int
845read_uint(void)
846{
847 skipblanks();
[f15c53d9]848 return read_uint_raw(DIAG_ERR, /*Expecting numeric field, found “%s”*/9, NULL);
[950a829]849}
850
[5b9b9d4]851extern int
852read_int(int min_val, int max_val)
853{
854 skipblanks();
855 unsigned n = 0;
856 filepos fp;
857
858 get_pos(&fp);
[1000d9d]859 bool negated = isMinus(ch);
860 unsigned limit;
861 if (negated) {
862 limit = (unsigned)(min_val == INT_MIN ? INT_MIN : -min_val);
863 } else {
864 limit = (unsigned)max_val;
865 }
[5b9b9d4]866 if (isSign(ch)) nextch();
867
868 if (!isdigit(ch)) {
869bad_value:
870 set_pos(&fp);
871 /* TRANSLATORS: The first %d will be replaced by the (inclusive) lower
872 * bound and the second by the (inclusive) upper bound, for example:
873 * Expecting integer in range -60 to 60
874 */
875 compile_diagnostic(DIAG_ERR|DIAG_NUM, /*Expecting integer in range %d to %d*/489);
[fdd4ed76]876 longjmp(jbSkipLine, 1);
[5b9b9d4]877 }
878
879 while (isdigit(ch)) {
880 unsigned old_n = n;
881 n = n * 10 + (char)(ch - '0');
882 if (n > limit || n < old_n) {
883 goto bad_value;
884 }
885 nextch();
886 }
887 if (isDecimal(ch)) goto bad_value;
888
889 if (negated) {
890 if (n > (unsigned)INT_MAX) {
891 // Avoid unportable casting.
892 return INT_MIN;
893 }
894 return -(int)n;
895 }
896 return (int)n;
897}
898
[62be3ee]899static bool
900read_string_(string *pstr, int diag_type)
[76bb277a]901{
[0532954]902 s_clear(pstr);
[76bb277a]903
904 skipblanks();
905 if (ch == '\"') {
[1157304]906 /* String quoted in "" */
[76bb277a]907 nextch();
[457a390]908 while (1) {
909 if (isEol(ch)) {
[62be3ee]910 compile_diagnostic(diag_type|DIAG_COL, /*Missing \"*/69);
911 if (diag_type == DIAG_ERR) longjmp(jbSkipLine, 1);
912 return false;
[457a390]913 }
914
915 if (ch == '\"') break;
916
[94d9f64]917 s_appendch(pstr, ch);
[457a390]918 nextch();
919 }
[66f5fc4]920 nextch();
[457a390]921 } else {
[1157304]922 /* Unquoted string */
[457a390]923 while (1) {
924 if (isEol(ch) || isComm(ch)) {
[0532954]925 if (s_empty(pstr)) {
[62be3ee]926 compile_diagnostic(diag_type|DIAG_COL, /*Expecting string field*/121);
927 if (diag_type == DIAG_ERR) longjmp(jbSkipLine, 1);
928 return false;
[b97d134]929 }
[62be3ee]930 return true;
[76bb277a]931 }
932
[457a390]933 if (isBlank(ch)) break;
[76bb277a]934
[94d9f64]935 s_appendch(pstr, ch);
[457a390]936 nextch();
937 }
[76bb277a]938 }
[62be3ee]939 return true;
940}
941
942extern void
943read_string(string *pstr)
944{
945 (void)read_string_(pstr, DIAG_ERR);
946}
947
948extern bool
949read_string_warning(string *pstr)
950{
951 return read_string_(pstr, DIAG_WARN);
[76bb277a]952}
[950a829]953
[725d3b1]954extern void
955read_walls_srv_date(int *py, int *pm, int *pd)
956{
957 skipblanks();
958
959 filepos fp_date;
960 get_pos(&fp_date);
[f15c53d9]961 unsigned y = read_uint_raw(DIAG_ERR, /*Expecting date, found “%s”*/198, &fp_date);
[725d3b1]962 int separator = -2;
963 if (ch == '-' || ch == '/') {
964 separator = ch;
965 nextch();
966 }
967 filepos fp_month;
968 get_pos(&fp_month);
[f15c53d9]969 unsigned m = read_uint_raw(DIAG_ERR, /*Expecting date, found “%s”*/198, &fp_date);
[725d3b1]970 if (ch == separator) {
971 nextch();
972 }
973 filepos fp_day;
974 get_pos(&fp_day);
[f15c53d9]975 unsigned d = read_uint_raw(DIAG_ERR, /*Expecting date, found “%s”*/198, &fp_date);
[725d3b1]976
977 filepos fp_year;
978 if (y < 100) {
979 // Walls recommends ISO 8601 date format (yyyy-mm-dd and seemingly the
980 // non-standard variant yyyy/mm/dd), but also accepts "some date formats
981 // common in the U.S. (mm/dd/yy, mm-dd-yyyy, etc.)"
982 unsigned tmp = y;
983 y = d;
984 fp_year = fp_day;
985 d = m;
986 fp_day = fp_month;
987 m = tmp;
988 fp_month = fp_date;
989
990 if (y < 100) {
991 // FIXME: Are all 2 digit years 19xx?
992 y += 1900;
993
994 filepos fp_save;
995 get_pos(&fp_save);
996 set_pos(&fp_year);
997 /* TRANSLATORS: %d will be replaced by the assumed year, e.g. 1918 */
998 compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Assuming 2 digit year is %d*/76, y);
999 set_pos(&fp_save);
1000 }
1001 } else {
1002 if (y < 1900 || y > 2078) {
1003 set_pos(&fp_date);
1004 compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Invalid year (< 1900 or > 2078)*/58);
[fdd4ed76]1005 longjmp(jbSkipLine, 1);
[725d3b1]1006 }
1007 fp_year = fp_date;
1008 }
1009
1010 if (m < 1 || m > 12) {
1011 set_pos(&fp_month);
1012 compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Invalid month*/86);
[fdd4ed76]1013 longjmp(jbSkipLine, 1);
[725d3b1]1014 }
1015
[c7c0f93]1016 if (d < 1 || d > (unsigned)last_day(y, m)) {
[725d3b1]1017 set_pos(&fp_day);
1018 /* TRANSLATORS: e.g. 31st of April, or 32nd of any month */
1019 compile_diagnostic(DIAG_WARN|DIAG_UINT, /*Invalid day of the month*/87);
[fdd4ed76]1020 longjmp(jbSkipLine, 1);
[725d3b1]1021 }
1022
1023 if (py) *py = y;
1024 if (pm) *pm = m;
1025 if (pd) *pd = d;
1026}
Note: See TracBrowser for help on using the repository browser.