source: git/src/message.c

Last change on this file was 7b26c92, checked in by Olly Betts <olly@…>, 3 months ago

Add the start of a Czech translation

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