source: git/src/message.c @ f6e66b0

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

src/message.c: Add commented out mappings for the CP-1252 characters
we don't currently make use of.

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