source: git/src/message.c @ fc4ee5b

RELEASE/1.1RELEASE/1.2debug-cidebug-ci-sanitisersfaster-cavernloglog-selectstereostereo-2025walls-datawalls-data-hanging-as-warningwarn-only-for-hanging-survey
Last change on this file since fc4ee5b was ecffd4b3, checked in by Olly Betts <olly@…>, 22 years ago

Merged from 1.0 branch.

git-svn-id: file:///home/survex-svn/survex/branches/survex-1_1@2355 4b37db11-9a0c-4f06-9ece-9ab7cdaee568

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