source: git/src/message.c @ efcd7b7

v1.0.33
Last change on this file since efcd7b7 was 6e0976a, checked in by Olly Betts <olly@…>, 21 years ago

Pruned bogus extra #endif

git-svn-id: file:///home/survex-svn/survex/trunk@2661 4b37db11-9a0c-4f06-9ece-9ab7cdaee568

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