source: git/src/message.c @ f62e481f

v0.99-prerelease4
Last change on this file since f62e481f was 652ef1b, checked in by Olly Betts <olly@…>, 24 years ago

More } fixes

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