source: git/src/message.c @ 200d15e

v0.98
Last change on this file since 200d15e was 7d5f3c0, checked in by Olly Betts <olly@…>, 24 years ago

HAVE_SETJMP -> HAVE_SETJMP_H

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

  • Property mode set to 100644
File size: 24.3 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
683void
684msg_init(const char *argv0)
685{
686   char *p;
687
688#ifdef HAVE_SIGNAL
689   init_signals();
690#endif
691   /* Point to argv0 itself so we report a more helpful error if the code to work
692    * out the clean appname generates a signal */
693   appname_copy = argv0;
694#if (OS == UNIX)
695   /* use name as-is on Unix - programs run from path get name as supplied */
696   appname_copy = osstrdup(argv0);
697#else
698   /* use the lower-cased leafname on other platforms */
699   appname_copy = p = leaf_from_fnm(argv0);
700   while (*p) {
701      *p = tolower(*p);
702      p++;
703   }
704#endif
705
706   /* Look for env. var. "SURVEXHOME" or the like */
707   p = getenv("SURVEXHOME");
708   if (p && *p) {
709      pth_cfg_files = osstrdup(p);
710#if (OS==UNIX) && defined(DATADIR) && defined(PACKAGE)
711   } else {
712      /* under Unix, we compile in the configured path */
713      pth_cfg_files = DATADIR "/" PACKAGE;
714#else
715   } else if (argv0) {
716      /* else try the path on argv[0] */
717      pth_cfg_files = path_from_fnm(argv0);
718#endif
719   }
720
721   msg_lang = getenv("SURVEXLANG");
722#ifdef DEBUG
723   fprintf(stderr, "msg_lang = %p (= \"%s\")\n", msg_lang, msg_lang?msg_lang:"(null)");
724#endif
725
726   if (!msg_lang || !*msg_lang) {
727      msg_lang = getenv("LANG");
728      if (!msg_lang || !*msg_lang) {
729#if (OS==WIN32)
730         LCID locid;
731#endif
732         msg_lang = STRING(DEFAULTLANG);
733#if (OS==WIN32)
734         locid = GetUserDefaultLCID();
735         if (locid) {
736            WORD langid = LANGIDFROMLCID(locid);
737            switch (PRIMARYLANGID(langid)) {
738/* older mingw compilers don't seem to supply this value */
739#ifndef LANG_CATALAN
740# define LANG_CATALAN 0x03
741#endif
742             case LANG_CATALAN:
743               msg_lang = "ca";
744               break;
745             case LANG_ENGLISH:
746               if (SUBLANGID(langid) == SUBLANG_ENGLISH_US)
747                  msg_lang = "en_US";
748               else
749                  msg_lang = "en";
750               break;
751             case LANG_FRENCH:
752               msg_lang = "fr";
753               break;
754             case LANG_GERMAN:
755               switch (SUBLANGID(langid)) {
756                case SUBLANG_GERMAN_SWISS:
757                  msg_lang = "de_CH";
758                  break;
759                case SUBLANG_GERMAN:
760                  msg_lang = "de_DE";
761                  break;
762                default:
763                  msg_lang = "de";
764               }
765               break;
766             case LANG_ITALIAN:
767               msg_lang = "it";
768               break;
769             case LANG_PORTUGUESE:
770               if (SUBLANGID(langid) == SUBLANG_PORTUGUESE_BRAZILIAN)
771                  msg_lang = "pt_BR";
772               else
773                  msg_lang = "pt";
774               break;
775             case LANG_SPANISH:
776               msg_lang = "es";
777               break;
778            }
779         }
780#endif
781      }
782   }
783#ifdef DEBUG
784   fprintf(stderr, "msg_lang = %p (= \"%s\")\n", msg_lang, msg_lang?msg_lang:"(null)");
785#endif
786
787   /* On Mandrake LANG defaults to C */
788   if (strcmp(msg_lang, "C") == 0) msg_lang = "en";
789
790   msg_lang = osstrdup(msg_lang);
791
792   /* Convert en-us to en_US, etc */
793   p = strchr(msg_lang, '-');
794   if (p) {
795      *p++ = '_';
796      while (*p) {
797         *p = toupper(*p);
798         p++;
799      }
800   }
801
802   p = strchr(msg_lang, '_');
803   if (p) {
804      *p = '\0';
805      msg_lang2 = osstrdup(msg_lang);
806      *p = '_';
807   }
808
809#ifdef LC_MESSAGES
810   /* try to setlocale() appropriately too */
811   if (!setlocale(LC_MESSAGES, msg_lang)) {
812      if (msg_lang2) setlocale(LC_MESSAGES, msg_lang2);
813   }
814#endif
815
816   select_charset(default_charset());
817}
818
819/* Message may be overwritten by next call
820 * (but not in current implementation) */
821const char *
822msg(int en)
823{
824   /* NB can't use ASSERT here! */
825   static char badbuf[256];
826   if (en >= 1000 && en < 1000 + N_DONTEXTRACTMSGS)
827      return dontextract[en - 1000];
828   if (!msg_array) {
829      if (en != 1)  {
830         sprintf(badbuf, "Message %d requested before msg_array initialised\n", en);
831         return badbuf;
832      }
833      /* this should be the only other message which can be requested before
834       * the message file is opened and read... */
835      if (!dontextract) return "Out of memory (couldn't find %lu bytes).";
836      return dontextract[4];
837   }
838
839   if (en < 0 || en >= num_msgs) {
840      sprintf(badbuf, "Message %d out of range\n", en);
841      return badbuf;
842   }
843
844   return msg_array[en];
845}
846
847/* returns persistent copy of message */
848const char *
849msgPerm(int en)
850{
851   return msg(en);
852}
853
854void
855v_report(int severity, const char *fnm, int line, int en, va_list ap)
856{
857#ifdef AVEN
858   aven_v_report(severity, fnm, line, en, ap);
859#else
860   if (fnm) {
861      fputs(fnm, STDERR);
862      if (line) fprintf(STDERR, ":%d", line);
863   } else {
864      fputs(appname_copy, STDERR);
865   }
866   fputs(": ", STDERR);
867
868   if (severity == 0) {
869      fputs(msg(/*warning*/4), STDERR);
870      fputs(": ", STDERR);
871   }
872
873   vfprintf(STDERR, msg(en), ap);
874   fputnl(STDERR);
875#endif
876
877   switch (severity) {
878    case 0:
879      msg_warnings++;
880      break;
881    case 1:
882      msg_errors++;
883      if (msg_errors == 50)
884         fatalerror_in_file(fnm, 0, /*Too many errors - giving up*/19);
885      break;
886    case 2:
887      exit(EXIT_FAILURE);
888   }
889}
890
891void
892warning(int en, ...)
893{
894   va_list ap;
895   va_start(ap, en);
896   v_report(0, NULL, 0, en, ap);
897   va_end(ap);
898}
899
900void
901error(int en, ...)
902{
903   va_list ap;
904   va_start(ap, en);
905   v_report(1, NULL, 0, en, ap);
906   va_end(ap);
907}
908
909void
910fatalerror(int en, ...)
911{
912   va_list ap;
913   va_start(ap, en);
914   v_report(2, NULL, 0, en, ap);
915   va_end(ap);
916}
917
918void
919warning_in_file(const char *fnm, int line, int en, ...)
920{
921   va_list ap;
922   va_start(ap, en);
923   v_report(0, fnm, line, en, ap);
924   va_end(ap);
925}
926
927void
928error_in_file(const char *fnm, int line, int en, ...)
929{
930   va_list ap;
931   va_start(ap, en);
932   v_report(1, fnm, line, en, ap);
933   va_end(ap);
934}
935
936void
937fatalerror_in_file(const char *fnm, int line, int en, ...)
938{
939   va_list ap;
940   va_start(ap, en);
941   v_report(2, fnm, line, en, ap);
942   va_end(ap);
943}
944
945/* Code to support switching character set at runtime (e.g. for a printer
946 * driver to support different character sets on screen and on the printer)
947 */
948typedef struct charset_li {
949   struct charset_li *next;
950   int code;
951   char **msg_array;
952} charset_li;
953
954static charset_li *charset_head = NULL;
955
956static int charset = CHARSET_BAD;
957
958int
959select_charset(int charset_code)
960{
961   int old_charset = charset;
962   charset_li *p;
963
964#ifdef DEBUG
965   fprintf(stderr, "select_charset(%d), old charset = %d\n", charset_code,
966           charset);
967#endif
968
969   charset = charset_code;
970
971   /* check if we've already parsed messages for new charset */
972   for (p = charset_head; p; p = p->next) {
973#ifdef DEBUG
974      printf("%p: code %d msg_array %p\n", p, p->code, p->msg_array);
975#endif
976      if (p->code == charset) {
977         msg_array = p->msg_array;
978         return old_charset;
979      }
980   }
981
982   /* nope, got to reparse message file */
983   parse_msg_file(charset_code);
984
985   /* add to list */
986   p = osnew(charset_li);
987   p->code = charset;
988   p->msg_array = msg_array;
989   p->next = charset_head;
990   charset_head = p;
991
992   return old_charset;
993}
Note: See TracBrowser for help on using the repository browser.