source: git/src/message.c@ 85c0078

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

src/message.c: Document the CP-1250 and CP-1252 mappings are
complete, and which positions are unused in these character sets.

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