source: git/src/message.c @ 45c17c8

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 45c17c8 was a4140e9, checked in by Olly Betts <olly@…>, 24 years ago

Added support for MS Windows CP1252 (extended version of iso-8859-1).

(Unix version): now get character set from $LANG.

Transliterate accented characters that can't be represented in the
current character set.

aven: reporting errors in a message box wasn't working (wxWindows
FormatV bug - workaround using PrintfV instead).

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

  • Property mode set to 100644
File size: 22.4 KB
RevLine 
[db75e18]1/* > message.c
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
46# ifdef HAVE_SETJMP
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
[647407d]63/* This is the name of the default language.  Add -DDEFAULTLANG to CFLAGS
64 * e.g. with `CFLAGS="-DDEFAULTLANG=fr" ./configure'
[60f7018]65 */
66#ifndef DEFAULTLANG
[4432f2e]67# define DEFAULTLANG "en"
[60f7018]68#endif
69
70/* For funcs which want to be immune from messing around with different
71 * calling conventions */
72#ifndef CDECL
[2ca296b]73# define CDECL
[60f7018]74#endif
75
[25ab06b]76int msg_warnings = 0; /* keep track of how many warnings we've given */
77int msg_errors = 0;   /* and how many (non-fatal) errors */
[60f7018]78
[a4140e9]79/* in case osmalloc() fails before appname_copy is set up */
80static const char *appname_copy = "anonymous program";
[60f7018]81
82/* error code for failed osmalloc and osrealloc calls */
[db75e18]83static void
84outofmem(OSSIZE_T size)
85{
[bd1913f]86   fatalerror(/*Out of memory (couldn't find %lu bytes).*/1,
87              (unsigned long)size);
[60f7018]88}
89
[a420b49]90#ifdef TOMBSTONES
91#define TOMBSTONE_SIZE 16
[bd1913f]92static const char tombstone[TOMBSTONE_SIZE] = "012345\xfftombstone";
[a420b49]93#endif
94
[60f7018]95/* malloc with error catching if it fails. Also allows us to write special
96 * versions easily eg for DOS EMS or MS Windows.
97 */
[bd1913f]98void FAR *
[db75e18]99osmalloc(OSSIZE_T size)
100{
[60f7018]101   void FAR *p;
[a420b49]102#ifdef TOMBSTONES
103   size += TOMBSTONE_SIZE * 2;
104   p = malloc(size);
105#else
[db75e18]106   p = xosmalloc(size);
[a420b49]107#endif
[2ca296b]108   if (p == NULL) outofmem(size);
[a420b49]109#ifdef TOMBSTONES
[bd1913f]110   printf("osmalloc truep=%p truesize=%d\n", p, size);
[a420b49]111   memcpy(p, tombstone, TOMBSTONE_SIZE);
112   memcpy(p + size - TOMBSTONE_SIZE, tombstone, TOMBSTONE_SIZE);
113   *(size_t *)p = size;
114   p += TOMBSTONE_SIZE;
115#endif
[60f7018]116   return p;
117}
118
119/* realloc with error catching if it fails. */
[bd1913f]120void FAR *
[db75e18]121osrealloc(void *p, OSSIZE_T size)
122{
[a420b49]123   /* some pre-ANSI realloc implementations don't cope with a NULL pointer */
124   if (p == NULL) {
125      p = xosmalloc(size);
126   } else {
127#ifdef TOMBSTONES
128      int true_size;
129      size += TOMBSTONE_SIZE * 2;
130      p -= TOMBSTONE_SIZE;
131      true_size = *(size_t *)p;
[bd1913f]132      printf("osrealloc (in truep=%p truesize=%d)\n", p, true_size);
[a420b49]133      if (memcmp(p + sizeof(size_t), tombstone + sizeof(size_t),
134                 TOMBSTONE_SIZE - sizeof(size_t)) != 0) {
135         printf("start tombstone for block %p, size %d corrupted!",
[cb3d1e2]136                p + TOMBSTONE_SIZE, true_size - TOMBSTONE_SIZE * 2);
[a420b49]137      }
138      if (memcmp(p + true_size - TOMBSTONE_SIZE, tombstone,
139                 TOMBSTONE_SIZE) != 0) {
140         printf("end tombstone for block %p, size %d corrupted!",
[cb3d1e2]141                p + TOMBSTONE_SIZE, true_size - TOMBSTONE_SIZE * 2);
[a420b49]142      }
143      p = realloc(p, size);
144      if (p == NULL) outofmem(size);
[bd1913f]145      printf("osrealloc truep=%p truesize=%d\n", p, size);
[a420b49]146      memcpy(p, tombstone, TOMBSTONE_SIZE);
147      memcpy(p + size - TOMBSTONE_SIZE, tombstone, TOMBSTONE_SIZE);
148      *(size_t *)p = size;
149      p += TOMBSTONE_SIZE;
150#else
151      p = xosrealloc(p, size);
152#endif
153   }
[2ca296b]154   if (p == NULL) outofmem(size);
[60f7018]155   return p;
156}
157
[b2c945f]158char FAR *
[db75e18]159osstrdup(const char *str)
160{
[60f7018]161   char *p;
[db75e18]162   OSSIZE_T len;
163   len = strlen(str) + 1;
[0a3c5fa]164   p = osmalloc(len);
[db75e18]165   memcpy(p, str, len);
[60f7018]166   return p;
167}
168
[a420b49]169/* osfree is usually just a macro in osalloc.h */
170#ifdef TOMBSTONES
[bd1913f]171void
[a420b49]172osfree(void *p)
173{
174   int true_size;
175   if (!p) return;
176   p -= TOMBSTONE_SIZE;
177   true_size = *(size_t *)p;
[bd1913f]178   printf("osfree truep=%p truesize=%d\n", p, true_size);
[a420b49]179   if (memcmp(p + sizeof(size_t), tombstone + sizeof(size_t),
180              TOMBSTONE_SIZE - sizeof(size_t)) != 0) {
181      printf("start tombstone for block %p, size %d corrupted!",
[cb3d1e2]182             p + TOMBSTONE_SIZE, true_size - TOMBSTONE_SIZE * 2);
[a420b49]183   }
184   if (memcmp(p + true_size - TOMBSTONE_SIZE, tombstone,
185              TOMBSTONE_SIZE) != 0) {
186      printf("end tombstone for block %p, size %d corrupted!",
[cb3d1e2]187             p + TOMBSTONE_SIZE, true_size - TOMBSTONE_SIZE * 2);
[a420b49]188   }
189   free(p);
190}
191#endif
[60f7018]192
193#ifdef HAVE_SIGNAL
194
195static int sigReceived;
196
197/* for systems not using autoconf, assume the signal handler returns void
198 * unless specified elsewhere */
199#ifndef RETSIGTYPE
[2ca296b]200# define RETSIGTYPE void
[60f7018]201#endif
202
[bd1913f]203static CDECL RETSIGTYPE FAR
204report_sig(int sig)
205{
[db75e18]206   sigReceived = sig;
[2ca296b]207   longjmp(jmpbufSignal, 1);
[60f7018]208}
209
[db75e18]210static void
211init_signals(void)
212{
[60f7018]213   int en;
214   if (!setjmp(jmpbufSignal)) {
[0a3c5fa]215#if 1 /* disable these to get a core dump */
[db75e18]216      signal(SIGABRT, report_sig); /* abnormal termination eg abort() */
217      signal(SIGFPE,  report_sig); /* arithmetic error eg /0 or overflow */
218      signal(SIGILL,  report_sig); /* illegal function image eg illegal instruction */
219      signal(SIGSEGV, report_sig); /* illegal storage access eg access outside memory limits */
220#endif
[60f7018]221# ifdef SIGSTAK /* only on RISC OS AFAIK */
[db75e18]222      signal(SIGSTAK, report_sig); /* stack overflow */
[60f7018]223# endif
224      return;
225   }
[db75e18]226
[60f7018]227   switch (sigReceived) {
[bd1913f]228      case SIGABRT: en = /*Abnormal termination*/90; break;
229      case SIGFPE:  en = /*Arithmetic error*/91; break;
230      case SIGILL:  en = /*Illegal instruction*/92; break;
231      case SIGSEGV: en = /*Bad memory access*/94; break;
[60f7018]232# ifdef SIGSTAK
[bd1913f]233      case SIGSTAK: en = /*Stack overflow*/96; break;
[60f7018]234# endif
[bd1913f]235      default:      en = /*Unknown signal received*/97; break;
[60f7018]236   }
[db75e18]237   fputsnl(msg(en), STDERR);
[ea816ec]238
[c0a9908]239   /* Any of the signals we catch indicates a bug */
240   fatalerror(/*Bug in program detected! Please report this to the authors*/11);
[db75e18]241
[60f7018]242   exit(EXIT_FAILURE);
243}
244#endif
245
[bd1913f]246static int
247default_charset(void)
248{
[8769f9f]249#if (OS==RISCOS)
[ea816ec]250   /* RISCOS 3.1 and above CHARSET_RISCOS31 (ISO_8859_1 + extras in 128-159)
251    * RISCOS < 3.1 is ISO_8859_1 */
[2163157]252   int version;
[ea816ec]253   if (xwimpreadsysinfo_version(&version) != NULL) {
254      /* RISC OS 2 or some error (don't care which) */
255      return CHARSET_ISO_8859_1;
256   }
257
258   /* oddly wimp_VERSION_RO3 is RISC OS 3.1 */
259   if (version < wimp_VERSION_RO3) return CHARSET_ISO_8859_1;
[2163157]260
[2ca296b]261   return CHARSET_RISCOS31;
[60f7018]262#elif (OS==MSDOS)
263   return CHARSET_DOSCP850;
[a4140e9]264#elif (OS==WIN32)
265   return CHARSET_WINCP1252:
266#elif (OS==UNIX)
267#if defined(XCAVEROT) || defined(AVEN)
[8769f9f]268   return CHARSET_ISO_8859_1;
[a4140e9]269#else
270   char *p = strchr(msg_lang, '.');
271   if (p) {
272      char *chset = ++p;
273      size_t name_len;
274
275      while (*p != '\0' && *p != '@') p++;
276
277      name_len = p - chset;
278
279      if (name_len) {
280         int only_digit = 1;
281         size_t cnt;
282       
283         for (cnt = 0; cnt < name_len; ++cnt)
284            if (isalpha(chset[cnt])) {
285               only_digit = 0;
286               break;
287            }
288
289         if (only_digit) goto iso;
290         
291         switch (tolower(chset[0])) {
292          case 'i':
293            if (tolower(chset[1]) == 's' && tolower(chset[2]) == 'o') {
294               chset += 3;
295               iso:
296               if (strncmp(chset, "8859", 4) == 0) {
297                  chset += 4;
298                  while (chset < p && *chset && !isdigit(*chset)) chset++;
299                  switch (atoi(chset)) {
300                   case 1: return CHARSET_ISO_8859_1;
301                   default: return CHARSET_USASCII;               
302                  }
303               }
304            }
305            break;
306          case 'u':
307            if (tolower(chset[1]) == 't' && tolower(chset[2]) == 'f') {
308               chset += 3;
309               while (chset < p && *chset && !isdigit(*chset)) chset++;
310               switch (atoi(chset)) {
311                case 8: return CHARSET_UTF8;
312                default: return CHARSET_USASCII;
313               }
314            }
315         }
316      }
317   }
318   return CHARSET_USASCII;
319#endif
320#else
321# error Do not know operating system 'OS'
[60f7018]322#endif
323}
324
[48e4121]325#if (OS==MSDOS)
[db75e18]326static int
327xlate_dos_cp850(int unicode)
328{
[48e4121]329   switch (unicode) {
[d41b1353]330#include "uni2dos.h"
[48e4121]331   }
332   return 0;
333}
334#endif
335
[a4140e9]336/* It seems that Swedish and maybe some other scandanavian languages don't
337 * transliterate &auml; to ae - but it seems there may be conflicting views
338 * on this...
339 */
340#define umlaut_to_e() 1
341
[db75e18]342static int
[55de792]343add_unicode(int charset, unsigned char *p, int value)
[db75e18]344{
[55de792]345#ifdef DEBUG
346   fprintf(stderr, "add_unicode(%d, %p, %d)\n", charset, p, value);
347#endif
[4432f2e]348   if (value == 0) return 0;
[48e4121]349   switch (charset) {
[db75e18]350   case CHARSET_USASCII:
[6a4871e]351      if (value < 0x80) {
[48e4121]352         *p = value;
353         return 1;
354      }
355      break;
[db75e18]356   case CHARSET_ISO_8859_1:
[6a4871e]357      if (value < 0x100) {
[48e4121]358         *p = value;
359         return 1;
360      }
[ea816ec]361      break;
[48e4121]362#if (OS==RISCOS)
[ea816ec]363   case CHARSET_RISCOS31:
364      /* RISC OS 3.1 (and later) extensions to ISO-8859-1 */
365      switch (value) {
366       case 0x152: value = 0x9a; break; /* &OElig; */
367       case 0x153: value = 0x9b; break; /* &oelig; */
368       case 0x174: value = 0x81; break; /* &Wcirc; */
369       case 0x175: value = 0x82; break; /* &wcirc; */
370       case 0x176: value = 0x85; break; /* &Ycirc; */
371       case 0x177: value = 0x86; break; /* &ycirc; */
372      }
373      if (value < 0x100) {
374         *p = value;
375         return 1;
376      }
[48e4121]377      break;
[a4140e9]378#elif (OS==WIN32)
379   case CHARSET_WINCP1252:
380      /* MS Windows extensions to ISO-8859-1 */
381      /* there are a few other obscure ones we don't currently need */
382      switch (value) {
383       case 0x152: value = 0x8c; break; /* &OElig; */
384       case 0x153: value = 0x9c; break; /* &oelig; */
385      }
386      if (value < 0x100) {
387         *p = value;
388         return 1;
389      }
390      break;     
391#elif (OS==MSDOS)
[db75e18]392   case CHARSET_DOSCP850:
[48e4121]393      value = xlate_dos_cp850(value);
394      if (value) {
395         *p = value;
396         return 1;
397      }
398      break;
399#endif
[4432f2e]400   }
[a4140e9]401   /* Transliterate characters we can't represent */
402#ifdef DEBUG
403   fprintf(stderr, "transliterate `%c' 0x%x\n", value, value);
404#endif
405   switch (value) {
406    case 160:
407      *p = ' '; return 1;
408    case 161 /* ¡ */:
409      *p = '!'; return 1;
410    case 171 /* « */:
411      p[1] = *p = '<'; return 2;
412    case 187 /* » */:
413      p[1] = *p = '>'; return 2;
414    case 191 /* ¿ */:
415      *p = '?'; return 1;
416    case 192 /* À */: case 193 /* Á */: case 194 /* Â */: case 195 /* Ã */:
417      *p = 'A'; return 1;
418    case 197 /* Å */:
419      p[1] = *p = 'A'; return 2;
420    case 196 /* Ä */: /* &Auml; */
421      *p = 'A';
422      if (!umlaut_to_e()) return 1;
423      p[1] = 'E'; return 2;
424    case 198 /* Æ */:
425      *p = 'A'; p[1] = 'E'; return 2;
426    case 199 /* Ç */:
427      *p = 'C'; return 1;
428    case 200 /* È */: case 201 /* É */: case 202 /* Ê */: case 203 /* Ë */:
429      *p = 'E'; return 1;
430    case 204 /* Ì */: case 205 /* Í */: case 206 /* Î */: case 207 /* Ï */:
431      *p = 'I'; return 1;
432    case 208 /* Ð */: case 222 /* Þ */:
433      *p = 'T'; p[1] = 'H'; return 2;
434    case 209 /* Ñ */:
435      *p = 'N'; return 1;
436    case 210 /* Ò */: case 211 /* Ó */: case 212 /* Ô */: case 213 /* Õ */:
437      *p = 'O'; return 1;
438    case 214 /* Ö */: /* &Ouml; */ case 0x152: /* &OElig; */
439      *p = 'O'; p[1] = 'E'; return 2;
440    case 217 /* Ù */: case 218 /* Ú */: case 219 /* Û */:
441      *p = 'U'; return 1;
442    case 220 /* Ü */: /* &Uuml; */
443      *p = 'U'; p[1] = 'E'; return 2;
444    case 221 /* Ý */:
445      *p = 'Y'; return 1;
446    case 223 /* ß */:
447      p[1] = *p = 's'; return 2;
448    case 224 /* à */: case 225 /* á */: case 226 /* â */: case 227 /* ã */:
449      *p = 'a'; return 1;
450    case 228 /* ä */: /* &auml; */ case 230 /* æ */:
451      *p = 'a'; p[1] = 'e'; return 2;
452    case 229 /* å */:
453      p[1] = *p = 'a'; return 2;
454    case 231 /* ç */:
455      *p = 'c'; return 1;
456    case 232 /* è */: case 233 /* é */: case 234 /* ê */: case 235 /* ë */:
457      *p = 'e'; return 1;
458    case 236 /* ì */: case 237 /* í */: case 238 /* î */: case 239 /* ï */:
459      *p = 'i'; return 1;
460    case 241 /* ñ */:
461      *p = 'n'; return 1;
462    case 240 /* ð */: case 254 /* þ */:
463      *p = 't'; p[1] = 'h'; return 2;
464    case 242 /* ò */: case 243 /* ó */: case 244 /* ô */: case 245 /* õ */:
465      *p = 'o'; return 1;
466    case 246 /* ö */: /* &ouml; */ case 0x153: /* &oelig; */
467      *p = 'o'; p[1] = 'e'; return 2;
468    case 249 /* ù */: case 250 /* ú */: case 251 /* û */:
469      *p = 'u'; return 1;
470    case 252 /* ü */: /* &uuml; */
471      *p = 'u'; p[1] = 'e'; return 2;
472    case 253 /* ý */: case 255 /* ÿ */:
473      *p = 'y'; return 1;
474   }
475#ifdef DEBUG
476   fprintf(stderr, "failed to transliterate\n");
477#endif
[f1a5201]478   return 0;
[4432f2e]479}
480
[f2a6ce4]481/* fall back on looking in the current directory */
482static const char *pth_cfg_files = "";
483
[db75e18]484static int num_msgs = 0;
[55de792]485static char **msg_array = NULL;
[db75e18]486
[b83f907]487const char *msg_lang = NULL;
[c0a9908]488const char *msg_lang2 = NULL;
[b83f907]489
[db75e18]490static void
491parse_msg_file(int charset_code)
492{
493   FILE *fh;
494   unsigned char header[20];
[4432f2e]495   int i;
[db75e18]496   unsigned len;
[55de792]497   unsigned char *p;
[b164c18]498   char *fnm, *s;
[cb3d1e2]499
[55de792]500#ifdef DEBUG
501   fprintf(stderr, "parse_msg_file(%d)\n", charset_code);
502#endif
[db75e18]503
[0a3c5fa]504   fnm = osstrdup(msg_lang);
[b164c18]505   /* trim off charset from stuff like "de_DE.iso8859_1" */
506   s = strchr(fnm, '.');
507   if (s) *s = '\0';
[a2f9d5c]508
[b164c18]509   fh = fopenWithPthAndExt(pth_cfg_files, fnm, EXT_SVX_MSG, "rb", NULL);
[db75e18]510
511   if (!fh) {
512      /* e.g. if 'en-COCKNEY' is unknown, see if we know 'en' */
[b164c18]513      if (strlen(fnm) > 3 && fnm[2] == '-') {
514         fnm[2] = '\0';
515         fh = fopenWithPthAndExt(pth_cfg_files, fnm, EXT_SVX_MSG, "rb", NULL);
516         if (!fh) fnm[2] = '-'; /* for error reporting */
[db75e18]517      }
[48e4121]518   }
[db75e18]519
520   if (!fh) {
[b07f165]521      fatalerror(/*Can't open message file `%s' using path `%s'*/1000,
522                 fnm, pth_cfg_files);
[4432f2e]523   }
[db75e18]524
525   if (fread(header, 1, 20, fh) < 20 ||
526       memcmp(header, "Svx\nMsg\r\n\xfe\xff", 12) != 0) {
[b07f165]527      fatalerror(/*Problem with message file `%s'*/1001, fnm);
[db75e18]528   }
529
[b07f165]530   if (header[12] != 0)
531      fatalerror(/*I don't understand this message file version*/1002);
[db75e18]532
533   num_msgs = (header[14] << 8) | header[15];
534
535   len = 0;
536   for (i = 16; i < 20; i++) len = (len << 8) | header[i];
537
[0a3c5fa]538   p = osmalloc(len);
[b07f165]539   if (fread(p, 1, len, fh) < len)
540      fatalerror(/*Message file truncated?*/1003);
[2163157]541
[a420b49]542   fclose(fh);
[db75e18]543
[55de792]544#ifdef DEBUG
[b164c18]545   fprintf(stderr, "fnm = `%s', num_msgs = %d, len = %d\n", fnm, num_msgs, len);
[55de792]546#endif
[b164c18]547   osfree(fnm);
[55de792]548
[0a3c5fa]549   msg_array = osmalloc(sizeof(char *) * num_msgs);
[55de792]550
[db75e18]551   for (i = 0; i < num_msgs; i++) {
[55de792]552      unsigned char *to = p;
[db75e18]553      int ch;
[55de792]554      msg_array[i] = (char *)p;
[6a4871e]555
556      /* If we want UTF8 anyway, we just need to find the start of each
557       * message */
558      if (charset_code == CHARSET_UTF8) {
[49090c02]559         p += strlen((char *)p) + 1;
[6a4871e]560         continue;
[cb3d1e2]561      }
[6a4871e]562
[db75e18]563      while ((ch = *p++) != 0) {
564         /* A byte in the range 0x80-0xbf or 0xf0-0xff isn't valid in
565          * this state, (0xf0-0xfd mean values > 0xffff) so treat as
566          * literal and try to resync so we cope better when fed
567          * non-utf-8 data.  Similarly we abandon a multibyte sequence
568          * if we hit an invalid character. */
569         if (ch >= 0xc0 && ch < 0xf0) {
570            int ch1 = *p;
571            if ((ch1 & 0xc0) != 0x80) goto resync;
[cb3d1e2]572
[db75e18]573            if (ch < 0xe0) {
574               /* 2 byte sequence */
575               ch = ((ch & 0x1f) << 6) | (ch1 & 0x3f);
576               p++;
577            } else {
578               /* 3 byte sequence */
579               int ch2 = p[1];
580               if ((ch2 & 0xc0) != 0x80) goto resync;
581               ch = ((ch & 0x1f) << 12) | ((ch1 & 0x3f) << 6) | (ch2 & 0x3f);
582               p += 2;
583            }
584         }
[cb3d1e2]585
[db75e18]586         resync:
[cb3d1e2]587
[db75e18]588         if (ch < 127) {
589            *to++ = (char)ch;
590         } else {
[bd1913f]591            /* FIXME: this rather assumes a 2 byte UTF-8 code never
[db75e18]592             * transliterates to more than 2 characters */
593            to += add_unicode(charset_code, to, ch);
594         }
595      }
596      *to++ = '\0';
597   }
[4432f2e]598}
599
[f2a6ce4]600const char *
601msg_cfgpth(void)
602{
603   return pth_cfg_files;
604}
605
606void
607msg_init(const char *argv0)
[db75e18]608{
609   char *p;
610
611#ifdef HAVE_SIGNAL
612   init_signals();
613#endif
[0a3c5fa]614   /* Point to argv0 itself so we report a more helpful error if the code to work
615    * out the clean appname generates a signal */
[a4140e9]616   appname_copy = argv0;
[0a3c5fa]617#if (OS == UNIX)
618   /* use name as-is on Unix - programs run from path get name as supplied */
[a4140e9]619   appname_copy = osstrdup(argv0);
[0a3c5fa]620#else
621   /* use the lower-cased leafname on other platforms */
[a4140e9]622   appname_copy = p = leaf_from_fnm(argv0);
[4bfc8a7]623   while (*p) {
624      *p = tolower(*p);
625      p++;
626   }
[0a3c5fa]627#endif
[db75e18]628
629   /* Look for env. var. "SURVEXHOME" or the like */
630   p = getenv("SURVEXHOME");
631   if (p && *p) {
[0a3c5fa]632      pth_cfg_files = osstrdup(p);
[18f4759]633#if (OS==UNIX) && defined(DATADIR) && defined(PACKAGE)
[a420b49]634   } else {
635      /* under Unix, we compile in the configured path */
[18f4759]636      pth_cfg_files = DATADIR "/" PACKAGE;
[a420b49]637#else
[db75e18]638   } else if (argv0) {
639      /* else try the path on argv[0] */
[f2a6ce4]640      pth_cfg_files = path_from_fnm(argv0);
[a420b49]641#endif
[db75e18]642   }
643
[b164c18]644   msg_lang = getenv("SURVEXLANG");
645#ifdef DEBUG
[74044b7]646   fprintf(stderr, "msg_lang = %p (= \"%s\")\n", msg_lang, msg_lang?msg_lang:"(null)");
[b164c18]647#endif
648
649   if (!msg_lang || !*msg_lang) {
650      msg_lang = getenv("LANG");
[74044b7]651      if (!msg_lang || !*msg_lang) {
652#if (OS==WIN32)
653         LCID locid;
654#endif
655         msg_lang = DEFAULTLANG;
656#if (OS==WIN32)
657         locid = GetUserDefaultLCID();
658         if (locid) {
659            WORD langid = LANGIDFROMLCID(locid);
660            switch (PRIMARYLANGID(langid)) {
661             case LANG_CATALAN:
662               msg_lang = "ca";
663               break;
664             case LANG_ENGLISH:
665               if (SUBLANGID(langid) == SUBLANG_ENGLISH_US)
666                  msg_lang = "en_US";
667               else
668                  msg_lang = "en";
669               break;
670             case LANG_FRENCH:
671               msg_lang = "fr";
672               break;
673             case LANG_GERMAN:
674               switch (SUBLANGID(langid)) {
675                case SUBLANG_GERMAN_SWISS:
676                  msg_lang = "de_CH";
677                  break;
678                case SUBLANG_GERMAN:
679                  msg_lang = "de_DE";
680                  break;
681                default:
682                  msg_lang = "de";
683               }
684               break;
685             case LANG_ITALIAN:
686               msg_lang = "it";
687               break;
688             case LANG_PORTUGUESE:
689               if (SUBLANGID(langid) == SUBLANG_PORTUGUESE_BRAZILIAN)
690                  msg_lang = "pt_BR";
691               else
692                  msg_lang = "pt";
693               break;
694             case LANG_SPANISH:
695               msg_lang = "es";
696               break;
697            }
698         }
699#endif
700      }
[b164c18]701   }
702#ifdef DEBUG
703   fprintf(stderr, "msg_lang = %p (= \"%s\")\n", msg_lang, msg_lang?msg_lang:"(null)");
704#endif
705
706   /* On Mandrake LANG defaults to C */
707   if (strcmp(msg_lang, "C") == 0) msg_lang = "en";
708
[0a3c5fa]709   msg_lang = osstrdup(msg_lang);
[b164c18]710
711   /* Convert en-us to en_US, etc */
712   p = strchr(msg_lang, '-');
713   if (p) {
714      *p++ = '_';
715      while (*p) {
716         *p = toupper(*p);
717         p++;
718      }
719   }
720
721   p = strchr(msg_lang, '_');
722   if (p) {
723      *p = '\0';
[0a3c5fa]724      msg_lang2 = osstrdup(msg_lang);
[b164c18]725      *p = '_';
726   }
727
728#ifdef LC_MESSAGES
729   /* try to setlocale() appropriately too */
730   if (!setlocale(LC_MESSAGES, msg_lang)) {
731      if (msg_lang2) setlocale(LC_MESSAGES, msg_lang2);
732   }
733#endif
734
[db75e18]735   select_charset(default_charset());
736}
737
[b07f165]738/* no point extracting these errors as they won't get used if file opens */
[b8b184a]739/* FIXME: if DEFAULTLANG != "en" translate these... */
[b07f165]740static const char *dontextract[] = {
741   "Can't open message file `%s' using path `%s'", /*1000*/
742   "Problem with message file `%s'", /*1001*/
743   "I don't understand this message file version", /*1002*/
744   "Message file truncated?" /*1003*/
745};
746
[bd1913f]747/* message may be overwritten by next call
748 * (but not in current implementation) */
749const char *
[db75e18]750msg(int en)
751{
[eee67ab]752   /* NB can't use ASSERT here! */
753   static char badbuf[256];
[aed2a10]754   if (en >= 1000 && en < 1000 + (int)(sizeof(dontextract)/sizeof(char*)))
[b07f165]755      return dontextract[en - 1000];
[55de792]756   if (!msg_array) {
[eee67ab]757      if (en != 1)  {
758         sprintf(badbuf, "Message %d requested before msg_array initialised\n", en);
759         return badbuf;
760      }
[a420b49]761      /* this should be the only message which can be requested before
762       * the message file is opened and read... */
763      return "Out of memory (couldn't find %ul bytes).\n";
[db75e18]764   }
765
[eee67ab]766   if (en < 0 || en >= num_msgs) {
767      sprintf(badbuf, "Message %d out of range\n", en);
768      return badbuf;
769   }
[db75e18]770
[55de792]771   return msg_array[en];
[db75e18]772}
773
774/* returns persistent copy of message */
[bd1913f]775const char *
[db75e18]776msgPerm(int en)
777{
778   return msg(en);
779}
780
781void
782v_report(int severity, const char *fnm, int line, int en, va_list ap)
783{
[1f316f3]784#ifdef AVEN
[2e18955]785   aven_v_report(severity, fnm, line, en, ap);
[2163157]786#else
[db75e18]787   if (fnm) {
788      fputs(fnm, STDERR);
789      if (line) fprintf(STDERR, ":%d", line);
790   } else {
[a4140e9]791      fputs(appname_copy, STDERR);
[cb3d1e2]792   }
[db75e18]793   fputs(": ", STDERR);
794
795   if (severity == 0) {
796      fputs(msg(/*warning*/4), STDERR);
797      fputs(": ", STDERR);
798   }
799
800   vfprintf(STDERR, msg(en), ap);
801   fputnl(STDERR);
[1f316f3]802#endif
[cb3d1e2]803
[db75e18]804   switch (severity) {
805    case 0:
[25ab06b]806      msg_warnings++;
[db75e18]807      break;
808    case 1:
[25ab06b]809      msg_errors++;
810      if (msg_errors == 50)
[db75e18]811         fatalerror_in_file(fnm, 0, /*Too many errors - giving up*/19);
812      break;
813    case 2:
814      exit(EXIT_FAILURE);
815   }
816}
817
818void
819warning(int en, ...)
820{
821   va_list ap;
822   va_start(ap, en);
823   v_report(0, NULL, 0, en, ap);
824   va_end(ap);
825}
826
827void
828error(int en, ...)
829{
830   va_list ap;
831   va_start(ap, en);
832   v_report(1, NULL, 0, en, ap);
833   va_end(ap);
834}
835
836void
837fatalerror(int en, ...)
838{
839   va_list ap;
840   va_start(ap, en);
841   v_report(2, NULL, 0, en, ap);
842   va_end(ap);
843}
844
845void
846warning_in_file(const char *fnm, int line, int en, ...)
847{
848   va_list ap;
849   va_start(ap, en);
850   v_report(0, fnm, line, en, ap);
851   va_end(ap);
852}
853
854void
855error_in_file(const char *fnm, int line, int en, ...)
856{
857   va_list ap;
858   va_start(ap, en);
859   v_report(1, fnm, line, en, ap);
860   va_end(ap);
861}
862
863void
864fatalerror_in_file(const char *fnm, int line, int en, ...)
865{
866   va_list ap;
867   va_start(ap, en);
868   v_report(2, fnm, line, en, ap);
869   va_end(ap);
870}
871
872/* Code to support switching character set at runtime (e.g. for a printer
873 * driver to support different character sets on screen and on the printer)
874 */
875typedef struct charset_li {
876   struct charset_li *next;
877   int code;
[55de792]878   char **msg_array;
[db75e18]879} charset_li;
880
881static charset_li *charset_head = NULL;
882
883static int charset = CHARSET_BAD;
884
885int
886select_charset(int charset_code)
887{
888   int old_charset = charset;
889   charset_li *p;
890
[55de792]891#ifdef DEBUG
[bd1913f]892   fprintf(stderr, "select_charset(%d), old charset = %d\n", charset_code,
893           charset);
[55de792]894#endif
[cb3d1e2]895
[db75e18]896   charset = charset_code;
897
898   /* check if we've already parsed messages for new charset */
899   for (p = charset_head; p; p = p->next) {
[55de792]900#ifdef DEBUG
901      printf("%p: code %d msg_array %p\n", p, p->code, p->msg_array);
902#endif
[db75e18]903      if (p->code == charset) {
[55de792]904         msg_array = p->msg_array;
[db75e18]905         return old_charset;
906      }
907   }
908
909   /* nope, got to reparse message file */
910   parse_msg_file(charset_code);
911
912   /* add to list */
913   p = osnew(charset_li);
914   p->code = charset;
[55de792]915   p->msg_array = msg_array;
[db75e18]916   p->next = charset_head;
917   charset_head = p;
918
919   return old_charset;
920}
Note: See TracBrowser for help on using the repository browser.