source: git/src/message.c @ c8fcf66

line_contentsstereotravis-osx
Last change on this file since c8fcf66 was c8fcf66, checked in by Olly Betts <olly@…>, 5 years ago

src/avenprcore.cc,src/message.c,src/message.h: msg() has returned a
persistent copy of the message for decades, so the code's bound to
rely on that in some places. And there's no real reason it
shouldn't continue to as the messages are under 20KB of UTF-8 text
for any language, so lazy loading isn't worth the complication. So
remove msgPerm() and the unused msgFree() macro, and document that
msg() returns a persistent copy.

  • Property mode set to 100644
File size: 37.1 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   }
727#ifdef DEBUG
728   fprintf(stderr, "failed to transliterate\n");
729#endif
730   return 0;
731}
732
733#if OS_UNIX && defined DATADIR && defined PACKAGE
734/* Under Unix, we compile in the configured path */
735static const char *pth_cfg_files = DATADIR "/" PACKAGE;
736#else
737/* On other platforms, we fall back on looking in the current directory */
738static const char *pth_cfg_files = "";
739#endif
740
741static int num_msgs = 0;
742static char **msg_array = NULL;
743
744static bool msg_lang_explicit = fFalse;
745const char *msg_lang = NULL;
746const char *msg_lang2 = NULL;
747
748static char **
749parse_msgs(int n, unsigned char *p, int charset_code) {
750   int i;
751
752   char **msgs = osmalloc(n * sizeof(char *));
753
754   for (i = 0; i < n; i++) {
755      unsigned char *to = p;
756      int ch;
757      msgs[i] = (char *)p;
758
759      /* If we want UTF8 anyway, we just need to find the start of each
760       * message */
761      if (charset_code == CHARSET_UTF8) {
762         p += strlen((char *)p) + 1;
763         continue;
764      }
765
766      while ((ch = *p++) != 0) {
767         /* A byte in the range 0x80-0xbf or 0xf0-0xff isn't valid in
768          * this state, (0xf0-0xfd mean values > 0xffff) so treat as
769          * literal and try to resync so we cope better when fed
770          * non-utf-8 data.  Similarly we abandon a multibyte sequence
771          * if we hit an invalid character. */
772         if (ch >= 0xc0 && ch < 0xf0) {
773            int ch1 = *p;
774            if ((ch1 & 0xc0) != 0x80) goto resync;
775
776            if (ch < 0xe0) {
777               /* 2 byte sequence */
778               ch = ((ch & 0x1f) << 6) | (ch1 & 0x3f);
779               p++;
780            } else {
781               /* 3 byte sequence */
782               int ch2 = p[1];
783               if ((ch2 & 0xc0) != 0x80) goto resync;
784               ch = ((ch & 0x1f) << 12) | ((ch1 & 0x3f) << 6) | (ch2 & 0x3f);
785               p += 2;
786            }
787         }
788
789         resync:
790
791         if (ch < 127) {
792            *to++ = (char)ch;
793         } else {
794            /* We assume an N byte UTF-8 code never transliterates to more
795             * than N characters (so we can't transliterate © to (C) or
796             * ® to (R) for example) */
797            to += add_unicode(charset_code, to, ch);
798         }
799      }
800      *to++ = '\0';
801   }
802   return msgs;
803}
804
805/* This is the name of the default language, which can be set like so:
806 * ./configure --enable-defaultlang=fr
807 */
808#ifdef DEFAULTLANG
809/* No point extracting these errors as they won't get used if file opens */
810# include "../lib/defaultlang.h"
811#else
812#define N_DONTEXTRACTMSGS 5
813static unsigned char dontextractmsgs[] =
814   "Can't open message file \"%s\" using path \"%s\"\0"/*1000*/
815   "Problem with message file \"%s\"\0"/*1001*/
816   "I don't understand this message file version\0"/*1002*/
817   "Message file truncated?\0"/*1003*/
818   "Out of memory (couldn't find %lu bytes).\0"/*1004*/;
819#endif
820
821static char **dontextract = NULL;
822
823static void
824parse_msg_file(int charset_code)
825{
826   FILE *fh;
827   unsigned char header[20];
828   int i;
829   unsigned len;
830   unsigned char *p;
831   char *fnm, *s;
832   int n;
833
834#ifdef DEBUG
835   fprintf(stderr, "parse_msg_file(%d)\n", charset_code);
836#endif
837
838   /* sort out messages we need to print if we can't open the message file */
839   dontextract = parse_msgs(N_DONTEXTRACTMSGS, dontextractmsgs, charset_code);
840
841   fnm = osstrdup(msg_lang);
842   /* trim off charset from stuff like "de_DE.iso8859_1" */
843   s = strchr(fnm, '.');
844   if (s) *s = '\0';
845
846   fh = fopenWithPthAndExt(pth_cfg_files, fnm, EXT_SVX_MSG, "rb", NULL);
847
848   if (!fh && strlen(fnm) > 3 && fnm[2] == '_') {
849      /* e.g. if 'en_GB' is unknown, see if we know 'en' */
850      fnm[2] = '\0';
851      fh = fopenWithPthAndExt(pth_cfg_files, fnm, EXT_SVX_MSG, "rb", NULL);
852      if (!fh) fnm[2] = '_'; /* for error reporting */
853   }
854
855   if (!fh && !msg_lang_explicit) {
856      /* If msg_lang wasn't specified using environment variable SURVEX_LANG,
857       * then default to 'en' if we don't find messages for language msg_lang.
858       */
859      if (fnm[0] && fnm[1]) {
860         strcpy(fnm, "en");
861      } else {
862         osfree(fnm);
863         fnm = osstrdup("en");
864      }
865      fh = fopenWithPthAndExt(pth_cfg_files, fnm, EXT_SVX_MSG, "rb", NULL);
866   }
867
868   if (!fh) {
869      fatalerror(/*Can't open message file “%s” using path “%s”*/1000,
870                 fnm, pth_cfg_files);
871   }
872
873   if (fread(header, 1, 20, fh) < 20 ||
874       memcmp(header, "Svx\nMsg\r\n\xfe\xff", 12) != 0) {
875      fatalerror(/*Problem with message file “%s”*/1001, fnm);
876   }
877
878   if (header[12] != 0)
879      fatalerror(/*I don't understand this message file version*/1002);
880
881   n = (header[14] << 8) | header[15];
882
883   len = 0;
884   for (i = 16; i < 20; i++) len = (len << 8) | header[i];
885
886   p = osmalloc(len);
887   if (fread(p, 1, len, fh) < len)
888      fatalerror(/*Message file truncated?*/1003);
889
890   fclose(fh);
891
892#ifdef DEBUG
893   fprintf(stderr, "fnm = “%s”, n = %d, len = %d\n", fnm, n, len);
894#endif
895   osfree(fnm);
896
897   msg_array = parse_msgs(n, p, charset_code);
898   num_msgs = n;
899}
900
901const char *
902msg_cfgpth(void)
903{
904   return pth_cfg_files;
905}
906
907const char *
908msg_exepth(void)
909{
910   return exe_pth;
911}
912
913const char *
914msg_appname(void)
915{
916   return appname_copy;
917}
918
919void
920msg_init(char * const *argv)
921{
922   char *p;
923   SVX_ASSERT(argv);
924
925   /* Point to argv[0] itself so we report a more helpful error if the
926    * code to work out the clean appname generates a signal */
927   appname_copy = argv[0];
928#if OS_UNIX
929   /* use name as-is on Unix - programs run from path get name as supplied */
930   appname_copy = osstrdup(argv[0]);
931#else
932   /* use the lower-cased leafname on other platforms */
933   p = leaf_from_fnm(argv[0]);
934   appname_copy = p;
935   while (*p) {
936      *p = tolower(*p);
937      ++p;
938   }
939#endif
940
941   /* shortcut --version so you can check the version number even when the
942    * correct message file can't be found... */
943   if (argv[1] && strcmp(argv[1], "--version") == 0) {
944      cmdline_version();
945      exit(0);
946   }
947   if (argv[0]) {
948      exe_pth = path_from_fnm(argv[0]);
949#ifdef MACOSX_BUNDLE
950      /* If we're being built into a bundle, always look relative to
951       * the path to the binary. */
952#ifdef AVEN
953      /* Aven is packaged as an application, so we must look inside there. */
954      pth_cfg_files = use_path(exe_pth, "../Resources");
955#else
956      pth_cfg_files = use_path(exe_pth, "share/survex");
957#endif
958#elif OS_UNIX && defined DATADIR && defined PACKAGE
959      bool free_pth = fFalse;
960      char *pth = getenv("srcdir");
961      if (!pth || !pth[0]) {
962         pth = path_from_fnm(argv[0]);
963         free_pth = fTrue;
964      }
965      if (pth[0]) {
966         struct stat buf;
967#if OS_UNIX_MACOSX
968         /* On MacOS X the programs may be installed anywhere, with the
969          * share directory and the binaries in the same directory. */
970         p = use_path(pth, "share/survex/en.msg");
971         if (lstat(p, &buf) == 0 && S_ISREG(buf.st_mode)) {
972            pth_cfg_files = use_path(pth, "share/survex");
973            goto macosx_got_msg;
974         }
975         osfree(p);
976         /* The cavern which aven runs is a hardlinked copy alongside
977          * the aven binary.
978          */
979         p = use_path(pth, "../Resources/en.msg");
980         if (lstat(p, &buf) == 0 && S_ISREG(buf.st_mode)) {
981            pth_cfg_files = use_path(pth, "../Resources");
982            goto macosx_got_msg;
983         }
984         osfree(p);
985#endif
986         /* If we're run with an explicit path, check if "../lib/en.msg"
987          * from the program's path exists, and if so look there for
988          * support files - this allows us to test binaries in the build
989          * tree easily. */
990         p = use_path(pth, "../lib/en.msg");
991         if (lstat(p, &buf) == 0) {
992#ifdef S_ISREG
993            /* POSIX way */
994            if (S_ISREG(buf.st_mode)) {
995               pth_cfg_files = use_path(pth, "../lib");
996            }
997#else
998            /* BSD way */
999            if ((buf.st_mode & S_IFMT) == S_IFREG) {
1000               pth_cfg_files = use_path(pth, "../lib");
1001            }
1002#endif
1003         }
1004#if defined(__GNUC__) && defined(__APPLE_CC__)
1005macosx_got_msg:
1006#endif
1007         osfree(p);
1008      }
1009
1010      if (free_pth) osfree(pth);
1011#elif OS_WIN32
1012      DWORD len = 256;
1013      char *buf = NULL, *modname;
1014      while (1) {
1015          DWORD got;
1016          buf = osrealloc(buf, len);
1017          got = GetModuleFileName(NULL, buf, len);
1018          if (got < len) break;
1019          len += len;
1020      }
1021      modname = buf;
1022      /* Strange Win32 nastiness - strip prefix "\\?\" if present */
1023      if (strncmp(modname, "\\\\?\\", 4) == 0) modname += 4;
1024      pth_cfg_files = path_from_fnm(modname);
1025      osfree(buf);
1026#else
1027      /* Get the path to the support files from argv[0] */
1028      pth_cfg_files = path_from_fnm(argv[0]);
1029#endif
1030   }
1031
1032   msg_lang = getenv("SURVEXLANG");
1033#ifdef DEBUG
1034   fprintf(stderr, "msg_lang = %p (= \"%s\")\n", msg_lang, msg_lang?msg_lang:"(null)");
1035#endif
1036
1037   msg_lang_explicit = fTrue;
1038   if (!msg_lang || !*msg_lang) {
1039      msg_lang_explicit = fFalse;
1040      msg_lang = getenv("LC_ALL");
1041   }
1042   if (!msg_lang || !*msg_lang) {
1043      msg_lang = getenv("LC_MESSAGES");
1044      if (!msg_lang || !*msg_lang) {
1045         msg_lang = getenv("LANG");
1046         /* Something (AutoCAD?) on Microsoft Windows sets LANG to a number. */
1047         if (msg_lang && !isalpha(msg_lang[0])) msg_lang = NULL;
1048      }
1049      if (!msg_lang || !*msg_lang) {
1050#if OS_WIN32
1051         LCID locid;
1052#endif
1053#ifdef DEFAULTLANG
1054         msg_lang = STRING(DEFAULTLANG);
1055#else
1056         msg_lang = "en";
1057#endif
1058#if OS_WIN32
1059         /* GetUserDefaultUILanguage() requires Microsoft Windows 2000 or
1060          * newer.  For older versions, we use GetUserDefaultLCID().
1061          */
1062         {
1063            HMODULE win32 = GetModuleHandle(TEXT("kernel32.dll"));
1064            FARPROC f = GetProcAddress(win32, "GetUserDefaultUILanguage");
1065            if (f) {
1066               typedef LANGID (WINAPI *func_GetUserDefaultUILanguage)(void);
1067               func_GetUserDefaultUILanguage g;
1068               g = (func_GetUserDefaultUILanguage)f;
1069               locid = g();
1070            } else {
1071               locid = GetUserDefaultLCID();
1072            }
1073         }
1074         if (locid) {
1075            WORD langid = LANGIDFROMLCID(locid);
1076            switch (PRIMARYLANGID(langid)) {
1077             case LANG_BULGARIAN:
1078               msg_lang = "bg";
1079               break;
1080/* older mingw compilers don't seem to supply this value */
1081#ifndef LANG_CATALAN
1082# define LANG_CATALAN 0x03
1083#endif
1084             case LANG_CATALAN:
1085               msg_lang = "ca";
1086               break;
1087             case LANG_CHINESE:
1088               msg_lang = "zh";
1089               break;
1090             case LANG_ENGLISH:
1091               if (SUBLANGID(langid) == SUBLANG_ENGLISH_US)
1092                  msg_lang = "en_US";
1093               else
1094                  msg_lang = "en";
1095               break;
1096             case LANG_FRENCH:
1097               msg_lang = "fr";
1098               break;
1099             case LANG_GERMAN:
1100               switch (SUBLANGID(langid)) {
1101                case SUBLANG_GERMAN_SWISS:
1102                  msg_lang = "de_CH";
1103                  break;
1104                default:
1105                  msg_lang = "de";
1106               }
1107               break;
1108             case LANG_GREEK:
1109               msg_lang = "el";
1110               break;
1111             case LANG_HUNGARIAN:
1112               msg_lang = "hu";
1113               break;
1114             case LANG_INDONESIAN:
1115               msg_lang = "id";
1116               break;
1117             case LANG_ITALIAN:
1118               msg_lang = "it";
1119               break;
1120             case LANG_POLISH:
1121               msg_lang = "pl";
1122               break;
1123             case LANG_PORTUGUESE:
1124               if (SUBLANGID(langid) == SUBLANG_PORTUGUESE_BRAZILIAN)
1125                  msg_lang = "pt_BR";
1126               else
1127                  msg_lang = "pt";
1128               break;
1129             case LANG_ROMANIAN:
1130               msg_lang = "ro";
1131               break;
1132             case LANG_RUSSIAN:
1133               msg_lang = "ru";
1134               break;
1135             case LANG_SLOVAK:
1136               msg_lang = "sk";
1137               break;
1138             case LANG_SPANISH:
1139               msg_lang = "es";
1140               break;
1141            }
1142         }
1143#endif
1144      }
1145   }
1146#ifdef DEBUG
1147   fprintf(stderr, "msg_lang = %p (= \"%s\")\n", msg_lang, msg_lang?msg_lang:"(null)");
1148#endif
1149
1150   /* On Mandrake LANG defaults to C */
1151   if (strcmp(msg_lang, "C") == 0) msg_lang = "en";
1152
1153   msg_lang = osstrdup(msg_lang);
1154
1155   /* Convert en-us to en_US, etc */
1156   p = strchr(msg_lang, '-');
1157   if (p) {
1158      *p++ = '_';
1159      while (*p) {
1160         *p = toupper(*p);
1161         p++;
1162      }
1163   }
1164
1165   p = strchr(msg_lang, '_');
1166   if (p) {
1167      *p = '\0';
1168      msg_lang2 = osstrdup(msg_lang);
1169      *p = '_';
1170   }
1171
1172#ifdef LC_MESSAGES
1173   /* try to setlocale() appropriately too */
1174   if (!setlocale(LC_MESSAGES, msg_lang)) {
1175      if (msg_lang2) {
1176         (void)setlocale(LC_MESSAGES, msg_lang2);
1177      }
1178   }
1179#endif
1180
1181   select_charset(default_charset());
1182
1183#ifdef HAVE_SIGNAL
1184   /* Initialise signal handlers only after the messages have been as we need
1185    * the messages to usefully handle the signals. */
1186   init_signals();
1187#endif
1188}
1189
1190const char *
1191msg(int en)
1192{
1193   /* NB can't use SVX_ASSERT here! */
1194   static char badbuf[256];
1195   if (dontextract && en >= 1000 && en < 1000 + N_DONTEXTRACTMSGS)
1196      return dontextract[en - 1000];
1197   if (!msg_array) {
1198      if (en != 1)  {
1199         sprintf(badbuf, "Message %d requested before fully initialised\n", en);
1200         return badbuf;
1201      }
1202      /* this should be the only other message which can be requested before
1203       * the message file is opened and read... */
1204      if (!dontextract) return "Out of memory (couldn't find %lu bytes).";
1205      return dontextract[(/*Out of memory (couldn't find %lu bytes).*/1004)
1206                         - 1000];
1207   }
1208
1209   if (en < 0 || en >= num_msgs) {
1210      sprintf(badbuf, "Message %d out of range\n", en);
1211      return badbuf;
1212   }
1213
1214   if (en == 0) {
1215      const char *p = msg_array[0];
1216      if (!*p) p = "(C)";
1217      return p;
1218   }
1219
1220   return msg_array[en];
1221}
1222
1223void
1224v_report(int severity, const char *fnm, int line, int col, int en, va_list ap)
1225{
1226#ifdef AVEN
1227   (void)col;
1228   aven_v_report(severity, fnm, line, en, ap);
1229#else
1230   const char * level;
1231   if (fnm) {
1232      fputs(fnm, STDERR);
1233      if (line) fprintf(STDERR, ":%d", line);
1234      if (col > 0) fprintf(STDERR, ":%d", col);
1235   } else {
1236      fputs(appname_copy, STDERR);
1237   }
1238   fputs(": ", STDERR);
1239
1240   if (severity == 0) {
1241      /* TRANSLATORS: Indicates a warning message e.g.:
1242       * "spoon.svx:12: warning: *prefix is deprecated" */
1243      level = msg(/*warning*/4);
1244   } else {
1245      /* TRANSLATORS: Indicates an error message e.g.:
1246       * "spoon.svx:13:4: error: Field may not be omitted" */
1247      level = msg(/*error*/93);
1248   }
1249   fputs(level, STDERR);
1250   fputs(": ", STDERR);
1251
1252   vfprintf(STDERR, msg(en), ap);
1253   fputnl(STDERR);
1254#endif
1255
1256   switch (severity) {
1257    case 0:
1258      msg_warnings++;
1259      break;
1260    case 1:
1261      msg_errors++;
1262      if (msg_errors == 50)
1263         fatalerror_in_file(fnm, 0, /*Too many errors - giving up*/19);
1264      break;
1265    case 2:
1266      exit(EXIT_FAILURE);
1267   }
1268}
1269
1270void
1271warning(int en, ...)
1272{
1273   va_list ap;
1274   va_start(ap, en);
1275   v_report(0, NULL, 0, 0, en, ap);
1276   va_end(ap);
1277}
1278
1279void
1280error(int en, ...)
1281{
1282   va_list ap;
1283   va_start(ap, en);
1284   v_report(1, NULL, 0, 0, en, ap);
1285   va_end(ap);
1286}
1287
1288void
1289fatalerror(int en, ...)
1290{
1291   va_list ap;
1292   va_start(ap, en);
1293   v_report(2, NULL, 0, 0, en, ap);
1294   va_end(ap);
1295}
1296
1297void
1298warning_in_file(const char *fnm, int line, int en, ...)
1299{
1300   va_list ap;
1301   va_start(ap, en);
1302   v_report(0, fnm, line, 0, en, ap);
1303   va_end(ap);
1304}
1305
1306void
1307error_in_file(const char *fnm, int line, int en, ...)
1308{
1309   va_list ap;
1310   va_start(ap, en);
1311   v_report(1, fnm, line, 0, en, ap);
1312   va_end(ap);
1313}
1314
1315void
1316fatalerror_in_file(const char *fnm, int line, int en, ...)
1317{
1318   va_list ap;
1319   va_start(ap, en);
1320   v_report(2, fnm, line, 0, en, ap);
1321   va_end(ap);
1322}
1323
1324/* Code to support switching character set at runtime (e.g. for a printer
1325 * driver to support different character sets on screen and on the printer)
1326 */
1327typedef struct charset_li {
1328   struct charset_li *next;
1329   int code;
1330   char **msg_array;
1331} charset_li;
1332
1333static charset_li *charset_head = NULL;
1334
1335static int charset = CHARSET_BAD;
1336
1337int
1338select_charset(int charset_code)
1339{
1340   int old_charset = charset;
1341   charset_li *p;
1342
1343#ifdef DEBUG
1344   fprintf(stderr, "select_charset(%d), old charset = %d\n", charset_code,
1345           charset);
1346#endif
1347
1348   charset = charset_code;
1349
1350   /* check if we've already parsed messages for new charset */
1351   for (p = charset_head; p; p = p->next) {
1352#ifdef DEBUG
1353      printf("%p: code %d msg_array %p\n", p, p->code, p->msg_array);
1354#endif
1355      if (p->code == charset) {
1356         msg_array = p->msg_array;
1357         return old_charset;
1358      }
1359   }
1360
1361   /* nope, got to reparse message file */
1362   parse_msg_file(charset_code);
1363
1364   /* add to list */
1365   p = osnew(charset_li);
1366   p->code = charset;
1367   p->msg_array = msg_array;
1368   p->next = charset_head;
1369   charset_head = p;
1370
1371   return old_charset;
1372}
Note: See TracBrowser for help on using the repository browser.