2 * Fairly general purpose message and error routines
3 * Copyright (C) 1993-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
47 # define WIN32_LEAN_AND_MEAN
50 # include <sys/types.h>
55 /* For funcs which want to be immune from messing around with different
56 * calling conventions */
61 int msg_warnings
= 0; /* keep track of how many warnings we've given */
62 int msg_errors
= 0; /* and how many (non-fatal) errors */
64 /* in case osmalloc() fails before appname_copy is set up */
65 static const char *appname_copy
= "anonymous program";
67 /* Path to use to look for executables (used by aven to find cavern). */
68 static const char *exe_pth
= "";
70 /* error code for failed osmalloc and osrealloc calls */
72 outofmem(OSSIZE_T size
)
74 /* TRANSLATORS: "%lu" is a placeholder for the number of bytes which Survex
75 * was trying to allocate space for. */
76 fatalerror(/*Out of memory (couldn’t find %lu bytes).*/1,
81 #define TOMBSTONE_SIZE 16
82 static const char tombstone
[TOMBSTONE_SIZE
] = "012345\xfftombstone";
85 /* malloc with error catching if it fails. Also allows us to write special
86 * versions easily eg for MS Windows.
89 osmalloc(OSSIZE_T size
)
93 size
+= TOMBSTONE_SIZE
* 2;
98 if (p
== NULL
) outofmem(size
);
100 printf("osmalloc truep=%p truesize=%d\n", p
, size
);
101 memcpy(p
, tombstone
, TOMBSTONE_SIZE
);
102 memcpy(p
+ size
- TOMBSTONE_SIZE
, tombstone
, TOMBSTONE_SIZE
);
109 /* realloc with error catching if it fails. */
111 osrealloc(void *p
, OSSIZE_T size
)
113 /* some pre-ANSI realloc implementations don't cope with a NULL pointer */
119 size
+= TOMBSTONE_SIZE
* 2;
121 true_size
= *(size_t *)p
;
122 printf("osrealloc (in truep=%p truesize=%d)\n", p
, true_size
);
123 if (memcmp(p
+ sizeof(size_t), tombstone
+ sizeof(size_t),
124 TOMBSTONE_SIZE
- sizeof(size_t)) != 0) {
125 printf("start tombstone for block %p, size %d corrupted!",
126 p
+ TOMBSTONE_SIZE
, true_size
- TOMBSTONE_SIZE
* 2);
128 if (memcmp(p
+ true_size
- TOMBSTONE_SIZE
, tombstone
,
129 TOMBSTONE_SIZE
) != 0) {
130 printf("end tombstone for block %p, size %d corrupted!",
131 p
+ TOMBSTONE_SIZE
, true_size
- TOMBSTONE_SIZE
* 2);
133 p
= realloc(p
, size
);
134 if (p
== NULL
) outofmem(size
);
135 printf("osrealloc truep=%p truesize=%d\n", p
, size
);
136 memcpy(p
, tombstone
, TOMBSTONE_SIZE
);
137 memcpy(p
+ size
- TOMBSTONE_SIZE
, tombstone
, TOMBSTONE_SIZE
);
141 p
= xosrealloc(p
, size
);
144 if (p
== NULL
) outofmem(size
);
149 osstrdup(const char *str
)
153 len
= strlen(str
) + 1;
159 /* osfree is usually just a macro in osalloc.h */
167 true_size
= *(size_t *)p
;
168 printf("osfree truep=%p truesize=%d\n", p
, true_size
);
169 if (memcmp(p
+ sizeof(size_t), tombstone
+ sizeof(size_t),
170 TOMBSTONE_SIZE
- sizeof(size_t)) != 0) {
171 printf("start tombstone for block %p, size %d corrupted!",
172 p
+ TOMBSTONE_SIZE
, true_size
- TOMBSTONE_SIZE
* 2);
174 if (memcmp(p
+ true_size
- TOMBSTONE_SIZE
, tombstone
,
175 TOMBSTONE_SIZE
) != 0) {
176 printf("end tombstone for block %p, size %d corrupted!",
177 p
+ TOMBSTONE_SIZE
, true_size
- TOMBSTONE_SIZE
* 2);
184 default_charset(void)
186 if (getenv("SURVEX_UTF8")) return CHARSET_UTF8
;
189 # define CODEPAGE GetACP()
191 # define CODEPAGE GetConsoleOutputCP()
194 case 0: return CHARSET_UTF8
;
195 case 1252: return CHARSET_WINCP1252
;
196 case 1250: return CHARSET_WINCP1250
;
197 case 850: return CHARSET_DOSCP850
;
199 return CHARSET_USASCII
;
204 const char *p
= getenv("LC_ALL");
205 if (p
== NULL
|| p
[0] == '\0') {
206 p
= getenv("LC_CTYPE");
207 if (p
== NULL
|| p
[0] == '\0') {
209 /* Something (AutoCAD?) on Microsoft Windows sets LANG to a number. */
210 if (p
== NULL
|| !isalpha((unsigned char)p
[0]))
216 const char *q
= strchr(p
, '.');
221 const char *chset
= p
;
224 while (*p
!= '\0' && *p
!= '@') p
++;
226 name_len
= p
- chset
;
232 for (cnt
= 0; cnt
< name_len
; ++cnt
)
233 if (isalpha((unsigned char)chset
[cnt
])) {
238 if (only_digit
) goto iso
;
240 switch (tolower((unsigned char)chset
[0])) {
242 if (tolower((unsigned char)chset
[1]) == 's' &&
243 tolower((unsigned char)chset
[2]) == 'o') {
246 if (strncmp(chset
, "8859", 4) == 0) {
248 while (chset
< p
&& *chset
&& !isdigit((unsigned char)*chset
))
250 switch (atoi(chset
)) {
251 case 1: return CHARSET_ISO_8859_1
;
252 case 2: return CHARSET_ISO_8859_2
;
253 case 15: return CHARSET_ISO_8859_15
;
254 default: return CHARSET_USASCII
;
260 if (tolower((unsigned char)chset
[1]) == 't' &&
261 tolower((unsigned char)chset
[2]) == 'f') {
263 while (chset
< p
&& *chset
&& !isdigit((unsigned char)*chset
))
265 switch (atoi(chset
)) {
266 case 8: return CHARSET_UTF8
;
267 default: return CHARSET_USASCII
;
273 return CHARSET_USASCII
;
276 # error Do not know operating system!
280 /* It seems that Swedish and maybe some other scandanavian languages don't
281 * transliterate ä to ae - but it seems there may be conflicting views
284 #define umlaut_to_e() 1
286 /* values <= 127 already dealt with */
288 add_unicode(int charset
, unsigned char *p
, int value
)
291 fprintf(stderr
, "add_unicode(%d, %p, %d)\n", charset
, p
, value
);
293 if (value
== 0) return 0;
295 case CHARSET_USASCII
:
301 case CHARSET_ISO_8859_1
:
307 case CHARSET_ISO_8859_2
:
311 case 0xa0: case 0xa4: case 0xa7: case 0xa8: case 0xad: case 0xb0:
312 case 0xb4: case 0xb8: case 0xc1: case 0xc2: case 0xc4: case 0xc7:
313 case 0xc9: case 0xcb: case 0xcd: case 0xce: case 0xd3: case 0xd4:
314 case 0xd6: case 0xd7: case 0xda: case 0xdc: case 0xdd: case 0xdf:
315 case 0xe1: case 0xe2: case 0xe4: case 0xe7: case 0xe9: case 0xeb:
316 case 0xed: case 0xee: case 0xf3: case 0xf4: case 0xf6: case 0xf7:
317 case 0xfa: case 0xfc: case 0xfd:
319 case 0x104: v
= '\xa1'; break;
320 case 0x2d8: v
= '\xa2'; break;
321 case 0x141: v
= '\xa3'; break;
322 case 0x13d: v
= '\xa5'; break;
323 case 0x15a: v
= '\xa6'; break;
324 case 0x160: v
= '\xa9'; break;
325 case 0x15e: v
= '\xaa'; break; /* Scedil */
326 case 0x164: v
= '\xab'; break;
327 case 0x179: v
= '\xac'; break;
328 case 0x17d: v
= '\xae'; break;
329 case 0x17b: v
= '\xaf'; break;
330 case 0x105: v
= '\xb1'; break;
331 case 0x2db: v
= '\xb2'; break;
332 case 0x142: v
= '\xb3'; break;
333 case 0x13e: v
= '\xb5'; break;
334 case 0x15b: v
= '\xb6'; break;
335 case 0x2c7: v
= '\xb7'; break;
336 case 0x161: v
= '\xb9'; break;
337 case 0x15f: v
= '\xba'; break; /* scedil */
338 case 0x165: v
= '\xbb'; break;
339 case 0x17a: v
= '\xbc'; break;
340 case 0x2dd: v
= '\xbd'; break;
341 case 0x17e: v
= '\xbe'; break;
342 case 0x17c: v
= '\xbf'; break;
343 case 0x154: v
= '\xc0'; break;
344 case 0x102: v
= '\xc3'; break;
345 case 0x139: v
= '\xc5'; break;
346 case 0x106: v
= '\xc6'; break;
347 case 0x10c: v
= '\xc8'; break;
348 case 0x118: v
= '\xca'; break;
349 case 0x11a: v
= '\xcc'; break;
350 case 0x10e: v
= '\xcf'; break;
351 case 0x110: v
= '\xd0'; break;
352 case 0x143: v
= '\xd1'; break;
353 case 0x147: v
= '\xd2'; break;
354 case 0x150: v
= '\xd5'; break;
355 case 0x158: v
= '\xd8'; break;
356 case 0x16e: v
= '\xd9'; break;
357 case 0x170: v
= '\xdb'; break;
358 case 0x162: v
= '\xde'; break; /* Ţ */
359 case 0x155: v
= '\xe0'; break;
360 case 0x103: v
= '\xe3'; break;
361 case 0x13a: v
= '\xe5'; break;
362 case 0x107: v
= '\xe6'; break;
363 case 0x10d: v
= '\xe8'; break;
364 case 0x119: v
= '\xea'; break;
365 case 0x11b: v
= '\xec'; break;
366 case 0x10f: v
= '\xef'; break;
367 case 0x111: v
= '\xf0'; break;
368 case 0x144: v
= '\xf1'; break;
369 case 0x148: v
= '\xf2'; break;
370 case 0x151: v
= '\xf5'; break;
371 case 0x159: v
= '\xf8'; break;
372 case 0x16f: v
= '\xf9'; break;
373 case 0x171: v
= '\xfb'; break;
374 case 0x163: v
= '\xfe'; break; /* tcedil */
375 case 0x2d9: v
= '\xff'; break;
382 case CHARSET_ISO_8859_15
:
384 case 0xa4: case 0xa6: case 0xb0: case 0xc4:
385 case 0xd0: case 0xd4: case 0xd5: case 0xd6:
387 case 0x152: value
= 0xd4; break; /* Œ */
388 case 0x153: value
= 0xd5; break; /* œ */
390 case 0x0: value
= 0xa4; break; /* euro */
392 case 0x160: value
= 0xa6; break; /* Scaron */
393 case 0x161: value
= 0xb0; break; /* scaron */
394 case 0x17d: value
= 0xc4; break; /* Zcaron */
395 case 0x17e: value
= 0xd0; break; /* zcaron */
397 case 0x0: value
= 0xd6; break; /* Ydiersis */
407 case CHARSET_WINCP1250
:
408 /* MS Windows rough equivalent to ISO-8859-2 */
411 /* This mapping is complete - there are 5 unused positions:
412 * 0x81 0x83 0x88 0x90 0x98 */
414 case 0xa0: case 0xa4: case 0xa6: case 0xa7: case 0xa8: case 0xa9:
415 case 0xab: case 0xac: case 0xad: case 0xae: case 0xb0: case 0xb1:
416 case 0xb4: case 0xb5: case 0xb6: case 0xb7: case 0xb8: case 0xbb:
417 case 0xc1: case 0xc2: case 0xc4: case 0xc7: case 0xc9: case 0xcb:
418 case 0xcd: case 0xce: case 0xd3: case 0xd4: case 0xd6: case 0xd7:
419 case 0xda: case 0xdc: case 0xdd: case 0xdf: case 0xe1: case 0xe2:
420 case 0xe4: case 0xe7: case 0xe9: case 0xeb: case 0xed: case 0xee:
421 case 0xf3: case 0xf4: case 0xf6: case 0xf7: case 0xfa: case 0xfc:
424 case 0x20ac: v
= '\x80'; break;
425 case 0x201a: v
= '\x82'; break;
426 case 0x201e: v
= '\x84'; break;
427 case 0x2026: v
= '\x85'; break;
428 case 0x2020: v
= '\x86'; break;
429 case 0x2021: v
= '\x87'; break;
430 case 0x2030: v
= '\x89'; break;
431 case 0x0160: v
= '\x8a'; break;
432 case 0x2039: v
= '\x8b'; break;
433 case 0x015a: v
= '\x8c'; break;
434 case 0x0164: v
= '\x8d'; break;
435 case 0x017d: v
= '\x8e'; break;
436 case 0x0179: v
= '\x8f'; break;
437 case 0x2018: v
= '\x91'; break;
438 case 0x2019: v
= '\x92'; break;
439 case 0x201c: v
= '\x93'; break;
440 case 0x201d: v
= '\x94'; break;
441 case 0x2022: v
= '\x95'; break;
442 case 0x2013: v
= '\x96'; break;
443 case 0x2014: v
= '\x97'; break;
444 case 0x2122: v
= '\x99'; break;
445 case 0x0161: v
= '\x9a'; break;
446 case 0x203a: v
= '\x9b'; break;
447 case 0x015b: v
= '\x9c'; break;
448 case 0x0165: v
= '\x9d'; break;
449 case 0x017e: v
= '\x9e'; break;
450 case 0x017a: v
= '\x9f'; break;
451 case 0x02c7: v
= '\xa1'; break;
452 case 0x02d8: v
= '\xa2'; break;
453 case 0x0141: v
= '\xa3'; break;
454 case 0x0104: v
= '\xa5'; break;
455 case 0x015e: v
= '\xaa'; break; /* Scedil */
456 case 0x017b: v
= '\xaf'; break;
457 case 0x02db: v
= '\xb2'; break;
458 case 0x0142: v
= '\xb3'; break;
459 case 0x0105: v
= '\xb9'; break;
460 case 0x015f: v
= '\xba'; break; /* scedil */
461 case 0x013d: v
= '\xbc'; break;
462 case 0x02dd: v
= '\xbd'; break;
463 case 0x013e: v
= '\xbe'; break;
464 case 0x017c: v
= '\xbf'; break;
465 case 0x0154: v
= '\xc0'; break;
466 case 0x0102: v
= '\xc3'; break;
467 case 0x0139: v
= '\xc5'; break;
468 case 0x0106: v
= '\xc6'; break;
469 case 0x010c: v
= '\xc8'; break;
470 case 0x0118: v
= '\xca'; break;
471 case 0x011a: v
= '\xcc'; break;
472 case 0x010e: v
= '\xcf'; break;
473 case 0x0110: v
= '\xd0'; break;
474 case 0x0143: v
= '\xd1'; break;
475 case 0x0147: v
= '\xd2'; break;
476 case 0x0150: v
= '\xd5'; break;
477 case 0x0158: v
= '\xd8'; break;
478 case 0x016e: v
= '\xd9'; break;
479 case 0x0170: v
= '\xdb'; break;
480 case 0x0162: v
= '\xde'; break; /* Ţ */
481 case 0x0155: v
= '\xe0'; break;
482 case 0x0103: v
= '\xe3'; break;
483 case 0x013a: v
= '\xe5'; break;
484 case 0x0107: v
= '\xe6'; break;
485 case 0x010d: v
= '\xe8'; break;
486 case 0x0119: v
= '\xea'; break;
487 case 0x011b: v
= '\xec'; break;
488 case 0x010f: v
= '\xef'; break;
489 case 0x0111: v
= '\xf0'; break;
490 case 0x0144: v
= '\xf1'; break;
491 case 0x0148: v
= '\xf2'; break;
492 case 0x0151: v
= '\xf5'; break;
493 case 0x0159: v
= '\xf8'; break;
494 case 0x016f: v
= '\xf9'; break;
495 case 0x0171: v
= '\xfb'; break;
496 case 0x0163: v
= '\xfe'; break; /* tcedil */
497 case 0x02d9: v
= '\xff'; break;
504 case CHARSET_WINCP1252
:
505 /* MS Windows extensions to ISO-8859-1 */
506 /* This mapping is complete - there are 5 unused positions:
507 * 0x81 0x8d 0x8f 0x90 0x9d */
509 case 0x2026: value
= 0x85; break; /* hellip */
510 case 0x0160: value
= 0x8a; break; /* Scaron */
511 case 0x0152: value
= 0x8c; break; /* OElig */
512 case 0x017d: value
= 0x8e; break; /* Zcaron */
513 case 0x2019: value
= 0x92; break; /* rsquo */
514 case 0x201c: value
= 0x93; break; /* ldquo */
515 case 0x201d: value
= 0x94; break; /* rdquo */
516 case 0x0161: value
= 0x9a; break; /* scaron */
517 case 0x0153: value
= 0x9c; break; /* oelig */
518 case 0x017e: value
= 0x9e; break; /* zcaron */
520 /* there are a few other obscure ones we don't currently need */
521 case 0x20ac: value
= 0x80; break; /* euro */
522 case 0x201a: value
= 0x82; break; /* sbquo */
523 case 0x0192: value
= 0x83; break; /* fnof */
524 case 0x201e: value
= 0x84; break; /* bdquo */
525 case 0x2020: value
= 0x86; break; /* dagger */
526 case 0x2021: value
= 0x87; break; /* Dagger */
527 case 0x02c6: value
= 0x88; break; /* circ */
528 case 0x2030: value
= 0x89; break; /* permil */
529 case 0x2039: value
= 0x8b; break; /* lsaquo */
530 case 0x2018: value
= 0x91; break; /* lsquo */
531 case 0x2022: value
= 0x95; break; /* bull */
532 case 0x2013: value
= 0x96; break; /* ndash */
533 case 0x2014: value
= 0x97; break; /* mdash */
534 case 0x02dc: value
= 0x98; break; /* tilde */
535 case 0x2122: value
= 0x99; break; /* trade */
536 case 0x203a: value
= 0x9b; break; /* rsaquo */
537 case 0x0178: value
= 0x9f; break; /* Yuml */
547 case CHARSET_DOSCP850
: {
548 static const unsigned char uni2dostab
[] = {
549 255, 173, 189, 156, 207, 190, 221, 245,
550 249, 184, 166, 174, 170, 240, 169, 238,
551 248, 241, 253, 252, 239, 230, 244, 250,
552 247, 251, 167, 175, 172, 171, 243, 168,
553 183, 181, 182, 199, 142, 143, 146, 128,
554 212, 144, 210, 211, 222, 214, 215, 216,
555 209, 165, 227, 224, 226, 229, 153, 158,
556 157, 235, 233, 234, 154, 237, 232, 225,
557 133, 160, 131, 198, 132, 134, 145, 135,
558 138, 130, 136, 137, 141, 161, 140, 139,
559 208, 164, 149, 162, 147, 228, 148, 246,
560 155, 151, 163, 150, 129, 236, 231, 152
562 if (value
>= 160 && value
< 256) {
563 *p
= (int)uni2dostab
[value
- 160];
567 if (value
== 305) { /* LATIN SMALL LETTER DOTLESS I */
568 *p
= 213; /* "Modified CP850" has the Euro sign here. */
571 if (value
== 402) { /* LATIN SMALL LETTER F WITH HOOK */
580 /* Transliterate characters we can't represent */
582 fprintf(stderr
, "transliterate “%c” 0x%x\n", value
, value
);
590 *p
= 'd'; p
[1] = 'g'; return 2;
593 case 192 /* À */: case 193 /* Á */: case 194 /* Â */: case 195 /* Ã */:
596 p
[1] = *p
= 'A'; return 2;
597 case 196 /* Ä */: /* Ä */
599 if (!umlaut_to_e()) return 1;
600 p
[1] = 'E'; return 2;
602 *p
= 'A'; p
[1] = 'E'; return 2;
603 case 199 /* Ç */: case 268: /* Č */
605 case 270: /* Ď */
607 case 200 /* È */: case 201 /* É */: case 202 /* Ê */: case 203 /* Ë */:
609 case 204 /* Ì */: case 205 /* Í */: case 206 /* Î */: case 207 /* Ï */:
611 case 208 /* Ð */: case 222 /* Þ */:
612 *p
= 'T'; p
[1] = 'H'; return 2;
613 case 315: /* Ĺ */
614 case 317: /* Ľ */
618 case 210 /* Ò */: case 211 /* Ó */: case 212 /* Ô */: case 213 /* Õ */:
620 case 214 /* Ö */: /* Ö */ case 0x152: /* Œ */
621 *p
= 'O'; p
[1] = 'E'; return 2;
622 case 352: /* Š */
623 case 0x15e: /* Ş */
625 case 0x162: /* Ţ */
626 case 0x164: /* Ť */
628 case 217 /* Ù */: case 218 /* Ú */: case 219 /* Û */:
630 case 220 /* Ü */: /* Ü */
631 *p
= 'U'; p
[1] = 'E'; return 2;
634 case 381: /* Ž */
637 p
[1] = *p
= 's'; return 2;
638 case 224 /* à */: case 225 /* á */: case 226 /* â */: case 227 /* ã */:
639 case 259: /* ă */
641 case 228 /* ä */: /* ä */ case 230 /* æ */:
642 *p
= 'a'; p
[1] = 'e'; return 2;
644 p
[1] = *p
= 'a'; return 2;
645 case 231 /* ç */: case 269 /* č */:
647 case 271: /* ď */
649 case 232 /* è */: case 233 /* é */: case 234 /* ê */: case 235 /* ë */:
650 case 283 /* ě */:
652 case 236 /* ì */: case 237 /* í */: case 238 /* î */: case 239 /* ï */:
654 case 316 /* ĺ */:
655 case 318 /* ľ */:
657 case 241 /* ñ */: case 328 /* ň */:
659 case 345: /* ř */
661 case 353: /* š */
662 case 0x15f: /* ş */
664 case 357: /* ť */
665 case 0x163: /* ţ */
667 case 240 /* ð */: case 254 /* þ */:
668 *p
= 't'; p
[1] = 'h'; return 2;
669 case 242 /* ò */: case 243 /* ó */: case 244 /* ô */: case 245 /* õ */:
671 case 246 /* ö */: /* ö */ case 0x153: /* œ */
672 *p
= 'o'; p
[1] = 'e'; return 2;
673 case 249 /* ù */: case 250 /* ú */: case 251 /* û */:
674 case 367 /* ů */:
676 case 252 /* ü */: /* ü */
677 *p
= 'u'; p
[1] = 'e'; return 2;
678 case 253 /* ý */: case 255 /* ÿ */:
680 case 382: /* ž */
682 case 0x2019: /* ‘ */
684 case 171: /* « */ case 187: /* » */
685 case 0x201c: /* “ */ case 0x201d: /* ” */
687 case 0x2026: /* … */
688 *p
= '.'; p
[1] = '.'; p
[2] = '.'; return 3;
689 case 0x2192: /* → */
690 *p
= '-'; p
[1] = '>'; return 2;
691 case 0x1d4d: /* gradient symbol */
692 *p
= 'g'; p
[1] = 'r'; p
[2] = 'd'; return 3;
693 case 0x221e: /* infinity symbol */
694 *p
= 'i'; p
[1] = 'n'; p
[2] = 'f'; return 3;
697 /* 169 is reported (copyright symbol), but there isn't a good <= 2 ASCII
698 * character transliteration for that, so we handle that elsewhere. */
699 fprintf(stderr
, "failed to transliterate codepoint %d\n", value
);
704 #if OS_UNIX && defined DATADIR && defined PACKAGE
705 /* Under Unix, we compile in the configured path */
706 static const char *pth_cfg_files
= DATADIR
"/" PACKAGE
;
708 /* On other platforms, we fall back on looking in the current directory */
709 static const char *pth_cfg_files
= "";
712 static int num_msgs
= 0;
713 static char **msg_array
= NULL
;
715 static bool msg_lang_explicit
= false;
716 const char *msg_lang
= NULL
;
717 const char *msg_lang2
= NULL
;
720 parse_msgs(int n
, unsigned char *p
, int charset_code
) {
723 char **msgs
= osmalloc(n
* sizeof(char *));
725 for (i
= 0; i
< n
; i
++) {
726 unsigned char *to
= p
;
730 /* If we want UTF8 anyway, we just need to find the start of each
732 if (charset_code
== CHARSET_UTF8
) {
733 p
+= strlen((char *)p
) + 1;
737 while ((ch
= *p
++) != 0) {
738 /* A byte in the range 0x80-0xbf or 0xf0-0xff isn't valid in
739 * this state, (0xf0-0xfd mean values > 0xffff) so treat as
740 * literal and try to resync so we cope better when fed
741 * non-utf-8 data. Similarly we abandon a multibyte sequence
742 * if we hit an invalid character. */
743 if (ch
>= 0xc0 && ch
< 0xf0) {
745 if ((ch1
& 0xc0) != 0x80) goto resync
;
748 /* 2 byte sequence */
749 ch
= ((ch
& 0x1f) << 6) | (ch1
& 0x3f);
752 /* 3 byte sequence */
754 if ((ch2
& 0xc0) != 0x80) goto resync
;
755 ch
= ((ch
& 0x1f) << 12) | ((ch1
& 0x3f) << 6) | (ch2
& 0x3f);
765 /* We assume an N byte UTF-8 code never transliterates to more
766 * than N characters (so we can't transliterate © to (C) or
767 * ® to (R) for example) */
768 to
+= add_unicode(charset_code
, to
, ch
);
776 /* This is the name of the default language, which can be set like so:
777 * ./configure --enable-defaultlang=fr
780 /* No point extracting these errors as they won't get used if file opens */
781 # include "../lib/defaultlang.h"
783 #define N_DONTEXTRACTMSGS 5
784 static unsigned char dontextractmsgs
[] =
785 "Can't open message file \"%s\" using path \"%s\"\0"/*1000*/
786 "Problem with message file \"%s\"\0"/*1001*/
787 "I don't understand this message file version\0"/*1002*/
788 "Message file truncated?\0"/*1003*/
789 "Out of memory (couldn't find %lu bytes).\0"/*1004*/;
792 static char **dontextract
= NULL
;
795 parse_msg_file(int charset_code
)
798 unsigned char header
[20];
806 fprintf(stderr
, "parse_msg_file(%d)\n", charset_code
);
809 /* sort out messages we need to print if we can't open the message file */
810 dontextract
= parse_msgs(N_DONTEXTRACTMSGS
, dontextractmsgs
, charset_code
);
812 fnm
= osstrdup(msg_lang
);
813 /* trim off charset from stuff like "de_DE.iso8859_1" */
814 s
= strchr(fnm
, '.');
816 /* trim off any "@<something>" modifier. */
817 s
= strchr(fnm
, '@');
820 fh
= fopenWithPthAndExt(pth_cfg_files
, fnm
, EXT_SVX_MSG
, "rb", NULL
);
822 if (!fh
&& strlen(fnm
) > 3 && fnm
[2] == '_') {
823 /* e.g. if 'en_GB' is unknown, see if we know 'en' */
825 fh
= fopenWithPthAndExt(pth_cfg_files
, fnm
, EXT_SVX_MSG
, "rb", NULL
);
826 if (!fh
) fnm
[2] = '_'; /* for error reporting */
829 if (!fh
&& !msg_lang_explicit
) {
830 /* If msg_lang wasn't specified using environment variable SURVEXLANG,
831 * then default to 'en' if we don't find messages for language msg_lang.
833 if (fnm
[0] && fnm
[1]) {
837 fnm
= osstrdup("en");
839 fh
= fopenWithPthAndExt(pth_cfg_files
, fnm
, EXT_SVX_MSG
, "rb", NULL
);
843 fatalerror(/*Can't open message file “%s” using path “%s”*/1000,
847 if (fread(header
, 1, 20, fh
) < 20 ||
848 memcmp(header
, "Svx\nMsg\r\n\xfe\xff", 12) != 0) {
849 fatalerror(/*Problem with message file “%s”*/1001, fnm
);
853 fatalerror(/*I don't understand this message file version*/1002);
855 n
= (header
[14] << 8) | header
[15];
858 for (i
= 16; i
< 20; i
++) len
= (len
<< 8) | header
[i
];
861 if (fread(p
, 1, len
, fh
) < len
)
862 fatalerror(/*Message file truncated?*/1003);
867 fprintf(stderr
, "fnm = “%s”, n = %d, len = %d\n", fnm
, n
, len
);
871 msg_array
= parse_msgs(n
, p
, charset_code
);
878 return pth_cfg_files
;
894 (msg_init
)(char * const *argv
)
899 /* Point to argv[0] itself so we report a more helpful error if the
900 * code to work out the clean appname generates a signal */
901 appname_copy
= argv
[0];
903 /* use name as-is on Unix - programs run from path get name as supplied */
904 appname_copy
= osstrdup(argv
[0]);
906 /* use the lower-cased leafname on other platforms */
907 p
= leaf_from_fnm(argv
[0]);
910 *p
= tolower((unsigned char)*p
);
915 /* shortcut --version so you can check the version number even when the
916 * correct message file can't be found... */
917 if (argv
[1] && strcmp(argv
[1], "--version") == 0) {
922 exe_pth
= path_from_fnm(argv
[0]);
923 #if OS_UNIX && defined DATADIR && defined PACKAGE
924 bool free_pth
= false;
925 char *pth
= getenv("srcdir");
926 if (!pth
|| !pth
[0]) {
927 pth
= path_from_fnm(argv
[0]);
934 /* On macOS the programs may be installed anywhere, with the
935 * share directory and the binaries in the same directory. */
936 p
= use_path(pth
, "share/survex/en.msg");
937 if (stat(p
, &buf
) == 0 && S_ISREG(buf
.st_mode
)) {
938 pth_cfg_files
= use_path(pth
, "share/survex");
943 /* In the diskimage package, this case is used for aven, and for
944 * the hardlinked copies of cavern and extend alongside the aven
945 * binary, which are the ones which aven runs.
947 p
= use_path(pth
, "../Resources/en.msg");
948 if (stat(p
, &buf
) == 0 && S_ISREG(buf
.st_mode
)) {
949 pth_cfg_files
= use_path(pth
, "../Resources");
954 /* If we're run with an explicit path, check if "../lib/en.msg"
955 * from the program's path exists, and if so look there for
956 * support files - this allows us to test binaries in the build
958 p
= use_path(pth
, "../lib/en.msg");
959 if (stat(p
, &buf
) == 0) {
962 if (S_ISREG(buf
.st_mode
)) {
963 pth_cfg_files
= use_path(pth
, "../lib");
967 if ((buf
.st_mode
& S_IFMT
) == S_IFREG
) {
968 pth_cfg_files
= use_path(pth
, "../lib");
972 #if defined(__GNUC__) && defined(__APPLE_CC__)
978 if (free_pth
) osfree(pth
);
981 char *buf
= NULL
, *modname
;
984 buf
= osrealloc(buf
, len
);
985 got
= GetModuleFileName(NULL
, buf
, len
);
986 if (got
< len
) break;
990 /* Strange Win32 nastiness - strip prefix "\\?\" if present */
991 if (strncmp(modname
, "\\\\?\\", 4) == 0) modname
+= 4;
992 pth_cfg_files
= path_from_fnm(modname
);
995 /* Get the path to the support files from argv[0] */
996 pth_cfg_files
= path_from_fnm(argv
[0]);
1000 msg_lang
= getenv("SURVEXLANG");
1002 fprintf(stderr
, "msg_lang = %p (= \"%s\")\n", msg_lang
, msg_lang
?msg_lang
:"(null)");
1005 msg_lang_explicit
= true;
1006 if (!msg_lang
|| !*msg_lang
) {
1007 msg_lang_explicit
= false;
1008 msg_lang
= getenv("LC_ALL");
1010 if (!msg_lang
|| !*msg_lang
) {
1011 msg_lang
= getenv("LC_MESSAGES");
1012 if (!msg_lang
|| !*msg_lang
) {
1013 msg_lang
= getenv("LANG");
1014 /* Something (AutoCAD?) on Microsoft Windows sets LANG to a number. */
1015 if (msg_lang
&& !isalpha(msg_lang
[0])) msg_lang
= NULL
;
1017 if (!msg_lang
|| !*msg_lang
) {
1022 msg_lang
= STRING(DEFAULTLANG
);
1027 /* GetUserDefaultUILanguage() requires Microsoft Windows 2000 or
1028 * newer, but we don't support anything earlier than Vista.
1030 locid
= GetUserDefaultUILanguage();
1032 WORD langid
= LANGIDFROMLCID(locid
);
1033 switch (PRIMARYLANGID(langid
)) {
1034 case LANG_BULGARIAN
:
1037 /* older mingw compilers don't seem to supply this value */
1038 #ifndef LANG_CATALAN
1039 # define LANG_CATALAN 0x03
1048 if (SUBLANGID(langid
) == SUBLANG_ENGLISH_US
)
1057 switch (SUBLANGID(langid
)) {
1058 case SUBLANG_GERMAN_SWISS
:
1068 case LANG_HUNGARIAN
:
1071 case LANG_INDONESIAN
:
1080 case LANG_PORTUGUESE
:
1081 if (SUBLANGID(langid
) == SUBLANG_PORTUGUESE_BRAZILIAN
)
1104 fprintf(stderr
, "msg_lang = %p (= \"%s\")\n", msg_lang
, msg_lang
?msg_lang
:"(null)");
1107 /* On Mandrake LANG defaults to C */
1108 if (strcmp(msg_lang
, "C") == 0) msg_lang
= "en";
1110 { /* If msg_lang has a country code, snip it out to give msg_lang2. */
1112 while (isalpha((unsigned char)msg_lang
[b
])) {
1115 if (msg_lang
[b
] == '_') {
1118 while (isalpha((unsigned char)msg_lang
[e
])) {
1121 tmp
= osstrdup(msg_lang
);
1122 memmove(tmp
+ b
, tmp
+ e
, strlen(tmp
+ e
) + 1);
1128 /* try to setlocale() appropriately too */
1129 if (!setlocale(LC_MESSAGES
, msg_lang
)) {
1131 (void)setlocale(LC_MESSAGES
, msg_lang2
);
1136 select_charset(default_charset());
1140 /* Return message if messages available, else a fallback value. */
1142 msg_opt(int en
, const char * fallback
)
1144 /* NB can't use SVX_ASSERT here! */
1145 if (!msg_array
|| en
<= 0 || en
>= num_msgs
) {
1149 return msg_array
[en
];
1156 /* NB can't use SVX_ASSERT here! */
1157 if (dontextract
&& en
>= 1000 && en
< 1000 + N_DONTEXTRACTMSGS
)
1158 return dontextract
[en
- 1000];
1161 fprintf(STDERR
, "Message %d requested before fully initialised\n", en
);
1162 return "Message requested before fully initialised\n";
1164 /* this should be the only other message which can be requested before
1165 * the message file is opened and read... */
1166 if (!dontextract
) return "Out of memory (couldn't find %lu bytes).";
1167 return dontextract
[(/*Out of memory (couldn't find %lu bytes).*/1004)
1171 if (en
< 0 || en
>= num_msgs
) {
1172 fprintf(STDERR
, "Message %d out of range\n", en
);
1173 return "Message out of range\n";
1177 const char *p
= msg_array
[0];
1182 return msg_array
[en
];
1186 v_report(int severity
, const char *fnm
, int line
, int col
, int en
, va_list ap
)
1190 aven_v_report(severity
, fnm
, line
, en
, ap
);
1195 if (line
) fprintf(STDERR
, ":%d", line
);
1196 if (col
> 0) fprintf(STDERR
, ":%d", col
);
1198 fputs(appname_copy
, STDERR
);
1200 fputs(": ", STDERR
);
1204 /* TRANSLATORS: Indicates a informational message e.g.:
1205 * "spoon.svx:12: info: Declination: [...]" */
1206 level
= msg_opt(/*info*/485, "info");
1209 /* TRANSLATORS: Indicates a warning message e.g.:
1210 * "spoon.svx:12: warning: *prefix is deprecated" */
1211 level
= msg_opt(/*warning*/4, "warning");
1214 /* TRANSLATORS: Indicates an error message e.g.:
1215 * "spoon.svx:13:4: error: Field may not be omitted" */
1216 level
= msg_opt(/*error*/93, "error");
1219 fputs(level
, STDERR
);
1220 fputs(": ", STDERR
);
1222 vfprintf(STDERR
, msg(en
), ap
);
1232 if (msg_errors
== 50)
1233 fatalerror_in_file(fnm
, 0, /*Too many errors - giving up*/19);
1241 diag(int severity
, int en
, ...)
1245 v_report(severity
, NULL
, 0, 0, en
, ap
);
1250 diag_in_file(int severity
, const char *fnm
, int line
, int en
, ...)
1254 v_report(severity
, fnm
, line
, 0, en
, ap
);
1258 /* Code to support switching character set at runtime (e.g. for a printer
1259 * driver to support different character sets on screen and on the printer)
1261 typedef struct charset_li
{
1262 struct charset_li
*next
;
1267 static charset_li
*charset_head
= NULL
;
1269 static int charset
= CHARSET_BAD
;
1272 select_charset(int charset_code
)
1274 int old_charset
= charset
;
1278 fprintf(stderr
, "select_charset(%d), old charset = %d\n", charset_code
,
1282 charset
= charset_code
;
1284 /* check if we've already parsed messages for new charset */
1285 for (p
= charset_head
; p
; p
= p
->next
) {
1287 printf("%p: code %d msg_array %p\n", p
, p
->code
, p
->msg_array
);
1289 if (p
->code
== charset
) {
1290 msg_array
= p
->msg_array
;
1295 /* nope, got to reparse message file */
1296 parse_msg_file(charset_code
);
1299 p
= osnew(charset_li
);
1301 p
->msg_array
= msg_array
;
1302 p
->next
= charset_head
;