source: git/src/message.c @ 9fa129a

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

src/message.c: Add CP-1252 mappings for fancy quotes.

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