source: git/src/message.c @ 53496ab3

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

src/message.c: On Microsoft Windows 2000 and newer, use
GetUserDefaultUILanguage() to get the UI language to use. For older
versions, continue to use GetUserDefaultLCID().

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