source: git/src/message.c @ d5427e7

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

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

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