source: git/src/message.c @ 559cd60

Last change on this file since 559cd60 was 559cd60, checked in by Olly Betts <olly@…>, 7 weeks ago

Consistently refer to macOS not OS X, etc

Apple renamed it yet again.

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