source: git/src/message.c @ 7015222

RELEASE/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 7015222 was affaeee, checked in by Olly Betts <olly@…>, 20 years ago

Rework the OS== mechanism as defining WIN32 and UNIX causes problems with
third party headers which test for these defines and assume the wrong OS!

git-svn-id: file:///home/survex-svn/survex/branches/survex-1_1@3072 4b37db11-9a0c-4f06-9ece-9ab7cdaee568

  • Property mode set to 100644
File size: 33.7 KB
Line 
1/* message.c
2 * Fairly general purpose message and error routines
3 * Copyright (C) 1993-2003,2004,2005 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 "cmdline.h"
35#include "whichos.h"
36#include "filename.h"
37#include "message.h"
38#include "osdepend.h"
39#include "filelist.h"
40#include "debug.h"
41
42#ifdef AVEN
43# include "aven.h"
44#endif
45
46#ifdef HAVE_SIGNAL
47# ifdef HAVE_SETJMP_H
48#  include <setjmp.h>
49static jmp_buf jmpbufSignal;
50#  include <signal.h>
51# else
52#  undef HAVE_SIGNAL
53# endif
54#endif
55
56#if OS_WIN32
57# define WIN32_LEAN_AND_MEAN
58# include <windows.h>
59#elif OS_UNIX
60# include <sys/types.h>
61# include <sys/stat.h>
62#endif
63
64/* For funcs which want to be immune from messing around with different
65 * calling conventions */
66#ifndef CDECL
67# define CDECL
68#endif
69
70int msg_warnings = 0; /* keep track of how many warnings we've given */
71int msg_errors = 0;   /* and how many (non-fatal) errors */
72
73/* in case osmalloc() fails before appname_copy is set up */
74static const char *appname_copy = "anonymous program";
75
76/* Path to use to look for executables (used by aven to find cavern). */
77static const char *exe_pth = "";
78
79/* error code for failed osmalloc and osrealloc calls */
80static void
81outofmem(OSSIZE_T size)
82{
83   fatalerror(/*Out of memory (couldn't find %lu bytes).*/1,
84              (unsigned long)size);
85}
86
87#ifdef TOMBSTONES
88#define TOMBSTONE_SIZE 16
89static const char tombstone[TOMBSTONE_SIZE] = "012345\xfftombstone";
90#endif
91
92/* malloc with error catching if it fails. Also allows us to write special
93 * versions easily eg for MS Windows.
94 */
95void Far *
96osmalloc(OSSIZE_T size)
97{
98   void Far *p;
99#ifdef TOMBSTONES
100   size += TOMBSTONE_SIZE * 2;
101   p = malloc(size);
102#else
103   p = xosmalloc(size);
104#endif
105   if (p == NULL) outofmem(size);
106#ifdef TOMBSTONES
107   printf("osmalloc truep=%p truesize=%d\n", p, size);
108   memcpy(p, tombstone, TOMBSTONE_SIZE);
109   memcpy(p + size - TOMBSTONE_SIZE, tombstone, TOMBSTONE_SIZE);
110   *(size_t *)p = size;
111   p += TOMBSTONE_SIZE;
112#endif
113   return p;
114}
115
116/* realloc with error catching if it fails. */
117void Far *
118osrealloc(void *p, OSSIZE_T size)
119{
120   /* some pre-ANSI realloc implementations don't cope with a NULL pointer */
121   if (p == NULL) {
122      p = xosmalloc(size);
123   } else {
124#ifdef TOMBSTONES
125      int true_size;
126      size += TOMBSTONE_SIZE * 2;
127      p -= TOMBSTONE_SIZE;
128      true_size = *(size_t *)p;
129      printf("osrealloc (in truep=%p truesize=%d)\n", p, true_size);
130      if (memcmp(p + sizeof(size_t), tombstone + sizeof(size_t),
131                 TOMBSTONE_SIZE - sizeof(size_t)) != 0) {
132         printf("start tombstone for block %p, size %d corrupted!",
133                p + TOMBSTONE_SIZE, true_size - TOMBSTONE_SIZE * 2);
134      }
135      if (memcmp(p + true_size - TOMBSTONE_SIZE, tombstone,
136                 TOMBSTONE_SIZE) != 0) {
137         printf("end tombstone for block %p, size %d corrupted!",
138                p + TOMBSTONE_SIZE, true_size - TOMBSTONE_SIZE * 2);
139      }
140      p = realloc(p, size);
141      if (p == NULL) outofmem(size);
142      printf("osrealloc truep=%p truesize=%d\n", p, size);
143      memcpy(p, tombstone, TOMBSTONE_SIZE);
144      memcpy(p + size - TOMBSTONE_SIZE, tombstone, TOMBSTONE_SIZE);
145      *(size_t *)p = size;
146      p += TOMBSTONE_SIZE;
147#else
148      p = xosrealloc(p, size);
149#endif
150   }
151   if (p == NULL) outofmem(size);
152   return p;
153}
154
155char Far *
156osstrdup(const char *str)
157{
158   char *p;
159   OSSIZE_T len;
160   len = strlen(str) + 1;
161   p = osmalloc(len);
162   memcpy(p, str, len);
163   return p;
164}
165
166/* osfree is usually just a macro in osalloc.h */
167#ifdef TOMBSTONES
168void
169osfree(void *p)
170{
171   int true_size;
172   if (!p) return;
173   p -= TOMBSTONE_SIZE;
174   true_size = *(size_t *)p;
175   printf("osfree truep=%p truesize=%d\n", p, true_size);
176   if (memcmp(p + sizeof(size_t), tombstone + sizeof(size_t),
177              TOMBSTONE_SIZE - sizeof(size_t)) != 0) {
178      printf("start tombstone for block %p, size %d corrupted!",
179             p + TOMBSTONE_SIZE, true_size - TOMBSTONE_SIZE * 2);
180   }
181   if (memcmp(p + true_size - TOMBSTONE_SIZE, tombstone,
182              TOMBSTONE_SIZE) != 0) {
183      printf("end tombstone for block %p, size %d corrupted!",
184             p + TOMBSTONE_SIZE, true_size - TOMBSTONE_SIZE * 2);
185   }
186   free(p);
187}
188#endif
189
190#ifdef HAVE_SIGNAL
191
192static int sigReceived;
193
194/* for systems not using autoconf, assume the signal handler returns void
195 * unless specified elsewhere */
196#ifndef RETSIGTYPE
197# define RETSIGTYPE void
198#endif
199
200static CDECL RETSIGTYPE Far
201report_sig(int sig)
202{
203   sigReceived = sig;
204   longjmp(jmpbufSignal, 1);
205}
206
207static void
208init_signals(void)
209{
210   int en;
211   if (!setjmp(jmpbufSignal)) {
212      signal(SIGABRT, report_sig); /* abnormal termination eg abort() */
213      signal(SIGFPE,  report_sig); /* arithmetic error eg /0 or overflow */
214      signal(SIGILL,  report_sig); /* illegal function image eg illegal instruction */
215      signal(SIGSEGV, report_sig); /* illegal storage access eg access outside memory limits */
216# ifdef SIGSTAK /* only on RISC OS AFAIK */
217      signal(SIGSTAK, report_sig); /* stack overflow */
218# endif
219      return;
220   }
221
222   switch (sigReceived) {
223      case SIGABRT: en = /*Abnormal termination*/90; break;
224      case SIGFPE:  en = /*Arithmetic error*/91; break;
225      case SIGILL:  en = /*Illegal instruction*/92; break;
226      case SIGSEGV: en = /*Bad memory access*/94; break;
227# ifdef SIGSTAK
228      case SIGSTAK: en = /*Stack overflow*/96; break;
229# endif
230      default:      en = /*Unknown signal received*/97; break;
231   }
232   fputsnl(msg(en), STDERR);
233
234   /* Any of the signals we catch indicates a bug */
235   fatalerror(/*Bug in program detected! Please report this to the authors*/11);
236
237   exit(EXIT_FAILURE);
238}
239#endif
240
241static int
242default_charset(void)
243{
244#if OS_WIN32
245# ifdef AVEN
246#  define CODEPAGE GetACP()
247# else
248#  define CODEPAGE (getenv("SURVEX_UTF8") ? 0 : GetConsoleOutputCP())
249# endif
250   switch (CODEPAGE) {
251    case 0: return CHARSET_UTF8;
252    case 1252: return CHARSET_WINCP1252;
253    case 1250: return CHARSET_WINCP1250;
254    case 850: return CHARSET_DOSCP850;
255   }
256   return CHARSET_USASCII;
257#elif OS_UNIX
258#ifdef AVEN
259   return CHARSET_ISO_8859_1;
260#else
261   const char *p = getenv("LC_ALL");
262   if (p == NULL || p[0] == '\0') {
263      p = getenv("LC_CTYPE");
264      if (p == NULL || p[0] == '\0') {
265         p = msg_lang;
266      }
267   }
268
269   if (p) {
270      char *q = strchr(p, '.');
271      if (q) p = q + 1;
272   }
273
274   if (p) {
275      const char *chset = p;
276      size_t name_len;
277
278      while (*p != '\0' && *p != '@') p++;
279
280      name_len = p - chset;
281
282      if (name_len) {
283         int only_digit = 1;
284         size_t cnt;
285
286         for (cnt = 0; cnt < name_len; ++cnt)
287            if (isalpha((unsigned char)chset[cnt])) {
288               only_digit = 0;
289               break;
290            }
291
292         if (only_digit) goto iso;
293
294         switch (tolower(chset[0])) {
295          case 'i':
296            if (tolower(chset[1]) == 's' && tolower(chset[2]) == 'o') {
297               chset += 3;
298               iso:
299               if (strncmp(chset, "8859", 4) == 0) {
300                  chset += 4;
301                  while (chset < p && *chset && !isdigit((unsigned char)*chset))
302                     chset++;
303                  switch (atoi(chset)) {
304                   case 1: return CHARSET_ISO_8859_1;
305                   case 2: return CHARSET_ISO_8859_2;
306                   case 15: return CHARSET_ISO_8859_15;
307                   default: return CHARSET_USASCII;
308                  }
309               }
310            }
311            break;
312          case 'u':
313            if (tolower(chset[1]) == 't' && tolower(chset[2]) == 'f') {
314               chset += 3;
315               while (chset < p && *chset && !isdigit((unsigned char)*chset))
316                  chset++;
317               switch (atoi(chset)) {
318                case 8: return CHARSET_UTF8;
319                default: return CHARSET_USASCII;
320               }
321            }
322         }
323      }
324   }
325   return CHARSET_USASCII;
326#endif
327#else
328# error Do not know operating system!
329#endif
330}
331
332/* It seems that Swedish and maybe some other scandanavian languages don't
333 * transliterate &auml; to ae - but it seems there may be conflicting views
334 * on this...
335 */
336#define umlaut_to_e() 1
337
338/* values <= 127 already dealt with */
339static int
340add_unicode(int charset, unsigned char *p, int value)
341{
342#ifdef DEBUG
343   fprintf(stderr, "add_unicode(%d, %p, %d)\n", charset, p, value);
344#endif
345   if (value == 0) return 0;
346   switch (charset) {
347   case CHARSET_USASCII:
348      if (value < 0x80) {
349         *p = value;
350         return 1;
351      }
352      break;
353   case CHARSET_ISO_8859_1:
354      if (value < 0x100) {
355         *p = value;
356         return 1;
357      }
358      break;
359   case CHARSET_ISO_8859_2:
360      if (value >= 0xa0) {
361         int v = 0;
362         switch (value) {
363            case 0xa0: case 0xa4: case 0xa7: case 0xa8: case 0xad: case 0xb0:
364            case 0xb4: case 0xb8: case 0xc1: case 0xc2: case 0xc4: case 0xc7:
365            case 0xc9: case 0xcb: case 0xcd: case 0xce: case 0xd3: case 0xd4:
366            case 0xd6: case 0xd7: case 0xda: case 0xdc: case 0xdd: case 0xdf:
367            case 0xe1: case 0xe2: case 0xe4: case 0xe7: case 0xe9: case 0xeb:
368            case 0xed: case 0xee: case 0xf3: case 0xf4: case 0xf6: case 0xf7:
369            case 0xfa: case 0xfc: case 0xfd:
370               v = value; break;
371            case 0x104: v = '\xa1'; break;
372            case 0x2d8: v = '\xa2'; break;
373            case 0x141: v = '\xa3'; break;
374            case 0x13d: v = '\xa5'; break;
375            case 0x15a: v = '\xa6'; break;
376            case 0x160: v = '\xa9'; break;
377            case 0x15e: v = '\xaa'; break; /* Scedil */
378            case 0x164: v = '\xab'; break;
379            case 0x179: v = '\xac'; break;
380            case 0x17d: v = '\xae'; break;
381            case 0x17b: v = '\xaf'; break;
382            case 0x105: v = '\xb1'; break;
383            case 0x2db: v = '\xb2'; break;
384            case 0x142: v = '\xb3'; break;
385            case 0x13e: v = '\xb5'; break;
386            case 0x15b: v = '\xb6'; break;
387            case 0x2c7: v = '\xb7'; break;
388            case 0x161: v = '\xb9'; break;
389            case 0x15f: v = '\xba'; break; /* scedil */
390            case 0x165: v = '\xbb'; break;
391            case 0x17a: v = '\xbc'; break;
392            case 0x2dd: v = '\xbd'; break;
393            case 0x17e: v = '\xbe'; break;
394            case 0x17c: v = '\xbf'; break;
395            case 0x154: v = '\xc0'; break;
396            case 0x102: v = '\xc3'; break;
397            case 0x139: v = '\xc5'; break;
398            case 0x106: v = '\xc6'; break;
399            case 0x10c: v = '\xc8'; break;
400            case 0x118: v = '\xca'; break;
401            case 0x11a: v = '\xcc'; break;
402            case 0x10e: v = '\xcf'; break;
403            case 0x110: v = '\xd0'; break;
404            case 0x143: v = '\xd1'; break;
405            case 0x147: v = '\xd2'; break;
406            case 0x150: v = '\xd5'; break;
407            case 0x158: v = '\xd8'; break;
408            case 0x16e: v = '\xd9'; break;
409            case 0x170: v = '\xdb'; break;
410            case 0x162: v = '\xde'; break; /* &Tcedil; */
411            case 0x155: v = '\xe0'; break;
412            case 0x103: v = '\xe3'; break;
413            case 0x13a: v = '\xe5'; break;
414            case 0x107: v = '\xe6'; break;
415            case 0x10d: v = '\xe8'; break;
416            case 0x119: v = '\xea'; break;
417            case 0x11b: v = '\xec'; break;
418            case 0x10f: v = '\xef'; break;
419            case 0x111: v = '\xf0'; break;
420            case 0x144: v = '\xf1'; break;
421            case 0x148: v = '\xf2'; break;
422            case 0x151: v = '\xf5'; break;
423            case 0x159: v = '\xf8'; break;
424            case 0x16f: v = '\xf9'; break;
425            case 0x171: v = '\xfb'; break;
426            case 0x163: v = '\xfe'; break; /* tcedil */
427            case 0x2d9: v = '\xff'; break;
428         }
429         if (v == 0) break;
430         value = v;
431      }
432      *p = value;
433      return 1;
434   case CHARSET_ISO_8859_15:
435      switch (value) {
436       case 0xa4: case 0xa6: case 0xb0: case 0xc4:
437       case 0xd0: case 0xd4: case 0xd5: case 0xd6:
438         goto donthave;
439       case 0x152: value = 0xd4; break; /* &OElig; */
440       case 0x153: value = 0xd5; break; /* &oelig; */
441#if 0
442       case 0x0: value = 0xa4; break; /* euro */
443#endif
444       case 0x160: value = 0xa6; break; /* Scaron */
445       case 0x161: value = 0xb0; break; /* scaron */
446       case 0x17d: value = 0xc4; break; /* Zcaron */
447       case 0x17e: value = 0xd0; break; /* zcaron */
448#if 0
449       case 0x0: value = 0xd6; break; /* Ydiersis */
450#endif
451      }
452      if (value < 0x100) {
453         *p = value;
454         return 1;
455      }
456      donthave:
457      break;
458#if OS_WIN32
459   case CHARSET_WINCP1250:
460      /* MS Windows rough equivalent to ISO-8859-2 */
461      if (value >= 0x80) {
462         int v = 0;
463         switch (value) {
464            case 0xa0: case 0xa4: case 0xa6: case 0xa7: case 0xa8: case 0xa9:
465            case 0xab: case 0xac: case 0xad: case 0xae: case 0xb0: case 0xb1:
466            case 0xb4: case 0xb5: case 0xb6: case 0xb7: case 0xb8: case 0xbb:
467            case 0xc1: case 0xc2: case 0xc4: case 0xc7: case 0xc9: case 0xcb:
468            case 0xcd: case 0xce: case 0xd3: case 0xd4: case 0xd6: case 0xd7:
469            case 0xda: case 0xdc: case 0xdd: case 0xdf: case 0xe1: case 0xe2:
470            case 0xe4: case 0xe7: case 0xe9: case 0xeb: case 0xed: case 0xee:
471            case 0xf3: case 0xf4: case 0xf6: case 0xf7: case 0xfa: case 0xfc:
472            case 0xfd:
473               v = value; break;
474            case 0x20ac: v = '\x80'; break;
475            case 0x201a: v = '\x82'; break;
476            case 0x201e: v = '\x84'; break;
477            case 0x2026: v = '\x85'; break;
478            case 0x2020: v = '\x86'; break;
479            case 0x2021: v = '\x87'; break;
480            case 0x2030: v = '\x89'; break;
481            case 0x0160: v = '\x8a'; break;
482            case 0x2039: v = '\x8b'; break;
483            case 0x015a: v = '\x8c'; break;
484            case 0x0164: v = '\x8d'; break;
485            case 0x017d: v = '\x8e'; break;
486            case 0x0179: v = '\x8f'; break;
487            case 0x2018: v = '\x91'; break;
488            case 0x2019: v = '\x92'; break;
489            case 0x201c: v = '\x93'; break;
490            case 0x201d: v = '\x94'; break;
491            case 0x2022: v = '\x95'; break;
492            case 0x2013: v = '\x96'; break;
493            case 0x2014: v = '\x97'; break;
494            case 0x2122: v = '\x99'; break;
495            case 0x0161: v = '\x9a'; break;
496            case 0x203a: v = '\x9b'; break;
497            case 0x015b: v = '\x9c'; break;
498            case 0x0165: v = '\x9d'; break;
499            case 0x017e: v = '\x9e'; break;
500            case 0x017a: v = '\x9f'; break;
501            case 0x02c7: v = '\xa1'; break;
502            case 0x02d8: v = '\xa2'; break;
503            case 0x0141: v = '\xa3'; break;
504            case 0x0104: v = '\xa5'; break;
505            case 0x015e: v = '\xaa'; break; /* Scedil */
506            case 0x017b: v = '\xaf'; break;
507            case 0x02db: v = '\xb2'; break;
508            case 0x0142: v = '\xb3'; break;
509            case 0x0105: v = '\xb9'; break;
510            case 0x015f: v = '\xba'; break; /* scedil */
511            case 0x013d: v = '\xbc'; break;
512            case 0x02dd: v = '\xbd'; break;
513            case 0x013e: v = '\xbe'; break;
514            case 0x017c: v = '\xbf'; break;
515            case 0x0154: v = '\xc0'; break;
516            case 0x0102: v = '\xc3'; break;
517            case 0x0139: v = '\xc5'; break;
518            case 0x0106: v = '\xc6'; break;
519            case 0x010c: v = '\xc8'; break;
520            case 0x0118: v = '\xca'; break;
521            case 0x011a: v = '\xcc'; break;
522            case 0x010e: v = '\xcf'; break;
523            case 0x0110: v = '\xd0'; break;
524            case 0x0143: v = '\xd1'; break;
525            case 0x0147: v = '\xd2'; break;
526            case 0x0150: v = '\xd5'; break;
527            case 0x0158: v = '\xd8'; break;
528            case 0x016e: v = '\xd9'; break;
529            case 0x0170: v = '\xdb'; break;
530            case 0x0162: v = '\xde'; break; /* &Tcedil; */
531            case 0x0155: v = '\xe0'; break;
532            case 0x0103: v = '\xe3'; break;
533            case 0x013a: v = '\xe5'; break;
534            case 0x0107: v = '\xe6'; break;
535            case 0x010d: v = '\xe8'; break;
536            case 0x0119: v = '\xea'; break;
537            case 0x011b: v = '\xec'; break;
538            case 0x010f: v = '\xef'; break;
539            case 0x0111: v = '\xf0'; break;
540            case 0x0144: v = '\xf1'; break;
541            case 0x0148: v = '\xf2'; break;
542            case 0x0151: v = '\xf5'; break;
543            case 0x0159: v = '\xf8'; break;
544            case 0x016f: v = '\xf9'; break;
545            case 0x0171: v = '\xfb'; break;
546            case 0x0163: v = '\xfe'; break; /* tcedil */
547            case 0x02d9: v = '\xff'; break;
548         }
549         if (v == 0) break;
550         value = v;
551      }
552      *p = value;
553      return 1;
554   case CHARSET_WINCP1252:
555      /* MS Windows extensions to ISO-8859-1 */
556      switch (value) {
557       case 0x152: value = 0x8c; break; /* &OElig; */
558       case 0x153: value = 0x9c; break; /* &oelig; */
559#if 0
560      /* there are a few other obscure ones we don't currently need */
561#endif
562      }
563      if (value < 0x100) {
564         *p = value;
565         return 1;
566      }
567      break;
568#endif
569#if OS_WIN32
570   case CHARSET_DOSCP850: {
571      unsigned char uni2dostab[] = {
572         255, 173, 189, 156, 207, 190, 221, 245,
573         249, 184, 166, 174, 170, 240, 169, 238,
574         248, 241, 253, 252, 239, 230, 244, 250,
575         247, 251, 167, 175, 172, 171, 243, 168,
576         183, 181, 182, 199, 142, 143, 146, 128,
577         212, 144, 210, 211, 222, 214, 215, 216,
578         209, 165, 227, 224, 226, 229, 153, 158,
579         157, 235, 233, 234, 154, 237, 232, 225,
580         133, 160, 131, 198, 132, 134, 145, 135,
581         138, 130, 136, 137, 141, 161, 140, 139,
582         208, 164, 149, 162, 147, 228, 148, 246,
583         155, 151, 163, 150, 129, 236, 231, 152
584      };
585      if (value >= 160 && value < 256) {
586         *p = (int)uni2dostab[value - 160];
587         return 1;
588      }
589#if 0
590      if (value == 305) { /* LATIN SMALL LETTER DOTLESS I */
591         *p = 213;
592         return 1;
593      }
594      if (value == 402) { /* LATIN SMALL LETTER F WITH HOOK */
595         *p = 159;
596         return 1;
597      }
598#endif
599      break;
600   }
601#endif
602   }
603   /* Transliterate characters we can't represent */
604#ifdef DEBUG
605   fprintf(stderr, "transliterate `%c' 0x%x\n", value, value);
606#endif
607   switch (value) {
608    case 160:
609      *p = ' '; return 1;
610    case 161 /* ¡ */:
611      *p = '!'; return 1;
612    case 171 /* « */:
613      p[1] = *p = '<'; return 2;
614    case 187 /* » */:
615      p[1] = *p = '>'; return 2;
616    case 191 /* ¿ */:
617      *p = '?'; return 1;
618    case 192 /* À */: case 193 /* Á */: case 194 /* Â */: case 195 /* Ã */:
619      *p = 'A'; return 1;
620    case 197 /* Å */:
621      p[1] = *p = 'A'; return 2;
622    case 196 /* Ä */: /* &Auml; */
623      *p = 'A';
624      if (!umlaut_to_e()) return 1;
625      p[1] = 'E'; return 2;
626    case 198 /* Æ */:
627      *p = 'A'; p[1] = 'E'; return 2;
628    case 199 /* Ç */: case 268: /* &Ccaron; */
629      *p = 'C'; return 1;
630    case 270: /* &Dcaron; */
631      *p = 'D'; return 1;
632    case 200 /* È */: case 201 /* É */: case 202 /* Ê */: case 203 /* Ë */:
633      *p = 'E'; return 1;
634    case 204 /* Ì */: case 205 /* Í */: case 206 /* Î */: case 207 /* Ï */:
635      *p = 'I'; return 1;
636    case 208 /* Ð */: case 222 /* Þ */:
637      *p = 'T'; p[1] = 'H'; return 2;
638    case 315: /* &Lacute; */
639    case 317: /* &Lcaron; */
640      *p = 'L'; return 1;
641    case 209 /* Ñ */:
642      *p = 'N'; return 1;
643    case 210 /* Ò */: case 211 /* Ó */: case 212 /* Ô */: case 213 /* Õ */:
644      *p = 'O'; return 1;
645    case 214 /* Ö */: /* &Ouml; */ case 0x152: /* &OElig; */
646      *p = 'O'; p[1] = 'E'; return 2;
647    case 352: /* &Scaron; */
648    case 0x15e: /* &Scedil; */
649      *p = 'S'; return 1;
650    case 0x162: /* &Tcedil; */
651    case 0x164: /* &Tcaron; */
652      *p = 'T'; return 1;
653    case 217 /* Ù */: case 218 /* Ú */: case 219 /* Û */:
654      *p = 'U'; return 1;
655    case 220 /* Ü */: /* &Uuml; */
656      *p = 'U'; p[1] = 'E'; return 2;
657    case 221 /* Ý */:
658      *p = 'Y'; return 1;
659    case 381: /* &Zcaron; */
660      *p = 'Z'; return 1;
661    case 223 /* ß */:
662      p[1] = *p = 's'; return 2;
663    case 224 /* à */: case 225 /* á */: case 226 /* â */: case 227 /* ã */:
664    case 259: /* &abreve; */
665      *p = 'a'; return 1;
666    case 228 /* ä */: /* &auml; */ case 230 /* æ */:
667      *p = 'a'; p[1] = 'e'; return 2;
668    case 229 /* å */:
669      p[1] = *p = 'a'; return 2;
670    case 231 /* ç */: case 269 /* &ccaron; */:
671      *p = 'c'; return 1;
672    case 271: /* &dcaron; */
673      *p = 'd'; return 1;
674    case 232 /* è */: case 233 /* é */: case 234 /* ê */: case 235 /* ë */:
675    case 283 /* &ecaron; */:
676      *p = 'e'; return 1;
677    case 236 /* ì */: case 237 /* í */: case 238 /* î */: case 239 /* ï */:
678      *p = 'i'; return 1;
679    case 316 /* &lacute; */:
680    case 318 /* &lcaron; */:
681      *p = 'l'; return 1;
682    case 241 /* ñ */: case 328 /* &ncaron; */:
683      *p = 'n'; return 1;
684    case 345: /* &rcaron; */
685      *p = 'r'; return 1;
686    case 353: /* &scaron; */
687    case 0x15f: /* &scedil; */
688      *p = 's'; return 1;
689    case 357: /* &tcaron; */
690    case 0x163: /* &tcedil; */
691      *p = 't'; return 1;
692    case 240 /* ð */: case 254 /* þ */:
693      *p = 't'; p[1] = 'h'; return 2;
694    case 242 /* ò */: case 243 /* ó */: case 244 /* ô */: case 245 /* õ */:
695      *p = 'o'; return 1;
696    case 246 /* ö */: /* &ouml; */ case 0x153: /* &oelig; */
697      *p = 'o'; p[1] = 'e'; return 2;
698    case 249 /* ù */: case 250 /* ú */: case 251 /* û */:
699    case 367 /* &uring; */:
700      *p = 'u'; return 1;
701    case 252 /* ü */: /* &uuml; */
702      *p = 'u'; p[1] = 'e'; return 2;
703    case 253 /* ý */: case 255 /* ÿ */:
704      *p = 'y'; return 1;
705    case 382: /* &zcaron; */
706      *p = 'z'; return 1;
707   }
708#ifdef DEBUG
709   fprintf(stderr, "failed to transliterate\n");
710#endif
711   return 0;
712}
713
714#if OS_UNIX && defined DATADIR && defined PACKAGE
715/* Under Unix, we compile in the configured path */
716static const char *pth_cfg_files = DATADIR "/" PACKAGE;
717#else
718/* On other platforms, we fall back on looking in the current directory */
719static const char *pth_cfg_files = "";
720#endif
721
722static int num_msgs = 0;
723static char **msg_array = NULL;
724
725const char *msg_lang = NULL;
726const char *msg_lang2 = NULL;
727
728static char **
729parse_msgs(int n, unsigned char *p, int charset_code) {
730   int i;
731
732   char **msgs = osmalloc(n * sizeof(char *));
733
734   for (i = 0; i < n; i++) {
735      unsigned char *to = p;
736      int ch;
737      msgs[i] = (char *)p;
738
739      /* If we want UTF8 anyway, we just need to find the start of each
740       * message */
741      if (charset_code == CHARSET_UTF8) {
742         p += strlen((char *)p) + 1;
743         continue;
744      }
745
746      while ((ch = *p++) != 0) {
747         /* A byte in the range 0x80-0xbf or 0xf0-0xff isn't valid in
748          * this state, (0xf0-0xfd mean values > 0xffff) so treat as
749          * literal and try to resync so we cope better when fed
750          * non-utf-8 data.  Similarly we abandon a multibyte sequence
751          * if we hit an invalid character. */
752         if (ch >= 0xc0 && ch < 0xf0) {
753            int ch1 = *p;
754            if ((ch1 & 0xc0) != 0x80) goto resync;
755
756            if (ch < 0xe0) {
757               /* 2 byte sequence */
758               ch = ((ch & 0x1f) << 6) | (ch1 & 0x3f);
759               p++;
760            } else {
761               /* 3 byte sequence */
762               int ch2 = p[1];
763               if ((ch2 & 0xc0) != 0x80) goto resync;
764               ch = ((ch & 0x1f) << 12) | ((ch1 & 0x3f) << 6) | (ch2 & 0x3f);
765               p += 2;
766            }
767         }
768
769         resync:
770
771         if (ch < 127) {
772            *to++ = (char)ch;
773         } else {
774            /* We assume an N byte UTF-8 code never transliterates to more
775             * than N characters (so we can't transliterate © to (C) or
776             * ® to (R) for example) */
777            to += add_unicode(charset_code, to, ch);
778         }
779      }
780      *to++ = '\0';
781   }
782   return msgs;
783}
784
785/* This is the name of the default language, which can be set like so:
786 * ./configure --enable-defaultlang=fr
787 */
788#ifdef DEFAULTLANG
789/* No point extracting these errors as they won't get used if file opens */
790# include "../lib/defaultlang.h"
791#else
792#define N_DONTEXTRACTMSGS 5
793static unsigned char dontextractmsgs[] =
794   "Can't open message file `%s' using path `%s'\0"/*1000*/
795   "Problem with message file `%s'\0"/*1001*/
796   "I don't understand this message file version\0"/*1002*/
797   "Message file truncated?\0"/*1003*/
798   "Out of memory (couldn't find %lu bytes).\0"/*1004*/;
799#endif
800
801static char **dontextract = NULL;
802
803static void
804parse_msg_file(int charset_code)
805{
806   FILE *fh;
807   unsigned char header[20];
808   int i;
809   unsigned len;
810   unsigned char *p;
811   char *fnm, *s;
812   int n;
813
814#ifdef DEBUG
815   fprintf(stderr, "parse_msg_file(%d)\n", charset_code);
816#endif
817
818   /* sort out messages we need to print if we can't open the message file */
819   dontextract = parse_msgs(N_DONTEXTRACTMSGS, dontextractmsgs, charset_code);
820
821   fnm = osstrdup(msg_lang);
822   /* trim off charset from stuff like "de_DE.iso8859_1" */
823   s = strchr(fnm, '.');
824   if (s) *s = '\0';
825
826   fh = fopenWithPthAndExt(pth_cfg_files, fnm, EXT_SVX_MSG, "rb", NULL);
827
828   if (!fh) {
829      /* e.g. if 'en_GB' is unknown, see if we know 'en' */
830      if (strlen(fnm) > 3 && fnm[2] == '_') {
831         fnm[2] = '\0';
832         fh = fopenWithPthAndExt(pth_cfg_files, fnm, EXT_SVX_MSG, "rb", NULL);
833         if (!fh) fnm[2] = '_'; /* for error reporting */
834      }
835   }
836
837   if (!fh) {
838      fatalerror(/*Can't open message file `%s' using path `%s'*/1000,
839                 fnm, pth_cfg_files);
840   }
841
842   if (fread(header, 1, 20, fh) < 20 ||
843       memcmp(header, "Svx\nMsg\r\n\xfe\xff", 12) != 0) {
844      fatalerror(/*Problem with message file `%s'*/1001, fnm);
845   }
846
847   if (header[12] != 0)
848      fatalerror(/*I don't understand this message file version*/1002);
849
850   n = (header[14] << 8) | header[15];
851
852   len = 0;
853   for (i = 16; i < 20; i++) len = (len << 8) | header[i];
854
855   p = osmalloc(len);
856   if (fread(p, 1, len, fh) < len)
857      fatalerror(/*Message file truncated?*/1003);
858
859   fclose(fh);
860
861#ifdef DEBUG
862   fprintf(stderr, "fnm = `%s', n = %d, len = %d\n", fnm, n, len);
863#endif
864   osfree(fnm);
865
866   msg_array = parse_msgs(n, p, charset_code);
867   num_msgs = n;
868}
869
870const char *
871msg_cfgpth(void)
872{
873   return pth_cfg_files;
874}
875
876const char *
877msg_exepth(void)
878{
879   return exe_pth;
880}
881
882const char *
883msg_appname(void)
884{
885   return appname_copy;
886}
887
888void
889msg_init(char * const *argv)
890{
891   char *p;
892   SVX_ASSERT(argv);
893
894#ifdef HAVE_SIGNAL
895   init_signals();
896#endif
897   /* Point to argv[0] itself so we report a more helpful error if the
898    * code to work out the clean appname generates a signal */
899   appname_copy = argv[0];
900#if OS_UNIX
901   /* use name as-is on Unix - programs run from path get name as supplied */
902   appname_copy = osstrdup(argv[0]);
903#else
904   /* use the lower-cased leafname on other platforms */
905   p = leaf_from_fnm(argv[0]);
906   appname_copy = p;
907   while (*p) {
908      *p = tolower(*p);
909      ++p;
910   }
911#endif
912
913   /* shortcut --version so you can check the version number even when the
914    * correct message file can't be found... */
915   if (argv[1] && strcmp(argv[1], "--version") == 0) {
916      cmdline_version();
917      exit(0);
918   }
919   if (argv[0]) {
920      exe_pth = path_from_fnm(argv[0]);
921#ifdef MACOSX_BUNDLE
922      /* If we're being built into a bundle, always look relative to
923       * the path to the binary. */
924      pth_cfg_files = use_path(exe_pth, "share/survex");
925#elif OS_UNIX && defined DATADIR && defined PACKAGE
926      bool free_pth = fFalse;
927      char *pth = getenv("srcdir");
928      if (!pth || !pth[0]) {
929         pth = path_from_fnm(argv[0]);
930         free_pth = fTrue;
931      }
932      if (pth[0]) {
933         struct stat buf;
934#if OS_UNIX_MACOSX
935         /* On MacOS X the programs may be installed anywhere, with the
936          * share directory and the binaries in the same directory. */
937         p = use_path(pth, "share/survex/en.msg");
938         if (lstat(p, &buf) == 0 && S_ISREG(buf.st_mode)) {
939            pth_cfg_files = use_path(pth, "share/survex");
940            goto macosx_got_msg;
941         }
942         osfree(p);
943#endif
944         /* If we're run with an explicit path, check if "../lib/en.msg"
945          * from the program's path exists, and if so look there for
946          * support files - this allows us to test binaries in the build
947          * tree easily. */
948         p = use_path(pth, "../lib/en.msg");
949         if (lstat(p, &buf) == 0) {
950#ifdef S_ISDIR
951            /* POSIX way */
952            if (S_ISREG(buf.st_mode)) {
953               pth_cfg_files = use_path(pth, "../lib");
954            }
955#else
956            /* BSD way */
957            if ((buf.st_mode & S_IFMT) == S_IFREG) {
958               pth_cfg_files = use_path(pth, "../lib");
959            }
960#endif
961         }
962#if defined(__GNUC__) && defined(__APPLE_CC__)
963macosx_got_msg:
964#endif
965         osfree(p);
966      }
967
968      if (free_pth) osfree(pth);
969#elif OS_WIN32
970      DWORD len = 256;
971      char *buf = NULL, *modname;
972      while (1) {
973          DWORD got;
974          buf = osrealloc(buf, len);
975          got = GetModuleFileName(NULL, buf, len);
976          if (got < len) break;
977          len += len;
978      }
979      modname = buf;
980      /* Strange Win32 nastiness - strip prefix "\\?\" if present */
981      if (strncmp(modname, "\\\\?\\", 4) == 0) modname += 4;
982      pth_cfg_files = path_from_fnm(modname);
983      osfree(buf);
984#else
985      /* Get the path to the support files from argv[0] */
986      pth_cfg_files = path_from_fnm(argv[0]);
987#endif
988   }
989
990   msg_lang = getenv("SURVEXLANG");
991#ifdef DEBUG
992   fprintf(stderr, "msg_lang = %p (= \"%s\")\n", msg_lang, msg_lang?msg_lang:"(null)");
993#endif
994
995   if (!msg_lang || !*msg_lang) {
996      msg_lang = getenv("LC_MESSAGES");
997      if (!msg_lang || !*msg_lang) msg_lang = getenv("LANG");
998      if (!msg_lang || !*msg_lang) {
999#if OS_WIN32
1000         LCID locid;
1001#endif
1002#ifdef DEFAULTLANG
1003         msg_lang = STRING(DEFAULTLANG);
1004#else
1005         msg_lang = "en";
1006#endif
1007#if OS_WIN32
1008         locid = GetUserDefaultLCID();
1009         if (locid) {
1010            WORD langid = LANGIDFROMLCID(locid);
1011            switch (PRIMARYLANGID(langid)) {
1012/* older mingw compilers don't seem to supply this value */
1013#ifndef LANG_CATALAN
1014# define LANG_CATALAN 0x03
1015#endif
1016             case LANG_CATALAN:
1017               msg_lang = "ca";
1018               break;
1019             case LANG_CHINESE:
1020               msg_lang = "zh";
1021               break;
1022             case LANG_ENGLISH:
1023               if (SUBLANGID(langid) == SUBLANG_ENGLISH_US)
1024                  msg_lang = "en_US";
1025               else
1026                  msg_lang = "en";
1027               break;
1028             case LANG_FRENCH:
1029               msg_lang = "fr";
1030               break;
1031             case LANG_GERMAN:
1032               switch (SUBLANGID(langid)) {
1033                case SUBLANG_GERMAN_SWISS:
1034                  msg_lang = "de_CH";
1035                  break;
1036                case SUBLANG_GERMAN:
1037                  msg_lang = "de_DE";
1038                  break;
1039                default:
1040                  msg_lang = "de";
1041               }
1042               break;
1043             case LANG_ITALIAN:
1044               msg_lang = "it";
1045               break;
1046             case LANG_PORTUGUESE:
1047               if (SUBLANGID(langid) == SUBLANG_PORTUGUESE_BRAZILIAN)
1048                  msg_lang = "pt_BR";
1049               else
1050                  msg_lang = "pt";
1051               break;
1052             case LANG_ROMANIAN:
1053               msg_lang = "ro";
1054               break;
1055             case LANG_SLOVAK:
1056               msg_lang = "sk";
1057               break;
1058             case LANG_SPANISH:
1059               msg_lang = "es";
1060               break;
1061            }
1062         }
1063#endif
1064      }
1065   }
1066#ifdef DEBUG
1067   fprintf(stderr, "msg_lang = %p (= \"%s\")\n", msg_lang, msg_lang?msg_lang:"(null)");
1068#endif
1069
1070   /* On Mandrake LANG defaults to C */
1071   if (strcmp(msg_lang, "C") == 0) msg_lang = "en";
1072
1073   msg_lang = osstrdup(msg_lang);
1074
1075   /* Convert en-us to en_US, etc */
1076   p = strchr(msg_lang, '-');
1077   if (p) {
1078      *p++ = '_';
1079      while (*p) {
1080         *p = toupper(*p);
1081         p++;
1082      }
1083   }
1084
1085   p = strchr(msg_lang, '_');
1086   if (p) {
1087      *p = '\0';
1088      msg_lang2 = osstrdup(msg_lang);
1089      *p = '_';
1090   }
1091
1092#ifdef LC_MESSAGES
1093   /* try to setlocale() appropriately too */
1094   if (!setlocale(LC_MESSAGES, msg_lang)) {
1095      if (msg_lang2) setlocale(LC_MESSAGES, msg_lang2);
1096   }
1097#endif
1098
1099   select_charset(default_charset());
1100}
1101
1102/* Message may be overwritten by next call
1103 * (but not in current implementation) */
1104const char *
1105msg(int en)
1106{
1107   /* NB can't use SVX_ASSERT here! */
1108   static char badbuf[256];
1109   if (en >= 1000 && en < 1000 + N_DONTEXTRACTMSGS)
1110      return dontextract[en - 1000];
1111   if (!msg_array) {
1112      if (en != 1)  {
1113         sprintf(badbuf, "Message %d requested before msg_array initialised\n",
1114                 en);
1115         return badbuf;
1116      }
1117      /* this should be the only other message which can be requested before
1118       * the message file is opened and read... */
1119      if (!dontextract) return "Out of memory (couldn't find %lu bytes).";
1120      return dontextract[(/*Out of memory (couldn't find %lu bytes).*/1004)
1121                         - 1000];
1122   }
1123
1124   if (en < 0 || en >= num_msgs) {
1125      sprintf(badbuf, "Message %d out of range\n", en);
1126      return badbuf;
1127   }
1128
1129   if (en == 0) {
1130      const char *p = msg_array[0];
1131      if (!*p) p = "(C)";
1132      return p;
1133   }
1134
1135   return msg_array[en];
1136}
1137
1138/* returns persistent copy of message */
1139const char *
1140msgPerm(int en)
1141{
1142   return msg(en);
1143}
1144
1145void
1146v_report(int severity, const char *fnm, int line, int en, va_list ap)
1147{
1148#ifdef AVEN
1149   aven_v_report(severity, fnm, line, en, ap);
1150#else
1151   if (fnm) {
1152      fputs(fnm, STDERR);
1153      if (line) fprintf(STDERR, ":%d", line);
1154   } else {
1155      fputs(appname_copy, STDERR);
1156   }
1157   fputs(": ", STDERR);
1158
1159   if (severity == 0) {
1160      fputs(msg(/*warning*/4), STDERR);
1161      fputs(": ", STDERR);
1162   }
1163
1164   vfprintf(STDERR, msg(en), ap);
1165   fputnl(STDERR);
1166#endif
1167
1168   switch (severity) {
1169    case 0:
1170      msg_warnings++;
1171      break;
1172    case 1:
1173      msg_errors++;
1174      if (msg_errors == 50)
1175         fatalerror_in_file(fnm, 0, /*Too many errors - giving up*/19);
1176      break;
1177    case 2:
1178      exit(EXIT_FAILURE);
1179   }
1180}
1181
1182void
1183warning(int en, ...)
1184{
1185   va_list ap;
1186   va_start(ap, en);
1187   v_report(0, NULL, 0, en, ap);
1188   va_end(ap);
1189}
1190
1191void
1192error(int en, ...)
1193{
1194   va_list ap;
1195   va_start(ap, en);
1196   v_report(1, NULL, 0, en, ap);
1197   va_end(ap);
1198}
1199
1200void
1201fatalerror(int en, ...)
1202{
1203   va_list ap;
1204   va_start(ap, en);
1205   v_report(2, NULL, 0, en, ap);
1206   va_end(ap);
1207}
1208
1209void
1210warning_in_file(const char *fnm, int line, int en, ...)
1211{
1212   va_list ap;
1213   va_start(ap, en);
1214   v_report(0, fnm, line, en, ap);
1215   va_end(ap);
1216}
1217
1218void
1219error_in_file(const char *fnm, int line, int en, ...)
1220{
1221   va_list ap;
1222   va_start(ap, en);
1223   v_report(1, fnm, line, en, ap);
1224   va_end(ap);
1225}
1226
1227void
1228fatalerror_in_file(const char *fnm, int line, int en, ...)
1229{
1230   va_list ap;
1231   va_start(ap, en);
1232   v_report(2, fnm, line, en, ap);
1233   va_end(ap);
1234}
1235
1236/* Code to support switching character set at runtime (e.g. for a printer
1237 * driver to support different character sets on screen and on the printer)
1238 */
1239typedef struct charset_li {
1240   struct charset_li *next;
1241   int code;
1242   char **msg_array;
1243} charset_li;
1244
1245static charset_li *charset_head = NULL;
1246
1247static int charset = CHARSET_BAD;
1248
1249int
1250select_charset(int charset_code)
1251{
1252   int old_charset = charset;
1253   charset_li *p;
1254
1255#ifdef DEBUG
1256   fprintf(stderr, "select_charset(%d), old charset = %d\n", charset_code,
1257           charset);
1258#endif
1259
1260   charset = charset_code;
1261
1262   /* check if we've already parsed messages for new charset */
1263   for (p = charset_head; p; p = p->next) {
1264#ifdef DEBUG
1265      printf("%p: code %d msg_array %p\n", p, p->code, p->msg_array);
1266#endif
1267      if (p->code == charset) {
1268         msg_array = p->msg_array;
1269         return old_charset;
1270      }
1271   }
1272
1273   /* nope, got to reparse message file */
1274   parse_msg_file(charset_code);
1275
1276   /* add to list */
1277   p = osnew(charset_li);
1278   p->code = charset;
1279   p->msg_array = msg_array;
1280   p->next = charset_head;
1281   charset_head = p;
1282
1283   return old_charset;
1284}
Note: See TracBrowser for help on using the repository browser.