source: git/src/message.c @ 8ea82aa

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 8ea82aa was b88b171, checked in by Olly Betts <olly@…>, 20 years ago
  • aven: If aven is asked to load a .svx, .dat, or .mak file, run cavern on it, showing cavern's output in a window (with errors and warnings clickable to load the offending file into an editor), and then loading the resulting 3d file.
  • (Unix version): Check environmental variable LC_MESSAGES when deciding what language to use for messages.

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

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