source: git/src/message.c @ a46e1ec

RELEASE/1.2debug-cidebug-ci-sanitisersstereowalls-data
Last change on this file since a46e1ec was a46e1ec, checked in by Olly Betts <olly@…>, 9 years ago

src/message.c: Transliterate gradient and infinity symbols if the
current character set lacks them.

  • Property mode set to 100644
File size: 37.3 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         switch (value) {
471            case 0xa0: case 0xa4: case 0xa6: case 0xa7: case 0xa8: case 0xa9:
472            case 0xab: case 0xac: case 0xad: case 0xae: case 0xb0: case 0xb1:
473            case 0xb4: case 0xb5: case 0xb6: case 0xb7: case 0xb8: case 0xbb:
474            case 0xc1: case 0xc2: case 0xc4: case 0xc7: case 0xc9: case 0xcb:
475            case 0xcd: case 0xce: case 0xd3: case 0xd4: case 0xd6: case 0xd7:
476            case 0xda: case 0xdc: case 0xdd: case 0xdf: case 0xe1: case 0xe2:
477            case 0xe4: case 0xe7: case 0xe9: case 0xeb: case 0xed: case 0xee:
478            case 0xf3: case 0xf4: case 0xf6: case 0xf7: case 0xfa: case 0xfc:
479            case 0xfd:
480               v = value; break;
481            case 0x20ac: v = '\x80'; break;
482            case 0x201a: v = '\x82'; break;
483            case 0x201e: v = '\x84'; break;
484            case 0x2026: v = '\x85'; break;
485            case 0x2020: v = '\x86'; break;
486            case 0x2021: v = '\x87'; break;
487            case 0x2030: v = '\x89'; break;
488            case 0x0160: v = '\x8a'; break;
489            case 0x2039: v = '\x8b'; break;
490            case 0x015a: v = '\x8c'; break;
491            case 0x0164: v = '\x8d'; break;
492            case 0x017d: v = '\x8e'; break;
493            case 0x0179: v = '\x8f'; break;
494            case 0x2018: v = '\x91'; break;
495            case 0x2019: v = '\x92'; break;
496            case 0x201c: v = '\x93'; break;
497            case 0x201d: v = '\x94'; break;
498            case 0x2022: v = '\x95'; break;
499            case 0x2013: v = '\x96'; break;
500            case 0x2014: v = '\x97'; break;
501            case 0x2122: v = '\x99'; break;
502            case 0x0161: v = '\x9a'; break;
503            case 0x203a: v = '\x9b'; break;
504            case 0x015b: v = '\x9c'; break;
505            case 0x0165: v = '\x9d'; break;
506            case 0x017e: v = '\x9e'; break;
507            case 0x017a: v = '\x9f'; break;
508            case 0x02c7: v = '\xa1'; break;
509            case 0x02d8: v = '\xa2'; break;
510            case 0x0141: v = '\xa3'; break;
511            case 0x0104: v = '\xa5'; break;
512            case 0x015e: v = '\xaa'; break; /* Scedil */
513            case 0x017b: v = '\xaf'; break;
514            case 0x02db: v = '\xb2'; break;
515            case 0x0142: v = '\xb3'; break;
516            case 0x0105: v = '\xb9'; break;
517            case 0x015f: v = '\xba'; break; /* scedil */
518            case 0x013d: v = '\xbc'; break;
519            case 0x02dd: v = '\xbd'; break;
520            case 0x013e: v = '\xbe'; break;
521            case 0x017c: v = '\xbf'; break;
522            case 0x0154: v = '\xc0'; break;
523            case 0x0102: v = '\xc3'; break;
524            case 0x0139: v = '\xc5'; break;
525            case 0x0106: v = '\xc6'; break;
526            case 0x010c: v = '\xc8'; break;
527            case 0x0118: v = '\xca'; break;
528            case 0x011a: v = '\xcc'; break;
529            case 0x010e: v = '\xcf'; break;
530            case 0x0110: v = '\xd0'; break;
531            case 0x0143: v = '\xd1'; break;
532            case 0x0147: v = '\xd2'; break;
533            case 0x0150: v = '\xd5'; break;
534            case 0x0158: v = '\xd8'; break;
535            case 0x016e: v = '\xd9'; break;
536            case 0x0170: v = '\xdb'; break;
537            case 0x0162: v = '\xde'; break; /* &Tcedil; */
538            case 0x0155: v = '\xe0'; break;
539            case 0x0103: v = '\xe3'; break;
540            case 0x013a: v = '\xe5'; break;
541            case 0x0107: v = '\xe6'; break;
542            case 0x010d: v = '\xe8'; break;
543            case 0x0119: v = '\xea'; break;
544            case 0x011b: v = '\xec'; break;
545            case 0x010f: v = '\xef'; break;
546            case 0x0111: v = '\xf0'; break;
547            case 0x0144: v = '\xf1'; break;
548            case 0x0148: v = '\xf2'; break;
549            case 0x0151: v = '\xf5'; break;
550            case 0x0159: v = '\xf8'; break;
551            case 0x016f: v = '\xf9'; break;
552            case 0x0171: v = '\xfb'; break;
553            case 0x0163: v = '\xfe'; break; /* tcedil */
554            case 0x02d9: v = '\xff'; break;
555         }
556         if (v == 0) break;
557         value = v;
558      }
559      *p = value;
560      return 1;
561   case CHARSET_WINCP1252:
562      /* MS Windows extensions to ISO-8859-1 */
563      switch (value) {
564       case 0x2026: value = 0x85; break; /* hellip */
565       case 0x160: value = 0x8a; break; /* Scaron */
566       case 0x152: value = 0x8c; break; /* OElig */
567       case 0x17d: value = 0x8e; break; /* Zcaron */
568       case 0x161: value = 0x9a; break; /* scaron */
569       case 0x153: value = 0x9c; break; /* oelig */
570       case 0x17e: value = 0x9e; break; /* zcaron */
571#if 0
572      /* there are a few other obscure ones we don't currently need */
573#endif
574      }
575      if (value < 0x100) {
576         *p = value;
577         return 1;
578      }
579      break;
580#endif
581#if OS_WIN32
582   case CHARSET_DOSCP850: {
583      unsigned char uni2dostab[] = {
584         255, 173, 189, 156, 207, 190, 221, 245,
585         249, 184, 166, 174, 170, 240, 169, 238,
586         248, 241, 253, 252, 239, 230, 244, 250,
587         247, 251, 167, 175, 172, 171, 243, 168,
588         183, 181, 182, 199, 142, 143, 146, 128,
589         212, 144, 210, 211, 222, 214, 215, 216,
590         209, 165, 227, 224, 226, 229, 153, 158,
591         157, 235, 233, 234, 154, 237, 232, 225,
592         133, 160, 131, 198, 132, 134, 145, 135,
593         138, 130, 136, 137, 141, 161, 140, 139,
594         208, 164, 149, 162, 147, 228, 148, 246,
595         155, 151, 163, 150, 129, 236, 231, 152
596      };
597      if (value >= 160 && value < 256) {
598         *p = (int)uni2dostab[value - 160];
599         return 1;
600      }
601#if 0
602      if (value == 305) { /* LATIN SMALL LETTER DOTLESS I */
603         *p = 213;
604         return 1;
605      }
606      if (value == 402) { /* LATIN SMALL LETTER F WITH HOOK */
607         *p = 159;
608         return 1;
609      }
610#endif
611      break;
612   }
613#endif
614   }
615   /* Transliterate characters we can't represent */
616#ifdef DEBUG
617   fprintf(stderr, "transliterate “%c” 0x%x\n", value, value);
618#endif
619   switch (value) {
620    case 160:
621      *p = ' '; return 1;
622    case 161 /* ¡ */:
623      *p = '!'; return 1;
624    case 176 /* ° */:
625      *p = 'd'; p[1] = 'g'; return 2;
626    case 191 /* ¿ */:
627      *p = '?'; return 1;
628    case 192 /* À */: case 193 /* Á */: case 194 /* Â */: case 195 /* Ã */:
629      *p = 'A'; return 1;
630    case 197 /* Å */:
631      p[1] = *p = 'A'; return 2;
632    case 196 /* Ä */: /* &Auml; */
633      *p = 'A';
634      if (!umlaut_to_e()) return 1;
635      p[1] = 'E'; return 2;
636    case 198 /* Æ */:
637      *p = 'A'; p[1] = 'E'; return 2;
638    case 199 /* Ç */: case 268: /* &Ccaron; */
639      *p = 'C'; return 1;
640    case 270: /* &Dcaron; */
641      *p = 'D'; return 1;
642    case 200 /* È */: case 201 /* É */: case 202 /* Ê */: case 203 /* Ë */:
643      *p = 'E'; return 1;
644    case 204 /* Ì */: case 205 /* Í */: case 206 /* Î */: case 207 /* Ï */:
645      *p = 'I'; return 1;
646    case 208 /* Ð */: case 222 /* Þ */:
647      *p = 'T'; p[1] = 'H'; return 2;
648    case 315: /* &Lacute; */
649    case 317: /* &Lcaron; */
650      *p = 'L'; return 1;
651    case 209 /* Ñ */:
652      *p = 'N'; return 1;
653    case 210 /* Ò */: case 211 /* Ó */: case 212 /* Ô */: case 213 /* Õ */:
654      *p = 'O'; return 1;
655    case 214 /* Ö */: /* &Ouml; */ case 0x152: /* &OElig; */
656      *p = 'O'; p[1] = 'E'; return 2;
657    case 352: /* &Scaron; */
658    case 0x15e: /* &Scedil; */
659      *p = 'S'; return 1;
660    case 0x162: /* &Tcedil; */
661    case 0x164: /* &Tcaron; */
662      *p = 'T'; return 1;
663    case 217 /* Ù */: case 218 /* Ú */: case 219 /* Û */:
664      *p = 'U'; return 1;
665    case 220 /* Ü */: /* &Uuml; */
666      *p = 'U'; p[1] = 'E'; return 2;
667    case 221 /* Ý */:
668      *p = 'Y'; return 1;
669    case 381: /* &Zcaron; */
670      *p = 'Z'; return 1;
671    case 223 /* ß */:
672      p[1] = *p = 's'; return 2;
673    case 224 /* à */: case 225 /* á */: case 226 /* â */: case 227 /* ã */:
674    case 259: /* &abreve; */
675      *p = 'a'; return 1;
676    case 228 /* ä */: /* &auml; */ case 230 /* æ */:
677      *p = 'a'; p[1] = 'e'; return 2;
678    case 229 /* å */:
679      p[1] = *p = 'a'; return 2;
680    case 231 /* ç */: case 269 /* &ccaron; */:
681      *p = 'c'; return 1;
682    case 271: /* &dcaron; */
683      *p = 'd'; return 1;
684    case 232 /* è */: case 233 /* é */: case 234 /* ê */: case 235 /* ë */:
685    case 283 /* &ecaron; */:
686      *p = 'e'; return 1;
687    case 236 /* ì */: case 237 /* í */: case 238 /* î */: case 239 /* ï */:
688      *p = 'i'; return 1;
689    case 316 /* &lacute; */:
690    case 318 /* &lcaron; */:
691      *p = 'l'; return 1;
692    case 241 /* ñ */: case 328 /* &ncaron; */:
693      *p = 'n'; return 1;
694    case 345: /* &rcaron; */
695      *p = 'r'; return 1;
696    case 353: /* &scaron; */
697    case 0x15f: /* &scedil; */
698      *p = 's'; return 1;
699    case 357: /* &tcaron; */
700    case 0x163: /* &tcedil; */
701      *p = 't'; return 1;
702    case 240 /* ð */: case 254 /* þ */:
703      *p = 't'; p[1] = 'h'; return 2;
704    case 242 /* ò */: case 243 /* ó */: case 244 /* ô */: case 245 /* õ */:
705      *p = 'o'; return 1;
706    case 246 /* ö */: /* &ouml; */ case 0x153: /* &oelig; */
707      *p = 'o'; p[1] = 'e'; return 2;
708    case 249 /* ù */: case 250 /* ú */: case 251 /* û */:
709    case 367 /* &uring; */:
710      *p = 'u'; return 1;
711    case 252 /* ü */: /* &uuml; */
712      *p = 'u'; p[1] = 'e'; return 2;
713    case 253 /* ý */: case 255 /* ÿ */:
714      *p = 'y'; return 1;
715    case 382: /* &zcaron; */
716      *p = 'z'; return 1;
717    case 0x2019: /* &lsquo; */
718      *p = '\''; return 1;
719    case 171: /* « */ case 187: /* » */
720    case 0x201c: /* &ldquo; */ case 0x201d: /* &rdquo; */
721      *p = '"'; return 1;
722    case 0x2026: /* &hellip; */
723      *p = '.'; p[1] = '.'; p[2] = '.'; return 3;
724    case 0x2192: /* &rarr; */
725      *p = '-'; p[1] = '>'; return 2;
726    case 0x1d4d: /* gradient symbol */
727      *p = 'g'; p[1] = 'r'; p[2] = 'd'; return 3;
728    case 0x221e: /* infinity symbol */
729      *p = 'i'; p[1] = 'n'; p[2] = 'f'; return 3;
730   }
731#ifdef DEBUG
732   fprintf(stderr, "failed to transliterate\n");
733#endif
734   return 0;
735}
736
737#if OS_UNIX && defined DATADIR && defined PACKAGE
738/* Under Unix, we compile in the configured path */
739static const char *pth_cfg_files = DATADIR "/" PACKAGE;
740#else
741/* On other platforms, we fall back on looking in the current directory */
742static const char *pth_cfg_files = "";
743#endif
744
745static int num_msgs = 0;
746static char **msg_array = NULL;
747
748static bool msg_lang_explicit = fFalse;
749const char *msg_lang = NULL;
750const char *msg_lang2 = NULL;
751
752static char **
753parse_msgs(int n, unsigned char *p, int charset_code) {
754   int i;
755
756   char **msgs = osmalloc(n * sizeof(char *));
757
758   for (i = 0; i < n; i++) {
759      unsigned char *to = p;
760      int ch;
761      msgs[i] = (char *)p;
762
763      /* If we want UTF8 anyway, we just need to find the start of each
764       * message */
765      if (charset_code == CHARSET_UTF8) {
766         p += strlen((char *)p) + 1;
767         continue;
768      }
769
770      while ((ch = *p++) != 0) {
771         /* A byte in the range 0x80-0xbf or 0xf0-0xff isn't valid in
772          * this state, (0xf0-0xfd mean values > 0xffff) so treat as
773          * literal and try to resync so we cope better when fed
774          * non-utf-8 data.  Similarly we abandon a multibyte sequence
775          * if we hit an invalid character. */
776         if (ch >= 0xc0 && ch < 0xf0) {
777            int ch1 = *p;
778            if ((ch1 & 0xc0) != 0x80) goto resync;
779
780            if (ch < 0xe0) {
781               /* 2 byte sequence */
782               ch = ((ch & 0x1f) << 6) | (ch1 & 0x3f);
783               p++;
784            } else {
785               /* 3 byte sequence */
786               int ch2 = p[1];
787               if ((ch2 & 0xc0) != 0x80) goto resync;
788               ch = ((ch & 0x1f) << 12) | ((ch1 & 0x3f) << 6) | (ch2 & 0x3f);
789               p += 2;
790            }
791         }
792
793         resync:
794
795         if (ch < 127) {
796            *to++ = (char)ch;
797         } else {
798            /* We assume an N byte UTF-8 code never transliterates to more
799             * than N characters (so we can't transliterate © to (C) or
800             * ® to (R) for example) */
801            to += add_unicode(charset_code, to, ch);
802         }
803      }
804      *to++ = '\0';
805   }
806   return msgs;
807}
808
809/* This is the name of the default language, which can be set like so:
810 * ./configure --enable-defaultlang=fr
811 */
812#ifdef DEFAULTLANG
813/* No point extracting these errors as they won't get used if file opens */
814# include "../lib/defaultlang.h"
815#else
816#define N_DONTEXTRACTMSGS 5
817static unsigned char dontextractmsgs[] =
818   "Can't open message file \"%s\" using path \"%s\"\0"/*1000*/
819   "Problem with message file \"%s\"\0"/*1001*/
820   "I don't understand this message file version\0"/*1002*/
821   "Message file truncated?\0"/*1003*/
822   "Out of memory (couldn't find %lu bytes).\0"/*1004*/;
823#endif
824
825static char **dontextract = NULL;
826
827static void
828parse_msg_file(int charset_code)
829{
830   FILE *fh;
831   unsigned char header[20];
832   int i;
833   unsigned len;
834   unsigned char *p;
835   char *fnm, *s;
836   int n;
837
838#ifdef DEBUG
839   fprintf(stderr, "parse_msg_file(%d)\n", charset_code);
840#endif
841
842   /* sort out messages we need to print if we can't open the message file */
843   dontextract = parse_msgs(N_DONTEXTRACTMSGS, dontextractmsgs, charset_code);
844
845   fnm = osstrdup(msg_lang);
846   /* trim off charset from stuff like "de_DE.iso8859_1" */
847   s = strchr(fnm, '.');
848   if (s) *s = '\0';
849
850   fh = fopenWithPthAndExt(pth_cfg_files, fnm, EXT_SVX_MSG, "rb", NULL);
851
852   if (!fh && strlen(fnm) > 3 && fnm[2] == '_') {
853      /* e.g. if 'en_GB' is unknown, see if we know 'en' */
854      fnm[2] = '\0';
855      fh = fopenWithPthAndExt(pth_cfg_files, fnm, EXT_SVX_MSG, "rb", NULL);
856      if (!fh) fnm[2] = '_'; /* for error reporting */
857   }
858
859   if (!fh && !msg_lang_explicit) {
860      /* If msg_lang wasn't specified using environment variable SURVEX_LANG,
861       * then default to 'en' if we don't find messages for language msg_lang.
862       */
863      if (fnm[0] && fnm[1]) {
864         strcpy(fnm, "en");
865      } else {
866         osfree(fnm);
867         fnm = osstrdup("en");
868      }
869      fh = fopenWithPthAndExt(pth_cfg_files, fnm, EXT_SVX_MSG, "rb", NULL);
870   }
871
872   if (!fh) {
873      fatalerror(/*Can't open message file “%s” using path “%s”*/1000,
874                 fnm, pth_cfg_files);
875   }
876
877   if (fread(header, 1, 20, fh) < 20 ||
878       memcmp(header, "Svx\nMsg\r\n\xfe\xff", 12) != 0) {
879      fatalerror(/*Problem with message file “%s”*/1001, fnm);
880   }
881
882   if (header[12] != 0)
883      fatalerror(/*I don't understand this message file version*/1002);
884
885   n = (header[14] << 8) | header[15];
886
887   len = 0;
888   for (i = 16; i < 20; i++) len = (len << 8) | header[i];
889
890   p = osmalloc(len);
891   if (fread(p, 1, len, fh) < len)
892      fatalerror(/*Message file truncated?*/1003);
893
894   fclose(fh);
895
896#ifdef DEBUG
897   fprintf(stderr, "fnm = “%s”, n = %d, len = %d\n", fnm, n, len);
898#endif
899   osfree(fnm);
900
901   msg_array = parse_msgs(n, p, charset_code);
902   num_msgs = n;
903}
904
905const char *
906msg_cfgpth(void)
907{
908   return pth_cfg_files;
909}
910
911const char *
912msg_exepth(void)
913{
914   return exe_pth;
915}
916
917const char *
918msg_appname(void)
919{
920   return appname_copy;
921}
922
923void
924msg_init(char * const *argv)
925{
926   char *p;
927   SVX_ASSERT(argv);
928
929   /* Point to argv[0] itself so we report a more helpful error if the
930    * code to work out the clean appname generates a signal */
931   appname_copy = argv[0];
932#if OS_UNIX
933   /* use name as-is on Unix - programs run from path get name as supplied */
934   appname_copy = osstrdup(argv[0]);
935#else
936   /* use the lower-cased leafname on other platforms */
937   p = leaf_from_fnm(argv[0]);
938   appname_copy = p;
939   while (*p) {
940      *p = tolower(*p);
941      ++p;
942   }
943#endif
944
945   /* shortcut --version so you can check the version number even when the
946    * correct message file can't be found... */
947   if (argv[1] && strcmp(argv[1], "--version") == 0) {
948      cmdline_version();
949      exit(0);
950   }
951   if (argv[0]) {
952      exe_pth = path_from_fnm(argv[0]);
953#ifdef MACOSX_BUNDLE
954      /* If we're being built into a bundle, always look relative to
955       * the path to the binary. */
956#ifdef AVEN
957      /* Aven is packaged as an application, so we must look inside there. */
958      pth_cfg_files = use_path(exe_pth, "../Resources");
959#else
960      pth_cfg_files = use_path(exe_pth, "share/survex");
961#endif
962#elif OS_UNIX && defined DATADIR && defined PACKAGE
963      bool free_pth = fFalse;
964      char *pth = getenv("srcdir");
965      if (!pth || !pth[0]) {
966         pth = path_from_fnm(argv[0]);
967         free_pth = fTrue;
968      }
969      if (pth[0]) {
970         struct stat buf;
971#if OS_UNIX_MACOSX
972         /* On MacOS X the programs may be installed anywhere, with the
973          * share directory and the binaries in the same directory. */
974         p = use_path(pth, "share/survex/en.msg");
975         if (lstat(p, &buf) == 0 && S_ISREG(buf.st_mode)) {
976            pth_cfg_files = use_path(pth, "share/survex");
977            goto macosx_got_msg;
978         }
979         osfree(p);
980         /* The cavern which aven runs is a hardlinked copy alongside
981          * the aven binary.
982          */
983         p = use_path(pth, "../Resources/en.msg");
984         if (lstat(p, &buf) == 0 && S_ISREG(buf.st_mode)) {
985            pth_cfg_files = use_path(pth, "../Resources");
986            goto macosx_got_msg;
987         }
988         osfree(p);
989#endif
990         /* If we're run with an explicit path, check if "../lib/en.msg"
991          * from the program's path exists, and if so look there for
992          * support files - this allows us to test binaries in the build
993          * tree easily. */
994         p = use_path(pth, "../lib/en.msg");
995         if (lstat(p, &buf) == 0) {
996#ifdef S_ISREG
997            /* POSIX way */
998            if (S_ISREG(buf.st_mode)) {
999               pth_cfg_files = use_path(pth, "../lib");
1000            }
1001#else
1002            /* BSD way */
1003            if ((buf.st_mode & S_IFMT) == S_IFREG) {
1004               pth_cfg_files = use_path(pth, "../lib");
1005            }
1006#endif
1007         }
1008#if defined(__GNUC__) && defined(__APPLE_CC__)
1009macosx_got_msg:
1010#endif
1011         osfree(p);
1012      }
1013
1014      if (free_pth) osfree(pth);
1015#elif OS_WIN32
1016      DWORD len = 256;
1017      char *buf = NULL, *modname;
1018      while (1) {
1019          DWORD got;
1020          buf = osrealloc(buf, len);
1021          got = GetModuleFileName(NULL, buf, len);
1022          if (got < len) break;
1023          len += len;
1024      }
1025      modname = buf;
1026      /* Strange Win32 nastiness - strip prefix "\\?\" if present */
1027      if (strncmp(modname, "\\\\?\\", 4) == 0) modname += 4;
1028      pth_cfg_files = path_from_fnm(modname);
1029      osfree(buf);
1030#else
1031      /* Get the path to the support files from argv[0] */
1032      pth_cfg_files = path_from_fnm(argv[0]);
1033#endif
1034   }
1035
1036   msg_lang = getenv("SURVEXLANG");
1037#ifdef DEBUG
1038   fprintf(stderr, "msg_lang = %p (= \"%s\")\n", msg_lang, msg_lang?msg_lang:"(null)");
1039#endif
1040
1041   msg_lang_explicit = fTrue;
1042   if (!msg_lang || !*msg_lang) {
1043      msg_lang_explicit = fFalse;
1044      msg_lang = getenv("LC_ALL");
1045   }
1046   if (!msg_lang || !*msg_lang) {
1047      msg_lang = getenv("LC_MESSAGES");
1048      if (!msg_lang || !*msg_lang) {
1049         msg_lang = getenv("LANG");
1050         /* Something (AutoCAD?) on Microsoft Windows sets LANG to a number. */
1051         if (msg_lang && !isalpha(msg_lang[0])) msg_lang = NULL;
1052      }
1053      if (!msg_lang || !*msg_lang) {
1054#if OS_WIN32
1055         LCID locid;
1056#endif
1057#ifdef DEFAULTLANG
1058         msg_lang = STRING(DEFAULTLANG);
1059#else
1060         msg_lang = "en";
1061#endif
1062#if OS_WIN32
1063         /* GetUserDefaultUILanguage() requires Microsoft Windows 2000 or
1064          * newer.  For older versions, we use GetUserDefaultLCID().
1065          */
1066         {
1067            HMODULE win32 = GetModuleHandle(TEXT("kernel32.dll"));
1068            FARPROC f = GetProcAddress(win32, "GetUserDefaultUILanguage");
1069            if (f) {
1070               typedef LANGID (WINAPI *func_GetUserDefaultUILanguage)(void);
1071               func_GetUserDefaultUILanguage g;
1072               g = (func_GetUserDefaultUILanguage)f;
1073               locid = g();
1074            } else {
1075               locid = GetUserDefaultLCID();
1076            }
1077         }
1078         if (locid) {
1079            WORD langid = LANGIDFROMLCID(locid);
1080            switch (PRIMARYLANGID(langid)) {
1081             case LANG_BULGARIAN:
1082               msg_lang = "bg";
1083               break;
1084/* older mingw compilers don't seem to supply this value */
1085#ifndef LANG_CATALAN
1086# define LANG_CATALAN 0x03
1087#endif
1088             case LANG_CATALAN:
1089               msg_lang = "ca";
1090               break;
1091             case LANG_CHINESE:
1092               msg_lang = "zh";
1093               break;
1094             case LANG_ENGLISH:
1095               if (SUBLANGID(langid) == SUBLANG_ENGLISH_US)
1096                  msg_lang = "en_US";
1097               else
1098                  msg_lang = "en";
1099               break;
1100             case LANG_FRENCH:
1101               msg_lang = "fr";
1102               break;
1103             case LANG_GERMAN:
1104               switch (SUBLANGID(langid)) {
1105                case SUBLANG_GERMAN_SWISS:
1106                  msg_lang = "de_CH";
1107                  break;
1108                default:
1109                  msg_lang = "de";
1110               }
1111               break;
1112             case LANG_GREEK:
1113               msg_lang = "el";
1114               break;
1115             case LANG_HUNGARIAN:
1116               msg_lang = "hu";
1117               break;
1118             case LANG_INDONESIAN:
1119               msg_lang = "id";
1120               break;
1121             case LANG_ITALIAN:
1122               msg_lang = "it";
1123               break;
1124             case LANG_POLISH:
1125               msg_lang = "pl";
1126               break;
1127             case LANG_PORTUGUESE:
1128               if (SUBLANGID(langid) == SUBLANG_PORTUGUESE_BRAZILIAN)
1129                  msg_lang = "pt_BR";
1130               else
1131                  msg_lang = "pt";
1132               break;
1133             case LANG_ROMANIAN:
1134               msg_lang = "ro";
1135               break;
1136             case LANG_RUSSIAN:
1137               msg_lang = "ru";
1138               break;
1139             case LANG_SLOVAK:
1140               msg_lang = "sk";
1141               break;
1142             case LANG_SPANISH:
1143               msg_lang = "es";
1144               break;
1145            }
1146         }
1147#endif
1148      }
1149   }
1150#ifdef DEBUG
1151   fprintf(stderr, "msg_lang = %p (= \"%s\")\n", msg_lang, msg_lang?msg_lang:"(null)");
1152#endif
1153
1154   /* On Mandrake LANG defaults to C */
1155   if (strcmp(msg_lang, "C") == 0) msg_lang = "en";
1156
1157   msg_lang = osstrdup(msg_lang);
1158
1159   /* Convert en-us to en_US, etc */
1160   p = strchr(msg_lang, '-');
1161   if (p) {
1162      *p++ = '_';
1163      while (*p) {
1164         *p = toupper(*p);
1165         p++;
1166      }
1167   }
1168
1169   p = strchr(msg_lang, '_');
1170   if (p) {
1171      *p = '\0';
1172      msg_lang2 = osstrdup(msg_lang);
1173      *p = '_';
1174   }
1175
1176#ifdef LC_MESSAGES
1177   /* try to setlocale() appropriately too */
1178   if (!setlocale(LC_MESSAGES, msg_lang)) {
1179      if (msg_lang2) {
1180         (void)setlocale(LC_MESSAGES, msg_lang2);
1181      }
1182   }
1183#endif
1184
1185   select_charset(default_charset());
1186
1187#ifdef HAVE_SIGNAL
1188   /* Initialise signal handlers only after the messages have been as we need
1189    * the messages to usefully handle the signals. */
1190   init_signals();
1191#endif
1192}
1193
1194const char *
1195msg(int en)
1196{
1197   /* NB can't use SVX_ASSERT here! */
1198   if (dontextract && en >= 1000 && en < 1000 + N_DONTEXTRACTMSGS)
1199      return dontextract[en - 1000];
1200   if (!msg_array) {
1201      if (en != 1)  {
1202         fprintf(STDERR, "Message %d requested before fully initialised\n", en);
1203         return "Message requested before fully initialised\n";
1204      }
1205      /* this should be the only other message which can be requested before
1206       * the message file is opened and read... */
1207      if (!dontextract) return "Out of memory (couldn't find %lu bytes).";
1208      return dontextract[(/*Out of memory (couldn't find %lu bytes).*/1004)
1209                         - 1000];
1210   }
1211
1212   if (en < 0 || en >= num_msgs) {
1213      fprintf(STDERR, "Message %d out of range\n", en);
1214      return "Message out of range\n";
1215   }
1216
1217   if (en == 0) {
1218      const char *p = msg_array[0];
1219      if (!*p) p = "(C)";
1220      return p;
1221   }
1222
1223   return msg_array[en];
1224}
1225
1226void
1227v_report(int severity, const char *fnm, int line, int col, int en, va_list ap)
1228{
1229#ifdef AVEN
1230   (void)col;
1231   aven_v_report(severity, fnm, line, en, ap);
1232#else
1233   const char * level;
1234   if (fnm) {
1235      fputs(fnm, STDERR);
1236      if (line) fprintf(STDERR, ":%d", line);
1237      if (col > 0) fprintf(STDERR, ":%d", col);
1238   } else {
1239      fputs(appname_copy, STDERR);
1240   }
1241   fputs(": ", STDERR);
1242
1243   if (severity == 0) {
1244      /* TRANSLATORS: Indicates a warning message e.g.:
1245       * "spoon.svx:12: warning: *prefix is deprecated" */
1246      level = msg(/*warning*/4);
1247   } else {
1248      /* TRANSLATORS: Indicates an error message e.g.:
1249       * "spoon.svx:13:4: error: Field may not be omitted" */
1250      level = msg(/*error*/93);
1251   }
1252   fputs(level, STDERR);
1253   fputs(": ", STDERR);
1254
1255   vfprintf(STDERR, msg(en), ap);
1256   fputnl(STDERR);
1257#endif
1258
1259   switch (severity) {
1260    case 0:
1261      msg_warnings++;
1262      break;
1263    case 1:
1264      msg_errors++;
1265      if (msg_errors == 50)
1266         fatalerror_in_file(fnm, 0, /*Too many errors - giving up*/19);
1267      break;
1268    case 2:
1269      exit(EXIT_FAILURE);
1270   }
1271}
1272
1273void
1274warning(int en, ...)
1275{
1276   va_list ap;
1277   va_start(ap, en);
1278   v_report(0, NULL, 0, 0, en, ap);
1279   va_end(ap);
1280}
1281
1282void
1283error(int en, ...)
1284{
1285   va_list ap;
1286   va_start(ap, en);
1287   v_report(1, NULL, 0, 0, en, ap);
1288   va_end(ap);
1289}
1290
1291void
1292fatalerror(int en, ...)
1293{
1294   va_list ap;
1295   va_start(ap, en);
1296   v_report(2, NULL, 0, 0, en, ap);
1297   va_end(ap);
1298}
1299
1300void
1301warning_in_file(const char *fnm, int line, int en, ...)
1302{
1303   va_list ap;
1304   va_start(ap, en);
1305   v_report(0, fnm, line, 0, en, ap);
1306   va_end(ap);
1307}
1308
1309void
1310error_in_file(const char *fnm, int line, int en, ...)
1311{
1312   va_list ap;
1313   va_start(ap, en);
1314   v_report(1, fnm, line, 0, en, ap);
1315   va_end(ap);
1316}
1317
1318void
1319fatalerror_in_file(const char *fnm, int line, int en, ...)
1320{
1321   va_list ap;
1322   va_start(ap, en);
1323   v_report(2, fnm, line, 0, en, ap);
1324   va_end(ap);
1325}
1326
1327/* Code to support switching character set at runtime (e.g. for a printer
1328 * driver to support different character sets on screen and on the printer)
1329 */
1330typedef struct charset_li {
1331   struct charset_li *next;
1332   int code;
1333   char **msg_array;
1334} charset_li;
1335
1336static charset_li *charset_head = NULL;
1337
1338static int charset = CHARSET_BAD;
1339
1340int
1341select_charset(int charset_code)
1342{
1343   int old_charset = charset;
1344   charset_li *p;
1345
1346#ifdef DEBUG
1347   fprintf(stderr, "select_charset(%d), old charset = %d\n", charset_code,
1348           charset);
1349#endif
1350
1351   charset = charset_code;
1352
1353   /* check if we've already parsed messages for new charset */
1354   for (p = charset_head; p; p = p->next) {
1355#ifdef DEBUG
1356      printf("%p: code %d msg_array %p\n", p, p->code, p->msg_array);
1357#endif
1358      if (p->code == charset) {
1359         msg_array = p->msg_array;
1360         return old_charset;
1361      }
1362   }
1363
1364   /* nope, got to reparse message file */
1365   parse_msg_file(charset_code);
1366
1367   /* add to list */
1368   p = osnew(charset_li);
1369   p->code = charset;
1370   p->msg_array = msg_array;
1371   p->next = charset_head;
1372   charset_head = p;
1373
1374   return old_charset;
1375}
Note: See TracBrowser for help on using the repository browser.