3 * Copyright (C) 1991-2022 Olly Betts
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.
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.
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
26 #include <stddef.h> /* for offsetof */
30 #if PROJ_VERSION_MAJOR < 8 || \
31 (PROJ_VERSION_MAJOR == 8 && PROJ_VERSION_MINOR < 2)
32 /* Needed for proj_factors workaround */
33 # include <proj_experimental.h>
49 #define WGS84_DATUM_STRING "EPSG:4326"
52 default_grade(settings
*s
)
54 /* Values correspond to those in bcra5.svx */
55 s
->Var
[Q_POS
] = (real
)sqrd(0.05);
56 s
->Var
[Q_LENGTH
] = (real
)sqrd(0.05);
57 s
->Var
[Q_BACKLENGTH
] = (real
)sqrd(0.05);
58 s
->Var
[Q_COUNT
] = (real
)sqrd(0.05);
59 s
->Var
[Q_DX
] = s
->Var
[Q_DY
] = s
->Var
[Q_DZ
] = (real
)sqrd(0.05);
60 s
->Var
[Q_BEARING
] = (real
)sqrd(rad(0.5));
61 s
->Var
[Q_GRADIENT
] = (real
)sqrd(rad(0.5));
62 s
->Var
[Q_BACKBEARING
] = (real
)sqrd(rad(0.5));
63 s
->Var
[Q_BACKGRADIENT
] = (real
)sqrd(rad(0.5));
64 /* SD of plumbed legs (0.25 degrees?) */
65 s
->Var
[Q_PLUMB
] = (real
)sqrd(rad(0.25));
66 /* SD of level legs (0.25 degrees?) */
67 s
->Var
[Q_LEVEL
] = (real
)sqrd(rad(0.25));
68 s
->Var
[Q_DEPTH
] = (real
)sqrd(0.05);
72 default_truncate(settings
*s
)
74 s
->Truncate
= INT_MAX
;
78 default_case(settings
*s
)
83 static reading default_order
[] = { Fr
, To
, Tape
, Comp
, Clino
, End
};
86 default_style(settings
*s
)
88 s
->style
= STYLE_NORMAL
;
89 s
->ordering
= default_order
;
90 s
->dash_for_anon_wall_station
= fFalse
;
94 default_prefix(settings
*s
)
100 default_translate(settings
*s
)
104 if (s
->next
&& s
->next
->Translate
== s
->Translate
) {
105 t
= ((short*)osmalloc(ossizeof(short) * 257)) + 1;
106 memcpy(t
- 1, s
->Translate
- 1, sizeof(short) * 257);
109 /* SVX_ASSERT(EOF==-1);*/ /* important, since we rely on this */
111 memset(t
- 1, 0, sizeof(short) * 257);
112 for (i
= '0'; i
<= '9'; i
++) t
[i
] = SPECIAL_NAMES
;
113 for (i
= 'A'; i
<= 'Z'; i
++) t
[i
] = SPECIAL_NAMES
;
114 for (i
= 'a'; i
<= 'z'; i
++) t
[i
] = SPECIAL_NAMES
;
116 t
['\t'] |= SPECIAL_BLANK
;
117 t
[' '] |= SPECIAL_BLANK
;
118 t
[','] |= SPECIAL_BLANK
;
119 t
[';'] |= SPECIAL_COMMENT
;
120 t
['\032'] |= SPECIAL_EOL
; /* Ctrl-Z, so olde DOS text files are handled ok */
121 t
[EOF
] |= SPECIAL_EOL
;
122 t
['\n'] |= SPECIAL_EOL
;
123 t
['\r'] |= SPECIAL_EOL
;
124 t
['*'] |= SPECIAL_KEYWORD
;
125 t
['-'] |= SPECIAL_OMIT
;
126 t
['\\'] |= SPECIAL_ROOT
;
127 t
['.'] |= SPECIAL_SEPARATOR
;
128 t
['_'] |= SPECIAL_NAMES
;
129 t
['-'] |= SPECIAL_NAMES
; /* Added in 0.97 prerelease 4 */
130 t
['.'] |= SPECIAL_DECIMAL
;
131 t
['-'] |= SPECIAL_MINUS
;
132 t
['+'] |= SPECIAL_PLUS
;
134 t
['{'] |= SPECIAL_OPEN
;
135 t
['}'] |= SPECIAL_CLOSE
;
140 default_units(settings
*s
)
143 for (quantity
= 0; quantity
< Q_MAC
; quantity
++) {
144 if (TSTBIT(ANG_QMASK
, quantity
))
145 s
->units
[quantity
] = (real
)(M_PI
/ 180.0); /* degrees */
147 s
->units
[quantity
] = (real
)1.0; /* metres */
149 s
->f_clino_percent
= s
->f_backclino_percent
= fFalse
;
150 s
->f_bearing_quadrants
= s
->f_backbearing_quadrants
= fFalse
;
154 default_calib(settings
*s
)
157 for (quantity
= 0; quantity
< Q_MAC
; quantity
++) {
158 s
->z
[quantity
] = (real
)0.0;
159 s
->sc
[quantity
] = (real
)1.0;
164 default_flags(settings
*s
)
170 default_all(settings
*s
)
177 default_translate(s
);
187 static char *ucbuffer
= NULL
;
194 get_token_no_blanks();
198 get_token_no_blanks(void)
204 while (isalpha(ch
)) {
205 s_catchar(&buffer
, &buf_len
, (char)ch
);
209 if (!buffer
) s_catchar(&buffer
, &buf_len
, '\0');
211 ucbuffer
= osmalloc(buf_len
);
214 ucbuffer
[i
] = toupper(buffer
[i
]);
217 printf("get_token_no_blanks() got “%s”\n", buffer
);
227 while (!isBlank(ch
) && !isEol(ch
)) {
228 s_catchar(&buffer
, &buf_len
, (char)ch
);
232 if (!buffer
) s_catchar(&buffer
, &buf_len
, '\0');
234 printf("get_word() got “%s”\n", buffer
);
238 /* match_tok() now uses binary chop
239 * tab argument should be alphabetically sorted (ascending)
242 match_tok(const sztok
*tab
, int tab_size
)
244 int a
= 0, b
= tab_size
- 1, c
;
246 assert(tab_size
> 0); /* catch empty table */
247 /* printf("[%d,%d]",a,b); */
249 c
= (unsigned)(a
+ b
) / 2;
250 /* printf(" %d",c); */
251 r
= strcmp(tab
[c
].sz
, ucbuffer
);
252 if (r
== 0) return tab
[c
].tok
; /* match */
258 return tab
[tab_size
].tok
; /* no match */
262 CMD_NULL
= -1, CMD_ALIAS
, CMD_BEGIN
, CMD_CALIBRATE
, CMD_CASE
, CMD_COPYRIGHT
,
263 CMD_CS
, CMD_DATA
, CMD_DATE
, CMD_DECLINATION
, CMD_DEFAULT
, CMD_END
,
264 CMD_ENTRANCE
, CMD_EQUATE
, CMD_EXPORT
, CMD_FIX
, CMD_FLAGS
, CMD_INCLUDE
,
265 CMD_INFER
, CMD_INSTRUMENT
, CMD_PREFIX
, CMD_REF
, CMD_REQUIRE
, CMD_SD
,
266 CMD_SET
, CMD_SOLVE
, CMD_TEAM
, CMD_TITLE
, CMD_TRUNCATE
, CMD_UNITS
269 static const sztok cmd_tab
[] = {
270 {"ALIAS", CMD_ALIAS
},
271 {"BEGIN", CMD_BEGIN
},
272 {"CALIBRATE", CMD_CALIBRATE
},
274 {"COPYRIGHT", CMD_COPYRIGHT
},
278 {"DECLINATION", CMD_DECLINATION
},
279 #ifndef NO_DEPRECATED
280 {"DEFAULT", CMD_DEFAULT
},
283 {"ENTRANCE", CMD_ENTRANCE
},
284 {"EQUATE", CMD_EQUATE
},
285 {"EXPORT", CMD_EXPORT
},
287 {"FLAGS", CMD_FLAGS
},
288 {"INCLUDE", CMD_INCLUDE
},
289 {"INFER", CMD_INFER
},
290 {"INSTRUMENT",CMD_INSTRUMENT
},
291 #ifndef NO_DEPRECATED
292 {"PREFIX", CMD_PREFIX
},
295 {"REQUIRE", CMD_REQUIRE
},
298 {"SOLVE", CMD_SOLVE
},
300 {"TITLE", CMD_TITLE
},
301 {"TRUNCATE", CMD_TRUNCATE
},
302 {"UNITS", CMD_UNITS
},
306 /* masks for units which are length and angles respectively */
307 #define LEN_UMASK (BIT(UNITS_METRES) | BIT(UNITS_FEET) | BIT(UNITS_YARDS))
308 #define ANG_UMASK (BIT(UNITS_DEGS) | BIT(UNITS_GRADS) | BIT(UNITS_MINUTES))
310 /* ordering must be the same as the units enum */
311 const real factor_tab
[] = {
312 1.0, METRES_PER_FOOT
, (METRES_PER_FOOT
*3.0),
313 (M_PI
/180.0), (M_PI
/180.0), (M_PI
/200.0), 0.01, (M_PI
/180.0/60.0)
316 const int units_to_msgno
[] = {
320 /*°*/344, /* quadrants */
327 int get_length_units(int quantity
) {
328 double factor
= pcs
->units
[quantity
];
329 if (fabs(factor
- METRES_PER_FOOT
) <= REAL_EPSILON
||
330 fabs(factor
- METRES_PER_FOOT
* 3.0) <= REAL_EPSILON
) {
336 int get_angle_units(int quantity
) {
337 double factor
= pcs
->units
[quantity
];
338 if (fabs(factor
- M_PI
/ 200.0) <= REAL_EPSILON
) {
345 get_units(unsigned long qmask
, bool percent_ok
)
347 static const sztok utab
[] = {
348 {"DEGREES", UNITS_DEGS
},
349 {"DEGS", UNITS_DEGS
},
350 {"FEET", UNITS_FEET
},
351 {"GRADS", UNITS_GRADS
},
352 {"METERS", UNITS_METRES
},
353 {"METRES", UNITS_METRES
},
354 {"METRIC", UNITS_METRES
},
355 {"MILS", UNITS_DEPRECATED_ALIAS_FOR_GRADS
},
356 {"MINUTES", UNITS_MINUTES
},
357 {"PERCENT", UNITS_PERCENT
},
358 {"PERCENTAGE", UNITS_PERCENT
},
359 {"QUADRANTS", UNITS_QUADRANTS
},
360 {"QUADS", UNITS_QUADRANTS
},
361 {"YARDS", UNITS_YARDS
},
366 units
= match_tok(utab
, TABSIZE(utab
));
367 if (units
== UNITS_NULL
) {
368 compile_diagnostic(DIAG_ERR
|DIAG_BUF
|DIAG_SKIP
, /*Unknown units “%s”*/35, buffer
);
371 /* Survex has long misdefined "mils" as an alias for "grads", of which
372 * there are 400 in a circle. There are several definitions of "mils"
373 * with a circle containing 2000π SI milliradians, 6400 NATO mils, 6000
374 * Warsaw Pact mils, and 6300 Swedish streck, and they aren't in common
375 * use by cave surveyors, so we now just warn if mils are used.
377 if (units
== UNITS_DEPRECATED_ALIAS_FOR_GRADS
) {
378 compile_diagnostic(DIAG_WARN
|DIAG_BUF
|DIAG_SKIP
,
379 /*Units “%s” are deprecated, assuming “grads” - see manual for details*/479,
383 if (units
== UNITS_PERCENT
&& percent_ok
&&
384 !(qmask
& ~(BIT(Q_GRADIENT
)|BIT(Q_BACKGRADIENT
)))) {
387 if (units
== UNITS_QUADRANTS
&&
388 !(qmask
& ~(BIT(Q_BEARING
)|BIT(Q_BACKBEARING
)))) {
391 if (((qmask
& LEN_QMASK
) && !TSTBIT(LEN_UMASK
, units
)) ||
392 ((qmask
& ANG_QMASK
) && !TSTBIT(ANG_UMASK
, units
))) {
393 /* TRANSLATORS: Note: In English you talk about the *units* of a single
394 * measurement, but the correct term in other languages may be singular.
396 compile_diagnostic(DIAG_ERR
|DIAG_BUF
|DIAG_SKIP
, /*Invalid units “%s” for quantity*/37, buffer
);
402 /* returns mask with bit x set to indicate quantity x specified */
404 get_qlist(unsigned long mask_bad
)
406 static const sztok qtab
[] = {
408 {"BACKBEARING", Q_BACKBEARING
},
409 {"BACKCLINO", Q_BACKGRADIENT
}, /* alternative name */
410 {"BACKCOMPASS", Q_BACKBEARING
}, /* alternative name */
411 {"BACKGRADIENT", Q_BACKGRADIENT
},
412 {"BACKLENGTH", Q_BACKLENGTH
},
413 {"BACKTAPE", Q_BACKLENGTH
}, /* alternative name */
414 {"BEARING", Q_BEARING
},
415 {"CEILING", Q_UP
}, /* alternative name */
416 {"CLINO", Q_GRADIENT
}, /* alternative name */
417 {"COMPASS", Q_BEARING
}, /* alternative name */
419 {"COUNTER", Q_COUNT
}, /* alternative name */
420 {"DECLINATION", Q_DECLINATION
},
421 {"DEFAULT", Q_DEFAULT
}, /* not a real quantity... */
424 {"DX", Q_DX
}, /* alternative name */
425 {"DY", Q_DY
}, /* alternative name */
426 {"DZ", Q_DZ
}, /* alternative name */
428 {"FLOOR", Q_DOWN
}, /* alternative name */
429 {"GRADIENT", Q_GRADIENT
},
431 {"LENGTH", Q_LENGTH
},
435 {"POSITION", Q_POS
},
437 {"TAPE", Q_LENGTH
}, /* alternative name */
441 unsigned long qmask
= 0;
448 tok
= match_tok(qtab
, TABSIZE(qtab
));
449 if (tok
== Q_DEFAULT
&& !(mask_bad
& BIT(Q_DEFAULT
))) {
450 /* Only recognise DEFAULT if it is the first quantity, and then don't
451 * look for any more. */
453 return BIT(Q_DEFAULT
);
456 /* bail out if we reach the table end with no match */
457 if (tok
== Q_NULL
) break;
459 if (qmask
& mask_bad
) {
460 compile_diagnostic(DIAG_ERR
|DIAG_BUF
|DIAG_SKIP
, /*Unknown instrument “%s”*/39, buffer
);
466 /* TRANSLATORS: A "quantity" is something measured like "LENGTH",
467 * "BEARING", "ALTITUDE", etc. */
468 compile_diagnostic(DIAG_ERR
|DIAG_BUF
|DIAG_SKIP
, /*Unknown quantity “%s”*/34, buffer
);
476 #define SPECIAL_UNKNOWN 0
480 static const sztok chartab
[] = {
481 {"BLANK", SPECIAL_BLANK
},
482 /*FIXME {"CLOSE", SPECIAL_CLOSE }, */
483 {"COMMENT", SPECIAL_COMMENT
},
484 {"DECIMAL", SPECIAL_DECIMAL
},
485 {"EOL", SPECIAL_EOL
}, /* EOL won't work well */
486 {"KEYWORD", SPECIAL_KEYWORD
},
487 {"MINUS", SPECIAL_MINUS
},
488 {"NAMES", SPECIAL_NAMES
},
489 {"OMIT", SPECIAL_OMIT
},
490 /*FIXME {"OPEN", SPECIAL_OPEN }, */
491 {"PLUS", SPECIAL_PLUS
},
492 #ifndef NO_DEPRECATED
493 {"ROOT", SPECIAL_ROOT
},
495 {"SEPARATOR", SPECIAL_SEPARATOR
},
496 {NULL
, SPECIAL_UNKNOWN
}
502 mask
= match_tok(chartab
, TABSIZE(chartab
));
504 if (mask
== SPECIAL_UNKNOWN
) {
505 compile_diagnostic(DIAG_ERR
|DIAG_BUF
|DIAG_SKIP
, /*Unknown character class “%s”*/42, buffer
);
509 #ifndef NO_DEPRECATED
510 if (mask
== SPECIAL_ROOT
) {
511 if (root_depr_count
< 5) {
512 /* TRANSLATORS: Use of the ROOT character (which is "\" by default) is
513 * deprecated, so this error would be generated by:
517 * If you're unsure what "deprecated" means, see:
518 * https://en.wikipedia.org/wiki/Deprecation */
519 compile_diagnostic(DIAG_WARN
|DIAG_BUF
, /*ROOT is deprecated*/25);
520 if (++root_depr_count
== 5)
521 /* TRANSLATORS: If you're unsure what "deprecated" means, see:
522 * https://en.wikipedia.org/wiki/Deprecation */
523 compile_diagnostic(DIAG_WARN
, /*Further uses of this deprecated feature will not be reported*/95);
528 /* if we're currently using an inherited translation table, allocate a new
529 * table, and copy old one into it */
530 if (pcs
->next
&& pcs
->next
->Translate
== pcs
->Translate
) {
532 p
= ((short*)osmalloc(ossizeof(short) * 257)) + 1;
533 memcpy(p
- 1, pcs
->Translate
- 1, sizeof(short) * 257);
539 /* clear this flag for all non-alphanums */
540 for (i
= 0; i
< 256; i
++)
541 if (!isalnum(i
)) pcs
->Translate
[i
] &= ~mask
;
543 /* now set this flag for all specified chars */
546 pcs
->Translate
[ch
] |= mask
;
547 } else if (tolower(ch
) == 'x') {
556 hex
= isdigit(ch
) ? ch
- '0' : tolower(ch
) - 'a';
562 hex
= hex
<< 4 | (isdigit(ch
) ? ch
- '0' : tolower(ch
) - 'a');
563 pcs
->Translate
[hex
] |= mask
;
572 check_reentry(prefix
*survey
, const filepos
* fpos_ptr
)
574 /* Don't try to check "*prefix \" or "*begin \" */
575 if (!survey
->up
) return;
576 if (TSTBIT(survey
->sflags
, SFLAGS_PREFIX_ENTERED
)) {
577 static int reenter_depr_count
= 0;
580 if (reenter_depr_count
>= 5)
585 /* TRANSLATORS: The first of two warnings given when a survey which has
586 * already been completed is reentered. This example file crawl.svx:
588 * *begin crawl ; <- second warning here
591 * *begin crawl ; <- first warning here
597 * crawl.svx:4: Reentering an existing survey is deprecated
598 * crawl.svx:1: Originally entered here
600 * If you're unsure what "deprecated" means, see:
601 * https://en.wikipedia.org/wiki/Deprecation */
602 compile_diagnostic(DIAG_WARN
|DIAG_TOKEN
, /*Reentering an existing survey is deprecated*/29);
604 /* TRANSLATORS: The second of two warnings given when a survey which has
605 * already been completed is reentered. This example file crawl.svx:
608 * 1 2 9.45 234 -01 # <- second warning here
610 * *begin crawl # <- first warning here
616 * crawl.svx:3: Reentering an existing survey is deprecated
617 * crawl.svx:1: Originally entered here
619 * If you're unsure what "deprecated" means, see:
620 * https://en.wikipedia.org/wiki/Deprecation */
621 compile_diagnostic_pfx(DIAG_WARN
, survey
, /*Originally entered here*/30);
622 if (++reenter_depr_count
== 5) {
623 /* After we've warned about 5 uses of the same deprecated feature, we
624 * give up for the rest of the current processing run.
626 * If you're unsure what "deprecated" means, see:
627 * https://en.wikipedia.org/wiki/Deprecation */
628 compile_diagnostic(DIAG_WARN
, /*Further uses of this deprecated feature will not be reported*/95);
631 survey
->sflags
|= BIT(SFLAGS_PREFIX_ENTERED
);
632 survey
->filename
= file
.filename
;
633 survey
->line
= file
.line
;
637 #ifndef NO_DEPRECATED
641 static int prefix_depr_count
= 0;
644 /* Issue warning first, so "*prefix \" warns first that *prefix is
645 * deprecated and then that ROOT is...
647 if (prefix_depr_count
< 5) {
648 /* TRANSLATORS: If you're unsure what "deprecated" means, see:
649 * https://en.wikipedia.org/wiki/Deprecation */
650 compile_diagnostic(DIAG_WARN
|DIAG_BUF
, /**prefix is deprecated - use *begin and *end instead*/6);
651 if (++prefix_depr_count
== 5)
652 compile_diagnostic(DIAG_WARN
, /*Further uses of this deprecated feature will not be reported*/95);
655 survey
= read_prefix(PFX_SURVEY
|PFX_ALLOW_ROOT
);
656 pcs
->Prefix
= survey
;
657 check_reentry(survey
, &fp
);
664 /* Currently only two forms are supported:
665 * *alias station - ..
669 if (strcmp(ucbuffer
, "STATION") != 0)
672 if (strcmp(buffer
, "-") != 0)
675 if (*buffer
&& strcmp(buffer
, "..") != 0)
677 pcs
->dash_for_anon_wall_station
= (*buffer
!= '\0');
680 compile_diagnostic(DIAG_ERR
|DIAG_SKIP
, /*Bad *alias command*/397);
688 pcsNew
= osnew(settings
);
689 *pcsNew
= *pcs
; /* copy contents */
690 pcsNew
->begin_lineno
= file
.line
;
695 pcs
->begin_survey
= NULL
;
696 if (!isEol(ch
) && !isComm(ch
)) {
700 survey
= read_prefix(PFX_SURVEY
|PFX_ALLOW_ROOT
|PFX_WARN_SEPARATOR
);
701 pcs
->begin_survey
= survey
;
702 pcs
->Prefix
= survey
;
703 check_reentry(survey
, &fp
);
709 invalidate_pj_cached(void)
711 /* Invalidate the cached PJ. */
713 proj_destroy(pj_cached
);
719 report_declination(settings
*p
)
721 if (p
->min_declination
<= p
->max_declination
) {
724 const char* deg_sign
= msg(/*°*/344);
725 ymd_from_days_since_1900(p
->min_declination_days
, &y
, &m
, &d
);
726 snprintf(range
, sizeof(range
),
727 "%.1f%s @ %04d-%02d-%02d",
728 deg(p
->min_declination
), deg_sign
, y
, m
, d
);
729 if (p
->min_declination_days
!= p
->max_declination_days
) {
730 size_t len
= strlen(range
);
731 ymd_from_days_since_1900(p
->max_declination_days
, &y
, &m
, &d
);
732 snprintf(range
+ len
, sizeof(range
) - len
,
733 " / %.1f%s @ %04d-%02d-%02d",
734 deg(p
->max_declination
), deg_sign
, y
, m
, d
);
736 /* TRANSLATORS: This message gives information about the range of
737 * declination values and the grid convergence value calculated for
738 * each "*declination auto ..." command.
740 * The first %s will be replaced by the declination range (or single
741 * value), and %.1f%s by the grid convergence angle.
743 compile_diagnostic_at(DIAG_INFO
|DIAG_COL
, p
->dec_filename
, p
->dec_line
,
744 /*Declination: %s, grid convergence: %.1f%s*/484,
746 deg(p
->convergence
), deg_sign
);
748 fputs(p
->dec_context
, STDERR
);
750 free(p
->dec_context
);
751 p
->dec_context
= NULL
;
763 if (pcs
->dec_lat
!= p
->dec_lat
||
764 pcs
->dec_lon
!= p
->dec_lon
||
765 pcs
->dec_alt
!= p
->dec_alt
) {
766 report_declination(p
);
768 pcs
->min_declination_days
= p
->min_declination_days
;
769 pcs
->max_declination_days
= p
->max_declination_days
;
770 pcs
->min_declination
= p
->min_declination
;
771 pcs
->max_declination
= p
->max_declination
;
774 if (p
->proj_str
!= pcs
->proj_str
) {
775 if (!p
->proj_str
|| !pcs
->proj_str
||
776 strcmp(p
->proj_str
, pcs
->proj_str
) != 0) {
777 invalidate_pj_cached();
779 /* free proj_str if not used by parent */
783 /* don't free default ordering or ordering used by parent */
784 if (p
->ordering
!= default_order
&& p
->ordering
!= pcs
->ordering
)
785 osfree((reading
*)p
->ordering
);
787 /* free Translate if not used by parent */
788 if (p
->Translate
!= pcs
->Translate
)
789 osfree(p
->Translate
- 1);
791 /* free meta if not used by parent, or in this block */
792 if (p
->meta
&& p
->meta
!= pcs
->meta
&& p
->meta
->ref_count
== 0)
801 prefix
*survey
, *begin_survey
;
804 if (pcs
->begin_lineno
== 0) {
805 if (pcs
->next
== NULL
) {
806 /* more ENDs than BEGINs */
807 compile_diagnostic(DIAG_ERR
|DIAG_SKIP
, /*No matching BEGIN*/192);
809 compile_diagnostic(DIAG_ERR
|DIAG_SKIP
, /*END with no matching BEGIN in this file*/22);
814 begin_survey
= pcs
->begin_survey
;
818 /* note need to read using root *before* BEGIN */
820 if (isEol(ch
) || isComm(ch
)) {
824 survey
= read_prefix(PFX_SURVEY
|PFX_ALLOW_ROOT
);
827 if (survey
!= begin_survey
) {
831 /* TRANSLATORS: Used when a BEGIN command has no survey, but the
832 * END command does, e.g.:
836 * *end entrance <--[Message given here] */
837 compile_diagnostic(DIAG_ERR
|DIAG_TOKEN
, /*Matching BEGIN command has no survey name*/36);
839 /* TRANSLATORS: *BEGIN <survey> and *END <survey> should have the
840 * same <survey> if it’s given at all */
841 compile_diagnostic(DIAG_ERR
|DIAG_TOKEN
, /*Survey name doesn’t match BEGIN*/193);
845 /* TRANSLATORS: Used when a BEGIN command has a survey name, but the
846 * END command omits it, e.g.:
850 * *end <--[Message given here] */
851 compile_diagnostic(DIAG_WARN
|DIAG_COL
, /*Survey name omitted from END*/194);
859 prefix
*pfx
= read_prefix(PFX_STATION
);
860 pfx
->sflags
|= BIT(SFLAGS_ENTRANCE
);
863 static const prefix
* first_fix_name
= NULL
;
864 static const char * first_fix_filename
;
865 static unsigned first_fix_line
;
872 static prefix
*name_omit_already
= NULL
;
873 static const char * name_omit_already_filename
= NULL
;
874 static unsigned int name_omit_already_line
;
878 fix_name
= read_prefix(PFX_STATION
|PFX_ALLOW_ROOT
);
879 fix_name
->sflags
|= BIT(SFLAGS_FIXED
);
883 if (strcmp(ucbuffer
, "REFERENCE") == 0) {
884 /* suppress "unused fixed point" warnings for this station */
885 fix_name
->sflags
|= BIT(SFLAGS_USED
);
887 if (*ucbuffer
) set_pos(&fp
);
890 x
= read_numeric(fTrue
);
891 if (x
== HUGE_REAL
) {
892 /* If the end of the line isn't blank, read a number after all to
893 * get a more helpful error message */
894 if (!isEol(ch
) && !isComm(ch
)) x
= read_numeric(fFalse
);
896 if (x
== HUGE_REAL
) {
897 if (pcs
->proj_str
|| proj_str_out
) {
898 compile_diagnostic(DIAG_ERR
|DIAG_COL
|DIAG_SKIP
, /*Coordinates can't be omitted when coordinate system has been specified*/439);
902 if (fix_name
== name_omit_already
) {
903 compile_diagnostic(DIAG_WARN
|DIAG_COL
, /*Same station fixed twice with no coordinates*/61);
907 if (name_omit_already
) {
908 /* TRANSLATORS: Emitted after second and subsequent "FIX" command
909 * with no coordinates.
911 compile_diagnostic_at(DIAG_ERR
|DIAG_COL
,
912 name_omit_already_filename
,
913 name_omit_already_line
,
914 /*Already had FIX command with no coordinates for station “%s”*/441,
915 sprint_prefix(name_omit_already
));
917 /* TRANSLATORS: " *fix a " gives this message: */
918 compile_diagnostic(DIAG_INFO
|DIAG_COL
, /*FIX command with no coordinates - fixing at (0,0,0)*/54);
920 name_omit_already
= fix_name
;
921 name_omit_already_filename
= file
.filename
;
922 name_omit_already_line
= file
.line
;
925 x
= y
= z
= (real
)0.0;
928 y
= read_numeric(fFalse
);
929 z
= read_numeric(fFalse
);
931 if (pcs
->proj_str
&& proj_str_out
) {
932 PJ
*transform
= pj_cached
;
934 transform
= proj_create_crs_to_crs(PJ_DEFAULT_CTX
,
939 /* Normalise the output order so x is longitude and y latitude - by
940 * default new PROJ has them switched for EPSG:4326 which just seems
943 PJ
* pj_norm
= proj_normalize_for_visualization(PJ_DEFAULT_CTX
,
945 proj_destroy(transform
);
949 pj_cached
= transform
;
952 if (proj_angular_input(transform
, PJ_FWD
)) {
953 /* Input coordinate system expects radians. */
958 PJ_COORD coord
= {{x
, y
, z
, HUGE_VAL
}};
959 coord
= proj_trans(transform
, PJ_FWD
, coord
);
964 if (x
== HUGE_VAL
|| y
== HUGE_VAL
|| z
== HUGE_VAL
) {
965 compile_diagnostic(DIAG_ERR
, /*Failed to convert coordinates: %s*/436,
966 proj_errno_string(proj_errno(transform
)));
967 /* Set dummy values which are finite. */
970 } else if (pcs
->proj_str
) {
971 compile_diagnostic(DIAG_ERR
, /*The input projection is set but the output projection isn't*/437);
972 } else if (proj_str_out
) {
973 compile_diagnostic(DIAG_ERR
, /*The output projection is set but the input projection isn't*/438);
977 sdx
= read_numeric(fTrue
);
980 compile_diagnostic(DIAG_ERR
|DIAG_SKIP
|DIAG_NUM
, /*Standard deviation must be positive*/48);
983 if (sdx
!= HUGE_REAL
) {
985 real cxy
= 0, cyz
= 0, czx
= 0;
987 sdy
= read_numeric(fTrue
);
988 if (sdy
== HUGE_REAL
) {
989 /* only one variance given */
994 compile_diagnostic(DIAG_ERR
|DIAG_SKIP
|DIAG_NUM
, /*Standard deviation must be positive*/48);
998 sdz
= read_numeric(fTrue
);
999 if (sdz
== HUGE_REAL
) {
1000 /* two variances given - horizontal & vertical */
1006 compile_diagnostic(DIAG_ERR
|DIAG_SKIP
|DIAG_NUM
, /*Standard deviation must be positive*/48);
1009 cxy
= read_numeric(fTrue
);
1010 if (cxy
!= HUGE_REAL
) {
1011 /* covariances given */
1012 cyz
= read_numeric(fFalse
);
1013 czx
= read_numeric(fFalse
);
1019 stn
= StnFromPfx(fix_name
);
1021 node
*fixpt
= osnew(node
);
1023 name
= osnew(prefix
);
1024 name
->pos
= osnew(pos
);
1030 if (TSTBIT(pcs
->infer
, INFER_EXPORTS
)) {
1031 name
->min_export
= USHRT_MAX
;
1033 name
->min_export
= 0;
1035 name
->max_export
= 0;
1037 add_stn_to_list(&stnlist
, fixpt
);
1042 fixpt
->leg
[0] = fixpt
->leg
[1] = fixpt
->leg
[2] = NULL
;
1043 addfakeleg(fixpt
, stn
, 0, 0, 0,
1044 sdx
* sdx
, sdy
* sdy
, sdz
* sdz
1045 #ifndef NO_COVARIANCES
1051 if (!first_fix_name
) {
1052 /* We track if we've fixed a station yet, and if so what the name
1053 * of the first fix was, so that we can issue an error if the
1054 * output coordinate system is set after fixing a station. */
1055 first_fix_name
= fix_name
;
1056 first_fix_filename
= file
.filename
;
1057 first_fix_line
= file
.line
;
1064 if (!first_fix_name
) {
1065 /* We track if we've fixed a station yet, and if so what the name of the
1066 * first fix was, so that we can issue an error if the output coordinate
1067 * system is set after fixing a station. */
1068 first_fix_name
= fix_name
;
1069 first_fix_filename
= file
.filename
;
1070 first_fix_line
= file
.line
;
1073 stn
= StnFromPfx(fix_name
);
1082 if (x
!= POS(stn
, 0) || y
!= POS(stn
, 1) || z
!= POS(stn
, 2)) {
1083 compile_diagnostic(DIAG_ERR
, /*Station already fixed or equated to a fixed point*/46);
1086 /* TRANSLATORS: *fix a 1 2 3 / *fix a 1 2 3 */
1087 compile_diagnostic(DIAG_WARN
, /*Station already fixed at the same coordinates*/55);
1093 static const sztok flagtab
[] = {
1094 {"DUPLICATE", FLAGS_DUPLICATE
},
1095 {"NOT", FLAGS_NOT
},
1096 {"SPLAY", FLAGS_SPLAY
},
1097 {"SURFACE", FLAGS_SURFACE
},
1098 {NULL
, FLAGS_UNKNOWN
}
1101 bool fEmpty
= fTrue
;
1105 /* If buffer is empty, it could mean end of line, or maybe
1106 * some non-letter junk which is better reported later */
1107 if (!buffer
[0]) break;
1110 flag
= match_tok(flagtab
, TABSIZE(flagtab
));
1111 /* treat the second NOT in "NOT NOT" as an unknown flag */
1112 if (flag
== FLAGS_UNKNOWN
|| (fNot
&& flag
== FLAGS_NOT
)) {
1113 compile_diagnostic(DIAG_ERR
|DIAG_BUF
, /*FLAG “%s” unknown*/68, buffer
);
1114 /* Recover from “*FLAGS NOT BOGUS SURFACE” by ignoring "NOT BOGUS" */
1116 } else if (flag
== FLAGS_NOT
) {
1119 pcs
->flags
&= ~BIT(flag
);
1122 pcs
->flags
|= BIT(flag
);
1127 compile_diagnostic(DIAG_ERR
|DIAG_BUF
, /*Expecting “DUPLICATE”, “SPLAY”, or “SURFACE”*/188);
1128 } else if (fEmpty
) {
1129 compile_diagnostic(DIAG_ERR
|DIAG_BUF
, /*Expecting “NOT”, “DUPLICATE”, “SPLAY”, or “SURFACE”*/189);
1136 prefix
*name1
, *name2
;
1137 bool fOnlyOneStn
= fTrue
; /* to trap eg *equate entrance.6 */
1141 name1
= read_prefix(PFX_STATION
|PFX_ALLOW_ROOT
|PFX_SUSPECT_TYPO
);
1145 if (isEol(ch
) || isComm(ch
)) {
1148 /* TRANSLATORS: EQUATE is a command name, so shouldn’t be
1151 * Here "station" is a survey station, not a train station.
1153 compile_diagnostic(DIAG_ERR
|DIAG_SKIP
|DIAG_TOKEN
, /*Only one station in EQUATE command*/33);
1158 name1
= read_prefix(PFX_STATION
|PFX_ALLOW_ROOT
|PFX_SUSPECT_TYPO
);
1159 process_equate(name1
, name2
);
1160 fOnlyOneStn
= fFalse
;
1165 report_missing_export(prefix
*pfx
, int depth
)
1169 prefix
*survey
= pfx
;
1171 for (i
= depth
+ 1; i
; i
--) {
1172 survey
= survey
->up
;
1175 s
= osstrdup(sprint_prefix(survey
));
1176 p
= sprint_prefix(pfx
);
1177 if (survey
->filename
) {
1178 /* TRANSLATORS: A station must be exported out of each level it is in, so
1179 * this would give "Station “\outer.inner.1” not exported from survey
1182 * *equate entrance outer.inner.1
1190 * Here "survey" is a "cave map" rather than list of questions - it should be
1191 * translated to the terminology that cavers using the language would use.
1193 compile_diagnostic_pfx(DIAG_ERR
, survey
,
1194 /*Station “%s” not exported from survey “%s”*/26, p
, s
);
1196 compile_diagnostic(DIAG_ERR
, /*Station “%s” not exported from survey “%s”*/26, p
, s
);
1206 fExportUsed
= fTrue
;
1209 pfx
= read_prefix(PFX_STATION
|PFX_NEW
);
1211 /* The argument was an existing station. */
1215 while (p
!= NULL
&& p
!= pcs
->Prefix
) {
1219 /* Something like: *export \foo, but we've excluded use of root */
1222 /* *export \ or similar bogus stuff */
1225 printf("C min %d max %d depth %d pfx %s\n",
1226 pfx
->min_export
, pfx
->max_export
, depth
, sprint_prefix(pfx
));
1228 if (pfx
->min_export
== 0) {
1229 /* not encountered *export for this name before */
1230 if (pfx
->max_export
> depth
) report_missing_export(pfx
, depth
);
1231 pfx
->min_export
= pfx
->max_export
= depth
;
1232 } else if (pfx
->min_export
!= USHRT_MAX
) {
1233 /* FIXME: what to do if a station is marked for inferred exports
1234 * but is then explicitly exported? Currently we just ignore the
1235 * explicit export... */
1236 if (pfx
->min_export
- 1 > depth
) {
1237 report_missing_export(pfx
, depth
);
1238 } else if (pfx
->min_export
- 1 < depth
) {
1239 /* TRANSLATORS: Here "station" is a survey station, not a train station.
1241 * Exporting a station twice gives this error:
1248 compile_diagnostic(DIAG_ERR
, /*Station “%s” already exported*/66,
1249 sprint_prefix(pfx
));
1251 pfx
->min_export
= depth
;
1254 } while (!isEol(ch
) && !isComm(ch
));
1260 static const sztok dtab
[] = {
1262 {"BACKBEARING", BackComp
},
1263 {"BACKCLINO", BackClino
}, /* alternative name */
1264 {"BACKCOMPASS", BackComp
}, /* alternative name */
1265 {"BACKGRADIENT", BackClino
},
1266 {"BACKLENGTH", BackTape
},
1267 {"BACKTAPE", BackTape
}, /* alternative name */
1269 {"CEILING", Up
}, /* alternative name */
1270 {"CLINO", Clino
}, /* alternative name */
1271 {"COMPASS", Comp
}, /* alternative name */
1272 {"COUNT", Count
}, /* FrCount&ToCount in multiline */
1273 {"DEPTH", Depth
}, /* FrDepth&ToDepth in multiline */
1274 {"DEPTHCHANGE", DepthChange
},
1275 {"DIRECTION", Dir
},
1281 {"FLOOR", Down
}, /* alternative name */
1283 {"FROMCOUNT", FrCount
},
1284 {"FROMDEPTH", FrDepth
},
1285 {"GRADIENT", Clino
},
1286 {"IGNORE", Ignore
},
1287 {"IGNOREALL", IgnoreAll
},
1290 {"NEWLINE", Newline
},
1293 {"STATION", Station
}, /* Fr&To in multiline */
1294 {"TAPE", Tape
}, /* alternative name */
1296 {"TOCOUNT", ToCount
},
1297 {"TODEPTH", ToDepth
},
1302 #define MASK_stns BIT(Fr) | BIT(To) | BIT(Station)
1303 #define MASK_tape BIT(Tape) | BIT(BackTape) | BIT(FrCount) | BIT(ToCount) | BIT(Count)
1304 #define MASK_dpth BIT(FrDepth) | BIT(ToDepth) | BIT(Depth) | BIT(DepthChange)
1305 #define MASK_comp BIT(Comp) | BIT(BackComp)
1306 #define MASK_clin BIT(Clino) | BIT(BackClino)
1308 #define MASK_NORMAL MASK_stns | BIT(Dir) | MASK_tape | MASK_comp | MASK_clin
1309 #define MASK_DIVING MASK_NORMAL | MASK_dpth
1310 #define MASK_CARTESIAN MASK_stns | BIT(Dx) | BIT(Dy) | BIT(Dz)
1311 #define MASK_CYLPOLAR MASK_stns | BIT(Dir) | MASK_tape | MASK_comp | MASK_dpth
1312 #define MASK_NOSURVEY MASK_stns
1313 #define MASK_PASSAGE BIT(Station) | BIT(Left) | BIT(Right) | BIT(Up) | BIT(Down)
1315 /* readings which may be given for each style */
1316 static const unsigned long mask
[] = {
1317 MASK_NORMAL
, MASK_DIVING
, MASK_CARTESIAN
, MASK_CYLPOLAR
, MASK_NOSURVEY
,
1321 /* readings which may be omitted for each style */
1322 static const unsigned long mask_optional
[] = {
1323 BIT(Dir
) | BIT(Clino
) | BIT(BackClino
),
1324 BIT(Dir
) | BIT(Clino
) | BIT(BackClino
),
1328 0 /* BIT(Left) | BIT(Right) | BIT(Up) | BIT(Down), */
1331 /* all valid readings */
1332 static const unsigned long mask_all
[] = {
1333 MASK_NORMAL
| BIT(Newline
) | BIT(Ignore
) | BIT(IgnoreAll
) | BIT(End
),
1334 MASK_DIVING
| BIT(Newline
) | BIT(Ignore
) | BIT(IgnoreAll
) | BIT(End
),
1335 MASK_CARTESIAN
| BIT(Newline
) | BIT(Ignore
) | BIT(IgnoreAll
) | BIT(End
),
1336 MASK_CYLPOLAR
| BIT(Newline
) | BIT(Ignore
) | BIT(IgnoreAll
) | BIT(End
),
1337 MASK_NOSURVEY
| BIT(Ignore
) | BIT(IgnoreAll
) | BIT(End
),
1338 MASK_PASSAGE
| BIT(Ignore
) | BIT(IgnoreAll
) | BIT(End
)
1340 #define STYLE_DEFAULT -2
1341 #define STYLE_UNKNOWN -1
1343 static const sztok styletab
[] = {
1344 {"CARTESIAN", STYLE_CARTESIAN
},
1345 {"CYLPOLAR", STYLE_CYLPOLAR
},
1346 {"DEFAULT", STYLE_DEFAULT
},
1347 {"DIVING", STYLE_DIVING
},
1348 {"NORMAL", STYLE_NORMAL
},
1349 {"NOSURVEY", STYLE_NOSURVEY
},
1350 {"PASSAGE", STYLE_PASSAGE
},
1351 {"TOPOFIL", STYLE_NORMAL
},
1352 {NULL
, STYLE_UNKNOWN
}
1355 #define m_multi (BIT(Station) | BIT(Count) | BIT(Depth))
1359 unsigned long mUsed
= 0;
1360 int old_style
= pcs
->style
;
1362 /* after a bad *data command ignore survey data until the next
1363 * *data command to avoid an avalanche of errors */
1364 pcs
->style
= STYLE_IGNORE
;
1367 style
= match_tok(styletab
, TABSIZE(styletab
));
1369 if (style
== STYLE_DEFAULT
) {
1374 if (style
== STYLE_UNKNOWN
) {
1376 /* "*data" reinitialises the current style - for *data passage that
1377 * breaks the passage.
1379 pcs
->style
= style
= old_style
;
1382 /* TRANSLATORS: e.g. trying to refer to an invalid FNORD data style */
1383 compile_diagnostic(DIAG_ERR
|DIAG_BUF
|DIAG_SKIP
, /*Data style “%s” unknown*/65, buffer
);
1388 #ifndef NO_DEPRECATED
1389 /* Olde syntax had optional field for survey grade, so allow an omit
1390 * but issue a warning about it */
1392 static int data_depr_count
= 0;
1393 if (data_depr_count
< 5) {
1394 compile_diagnostic(DIAG_WARN
|DIAG_BUF
, /*“*data %s %c …” is deprecated - use “*data %s …” instead*/104,
1395 buffer
, ch
, buffer
);
1396 if (++data_depr_count
== 5)
1397 compile_diagnostic(DIAG_WARN
, /*Further uses of this deprecated feature will not be reported*/95);
1403 int kMac
= 6; /* minimum for NORMAL style */
1404 reading
*new_order
= osmalloc(kMac
* sizeof(reading
));
1405 char *style_name
= osstrdup(buffer
);
1410 d
= match_tok(dtab
, TABSIZE(dtab
));
1411 /* only token allowed after IGNOREALL is NEWLINE */
1412 if (k
&& new_order
[k
- 1] == IgnoreAll
&& d
!= Newline
) {
1416 /* Note: an unknown token is reported as trailing garbage */
1417 if (!TSTBIT(mask_all
[style
], d
)) {
1418 /* TRANSLATORS: a data "style" is something like NORMAL, DIVING, etc.
1419 * a "reading" is one of FROM, TO, TAPE, COMPASS, CLINO for NORMAL
1420 * style. Neither "style" nor "reading" is a keyword in the program.
1422 * This error complains about a "DEPTH" gauge reading in "NORMAL"
1423 * style, for example.
1425 compile_diagnostic(DIAG_ERR
|DIAG_BUF
|DIAG_SKIP
,
1426 /*Reading “%s” not allowed in data style “%s”*/63,
1427 buffer
, style_name
);
1432 if (TSTBIT(mUsed
, Newline
) && TSTBIT(m_multi
, d
)) {
1433 /* TRANSLATORS: caused by e.g.
1435 * *data diving station newline depth tape compass
1437 * ("depth" needs to occur before "newline"). */
1438 compile_diagnostic(DIAG_ERR
|DIAG_BUF
|DIAG_SKIP
,
1439 /*Reading “%s” must occur before NEWLINE*/225, buffer
);
1444 /* Check for duplicates unless it's a special reading:
1445 * IGNOREALL,IGNORE (duplicates allowed) ; END (not possible)
1447 if (!((BIT(Ignore
) | BIT(End
) | BIT(IgnoreAll
)) & BIT(d
))) {
1448 if (TSTBIT(mUsed
, d
)) {
1449 /* TRANSLATORS: complains about a situation like trying to define
1450 * two from stations per leg */
1451 compile_diagnostic(DIAG_ERR
|DIAG_BUF
|DIAG_SKIP
, /*Duplicate reading “%s”*/67, buffer
);
1456 /* Check for previously listed readings which are incompatible
1457 * with this one - e.g. Count vs FrCount */
1461 if (mUsed
& (BIT(Fr
) | BIT(To
))) fBad
= fTrue
;
1464 if (TSTBIT(mUsed
, Station
)) fBad
= fTrue
;
1467 if (mUsed
& (BIT(FrCount
) | BIT(ToCount
) | BIT(Tape
)))
1470 case FrCount
: case ToCount
:
1471 if (mUsed
& (BIT(Count
) | BIT(Tape
)))
1475 if (mUsed
& (BIT(FrDepth
) | BIT(ToDepth
) | BIT(DepthChange
)))
1478 case FrDepth
: case ToDepth
:
1479 if (mUsed
& (BIT(Depth
) | BIT(DepthChange
))) fBad
= fTrue
;
1482 if (mUsed
& (BIT(FrDepth
) | BIT(ToDepth
) | BIT(Depth
)))
1486 if (mUsed
& ~m_multi
) {
1487 /* TRANSLATORS: e.g.
1489 * *data normal from to tape newline compass clino */
1490 compile_diagnostic(DIAG_ERR
|DIAG_BUF
|DIAG_SKIP
, /*NEWLINE can only be preceded by STATION, DEPTH, and COUNT*/226);
1496 /* TRANSLATORS: error from:
1498 * *data normal newline from to tape compass clino */
1499 compile_diagnostic(DIAG_ERR
|DIAG_BUF
|DIAG_SKIP
, /*NEWLINE can’t be the first reading*/222);
1505 default: /* avoid compiler warnings about unhandled enums */
1509 /* Not entirely happy with phrasing this... */
1510 /* TRANSLATORS: This is an error from the *DATA command. It
1511 * means that a reading (which will appear where %s is isn't
1512 * valid as the list of readings has already included the same
1513 * reading, or an equivalent one (e.g. you can't have both
1514 * DEPTH and DEPTHCHANGE together). */
1515 compile_diagnostic(DIAG_ERR
|DIAG_BUF
|DIAG_SKIP
, /*Reading “%s” duplicates previous reading(s)*/77,
1521 mUsed
|= BIT(d
); /* used to catch duplicates */
1524 if (k
&& new_order
[k
- 1] == IgnoreAll
) {
1525 SVX_ASSERT(d
== Newline
);
1527 d
= IgnoreAllAndNewLine
;
1531 new_order
= osrealloc(new_order
, kMac
* sizeof(reading
));
1536 if (k
>= 2 && new_order
[k
- 2] == Newline
) {
1537 /* TRANSLATORS: error from:
1539 * *data normal from to tape compass clino newline */
1540 compile_diagnostic(DIAG_ERR
|DIAG_BUF
|DIAG_SKIP
, /*NEWLINE can’t be the last reading*/223);
1546 if (style
== STYLE_NOSURVEY
) {
1547 if (TSTBIT(mUsed
, Station
)) {
1550 new_order
= osrealloc(new_order
, kMac
* sizeof(reading
));
1552 new_order
[k
- 1] = Newline
;
1553 new_order
[k
++] = End
;
1555 } else if (style
== STYLE_PASSAGE
) {
1556 /* Station doesn't mean "multiline" for STYLE_PASSAGE. */
1557 } else if (!TSTBIT(mUsed
, Newline
) && (m_multi
& mUsed
)) {
1558 /* TRANSLATORS: Error given by something like:
1560 * *data normal station tape compass clino
1562 * ("station" signifies interleaved data). */
1563 compile_diagnostic(DIAG_ERR
|DIAG_SKIP
, /*Interleaved readings, but no NEWLINE*/224);
1570 printf("mUsed = 0x%x\n", mUsed
);
1573 /* Check the supplied readings form a sufficient set. */
1574 if (style
!= STYLE_PASSAGE
) {
1575 if ((mUsed
& (BIT(Fr
) | BIT(To
))) == (BIT(Fr
) | BIT(To
)))
1576 mUsed
|= BIT(Station
);
1577 else if (TSTBIT(mUsed
, Station
))
1578 mUsed
|= BIT(Fr
) | BIT(To
);
1581 if (mUsed
& (BIT(Comp
) | BIT(BackComp
)))
1582 mUsed
|= BIT(Comp
) | BIT(BackComp
);
1584 if (mUsed
& (BIT(Clino
) | BIT(BackClino
)))
1585 mUsed
|= BIT(Clino
) | BIT(BackClino
);
1587 if ((mUsed
& (BIT(FrDepth
) | BIT(ToDepth
))) == (BIT(FrDepth
) | BIT(ToDepth
)))
1588 mUsed
|= BIT(Depth
) | BIT(DepthChange
);
1589 else if (mUsed
& (BIT(Depth
) | BIT(DepthChange
)))
1590 mUsed
|= BIT(FrDepth
) | BIT(ToDepth
) | BIT(Depth
) | BIT(DepthChange
);
1592 if ((mUsed
& (BIT(FrCount
) | BIT(ToCount
))) == (BIT(FrCount
) | BIT(ToCount
)))
1593 mUsed
|= BIT(Count
) | BIT(Tape
) | BIT(BackTape
);
1594 else if (mUsed
& (BIT(Count
) | BIT(Tape
) | BIT(BackTape
)))
1595 mUsed
|= BIT(FrCount
) | BIT(ToCount
) | BIT(Count
) | BIT(Tape
) | BIT(BackTape
);
1598 printf("mUsed = 0x%x, opt = 0x%x, mask = 0x%x\n", mUsed
,
1599 mask_optional
[style
], mask
[style
]);
1602 if (((mUsed
&~ BIT(Newline
)) | mask_optional
[style
]) != mask
[style
]) {
1603 /* Test should only fail with too few bits set, not too many */
1604 SVX_ASSERT((((mUsed
&~ BIT(Newline
)) | mask_optional
[style
])
1605 &~ mask
[style
]) == 0);
1606 /* TRANSLATORS: i.e. not enough readings for the style. */
1607 compile_diagnostic(DIAG_ERR
|DIAG_SKIP
, /*Too few readings for data style “%s”*/64, style_name
);
1613 /* don't free default ordering or ordering used by parent */
1614 if (pcs
->ordering
!= default_order
&&
1615 !(pcs
->next
&& pcs
->next
->ordering
== pcs
->ordering
))
1616 osfree((reading
*)pcs
->ordering
);
1619 pcs
->ordering
= new_order
;
1624 if (style
== STYLE_PASSAGE
) {
1625 lrudlist
* new_psg
= osnew(lrudlist
);
1626 new_psg
->tube
= NULL
;
1627 new_psg
->next
= model
;
1629 next_lrud
= &(new_psg
->tube
);
1636 int units
, quantity
;
1637 unsigned long qmask
;
1638 unsigned long m
; /* mask with bit x set to indicate quantity x specified */
1642 qmask
= get_qlist(BIT(Q_POS
)|BIT(Q_PLUMB
)|BIT(Q_LEVEL
));
1645 if (qmask
== BIT(Q_DEFAULT
)) {
1651 factor
= read_numeric(fTrue
);
1652 if (factor
== 0.0) {
1654 /* TRANSLATORS: error message given by "*units tape 0 feet" - it’s
1655 * meaningless to say your tape is marked in "0 feet" (but you might
1656 * measure distance by counting knots on a diving line, and tie them
1657 * every "2 feet"). */
1658 compile_diagnostic(DIAG_ERR
|DIAG_TOKEN
, /**UNITS factor must be non-zero*/200);
1663 units
= get_units(qmask
, fTrue
);
1664 if (units
== UNITS_NULL
) return;
1665 if (TSTBIT(qmask
, Q_GRADIENT
))
1666 pcs
->f_clino_percent
= (units
== UNITS_PERCENT
);
1667 if (TSTBIT(qmask
, Q_BACKGRADIENT
))
1668 pcs
->f_backclino_percent
= (units
== UNITS_PERCENT
);
1670 if (TSTBIT(qmask
, Q_BEARING
)) {
1671 pcs
->f_bearing_quadrants
= (units
== UNITS_QUADRANTS
);
1673 if (TSTBIT(qmask
, Q_BACKBEARING
)) {
1674 pcs
->f_backbearing_quadrants
= (units
== UNITS_QUADRANTS
);
1677 if (factor
== HUGE_REAL
) {
1678 factor
= factor_tab
[units
];
1680 factor
*= factor_tab
[units
];
1683 for (quantity
= 0, m
= BIT(quantity
); m
<= qmask
; quantity
++, m
<<= 1)
1684 if (qmask
& m
) pcs
->units
[quantity
] = factor
;
1691 unsigned long qmask
, m
;
1695 qmask
= get_qlist(BIT(Q_POS
)|BIT(Q_PLUMB
)|BIT(Q_LEVEL
));
1696 if (!qmask
) return; /* error already reported */
1698 if (qmask
== BIT(Q_DEFAULT
)) {
1703 if (((qmask
& LEN_QMASK
)) && ((qmask
& ANG_QMASK
))) {
1704 /* TRANSLATORS: e.g.
1706 * *calibrate tape compass 1 1
1708 compile_diagnostic(DIAG_ERR
|DIAG_SKIP
, /*Can’t calibrate angular and length quantities together*/227);
1712 z
= read_numeric(fFalse
);
1714 sc
= read_numeric(fTrue
);
1715 if (sc
== HUGE_REAL
) {
1717 int units
= get_units(qmask
, fFalse
);
1718 if (units
== UNITS_NULL
) {
1721 z
*= factor_tab
[units
];
1722 sc
= read_numeric(fTrue
);
1723 if (sc
== HUGE_REAL
) {
1726 /* Adjustment applied is: (reading - z) * sc
1727 * We want: reading * sc - z
1728 * So divide z by sc so the applied adjustment does what we want.
1737 if (sc
== HUGE_REAL
) sc
= (real
)1.0;
1738 /* check for declination scale */
1739 if (TSTBIT(qmask
, Q_DECLINATION
) && sc
!= 1.0) {
1741 /* TRANSLATORS: DECLINATION is a built-in keyword, so best not to
1743 compile_diagnostic(DIAG_ERR
|DIAG_TOKEN
, /*Scale factor must be 1.0 for DECLINATION*/40);
1749 /* TRANSLATORS: If the scale factor for an instrument is zero, then any
1750 * reading would be mapped to zero, which doesn't make sense. */
1751 compile_diagnostic(DIAG_ERR
|DIAG_TOKEN
, /*Scale factor must be non-zero*/391);
1755 for (quantity
= 0, m
= BIT(quantity
); m
<= qmask
; quantity
++, m
<<= 1) {
1757 pcs
->z
[quantity
] = pcs
->units
[quantity
] * z
;
1758 pcs
->sc
[quantity
] = sc
;
1764 cmd_declination(void)
1766 real v
= read_numeric(fTrue
);
1767 if (v
== HUGE_REAL
) {
1768 get_token_no_blanks();
1769 if (strcmp(ucbuffer
, "AUTO") != 0) {
1770 compile_diagnostic(DIAG_ERR
|DIAG_SKIP
|DIAG_COL
, /*Expected number or “AUTO”*/309);
1773 /* *declination auto X Y Z */
1774 real x
= read_numeric(fFalse
);
1775 real y
= read_numeric(fFalse
);
1776 real z
= read_numeric(fFalse
);
1777 if (!pcs
->proj_str
) {
1778 compile_diagnostic(DIAG_ERR
, /*Input coordinate system must be specified for “*DECLINATION AUTO”*/301);
1781 /* Convert to WGS84 lat long. */
1782 PJ
*transform
= proj_create_crs_to_crs(PJ_DEFAULT_CTX
,
1787 /* Normalise the output order so x is longitude and y latitude - by
1788 * default new PROJ has them switched for EPSG:4326 which just seems
1791 PJ
* pj_norm
= proj_normalize_for_visualization(PJ_DEFAULT_CTX
,
1793 proj_destroy(transform
);
1794 transform
= pj_norm
;
1797 if (proj_angular_input(transform
, PJ_FWD
)) {
1798 /* Input coordinate system expects radians. */
1803 PJ_COORD coord
= {{x
, y
, z
, HUGE_VAL
}};
1804 coord
= proj_trans(transform
, PJ_FWD
, coord
);
1809 if (x
== HUGE_VAL
|| y
== HUGE_VAL
|| z
== HUGE_VAL
) {
1810 compile_diagnostic(DIAG_ERR
, /*Failed to convert coordinates: %s*/436,
1811 proj_errno_string(proj_errno(transform
)));
1812 /* Set dummy values which are finite. */
1815 proj_destroy(transform
);
1817 report_declination(pcs
);
1819 double lon
= rad(x
);
1820 double lat
= rad(y
);
1821 pcs
->z
[Q_DECLINATION
] = HUGE_REAL
;
1825 pcs
->dec_filename
= file
.filename
;
1826 pcs
->dec_line
= file
.line
;
1827 pcs
->dec_context
= grab_line();
1828 /* Invalidate cached declination. */
1829 pcs
->declination
= HUGE_REAL
;
1831 // PJ_DEFAULT_CTX is really just NULL, but PROJ < 8.1.0
1832 // dereferences the context without a NULL check inside
1833 // proj_create_ellipsoidal_2D_cs() so create a context
1834 // temporarily to avoid a segmentation fault.
1835 PJ_CONTEXT
* ctx
= PJ_DEFAULT_CTX
;
1836 #if PROJ_VERSION_MAJOR < 8 || \
1837 (PROJ_VERSION_MAJOR == 8 && PROJ_VERSION_MINOR < 1)
1838 ctx
= proj_context_create();
1841 PJ
*pj
= proj_create(ctx
, proj_str_out
);
1845 #if PROJ_VERSION_MAJOR < 8 || \
1846 (PROJ_VERSION_MAJOR == 8 && PROJ_VERSION_MINOR < 2)
1847 /* Code adapted from fix in PROJ 8.2.0 to make proj_factors() work in
1848 * cases we need (e.g. a CRS specified as "EPSG:<number>").
1850 switch (proj_get_type(pj
)) {
1851 case PJ_TYPE_PROJECTED_CRS
: {
1852 /* If it is a projected CRS, then compute the factors on the conversion
1853 * associated to it. We need to start from a temporary geographic CRS
1854 * using the same datum as the one of the projected CRS, and with
1855 * input coordinates being in longitude, latitude order in radian,
1856 * to be consistent with the expectations of the lp input parameter.
1859 PJ
* geodetic_crs
= proj_get_source_crs(ctx
, pj
);
1862 PJ
* datum
= proj_crs_get_datum(ctx
, geodetic_crs
);
1863 #if PROJ_VERSION_MAJOR == 8 || \
1864 (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
1865 /* PROJ 7.2.0 upgraded to EPSG 10.x which added the concept
1866 * of a datum ensemble, and this version of PROJ also added
1867 * an API to deal with these.
1869 * If we're using PROJ < 7.2.0 then its EPSG database won't
1870 * have datum ensembles, so we don't need any code to handle
1874 datum
= proj_crs_get_datum_ensemble(ctx
, geodetic_crs
);
1877 PJ
* cs
= proj_create_ellipsoidal_2D_cs(
1878 ctx
, PJ_ELLPS2D_LONGITUDE_LATITUDE
, "Radian", 1.0);
1879 PJ
* temp
= proj_create_geographic_crs_from_datum(
1880 ctx
, "unnamed crs", datum
, cs
);
1881 proj_destroy(datum
);
1883 proj_destroy(geodetic_crs
);
1884 PJ
* newOp
= proj_create_crs_to_crs_from_pj(ctx
, temp
, pj
, NULL
, NULL
);
1896 PJ_FACTORS factors
= proj_factors(pj
, lp
);
1897 pcs
->convergence
= factors
.meridian_convergence
;
1899 #if PROJ_VERSION_MAJOR < 8 || \
1900 (PROJ_VERSION_MAJOR == 8 && PROJ_VERSION_MINOR < 1)
1901 proj_context_destroy(ctx
);
1905 /* *declination D UNITS */
1906 int units
= get_units(BIT(Q_DECLINATION
), fFalse
);
1907 if (units
== UNITS_NULL
) {
1910 pcs
->z
[Q_DECLINATION
] = -v
* factor_tab
[units
];
1911 pcs
->convergence
= 0;
1915 #ifndef NO_DEPRECATED
1919 static const sztok defaulttab
[] = {
1920 { "CALIBRATE", CMD_CALIBRATE
},
1921 { "DATA", CMD_DATA
},
1922 { "UNITS", CMD_UNITS
},
1925 static int default_depr_count
= 0;
1927 if (default_depr_count
< 5) {
1928 /* TRANSLATORS: If you're unsure what "deprecated" means, see:
1929 * https://en.wikipedia.org/wiki/Deprecation */
1930 compile_diagnostic(DIAG_WARN
|DIAG_COL
, /**DEFAULT is deprecated - use *CALIBRATE/DATA/SD/UNITS with argument DEFAULT instead*/20);
1931 if (++default_depr_count
== 5)
1932 compile_diagnostic(DIAG_WARN
, /*Further uses of this deprecated feature will not be reported*/95);
1936 switch (match_tok(defaulttab
, TABSIZE(defaulttab
))) {
1948 compile_diagnostic(DIAG_ERR
|DIAG_BUF
|DIAG_SKIP
, /*Unknown setting “%s”*/41, buffer
);
1956 char *pth
, *fnm
= NULL
;
1958 #ifndef NO_DEPRECATED
1963 pth
= path_from_fnm(file
.filename
);
1965 read_string(&fnm
, &fnm_len
);
1967 #ifndef NO_DEPRECATED
1968 /* Since *begin / *end nesting cannot cross file boundaries we only
1969 * need to preserve the prefix if the deprecated *prefix command
1972 root
= pcs
->Prefix
; /* Root for include file is current prefix */
1976 data_file(pth
, fnm
);
1978 #ifndef NO_DEPRECATED
1979 root
= root_store
; /* and restore root */
1992 unsigned long qmask
, m
;
1994 qmask
= get_qlist(BIT(Q_DECLINATION
));
1995 if (!qmask
) return; /* no quantities found - error already reported */
1997 if (qmask
== BIT(Q_DEFAULT
)) {
2001 sd
= read_numeric(fFalse
);
2002 if (sd
<= (real
)0.0) {
2003 compile_diagnostic(DIAG_ERR
|DIAG_SKIP
|DIAG_COL
, /*Standard deviation must be positive*/48);
2006 units
= get_units(qmask
, fFalse
);
2007 if (units
== UNITS_NULL
) return;
2009 sd
*= factor_tab
[units
];
2010 variance
= sqrd(sd
);
2012 for (quantity
= 0, m
= BIT(quantity
); m
<= qmask
; quantity
++, m
<<= 1)
2013 if (qmask
& m
) pcs
->Var
[quantity
] = variance
;
2019 if (!fExplicitTitle
&& pcs
->Prefix
== root
) {
2020 /* If we don't have an explicit title yet, and we're currently in the
2021 * root prefix, use this title explicitly. */
2022 fExplicitTitle
= fTrue
;
2023 read_string(&survey_title
, &survey_title_len
);
2025 /* parse and throw away this title (but still check rest of line) */
2028 read_string(&s
, &len
);
2033 static const sztok case_tab
[] = {
2045 setting
= match_tok(case_tab
, TABSIZE(case_tab
));
2046 if (setting
!= -1) {
2047 pcs
->Case
= setting
;
2049 compile_diagnostic(DIAG_ERR
|DIAG_BUF
|DIAG_SKIP
, /*Found “%s”, expecting “PRESERVE”, “TOUPPER”, or “TOLOWER”*/10, buffer
);
2069 static const sztok cs_tab
[] = {
2070 {"CUSTOM", CS_CUSTOM
},
2071 {"EPSG", CS_EPSG
}, /* EPSG:<number> */
2072 {"ESRI", CS_ESRI
}, /* ESRI:<number> */
2073 {"EUR", CS_EUR
}, /* EUR79Z30 */
2074 {"IJTSK", CS_IJTSK
}, /* IJTSK or IJTSK03 */
2075 {"JTSK", CS_JTSK
}, /* JTSK or JTSK03 */
2076 {"LAT", CS_LAT
}, /* LAT-LONG */
2077 {"LOCAL", CS_LOCAL
},
2078 {"LONG", CS_LONG
}, /* LONG-LAT */
2079 {"OSGB", CS_OSGB
}, /* OSGB:<H, N, O, S or T><A-Z except I> */
2080 {"S", CS_S_MERC
}, /* S-MERC */
2081 {"UTM", CS_UTM
}, /* UTM<zone><N or S or nothing> */
2088 char * proj_str
= NULL
;
2091 int cs_sub
= INT_MIN
;
2093 bool output
= fFalse
;
2094 enum { YES
, NO
, MAYBE
} ok_for_output
= YES
;
2095 static bool had_cs
= fFalse
;
2099 if (first_fix_name
) {
2100 compile_diagnostic_at(DIAG_ERR
,
2101 first_fix_filename
, first_fix_line
,
2102 /*Station “%s” fixed before CS command first used*/442,
2103 sprint_prefix(first_fix_name
));
2108 /* Note get_token() only accepts letters - it'll stop at digits so "UTM12"
2109 * will give token "UTM". */
2111 if (strcmp(ucbuffer
, "OUT") == 0) {
2116 cs
= match_tok(cs_tab
, TABSIZE(cs_tab
));
2121 ok_for_output
= MAYBE
;
2123 read_string(&proj_str
, &proj_str_len
);
2126 case CS_EPSG
: case CS_ESRI
:
2127 ok_for_output
= MAYBE
;
2128 if (ch
== ':' && isdigit(nextch())) {
2129 unsigned n
= read_uint();
2137 read_uint() == 79 &&
2138 (ch
== 'Z' || ch
== 'z') &&
2139 isdigit(nextch()) &&
2140 read_uint() == 30) {
2149 if (nextch() == '3') {
2157 case CS_LAT
: case CS_LONG
:
2161 get_token_no_blanks();
2162 cs_class cs2
= match_tok(cs_tab
, TABSIZE(cs_tab
));
2163 if ((cs
^ cs2
) == (CS_LAT
^ CS_LONG
)) {
2173 int uch1
= toupper(nextch());
2174 if (strchr("HNOST", uch1
)) {
2175 int uch2
= toupper(nextch());
2176 if (uch2
>= 'A' && uch2
<= 'Z' && uch2
!= 'I') {
2179 if (uch1
> 'I') --uch1
;
2181 if (uch2
> 'I') --uch2
;
2185 x
= (x
* 5) + uch2
% 5;
2186 y
= (y
* 5) + uch2
/ 5;
2187 cs_sub
= y
* 25 + x
;
2195 get_token_no_blanks();
2196 if (strcmp(ucbuffer
, "MERC") == 0) {
2203 unsigned n
= read_uint();
2204 if (n
>= 1 && n
<= 60) {
2205 int uch
= toupper(ch
);
2210 } else if (uch
== 'N') {
2217 if (cs_sub
== INT_MIN
|| isalnum(ch
)) {
2219 compile_diagnostic(DIAG_ERR
|DIAG_STRING
, /*Unknown coordinate system*/434);
2223 /* Actually handle the cs */
2228 /* proj_str already set */
2231 proj_str
= osmalloc(32);
2232 sprintf(proj_str
, "EPSG:%d", cs_sub
);
2235 proj_str
= osmalloc(32);
2236 sprintf(proj_str
, "ESRI:%d", cs_sub
);
2239 proj_str
= osstrdup("+proj=utm +zone=30 +ellps=intl +towgs84=-86,-98,-119,0,0,0,0 +no_defs");
2243 proj_str
= osstrdup("+proj=krovak +ellps=bessel +towgs84=570.8285,85.6769,462.842,4.9984,1.5867,5.2611,3.5623 +no_defs");
2245 proj_str
= osstrdup("+proj=krovak +ellps=bessel +towgs84=485.021,169.465,483.839,7.786342,4.397554,4.102655,0 +no_defs");
2249 proj_str
= osstrdup("+proj=krovak +czech +ellps=bessel +towgs84=570.8285,85.6769,462.842,4.9984,1.5867,5.2611,3.5623 +no_defs");
2251 proj_str
= osstrdup("+proj=krovak +czech +ellps=bessel +towgs84=485.021,169.465,483.839,7.786342,4.397554,4.102655,0 +no_defs");
2254 /* FIXME: Requires PROJ >= 4.8.0 for +axis, and the SDs will be
2255 * misapplied, so we may want to swap ourselves. Also, while
2256 * therion supports lat-long, I'm not totally convinced that it is
2257 * sensible to do so - people often say "lat-long", but probably
2258 * don't think that that's actually "Northing, Easting". This
2259 * seems like it'll result in people accidentally getting X and Y
2260 * swapped in their fixed points...
2263 proj_str
= osstrdup("+proj=longlat +ellps=WGS84 +datum=WGS84 +axis=neu +no_defs");
2267 /* FIXME: Is it useful to be able to explicitly specify this? */
2270 proj_str
= osstrdup("EPSG:4326");
2273 int x
= 14 - (cs_sub
% 25);
2274 int y
= (cs_sub
/ 25) - 20;
2275 proj_str
= osmalloc(160);
2276 sprintf(proj_str
, "+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=%d +y_0=%d +ellps=airy +datum=OSGB36 +units=m +no_defs", x
* 100000, y
* 100000);
2280 proj_str
= osstrdup("EPSG:3857");
2283 proj_str
= osmalloc(32);
2285 sprintf(proj_str
, "EPSG:%d", 32600 + cs_sub
);
2287 sprintf(proj_str
, "EPSG:%d", 32700 - cs_sub
);
2293 /* printf("CS %d:%d\n", (int)cs, cs_sub); */
2295 compile_diagnostic(DIAG_ERR
|DIAG_STRING
, /*Unknown coordinate system*/434);
2301 if (ok_for_output
== NO
) {
2303 compile_diagnostic(DIAG_ERR
|DIAG_STRING
, /*Coordinate system unsuitable for output*/435);
2308 if (proj_str_out
&& strcmp(proj_str
, proj_str_out
) == 0) {
2309 /* Same as the output cs that's already set, so nothing to do. */
2314 if (ok_for_output
== MAYBE
) {
2315 /* We only actually create the transformation from input to output when
2316 * we need it, but for a custom proj string or EPSG/ESRI code we need
2317 * to check that the specified coordinate system is valid and also if
2318 * it's suitable for output so we need to test creating it here.
2320 PJ
* pj
= proj_create(PJ_DEFAULT_CTX
, proj_str
);
2323 compile_diagnostic(DIAG_ERR
|DIAG_STRING
, /*Invalid coordinate system: %s*/443,
2324 proj_errno_string(proj_context_errno(PJ_DEFAULT_CTX
)));
2329 int type
= proj_get_type(pj
);
2330 if (type
== PJ_TYPE_GEOGRAPHIC_2D_CRS
||
2331 type
== PJ_TYPE_GEOGRAPHIC_3D_CRS
) {
2333 compile_diagnostic(DIAG_ERR
|DIAG_STRING
, /*Coordinate system unsuitable for output*/435);
2341 /* If the output cs is already set, subsequent attempts to set it
2342 * are silently ignored (so you can combine two datasets and set
2343 * the output cs to use before you include either).
2347 proj_str_out
= proj_str
;
2350 if (proj_str_out
&& strcmp(proj_str
, proj_str_out
) == 0) {
2351 /* Same as the current output projection, so valid for input. */
2352 } else if (pcs
->proj_str
&& strcmp(proj_str
, pcs
->proj_str
) == 0) {
2353 /* Same as the current input projection, so nothing to do! */
2355 } else if (ok_for_output
== MAYBE
) {
2356 /* (ok_for_output == MAYBE) also happens to indicate whether we need
2357 * to check that the coordinate system is valid for input.
2359 PJ
* pj
= proj_create(PJ_DEFAULT_CTX
, proj_str
);
2362 compile_diagnostic(DIAG_ERR
|DIAG_STRING
, /*Invalid coordinate system: %s*/443,
2363 proj_errno_string(proj_context_errno(PJ_DEFAULT_CTX
)));
2370 /* Free current input proj_str if not used by parent. */
2372 if (!p
->next
|| p
->proj_str
!= p
->next
->proj_str
)
2373 osfree(p
->proj_str
);
2374 p
->proj_str
= proj_str
;
2375 invalidate_pj_cached();
2379 static const sztok infer_tab
[] = {
2380 { "EQUATES", INFER_EQUATES
},
2381 { "EXPORTS", INFER_EXPORTS
},
2382 { "PLUMBS", INFER_PLUMBS
},
2384 { "SUBSURVEYS", INFER_SUBSURVEYS
},
2386 { NULL
, INFER_NULL
}
2389 static const sztok onoff_tab
[] = {
2401 setting
= match_tok(infer_tab
, TABSIZE(infer_tab
));
2402 if (setting
== INFER_NULL
) {
2403 compile_diagnostic(DIAG_ERR
|DIAG_BUF
|DIAG_SKIP
, /*Found “%s”, expecting “EQUATES”, “EXPORTS”, or “PLUMBS”*/31, buffer
);
2407 on
= match_tok(onoff_tab
, TABSIZE(onoff_tab
));
2409 compile_diagnostic(DIAG_ERR
|DIAG_BUF
|DIAG_SKIP
, /*Found “%s”, expecting “ON” or “OFF”*/32, buffer
);
2414 pcs
->infer
|= BIT(setting
);
2415 if (setting
== INFER_EXPORTS
) fExportUsed
= fTrue
;
2417 pcs
->infer
&= ~BIT(setting
);
2424 unsigned int truncate_at
= 0; /* default is no truncation */
2430 if (strcmp(ucbuffer
, "OFF") != 0) {
2431 if (*ucbuffer
) set_pos(&fp
);
2432 truncate_at
= read_uint();
2434 /* for backward compatibility, "*truncate 0" means "*truncate off" */
2435 pcs
->Truncate
= (truncate_at
== 0) ? INT_MAX
: truncate_at
;
2441 /* Just syntax check for now. */
2444 read_string(&ref
, &ref_len
);
2451 const unsigned int version
[] = {COMMAVERSION
};
2452 const unsigned int *ver
= version
;
2458 int diff
= *ver
++ - read_uint();
2459 if (diff
> 0) break;
2465 /* find end of version number */
2466 while (isdigit(ch
) || ch
== '.') nextch();
2468 len
= (size_t)(fp_tmp
.offset
- fp
.offset
);
2469 v
= osmalloc(len
+ 1);
2471 for (i
= 0; i
< len
; i
++) {
2476 /* TRANSLATORS: Feel free to translate as "or newer" instead of "or
2477 * greater" if that gives a more natural translation. It's
2478 * technically not quite right when there are parallel active release
2479 * series (e.g. Survex 1.0.40 was released *after* 1.2.0), but this
2480 * seems unlikely to confuse users. "Survex" is the name of the
2481 * software, so should not be translated.
2483 * Here "survey" is a "cave map" rather than list of questions - it should be
2484 * translated to the terminology that cavers using the language would use.
2486 fatalerror_in_file(file
.filename
, file
.line
, /*Survex version %s or greater required to process this survey data.*/2, v
);
2488 if (ch
!= '.') break;
2490 if (!isdigit(ch
) || ver
== version
+ sizeof(version
) / sizeof(*version
))
2493 /* skip rest of version number */
2494 while (isdigit(ch
) || ch
== '.') nextch();
2497 /* allocate new meta_data if need be */
2499 copy_on_write_meta(settings
*s
)
2501 if (!s
->meta
|| s
->meta
->ref_count
!= 0) {
2502 meta_data
* meta_new
= osnew(meta_data
);
2504 meta_new
->days1
= meta_new
->days2
= -1;
2506 *meta_new
= *(s
->meta
);
2508 meta_new
->ref_count
= 0;
2516 int year
, month
, day
;
2518 bool implicit_range
= fFalse
;
2522 read_date(&year
, &month
, &day
);
2523 days1
= days_since_1900(year
, month
? month
: 1, day
? day
: 1);
2525 if (days1
> current_days_since_1900
) {
2527 compile_diagnostic(DIAG_WARN
|DIAG_DATE
, /*Date is in the future!*/80);
2534 read_date(&year
, &month
, &day
);
2540 implicit_range
= fTrue
;
2543 if (month
== 0) month
= 12;
2544 if (day
== 0) day
= last_day(year
, month
);
2545 days2
= days_since_1900(year
, month
, day
);
2547 if (!implicit_range
&& days2
> current_days_since_1900
) {
2549 compile_diagnostic(DIAG_WARN
|DIAG_DATE
, /*Date is in the future!*/80);
2552 if (days2
< days1
) {
2554 compile_diagnostic(DIAG_ERR
|DIAG_TOKEN
, /*End of date range is before the start*/81);
2561 if (!pcs
->meta
|| pcs
->meta
->days1
!= days1
|| pcs
->meta
->days2
!= days2
) {
2562 copy_on_write_meta(pcs
);
2563 pcs
->meta
->days1
= days1
;
2564 pcs
->meta
->days2
= days2
;
2565 /* Invalidate cached declination. */
2566 pcs
->declination
= HUGE_REAL
;
2570 typedef void (*cmd_fn
)(void);
2572 static const cmd_fn cmd_funcs
[] = {
2577 skipline
, /*cmd_copyright,*/
2582 #ifndef NO_DEPRECATED
2593 skipline
, /*cmd_instrument,*/
2594 #ifndef NO_DEPRECATED
2602 skipline
, /*cmd_team,*/
2609 handle_command(void)
2613 cmdtok
= match_tok(cmd_tab
, TABSIZE(cmd_tab
));
2615 if (cmdtok
< 0 || cmdtok
>= (int)(sizeof(cmd_funcs
) / sizeof(cmd_fn
))) {
2616 compile_diagnostic(DIAG_ERR
|DIAG_BUF
|DIAG_SKIP
, /*Unknown command “%s”*/12, buffer
);
2623 /* TRANSLATORS: The *EXPORT command is only valid just after *BEGIN
2624 * <SURVEY>, so this would generate this error:
2630 compile_diagnostic(DIAG_ERR
, /**EXPORT must immediately follow “*BEGIN <SURVEY>”*/57);
2639 case CMD_DECLINATION
:
2643 case CMD_INSTRUMENT
:
2652 /* These can occur between *begin and *export */
2655 /* NB: additional handling for "*begin <survey>" in cmd_begin */
2656 f_export_ok
= fFalse
;
2660 cmd_funcs
[cmdtok
]();