source: git/src/message.c @ 4f222d2

RELEASE/1.0RELEASE/1.1RELEASE/1.2debug-cidebug-ci-sanitisersfaster-cavernloglog-selectstereostereo-2025walls-datawalls-data-hanging-as-warningwarn-only-for-hanging-survey
Last change on this file since 4f222d2 was fd5f670, checked in by Olly Betts <olly@…>, 24 years ago

Asking for messages in language en_GB now tries language en as it
was meant to.

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

  • Property mode set to 100644
File size: 25.7 KB
RevLine 
[fd5f670]1/* message.c
[db75e18]2 * Fairly general purpose message and error routines
[704ad2b]3 * Copyright (C) 1993-2001 Olly Betts
[846746e]4 *
[89231c4]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.
[846746e]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
[89231c4]12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
[846746e]14 *
[89231c4]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
[60f7018]18 */
19
[55de792]20/*#define DEBUG 1*/
21
[db75e18]22#ifdef HAVE_CONFIG_H
23# include <config.h>
24#endif
[60f7018]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>
[f764581]32#include <locale.h>
[60f7018]33
34#include "whichos.h"
[4432f2e]35#include "filename.h"
36#include "message.h"
[60f7018]37#include "osdepend.h"
38#include "filelist.h"
39#include "debug.h"
40
[a4140e9]41#ifdef AVEN
42#include "aven.h"
43#endif
44
[60f7018]45#ifdef HAVE_SIGNAL
[7d5f3c0]46# ifdef HAVE_SETJMP_H
[60f7018]47#  include <setjmp.h>
48static jmp_buf jmpbufSignal;
49#  include <signal.h>
50# else
51#  undef HAVE_SIGNAL
52# endif
53#endif
54
[6754d07]55#if (OS==WIN32)
56#include <windows.h>
57#endif
58
[2163157]59#if (OS==RISCOS)
60#include "oslib/wimpreadsy.h"
61#endif
62
[60f7018]63/* For funcs which want to be immune from messing around with different
64 * calling conventions */
65#ifndef CDECL
[2ca296b]66# define CDECL
[60f7018]67#endif
68
[25ab06b]69int msg_warnings = 0; /* keep track of how many warnings we've given */
70int msg_errors = 0;   /* and how many (non-fatal) errors */
[60f7018]71
[a4140e9]72/* in case osmalloc() fails before appname_copy is set up */
73static const char *appname_copy = "anonymous program";
[60f7018]74
75/* error code for failed osmalloc and osrealloc calls */
[db75e18]76static void
77outofmem(OSSIZE_T size)
78{
[bd1913f]79   fatalerror(/*Out of memory (couldn't find %lu bytes).*/1,
80              (unsigned long)size);
[60f7018]81}
82
[a420b49]83#ifdef TOMBSTONES
84#define TOMBSTONE_SIZE 16
[bd1913f]85static const char tombstone[TOMBSTONE_SIZE] = "012345\xfftombstone";
[a420b49]86#endif
87
[60f7018]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 */
[bd1913f]91void FAR *
[db75e18]92osmalloc(OSSIZE_T size)
93{
[60f7018]94   void FAR *p;
[a420b49]95#ifdef TOMBSTONES
96   size += TOMBSTONE_SIZE * 2;
97   p = malloc(size);
98#else
[db75e18]99   p = xosmalloc(size);
[a420b49]100#endif
[2ca296b]101   if (p == NULL) outofmem(size);
[a420b49]102#ifdef TOMBSTONES
[bd1913f]103   printf("osmalloc truep=%p truesize=%d\n", p, size);
[a420b49]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
[60f7018]109   return p;
110}
111
112/* realloc with error catching if it fails. */
[bd1913f]113void FAR *
[db75e18]114osrealloc(void *p, OSSIZE_T size)
115{
[a420b49]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;
[bd1913f]125      printf("osrealloc (in truep=%p truesize=%d)\n", p, true_size);
[a420b49]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!",
[cb3d1e2]129                p + TOMBSTONE_SIZE, true_size - TOMBSTONE_SIZE * 2);
[a420b49]130      }
131      if (memcmp(p + true_size - TOMBSTONE_SIZE, tombstone,
132                 TOMBSTONE_SIZE) != 0) {
133         printf("end tombstone for block %p, size %d corrupted!",
[cb3d1e2]134                p + TOMBSTONE_SIZE, true_size - TOMBSTONE_SIZE * 2);
[a420b49]135      }
136      p = realloc(p, size);
137      if (p == NULL) outofmem(size);
[bd1913f]138      printf("osrealloc truep=%p truesize=%d\n", p, size);
[a420b49]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   }
[2ca296b]147   if (p == NULL) outofmem(size);
[60f7018]148   return p;
149}
150
[b2c945f]151char FAR *
[db75e18]152osstrdup(const char *str)
153{
[60f7018]154   char *p;
[db75e18]155   OSSIZE_T len;
156   len = strlen(str) + 1;
[0a3c5fa]157   p = osmalloc(len);
[db75e18]158   memcpy(p, str, len);
[60f7018]159   return p;
160}
161
[a420b49]162/* osfree is usually just a macro in osalloc.h */
163#ifdef TOMBSTONES
[bd1913f]164void
[a420b49]165osfree(void *p)
166{
167   int true_size;
168   if (!p) return;
169   p -= TOMBSTONE_SIZE;
170   true_size = *(size_t *)p;
[bd1913f]171   printf("osfree truep=%p truesize=%d\n", p, true_size);
[a420b49]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!",
[cb3d1e2]175             p + TOMBSTONE_SIZE, true_size - TOMBSTONE_SIZE * 2);
[a420b49]176   }
177   if (memcmp(p + true_size - TOMBSTONE_SIZE, tombstone,
178              TOMBSTONE_SIZE) != 0) {
179      printf("end tombstone for block %p, size %d corrupted!",
[cb3d1e2]180             p + TOMBSTONE_SIZE, true_size - TOMBSTONE_SIZE * 2);
[a420b49]181   }
182   free(p);
183}
184#endif
[60f7018]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
[2ca296b]193# define RETSIGTYPE void
[60f7018]194#endif
195
[bd1913f]196static CDECL RETSIGTYPE FAR
197report_sig(int sig)
198{
[db75e18]199   sigReceived = sig;
[2ca296b]200   longjmp(jmpbufSignal, 1);
[60f7018]201}
202
[db75e18]203static void
204init_signals(void)
205{
[60f7018]206   int en;
207   if (!setjmp(jmpbufSignal)) {
[0a3c5fa]208#if 1 /* disable these to get a core dump */
[db75e18]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
[60f7018]214# ifdef SIGSTAK /* only on RISC OS AFAIK */
[db75e18]215      signal(SIGSTAK, report_sig); /* stack overflow */
[60f7018]216# endif
217      return;
218   }
[db75e18]219
[60f7018]220   switch (sigReceived) {
[bd1913f]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;
[60f7018]225# ifdef SIGSTAK
[bd1913f]226      case SIGSTAK: en = /*Stack overflow*/96; break;
[60f7018]227# endif
[bd1913f]228      default:      en = /*Unknown signal received*/97; break;
[60f7018]229   }
[db75e18]230   fputsnl(msg(en), STDERR);
[ea816ec]231
[c0a9908]232   /* Any of the signals we catch indicates a bug */
233   fatalerror(/*Bug in program detected! Please report this to the authors*/11);
[db75e18]234
[60f7018]235   exit(EXIT_FAILURE);
236}
237#endif
238
[bd1913f]239static int
240default_charset(void)
241{
[8769f9f]242#if (OS==RISCOS)
[ea816ec]243   /* RISCOS 3.1 and above CHARSET_RISCOS31 (ISO_8859_1 + extras in 128-159)
244    * RISCOS < 3.1 is ISO_8859_1 */
[2163157]245   int version;
[ea816ec]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;
[2163157]253
[2ca296b]254   return CHARSET_RISCOS31;
[60f7018]255#elif (OS==MSDOS)
256   return CHARSET_DOSCP850;
[a4140e9]257#elif (OS==WIN32)
[d5bd3a7]258# ifdef AVEN
[31ccf72]259#  define CODEPAGE GetACP()
[d5bd3a7]260# else
[31ccf72]261#  define CODEPAGE GetConsoleOutputCP()
262# endif
263   switch (CODEPAGE) {
[dc639a8]264    case 1252: return CHARSET_WINCP1252;
265    case 850: return CHARSET_DOSCP850;
[d5bd3a7]266   }
267   return CHARSET_USASCII;
[a4140e9]268#elif (OS==UNIX)
269#if defined(XCAVEROT) || defined(AVEN)
[8769f9f]270   return CHARSET_ISO_8859_1;
[a4140e9]271#else
[8c15864]272   const char *p = getenv("LC_ALL");
[31ccf72]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
[a4140e9]285   if (p) {
[8c15864]286      const char *chset = p;
[a4140e9]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;
[f8e03f3]315                   case 15: return CHARSET_ISO_8859_15;
[a4140e9]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'
[60f7018]337#endif
338}
339
[dc639a8]340#if (OS==MSDOS || OS==WIN32)
[db75e18]341static int
342xlate_dos_cp850(int unicode)
343{
[48e4121]344   switch (unicode) {
[d41b1353]345#include "uni2dos.h"
[48e4121]346   }
347   return 0;
348}
349#endif
350
[a4140e9]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
[db75e18]357static int
[55de792]358add_unicode(int charset, unsigned char *p, int value)
[db75e18]359{
[55de792]360#ifdef DEBUG
361   fprintf(stderr, "add_unicode(%d, %p, %d)\n", charset, p, value);
362#endif
[4432f2e]363   if (value == 0) return 0;
[48e4121]364   switch (charset) {
[db75e18]365   case CHARSET_USASCII:
[6a4871e]366      if (value < 0x80) {
[48e4121]367         *p = value;
368         return 1;
369      }
370      break;
[db75e18]371   case CHARSET_ISO_8859_1:
[6a4871e]372      if (value < 0x100) {
[48e4121]373         *p = value;
374         return 1;
375      }
[ea816ec]376      break;
[f8e03f3]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;
[48e4121]399#if (OS==RISCOS)
[ea816ec]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; */
[f8e03f3]405#if 0
[ea816ec]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; */
[f8e03f3]410#endif
[ea816ec]411      }
412      if (value < 0x100) {
413         *p = value;
414         return 1;
415      }
[48e4121]416      break;
[a4140e9]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; */
[f8e03f3]423#if 0
424      /* there are a few other obscure ones we don't currently need */
425#endif
[a4140e9]426      }
427      if (value < 0x100) {
428         *p = value;
429         return 1;
430      }
[e2ae719]431      break;
432#endif
433#if (OS==MSDOS || OS==WIN32)
[db75e18]434   case CHARSET_DOSCP850:
[48e4121]435      value = xlate_dos_cp850(value);
436      if (value) {
437         *p = value;
438         return 1;
439      }
440      break;
441#endif
[4432f2e]442   }
[a4140e9]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
[f1a5201]520   return 0;
[4432f2e]521}
522
[f2a6ce4]523/* fall back on looking in the current directory */
524static const char *pth_cfg_files = "";
525
[db75e18]526static int num_msgs = 0;
[55de792]527static char **msg_array = NULL;
[db75e18]528
[b83f907]529const char *msg_lang = NULL;
[c0a9908]530const char *msg_lang2 = NULL;
[b83f907]531
[d5bd3a7]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
[0874cd6a]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
[d5bd3a7]593/* No point extracting these errors as they won't get used if file opens */
[0874cd6a]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
[d5bd3a7]607
608static char **dontextract = NULL;
609
[db75e18]610static void
611parse_msg_file(int charset_code)
612{
613   FILE *fh;
614   unsigned char header[20];
[d5bd3a7]615   int i;   
[db75e18]616   unsigned len;
[55de792]617   unsigned char *p;
[b164c18]618   char *fnm, *s;
[d5bd3a7]619   int n;
[cb3d1e2]620
[55de792]621#ifdef DEBUG
622   fprintf(stderr, "parse_msg_file(%d)\n", charset_code);
623#endif
[db75e18]624
[d5bd3a7]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
[0a3c5fa]628   fnm = osstrdup(msg_lang);
[b164c18]629   /* trim off charset from stuff like "de_DE.iso8859_1" */
630   s = strchr(fnm, '.');
631   if (s) *s = '\0';
[a2f9d5c]632
[b164c18]633   fh = fopenWithPthAndExt(pth_cfg_files, fnm, EXT_SVX_MSG, "rb", NULL);
[db75e18]634
635   if (!fh) {
[fd5f670]636      /* e.g. if 'en_GB' is unknown, see if we know 'en' */
637      if (strlen(fnm) > 3 && fnm[2] == '_') {
[b164c18]638         fnm[2] = '\0';
639         fh = fopenWithPthAndExt(pth_cfg_files, fnm, EXT_SVX_MSG, "rb", NULL);
[fd5f670]640         if (!fh) fnm[2] = '_'; /* for error reporting */
[db75e18]641      }
[48e4121]642   }
[db75e18]643
644   if (!fh) {
[b07f165]645      fatalerror(/*Can't open message file `%s' using path `%s'*/1000,
646                 fnm, pth_cfg_files);
[4432f2e]647   }
[db75e18]648
649   if (fread(header, 1, 20, fh) < 20 ||
650       memcmp(header, "Svx\nMsg\r\n\xfe\xff", 12) != 0) {
[b07f165]651      fatalerror(/*Problem with message file `%s'*/1001, fnm);
[db75e18]652   }
653
[b07f165]654   if (header[12] != 0)
655      fatalerror(/*I don't understand this message file version*/1002);
[db75e18]656
[d5bd3a7]657   n = (header[14] << 8) | header[15];
[db75e18]658
659   len = 0;
660   for (i = 16; i < 20; i++) len = (len << 8) | header[i];
661
[0a3c5fa]662   p = osmalloc(len);
[b07f165]663   if (fread(p, 1, len, fh) < len)
664      fatalerror(/*Message file truncated?*/1003);
[2163157]665
[a420b49]666   fclose(fh);
[db75e18]667
[55de792]668#ifdef DEBUG
[d5bd3a7]669   fprintf(stderr, "fnm = `%s', n = %d, len = %d\n", fnm, n, len);
[55de792]670#endif
[b164c18]671   osfree(fnm);
[55de792]672
[d5bd3a7]673   msg_array = parse_msgs(n, p, charset_code);
674   num_msgs = n;
[4432f2e]675}
676
[f2a6ce4]677const char *
678msg_cfgpth(void)
679{
680   return pth_cfg_files;
681}
682
[bdfe97f]683const char *
684msg_appname(void)
685{
686   return appname_copy;
687}
688
[f2a6ce4]689void
[bdfe97f]690msg_init(char * const *argv)
[db75e18]691{
692   char *p;
[bdfe97f]693   ASSERT(argv);
[db75e18]694
695#ifdef HAVE_SIGNAL
696   init_signals();
697#endif
[bdfe97f]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];
[0a3c5fa]701#if (OS == UNIX)
702   /* use name as-is on Unix - programs run from path get name as supplied */
[bdfe97f]703   appname_copy = osstrdup(argv[0]);
[0a3c5fa]704#else
705   /* use the lower-cased leafname on other platforms */
[bdfe97f]706   appname_copy = p = leaf_from_fnm(argv[0]);
[4bfc8a7]707   while (*p) {
708      *p = tolower(*p);
709      p++;
710   }
[0a3c5fa]711#endif
[db75e18]712
[bdfe97f]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
[db75e18]720   /* Look for env. var. "SURVEXHOME" or the like */
721   p = getenv("SURVEXHOME");
722   if (p && *p) {
[0a3c5fa]723      pth_cfg_files = osstrdup(p);
[18f4759]724#if (OS==UNIX) && defined(DATADIR) && defined(PACKAGE)
[a420b49]725   } else {
726      /* under Unix, we compile in the configured path */
[18f4759]727      pth_cfg_files = DATADIR "/" PACKAGE;
[a420b49]728#else
[bdfe97f]729   } else if (argv[0]) {
[db75e18]730      /* else try the path on argv[0] */
[bdfe97f]731      pth_cfg_files = path_from_fnm(argv[0]);
[a420b49]732#endif
[db75e18]733   }
734
[b164c18]735   msg_lang = getenv("SURVEXLANG");
736#ifdef DEBUG
[74044b7]737   fprintf(stderr, "msg_lang = %p (= \"%s\")\n", msg_lang, msg_lang?msg_lang:"(null)");
[b164c18]738#endif
739
740   if (!msg_lang || !*msg_lang) {
741      msg_lang = getenv("LANG");
[74044b7]742      if (!msg_lang || !*msg_lang) {
743#if (OS==WIN32)
744         LCID locid;
745#endif
[37fca9b]746#ifdef DEFAULTLANG
[d5bd3a7]747         msg_lang = STRING(DEFAULTLANG);
[37fca9b]748#else
749         msg_lang = "en";
750#endif
[74044b7]751#if (OS==WIN32)
752         locid = GetUserDefaultLCID();
753         if (locid) {
754            WORD langid = LANGIDFROMLCID(locid);
755            switch (PRIMARYLANGID(langid)) {
[761c5f9]756/* older mingw compilers don't seem to supply this value */
757#ifndef LANG_CATALAN
758# define LANG_CATALAN 0x03
759#endif
[74044b7]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         }
[44740e7]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     
[84faf78b]847         }
848#endif
[74044b7]849      }
[b164c18]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
[0a3c5fa]858   msg_lang = osstrdup(msg_lang);
[b164c18]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';
[0a3c5fa]873      msg_lang2 = osstrdup(msg_lang);
[b164c18]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
[db75e18]884   select_charset(default_charset());
885}
886
[d5bd3a7]887/* Message may be overwritten by next call
[bd1913f]888 * (but not in current implementation) */
889const char *
[db75e18]890msg(int en)
891{
[eee67ab]892   /* NB can't use ASSERT here! */
893   static char badbuf[256];
[d5bd3a7]894   if (en >= 1000 && en < 1000 + N_DONTEXTRACTMSGS)
[b07f165]895      return dontextract[en - 1000];
[55de792]896   if (!msg_array) {
[eee67ab]897      if (en != 1)  {
898         sprintf(badbuf, "Message %d requested before msg_array initialised\n", en);
899         return badbuf;
900      }
[d5bd3a7]901      /* this should be the only other message which can be requested before
[a420b49]902       * the message file is opened and read... */
[d5bd3a7]903      if (!dontextract) return "Out of memory (couldn't find %lu bytes).";
904      return dontextract[4];
[db75e18]905   }
906
[eee67ab]907   if (en < 0 || en >= num_msgs) {
908      sprintf(badbuf, "Message %d out of range\n", en);
909      return badbuf;
910   }
[db75e18]911
[55de792]912   return msg_array[en];
[db75e18]913}
914
915/* returns persistent copy of message */
[bd1913f]916const char *
[db75e18]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{
[1f316f3]925#ifdef AVEN
[2e18955]926   aven_v_report(severity, fnm, line, en, ap);
[2163157]927#else
[db75e18]928   if (fnm) {
929      fputs(fnm, STDERR);
930      if (line) fprintf(STDERR, ":%d", line);
931   } else {
[a4140e9]932      fputs(appname_copy, STDERR);
[cb3d1e2]933   }
[db75e18]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);
[1f316f3]943#endif
[cb3d1e2]944
[db75e18]945   switch (severity) {
946    case 0:
[25ab06b]947      msg_warnings++;
[db75e18]948      break;
949    case 1:
[25ab06b]950      msg_errors++;
951      if (msg_errors == 50)
[db75e18]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;
[55de792]1019   char **msg_array;
[db75e18]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
[55de792]1032#ifdef DEBUG
[bd1913f]1033   fprintf(stderr, "select_charset(%d), old charset = %d\n", charset_code,
1034           charset);
[55de792]1035#endif
[cb3d1e2]1036
[db75e18]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) {
[55de792]1041#ifdef DEBUG
1042      printf("%p: code %d msg_array %p\n", p, p->code, p->msg_array);
1043#endif
[db75e18]1044      if (p->code == charset) {
[55de792]1045         msg_array = p->msg_array;
[db75e18]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;
[55de792]1056   p->msg_array = msg_array;
[db75e18]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.