source: git/src/message.c @ e75aec2

RELEASE/1.0
Last change on this file since e75aec2 was e75aec2, checked in by Olly Betts <olly@…>, 13 years ago

Backport changes from 1.2.0:
src/message.c: 0x2026 (&hellip;): Handle for WINCP1252, and
transliterate to '...' for charsets which don't have it. 0x2192
(&rarr;): Transliterate to '->' for charsets which don't have it.
Handle &Scaron;, &scaron;, &Zcaron;, &zcaron; for WINCP1252.
lib/messages.txt: Use '&rarr;' instead of '->' and '&hellip;' instead
of '...'.
lib/named-entities.txt: Add hellip and rarr.
lib/messages.txt: Sync common messages with 1.2.0.

git-svn-id: file:///home/survex-svn/survex/branches/1.0@3695 4b37db11-9a0c-4f06-9ece-9ab7cdaee568

  • Property mode set to 100644
File size: 49.6 KB
Line 
1/* message.c
2 * Fairly general purpose message and error routines
3 * Copyright (C) 1993-2003,2004,2005,2006,2007,2011 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==MSDOS)
60# include <dos.h>
61# ifdef __DJGPP__
62#  include <dpmi.h>
63#  include <go32.h>
64#  include <sys/movedata.h>
65# endif
66#elif (OS==RISCOS)
67# include "oslib/wimpreadsy.h"
68# include "oslib/territory.h"
69#elif (OS==UNIX)
70# include <sys/types.h>
71# include <sys/stat.h>
72#endif
73
74/* For funcs which want to be immune from messing around with different
75 * calling conventions */
76#ifndef CDECL
77# define CDECL
78#endif
79
80int msg_warnings = 0; /* keep track of how many warnings we've given */
81int msg_errors = 0;   /* and how many (non-fatal) errors */
82
83/* in case osmalloc() fails before appname_copy is set up */
84static const char *appname_copy = "anonymous program";
85
86/* error code for failed osmalloc and osrealloc calls */
87static void
88outofmem(OSSIZE_T size)
89{
90   fatalerror(/*Out of memory (couldn't find %lu bytes).*/1,
91              (unsigned long)size);
92}
93
94#ifdef TOMBSTONES
95#define TOMBSTONE_SIZE 16
96static const char tombstone[TOMBSTONE_SIZE] = "012345\xfftombstone";
97#endif
98
99/* malloc with error catching if it fails. Also allows us to write special
100 * versions easily eg for DOS EMS or MS Windows.
101 */
102void Far *
103osmalloc(OSSIZE_T size)
104{
105   void Far *p;
106#ifdef TOMBSTONES
107   size += TOMBSTONE_SIZE * 2;
108   p = malloc(size);
109#else
110   p = xosmalloc(size);
111#endif
112   if (p == NULL) outofmem(size);
113#ifdef TOMBSTONES
114   printf("osmalloc truep=%p truesize=%d\n", p, size);
115   memcpy(p, tombstone, TOMBSTONE_SIZE);
116   memcpy(p + size - TOMBSTONE_SIZE, tombstone, TOMBSTONE_SIZE);
117   *(size_t *)p = size;
118   p += TOMBSTONE_SIZE;
119#endif
120   return p;
121}
122
123/* realloc with error catching if it fails. */
124void Far *
125osrealloc(void *p, OSSIZE_T size)
126{
127   /* some pre-ANSI realloc implementations don't cope with a NULL pointer */
128   if (p == NULL) {
129      p = xosmalloc(size);
130   } else {
131#ifdef TOMBSTONES
132      int true_size;
133      size += TOMBSTONE_SIZE * 2;
134      p -= TOMBSTONE_SIZE;
135      true_size = *(size_t *)p;
136      printf("osrealloc (in truep=%p truesize=%d)\n", p, true_size);
137      if (memcmp(p + sizeof(size_t), tombstone + sizeof(size_t),
138                 TOMBSTONE_SIZE - sizeof(size_t)) != 0) {
139         printf("start tombstone for block %p, size %d corrupted!",
140                p + TOMBSTONE_SIZE, true_size - TOMBSTONE_SIZE * 2);
141      }
142      if (memcmp(p + true_size - TOMBSTONE_SIZE, tombstone,
143                 TOMBSTONE_SIZE) != 0) {
144         printf("end tombstone for block %p, size %d corrupted!",
145                p + TOMBSTONE_SIZE, true_size - TOMBSTONE_SIZE * 2);
146      }
147      p = realloc(p, size);
148      if (p == NULL) outofmem(size);
149      printf("osrealloc truep=%p truesize=%d\n", p, size);
150      memcpy(p, tombstone, TOMBSTONE_SIZE);
151      memcpy(p + size - TOMBSTONE_SIZE, tombstone, TOMBSTONE_SIZE);
152      *(size_t *)p = size;
153      p += TOMBSTONE_SIZE;
154#else
155      p = xosrealloc(p, size);
156#endif
157   }
158   if (p == NULL) outofmem(size);
159   return p;
160}
161
162char Far *
163osstrdup(const char *str)
164{
165   char *p;
166   OSSIZE_T len;
167   len = strlen(str) + 1;
168   p = osmalloc(len);
169   memcpy(p, str, len);
170   return p;
171}
172
173/* osfree is usually just a macro in osalloc.h */
174#ifdef TOMBSTONES
175void
176osfree(void *p)
177{
178   int true_size;
179   if (!p) return;
180   p -= TOMBSTONE_SIZE;
181   true_size = *(size_t *)p;
182   printf("osfree truep=%p truesize=%d\n", p, true_size);
183   if (memcmp(p + sizeof(size_t), tombstone + sizeof(size_t),
184              TOMBSTONE_SIZE - sizeof(size_t)) != 0) {
185      printf("start tombstone for block %p, size %d corrupted!",
186             p + TOMBSTONE_SIZE, true_size - TOMBSTONE_SIZE * 2);
187   }
188   if (memcmp(p + true_size - TOMBSTONE_SIZE, tombstone,
189              TOMBSTONE_SIZE) != 0) {
190      printf("end tombstone for block %p, size %d corrupted!",
191             p + TOMBSTONE_SIZE, true_size - TOMBSTONE_SIZE * 2);
192   }
193   free(p);
194}
195#endif
196
197#ifdef HAVE_SIGNAL
198
199static int sigReceived;
200
201/* for systems not using autoconf, assume the signal handler returns void
202 * unless specified elsewhere */
203#ifndef RETSIGTYPE
204# define RETSIGTYPE void
205#endif
206
207static CDECL RETSIGTYPE Far
208report_sig(int sig)
209{
210   sigReceived = sig;
211   longjmp(jmpbufSignal, 1);
212}
213
214static void
215init_signals(void)
216{
217   int en;
218   if (!setjmp(jmpbufSignal)) {
219      signal(SIGABRT, report_sig); /* abnormal termination eg abort() */
220      signal(SIGFPE,  report_sig); /* arithmetic error eg /0 or overflow */
221      signal(SIGILL,  report_sig); /* illegal function image eg illegal instruction */
222      signal(SIGSEGV, report_sig); /* illegal storage access eg access outside memory limits */
223# ifdef SIGSTAK /* only on RISC OS AFAIK */
224      signal(SIGSTAK, report_sig); /* stack overflow */
225# endif
226      return;
227   }
228
229   /* Remove that signal handler to avoid the possibility of an infinite loop.
230    */
231   signal(sigReceived, SIG_DFL);
232
233   switch (sigReceived) {
234      case SIGABRT: en = /*Abnormal termination*/90; break;
235      case SIGFPE:  en = /*Arithmetic error*/91; break;
236      case SIGILL:  en = /*Illegal instruction*/92; break;
237      case SIGSEGV: en = /*Bad memory access*/94; break;
238# ifdef SIGSTAK
239      case SIGSTAK: en = /*Stack overflow*/96; break;
240# endif
241      default:      en = /*Unknown signal received*/97; break;
242   }
243   fputsnl(msg(en), STDERR);
244
245   /* Any of the signals we catch indicates a bug */
246   fatalerror(/*Bug in program detected! Please report this to the authors*/11);
247
248   exit(EXIT_FAILURE);
249}
250#endif
251
252static int
253default_charset(void)
254{
255#if (OS==RISCOS)
256   /* RISCOS 3.1 and above CHARSET_RISCOS31 (ISO_8859_1 + extras in 128-159)
257    * RISCOS < 3.1 is ISO_8859_1 */
258   int version;
259   if (xwimpreadsysinfo_version(&version) != NULL) {
260      /* RISC OS 2 or some error (don't care which) */
261      return CHARSET_ISO_8859_1;
262   }
263
264   /* oddly wimp_VERSION_RO3 is RISC OS 3.1 */
265   if (version < wimp_VERSION_RO3) return CHARSET_ISO_8859_1;
266
267   return CHARSET_RISCOS31;
268#elif (OS==MSDOS)
269#ifdef __DJGPP__
270   __dpmi_regs r;
271   r.x.ax = 0x6501;
272   r.x.bx = 0xffff;
273   r.x.dx = 0xffff;
274   /* Use DJGPP's transfer buffer (which is at least 2K) */
275   r.x.es = __tb >> 4;
276   r.x.di = __tb & 0x0f;
277   r.x.cx = 2048;
278   /* bit 1 is the carry flag */
279   if (__dpmi_int(0x21, &r) != -1 && !(r.x.flags & 1)) {
280      unsigned short p;
281      dosmemget(__tb + 5, 2, &p);
282#else
283   union REGS r;
284   struct SREGS s = { 0 };
285
286   unsigned char buf[48];
287   r.x.ax = 0x6501;
288   r.x.bx = 0xffff;
289   r.x.dx = 0xffff;
290   s.es = FP_SEG(buf);
291   r.x.di = FP_OFF(buf);
292   r.x.cx = 48;
293   intdosx(&r, &r, &s);
294   if (!r.x.cflag) {
295      unsigned short p = buf[5] | (buf[6] << 8);
296#endif
297      if (p == 437) return CHARSET_DOSCP437;
298      if (p == 850) return CHARSET_DOSCP850;
299      if (p == 912) return CHARSET_ISO_8859_2;
300   }
301   return CHARSET_USASCII;
302#elif (OS==WIN32)
303# ifdef AVEN
304#  define CODEPAGE GetACP()
305# else
306#  define CODEPAGE GetConsoleOutputCP()
307# endif
308   switch (CODEPAGE) {
309    case 1252: return CHARSET_WINCP1252;
310    case 1250: return CHARSET_WINCP1250;
311    case 850: return CHARSET_DOSCP850;
312   }
313   return CHARSET_USASCII;
314#elif (OS==UNIX)
315#if defined(XCAVEROT) || defined(AVEN)
316   return CHARSET_ISO_8859_1;
317#else
318   const char *p = getenv("LC_ALL");
319   if (p == NULL || p[0] == '\0') {
320      p = getenv("LC_CTYPE");
321      if (p == NULL || p[0] == '\0') {
322         p = msg_lang;
323      }
324   }
325
326   if (p) {
327      char *q = strchr(p, '.');
328      if (q) p = q + 1;
329   }
330
331   if (p) {
332      const char *chset = p;
333      size_t name_len;
334
335      while (*p != '\0' && *p != '@') p++;
336
337      name_len = p - chset;
338
339      if (name_len) {
340         int only_digit = 1;
341         size_t cnt;
342
343         for (cnt = 0; cnt < name_len; ++cnt)
344            if (isalpha((unsigned char)chset[cnt])) {
345               only_digit = 0;
346               break;
347            }
348
349         if (only_digit) goto iso;
350
351         switch (tolower(chset[0])) {
352          case 'i':
353            if (tolower(chset[1]) == 's' && tolower(chset[2]) == 'o') {
354               chset += 3;
355               iso:
356               if (strncmp(chset, "8859", 4) == 0) {
357                  chset += 4;
358                  while (chset < p && *chset && !isdigit((unsigned char)*chset))
359                     chset++;
360                  switch (atoi(chset)) {
361                   case 1: return CHARSET_ISO_8859_1;
362                   case 2: return CHARSET_ISO_8859_2;
363                   case 15: return CHARSET_ISO_8859_15;
364                   default: return CHARSET_USASCII;
365                  }
366               }
367            }
368            break;
369          case 'u':
370            if (tolower(chset[1]) == 't' && tolower(chset[2]) == 'f') {
371               chset += 3;
372               while (chset < p && *chset && !isdigit((unsigned char)*chset))
373                  chset++;
374               switch (atoi(chset)) {
375                case 8: return CHARSET_UTF8;
376                default: return CHARSET_USASCII;
377               }
378            }
379         }
380      }
381   }
382   return CHARSET_USASCII;
383#endif
384#else
385# error Do not know operating system 'OS'
386#endif
387}
388
389/* It seems that Swedish and maybe some other scandanavian languages don't
390 * transliterate &auml; to ae - but it seems there may be conflicting views
391 * on this...
392 */
393#define umlaut_to_e() 1
394
395/* values <= 127 already dealt with */
396static int
397add_unicode(int charset, unsigned char *p, int value)
398{
399#ifdef DEBUG
400   fprintf(stderr, "add_unicode(%d, %p, %d)\n", charset, p, value);
401#endif
402   if (value == 0) return 0;
403   switch (charset) {
404   case CHARSET_USASCII:
405      if (value < 0x80) {
406         *p = value;
407         return 1;
408      }
409      break;
410   case CHARSET_ISO_8859_1:
411      if (value < 0x100) {
412         *p = value;
413         return 1;
414      }
415      break;
416   case CHARSET_ISO_8859_2:
417      if (value >= 0xa0) {
418         int v = 0;
419         switch (value) {
420            case 0xa0: case 0xa4: case 0xa7: case 0xa8: case 0xad: case 0xb0:
421            case 0xb4: case 0xb8: case 0xc1: case 0xc2: case 0xc4: case 0xc7:
422            case 0xc9: case 0xcb: case 0xcd: case 0xce: case 0xd3: case 0xd4:
423            case 0xd6: case 0xd7: case 0xda: case 0xdc: case 0xdd: case 0xdf:
424            case 0xe1: case 0xe2: case 0xe4: case 0xe7: case 0xe9: case 0xeb:
425            case 0xed: case 0xee: case 0xf3: case 0xf4: case 0xf6: case 0xf7:
426            case 0xfa: case 0xfc: case 0xfd:
427               v = value; break;
428            case 0x104: v = '\xa1'; break;
429            case 0x2d8: v = '\xa2'; break;
430            case 0x141: v = '\xa3'; break;
431            case 0x13d: v = '\xa5'; break;
432            case 0x15a: v = '\xa6'; break;
433            case 0x160: v = '\xa9'; break;
434            case 0x15e: v = '\xaa'; break; /* Scedil */
435            case 0x164: v = '\xab'; break;
436            case 0x179: v = '\xac'; break;
437            case 0x17d: v = '\xae'; break;
438            case 0x17b: v = '\xaf'; break;
439            case 0x105: v = '\xb1'; break;
440            case 0x2db: v = '\xb2'; break;
441            case 0x142: v = '\xb3'; break;
442            case 0x13e: v = '\xb5'; break;
443            case 0x15b: v = '\xb6'; break;
444            case 0x2c7: v = '\xb7'; break;
445            case 0x161: v = '\xb9'; break;
446            case 0x15f: v = '\xba'; break; /* scedil */
447            case 0x165: v = '\xbb'; break;
448            case 0x17a: v = '\xbc'; break;
449            case 0x2dd: v = '\xbd'; break;
450            case 0x17e: v = '\xbe'; break;
451            case 0x17c: v = '\xbf'; break;
452            case 0x154: v = '\xc0'; break;
453            case 0x102: v = '\xc3'; break;
454            case 0x139: v = '\xc5'; break;
455            case 0x106: v = '\xc6'; break;
456            case 0x10c: v = '\xc8'; break;
457            case 0x118: v = '\xca'; break;
458            case 0x11a: v = '\xcc'; break;
459            case 0x10e: v = '\xcf'; break;
460            case 0x110: v = '\xd0'; break;
461            case 0x143: v = '\xd1'; break;
462            case 0x147: v = '\xd2'; break;
463            case 0x150: v = '\xd5'; break;
464            case 0x158: v = '\xd8'; break;
465            case 0x16e: v = '\xd9'; break;
466            case 0x170: v = '\xdb'; break;
467            case 0x162: v = '\xde'; break; /* &Tcedil; */
468            case 0x155: v = '\xe0'; break;
469            case 0x103: v = '\xe3'; break;
470            case 0x13a: v = '\xe5'; break;
471            case 0x107: v = '\xe6'; break;
472            case 0x10d: v = '\xe8'; break;
473            case 0x119: v = '\xea'; break;
474            case 0x11b: v = '\xec'; break;
475            case 0x10f: v = '\xef'; break;
476            case 0x111: v = '\xf0'; break;
477            case 0x144: v = '\xf1'; break;
478            case 0x148: v = '\xf2'; break;
479            case 0x151: v = '\xf5'; break;
480            case 0x159: v = '\xf8'; break;
481            case 0x16f: v = '\xf9'; break;
482            case 0x171: v = '\xfb'; break;
483            case 0x163: v = '\xfe'; break; /* tcedil */
484            case 0x2d9: v = '\xff'; break;
485         }
486         if (v == 0) break;
487         value = v;
488      }
489      *p = value;
490      return 1;
491   case CHARSET_ISO_8859_15:
492      switch (value) {
493       case 0xa4: case 0xa6: case 0xb0: case 0xc4:
494       case 0xd0: case 0xd4: case 0xd5: case 0xd6:
495         goto donthave;
496       case 0x152: value = 0xd4; break; /* &OElig; */
497       case 0x153: value = 0xd5; break; /* &oelig; */
498#if 0
499       case 0x0: value = 0xa4; break; /* euro */
500#endif
501       case 0x160: value = 0xa6; break; /* Scaron */
502       case 0x161: value = 0xb0; break; /* scaron */
503       case 0x17d: value = 0xc4; break; /* Zcaron */
504       case 0x17e: value = 0xd0; break; /* zcaron */
505#if 0
506       case 0x0: value = 0xd6; break; /* Ydiersis */
507#endif
508      }
509      if (value < 0x100) {
510         *p = value;
511         return 1;
512      }
513      donthave:
514      break;
515#if (OS==RISCOS)
516   case CHARSET_RISCOS31:
517      /* RISC OS 3.1 (and later) extensions to ISO-8859-1 */
518      switch (value) {
519       case 0x152: value = 0x9a; break; /* &OElig; */
520       case 0x153: value = 0x9b; break; /* &oelig; */
521#if 0
522       case 0x174: value = 0x81; break; /* &Wcirc; */
523       case 0x175: value = 0x82; break; /* &wcirc; */
524       case 0x176: value = 0x85; break; /* &Ycirc; */
525       case 0x177: value = 0x86; break; /* &ycirc; */
526#endif
527      }
528      if (value < 0x100) {
529         *p = value;
530         return 1;
531      }
532      break;
533#elif (OS==WIN32)
534   case CHARSET_WINCP1250:
535      /* MS Windows rough equivalent to ISO-8859-2 */
536      if (value >= 0x80) {
537         int v = 0;
538         switch (value) {
539            case 0xa0: case 0xa4: case 0xa6: case 0xa7: case 0xa8: case 0xa9:
540            case 0xab: case 0xac: case 0xad: case 0xae: case 0xb0: case 0xb1:
541            case 0xb4: case 0xb5: case 0xb6: case 0xb7: case 0xb8: case 0xbb:
542            case 0xc1: case 0xc2: case 0xc4: case 0xc7: case 0xc9: case 0xcb:
543            case 0xcd: case 0xce: case 0xd3: case 0xd4: case 0xd6: case 0xd7:
544            case 0xda: case 0xdc: case 0xdd: case 0xdf: case 0xe1: case 0xe2:
545            case 0xe4: case 0xe7: case 0xe9: case 0xeb: case 0xed: case 0xee:
546            case 0xf3: case 0xf4: case 0xf6: case 0xf7: case 0xfa: case 0xfc:
547            case 0xfd:
548               v = value; break;
549            case 0x20ac: v = '\x80'; break;
550            case 0x201a: v = '\x82'; break;
551            case 0x201e: v = '\x84'; break;
552            case 0x2026: v = '\x85'; break;
553            case 0x2020: v = '\x86'; break;
554            case 0x2021: v = '\x87'; break;
555            case 0x2030: v = '\x89'; break;
556            case 0x0160: v = '\x8a'; break;
557            case 0x2039: v = '\x8b'; break;
558            case 0x015a: v = '\x8c'; break;
559            case 0x0164: v = '\x8d'; break;
560            case 0x017d: v = '\x8e'; break;
561            case 0x0179: v = '\x8f'; break;
562            case 0x2018: v = '\x91'; break;
563            case 0x2019: v = '\x92'; break;
564            case 0x201c: v = '\x93'; break;
565            case 0x201d: v = '\x94'; break;
566            case 0x2022: v = '\x95'; break;
567            case 0x2013: v = '\x96'; break;
568            case 0x2014: v = '\x97'; break;
569            case 0x2122: v = '\x99'; break;
570            case 0x0161: v = '\x9a'; break;
571            case 0x203a: v = '\x9b'; break;
572            case 0x015b: v = '\x9c'; break;
573            case 0x0165: v = '\x9d'; break;
574            case 0x017e: v = '\x9e'; break;
575            case 0x017a: v = '\x9f'; break;
576            case 0x02c7: v = '\xa1'; break;
577            case 0x02d8: v = '\xa2'; break;
578            case 0x0141: v = '\xa3'; break;
579            case 0x0104: v = '\xa5'; break;
580            case 0x015e: v = '\xaa'; break; /* Scedil */
581            case 0x017b: v = '\xaf'; break;
582            case 0x02db: v = '\xb2'; break;
583            case 0x0142: v = '\xb3'; break;
584            case 0x0105: v = '\xb9'; break;
585            case 0x015f: v = '\xba'; break; /* scedil */
586            case 0x013d: v = '\xbc'; break;
587            case 0x02dd: v = '\xbd'; break;
588            case 0x013e: v = '\xbe'; break;
589            case 0x017c: v = '\xbf'; break;
590            case 0x0154: v = '\xc0'; break;
591            case 0x0102: v = '\xc3'; break;
592            case 0x0139: v = '\xc5'; break;
593            case 0x0106: v = '\xc6'; break;
594            case 0x010c: v = '\xc8'; break;
595            case 0x0118: v = '\xca'; break;
596            case 0x011a: v = '\xcc'; break;
597            case 0x010e: v = '\xcf'; break;
598            case 0x0110: v = '\xd0'; break;
599            case 0x0143: v = '\xd1'; break;
600            case 0x0147: v = '\xd2'; break;
601            case 0x0150: v = '\xd5'; break;
602            case 0x0158: v = '\xd8'; break;
603            case 0x016e: v = '\xd9'; break;
604            case 0x0170: v = '\xdb'; break;
605            case 0x0162: v = '\xde'; break; /* &Tcedil; */
606            case 0x0155: v = '\xe0'; break;
607            case 0x0103: v = '\xe3'; break;
608            case 0x013a: v = '\xe5'; break;
609            case 0x0107: v = '\xe6'; break;
610            case 0x010d: v = '\xe8'; break;
611            case 0x0119: v = '\xea'; break;
612            case 0x011b: v = '\xec'; break;
613            case 0x010f: v = '\xef'; break;
614            case 0x0111: v = '\xf0'; break;
615            case 0x0144: v = '\xf1'; break;
616            case 0x0148: v = '\xf2'; break;
617            case 0x0151: v = '\xf5'; break;
618            case 0x0159: v = '\xf8'; break;
619            case 0x016f: v = '\xf9'; break;
620            case 0x0171: v = '\xfb'; break;
621            case 0x0163: v = '\xfe'; break; /* tcedil */
622            case 0x02d9: v = '\xff'; break;
623         }
624         if (v == 0) break;
625         value = v;
626      }
627      *p = value;
628      return 1;
629   case CHARSET_WINCP1252:
630      /* MS Windows extensions to ISO-8859-1 */
631      switch (value) {
632       case 0x2026: value = 0x85; break; /* hellip */
633       case 0x160: value = 0x8a; break; /* Scaron */
634       case 0x152: value = 0x8c; break; /* OElig */
635       case 0x17d: value = 0x8e; break; /* Zcaron */
636       case 0x161: value = 0x9a; break; /* scaron */
637       case 0x153: value = 0x9c; break; /* oelig */
638       case 0x17e: value = 0x9e; break; /* zcaron */
639#if 0
640      /* there are a few other obscure ones we don't currently need */
641#endif
642      }
643      if (value < 0x100) {
644         *p = value;
645         return 1;
646      }
647      break;
648#endif
649#if (OS==MSDOS)
650   case CHARSET_DOSCP437: {
651      unsigned char uni2dostab[] = {
652          255, 173, 155, 156,   0, 157,   0,   0,
653            0,   0, 166, 174, 170,   0,   0,   0,
654          248, 241, 253,   0,   0, 230,   0, 250,
655            0,   0, 167, 175, 172, 171,   0, 168,
656            0,   0,   0,   0, 142, 143, 146, 128,
657            0, 144,   0,   0,   0,   0,   0,   0,
658            0, 165,   0,   0,   0,   0, 153,   0,
659            0,   0,   0,   0, 154,   0,   0, 225,
660          133, 160, 131,   0, 132, 134, 145, 135,
661          138, 130, 136, 137, 141, 161, 140, 139,
662            0, 164, 149, 162, 147,   0, 148, 246,
663            0, 151, 163, 150, 129,   0,   0, 152,
664      };
665      if (value >= 160 && value < 256) {
666         int ch = (int)uni2dostab[value - 160];
667         if (!ch) break;
668         *p = ch;
669         return 1;
670      }
671#if 0
672      switch (value) {
673          case 8359: *p = 158; return 1; /* PESETA SIGN */
674          case 402: *p = 159; return 1; /* LATIN SMALL LETTER F WITH HOOK */
675          case 8976: *p = 169; return 1; /* REVERSED NOT SIGN */
676          case 945: *p = 224; return 1; /* GREEK SMALL LETTER ALPHA */
677          case 915: *p = 226; return 1; /* GREEK CAPITAL LETTER GAMMA */
678          case 960: *p = 227; return 1; /* GREEK SMALL LETTER PI */
679          case 931: *p = 228; return 1; /* GREEK CAPITAL LETTER SIGMA */
680          case 963: *p = 229; return 1; /* GREEK SMALL LETTER SIGMA */
681          case 964: *p = 231; return 1; /* GREEK SMALL LETTER TAU */
682          case 934: *p = 232; return 1; /* GREEK CAPITAL LETTER PHI */
683          case 920: *p = 233; return 1; /* GREEK CAPITAL LETTER THETA */
684          case 937: *p = 234; return 1; /* GREEK CAPITAL LETTER OMEGA */
685          case 948: *p = 235; return 1; /* GREEK SMALL LETTER DELTA */
686          case 8734: *p = 236; return 1; /* INFINITY */
687          case 966: *p = 237; return 1; /* GREEK SMALL LETTER PHI */
688          case 949: *p = 238; return 1; /* GREEK SMALL LETTER EPSILON */
689          case 8745: *p = 239; return 1; /* INTERSECTION */
690          case 8801: *p = 240; return 1; /* IDENTICAL TO */
691          case 8805: *p = 242; return 1; /* GREATER-THAN OR EQUAL TO */
692          case 8804: *p = 243; return 1; /* LESS-THAN OR EQUAL TO */
693          case 8992: *p = 244; return 1; /* TOP HALF INTEGRAL */
694          case 8993: *p = 245; return 1; /* BOTTOM HALF INTEGRAL */
695          case 8776: *p = 247; return 1; /* ALMOST EQUAL TO */
696          case 8729: *p = 249; return 1; /* BULLET OPERATOR */
697          case 8730: *p = 251; return 1; /* SQUARE ROOT */
698          case 8319: *p = 252; return 1; /* SUPERSCRIPT LATIN SMALL LETTER N */
699          case 9632: *p = 254; return 1; /* BLACK SQUARE */
700      }
701#endif
702      break;
703   }
704#endif
705#if (OS==MSDOS || OS==WIN32)
706   case CHARSET_DOSCP850: {
707      unsigned char uni2dostab[] = {
708         255, 173, 189, 156, 207, 190, 221, 245,
709         249, 184, 166, 174, 170, 240, 169, 238,
710         248, 241, 253, 252, 239, 230, 244, 250,
711         247, 251, 167, 175, 172, 171, 243, 168,
712         183, 181, 182, 199, 142, 143, 146, 128,
713         212, 144, 210, 211, 222, 214, 215, 216,
714         209, 165, 227, 224, 226, 229, 153, 158,
715         157, 235, 233, 234, 154, 237, 232, 225,
716         133, 160, 131, 198, 132, 134, 145, 135,
717         138, 130, 136, 137, 141, 161, 140, 139,
718         208, 164, 149, 162, 147, 228, 148, 246,
719         155, 151, 163, 150, 129, 236, 231, 152
720      };
721      if (value >= 160 && value < 256) {
722         *p = (int)uni2dostab[value - 160];
723         return 1;
724      }
725#if 0
726      if (value == 305) { /* LATIN SMALL LETTER DOTLESS I */
727         *p = 213;
728         return 1;
729      }
730      if (value == 402) { /* LATIN SMALL LETTER F WITH HOOK */
731         *p = 159;
732         return 1;
733      }
734#endif
735      break;
736   }
737#endif
738   }
739   /* Transliterate characters we can't represent */
740#ifdef DEBUG
741   fprintf(stderr, "transliterate `%c' 0x%x\n", value, value);
742#endif
743   switch (value) {
744    case 160:
745      *p = ' '; return 1;
746    case 161 /* ¡ */:
747      *p = '!'; return 1;
748    case 171 /* « */:
749      p[1] = *p = '<'; return 2;
750    case 187 /* » */:
751      p[1] = *p = '>'; return 2;
752    case 191 /* ¿ */:
753      *p = '?'; return 1;
754    case 192 /* À */: case 193 /* Á */: case 194 /* Â */: case 195 /* Ã */:
755      *p = 'A'; return 1;
756    case 197 /* Å */:
757      p[1] = *p = 'A'; return 2;
758    case 196 /* Ä */: /* &Auml; */
759      *p = 'A';
760      if (!umlaut_to_e()) return 1;
761      p[1] = 'E'; return 2;
762    case 198 /* Æ */:
763      *p = 'A'; p[1] = 'E'; return 2;
764    case 199 /* Ç */: case 268: /* &Ccaron; */
765      *p = 'C'; return 1;
766    case 270: /* &Dcaron; */
767      *p = 'D'; return 1;
768    case 200 /* È */: case 201 /* É */: case 202 /* Ê */: case 203 /* Ë */:
769      *p = 'E'; return 1;
770    case 204 /* Ì */: case 205 /* Í */: case 206 /* Î */: case 207 /* Ï */:
771      *p = 'I'; return 1;
772    case 208 /* Ð */: case 222 /* Þ */:
773      *p = 'T'; p[1] = 'H'; return 2;
774    case 315: /* &Lacute; */
775    case 317: /* &Lcaron; */
776      *p = 'L'; return 1;
777    case 209 /* Ñ */:
778      *p = 'N'; return 1;
779    case 210 /* Ò */: case 211 /* Ó */: case 212 /* Ô */: case 213 /* Õ */:
780      *p = 'O'; return 1;
781    case 214 /* Ö */: /* &Ouml; */ case 0x152: /* &OElig; */
782      *p = 'O'; p[1] = 'E'; return 2;
783    case 352: /* &Scaron; */
784    case 0x15e: /* &Scedil; */
785      *p = 'S'; return 1;
786    case 0x162: /* &Tcedil; */
787    case 0x164: /* &Tcaron; */
788      *p = 'T'; return 1;
789    case 217 /* Ù */: case 218 /* Ú */: case 219 /* Û */:
790      *p = 'U'; return 1;
791    case 220 /* Ü */: /* &Uuml; */
792      *p = 'U'; p[1] = 'E'; return 2;
793    case 221 /* Ý */:
794      *p = 'Y'; return 1;
795    case 381: /* &Zcaron; */
796      *p = 'Z'; return 1;
797    case 223 /* ß */:
798      p[1] = *p = 's'; return 2;
799    case 224 /* à */: case 225 /* á */: case 226 /* â */: case 227 /* ã */:
800    case 259: /* &abreve; */
801      *p = 'a'; return 1;
802    case 228 /* ä */: /* &auml; */ case 230 /* æ */:
803      *p = 'a'; p[1] = 'e'; return 2;
804    case 229 /* å */:
805      p[1] = *p = 'a'; return 2;
806    case 231 /* ç */: case 269 /* &ccaron; */:
807      *p = 'c'; return 1;
808    case 271: /* &dcaron; */
809      *p = 'd'; return 1;
810    case 232 /* è */: case 233 /* é */: case 234 /* ê */: case 235 /* ë */:
811    case 283 /* &ecaron; */:
812      *p = 'e'; return 1;
813    case 236 /* ì */: case 237 /* í */: case 238 /* î */: case 239 /* ï */:
814      *p = 'i'; return 1;
815    case 316 /* &lacute; */:
816    case 318 /* &lcaron; */:
817      *p = 'l'; return 1;
818    case 241 /* ñ */: case 328 /* &ncaron; */:
819      *p = 'n'; return 1;
820    case 345: /* &rcaron; */
821      *p = 'r'; return 1;
822    case 353: /* &scaron; */
823    case 0x15f: /* &scedil; */
824      *p = 's'; return 1;
825    case 357: /* &tcaron; */
826    case 0x163: /* &tcedil; */
827      *p = 't'; return 1;
828    case 240 /* ð */: case 254 /* þ */:
829      *p = 't'; p[1] = 'h'; return 2;
830    case 242 /* ò */: case 243 /* ó */: case 244 /* ô */: case 245 /* õ */:
831      *p = 'o'; return 1;
832    case 246 /* ö */: /* &ouml; */ case 0x153: /* &oelig; */
833      *p = 'o'; p[1] = 'e'; return 2;
834    case 249 /* ù */: case 250 /* ú */: case 251 /* û */:
835    case 367 /* &uring; */:
836      *p = 'u'; return 1;
837    case 252 /* ü */: /* &uuml; */
838      *p = 'u'; p[1] = 'e'; return 2;
839    case 253 /* ý */: case 255 /* ÿ */:
840      *p = 'y'; return 1;
841    case 382: /* &zcaron; */
842      *p = 'z'; return 1;
843    case 0x2026: /* &hellip; */
844      *p = '.'; p[1] = '.'; p[2] = '.'; return 3;
845    case 0x2192: /* &rarr; */
846      *p = '-'; p[1] = '>'; return 2;
847   }
848#ifdef DEBUG
849   fprintf(stderr, "failed to transliterate\n");
850#endif
851   return 0;
852}
853
854#if (OS==UNIX) && defined(DATADIR) && defined(PACKAGE)
855/* Under Unix, we compile in the configured path */
856static const char *pth_cfg_files = DATADIR "/" PACKAGE;
857#else
858/* On other platforms, we fall back on looking in the current directory */
859static const char *pth_cfg_files = "";
860#endif
861
862static int num_msgs = 0;
863static char **msg_array = NULL;
864
865const char *msg_lang = NULL;
866const char *msg_lang2 = NULL;
867
868static char **
869parse_msgs(int n, unsigned char *p, int charset_code) {
870   int i;
871
872   char **msgs = osmalloc(n * sizeof(char *));
873
874   for (i = 0; i < n; i++) {
875      unsigned char *to = p;
876      int ch;
877      msgs[i] = (char *)p;
878
879      /* If we want UTF8 anyway, we just need to find the start of each
880       * message */
881      if (charset_code == CHARSET_UTF8) {
882         p += strlen((char *)p) + 1;
883         continue;
884      }
885
886      while ((ch = *p++) != 0) {
887         /* A byte in the range 0x80-0xbf or 0xf0-0xff isn't valid in
888          * this state, (0xf0-0xfd mean values > 0xffff) so treat as
889          * literal and try to resync so we cope better when fed
890          * non-utf-8 data.  Similarly we abandon a multibyte sequence
891          * if we hit an invalid character. */
892         if (ch >= 0xc0 && ch < 0xf0) {
893            int ch1 = *p;
894            if ((ch1 & 0xc0) != 0x80) goto resync;
895
896            if (ch < 0xe0) {
897               /* 2 byte sequence */
898               ch = ((ch & 0x1f) << 6) | (ch1 & 0x3f);
899               p++;
900            } else {
901               /* 3 byte sequence */
902               int ch2 = p[1];
903               if ((ch2 & 0xc0) != 0x80) goto resync;
904               ch = ((ch & 0x1f) << 12) | ((ch1 & 0x3f) << 6) | (ch2 & 0x3f);
905               p += 2;
906            }
907         }
908
909         resync:
910
911         if (ch < 127) {
912            *to++ = (char)ch;
913         } else {
914            /* We assume an N byte UTF-8 code never transliterates to more
915             * than N characters (so we can't transliterate © to (C) or
916             * ® to (R) for example) */
917            to += add_unicode(charset_code, to, ch);
918         }
919      }
920      *to++ = '\0';
921   }
922   return msgs;
923}
924
925/* This is the name of the default language, which can be set like so:
926 * ./configure --enable-defaultlang=fr
927 */
928#ifdef DEFAULTLANG
929/* No point extracting these errors as they won't get used if file opens */
930# include "../lib/defaultlang.h"
931#else
932#define N_DONTEXTRACTMSGS 5
933static unsigned char dontextractmsgs[] =
934   "Can't open message file `%s' using path `%s'\0"/*1000*/
935   "Problem with message file `%s'\0"/*1001*/
936   "I don't understand this message file version\0"/*1002*/
937   "Message file truncated?\0"/*1003*/
938   "Out of memory (couldn't find %lu bytes).\0"/*1004*/;
939#endif
940
941static char **dontextract = NULL;
942
943static void
944parse_msg_file(int charset_code)
945{
946   FILE *fh;
947   unsigned char header[20];
948   int i;
949   unsigned len;
950   unsigned char *p;
951   char *fnm, *s;
952   int n;
953
954#ifdef DEBUG
955   fprintf(stderr, "parse_msg_file(%d)\n", charset_code);
956#endif
957
958   /* sort out messages we need to print if we can't open the message file */
959   dontextract = parse_msgs(N_DONTEXTRACTMSGS, dontextractmsgs, charset_code);
960
961   fnm = osstrdup(msg_lang);
962   /* trim off charset from stuff like "de_DE.iso8859_1" */
963   s = strchr(fnm, '.');
964   if (s) *s = '\0';
965
966   fh = fopenWithPthAndExt(pth_cfg_files, fnm, EXT_SVX_MSG, "rb", NULL);
967
968   if (!fh) {
969      /* e.g. if 'en_GB' is unknown, see if we know 'en' */
970      if (strlen(fnm) > 3 && fnm[2] == '_') {
971         fnm[2] = '\0';
972         fh = fopenWithPthAndExt(pth_cfg_files, fnm, EXT_SVX_MSG, "rb", NULL);
973         if (!fh) fnm[2] = '_'; /* for error reporting */
974      }
975   }
976
977   if (!fh) {
978      fatalerror(/*Can't open message file `%s' using path `%s'*/1000,
979                 fnm, pth_cfg_files);
980   }
981
982   if (fread(header, 1, 20, fh) < 20 ||
983       memcmp(header, "Svx\nMsg\r\n\xfe\xff", 12) != 0) {
984      fatalerror(/*Problem with message file `%s'*/1001, fnm);
985   }
986
987   if (header[12] != 0)
988      fatalerror(/*I don't understand this message file version*/1002);
989
990   n = (header[14] << 8) | header[15];
991
992   len = 0;
993   for (i = 16; i < 20; i++) len = (len << 8) | header[i];
994
995   p = osmalloc(len);
996   if (fread(p, 1, len, fh) < len)
997      fatalerror(/*Message file truncated?*/1003);
998
999   fclose(fh);
1000
1001#ifdef DEBUG
1002   fprintf(stderr, "fnm = `%s', n = %d, len = %d\n", fnm, n, len);
1003#endif
1004   osfree(fnm);
1005
1006   msg_array = parse_msgs(n, p, charset_code);
1007   num_msgs = n;
1008}
1009
1010const char *
1011msg_cfgpth(void)
1012{
1013   return pth_cfg_files;
1014}
1015
1016const char *
1017msg_appname(void)
1018{
1019   return appname_copy;
1020}
1021
1022void
1023msg_init(char * const *argv)
1024{
1025   char *p;
1026   SVX_ASSERT(argv);
1027
1028#ifdef HAVE_SIGNAL
1029   init_signals();
1030#endif
1031   /* Point to argv[0] itself so we report a more helpful error if the
1032    * code to work out the clean appname generates a signal */
1033   appname_copy = argv[0];
1034#if (OS == UNIX)
1035   /* use name as-is on Unix - programs run from path get name as supplied */
1036   appname_copy = osstrdup(argv[0]);
1037#else
1038   /* use the lower-cased leafname on other platforms */
1039   p = leaf_from_fnm(argv[0]);
1040   appname_copy = p;
1041   while (*p) {
1042      *p = tolower(*p);
1043      ++p;
1044   }
1045#endif
1046
1047   /* shortcut --version so you can check the version number even when the
1048    * correct message file can't be found... */
1049   if (argv[1] && strcmp(argv[1], "--version") == 0) {
1050      cmdline_version();
1051      exit(0);
1052   }
1053   if (argv[0]) {
1054#ifdef MACOSX_BUNDLE
1055      /* If we're being built into a bundle, always look relative to
1056       * the path to the binary. */
1057      char * pth = path_from_fnm(argv[0]);
1058      pth_cfg_files = use_path(pth, "share/survex");
1059      osfree(pth);
1060#elif (OS==UNIX) && defined(DATADIR) && defined(PACKAGE)
1061      bool free_pth = fFalse;
1062      char *pth = getenv("srcdir");
1063      if (!pth || !pth[0]) {
1064         pth = path_from_fnm(argv[0]);
1065         free_pth = fTrue;
1066      }
1067      if (pth[0]) {
1068         struct stat buf;
1069#if defined(__GNUC__) && defined(__APPLE_CC__)
1070         /* On MacOS X the programs may be installed anywhere, with the
1071          * share directory and the binaries in the same directory. */
1072         p = use_path(pth, "share/survex/en.msg");
1073         if (lstat(p, &buf) == 0 && S_ISREG(buf.st_mode)) {
1074            pth_cfg_files = use_path(pth, "share/survex");
1075            goto macosx_got_msg;
1076         }
1077         osfree(p);
1078#endif
1079         /* If we're run with an explicit path, check if "../lib/en.msg"
1080          * from the program's path exists, and if so look there for
1081          * support files - this allows us to test binaries in the build
1082          * tree easily. */
1083         p = use_path(pth, "../lib/en.msg");
1084         if (lstat(p, &buf) == 0) {
1085#ifdef S_ISDIR
1086            /* POSIX way */
1087            if (S_ISREG(buf.st_mode)) {
1088               pth_cfg_files = use_path(pth, "../lib");
1089            }
1090#else
1091            /* BSD way */
1092            if ((buf.st_mode & S_IFMT) == S_IFREG) {
1093               pth_cfg_files = use_path(pth, "../lib");
1094            }
1095#endif
1096         }
1097#if defined(__GNUC__) && defined(__APPLE_CC__)
1098macosx_got_msg:
1099#endif
1100         osfree(p);
1101      }
1102
1103      if (free_pth) osfree(pth);
1104#elif (OS==WIN32)
1105      DWORD len = 256;
1106      char *buf = NULL, *modname;
1107      while (1) {
1108          DWORD got;
1109          buf = osrealloc(buf, len);
1110          got = GetModuleFileName(NULL, buf, len);
1111          if (got < len) break;
1112          len += len;
1113      }
1114      modname = buf;
1115      /* Strange Win32 nastiness - strip prefix "\\?\" if present */
1116      if (strncmp(modname, "\\\\?\\", 4) == 0) modname += 4;
1117      pth_cfg_files = path_from_fnm(modname);
1118      osfree(buf);
1119#else
1120      /* Get the path to the support files from argv[0] */
1121      pth_cfg_files = path_from_fnm(argv[0]);
1122#endif
1123   }
1124
1125   msg_lang = getenv("SURVEXLANG");
1126#ifdef DEBUG
1127   fprintf(stderr, "msg_lang = %p (= \"%s\")\n", msg_lang, msg_lang?msg_lang:"(null)");
1128#endif
1129
1130   if (!msg_lang || !*msg_lang) {
1131      msg_lang = getenv("LC_MESSAGES");
1132      if (!msg_lang || !*msg_lang) {
1133         msg_lang = getenv("LANG");
1134         /* Something (AutoCAD?) on Microsoft Windows sets LANG to a number. */
1135         if (msg_lang && !isalpha(msg_lang[0])) msg_lang = NULL;
1136      }
1137      if (!msg_lang || !*msg_lang) {
1138#if (OS==WIN32)
1139         LCID locid;
1140#elif (OS==RISCOS)
1141         territory_t t;
1142#endif
1143#ifdef DEFAULTLANG
1144         msg_lang = STRING(DEFAULTLANG);
1145#else
1146         msg_lang = "en";
1147#endif
1148#if (OS==WIN32)
1149         locid = GetUserDefaultLCID();
1150         if (locid) {
1151            WORD langid = LANGIDFROMLCID(locid);
1152            switch (PRIMARYLANGID(langid)) {
1153/* older mingw compilers don't seem to supply this value */
1154#ifndef LANG_CATALAN
1155# define LANG_CATALAN 0x03
1156#endif
1157             case LANG_CATALAN:
1158               msg_lang = "ca";
1159               break;
1160             case LANG_CHINESE:
1161               msg_lang = "zh";
1162               break;
1163             case LANG_ENGLISH:
1164               if (SUBLANGID(langid) == SUBLANG_ENGLISH_US)
1165                  msg_lang = "en_US";
1166               else
1167                  msg_lang = "en";
1168               break;
1169             case LANG_FRENCH:
1170               msg_lang = "fr";
1171               break;
1172             case LANG_GERMAN:
1173               switch (SUBLANGID(langid)) {
1174                case SUBLANG_GERMAN_SWISS:
1175                  msg_lang = "de_CH";
1176                  break;
1177                case SUBLANG_GERMAN:
1178                  msg_lang = "de_DE";
1179                  break;
1180                default:
1181                  msg_lang = "de";
1182               }
1183               break;
1184             case LANG_ITALIAN:
1185               msg_lang = "it";
1186               break;
1187             case LANG_PORTUGUESE:
1188               if (SUBLANGID(langid) == SUBLANG_PORTUGUESE_BRAZILIAN)
1189                  msg_lang = "pt_BR";
1190               else
1191                  msg_lang = "pt";
1192               break;
1193             case LANG_ROMANIAN:
1194               msg_lang = "ro";
1195               break;
1196             case LANG_SLOVAK:
1197               msg_lang = "sk";
1198               break;
1199             case LANG_SPANISH:
1200               msg_lang = "es";
1201               break;
1202            }
1203         }
1204#elif (OS==RISCOS)
1205         if (!xterritory_number(&t)) switch (t) {
1206          case 1: /* UK */
1207          case 2: /* Master */
1208          case 3: /* Compact */
1209          case 17: /* Canada1 */
1210          case 19: /* Canada */
1211          case 22: /* Ireland */
1212            msg_lang = "en";
1213            break;
1214          case 4: /* Italy */
1215            msg_lang = "it";
1216            break;
1217          case 5: /* Spain (or ca) */
1218          case 27: /* Mexico */
1219          case 28: /* LatinAm (or pt_BR) */
1220            msg_lang = "es";
1221            break;
1222          case 6: /* France */
1223          case 18: /* Canada2 */
1224            msg_lang = "fr";
1225            break;
1226          case 7: /* Germany */
1227            msg_lang = "de_DE";
1228            break;
1229          case 8: /* Portugal */
1230            msg_lang = "pt";
1231            break;
1232          case 23: /* Hong Kong */
1233            msg_lang = "zh";
1234            break;
1235          case 48: /* USA */
1236            msg_lang = "en_US";
1237            break;
1238#if 0
1239          case 9: /* Esperanto */
1240          case 10: /* Greece */
1241          case 11: /* Sweden */
1242          case 12: /* Finland */
1243          case 13: /* Unused */
1244          case 14: /* Denmark */
1245          case 15: /* Norway */
1246          case 16: /* Iceland */
1247          case 20: /* Turkey */
1248          case 21: /* Arabic */
1249          case 24: /* Russia */
1250          case 25: /* Russia2 */
1251          case 26: /* Israel */
1252#endif
1253         }
1254#elif (OS==MSDOS)
1255           {
1256              int country_code;
1257# ifdef __DJGPP__
1258              __dpmi_regs r;
1259              r.x.ax = 0x6501;
1260              r.x.bx = 0xffff;
1261              r.x.dx = 0xffff;
1262              /* Use DJGPP's transfer buffer (which is at least 2K) */
1263              r.x.es = __tb >> 4;
1264              r.x.di = __tb & 0x0f;
1265              r.x.cx = 2048;
1266              /* bit 1 is the carry flag */
1267              if (__dpmi_int(0x21, &r) != -1 && !(r.x.flags & 1)) {
1268                 unsigned short val;
1269                 dosmemget(__tb + 3, 2, &val);
1270                 country_code = val;
1271# else
1272              union REGS r;
1273              r.x.ax = 0x3800; /* get current country info */
1274              r.x.dx = 0;
1275              intdos(&r, &r);
1276              if (!r.x.cflag) {
1277                 country_code = r.x.bx;
1278# endif
1279                 /* List of country codes taken from:
1280                  * http://www.delorie.com/djgpp/doc/rbinter/it/00/14.html */
1281                 /* The mappings here are guesses at best in most cases.
1282                  * In a lot of cases we pick a language because we have
1283                  * a translation in it, rather than because it's the most
1284                  * widely used or understood in that country. */
1285                 /* Improvements welcome */
1286                 switch (country_code) {
1287                     case 1: /* United States */
1288                     case 670: /* Saipan / N. Mariana Island */
1289                     case 671: /* Guam */
1290                     case 680: /* Palau */
1291                     case 684: /* American Samoa */
1292                     case 691: /* Micronesia */
1293                     case 692: /* Marshall Islands */
1294                         msg_lang = "en_US";
1295                         break;
1296                     case 4: /* Canada (English) */
1297                     case 27: /* South Africa */
1298                     case 44: /* United Kingdom */
1299                     case 61: /* International English / Australia */
1300                     case 64: /* New Zealand */
1301                     case 99: /* Asia (English) */
1302                     case 220: /* Gambia */
1303                     case 231: /* Liberia */
1304                     case 232: /* Sierra Leone */
1305                     case 233: /* Ghana */
1306                     case 254: /* Kenya */
1307                     case 256: /* Uganda */
1308                     case 260: /* Zambia */
1309                     case 263: /* Zimbabwe */
1310                     case 264: /* Namibia */
1311                     case 267: /* Botswana */
1312                     case 268: /* Swaziland */
1313                     case 290: /* St. Helena */
1314                     case 297: /* Aruba */
1315                     case 350: /* Gibraltar */
1316                     case 353: /* Ireland */
1317                     case 356: /* Malta */
1318                     case 500: /* Falkland Islands */
1319                     case 501: /* Belize */
1320                     case 592: /* Guyana */
1321                     case 672: /* Norfolk Island (Australia) / Christmas Island/Cocos Islands / Antartica */
1322                     case 673: /* Brunei Darussalam */
1323                     case 674: /* Nauru */
1324                     case 675: /* Papua New Guinea */
1325                     case 676: /* Tonga Islands */
1326                     case 677: /* Solomon Islands */
1327                     case 679: /* Fiji */
1328                     case 682: /* Cook Islands */
1329                     case 683: /* Niue */
1330                     case 685: /* Western Samoa */
1331                     case 686: /* Kiribati */
1332                         /* I believe only some of these are English speaking... */
1333                     case 809: /* Antigua and Barbuda / Anguilla / Bahamas / Barbados / Bermuda
1334                                  British Virgin Islands / Cayman Islands / Dominica
1335                                  Dominican Republic / Grenada / Jamaica / Montserra
1336                                  St. Kitts and Nevis / St. Lucia / St. Vincent and Grenadines
1337                                  Trinidad and Tobago / Turks and Caicos */
1338                         msg_lang = "en";
1339                         break;
1340                     case 2: /* Canadian-French */
1341                     case 32: /* Belgium */ /* maybe */
1342                     case 33: /* France */
1343                     case 213: /* Algeria */
1344                     case 216: /* Tunisia */
1345                     case 221: /* Senegal */
1346                     case 223: /* Mali */
1347                     case 225: /* Ivory Coast */
1348                     case 226: /* Burkina Faso */
1349                     case 227: /* Niger */
1350                     case 228: /* Togo */
1351                     case 229: /* Benin */
1352                     case 230: /* Mauritius */
1353                     case 235: /* Chad */
1354                     case 236: /* Central African Republic */
1355                     case 237: /* Cameroon */
1356                     case 241: /* Gabon */
1357                     case 242: /* Congo */
1358                     case 250: /* Rwhanda */
1359                     case 253: /* Djibouti */
1360                     case 257: /* Burundi */
1361                     case 261: /* Madagascar */
1362                     case 262: /* Reunion Island */
1363                     case 269: /* Comoros */
1364                     case 270: /* Mayotte */
1365                     case 352: /* Luxembourg (or de or ...) */
1366                     case 508: /* St. Pierre and Miquelon */
1367                     case 509: /* Haiti */
1368                     case 590: /* Guadeloupe */
1369                     case 594: /* French Guiana */
1370                     case 596: /* Martinique / French Antilles */
1371                     case 678: /* Vanuatu */
1372                     case 681: /* Wallis & Futuna */
1373                     case 687: /* New Caledonia */
1374                     case 689: /* French Polynesia */
1375                     case 961: /* Lebanon */
1376                         msg_lang = "fr";
1377                         break;
1378                     case 3: /* Latin America */
1379                     case 34: /* Spain */
1380                     case 51: /* Peru */
1381                     case 52: /* Mexico */
1382                     case 53: /* Cuba */
1383                     case 54: /* Argentina */
1384                     case 56: /* Chile */
1385                     case 57: /* Columbia */
1386                     case 58: /* Venezuela */
1387                     case 63: /* Philippines */
1388                     case 240: /* Equatorial Guinea */
1389                     case 502: /* Guatemala */
1390                     case 503: /* El Salvador */
1391                     case 504: /* Honduras */
1392                     case 505: /* Nicraragua */
1393                     case 506: /* Costa Rica */
1394                     case 507: /* Panama */
1395                     case 591: /* Bolivia */
1396                     case 593: /* Ecuador */
1397                     case 595: /* Paraguay */
1398                     case 598: /* Uruguay */
1399                         msg_lang = "es";
1400                         break;
1401                     case 39: /* Italy / San Marino / Vatican City */
1402                         msg_lang = "it";
1403                         break;
1404                     case 41: /* Switzerland / Liechtenstein */ /* or fr or ... */
1405                         msg_lang = "de_CH";
1406                         break;
1407                     case 43: /* Austria (DR DOS 5.0) */
1408                         msg_lang = "de";
1409                         break;
1410                     case 49: /* Germany */
1411                         msg_lang = "de_DE";
1412                         break;
1413                     case 55: /* Brazil (not supported by DR DOS 5.0) */
1414                         msg_lang = "pt_BR";
1415                         break;
1416                     case 238: /* Cape Verde Islands */
1417                     case 244: /* Angola */
1418                     case 245: /* Guinea-Bissau */
1419                     case 259: /* Mozambique */
1420                     case 351: /* Portugal */
1421                         msg_lang = "pt";
1422                         break;
1423                     case 42: /* Czechoslovakia / Tjekia / Slovakia (not supported by DR DOS 5.0) */
1424                     case 421: /* Czech Republic / Tjekia (PC DOS 7+) */
1425                     case 422: /* Slovakia (reported as 421 due to a bug in COUNTRY.SYS) */
1426                         msg_lang = "sk";
1427                         break;
1428                     case 65: /* Singapore */
1429                     case 86: /* China (MS-DOS 5.0+) */
1430                     case 88: /* Taiwan (MS-DOS 5.0+) */
1431                     case 852: /* Hong Kong */
1432                     case 853: /* Macao */
1433                     case 886: /* Taiwan (MS-DOS 6.22+) */
1434                         msg_lang = "zh";
1435                         break;
1436#if 0
1437                     case 7: /* Russia */
1438                     case 20: /* Egypt */
1439                     case 30: /* Greece */
1440                     case 31: /* Netherlands */
1441                     case 35: /* Bulgaria??? */
1442                     case 36: /* Hungary (not supported by DR DOS 5.0) */
1443                     case 38: /* Yugoslavia (not supported by DR DOS 5.0) -- obsolete */
1444#endif
1445                     case 40: /* Romania */
1446                         msg_lang = "ro";
1447                         break;
1448#if 0
1449                     case 45: /* Denmark */
1450                     case 46: /* Sweden */
1451                     case 47: /* Norway */
1452                     case 48: /* Poland (not supported by DR DOS 5.0) */
1453                     case 60: /* Malaysia */
1454                     case 62: /* Indonesia / East Timor */
1455                     case 66: /* Thailand (or Taiwan??? ) */
1456                     case 81: /* Japan (DR DOS 5.0, MS-DOS 5.0+) */
1457                     case 82: /* South Korea (DR DOS 5.0) */
1458                     case 84: /* Vietnam */
1459                     case 90: /* Turkey (MS-DOS 5.0+) */
1460                     case 91: /* India */
1461                     case 92: /* Pakistan */
1462                     case 93: /* Afghanistan */
1463                     case 94: /* Sri Lanka */
1464                     case 98: /* Iran */
1465                     case 102: /* ??? (Hebrew MS-DOS 5.0) */
1466                     case 112: /* Belarus */
1467                     case 200: /* Thailand (PC DOS 6.1+) (reported as 01due to a bug in PC DOS COUNTRY.SYS) */
1468                     case 212: /* Morocco */
1469                     case 218: /* Libya */
1470                     case 222: /* Maruitania */
1471                     case 224: /* African Guinea */
1472                     case 234: /* Nigeria */
1473                     case 239: /* Sao Tome and Principe */
1474                     case 243: /* Zaire */
1475                     case 246: /* Diego Garcia */
1476                     case 247: /* Ascension Isle */
1477                     case 248: /* Seychelles */
1478                     case 249: /* Sudan */
1479                     case 251: /* Ethiopia */
1480                     case 252: /* Somalia */
1481                     case 255: /* Tanzania */
1482                     case 265: /* Malawi */
1483                     case 266: /* Lesotho */
1484                     case 298: /* Faroe Islands */
1485                     case 299: /* Greenland */
1486                     case 354: /* Iceland */
1487                     case 355: /* Albania */
1488                     case 357: /* Cyprus */
1489                     case 358: /* Finland */
1490                     case 359: /* Bulgaria */
1491                     case 370: /* Lithuania (reported as 372 due to a bug in MS-DOS COUNTRY.SYS) */
1492                     case 371: /* Latvia (reported as 372 due to a bug in MS-DOS COUNTRY.SYS) */
1493                     case 372: /* Estonia */
1494                     case 373: /* Moldova */ /* FIXME: similar to Romanian? */
1495                     case 375: /* ??? (MS-DOS 7.10 / Windows98) */
1496                     case 380: /* Ukraine */
1497                     case 381: /* Serbia / Montenegro */
1498                     case 384: /* Croatia */
1499                     case 385: /* Croatia (PC DOS 7+) */
1500                     case 386: /* Slovenia */
1501                     case 387: /* Bosnia-Herzegovina (Latin) */
1502                     case 388: /* Bosnia-Herzegovina (Cyrillic) (PC DOS 7+) (reported as 381 due to a bug in PC DOS COUNTRY.SYS) */
1503                     case 389: /* FYR Macedonia */
1504                     case 597: /* Suriname (nl) */
1505                     case 599: /* Netherland Antilles (nl) */
1506                     case 666: /* Russia??? (PTS-DOS 6.51 KEYB) */
1507                     case 667: /* Poland??? (PTS-DOS 6.51 KEYB) */
1508                     case 668: /* Poland??? (Slavic??? ) (PTS-DOS 6.51 KEYB) */
1509                     case 688: /* Tuvalu */
1510                     case 690: /* Tokealu */
1511                     case 711: /* ??? (currency = EA$, code pages 437,737,850,852,855,857) */
1512                     case 785: /* Arabic (Middle East/Saudi Arabia/etc.) */
1513                     case 804: /* Ukraine */
1514                     case 850: /* North Korea */
1515                     case 855: /* Cambodia */
1516                     case 856: /* Laos */
1517                     case 880: /* Bangladesh */
1518                     case 960: /* Maldives */
1519                     case 962: /* Jordan */
1520                     case 963: /* Syria / Syrian Arab Republic */
1521                     case 964: /* Iraq */
1522                     case 965: /* Kuwait */
1523                     case 966: /* Saudi Arabia */
1524                     case 967: /* Yemen */
1525                     case 968: /* Oman */
1526                     case 969: /* Yemen??? (Arabic MS-DOS 5.0) */
1527                     case 971: /* United Arab Emirates */
1528                     case 972: /* Israel (Hebrew) (DR DOS 5.0,MS-DOS 5.0+) */
1529                     case 973: /* Bahrain */
1530                     case 974: /* Qatar */
1531                     case 975: /* Bhutan */
1532                     case 976: /* Mongolia */
1533                     case 977: /* Nepal */
1534                     case 995: /* Myanmar (Burma) */
1535#endif
1536                 }
1537              }
1538           }
1539#endif
1540      }
1541   }
1542#ifdef DEBUG
1543   fprintf(stderr, "msg_lang = %p (= \"%s\")\n", msg_lang, msg_lang?msg_lang:"(null)");
1544#endif
1545
1546   /* On Mandrake LANG defaults to C */
1547   if (strcmp(msg_lang, "C") == 0) msg_lang = "en";
1548
1549   msg_lang = osstrdup(msg_lang);
1550
1551   /* Convert en-us to en_US, etc */
1552   p = strchr(msg_lang, '-');
1553   if (p) {
1554      *p++ = '_';
1555      while (*p) {
1556         *p = toupper(*p);
1557         p++;
1558      }
1559   }
1560
1561   p = strchr(msg_lang, '_');
1562   if (p) {
1563      *p = '\0';
1564      msg_lang2 = osstrdup(msg_lang);
1565      *p = '_';
1566   }
1567
1568#ifdef LC_MESSAGES
1569   /* try to setlocale() appropriately too */
1570   if (!setlocale(LC_MESSAGES, msg_lang)) {
1571      if (msg_lang2) setlocale(LC_MESSAGES, msg_lang2);
1572   }
1573#endif
1574
1575   select_charset(default_charset());
1576}
1577
1578/* Message may be overwritten by next call
1579 * (but not in current implementation) */
1580const char *
1581msg(int en)
1582{
1583   /* NB can't use SVX_ASSERT here! */
1584   static char badbuf[256];
1585   if (en >= 1000 && en < 1000 + N_DONTEXTRACTMSGS)
1586      return dontextract[en - 1000];
1587   if (!msg_array) {
1588      if (en != 1)  {
1589         sprintf(badbuf, "Message %d requested before msg_array initialised\n",
1590                 en);
1591         return badbuf;
1592      }
1593      /* this should be the only other message which can be requested before
1594       * the message file is opened and read... */
1595      if (!dontextract) return "Out of memory (couldn't find %lu bytes).";
1596      return dontextract[(/*Out of memory (couldn't find %lu bytes).*/1004)
1597                         - 1000];
1598   }
1599
1600   if (en < 0 || en >= num_msgs) {
1601      sprintf(badbuf, "Message %d out of range\n", en);
1602      return badbuf;
1603   }
1604
1605   if (en == 0) {
1606      const char *p = msg_array[0];
1607      if (!*p) p = "(C)";
1608      return p;
1609   }
1610
1611   return msg_array[en];
1612}
1613
1614/* returns persistent copy of message */
1615const char *
1616msgPerm(int en)
1617{
1618   return msg(en);
1619}
1620
1621void
1622v_report(int severity, const char *fnm, int line, int en, va_list ap)
1623{
1624#ifdef AVEN
1625   aven_v_report(severity, fnm, line, en, ap);
1626#else
1627   if (fnm) {
1628      fputs(fnm, STDERR);
1629      if (line) fprintf(STDERR, ":%d", line);
1630   } else {
1631      fputs(appname_copy, STDERR);
1632   }
1633   fputs(": ", STDERR);
1634
1635   if (severity == 0) {
1636      fputs(msg(/*warning*/4), STDERR);
1637      fputs(": ", STDERR);
1638   }
1639
1640   vfprintf(STDERR, msg(en), ap);
1641   fputnl(STDERR);
1642#endif
1643
1644   switch (severity) {
1645    case 0:
1646      msg_warnings++;
1647      break;
1648    case 1:
1649      msg_errors++;
1650      if (msg_errors == 50)
1651         fatalerror_in_file(fnm, 0, /*Too many errors - giving up*/19);
1652      break;
1653    case 2:
1654      exit(EXIT_FAILURE);
1655   }
1656}
1657
1658void
1659warning(int en, ...)
1660{
1661   va_list ap;
1662   va_start(ap, en);
1663   v_report(0, NULL, 0, en, ap);
1664   va_end(ap);
1665}
1666
1667void
1668error(int en, ...)
1669{
1670   va_list ap;
1671   va_start(ap, en);
1672   v_report(1, NULL, 0, en, ap);
1673   va_end(ap);
1674}
1675
1676void
1677fatalerror(int en, ...)
1678{
1679   va_list ap;
1680   va_start(ap, en);
1681   v_report(2, NULL, 0, en, ap);
1682   va_end(ap);
1683}
1684
1685void
1686warning_in_file(const char *fnm, int line, int en, ...)
1687{
1688   va_list ap;
1689   va_start(ap, en);
1690   v_report(0, fnm, line, en, ap);
1691   va_end(ap);
1692}
1693
1694void
1695error_in_file(const char *fnm, int line, int en, ...)
1696{
1697   va_list ap;
1698   va_start(ap, en);
1699   v_report(1, fnm, line, en, ap);
1700   va_end(ap);
1701}
1702
1703void
1704fatalerror_in_file(const char *fnm, int line, int en, ...)
1705{
1706   va_list ap;
1707   va_start(ap, en);
1708   v_report(2, fnm, line, en, ap);
1709   va_end(ap);
1710}
1711
1712/* Code to support switching character set at runtime (e.g. for a printer
1713 * driver to support different character sets on screen and on the printer)
1714 */
1715typedef struct charset_li {
1716   struct charset_li *next;
1717   int code;
1718   char **msg_array;
1719} charset_li;
1720
1721static charset_li *charset_head = NULL;
1722
1723static int charset = CHARSET_BAD;
1724
1725int
1726select_charset(int charset_code)
1727{
1728   int old_charset = charset;
1729   charset_li *p;
1730
1731#ifdef DEBUG
1732   fprintf(stderr, "select_charset(%d), old charset = %d\n", charset_code,
1733           charset);
1734#endif
1735
1736   charset = charset_code;
1737
1738   /* check if we've already parsed messages for new charset */
1739   for (p = charset_head; p; p = p->next) {
1740#ifdef DEBUG
1741      printf("%p: code %d msg_array %p\n", p, p->code, p->msg_array);
1742#endif
1743      if (p->code == charset) {
1744         msg_array = p->msg_array;
1745         return old_charset;
1746      }
1747   }
1748
1749   /* nope, got to reparse message file */
1750   parse_msg_file(charset_code);
1751
1752   /* add to list */
1753   p = osnew(charset_li);
1754   p->code = charset;
1755   p->msg_array = msg_array;
1756   p->next = charset_head;
1757   charset_head = p;
1758
1759   return old_charset;
1760}
Note: See TracBrowser for help on using the repository browser.