source: git/src/message.c@ 7061908

RELEASE/1.2 debug-ci debug-ci-sanitisers faster-cavernlog log-select main stereo-2025 walls-data walls-data-hanging-as-warning warn-only-for-hanging-survey
Last change on this file since 7061908 was c9a6e18, checked in by Olly Betts <olly@…>, 7 years ago

Dereference symlinks when testing for file/directory

The code was carefully using lstat() where available to try to do
this, but that's backwards - regular stat() dereferences a symlink
and lstat() doesn't!

  • Property mode set to 100644
File size: 37.4 KB
Line 
1/* message.c
2 * Fairly general purpose message and error routines
3 * Copyright (C) 1993-2003,2004,2005,2006,2007,2010,2011,2012,2014,2015,2016,2017,2019 Olly Betts
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20/*#define DEBUG 1*/
21
22#ifdef HAVE_CONFIG_H
23# include <config.h>
24#endif
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <ctype.h>
30#include <limits.h>
31#include <errno.h>
32#include <locale.h>
33
34#include "cmdline.h"
35#include "whichos.h"
36#include "filename.h"
37#include "message.h"
38#include "osdepend.h"
39#include "filelist.h"
40#include "debug.h"
41#include "str.h"
42
43#ifdef AVEN
44# include "aven.h"
45#endif
46
47#if OS_WIN32
48# define WIN32_LEAN_AND_MEAN
49# include <windows.h>
50#elif OS_UNIX
51# include <sys/types.h>
52#endif
53
54#include <sys/stat.h>
55
56/* For funcs which want to be immune from messing around with different
57 * calling conventions */
58#ifndef CDECL
59# define CDECL
60#endif
61
62int msg_warnings = 0; /* keep track of how many warnings we've given */
63int msg_errors = 0; /* and how many (non-fatal) errors */
64
65/* in case osmalloc() fails before appname_copy is set up */
66static const char *appname_copy = "anonymous program";
67
68/* Path to use to look for executables (used by aven to find cavern). */
69static const char *exe_pth = "";
70
71/* error code for failed osmalloc and osrealloc calls */
72static void
73outofmem(OSSIZE_T size)
74{
75 /* TRANSLATORS: "%lu" is a placeholder for the number of bytes which Survex
76 * was trying to allocate space for. */
77 fatalerror(/*Out of memory (couldn’t find %lu bytes).*/1,
78 (unsigned long)size);
79}
80
81#ifdef TOMBSTONES
82#define TOMBSTONE_SIZE 16
83static const char tombstone[TOMBSTONE_SIZE] = "012345\xfftombstone";
84#endif
85
86/* malloc with error catching if it fails. Also allows us to write special
87 * versions easily eg for MS Windows.
88 */
89void *
90osmalloc(OSSIZE_T size)
91{
92 void *p;
93#ifdef TOMBSTONES
94 size += TOMBSTONE_SIZE * 2;
95 p = malloc(size);
96#else
97 p = xosmalloc(size);
98#endif
99 if (p == NULL) outofmem(size);
100#ifdef TOMBSTONES
101 printf("osmalloc truep=%p truesize=%d\n", p, size);
102 memcpy(p, tombstone, TOMBSTONE_SIZE);
103 memcpy(p + size - TOMBSTONE_SIZE, tombstone, TOMBSTONE_SIZE);
104 *(size_t *)p = size;
105 p += TOMBSTONE_SIZE;
106#endif
107 return p;
108}
109
110/* realloc with error catching if it fails. */
111void *
112osrealloc(void *p, OSSIZE_T size)
113{
114 /* some pre-ANSI realloc implementations don't cope with a NULL pointer */
115 if (p == NULL) {
116 p = xosmalloc(size);
117 } else {
118#ifdef TOMBSTONES
119 int true_size;
120 size += TOMBSTONE_SIZE * 2;
121 p -= TOMBSTONE_SIZE;
122 true_size = *(size_t *)p;
123 printf("osrealloc (in truep=%p truesize=%d)\n", p, true_size);
124 if (memcmp(p + sizeof(size_t), tombstone + sizeof(size_t),
125 TOMBSTONE_SIZE - sizeof(size_t)) != 0) {
126 printf("start tombstone for block %p, size %d corrupted!",
127 p + TOMBSTONE_SIZE, true_size - TOMBSTONE_SIZE * 2);
128 }
129 if (memcmp(p + true_size - TOMBSTONE_SIZE, tombstone,
130 TOMBSTONE_SIZE) != 0) {
131 printf("end tombstone for block %p, size %d corrupted!",
132 p + TOMBSTONE_SIZE, true_size - TOMBSTONE_SIZE * 2);
133 }
134 p = realloc(p, size);
135 if (p == NULL) outofmem(size);
136 printf("osrealloc truep=%p truesize=%d\n", p, size);
137 memcpy(p, tombstone, TOMBSTONE_SIZE);
138 memcpy(p + size - TOMBSTONE_SIZE, tombstone, TOMBSTONE_SIZE);
139 *(size_t *)p = size;
140 p += TOMBSTONE_SIZE;
141#else
142 p = xosrealloc(p, size);
143#endif
144 }
145 if (p == NULL) outofmem(size);
146 return p;
147}
148
149char *
150osstrdup(const char *str)
151{
152 char *p;
153 OSSIZE_T len;
154 len = strlen(str) + 1;
155 p = osmalloc(len);
156 memcpy(p, str, len);
157 return p;
158}
159
160/* osfree is usually just a macro in osalloc.h */
161#ifdef TOMBSTONES
162void
163osfree(void *p)
164{
165 int true_size;
166 if (!p) return;
167 p -= TOMBSTONE_SIZE;
168 true_size = *(size_t *)p;
169 printf("osfree truep=%p truesize=%d\n", p, true_size);
170 if (memcmp(p + sizeof(size_t), tombstone + sizeof(size_t),
171 TOMBSTONE_SIZE - sizeof(size_t)) != 0) {
172 printf("start tombstone for block %p, size %d corrupted!",
173 p + TOMBSTONE_SIZE, true_size - TOMBSTONE_SIZE * 2);
174 }
175 if (memcmp(p + true_size - TOMBSTONE_SIZE, tombstone,
176 TOMBSTONE_SIZE) != 0) {
177 printf("end tombstone for block %p, size %d corrupted!",
178 p + TOMBSTONE_SIZE, true_size - TOMBSTONE_SIZE * 2);
179 }
180 free(p);
181}
182#endif
183
184static int
185default_charset(void)
186{
187 if (getenv("SURVEX_UTF8")) return CHARSET_UTF8;
188#if OS_WIN32
189# ifdef AVEN
190# define CODEPAGE GetACP()
191# else
192# define CODEPAGE GetConsoleOutputCP()
193# endif
194 switch (CODEPAGE) {
195 case 0: return CHARSET_UTF8;
196 case 1252: return CHARSET_WINCP1252;
197 case 1250: return CHARSET_WINCP1250;
198 case 850: return CHARSET_DOSCP850;
199 }
200 return CHARSET_USASCII;
201#elif OS_UNIX
202#ifdef AVEN
203 return CHARSET_UTF8;
204#else
205 const char *p = getenv("LC_ALL");
206 if (p == NULL || p[0] == '\0') {
207 p = getenv("LC_CTYPE");
208 if (p == NULL || p[0] == '\0') {
209 p = getenv("LANG");
210 /* Something (AutoCAD?) on Microsoft Windows sets LANG to a number. */
211 if (p == NULL || !isalpha((unsigned char)p[0]))
212 p = msg_lang;
213 }
214 }
215
216 if (p) {
217 const char *q = strchr(p, '.');
218 if (q) p = q + 1;
219 }
220
221 if (p) {
222 const char *chset = p;
223 size_t name_len;
224
225 while (*p != '\0' && *p != '@') p++;
226
227 name_len = p - chset;
228
229 if (name_len) {
230 int only_digit = 1;
231 size_t cnt;
232
233 for (cnt = 0; cnt < name_len; ++cnt)
234 if (isalpha((unsigned char)chset[cnt])) {
235 only_digit = 0;
236 break;
237 }
238
239 if (only_digit) goto iso;
240
241 switch (tolower(chset[0])) {
242 case 'i':
243 if (tolower(chset[1]) == 's' && tolower(chset[2]) == 'o') {
244 chset += 3;
245 iso:
246 if (strncmp(chset, "8859", 4) == 0) {
247 chset += 4;
248 while (chset < p && *chset && !isdigit((unsigned char)*chset))
249 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;
255 }
256 }
257 }
258 break;
259 case 'u':
260 if (tolower(chset[1]) == 't' && tolower(chset[2]) == 'f') {
261 chset += 3;
262 while (chset < p && *chset && !isdigit((unsigned char)*chset))
263 chset++;
264 switch (atoi(chset)) {
265 case 8: return CHARSET_UTF8;
266 default: return CHARSET_USASCII;
267 }
268 }
269 }
270 }
271 }
272 return CHARSET_USASCII;
273#endif
274#else
275# error Do not know operating system!
276#endif
277}
278
279/* It seems that Swedish and maybe some other scandanavian languages don't
280 * transliterate &auml; to ae - but it seems there may be conflicting views
281 * on this...
282 */
283#define umlaut_to_e() 1
284
285/* values <= 127 already dealt with */
286static int
287add_unicode(int charset, unsigned char *p, int value)
288{
289#ifdef DEBUG
290 fprintf(stderr, "add_unicode(%d, %p, %d)\n", charset, p, value);
291#endif
292 if (value == 0) return 0;
293 switch (charset) {
294 case CHARSET_USASCII:
295 if (value < 0x80) {
296 *p = value;
297 return 1;
298 }
299 break;
300 case CHARSET_ISO_8859_1:
301 if (value < 0x100) {
302 *p = value;
303 return 1;
304 }
305 break;
306 case CHARSET_ISO_8859_2:
307 if (value >= 0xa0) {
308 int v = 0;
309 switch (value) {
310 case 0xa0: case 0xa4: case 0xa7: case 0xa8: case 0xad: case 0xb0:
311 case 0xb4: case 0xb8: case 0xc1: case 0xc2: case 0xc4: case 0xc7:
312 case 0xc9: case 0xcb: case 0xcd: case 0xce: case 0xd3: case 0xd4:
313 case 0xd6: case 0xd7: case 0xda: case 0xdc: case 0xdd: case 0xdf:
314 case 0xe1: case 0xe2: case 0xe4: case 0xe7: case 0xe9: case 0xeb:
315 case 0xed: case 0xee: case 0xf3: case 0xf4: case 0xf6: case 0xf7:
316 case 0xfa: case 0xfc: case 0xfd:
317 v = value; break;
318 case 0x104: v = '\xa1'; break;
319 case 0x2d8: v = '\xa2'; break;
320 case 0x141: v = '\xa3'; break;
321 case 0x13d: v = '\xa5'; break;
322 case 0x15a: v = '\xa6'; break;
323 case 0x160: v = '\xa9'; break;
324 case 0x15e: v = '\xaa'; break; /* Scedil */
325 case 0x164: v = '\xab'; break;
326 case 0x179: v = '\xac'; break;
327 case 0x17d: v = '\xae'; break;
328 case 0x17b: v = '\xaf'; break;
329 case 0x105: v = '\xb1'; break;
330 case 0x2db: v = '\xb2'; break;
331 case 0x142: v = '\xb3'; break;
332 case 0x13e: v = '\xb5'; break;
333 case 0x15b: v = '\xb6'; break;
334 case 0x2c7: v = '\xb7'; break;
335 case 0x161: v = '\xb9'; break;
336 case 0x15f: v = '\xba'; break; /* scedil */
337 case 0x165: v = '\xbb'; break;
338 case 0x17a: v = '\xbc'; break;
339 case 0x2dd: v = '\xbd'; break;
340 case 0x17e: v = '\xbe'; break;
341 case 0x17c: v = '\xbf'; break;
342 case 0x154: v = '\xc0'; break;
343 case 0x102: v = '\xc3'; break;
344 case 0x139: v = '\xc5'; break;
345 case 0x106: v = '\xc6'; break;
346 case 0x10c: v = '\xc8'; break;
347 case 0x118: v = '\xca'; break;
348 case 0x11a: v = '\xcc'; break;
349 case 0x10e: v = '\xcf'; break;
350 case 0x110: v = '\xd0'; break;
351 case 0x143: v = '\xd1'; break;
352 case 0x147: v = '\xd2'; break;
353 case 0x150: v = '\xd5'; break;
354 case 0x158: v = '\xd8'; break;
355 case 0x16e: v = '\xd9'; break;
356 case 0x170: v = '\xdb'; break;
357 case 0x162: v = '\xde'; break; /* &Tcedil; */
358 case 0x155: v = '\xe0'; break;
359 case 0x103: v = '\xe3'; break;
360 case 0x13a: v = '\xe5'; break;
361 case 0x107: v = '\xe6'; break;
362 case 0x10d: v = '\xe8'; break;
363 case 0x119: v = '\xea'; break;
364 case 0x11b: v = '\xec'; break;
365 case 0x10f: v = '\xef'; break;
366 case 0x111: v = '\xf0'; break;
367 case 0x144: v = '\xf1'; break;
368 case 0x148: v = '\xf2'; break;
369 case 0x151: v = '\xf5'; break;
370 case 0x159: v = '\xf8'; break;
371 case 0x16f: v = '\xf9'; break;
372 case 0x171: v = '\xfb'; break;
373 case 0x163: v = '\xfe'; break; /* tcedil */
374 case 0x2d9: v = '\xff'; break;
375 }
376 if (v == 0) break;
377 value = v;
378 }
379 *p = value;
380 return 1;
381 case CHARSET_ISO_8859_15:
382 switch (value) {
383 case 0xa4: case 0xa6: case 0xb0: case 0xc4:
384 case 0xd0: case 0xd4: case 0xd5: case 0xd6:
385 goto donthave;
386 case 0x152: value = 0xd4; break; /* &OElig; */
387 case 0x153: value = 0xd5; break; /* &oelig; */
388#if 0
389 case 0x0: value = 0xa4; break; /* euro */
390#endif
391 case 0x160: value = 0xa6; break; /* Scaron */
392 case 0x161: value = 0xb0; break; /* scaron */
393 case 0x17d: value = 0xc4; break; /* Zcaron */
394 case 0x17e: value = 0xd0; break; /* zcaron */
395#if 0
396 case 0x0: value = 0xd6; break; /* Ydiersis */
397#endif
398 }
399 if (value < 0x100) {
400 *p = value;
401 return 1;
402 }
403 donthave:
404 break;
405#if OS_WIN32
406 case CHARSET_WINCP1250:
407 /* MS Windows rough equivalent to ISO-8859-2 */
408 if (value >= 0x80) {
409 int v = 0;
410 /* This mapping is complete - there are 5 unused positions:
411 * 0x81 0x83 0x88 0x90 0x98 */
412 switch (value) {
413 case 0xa0: case 0xa4: case 0xa6: case 0xa7: case 0xa8: case 0xa9:
414 case 0xab: case 0xac: case 0xad: case 0xae: case 0xb0: case 0xb1:
415 case 0xb4: case 0xb5: case 0xb6: case 0xb7: case 0xb8: case 0xbb:
416 case 0xc1: case 0xc2: case 0xc4: case 0xc7: case 0xc9: case 0xcb:
417 case 0xcd: case 0xce: case 0xd3: case 0xd4: case 0xd6: case 0xd7:
418 case 0xda: case 0xdc: case 0xdd: case 0xdf: case 0xe1: case 0xe2:
419 case 0xe4: case 0xe7: case 0xe9: case 0xeb: case 0xed: case 0xee:
420 case 0xf3: case 0xf4: case 0xf6: case 0xf7: case 0xfa: case 0xfc:
421 case 0xfd:
422 v = value; break;
423 case 0x20ac: v = '\x80'; break;
424 case 0x201a: v = '\x82'; break;
425 case 0x201e: v = '\x84'; break;
426 case 0x2026: v = '\x85'; break;
427 case 0x2020: v = '\x86'; break;
428 case 0x2021: v = '\x87'; break;
429 case 0x2030: v = '\x89'; break;
430 case 0x0160: v = '\x8a'; break;
431 case 0x2039: v = '\x8b'; break;
432 case 0x015a: v = '\x8c'; break;
433 case 0x0164: v = '\x8d'; break;
434 case 0x017d: v = '\x8e'; break;
435 case 0x0179: v = '\x8f'; break;
436 case 0x2018: v = '\x91'; break;
437 case 0x2019: v = '\x92'; break;
438 case 0x201c: v = '\x93'; break;
439 case 0x201d: v = '\x94'; break;
440 case 0x2022: v = '\x95'; break;
441 case 0x2013: v = '\x96'; break;
442 case 0x2014: v = '\x97'; break;
443 case 0x2122: v = '\x99'; break;
444 case 0x0161: v = '\x9a'; break;
445 case 0x203a: v = '\x9b'; break;
446 case 0x015b: v = '\x9c'; break;
447 case 0x0165: v = '\x9d'; break;
448 case 0x017e: v = '\x9e'; break;
449 case 0x017a: v = '\x9f'; break;
450 case 0x02c7: v = '\xa1'; break;
451 case 0x02d8: v = '\xa2'; break;
452 case 0x0141: v = '\xa3'; break;
453 case 0x0104: v = '\xa5'; break;
454 case 0x015e: v = '\xaa'; break; /* Scedil */
455 case 0x017b: v = '\xaf'; break;
456 case 0x02db: v = '\xb2'; break;
457 case 0x0142: v = '\xb3'; break;
458 case 0x0105: v = '\xb9'; break;
459 case 0x015f: v = '\xba'; break; /* scedil */
460 case 0x013d: v = '\xbc'; break;
461 case 0x02dd: v = '\xbd'; break;
462 case 0x013e: v = '\xbe'; break;
463 case 0x017c: v = '\xbf'; break;
464 case 0x0154: v = '\xc0'; break;
465 case 0x0102: v = '\xc3'; break;
466 case 0x0139: v = '\xc5'; break;
467 case 0x0106: v = '\xc6'; break;
468 case 0x010c: v = '\xc8'; break;
469 case 0x0118: v = '\xca'; break;
470 case 0x011a: v = '\xcc'; break;
471 case 0x010e: v = '\xcf'; break;
472 case 0x0110: v = '\xd0'; break;
473 case 0x0143: v = '\xd1'; break;
474 case 0x0147: v = '\xd2'; break;
475 case 0x0150: v = '\xd5'; break;
476 case 0x0158: v = '\xd8'; break;
477 case 0x016e: v = '\xd9'; break;
478 case 0x0170: v = '\xdb'; break;
479 case 0x0162: v = '\xde'; break; /* &Tcedil; */
480 case 0x0155: v = '\xe0'; break;
481 case 0x0103: v = '\xe3'; break;
482 case 0x013a: v = '\xe5'; break;
483 case 0x0107: v = '\xe6'; break;
484 case 0x010d: v = '\xe8'; break;
485 case 0x0119: v = '\xea'; break;
486 case 0x011b: v = '\xec'; break;
487 case 0x010f: v = '\xef'; break;
488 case 0x0111: v = '\xf0'; break;
489 case 0x0144: v = '\xf1'; break;
490 case 0x0148: v = '\xf2'; break;
491 case 0x0151: v = '\xf5'; break;
492 case 0x0159: v = '\xf8'; break;
493 case 0x016f: v = '\xf9'; break;
494 case 0x0171: v = '\xfb'; break;
495 case 0x0163: v = '\xfe'; break; /* tcedil */
496 case 0x02d9: v = '\xff'; break;
497 }
498 if (v == 0) break;
499 value = v;
500 }
501 *p = value;
502 return 1;
503 case CHARSET_WINCP1252:
504 /* MS Windows extensions to ISO-8859-1 */
505 /* This mapping is complete - there are 5 unused positions:
506 * 0x81 0x8d 0x8f 0x90 0x9d */
507 switch (value) {
508 case 0x2026: value = 0x85; break; /* hellip */
509 case 0x0160: value = 0x8a; break; /* Scaron */
510 case 0x0152: value = 0x8c; break; /* OElig */
511 case 0x017d: value = 0x8e; break; /* Zcaron */
512 case 0x2019: value = 0x92; break; /* lsquo */
513 case 0x201c: value = 0x93; break; /* ldquo */
514 case 0x201d: value = 0x94; break; /* rdquo */
515 case 0x0161: value = 0x9a; break; /* scaron */
516 case 0x0153: value = 0x9c; break; /* oelig */
517 case 0x017e: value = 0x9e; break; /* zcaron */
518#if 0
519 /* there are a few other obscure ones we don't currently need */
520 case 0x20ac: value = 0x80; break;
521 case 0x201a: value = 0x82; break;
522 case 0x0192: value = 0x83; break;
523 case 0x201e: value = 0x84; break;
524 case 0x2020: value = 0x86; break;
525 case 0x2021: value = 0x87; break;
526 case 0x02c6: value = 0x88; break;
527 case 0x2030: value = 0x89; break;
528 case 0x2039: value = 0x8b; break;
529 case 0x2018: value = 0x91; break;
530 case 0x2022: value = 0x95; break;
531 case 0x2013: value = 0x96; break;
532 case 0x2014: value = 0x97; break;
533 case 0x02dc: value = 0x98; break;
534 case 0x2122: value = 0x99; break;
535 case 0x203a: value = 0x9b; break;
536 case 0x0178: value = 0x9f; break;
537#endif
538 }
539 if (value < 0x100) {
540 *p = value;
541 return 1;
542 }
543 break;
544#endif
545#if OS_WIN32
546 case CHARSET_DOSCP850: {
547 static const unsigned char uni2dostab[] = {
548 255, 173, 189, 156, 207, 190, 221, 245,
549 249, 184, 166, 174, 170, 240, 169, 238,
550 248, 241, 253, 252, 239, 230, 244, 250,
551 247, 251, 167, 175, 172, 171, 243, 168,
552 183, 181, 182, 199, 142, 143, 146, 128,
553 212, 144, 210, 211, 222, 214, 215, 216,
554 209, 165, 227, 224, 226, 229, 153, 158,
555 157, 235, 233, 234, 154, 237, 232, 225,
556 133, 160, 131, 198, 132, 134, 145, 135,
557 138, 130, 136, 137, 141, 161, 140, 139,
558 208, 164, 149, 162, 147, 228, 148, 246,
559 155, 151, 163, 150, 129, 236, 231, 152
560 };
561 if (value >= 160 && value < 256) {
562 *p = (int)uni2dostab[value - 160];
563 return 1;
564 }
565#if 0
566 if (value == 305) { /* LATIN SMALL LETTER DOTLESS I */
567 *p = 213; /* "Modified CP850" has the Euro sign here. */
568 return 1;
569 }
570 if (value == 402) { /* LATIN SMALL LETTER F WITH HOOK */
571 *p = 159;
572 return 1;
573 }
574#endif
575 break;
576 }
577#endif
578 }
579 /* Transliterate characters we can't represent */
580#ifdef DEBUG
581 fprintf(stderr, "transliterate “%c” 0x%x\n", value, value);
582#endif
583 switch (value) {
584 case 160:
585 *p = ' '; return 1;
586 case 161 /* ¡ */:
587 *p = '!'; return 1;
588 case 176 /* ° */:
589 *p = 'd'; p[1] = 'g'; return 2;
590 case 191 /* ¿ */:
591 *p = '?'; return 1;
592 case 192 /* À */: case 193 /* Á */: case 194 /* Â */: case 195 /* Ã */:
593 *p = 'A'; return 1;
594 case 197 /* Å */:
595 p[1] = *p = 'A'; return 2;
596 case 196 /* Ä */: /* &Auml; */
597 *p = 'A';
598 if (!umlaut_to_e()) return 1;
599 p[1] = 'E'; return 2;
600 case 198 /* Æ */:
601 *p = 'A'; p[1] = 'E'; return 2;
602 case 199 /* Ç */: case 268: /* &Ccaron; */
603 *p = 'C'; return 1;
604 case 270: /* &Dcaron; */
605 *p = 'D'; return 1;
606 case 200 /* È */: case 201 /* É */: case 202 /* Ê */: case 203 /* Ë */:
607 *p = 'E'; return 1;
608 case 204 /* Ì */: case 205 /* Í */: case 206 /* Î */: case 207 /* Ï */:
609 *p = 'I'; return 1;
610 case 208 /* Ð */: case 222 /* Þ */:
611 *p = 'T'; p[1] = 'H'; return 2;
612 case 315: /* &Lacute; */
613 case 317: /* &Lcaron; */
614 *p = 'L'; return 1;
615 case 209 /* Ñ */:
616 *p = 'N'; return 1;
617 case 210 /* Ò */: case 211 /* Ó */: case 212 /* Ô */: case 213 /* Õ */:
618 *p = 'O'; return 1;
619 case 214 /* Ö */: /* &Ouml; */ case 0x152: /* &OElig; */
620 *p = 'O'; p[1] = 'E'; return 2;
621 case 352: /* &Scaron; */
622 case 0x15e: /* &Scedil; */
623 *p = 'S'; return 1;
624 case 0x162: /* &Tcedil; */
625 case 0x164: /* &Tcaron; */
626 *p = 'T'; return 1;
627 case 217 /* Ù */: case 218 /* Ú */: case 219 /* Û */:
628 *p = 'U'; return 1;
629 case 220 /* Ü */: /* &Uuml; */
630 *p = 'U'; p[1] = 'E'; return 2;
631 case 221 /* Ý */:
632 *p = 'Y'; return 1;
633 case 381: /* &Zcaron; */
634 *p = 'Z'; return 1;
635 case 223 /* ß */:
636 p[1] = *p = 's'; return 2;
637 case 224 /* à */: case 225 /* á */: case 226 /* â */: case 227 /* ã */:
638 case 259: /* &abreve; */
639 *p = 'a'; return 1;
640 case 228 /* ä */: /* &auml; */ case 230 /* æ */:
641 *p = 'a'; p[1] = 'e'; return 2;
642 case 229 /* å */:
643 p[1] = *p = 'a'; return 2;
644 case 231 /* ç */: case 269 /* &ccaron; */:
645 *p = 'c'; return 1;
646 case 271: /* &dcaron; */
647 *p = 'd'; return 1;
648 case 232 /* è */: case 233 /* é */: case 234 /* ê */: case 235 /* ë */:
649 case 283 /* &ecaron; */:
650 *p = 'e'; return 1;
651 case 236 /* ì */: case 237 /* í */: case 238 /* î */: case 239 /* ï */:
652 *p = 'i'; return 1;
653 case 316 /* &lacute; */:
654 case 318 /* &lcaron; */:
655 *p = 'l'; return 1;
656 case 241 /* ñ */: case 328 /* &ncaron; */:
657 *p = 'n'; return 1;
658 case 345: /* &rcaron; */
659 *p = 'r'; return 1;
660 case 353: /* &scaron; */
661 case 0x15f: /* &scedil; */
662 *p = 's'; return 1;
663 case 357: /* &tcaron; */
664 case 0x163: /* &tcedil; */
665 *p = 't'; return 1;
666 case 240 /* ð */: case 254 /* þ */:
667 *p = 't'; p[1] = 'h'; return 2;
668 case 242 /* ò */: case 243 /* ó */: case 244 /* ô */: case 245 /* õ */:
669 *p = 'o'; return 1;
670 case 246 /* ö */: /* &ouml; */ case 0x153: /* &oelig; */
671 *p = 'o'; p[1] = 'e'; return 2;
672 case 249 /* ù */: case 250 /* ú */: case 251 /* û */:
673 case 367 /* &uring; */:
674 *p = 'u'; return 1;
675 case 252 /* ü */: /* &uuml; */
676 *p = 'u'; p[1] = 'e'; return 2;
677 case 253 /* ý */: case 255 /* ÿ */:
678 *p = 'y'; return 1;
679 case 382: /* &zcaron; */
680 *p = 'z'; return 1;
681 case 0x2019: /* &lsquo; */
682 *p = '\''; return 1;
683 case 171: /* « */ case 187: /* » */
684 case 0x201c: /* &ldquo; */ case 0x201d: /* &rdquo; */
685 *p = '"'; return 1;
686 case 0x2026: /* &hellip; */
687 *p = '.'; p[1] = '.'; p[2] = '.'; return 3;
688 case 0x2192: /* &rarr; */
689 *p = '-'; p[1] = '>'; return 2;
690 case 0x1d4d: /* gradient symbol */
691 *p = 'g'; p[1] = 'r'; p[2] = 'd'; return 3;
692 case 0x221e: /* infinity symbol */
693 *p = 'i'; p[1] = 'n'; p[2] = 'f'; return 3;
694 }
695#ifdef DEBUG
696 /* 169 is reported (copyright symbol), but there isn't a good <= 2 ASCII
697 * character transliteration for that, so we handle that elsewhere. */
698 fprintf(stderr, "failed to transliterate codepoint %d\n", value);
699#endif
700 return 0;
701}
702
703#if OS_UNIX && defined DATADIR && defined PACKAGE
704/* Under Unix, we compile in the configured path */
705static const char *pth_cfg_files = DATADIR "/" PACKAGE;
706#else
707/* On other platforms, we fall back on looking in the current directory */
708static const char *pth_cfg_files = "";
709#endif
710
711static int num_msgs = 0;
712static char **msg_array = NULL;
713
714static bool msg_lang_explicit = fFalse;
715const char *msg_lang = NULL;
716const char *msg_lang2 = NULL;
717
718static char **
719parse_msgs(int n, unsigned char *p, int charset_code) {
720 int i;
721
722 char **msgs = osmalloc(n * sizeof(char *));
723
724 for (i = 0; i < n; i++) {
725 unsigned char *to = p;
726 int ch;
727 msgs[i] = (char *)p;
728
729 /* If we want UTF8 anyway, we just need to find the start of each
730 * message */
731 if (charset_code == CHARSET_UTF8) {
732 p += strlen((char *)p) + 1;
733 continue;
734 }
735
736 while ((ch = *p++) != 0) {
737 /* A byte in the range 0x80-0xbf or 0xf0-0xff isn't valid in
738 * this state, (0xf0-0xfd mean values > 0xffff) so treat as
739 * literal and try to resync so we cope better when fed
740 * non-utf-8 data. Similarly we abandon a multibyte sequence
741 * if we hit an invalid character. */
742 if (ch >= 0xc0 && ch < 0xf0) {
743 int ch1 = *p;
744 if ((ch1 & 0xc0) != 0x80) goto resync;
745
746 if (ch < 0xe0) {
747 /* 2 byte sequence */
748 ch = ((ch & 0x1f) << 6) | (ch1 & 0x3f);
749 p++;
750 } else {
751 /* 3 byte sequence */
752 int ch2 = p[1];
753 if ((ch2 & 0xc0) != 0x80) goto resync;
754 ch = ((ch & 0x1f) << 12) | ((ch1 & 0x3f) << 6) | (ch2 & 0x3f);
755 p += 2;
756 }
757 }
758
759 resync:
760
761 if (ch < 127) {
762 *to++ = (char)ch;
763 } else {
764 /* We assume an N byte UTF-8 code never transliterates to more
765 * than N characters (so we can't transliterate © to (C) or
766 * ® to (R) for example) */
767 to += add_unicode(charset_code, to, ch);
768 }
769 }
770 *to++ = '\0';
771 }
772 return msgs;
773}
774
775/* This is the name of the default language, which can be set like so:
776 * ./configure --enable-defaultlang=fr
777 */
778#ifdef DEFAULTLANG
779/* No point extracting these errors as they won't get used if file opens */
780# include "../lib/defaultlang.h"
781#else
782#define N_DONTEXTRACTMSGS 5
783static unsigned char dontextractmsgs[] =
784 "Can't open message file \"%s\" using path \"%s\"\0"/*1000*/
785 "Problem with message file \"%s\"\0"/*1001*/
786 "I don't understand this message file version\0"/*1002*/
787 "Message file truncated?\0"/*1003*/
788 "Out of memory (couldn't find %lu bytes).\0"/*1004*/;
789#endif
790
791static char **dontextract = NULL;
792
793static void
794parse_msg_file(int charset_code)
795{
796 FILE *fh;
797 unsigned char header[20];
798 int i;
799 unsigned len;
800 unsigned char *p;
801 char *fnm, *s;
802 int n;
803
804#ifdef DEBUG
805 fprintf(stderr, "parse_msg_file(%d)\n", charset_code);
806#endif
807
808 /* sort out messages we need to print if we can't open the message file */
809 dontextract = parse_msgs(N_DONTEXTRACTMSGS, dontextractmsgs, charset_code);
810
811 fnm = osstrdup(msg_lang);
812 /* trim off charset from stuff like "de_DE.iso8859_1" */
813 s = strchr(fnm, '.');
814 if (s) *s = '\0';
815 /* trim off any "@<something>" modifier. */
816 s = strchr(fnm, '@');
817 if (s) *s = '\0';
818
819 fh = fopenWithPthAndExt(pth_cfg_files, fnm, EXT_SVX_MSG, "rb", NULL);
820
821 if (!fh && strlen(fnm) > 3 && fnm[2] == '_') {
822 /* e.g. if 'en_GB' is unknown, see if we know 'en' */
823 fnm[2] = '\0';
824 fh = fopenWithPthAndExt(pth_cfg_files, fnm, EXT_SVX_MSG, "rb", NULL);
825 if (!fh) fnm[2] = '_'; /* for error reporting */
826 }
827
828 if (!fh && !msg_lang_explicit) {
829 /* If msg_lang wasn't specified using environment variable SURVEXLANG,
830 * then default to 'en' if we don't find messages for language msg_lang.
831 */
832 if (fnm[0] && fnm[1]) {
833 strcpy(fnm, "en");
834 } else {
835 osfree(fnm);
836 fnm = osstrdup("en");
837 }
838 fh = fopenWithPthAndExt(pth_cfg_files, fnm, EXT_SVX_MSG, "rb", NULL);
839 }
840
841 if (!fh) {
842 fatalerror(/*Can't open message file “%s” using path “%s”*/1000,
843 fnm, pth_cfg_files);
844 }
845
846 if (fread(header, 1, 20, fh) < 20 ||
847 memcmp(header, "Svx\nMsg\r\n\xfe\xff", 12) != 0) {
848 fatalerror(/*Problem with message file “%s”*/1001, fnm);
849 }
850
851 if (header[12] != 0)
852 fatalerror(/*I don't understand this message file version*/1002);
853
854 n = (header[14] << 8) | header[15];
855
856 len = 0;
857 for (i = 16; i < 20; i++) len = (len << 8) | header[i];
858
859 p = osmalloc(len);
860 if (fread(p, 1, len, fh) < len)
861 fatalerror(/*Message file truncated?*/1003);
862
863 fclose(fh);
864
865#ifdef DEBUG
866 fprintf(stderr, "fnm = “%s”, n = %d, len = %d\n", fnm, n, len);
867#endif
868 osfree(fnm);
869
870 msg_array = parse_msgs(n, p, charset_code);
871 num_msgs = n;
872}
873
874const char *
875msg_cfgpth(void)
876{
877 return pth_cfg_files;
878}
879
880const char *
881msg_exepth(void)
882{
883 return exe_pth;
884}
885
886const char *
887msg_appname(void)
888{
889 return appname_copy;
890}
891
892void
893msg_init_(char * const *argv)
894{
895 char *p;
896#if OS_UNIX_MACOSX
897 int msg_macosx_relocatable = 0;
898#endif
899 SVX_ASSERT(argv);
900
901 /* Point to argv[0] itself so we report a more helpful error if the
902 * code to work out the clean appname generates a signal */
903 appname_copy = argv[0];
904#if OS_UNIX
905 /* use name as-is on Unix - programs run from path get name as supplied */
906 appname_copy = osstrdup(argv[0]);
907#else
908 /* use the lower-cased leafname on other platforms */
909 p = leaf_from_fnm(argv[0]);
910 appname_copy = p;
911 while (*p) {
912 *p = tolower(*p);
913 ++p;
914 }
915#endif
916
917 /* shortcut --version so you can check the version number even when the
918 * correct message file can't be found... */
919 if (argv[1] && strcmp(argv[1], "--version") == 0) {
920 cmdline_version();
921 exit(0);
922 }
923 if (argv[0]) {
924 exe_pth = path_from_fnm(argv[0]);
925#if OS_UNIX && defined DATADIR && defined PACKAGE
926 bool free_pth = fFalse;
927 char *pth = getenv("srcdir");
928 if (!pth || !pth[0]) {
929 pth = path_from_fnm(argv[0]);
930 free_pth = fTrue;
931 }
932 if (pth[0]) {
933 struct stat buf;
934#if OS_UNIX_MACOSX
935# ifndef AVEN
936 /* On MacOS X the programs may be installed anywhere, with the
937 * share directory and the binaries in the same directory. */
938 p = use_path(pth, "share/survex/en.msg");
939 if (stat(p, &buf) == 0 && S_ISREG(buf.st_mode)) {
940 pth_cfg_files = use_path(pth, "share/survex");
941 msg_macosx_relocatable = 1;
942 goto macosx_got_msg;
943 }
944 osfree(p);
945# endif
946 /* In the diskimage package, this case is used for aven, and for
947 * the hardlinked copies of cavern and extend alongside the aven
948 * binary, which are the ones which aven runs.
949 */
950 p = use_path(pth, "../Resources/en.msg");
951 if (stat(p, &buf) == 0 && S_ISREG(buf.st_mode)) {
952 pth_cfg_files = use_path(pth, "../Resources");
953 msg_macosx_relocatable = 1;
954 goto macosx_got_msg;
955 }
956 osfree(p);
957#endif
958 /* If we're run with an explicit path, check if "../lib/en.msg"
959 * from the program's path exists, and if so look there for
960 * support files - this allows us to test binaries in the build
961 * tree easily. */
962 p = use_path(pth, "../lib/en.msg");
963 if (stat(p, &buf) == 0) {
964#ifdef S_ISREG
965 /* POSIX way */
966 if (S_ISREG(buf.st_mode)) {
967 pth_cfg_files = use_path(pth, "../lib");
968 }
969#else
970 /* BSD way */
971 if ((buf.st_mode & S_IFMT) == S_IFREG) {
972 pth_cfg_files = use_path(pth, "../lib");
973 }
974#endif
975 }
976#if defined(__GNUC__) && defined(__APPLE_CC__)
977macosx_got_msg:
978#endif
979 osfree(p);
980 }
981
982 if (free_pth) osfree(pth);
983#elif OS_WIN32
984 DWORD len = 256;
985 char *buf = NULL, *modname;
986 while (1) {
987 DWORD got;
988 buf = osrealloc(buf, len);
989 got = GetModuleFileName(NULL, buf, len);
990 if (got < len) break;
991 len += len;
992 }
993 modname = buf;
994 /* Strange Win32 nastiness - strip prefix "\\?\" if present */
995 if (strncmp(modname, "\\\\?\\", 4) == 0) modname += 4;
996 pth_cfg_files = path_from_fnm(modname);
997 osfree(buf);
998#else
999 /* Get the path to the support files from argv[0] */
1000 pth_cfg_files = path_from_fnm(argv[0]);
1001#endif
1002 }
1003
1004 msg_lang = getenv("SURVEXLANG");
1005#ifdef DEBUG
1006 fprintf(stderr, "msg_lang = %p (= \"%s\")\n", msg_lang, msg_lang?msg_lang:"(null)");
1007#endif
1008
1009 msg_lang_explicit = fTrue;
1010 if (!msg_lang || !*msg_lang) {
1011 msg_lang_explicit = fFalse;
1012 msg_lang = getenv("LC_ALL");
1013 }
1014 if (!msg_lang || !*msg_lang) {
1015 msg_lang = getenv("LC_MESSAGES");
1016 if (!msg_lang || !*msg_lang) {
1017 msg_lang = getenv("LANG");
1018 /* Something (AutoCAD?) on Microsoft Windows sets LANG to a number. */
1019 if (msg_lang && !isalpha(msg_lang[0])) msg_lang = NULL;
1020 }
1021 if (!msg_lang || !*msg_lang) {
1022#if OS_WIN32
1023 LCID locid;
1024#endif
1025#ifdef DEFAULTLANG
1026 msg_lang = STRING(DEFAULTLANG);
1027#else
1028 msg_lang = "en";
1029#endif
1030#if OS_WIN32
1031 /* GetUserDefaultUILanguage() requires Microsoft Windows 2000 or
1032 * newer. For older versions, we use GetUserDefaultLCID().
1033 */
1034 {
1035 HMODULE win32 = GetModuleHandle(TEXT("kernel32.dll"));
1036 FARPROC f = GetProcAddress(win32, "GetUserDefaultUILanguage");
1037 if (f) {
1038 typedef LANGID (WINAPI *func_GetUserDefaultUILanguage)(void);
1039 func_GetUserDefaultUILanguage g;
1040 g = (func_GetUserDefaultUILanguage)f;
1041 locid = g();
1042 } else {
1043 locid = GetUserDefaultLCID();
1044 }
1045 }
1046 if (locid) {
1047 WORD langid = LANGIDFROMLCID(locid);
1048 switch (PRIMARYLANGID(langid)) {
1049 case LANG_BULGARIAN:
1050 msg_lang = "bg";
1051 break;
1052/* older mingw compilers don't seem to supply this value */
1053#ifndef LANG_CATALAN
1054# define LANG_CATALAN 0x03
1055#endif
1056 case LANG_CATALAN:
1057 msg_lang = "ca";
1058 break;
1059 case LANG_CHINESE:
1060 msg_lang = "zh_CN";
1061 break;
1062 case LANG_ENGLISH:
1063 if (SUBLANGID(langid) == SUBLANG_ENGLISH_US)
1064 msg_lang = "en_US";
1065 else
1066 msg_lang = "en";
1067 break;
1068 case LANG_FRENCH:
1069 msg_lang = "fr";
1070 break;
1071 case LANG_GERMAN:
1072 switch (SUBLANGID(langid)) {
1073 case SUBLANG_GERMAN_SWISS:
1074 msg_lang = "de_CH";
1075 break;
1076 default:
1077 msg_lang = "de";
1078 }
1079 break;
1080 case LANG_GREEK:
1081 msg_lang = "el";
1082 break;
1083 case LANG_HUNGARIAN:
1084 msg_lang = "hu";
1085 break;
1086 case LANG_INDONESIAN:
1087 msg_lang = "id";
1088 break;
1089 case LANG_ITALIAN:
1090 msg_lang = "it";
1091 break;
1092 case LANG_POLISH:
1093 msg_lang = "pl";
1094 break;
1095 case LANG_PORTUGUESE:
1096 if (SUBLANGID(langid) == SUBLANG_PORTUGUESE_BRAZILIAN)
1097 msg_lang = "pt_BR";
1098 else
1099 msg_lang = "pt";
1100 break;
1101 case LANG_ROMANIAN:
1102 msg_lang = "ro";
1103 break;
1104 case LANG_RUSSIAN:
1105 msg_lang = "ru";
1106 break;
1107 case LANG_SLOVAK:
1108 msg_lang = "sk";
1109 break;
1110 case LANG_SPANISH:
1111 msg_lang = "es";
1112 break;
1113 }
1114 }
1115#endif
1116 }
1117 }
1118#ifdef DEBUG
1119 fprintf(stderr, "msg_lang = %p (= \"%s\")\n", msg_lang, msg_lang?msg_lang:"(null)");
1120#endif
1121
1122 /* On Mandrake LANG defaults to C */
1123 if (strcmp(msg_lang, "C") == 0) msg_lang = "en";
1124
1125 { /* If msg_lang has a country code, snip it out to give msg_lang2. */
1126 size_t b = 0;
1127 while (isalpha((unsigned char)msg_lang[b])) {
1128 ++b;
1129 }
1130 if (msg_lang[b] == '_') {
1131 char * tmp;
1132 size_t e = b + 1;
1133 while (isalpha((unsigned char)msg_lang[e])) {
1134 ++e;
1135 }
1136 tmp = osstrdup(msg_lang);
1137 memmove(tmp + b, tmp + e, strlen(tmp + e) + 1);
1138 msg_lang2 = tmp;
1139 }
1140 }
1141
1142#ifdef LC_MESSAGES
1143 /* try to setlocale() appropriately too */
1144 if (!setlocale(LC_MESSAGES, msg_lang)) {
1145 if (msg_lang2) {
1146 (void)setlocale(LC_MESSAGES, msg_lang2);
1147 }
1148 }
1149#endif
1150
1151 select_charset(default_charset());
1152}
1153
1154const char *
1155msg_proj_finder_(const char * file)
1156{
1157 struct stat sb;
1158 static char * r = NULL;
1159 static int r_len = 0;
1160 s_zero(&r);
1161 s_cat(&r, &r_len, pth_cfg_files);
1162 s_cat(&r, &r_len, "/proj/");
1163 s_cat(&r, &r_len, file);
1164 if (stat(r, &sb) < 0) return NULL;
1165 return r;
1166}
1167
1168#ifndef AVEN
1169/* Return message if messages available, else a fallback value. */
1170static const char *
1171msg_opt(int en, const char * fallback)
1172{
1173 /* NB can't use SVX_ASSERT here! */
1174 if (!msg_array || en <= 0 || en >= num_msgs) {
1175 return fallback;
1176 }
1177
1178 return msg_array[en];
1179}
1180#endif
1181
1182const char *
1183msg(int en)
1184{
1185 /* NB can't use SVX_ASSERT here! */
1186 if (dontextract && en >= 1000 && en < 1000 + N_DONTEXTRACTMSGS)
1187 return dontextract[en - 1000];
1188 if (!msg_array) {
1189 if (en != 1) {
1190 fprintf(STDERR, "Message %d requested before fully initialised\n", en);
1191 return "Message requested before fully initialised\n";
1192 }
1193 /* this should be the only other message which can be requested before
1194 * the message file is opened and read... */
1195 if (!dontextract) return "Out of memory (couldn't find %lu bytes).";
1196 return dontextract[(/*Out of memory (couldn't find %lu bytes).*/1004)
1197 - 1000];
1198 }
1199
1200 if (en < 0 || en >= num_msgs) {
1201 fprintf(STDERR, "Message %d out of range\n", en);
1202 return "Message out of range\n";
1203 }
1204
1205 if (en == 0) {
1206 const char *p = msg_array[0];
1207 if (!*p) p = "(C)";
1208 return p;
1209 }
1210
1211 return msg_array[en];
1212}
1213
1214void
1215v_report(int severity, const char *fnm, int line, int col, int en, va_list ap)
1216{
1217#ifdef AVEN
1218 (void)col;
1219 aven_v_report(severity, fnm, line, en, ap);
1220#else
1221 const char * level;
1222 if (fnm) {
1223 fputs(fnm, STDERR);
1224 if (line) fprintf(STDERR, ":%d", line);
1225 if (col > 0) fprintf(STDERR, ":%d", col);
1226 } else {
1227 fputs(appname_copy, STDERR);
1228 }
1229 fputs(": ", STDERR);
1230
1231 if (severity == 0) {
1232 /* TRANSLATORS: Indicates a warning message e.g.:
1233 * "spoon.svx:12: warning: *prefix is deprecated" */
1234 level = msg_opt(/*warning*/4, "warning");
1235 } else {
1236 /* TRANSLATORS: Indicates an error message e.g.:
1237 * "spoon.svx:13:4: error: Field may not be omitted" */
1238 level = msg_opt(/*error*/93, "error");
1239 }
1240 fputs(level, STDERR);
1241 fputs(": ", STDERR);
1242
1243 vfprintf(STDERR, msg(en), ap);
1244 fputnl(STDERR);
1245#endif
1246
1247 switch (severity) {
1248 case 0:
1249 msg_warnings++;
1250 break;
1251 case 1:
1252 msg_errors++;
1253 if (msg_errors == 50)
1254 fatalerror_in_file(fnm, 0, /*Too many errors - giving up*/19);
1255 break;
1256 case 2:
1257 exit(EXIT_FAILURE);
1258 }
1259}
1260
1261void
1262warning(int en, ...)
1263{
1264 va_list ap;
1265 va_start(ap, en);
1266 v_report(0, NULL, 0, 0, en, ap);
1267 va_end(ap);
1268}
1269
1270void
1271error(int en, ...)
1272{
1273 va_list ap;
1274 va_start(ap, en);
1275 v_report(1, NULL, 0, 0, en, ap);
1276 va_end(ap);
1277}
1278
1279void
1280fatalerror(int en, ...)
1281{
1282 va_list ap;
1283 va_start(ap, en);
1284 v_report(2, NULL, 0, 0, en, ap);
1285 va_end(ap);
1286}
1287
1288void
1289warning_in_file(const char *fnm, int line, int en, ...)
1290{
1291 va_list ap;
1292 va_start(ap, en);
1293 v_report(0, fnm, line, 0, en, ap);
1294 va_end(ap);
1295}
1296
1297void
1298error_in_file(const char *fnm, int line, int en, ...)
1299{
1300 va_list ap;
1301 va_start(ap, en);
1302 v_report(1, fnm, line, 0, en, ap);
1303 va_end(ap);
1304}
1305
1306void
1307fatalerror_in_file(const char *fnm, int line, int en, ...)
1308{
1309 va_list ap;
1310 va_start(ap, en);
1311 v_report(2, fnm, line, 0, en, ap);
1312 va_end(ap);
1313}
1314
1315/* Code to support switching character set at runtime (e.g. for a printer
1316 * driver to support different character sets on screen and on the printer)
1317 */
1318typedef struct charset_li {
1319 struct charset_li *next;
1320 int code;
1321 char **msg_array;
1322} charset_li;
1323
1324static charset_li *charset_head = NULL;
1325
1326static int charset = CHARSET_BAD;
1327
1328int
1329select_charset(int charset_code)
1330{
1331 int old_charset = charset;
1332 charset_li *p;
1333
1334#ifdef DEBUG
1335 fprintf(stderr, "select_charset(%d), old charset = %d\n", charset_code,
1336 charset);
1337#endif
1338
1339 charset = charset_code;
1340
1341 /* check if we've already parsed messages for new charset */
1342 for (p = charset_head; p; p = p->next) {
1343#ifdef DEBUG
1344 printf("%p: code %d msg_array %p\n", p, p->code, p->msg_array);
1345#endif
1346 if (p->code == charset) {
1347 msg_array = p->msg_array;
1348 return old_charset;
1349 }
1350 }
1351
1352 /* nope, got to reparse message file */
1353 parse_msg_file(charset_code);
1354
1355 /* add to list */
1356 p = osnew(charset_li);
1357 p->code = charset;
1358 p->msg_array = msg_array;
1359 p->next = charset_head;
1360 charset_head = p;
1361
1362 return old_charset;
1363}
Note: See TracBrowser for help on using the repository browser.