source: git/src/message.c @ 9965b2b

RELEASE/1.2debug-cidebug-ci-sanitisersstereowalls-data
Last change on this file since 9965b2b was 9965b2b, checked in by Olly Betts <olly@…>, 14 years ago

src/: Strip out "Far" and "Huge" which we no longer need now that
DOS memory models are no longer relevant.

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

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