source: git/src/message.c @ 3f1dd91f

RELEASE/1.2debug-cidebug-ci-sanitiserswalls-data
Last change on this file since 3f1dd91f was 3f1dd91f, checked in by Olly Betts <olly@…>, 5 years ago

Make things work with PROJ 6

PROJ 6 no longer ships the esri projection list and doesn't implement
compatibility support so that +init=esri:<CODE> works. We can work
around this by installing a copy of the esri projection list which
PROJ used to provide and hooking things up so that PROJ finds it.

Addresses the remaining blocker in #102, reported by Bas Couwenberg.
Also reported by Richard Knapp and Martin Sluka.

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