source: git/src/message.c @ f17dbe2

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 f17dbe2 was 1b92879, checked in by Olly Betts <olly@…>, 24 years ago

(MSDOS version): pick a default language for messages by looking at the
currently set country.

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

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