source: git/src/message.c @ 03daa88

RELEASE/1.0RELEASE/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 03daa88 was 03daa88, checked in by Olly Betts <olly@…>, 24 years ago

Fettled DOS countrycode stuff.

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

  • Property mode set to 100644
File size: 36.0 KB
Line 
1/* message.c
2 * Fairly general purpose message and error routines
3 * Copyright (C) 1993-2001 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 "whichos.h"
35#include "filename.h"
36#include "message.h"
37#include "osdepend.h"
38#include "filelist.h"
39#include "debug.h"
40
41#ifdef AVEN
42# include "aven.h"
43#endif
44
45#ifdef HAVE_SIGNAL
46# ifdef HAVE_SETJMP_H
47#  include <setjmp.h>
48static jmp_buf jmpbufSignal;
49#  include <signal.h>
50# else
51#  undef HAVE_SIGNAL
52# endif
53#endif
54
55#if (OS==WIN32)
56# include <windows.h>
57#elif (OS==MSDOS)
58#include <dos.h>
59# ifdef __DJGPP__
60#  include <dpmi.h>
61# endif
62#elif (OS==RISCOS)
63# include "oslib/wimpreadsy.h"
64#endif
65
66/* For funcs which want to be immune from messing around with different
67 * calling conventions */
68#ifndef CDECL
69# define CDECL
70#endif
71
72int msg_warnings = 0; /* keep track of how many warnings we've given */
73int msg_errors = 0;   /* and how many (non-fatal) errors */
74
75/* in case osmalloc() fails before appname_copy is set up */
76static const char *appname_copy = "anonymous program";
77
78/* error code for failed osmalloc and osrealloc calls */
79static void
80outofmem(OSSIZE_T size)
81{
82   fatalerror(/*Out of memory (couldn't find %lu bytes).*/1,
83              (unsigned long)size);
84}
85
86#ifdef TOMBSTONES
87#define TOMBSTONE_SIZE 16
88static const char tombstone[TOMBSTONE_SIZE] = "012345\xfftombstone";
89#endif
90
91/* malloc with error catching if it fails. Also allows us to write special
92 * versions easily eg for DOS EMS or MS Windows.
93 */
94void FAR *
95osmalloc(OSSIZE_T size)
96{
97   void FAR *p;
98#ifdef TOMBSTONES
99   size += TOMBSTONE_SIZE * 2;
100   p = malloc(size);
101#else
102   p = xosmalloc(size);
103#endif
104   if (p == NULL) outofmem(size);
105#ifdef TOMBSTONES
106   printf("osmalloc truep=%p truesize=%d\n", p, size);
107   memcpy(p, tombstone, TOMBSTONE_SIZE);
108   memcpy(p + size - TOMBSTONE_SIZE, tombstone, TOMBSTONE_SIZE);
109   *(size_t *)p = size;
110   p += TOMBSTONE_SIZE;
111#endif
112   return p;
113}
114
115/* realloc with error catching if it fails. */
116void FAR *
117osrealloc(void *p, OSSIZE_T size)
118{
119   /* some pre-ANSI realloc implementations don't cope with a NULL pointer */
120   if (p == NULL) {
121      p = xosmalloc(size);
122   } else {
123#ifdef TOMBSTONES
124      int true_size;
125      size += TOMBSTONE_SIZE * 2;
126      p -= TOMBSTONE_SIZE;
127      true_size = *(size_t *)p;
128      printf("osrealloc (in truep=%p truesize=%d)\n", p, true_size);
129      if (memcmp(p + sizeof(size_t), tombstone + sizeof(size_t),
130                 TOMBSTONE_SIZE - sizeof(size_t)) != 0) {
131         printf("start tombstone for block %p, size %d corrupted!",
132                p + TOMBSTONE_SIZE, true_size - TOMBSTONE_SIZE * 2);
133      }
134      if (memcmp(p + true_size - TOMBSTONE_SIZE, tombstone,
135                 TOMBSTONE_SIZE) != 0) {
136         printf("end tombstone for block %p, size %d corrupted!",
137                p + TOMBSTONE_SIZE, true_size - TOMBSTONE_SIZE * 2);
138      }
139      p = realloc(p, size);
140      if (p == NULL) outofmem(size);
141      printf("osrealloc truep=%p truesize=%d\n", p, size);
142      memcpy(p, tombstone, TOMBSTONE_SIZE);
143      memcpy(p + size - TOMBSTONE_SIZE, tombstone, TOMBSTONE_SIZE);
144      *(size_t *)p = size;
145      p += TOMBSTONE_SIZE;
146#else
147      p = xosrealloc(p, size);
148#endif
149   }
150   if (p == NULL) outofmem(size);
151   return p;
152}
153
154char FAR *
155osstrdup(const char *str)
156{
157   char *p;
158   OSSIZE_T len;
159   len = strlen(str) + 1;
160   p = osmalloc(len);
161   memcpy(p, str, len);
162   return p;
163}
164
165/* osfree is usually just a macro in osalloc.h */
166#ifdef TOMBSTONES
167void
168osfree(void *p)
169{
170   int true_size;
171   if (!p) return;
172   p -= TOMBSTONE_SIZE;
173   true_size = *(size_t *)p;
174   printf("osfree truep=%p truesize=%d\n", p, true_size);
175   if (memcmp(p + sizeof(size_t), tombstone + sizeof(size_t),
176              TOMBSTONE_SIZE - sizeof(size_t)) != 0) {
177      printf("start tombstone for block %p, size %d corrupted!",
178             p + TOMBSTONE_SIZE, true_size - TOMBSTONE_SIZE * 2);
179   }
180   if (memcmp(p + true_size - TOMBSTONE_SIZE, tombstone,
181              TOMBSTONE_SIZE) != 0) {
182      printf("end tombstone for block %p, size %d corrupted!",
183             p + TOMBSTONE_SIZE, true_size - TOMBSTONE_SIZE * 2);
184   }
185   free(p);
186}
187#endif
188
189#ifdef HAVE_SIGNAL
190
191static int sigReceived;
192
193/* for systems not using autoconf, assume the signal handler returns void
194 * unless specified elsewhere */
195#ifndef RETSIGTYPE
196# define RETSIGTYPE void
197#endif
198
199static CDECL RETSIGTYPE FAR
200report_sig(int sig)
201{
202   sigReceived = sig;
203   longjmp(jmpbufSignal, 1);
204}
205
206static void
207init_signals(void)
208{
209   int en;
210   if (!setjmp(jmpbufSignal)) {
211#if 1 /* disable these to get a core dump */
212      signal(SIGABRT, report_sig); /* abnormal termination eg abort() */
213      signal(SIGFPE,  report_sig); /* arithmetic error eg /0 or overflow */
214      signal(SIGILL,  report_sig); /* illegal function image eg illegal instruction */
215      signal(SIGSEGV, report_sig); /* illegal storage access eg access outside memory limits */
216#endif
217# ifdef SIGSTAK /* only on RISC OS AFAIK */
218      signal(SIGSTAK, report_sig); /* stack overflow */
219# endif
220      return;
221   }
222
223   switch (sigReceived) {
224      case SIGABRT: en = /*Abnormal termination*/90; break;
225      case SIGFPE:  en = /*Arithmetic error*/91; break;
226      case SIGILL:  en = /*Illegal instruction*/92; break;
227      case SIGSEGV: en = /*Bad memory access*/94; break;
228# ifdef SIGSTAK
229      case SIGSTAK: en = /*Stack overflow*/96; break;
230# endif
231      default:      en = /*Unknown signal received*/97; break;
232   }
233   fputsnl(msg(en), STDERR);
234
235   /* Any of the signals we catch indicates a bug */
236   fatalerror(/*Bug in program detected! Please report this to the authors*/11);
237
238   exit(EXIT_FAILURE);
239}
240#endif
241
242static int
243default_charset(void)
244{
245#if (OS==RISCOS)
246   /* RISCOS 3.1 and above CHARSET_RISCOS31 (ISO_8859_1 + extras in 128-159)
247    * RISCOS < 3.1 is ISO_8859_1 */
248   int version;
249   if (xwimpreadsysinfo_version(&version) != NULL) {
250      /* RISC OS 2 or some error (don't care which) */
251      return CHARSET_ISO_8859_1;
252   }
253
254   /* oddly wimp_VERSION_RO3 is RISC OS 3.1 */
255   if (version < wimp_VERSION_RO3) return CHARSET_ISO_8859_1;
256
257   return CHARSET_RISCOS31;
258#elif (OS==MSDOS)
259   return CHARSET_DOSCP850;
260#elif (OS==WIN32)
261# ifdef AVEN
262#  define CODEPAGE GetACP()
263# else
264#  define CODEPAGE GetConsoleOutputCP()
265# endif
266   switch (CODEPAGE) {
267    case 1252: return CHARSET_WINCP1252;
268    case 850: return CHARSET_DOSCP850;
269   }
270   return CHARSET_USASCII;
271#elif (OS==UNIX)
272#if defined(XCAVEROT) || defined(AVEN)
273   return CHARSET_ISO_8859_1;
274#else
275   const char *p = getenv("LC_ALL");
276   if (p == NULL || p[0] == '\0') {
277      p = getenv("LC_CTYPE");
278      if (p == NULL || p[0] == '\0') {
279         p = msg_lang;
280      }
281   }
282
283   if (p) {
284      char *q = strchr(p, '.');
285      if (q) p = q + 1;
286   }
287
288   if (p) {
289      const char *chset = p;
290      size_t name_len;
291
292      while (*p != '\0' && *p != '@') p++;
293
294      name_len = p - chset;
295
296      if (name_len) {
297         int only_digit = 1;
298         size_t cnt;
299       
300         for (cnt = 0; cnt < name_len; ++cnt)
301            if (isalpha(chset[cnt])) {
302               only_digit = 0;
303               break;
304            }
305
306         if (only_digit) goto iso;
307         
308         switch (tolower(chset[0])) {
309          case 'i':
310            if (tolower(chset[1]) == 's' && tolower(chset[2]) == 'o') {
311               chset += 3;
312               iso:
313               if (strncmp(chset, "8859", 4) == 0) {
314                  chset += 4;
315                  while (chset < p && *chset && !isdigit(*chset)) chset++;
316                  switch (atoi(chset)) {
317                   case 1: return CHARSET_ISO_8859_1;
318                   case 15: return CHARSET_ISO_8859_15;
319                   default: return CHARSET_USASCII;               
320                  }
321               }
322            }
323            break;
324          case 'u':
325            if (tolower(chset[1]) == 't' && tolower(chset[2]) == 'f') {
326               chset += 3;
327               while (chset < p && *chset && !isdigit(*chset)) chset++;
328               switch (atoi(chset)) {
329                case 8: return CHARSET_UTF8;
330                default: return CHARSET_USASCII;
331               }
332            }
333         }
334      }
335   }
336   return CHARSET_USASCII;
337#endif
338#else
339# error Do not know operating system 'OS'
340#endif
341}
342
343/* It seems that Swedish and maybe some other scandanavian languages don't
344 * transliterate &auml; to ae - but it seems there may be conflicting views
345 * on this...
346 */
347#define umlaut_to_e() 1
348
349/* values <= 127 already dealt with */
350static int
351add_unicode(int charset, unsigned char *p, int value)
352{
353#ifdef DEBUG
354   fprintf(stderr, "add_unicode(%d, %p, %d)\n", charset, p, value);
355#endif
356   if (value == 0) return 0;
357   switch (charset) {
358   case CHARSET_USASCII:
359      if (value < 0x80) {
360         *p = value;
361         return 1;
362      }
363      break;
364   case CHARSET_ISO_8859_1:
365      if (value < 0x100) {
366         *p = value;
367         return 1;
368      }
369      break;
370   case CHARSET_ISO_8859_15:
371      switch (value) {
372       case 0xa4: case 0xa6: case 0xb0: case 0xc4:
373       case 0xd0: case 0xd4: case 0xd5: case 0xd6:
374         goto donthave;
375       case 0x152: value = 0xd4; break; /* &OElig; */
376       case 0x153: value = 0xd5; break; /* &oelig; */
377#if 0
378       case 0x0: value = 0xa4; break; /* euro */
379       case 0x0: value = 0xa6; break; /* Scaron */
380       case 0x0: value = 0xb0; break; /* scaron */
381       case 0x0: value = 0xc4; break; /* Zcaron */
382       case 0x0: value = 0xd0; break; /* zcaron */
383       case 0x0: value = 0xd6; break; /* Ydiersis */
384#endif
385      }
386      if (value < 0x100) {
387         *p = value;
388         return 1;
389      }
390      donthave:
391      break;
392#if (OS==RISCOS)
393   case CHARSET_RISCOS31:
394      /* RISC OS 3.1 (and later) extensions to ISO-8859-1 */
395      switch (value) {
396       case 0x152: value = 0x9a; break; /* &OElig; */
397       case 0x153: value = 0x9b; break; /* &oelig; */
398#if 0
399       case 0x174: value = 0x81; break; /* &Wcirc; */
400       case 0x175: value = 0x82; break; /* &wcirc; */
401       case 0x176: value = 0x85; break; /* &Ycirc; */
402       case 0x177: value = 0x86; break; /* &ycirc; */
403#endif
404      }
405      if (value < 0x100) {
406         *p = value;
407         return 1;
408      }
409      break;
410#elif (OS==WIN32)
411   case CHARSET_WINCP1252:
412      /* MS Windows extensions to ISO-8859-1 */
413      switch (value) {
414       case 0x152: value = 0x8c; break; /* &OElig; */
415       case 0x153: value = 0x9c; break; /* &oelig; */
416#if 0
417      /* there are a few other obscure ones we don't currently need */
418#endif
419      }
420      if (value < 0x100) {
421         *p = value;
422         return 1;
423      }
424      break;
425#endif
426#if (OS==MSDOS || OS==WIN32)
427   case CHARSET_DOSCP850: {
428      unsigned char uni2dostab[] = {
429         255, 173, 189, 156, 207, 190, 221, 245,
430         249, 184, 166, 174, 170, 240, 169, 238,
431         248, 241, 253, 252, 239, 230, 244, 250,
432         247, 251, 167, 175, 172, 171, 243, 168,
433         183, 181, 182, 199, 142, 143, 146, 128,
434         212, 144, 210, 211, 222, 214, 215, 216,
435         209, 165, 227, 224, 226, 229, 153, 158,
436         157, 235, 233, 234, 154, 237, 232, 225,
437         133, 160, 131, 198, 132, 134, 145, 135,
438         138, 130, 136, 137, 141, 161, 140, 139,
439         208, 164, 149, 162, 147, 228, 148, 246,
440         155, 151, 163, 150, 129, 236, 231, 152
441      };
442      if (value >= 160 && value < 256) {
443         *p = (int)uni2dostab[value - 160];
444         return 1;
445      }
446      if (value == 305) {
447         *p = 213;
448         return 1;
449      }
450      if (value == 402) {
451         *p = 159;
452         return 1;
453      }
454      break;
455   }
456#endif
457   }
458   /* Transliterate characters we can't represent */
459#ifdef DEBUG
460   fprintf(stderr, "transliterate `%c' 0x%x\n", value, value);
461#endif
462   switch (value) {
463    case 160:
464      *p = ' '; return 1;
465    case 161 /* ¡ */:
466      *p = '!'; return 1;
467    case 171 /* « */:
468      p[1] = *p = '<'; return 2;
469    case 187 /* » */:
470      p[1] = *p = '>'; return 2;
471    case 191 /* ¿ */:
472      *p = '?'; return 1;
473    case 192 /* À */: case 193 /* Á */: case 194 /* Â */: case 195 /* Ã */:
474      *p = 'A'; return 1;
475    case 197 /* Å */:
476      p[1] = *p = 'A'; return 2;
477    case 196 /* Ä */: /* &Auml; */
478      *p = 'A';
479      if (!umlaut_to_e()) return 1;
480      p[1] = 'E'; return 2;
481    case 198 /* Æ */:
482      *p = 'A'; p[1] = 'E'; return 2;
483    case 199 /* Ç */:
484      *p = 'C'; return 1;
485    case 200 /* È */: case 201 /* É */: case 202 /* Ê */: case 203 /* Ë */:
486      *p = 'E'; return 1;
487    case 204 /* Ì */: case 205 /* Í */: case 206 /* Î */: case 207 /* Ï */:
488      *p = 'I'; return 1;
489    case 208 /* Ð */: case 222 /* Þ */:
490      *p = 'T'; p[1] = 'H'; return 2;
491    case 209 /* Ñ */:
492      *p = 'N'; return 1;
493    case 210 /* Ò */: case 211 /* Ó */: case 212 /* Ô */: case 213 /* Õ */:
494      *p = 'O'; return 1;
495    case 214 /* Ö */: /* &Ouml; */ case 0x152: /* &OElig; */
496      *p = 'O'; p[1] = 'E'; return 2;
497    case 217 /* Ù */: case 218 /* Ú */: case 219 /* Û */:
498      *p = 'U'; return 1;
499    case 220 /* Ü */: /* &Uuml; */
500      *p = 'U'; p[1] = 'E'; return 2;
501    case 221 /* Ý */:
502      *p = 'Y'; return 1;
503    case 223 /* ß */:
504      p[1] = *p = 's'; return 2;
505    case 224 /* à */: case 225 /* á */: case 226 /* â */: case 227 /* ã */:
506      *p = 'a'; return 1;
507    case 228 /* ä */: /* &auml; */ case 230 /* æ */:
508      *p = 'a'; p[1] = 'e'; return 2;
509    case 229 /* å */:
510      p[1] = *p = 'a'; return 2;
511    case 231 /* ç */:
512      *p = 'c'; return 1;
513    case 232 /* è */: case 233 /* é */: case 234 /* ê */: case 235 /* ë */:
514      *p = 'e'; return 1;
515    case 236 /* ì */: case 237 /* í */: case 238 /* î */: case 239 /* ï */:
516      *p = 'i'; return 1;
517    case 241 /* ñ */:
518      *p = 'n'; return 1;
519    case 240 /* ð */: case 254 /* þ */:
520      *p = 't'; p[1] = 'h'; return 2;
521    case 242 /* ò */: case 243 /* ó */: case 244 /* ô */: case 245 /* õ */:
522      *p = 'o'; return 1;
523    case 246 /* ö */: /* &ouml; */ case 0x153: /* &oelig; */
524      *p = 'o'; p[1] = 'e'; return 2;
525    case 249 /* ù */: case 250 /* ú */: case 251 /* û */:
526      *p = 'u'; return 1;
527    case 252 /* ü */: /* &uuml; */
528      *p = 'u'; p[1] = 'e'; return 2;
529    case 253 /* ý */: case 255 /* ÿ */:
530      *p = 'y'; return 1;
531   }
532#ifdef DEBUG
533   fprintf(stderr, "failed to transliterate\n");
534#endif
535   return 0;
536}
537
538/* fall back on looking in the current directory */
539static const char *pth_cfg_files = "";
540
541static int num_msgs = 0;
542static char **msg_array = NULL;
543
544const char *msg_lang = NULL;
545const char *msg_lang2 = NULL;
546
547static char **
548parse_msgs(int n, unsigned char *p, int charset_code) {
549   int i;
550
551   char **msgs = osmalloc(n * sizeof(char *));
552
553   for (i = 0; i < n; i++) {
554      unsigned char *to = p;
555      int ch;
556      msgs[i] = (char *)p;
557
558      /* If we want UTF8 anyway, we just need to find the start of each
559       * message */
560      if (charset_code == CHARSET_UTF8) {
561         p += strlen((char *)p) + 1;
562         continue;
563      }
564
565      while ((ch = *p++) != 0) {
566         /* A byte in the range 0x80-0xbf or 0xf0-0xff isn't valid in
567          * this state, (0xf0-0xfd mean values > 0xffff) so treat as
568          * literal and try to resync so we cope better when fed
569          * non-utf-8 data.  Similarly we abandon a multibyte sequence
570          * if we hit an invalid character. */
571         if (ch >= 0xc0 && ch < 0xf0) {
572            int ch1 = *p;
573            if ((ch1 & 0xc0) != 0x80) goto resync;
574
575            if (ch < 0xe0) {
576               /* 2 byte sequence */
577               ch = ((ch & 0x1f) << 6) | (ch1 & 0x3f);
578               p++;
579            } else {
580               /* 3 byte sequence */
581               int ch2 = p[1];
582               if ((ch2 & 0xc0) != 0x80) goto resync;
583               ch = ((ch & 0x1f) << 12) | ((ch1 & 0x3f) << 6) | (ch2 & 0x3f);
584               p += 2;
585            }
586         }
587
588         resync:
589
590         if (ch < 127) {
591            *to++ = (char)ch;
592         } else {
593            /* We assume an N byte UTF-8 code never transliterates to more
594             * than N characters (so we can't transliterate © to (C) or
595             * ® to (R) for example) */
596            to += add_unicode(charset_code, to, ch);
597         }
598      }
599      *to++ = '\0';
600   }
601   return msgs;
602}
603
604/* This is the name of the default language.  Add -DDEFAULTLANG to CFLAGS
605 * e.g. with `CFLAGS="-DDEFAULTLANG=fr" ./configure'
606 */
607#ifdef DEFAULTLANG
608/* No point extracting these errors as they won't get used if file opens */
609/* FIXME: this works on gcc but not some other compilers (e.g. norcroft),
610 * and also ../lib/fr.h, etc don't go into srcN_NN.zip */
611# define HDR(D) "../lib/"STRING(D)".h"
612# include HDR(DEFAULTLANG)
613#else
614#define N_DONTEXTRACTMSGS 5
615static unsigned char dontextractmsgs[] =
616   "Can't open message file `%s' using path `%s'\0"/*1000*/
617   "Problem with message file `%s'\0"/*1001*/
618   "I don't understand this message file version\0"/*1002*/
619   "Message file truncated?\0"/*1003*/
620   "Out of memory (couldn't find %lu bytes).\0"/*1004*/;
621#endif
622
623static char **dontextract = NULL;
624
625static void
626parse_msg_file(int charset_code)
627{
628   FILE *fh;
629   unsigned char header[20];
630   int i;   
631   unsigned len;
632   unsigned char *p;
633   char *fnm, *s;
634   int n;
635
636#ifdef DEBUG
637   fprintf(stderr, "parse_msg_file(%d)\n", charset_code);
638#endif
639
640   /* sort out messages we need to print if we can't open the message file */
641   dontextract = parse_msgs(N_DONTEXTRACTMSGS, dontextractmsgs, charset_code);
642
643   fnm = osstrdup(msg_lang);
644   /* trim off charset from stuff like "de_DE.iso8859_1" */
645   s = strchr(fnm, '.');
646   if (s) *s = '\0';
647
648   fh = fopenWithPthAndExt(pth_cfg_files, fnm, EXT_SVX_MSG, "rb", NULL);
649
650   if (!fh) {
651      /* e.g. if 'en_GB' is unknown, see if we know 'en' */
652      if (strlen(fnm) > 3 && fnm[2] == '_') {
653         fnm[2] = '\0';
654         fh = fopenWithPthAndExt(pth_cfg_files, fnm, EXT_SVX_MSG, "rb", NULL);
655         if (!fh) fnm[2] = '_'; /* for error reporting */
656      }
657   }
658
659   if (!fh) {
660      fatalerror(/*Can't open message file `%s' using path `%s'*/1000,
661                 fnm, pth_cfg_files);
662   }
663
664   if (fread(header, 1, 20, fh) < 20 ||
665       memcmp(header, "Svx\nMsg\r\n\xfe\xff", 12) != 0) {
666      fatalerror(/*Problem with message file `%s'*/1001, fnm);
667   }
668
669   if (header[12] != 0)
670      fatalerror(/*I don't understand this message file version*/1002);
671
672   n = (header[14] << 8) | header[15];
673
674   len = 0;
675   for (i = 16; i < 20; i++) len = (len << 8) | header[i];
676
677   p = osmalloc(len);
678   if (fread(p, 1, len, fh) < len)
679      fatalerror(/*Message file truncated?*/1003);
680
681   fclose(fh);
682
683#ifdef DEBUG
684   fprintf(stderr, "fnm = `%s', n = %d, len = %d\n", fnm, n, len);
685#endif
686   osfree(fnm);
687
688   msg_array = parse_msgs(n, p, charset_code);
689   num_msgs = n;
690}
691
692const char *
693msg_cfgpth(void)
694{
695   return pth_cfg_files;
696}
697
698const char *
699msg_appname(void)
700{
701   return appname_copy;
702}
703
704void
705msg_init(char * const *argv)
706{
707   char *p;
708   ASSERT(argv);
709
710#ifdef HAVE_SIGNAL
711   init_signals();
712#endif
713   /* Point to argv[0] itself so we report a more helpful error if the
714    * code to work out the clean appname generates a signal */
715   appname_copy = argv[0];
716#if (OS == UNIX)
717   /* use name as-is on Unix - programs run from path get name as supplied */
718   appname_copy = osstrdup(argv[0]);
719#else
720   /* use the lower-cased leafname on other platforms */
721   appname_copy = p = leaf_from_fnm(argv[0]);
722   while (*p) {
723      *p = tolower(*p);
724      p++;
725   }
726#endif
727
728   /* shortcut --version so you can check the version number when the correct
729    * message file can't be found... */
730   if (argv[1] && strcmp(argv[1], "--version") == 0) {
731      cmdline_version();
732      exit(0);
733   }
734
735   /* Look for env. var. "SURVEXHOME" or the like */
736   p = getenv("SURVEXHOME");
737   if (p && *p) {
738      pth_cfg_files = osstrdup(p);
739#if (OS==UNIX) && defined(DATADIR) && defined(PACKAGE)
740   } else {
741      /* under Unix, we compile in the configured path */
742      pth_cfg_files = DATADIR "/" PACKAGE;
743#else
744   } else if (argv[0]) {
745      /* else try the path on argv[0] */
746      pth_cfg_files = path_from_fnm(argv[0]);
747#endif
748   }
749
750   msg_lang = getenv("SURVEXLANG");
751#ifdef DEBUG
752   fprintf(stderr, "msg_lang = %p (= \"%s\")\n", msg_lang, msg_lang?msg_lang:"(null)");
753#endif
754
755   if (!msg_lang || !*msg_lang) {
756      msg_lang = getenv("LANG");
757      if (!msg_lang || !*msg_lang) {
758#if (OS==WIN32)
759         LCID locid;
760#endif
761#ifdef DEFAULTLANG
762         msg_lang = STRING(DEFAULTLANG);
763#else
764         msg_lang = "en";
765#endif
766#if (OS==WIN32)
767         locid = GetUserDefaultLCID();
768         if (locid) {
769            WORD langid = LANGIDFROMLCID(locid);
770            switch (PRIMARYLANGID(langid)) {
771/* older mingw compilers don't seem to supply this value */
772#ifndef LANG_CATALAN
773# define LANG_CATALAN 0x03
774#endif
775             case LANG_CATALAN:
776               msg_lang = "ca";
777               break;
778             case LANG_ENGLISH:
779               if (SUBLANGID(langid) == SUBLANG_ENGLISH_US)
780                  msg_lang = "en_US";
781               else
782                  msg_lang = "en";
783               break;
784             case LANG_FRENCH:
785               msg_lang = "fr";
786               break;
787             case LANG_GERMAN:
788               switch (SUBLANGID(langid)) {
789                case SUBLANG_GERMAN_SWISS:
790                  msg_lang = "de_CH";
791                  break;
792                case SUBLANG_GERMAN:
793                  msg_lang = "de_DE";
794                  break;
795                default:
796                  msg_lang = "de";
797               }
798               break;
799             case LANG_ITALIAN:
800               msg_lang = "it";
801               break;
802             case LANG_PORTUGUESE:
803               if (SUBLANGID(langid) == SUBLANG_PORTUGUESE_BRAZILIAN)
804                  msg_lang = "pt_BR";
805               else
806                  msg_lang = "pt";
807               break;
808             case LANG_SPANISH:
809               msg_lang = "es";
810               break;
811            }
812         }
813#elif (OS==RISCOS)
814         switch (xterritory_number()) {
815          case 1: /* UK */
816          case 2: /* Master */
817          case 3: /* Compact */
818          case 17: /* Canada1 */
819          case 19: /* Canada */
820          case 22: /* Ireland */
821            msg_lang = "en";
822            break;
823          case 4: /* Italy */
824            msg_lang = "it";
825            break;
826          case 5: /* Spain (or ca) */
827          case 27: /* Mexico */
828          case 28: /* LatinAm (or pt_BR) */
829            msg_lang = "es";
830            break;
831          case 6: /* France */
832          case 18: /* Canada2 */
833            msg_lang = "fr";
834            break;
835          case 7: /* Germany */
836            msg_lang = "de_DE";
837            break;
838          case 8: /* Portugal */
839            msg_lang = "pt";
840            break;
841          case 48: /* USA */
842            msg_lang = "en_US";
843            break;
844#if 0
845          case 9: /* Esperanto */
846          case 10: /* Greece */
847          case 11: /* Sweden */
848          case 12: /* Finland */
849          case 13: /* Unused */
850          case 14: /* Denmark */
851          case 15: /* Norway */
852          case 16: /* Iceland */
853          case 20: /* Turkey */
854          case 21: /* Arabic */
855          case 23: /* Hong Kong */
856          case 24: /* Russia */
857          case 25: /* Russia2 */
858          case 26: /* Israel */
859#endif     
860         }
861#elif (OS==MSDOS)
862           {
863              int country_code;
864# ifdef __DJGPP__
865              int sel;
866              int seg = __dpmi_allocate_dos_memory(3, &sel);
867              if (seg != -1) {
868                 __dpmi_regs r;
869                 r.x.ax = 0x3800;
870                 r.d.edx = seg;
871                 if (__dpmi_int(0x21, &r) != -1 && !r.x.flags) {
872                    country_code = r.x.bx;
873# else
874              union REGS r;
875              r.x.ax = 0x3800; /* get current country info */
876              r.x.dx = 0;
877              intdos(&r, &r);
878              if (!r.x.cflag) {
879                 if (1) {
880                    country_code = r.x.bx;
881# endif
882                    /* List of country codes taken from:
883                     * http://www.delorie.com/djgpp/doc/rbinter/it/00/14.html */
884                    /* The mappings here are guesses at best in most cases.
885                     * In a lot of cases we pick a language because we have
886                     * a translation in it, rather than because it's the most
887                     * widely used or understood in that country. */
888                    /* Improvements welcome */
889                    switch (country_code) {
890                     case 1: /* United States */
891                     case 670: /* Saipan / N. Mariana Island */
892                     case 671: /* Guam */
893                     case 680: /* Palau */
894                     case 684: /* American Samoa */
895                     case 691: /* Micronesia */
896                     case 692: /* Marshall Islands */
897                       msg_lang = "en_US";
898                       break;
899                     case 4: /* Canada (English) */
900                     case 27: /* South Africa */
901                     case 44: /* United Kingdom */
902                     case 61: /* International English / Australia */
903                     case 64: /* New Zealand */
904                     case 99: /* Asia (English) */
905                     case 220: /* Gambia */
906                     case 231: /* Liberia */
907                     case 232: /* Sierra Leone */
908                     case 233: /* Ghana */
909                     case 254: /* Kenya */
910                     case 256: /* Uganda */
911                     case 260: /* Zambia */
912                     case 263: /* Zimbabwe */
913                     case 264: /* Namibia */
914                     case 267: /* Botswana */
915                     case 268: /* Swaziland */
916                     case 290: /* St. Helena */
917                     case 297: /* Aruba */
918                     case 350: /* Gibraltar */
919                     case 353: /* Ireland */
920                     case 356: /* Malta */
921                     case 500: /* Falkland Islands */
922                     case 501: /* Belize */
923                     case 592: /* Guyana */
924                     case 672: /* Norfolk Island (Australia) / Christmas Island/Cocos Islands / Antartica */
925                     case 673: /* Brunei Darussalam */
926                     case 674: /* Nauru */
927                     case 675: /* Papua New Guinea */
928                     case 676: /* Tonga Islands */
929                     case 677: /* Solomon Islands */
930                     case 679: /* Fiji */
931                     case 682: /* Cook Islands */
932                     case 683: /* Niue */
933                     case 685: /* Western Samoa */
934                     case 686: /* Kiribati */
935                       /* I believe only some of these are English speaking... */
936                     case 809: /* Antigua and Barbuda / Anguilla / Bahamas / Barbados / Bermuda
937                                British Virgin Islands / Cayman Islands / Dominica
938                                Dominican Republic / Grenada / Jamaica / Montserra
939                                St. Kitts and Nevis / St. Lucia / St. Vincent and Grenadines
940                                Trinidad and Tobago / Turks and Caicos */
941                       msg_lang = "en";
942                       break;
943                     case 2: /* Canadian-French */
944                     case 32: /* Belgium */ /* maybe */
945                     case 33: /* France */
946                     case 213: /* Algeria */
947                     case 216: /* Tunisia */
948                     case 221: /* Senegal */
949                     case 223: /* Mali */
950                     case 225: /* Ivory Coast */
951                     case 226: /* Burkina Faso */
952                     case 227: /* Niger */
953                     case 228: /* Togo */
954                     case 229: /* Benin */
955                     case 230: /* Mauritius */
956                     case 235: /* Chad */
957                     case 236: /* Central African Republic */
958                     case 237: /* Cameroon */
959                     case 241: /* Gabon */
960                     case 242: /* Congo */
961                     case 250: /* Rwhanda */
962                     case 253: /* Djibouti */
963                     case 257: /* Burundi */
964                     case 261: /* Madagascar */
965                     case 262: /* Reunion Island */
966                     case 269: /* Comoros */
967                     case 270: /* Mayotte */
968                     case 352: /* Luxembourg (or de or ...) */
969                     case 508: /* St. Pierre and Miquelon */
970                     case 509: /* Haiti */
971                     case 590: /* Guadeloupe */
972                     case 594: /* French Guiana */
973                     case 596: /* Martinique / French Antilles */
974                     case 678: /* Vanuatu */
975                     case 681: /* Wallis & Futuna */
976                     case 687: /* New Caledonia */
977                     case 689: /* French Polynesia */
978                     case 961: /* Lebanon */
979                       msg_lang = "fr";
980                       break;
981                     case 3: /* Latin America */
982                     case 34: /* Spain */
983                     case 51: /* Peru */
984                     case 52: /* Mexico */
985                     case 53: /* Cuba */
986                     case 54: /* Argentina */
987                     case 56: /* Chile */
988                     case 57: /* Columbia */
989                     case 58: /* Venezuela */
990                     case 63: /* Philippines */
991                     case 240: /* Equatorial Guinea */
992                     case 502: /* Guatemala */
993                     case 503: /* El Salvador */
994                     case 504: /* Honduras */
995                     case 505: /* Nicraragua */
996                     case 506: /* Costa Rica */
997                     case 507: /* Panama */
998                     case 591: /* Bolivia */
999                     case 593: /* Ecuador */
1000                     case 595: /* Paraguay */
1001                     case 598: /* Uruguay */
1002                       msg_lang = "es";
1003                       break;
1004                     case 39: /* Italy / San Marino / Vatican City */
1005                       msg_lang = "it";
1006                       break;
1007                     case 41: /* Switzerland / Liechtenstein */ /* or fr or ... */
1008                       msg_lang = "de_CH";
1009                       break;
1010                     case 43: /* Austria (DR DOS 5.0) */
1011                       msg_lang = "de";
1012                       break;
1013                     case 49: /* Germany */
1014                       msg_lang = "de_DE";
1015                       break;
1016                     case 55: /* Brazil (not supported by DR DOS 5.0) */
1017                       msg_lang = "pt_BR";
1018                       break;
1019                     case 238: /* Cape Verde Islands */
1020                     case 244: /* Angola */
1021                     case 245: /* Guinea-Bissau */
1022                     case 259: /* Mozambique */
1023                     case 351: /* Portugal */
1024                       msg_lang = "pt";
1025                       break;
1026#if 0
1027                     case 7: /* Russia */
1028                     case 20: /* Egypt */
1029                     case 30: /* Greece */
1030                     case 31: /* Netherlands */
1031                     case 35: /* Bulgaria??? */
1032                     case 36: /* Hungary (not supported by DR DOS 5.0) */
1033                     case 38: /* Yugoslavia (not supported by DR DOS 5.0) -- obsolete */
1034                     case 40: /* Romania */
1035                     case 42: /* Czechoslovakia / Tjekia / Slovakia (not supported by DR DOS 5.0) */
1036                     case 45: /* Denmark */
1037                     case 46: /* Sweden */
1038                     case 47: /* Norway */
1039                     case 48: /* Poland (not supported by DR DOS 5.0) */
1040                     case 60: /* Malaysia */
1041                     case 62: /* Indonesia / East Timor */
1042                     case 65: /* Singapore */
1043                     case 66: /* Thailand (or Taiwan???) */
1044                     case 81: /* Japan (DR DOS 5.0, MS-DOS 5.0+) */
1045                     case 82: /* South Korea (DR DOS 5.0) */
1046                     case 84: /* Vietnam */
1047                     case 86: /* China (MS-DOS 5.0+) */
1048                     case 88: /* Taiwan (MS-DOS 5.0+) */
1049                     case 90: /* Turkey (MS-DOS 5.0+) */
1050                     case 91: /* India */
1051                     case 92: /* Pakistan */
1052                     case 93: /* Afghanistan */
1053                     case 94: /* Sri Lanka */
1054                     case 98: /* Iran */
1055                     case 102: /* ??? (Hebrew MS-DOS 5.0) */
1056                     case 112: /* Belarus */
1057                     case 200: /* Thailand (PC DOS 6.1+) (reported as 01due to a bug in PC DOS COUNTRY.SYS) */
1058                     case 212: /* Morocco */
1059                     case 218: /* Libya */
1060                     case 222: /* Maruitania */
1061                     case 224: /* African Guinea */
1062                     case 234: /* Nigeria */
1063                     case 239: /* Sao Tome and Principe */
1064                     case 243: /* Zaire */
1065                     case 246: /* Diego Garcia */
1066                     case 247: /* Ascension Isle */
1067                     case 248: /* Seychelles */
1068                     case 249: /* Sudan */
1069                     case 251: /* Ethiopia */
1070                     case 252: /* Somalia */
1071                     case 255: /* Tanzania */
1072                     case 265: /* Malawi */
1073                     case 266: /* Lesotho */
1074                     case 298: /* Faroe Islands */
1075                     case 299: /* Greenland */
1076                     case 354: /* Iceland */
1077                     case 355: /* Albania */
1078                     case 357: /* Cyprus */
1079                     case 358: /* Finland */
1080                     case 359: /* Bulgaria */
1081                     case 370: /* Lithuania (reported as 372 due to a bug in MS-DOS COUNTRY.SYS) */
1082                     case 371: /* Latvia (reported as 372 due to a bug in MS-DOS COUNTRY.SYS) */
1083                     case 372: /* Estonia */
1084                     case 373: /* Moldova */
1085                     case 375: /* ??? (MS-DOS 7.10 / Windows98) */
1086                     case 380: /* Ukraine */
1087                     case 381: /* Serbia / Montenegro */
1088                     case 384: /* Croatia */
1089                     case 385: /* Croatia (PC DOS 7+) */
1090                     case 386: /* Slovenia */
1091                     case 387: /* Bosnia-Herzegovina (Latin) */
1092                     case 388: /* Bosnia-Herzegovina (Cyrillic) (PC DOS 7+) (reported as 381 due to a bug in PC DOS COUNTRY.SYS) */
1093                     case 389: /* FYR Macedonia */
1094                     case 421: /* Czech Republic / Tjekia (PC DOS 7+) */
1095                     case 422: /* Slovakia (reported as 421 due to a bug in COUNTRY.SYS) */
1096                     case 597: /* Suriname (nl) */
1097                     case 599: /* Netherland Antilles (nl) */
1098                     case 666: /* Russia??? (PTS-DOS 6.51 KEYB) */
1099                     case 667: /* Poland??? (PTS-DOS 6.51 KEYB) */
1100                     case 668: /* Poland??? (Slavic???) (PTS-DOS 6.51 KEYB) */
1101                     case 688: /* Tuvalu */
1102                     case 690: /* Tokealu */
1103                     case 711: /* ??? (currency = EA$, code pages 437,737,850,852,855,857) */
1104                     case 785: /* Arabic (Middle East/Saudi Arabia/etc.) */
1105                     case 804: /* Ukraine */
1106                     case 850: /* North Korea */
1107                     case 852: /* Hong Kong */
1108                     case 853: /* Macao */
1109                     case 855: /* Cambodia */
1110                     case 856: /* Laos */
1111                     case 880: /* Bangladesh */
1112                     case 886: /* Taiwan (MS-DOS 6.22+) */
1113                     case 960: /* Maldives */
1114                     case 962: /* Jordan */
1115                     case 963: /* Syria / Syrian Arab Republic */
1116                     case 964: /* Iraq */
1117                     case 965: /* Kuwait */
1118                     case 966: /* Saudi Arabia */
1119                     case 967: /* Yemen */
1120                     case 968: /* Oman */
1121                     case 969: /* Yemen??? (Arabic MS-DOS 5.0) */
1122                     case 971: /* United Arab Emirates */
1123                     case 972: /* Israel (Hebrew) (DR DOS 5.0,MS-DOS 5.0+) */
1124                     case 973: /* Bahrain */
1125                     case 974: /* Qatar */
1126                     case 975: /* Bhutan */
1127                     case 976: /* Mongolia */
1128                     case 977: /* Nepal */
1129                     case 995: /* Myanmar (Burma) */
1130#endif
1131                    }
1132                 }               
1133# ifdef __DJGPP__
1134                 (void)__dpmi_free_dos_memory(sel);
1135# endif
1136              }
1137           }
1138#endif
1139      }
1140   }
1141#ifdef DEBUG
1142   fprintf(stderr, "msg_lang = %p (= \"%s\")\n", msg_lang, msg_lang?msg_lang:"(null)");
1143#endif
1144
1145   /* On Mandrake LANG defaults to C */
1146   if (strcmp(msg_lang, "C") == 0) msg_lang = "en";
1147
1148   msg_lang = osstrdup(msg_lang);
1149
1150   /* Convert en-us to en_US, etc */
1151   p = strchr(msg_lang, '-');
1152   if (p) {
1153      *p++ = '_';
1154      while (*p) {
1155         *p = toupper(*p);
1156         p++;
1157      }
1158   }
1159
1160   p = strchr(msg_lang, '_');
1161   if (p) {
1162      *p = '\0';
1163      msg_lang2 = osstrdup(msg_lang);
1164      *p = '_';
1165   }
1166
1167#ifdef LC_MESSAGES
1168   /* try to setlocale() appropriately too */
1169   if (!setlocale(LC_MESSAGES, msg_lang)) {
1170      if (msg_lang2) setlocale(LC_MESSAGES, msg_lang2);
1171   }
1172#endif
1173
1174   select_charset(default_charset());
1175}
1176
1177/* Message may be overwritten by next call
1178 * (but not in current implementation) */
1179const char *
1180msg(int en)
1181{
1182   /* NB can't use ASSERT here! */
1183   static char badbuf[256];
1184   if (en >= 1000 && en < 1000 + N_DONTEXTRACTMSGS)
1185      return dontextract[en - 1000];
1186   if (!msg_array) {
1187      if (en != 1)  {
1188         sprintf(badbuf, "Message %d requested before msg_array initialised\n", en);
1189         return badbuf;
1190      }
1191      /* this should be the only other message which can be requested before
1192       * the message file is opened and read... */
1193      if (!dontextract) return "Out of memory (couldn't find %lu bytes).";
1194      return dontextract[4];
1195   }
1196
1197   if (en < 0 || en >= num_msgs) {
1198      sprintf(badbuf, "Message %d out of range\n", en);
1199      return badbuf;
1200   }
1201
1202   if (en == 0) {
1203      char *p = msg_array[0];
1204      if (!*p) p = "(C)";
1205      return p;
1206   }
1207
1208   return msg_array[en];
1209}
1210
1211/* returns persistent copy of message */
1212const char *
1213msgPerm(int en)
1214{
1215   return msg(en);
1216}
1217
1218void
1219v_report(int severity, const char *fnm, int line, int en, va_list ap)
1220{
1221#ifdef AVEN
1222   aven_v_report(severity, fnm, line, en, ap);
1223#else
1224   if (fnm) {
1225      fputs(fnm, STDERR);
1226      if (line) fprintf(STDERR, ":%d", line);
1227   } else {
1228      fputs(appname_copy, STDERR);
1229   }
1230   fputs(": ", STDERR);
1231
1232   if (severity == 0) {
1233      fputs(msg(/*warning*/4), STDERR);
1234      fputs(": ", STDERR);
1235   }
1236
1237   vfprintf(STDERR, msg(en), ap);
1238   fputnl(STDERR);
1239#endif
1240
1241   switch (severity) {
1242    case 0:
1243      msg_warnings++;
1244      break;
1245    case 1:
1246      msg_errors++;
1247      if (msg_errors == 50)
1248         fatalerror_in_file(fnm, 0, /*Too many errors - giving up*/19);
1249      break;
1250    case 2:
1251      exit(EXIT_FAILURE);
1252   }
1253}
1254
1255void
1256warning(int en, ...)
1257{
1258   va_list ap;
1259   va_start(ap, en);
1260   v_report(0, NULL, 0, en, ap);
1261   va_end(ap);
1262}
1263
1264void
1265error(int en, ...)
1266{
1267   va_list ap;
1268   va_start(ap, en);
1269   v_report(1, NULL, 0, en, ap);
1270   va_end(ap);
1271}
1272
1273void
1274fatalerror(int en, ...)
1275{
1276   va_list ap;
1277   va_start(ap, en);
1278   v_report(2, NULL, 0, en, ap);
1279   va_end(ap);
1280}
1281
1282void
1283warning_in_file(const char *fnm, int line, int en, ...)
1284{
1285   va_list ap;
1286   va_start(ap, en);
1287   v_report(0, fnm, line, en, ap);
1288   va_end(ap);
1289}
1290
1291void
1292error_in_file(const char *fnm, int line, int en, ...)
1293{
1294   va_list ap;
1295   va_start(ap, en);
1296   v_report(1, fnm, line, en, ap);
1297   va_end(ap);
1298}
1299
1300void
1301fatalerror_in_file(const char *fnm, int line, int en, ...)
1302{
1303   va_list ap;
1304   va_start(ap, en);
1305   v_report(2, fnm, line, en, ap);
1306   va_end(ap);
1307}
1308
1309/* Code to support switching character set at runtime (e.g. for a printer
1310 * driver to support different character sets on screen and on the printer)
1311 */
1312typedef struct charset_li {
1313   struct charset_li *next;
1314   int code;
1315   char **msg_array;
1316} charset_li;
1317
1318static charset_li *charset_head = NULL;
1319
1320static int charset = CHARSET_BAD;
1321
1322int
1323select_charset(int charset_code)
1324{
1325   int old_charset = charset;
1326   charset_li *p;
1327
1328#ifdef DEBUG
1329   fprintf(stderr, "select_charset(%d), old charset = %d\n", charset_code,
1330           charset);
1331#endif
1332
1333   charset = charset_code;
1334
1335   /* check if we've already parsed messages for new charset */
1336   for (p = charset_head; p; p = p->next) {
1337#ifdef DEBUG
1338      printf("%p: code %d msg_array %p\n", p, p->code, p->msg_array);
1339#endif
1340      if (p->code == charset) {
1341         msg_array = p->msg_array;
1342         return old_charset;
1343      }
1344   }
1345
1346   /* nope, got to reparse message file */
1347   parse_msg_file(charset_code);
1348
1349   /* add to list */
1350   p = osnew(charset_li);
1351   p->code = charset;
1352   p->msg_array = msg_array;
1353   p->next = charset_head;
1354   charset_head = p;
1355
1356   return old_charset;
1357}
Note: See TracBrowser for help on using the repository browser.