source: git/src/message.c @ 680e310

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

NEWS,src/message.c: Ignore LANG if it starts with a digit to avoid
problems with bogus value for LANG which AutoCAD installation seems
to set on MS Windows (backported from 1.1.13).

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

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