source: git/src/message.c @ 6340494

RELEASE/1.2debug-cidebug-ci-sanitisersstereowalls-data
Last change on this file since 6340494 was 6340494, checked in by Olly Betts <olly@…>, 9 years ago
  • .gitignore,doc/manual.sgml,lib/Makefile.am,src/message.c: There's no

need to duplicate de.msg to de_DE.msg, as the code will fall-back to
de from de_DE anyway.

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