source: git/src/message.c

v1.4.8
Last change on this file was b3b0900, checked in by Olly Betts <olly@…>, 32 hours ago

Clean up header includes

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