source: git/src/message.c @ 9fd4874

v1.1.12
Last change on this file since 9fd4874 was b72f4b5, checked in by Olly Betts <olly@…>, 18 years ago

Apply Mark's OS X changes.

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

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