source: git/src/message.c @ b46bf92

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 b46bf92 was 2fdd67c, checked in by Olly Betts <olly@…>, 20 years ago

Remove now dead code to support RISC OS and MS DOS

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

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