source: git/src/message.c @ 96b1029

v0.99-prerelease1
Last change on this file since 96b1029 was 84faf78b, checked in by Olly Betts <olly@…>, 24 years ago

Fixed compilation problem.

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

  • Property mode set to 100644
File size: 25.7 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#endif
58
59#if (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#if (OS==MSDOS || OS==WIN32)
341static int
342xlate_dos_cp850(int unicode)
343{
344   switch (unicode) {
345#include "uni2dos.h"
346   }
347   return 0;
348}
349#endif
350
351/* It seems that Swedish and maybe some other scandanavian languages don't
352 * transliterate &auml; to ae - but it seems there may be conflicting views
353 * on this...
354 */
355#define umlaut_to_e() 1
356
357static int
358add_unicode(int charset, unsigned char *p, int value)
359{
360#ifdef DEBUG
361   fprintf(stderr, "add_unicode(%d, %p, %d)\n", charset, p, value);
362#endif
363   if (value == 0) return 0;
364   switch (charset) {
365   case CHARSET_USASCII:
366      if (value < 0x80) {
367         *p = value;
368         return 1;
369      }
370      break;
371   case CHARSET_ISO_8859_1:
372      if (value < 0x100) {
373         *p = value;
374         return 1;
375      }
376      break;
377   case CHARSET_ISO_8859_15:
378      switch (value) {
379       case 0xa4: case 0xa6: case 0xb0: case 0xc4:
380       case 0xd0: case 0xd4: case 0xd5: case 0xd6:
381         goto donthave;
382       case 0x152: value = 0xd4; break; /* &OElig; */
383       case 0x153: value = 0xd5; break; /* &oelig; */
384#if 0
385       case 0x0: value = 0xa4; break; /* euro */
386       case 0x0: value = 0xa6; break; /* Scaron */
387       case 0x0: value = 0xb0; break; /* scaron */
388       case 0x0: value = 0xc4; break; /* Zcaron */
389       case 0x0: value = 0xd0; break; /* zcaron */
390       case 0x0: value = 0xd6; break; /* Ydiersis */
391#endif
392      }
393      if (value < 0x100) {
394         *p = value;
395         return 1;
396      }
397      donthave:
398      break;
399#if (OS==RISCOS)
400   case CHARSET_RISCOS31:
401      /* RISC OS 3.1 (and later) extensions to ISO-8859-1 */
402      switch (value) {
403       case 0x152: value = 0x9a; break; /* &OElig; */
404       case 0x153: value = 0x9b; break; /* &oelig; */
405#if 0
406       case 0x174: value = 0x81; break; /* &Wcirc; */
407       case 0x175: value = 0x82; break; /* &wcirc; */
408       case 0x176: value = 0x85; break; /* &Ycirc; */
409       case 0x177: value = 0x86; break; /* &ycirc; */
410#endif
411      }
412      if (value < 0x100) {
413         *p = value;
414         return 1;
415      }
416      break;
417#elif (OS==WIN32)
418   case CHARSET_WINCP1252:
419      /* MS Windows extensions to ISO-8859-1 */
420      switch (value) {
421       case 0x152: value = 0x8c; break; /* &OElig; */
422       case 0x153: value = 0x9c; break; /* &oelig; */
423#if 0
424      /* there are a few other obscure ones we don't currently need */
425#endif
426      }
427      if (value < 0x100) {
428         *p = value;
429         return 1;
430      }
431      break;
432#endif
433#if (OS==MSDOS || OS==WIN32)
434   case CHARSET_DOSCP850:
435      value = xlate_dos_cp850(value);
436      if (value) {
437         *p = value;
438         return 1;
439      }
440      break;
441#endif
442   }
443   /* Transliterate characters we can't represent */
444#ifdef DEBUG
445   fprintf(stderr, "transliterate `%c' 0x%x\n", value, value);
446#endif
447   switch (value) {
448    case 160:
449      *p = ' '; return 1;
450    case 161 /* ¡ */:
451      *p = '!'; return 1;
452    case 171 /* « */:
453      p[1] = *p = '<'; return 2;
454    case 187 /* » */:
455      p[1] = *p = '>'; return 2;
456    case 191 /* ¿ */:
457      *p = '?'; return 1;
458    case 192 /* À */: case 193 /* Á */: case 194 /* Â */: case 195 /* Ã */:
459      *p = 'A'; return 1;
460    case 197 /* Å */:
461      p[1] = *p = 'A'; return 2;
462    case 196 /* Ä */: /* &Auml; */
463      *p = 'A';
464      if (!umlaut_to_e()) return 1;
465      p[1] = 'E'; return 2;
466    case 198 /* Æ */:
467      *p = 'A'; p[1] = 'E'; return 2;
468    case 199 /* Ç */:
469      *p = 'C'; return 1;
470    case 200 /* È */: case 201 /* É */: case 202 /* Ê */: case 203 /* Ë */:
471      *p = 'E'; return 1;
472    case 204 /* Ì */: case 205 /* Í */: case 206 /* Î */: case 207 /* Ï */:
473      *p = 'I'; return 1;
474    case 208 /* Ð */: case 222 /* Þ */:
475      *p = 'T'; p[1] = 'H'; return 2;
476    case 209 /* Ñ */:
477      *p = 'N'; return 1;
478    case 210 /* Ò */: case 211 /* Ó */: case 212 /* Ô */: case 213 /* Õ */:
479      *p = 'O'; return 1;
480    case 214 /* Ö */: /* &Ouml; */ case 0x152: /* &OElig; */
481      *p = 'O'; p[1] = 'E'; return 2;
482    case 217 /* Ù */: case 218 /* Ú */: case 219 /* Û */:
483      *p = 'U'; return 1;
484    case 220 /* Ü */: /* &Uuml; */
485      *p = 'U'; p[1] = 'E'; return 2;
486    case 221 /* Ý */:
487      *p = 'Y'; return 1;
488    case 223 /* ß */:
489      p[1] = *p = 's'; return 2;
490    case 224 /* à */: case 225 /* á */: case 226 /* â */: case 227 /* ã */:
491      *p = 'a'; return 1;
492    case 228 /* ä */: /* &auml; */ case 230 /* æ */:
493      *p = 'a'; p[1] = 'e'; return 2;
494    case 229 /* å */:
495      p[1] = *p = 'a'; return 2;
496    case 231 /* ç */:
497      *p = 'c'; return 1;
498    case 232 /* è */: case 233 /* é */: case 234 /* ê */: case 235 /* ë */:
499      *p = 'e'; return 1;
500    case 236 /* ì */: case 237 /* í */: case 238 /* î */: case 239 /* ï */:
501      *p = 'i'; return 1;
502    case 241 /* ñ */:
503      *p = 'n'; return 1;
504    case 240 /* ð */: case 254 /* þ */:
505      *p = 't'; p[1] = 'h'; return 2;
506    case 242 /* ò */: case 243 /* ó */: case 244 /* ô */: case 245 /* õ */:
507      *p = 'o'; return 1;
508    case 246 /* ö */: /* &ouml; */ case 0x153: /* &oelig; */
509      *p = 'o'; p[1] = 'e'; return 2;
510    case 249 /* ù */: case 250 /* ú */: case 251 /* û */:
511      *p = 'u'; return 1;
512    case 252 /* ü */: /* &uuml; */
513      *p = 'u'; p[1] = 'e'; return 2;
514    case 253 /* ý */: case 255 /* ÿ */:
515      *p = 'y'; return 1;
516   }
517#ifdef DEBUG
518   fprintf(stderr, "failed to transliterate\n");
519#endif
520   return 0;
521}
522
523/* fall back on looking in the current directory */
524static const char *pth_cfg_files = "";
525
526static int num_msgs = 0;
527static char **msg_array = NULL;
528
529const char *msg_lang = NULL;
530const char *msg_lang2 = NULL;
531
532static char **
533parse_msgs(int n, unsigned char *p, int charset_code) {
534   int i;
535
536   char **msgs = osmalloc(n * sizeof(char *));
537
538   for (i = 0; i < n; i++) {
539      unsigned char *to = p;
540      int ch;
541      msgs[i] = (char *)p;
542
543      /* If we want UTF8 anyway, we just need to find the start of each
544       * message */
545      if (charset_code == CHARSET_UTF8) {
546         p += strlen((char *)p) + 1;
547         continue;
548      }
549
550      while ((ch = *p++) != 0) {
551         /* A byte in the range 0x80-0xbf or 0xf0-0xff isn't valid in
552          * this state, (0xf0-0xfd mean values > 0xffff) so treat as
553          * literal and try to resync so we cope better when fed
554          * non-utf-8 data.  Similarly we abandon a multibyte sequence
555          * if we hit an invalid character. */
556         if (ch >= 0xc0 && ch < 0xf0) {
557            int ch1 = *p;
558            if ((ch1 & 0xc0) != 0x80) goto resync;
559
560            if (ch < 0xe0) {
561               /* 2 byte sequence */
562               ch = ((ch & 0x1f) << 6) | (ch1 & 0x3f);
563               p++;
564            } else {
565               /* 3 byte sequence */
566               int ch2 = p[1];
567               if ((ch2 & 0xc0) != 0x80) goto resync;
568               ch = ((ch & 0x1f) << 12) | ((ch1 & 0x3f) << 6) | (ch2 & 0x3f);
569               p += 2;
570            }
571         }
572
573         resync:
574
575         if (ch < 127) {
576            *to++ = (char)ch;
577         } else {
578            /* We assume an N byte UTF-8 code never transliterates to more
579             * than N characters (so we can't transliterate © to (C) or
580             * ® to (R) for example) */
581            to += add_unicode(charset_code, to, ch);
582         }
583      }
584      *to++ = '\0';
585   }
586   return msgs;
587}
588
589/* This is the name of the default language.  Add -DDEFAULTLANG to CFLAGS
590 * e.g. with `CFLAGS="-DDEFAULTLANG=fr" ./configure'
591 */
592#ifdef DEFAULTLANG
593/* No point extracting these errors as they won't get used if file opens */
594/* FIXME: this works on gcc but not some other compilers (e.g. norcroft),
595 * and also ../lib/fr.h, etc don't go into srcN_NN.zip */
596# define HDR(D) "../lib/"STRING(D)".h"
597# include HDR(DEFAULTLANG)
598#else
599#define N_DONTEXTRACTMSGS 5
600static unsigned char dontextractmsgs[] =
601   "Can't open message file `%s' using path `%s'\0"/*1000*/
602   "Problem with message file `%s'\0"/*1001*/
603   "I don't understand this message file version\0"/*1002*/
604   "Message file truncated?\0"/*1003*/
605   "Out of memory (couldn't find %lu bytes).\0"/*1004*/;
606#endif
607
608static char **dontextract = NULL;
609
610static void
611parse_msg_file(int charset_code)
612{
613   FILE *fh;
614   unsigned char header[20];
615   int i;   
616   unsigned len;
617   unsigned char *p;
618   char *fnm, *s;
619   int n;
620
621#ifdef DEBUG
622   fprintf(stderr, "parse_msg_file(%d)\n", charset_code);
623#endif
624
625   /* sort out messages we need to print if we can't open the message file */
626   dontextract = parse_msgs(N_DONTEXTRACTMSGS, dontextractmsgs, charset_code);
627
628   fnm = osstrdup(msg_lang);
629   /* trim off charset from stuff like "de_DE.iso8859_1" */
630   s = strchr(fnm, '.');
631   if (s) *s = '\0';
632
633   fh = fopenWithPthAndExt(pth_cfg_files, fnm, EXT_SVX_MSG, "rb", NULL);
634
635   if (!fh) {
636      /* e.g. if 'en-COCKNEY' is unknown, see if we know 'en' */
637      if (strlen(fnm) > 3 && fnm[2] == '-') {
638         fnm[2] = '\0';
639         fh = fopenWithPthAndExt(pth_cfg_files, fnm, EXT_SVX_MSG, "rb", NULL);
640         if (!fh) fnm[2] = '-'; /* for error reporting */
641      }
642   }
643
644   if (!fh) {
645      fatalerror(/*Can't open message file `%s' using path `%s'*/1000,
646                 fnm, pth_cfg_files);
647   }
648
649   if (fread(header, 1, 20, fh) < 20 ||
650       memcmp(header, "Svx\nMsg\r\n\xfe\xff", 12) != 0) {
651      fatalerror(/*Problem with message file `%s'*/1001, fnm);
652   }
653
654   if (header[12] != 0)
655      fatalerror(/*I don't understand this message file version*/1002);
656
657   n = (header[14] << 8) | header[15];
658
659   len = 0;
660   for (i = 16; i < 20; i++) len = (len << 8) | header[i];
661
662   p = osmalloc(len);
663   if (fread(p, 1, len, fh) < len)
664      fatalerror(/*Message file truncated?*/1003);
665
666   fclose(fh);
667
668#ifdef DEBUG
669   fprintf(stderr, "fnm = `%s', n = %d, len = %d\n", fnm, n, len);
670#endif
671   osfree(fnm);
672
673   msg_array = parse_msgs(n, p, charset_code);
674   num_msgs = n;
675}
676
677const char *
678msg_cfgpth(void)
679{
680   return pth_cfg_files;
681}
682
683const char *
684msg_appname(void)
685{
686   return appname_copy;
687}
688
689void
690msg_init(char * const *argv)
691{
692   char *p;
693   ASSERT(argv);
694
695#ifdef HAVE_SIGNAL
696   init_signals();
697#endif
698   /* Point to argv[0] itself so we report a more helpful error if the
699    * code to work out the clean appname generates a signal */
700   appname_copy = argv[0];
701#if (OS == UNIX)
702   /* use name as-is on Unix - programs run from path get name as supplied */
703   appname_copy = osstrdup(argv[0]);
704#else
705   /* use the lower-cased leafname on other platforms */
706   appname_copy = p = leaf_from_fnm(argv[0]);
707   while (*p) {
708      *p = tolower(*p);
709      p++;
710   }
711#endif
712
713   /* shortcut --version so you can check the version number when the correct
714    * message file can't be found... */
715   if (argv[1] && strcmp(argv[1], "--version") == 0) {
716      cmdline_version();
717      exit(0);
718   }
719
720   /* Look for env. var. "SURVEXHOME" or the like */
721   p = getenv("SURVEXHOME");
722   if (p && *p) {
723      pth_cfg_files = osstrdup(p);
724#if (OS==UNIX) && defined(DATADIR) && defined(PACKAGE)
725   } else {
726      /* under Unix, we compile in the configured path */
727      pth_cfg_files = DATADIR "/" PACKAGE;
728#else
729   } else if (argv[0]) {
730      /* else try the path on argv[0] */
731      pth_cfg_files = path_from_fnm(argv[0]);
732#endif
733   }
734
735   msg_lang = getenv("SURVEXLANG");
736#ifdef DEBUG
737   fprintf(stderr, "msg_lang = %p (= \"%s\")\n", msg_lang, msg_lang?msg_lang:"(null)");
738#endif
739
740   if (!msg_lang || !*msg_lang) {
741      msg_lang = getenv("LANG");
742      if (!msg_lang || !*msg_lang) {
743#if (OS==WIN32)
744         LCID locid;
745#endif
746#ifdef DEFAULTLANG
747         msg_lang = STRING(DEFAULTLANG);
748#else
749         msg_lang = "en";
750#endif
751#if (OS==WIN32)
752         locid = GetUserDefaultLCID();
753         if (locid) {
754            WORD langid = LANGIDFROMLCID(locid);
755            switch (PRIMARYLANGID(langid)) {
756/* older mingw compilers don't seem to supply this value */
757#ifndef LANG_CATALAN
758# define LANG_CATALAN 0x03
759#endif
760             case LANG_CATALAN:
761               msg_lang = "ca";
762               break;
763             case LANG_ENGLISH:
764               if (SUBLANGID(langid) == SUBLANG_ENGLISH_US)
765                  msg_lang = "en_US";
766               else
767                  msg_lang = "en";
768               break;
769             case LANG_FRENCH:
770               msg_lang = "fr";
771               break;
772             case LANG_GERMAN:
773               switch (SUBLANGID(langid)) {
774                case SUBLANG_GERMAN_SWISS:
775                  msg_lang = "de_CH";
776                  break;
777                case SUBLANG_GERMAN:
778                  msg_lang = "de_DE";
779                  break;
780                default:
781                  msg_lang = "de";
782               }
783               break;
784             case LANG_ITALIAN:
785               msg_lang = "it";
786               break;
787             case LANG_PORTUGUESE:
788               if (SUBLANGID(langid) == SUBLANG_PORTUGUESE_BRAZILIAN)
789                  msg_lang = "pt_BR";
790               else
791                  msg_lang = "pt";
792               break;
793             case LANG_SPANISH:
794               msg_lang = "es";
795               break;
796            }
797         }
798#elif (OS==RISCOS)
799         switch (xterritory_number()) {
800          case 1: /* UK */
801          case 2: /* Master */
802          case 3: /* Compact */
803          case 17: /* Canada1 */
804          case 19: /* Canada */
805          case 22: /* Ireland */
806            msg_lang = "en";
807            break;
808          case 4: /* Italy */
809            msg_lang = "it";
810            break;
811          case 5: /* Spain */
812          case 27: /* Mexico */
813            msg_lang = "es"; /* or possibly ca for Spain */
814            break;
815          case 6: /* France */
816          case 18: /* Canada2 */
817            msg_lang = "fr";
818            break;
819          case 7: /* Germany */
820            msg_lang = "de_DE";
821            break;
822          case 8: /* Portugal */
823            msg_lang = "pt";
824            break;
825          case 48: /* USA */
826            msg_lang = "en_US";
827            break;
828          case 28: /* LatinAm */
829            msg_lang = "pt_BR"; /* or many other possibilities */
830            break;         
831#if 0
832          case 9: /* Esperanto */
833          case 10: /* Greece */
834          case 11: /* Sweden */
835          case 12: /* Finland */
836          case 13: /* Unused */
837          case 14: /* Denmark */
838          case 15: /* Norway */
839          case 16: /* Iceland */
840          case 20: /* Turkey */
841          case 21: /* Arabic */
842          case 23: /* Hong Kong */
843          case 24: /* Russia */
844          case 25: /* Russia2 */
845          case 26: /* Israel */
846#endif     
847         }
848#endif
849      }
850   }
851#ifdef DEBUG
852   fprintf(stderr, "msg_lang = %p (= \"%s\")\n", msg_lang, msg_lang?msg_lang:"(null)");
853#endif
854
855   /* On Mandrake LANG defaults to C */
856   if (strcmp(msg_lang, "C") == 0) msg_lang = "en";
857
858   msg_lang = osstrdup(msg_lang);
859
860   /* Convert en-us to en_US, etc */
861   p = strchr(msg_lang, '-');
862   if (p) {
863      *p++ = '_';
864      while (*p) {
865         *p = toupper(*p);
866         p++;
867      }
868   }
869
870   p = strchr(msg_lang, '_');
871   if (p) {
872      *p = '\0';
873      msg_lang2 = osstrdup(msg_lang);
874      *p = '_';
875   }
876
877#ifdef LC_MESSAGES
878   /* try to setlocale() appropriately too */
879   if (!setlocale(LC_MESSAGES, msg_lang)) {
880      if (msg_lang2) setlocale(LC_MESSAGES, msg_lang2);
881   }
882#endif
883
884   select_charset(default_charset());
885}
886
887/* Message may be overwritten by next call
888 * (but not in current implementation) */
889const char *
890msg(int en)
891{
892   /* NB can't use ASSERT here! */
893   static char badbuf[256];
894   if (en >= 1000 && en < 1000 + N_DONTEXTRACTMSGS)
895      return dontextract[en - 1000];
896   if (!msg_array) {
897      if (en != 1)  {
898         sprintf(badbuf, "Message %d requested before msg_array initialised\n", en);
899         return badbuf;
900      }
901      /* this should be the only other message which can be requested before
902       * the message file is opened and read... */
903      if (!dontextract) return "Out of memory (couldn't find %lu bytes).";
904      return dontextract[4];
905   }
906
907   if (en < 0 || en >= num_msgs) {
908      sprintf(badbuf, "Message %d out of range\n", en);
909      return badbuf;
910   }
911
912   return msg_array[en];
913}
914
915/* returns persistent copy of message */
916const char *
917msgPerm(int en)
918{
919   return msg(en);
920}
921
922void
923v_report(int severity, const char *fnm, int line, int en, va_list ap)
924{
925#ifdef AVEN
926   aven_v_report(severity, fnm, line, en, ap);
927#else
928   if (fnm) {
929      fputs(fnm, STDERR);
930      if (line) fprintf(STDERR, ":%d", line);
931   } else {
932      fputs(appname_copy, STDERR);
933   }
934   fputs(": ", STDERR);
935
936   if (severity == 0) {
937      fputs(msg(/*warning*/4), STDERR);
938      fputs(": ", STDERR);
939   }
940
941   vfprintf(STDERR, msg(en), ap);
942   fputnl(STDERR);
943#endif
944
945   switch (severity) {
946    case 0:
947      msg_warnings++;
948      break;
949    case 1:
950      msg_errors++;
951      if (msg_errors == 50)
952         fatalerror_in_file(fnm, 0, /*Too many errors - giving up*/19);
953      break;
954    case 2:
955      exit(EXIT_FAILURE);
956   }
957}
958
959void
960warning(int en, ...)
961{
962   va_list ap;
963   va_start(ap, en);
964   v_report(0, NULL, 0, en, ap);
965   va_end(ap);
966}
967
968void
969error(int en, ...)
970{
971   va_list ap;
972   va_start(ap, en);
973   v_report(1, NULL, 0, en, ap);
974   va_end(ap);
975}
976
977void
978fatalerror(int en, ...)
979{
980   va_list ap;
981   va_start(ap, en);
982   v_report(2, NULL, 0, en, ap);
983   va_end(ap);
984}
985
986void
987warning_in_file(const char *fnm, int line, int en, ...)
988{
989   va_list ap;
990   va_start(ap, en);
991   v_report(0, fnm, line, en, ap);
992   va_end(ap);
993}
994
995void
996error_in_file(const char *fnm, int line, int en, ...)
997{
998   va_list ap;
999   va_start(ap, en);
1000   v_report(1, fnm, line, en, ap);
1001   va_end(ap);
1002}
1003
1004void
1005fatalerror_in_file(const char *fnm, int line, int en, ...)
1006{
1007   va_list ap;
1008   va_start(ap, en);
1009   v_report(2, fnm, line, en, ap);
1010   va_end(ap);
1011}
1012
1013/* Code to support switching character set at runtime (e.g. for a printer
1014 * driver to support different character sets on screen and on the printer)
1015 */
1016typedef struct charset_li {
1017   struct charset_li *next;
1018   int code;
1019   char **msg_array;
1020} charset_li;
1021
1022static charset_li *charset_head = NULL;
1023
1024static int charset = CHARSET_BAD;
1025
1026int
1027select_charset(int charset_code)
1028{
1029   int old_charset = charset;
1030   charset_li *p;
1031
1032#ifdef DEBUG
1033   fprintf(stderr, "select_charset(%d), old charset = %d\n", charset_code,
1034           charset);
1035#endif
1036
1037   charset = charset_code;
1038
1039   /* check if we've already parsed messages for new charset */
1040   for (p = charset_head; p; p = p->next) {
1041#ifdef DEBUG
1042      printf("%p: code %d msg_array %p\n", p, p->code, p->msg_array);
1043#endif
1044      if (p->code == charset) {
1045         msg_array = p->msg_array;
1046         return old_charset;
1047      }
1048   }
1049
1050   /* nope, got to reparse message file */
1051   parse_msg_file(charset_code);
1052
1053   /* add to list */
1054   p = osnew(charset_li);
1055   p->code = charset;
1056   p->msg_array = msg_array;
1057   p->next = charset_head;
1058   charset_head = p;
1059
1060   return old_charset;
1061}
Note: See TracBrowser for help on using the repository browser.