source: git/src/message.c @ 9203b37f

RELEASE/1.0RELEASE/1.1RELEASE/1.2debug-cidebug-ci-sanitisersfaster-cavernloglog-selectstereostereo-2025walls-datawalls-data-hanging-as-warningwarn-only-for-hanging-survey
Last change on this file since 9203b37f was cc089ef, checked in by Olly Betts <olly@…>, 25 years ago

Tidied up some #include-s

git-svn-id: file:///home/survex-svn/survex/trunk@386 4b37db11-9a0c-4f06-9ece-9ab7cdaee568

  • Property mode set to 100644
File size: 17.0 KB
Line 
1/* > message.c
2 * Fairly general purpose message and error routines
3 * Copyright (C) 1993-2000 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19
20/*#define DEBUG 1*/
21
22#ifdef HAVE_CONFIG_H
23# include <config.h>
24#endif
25
26#include <stdarg.h>
27
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <ctype.h>
32#include <limits.h>
33#include <errno.h>
34
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
42#ifdef HAVE_SIGNAL
43# ifdef HAVE_SETJMP
44#  include <setjmp.h>
45static jmp_buf jmpbufSignal;
46#  include <signal.h>
47# else
48#  undef HAVE_SIGNAL
49# endif
50#endif
51
52/* This is the name of the default language -- set like this so folks can
53 * add (for eg) -DDEFAULTLANG="fr" to UFLG in the makefile
54 * FIXME - update wrt automake/autoconf
55 */
56#ifndef DEFAULTLANG
57# define DEFAULTLANG "en"
58#endif
59
60/* For funcs which want to be immune from messing around with different
61 * calling conventions */
62#ifndef CDECL
63# define CDECL
64#endif
65
66static int cWarnings = 0; /* keep track of how many warnings we've given */
67static int cErrors = 0;   /* and how many (non-fatal) errors */
68
69extern int error_summary(void) {
70   fprintf(STDERR, msg(/*There were %d warning(s) and %d non-fatal error(s).*/16),
71           cWarnings, cErrors);
72   fputnl(STDERR);
73   return (cErrors ? EXIT_FAILURE : EXIT_SUCCESS);
74}
75
76/* in case osmalloc() fails before szAppNameCopy is set up */
77const char *szAppNameCopy = "anonymous program";
78
79/* error code for failed osmalloc and osrealloc calls */
80static void
81outofmem(OSSIZE_T size)
82{
83   fatalerror(1/*Out of memory (couldn't find %lu bytes).*/, (unsigned long)size);
84}
85
86#ifdef TOMBSTONES
87#define TOMBSTONE_SIZE 16
88static char tombstone[TOMBSTONE_SIZE] = "012345\xfftombstone";
89#endif
90
91/* malloc with error catching if it fails. Also allows us to write special
92 * versions easily eg for DOS EMS or MS Windows.
93 */
94extern void FAR *
95osmalloc(OSSIZE_T size)
96{
97   void FAR *p;
98#ifdef TOMBSTONES
99   size += TOMBSTONE_SIZE * 2;
100   p = malloc(size);
101#else
102   p = xosmalloc(size);
103#endif
104   if (p == NULL) outofmem(size);
105#ifdef TOMBSTONES
106printf("osmalloc truep=%p truesize=%d\n",p,size);
107   memcpy(p, tombstone, TOMBSTONE_SIZE);
108   memcpy(p + size - TOMBSTONE_SIZE, tombstone, TOMBSTONE_SIZE);
109   *(size_t *)p = size;
110   p += TOMBSTONE_SIZE;
111#endif
112   return p;
113}
114
115/* realloc with error catching if it fails. */
116extern void FAR *
117osrealloc(void *p, OSSIZE_T size)
118{
119   /* some pre-ANSI realloc implementations don't cope with a NULL pointer */
120   if (p == NULL) {
121      p = xosmalloc(size);
122   } else {
123#ifdef TOMBSTONES
124      int true_size;
125      size += TOMBSTONE_SIZE * 2;
126      p -= TOMBSTONE_SIZE;
127      true_size = *(size_t *)p;
128printf("osrealloc (in truep=%p truesize=%d)\n",p,true_size);
129      if (memcmp(p + sizeof(size_t), tombstone + sizeof(size_t),
130                 TOMBSTONE_SIZE - sizeof(size_t)) != 0) {
131         printf("start tombstone for block %p, size %d corrupted!",
132                p + TOMBSTONE_SIZE, true_size - TOMBSTONE_SIZE * 2);     
133      }
134      if (memcmp(p + true_size - TOMBSTONE_SIZE, tombstone,
135                 TOMBSTONE_SIZE) != 0) {
136         printf("end tombstone for block %p, size %d corrupted!",
137                p + TOMBSTONE_SIZE, true_size - TOMBSTONE_SIZE * 2);     
138      }
139      p = realloc(p, size);
140      if (p == NULL) outofmem(size);
141printf("osrealloc truep=%p truesize=%d\n",p,size);
142      memcpy(p, tombstone, TOMBSTONE_SIZE);
143      memcpy(p + size - TOMBSTONE_SIZE, tombstone, TOMBSTONE_SIZE);
144      *(size_t *)p = size;
145      p += TOMBSTONE_SIZE;
146#else
147      p = xosrealloc(p, size);
148#endif
149   }
150   if (p == NULL) outofmem(size);
151   return p;
152}
153
154extern void FAR *
155osstrdup(const char *str)
156{
157   char *p;
158   OSSIZE_T len;
159   len = strlen(str) + 1;
160   p = osmalloc(len);
161   memcpy(p, str, len);
162   return p;
163}
164
165/* osfree is usually just a macro in osalloc.h */
166#ifdef TOMBSTONES
167extern void
168osfree(void *p)
169{
170   int true_size;
171   if (!p) return;
172   p -= TOMBSTONE_SIZE;
173   true_size = *(size_t *)p;
174printf("osfree truep=%p truesize=%d\n",p,true_size);
175   if (memcmp(p + sizeof(size_t), tombstone + sizeof(size_t),
176              TOMBSTONE_SIZE - sizeof(size_t)) != 0) {
177      printf("start tombstone for block %p, size %d corrupted!",
178             p + TOMBSTONE_SIZE, true_size - TOMBSTONE_SIZE * 2);     
179   }
180   if (memcmp(p + true_size - TOMBSTONE_SIZE, tombstone,
181              TOMBSTONE_SIZE) != 0) {
182      printf("end tombstone for block %p, size %d corrupted!",
183             p + TOMBSTONE_SIZE, true_size - TOMBSTONE_SIZE * 2);     
184   }
185   free(p);
186}
187#endif
188
189#ifdef HAVE_SIGNAL
190
191static int sigReceived;
192
193/* for systems not using autoconf, assume the signal handler returns void
194 * unless specified elsewhere */
195#ifndef RETSIGTYPE
196# define RETSIGTYPE void
197#endif
198
199static CDECL RETSIGTYPE FAR report_sig( int sig ) {
200   sigReceived = sig;
201   longjmp(jmpbufSignal, 1);
202}
203
204static void
205init_signals(void)
206{
207   int en;
208   if (!setjmp(jmpbufSignal)) {
209#if 0 /* FIXME: disable for now so we get a core dump */
210      signal(SIGABRT, report_sig); /* abnormal termination eg abort() */
211      signal(SIGFPE,  report_sig); /* arithmetic error eg /0 or overflow */
212      signal(SIGILL,  report_sig); /* illegal function image eg illegal instruction */
213      signal(SIGSEGV, report_sig); /* illegal storage access eg access outside memory limits */
214#endif
215      signal(SIGINT,  report_sig); /* interactive attention eg interrupt */
216      signal(SIGTERM, report_sig); /* termination request sent to program */
217# ifdef SIGSTAK /* only on RISC OS AFAIK */
218      signal(SIGSTAK, report_sig); /* stack overflow */
219# endif
220      return;
221   }
222
223   switch (sigReceived) {
224   case SIGABRT: en=90; break;
225   case SIGFPE:  en=91; break;
226   case SIGILL:  en=92; break;
227   case SIGINT:  en=93; break;
228   case SIGSEGV: en=94; break;
229   case SIGTERM: en=95; break;
230# ifdef SIGSTAK
231   case SIGSTAK: en=96; break;
232# endif
233   default:      en=97; break;
234   }
235   fputsnl(msg(en), STDERR);
236   if (errno >= 0) {
237# ifdef HAVE_STRERROR
238      fputsnl(strerror(errno), STDERR);
239# elif defined(HAVE_SYS_ERRLIST)
240      if (errno < sys_nerr) fputsnl(STDERR, sys_errlist[errno]);
241# elif defined(HAVE_PERROR)
242      perror(NULL); /* always goes to stderr */
243      /* if (arg!=NULL && *arg!='\0') fputs("<arg>: <err>\n",stderr); */
244      /* else fputs("<err>\n",stderr); */
245# else
246      fprintf(STDERR, "error code %d\n", errno);
247# endif
248   }
249   /* Any signals apart from SIGINT and SIGTERM suggest a bug */
250   if (sigReceived != SIGINT && sigReceived != SIGTERM)
251      fatalerror(/*Bug in program detected! Please report this to the authors*/11);
252
253   exit(EXIT_FAILURE);
254}
255#endif
256
257#define CHARSET_BAD       -1
258#define CHARSET_USASCII    0
259#define CHARSET_ISO_8859_1 1
260#define CHARSET_DOSCP850   2
261#define CHARSET_RISCOS31   3
262static int default_charset( void ) {
263#ifdef ISO8859_1
264   return CHARSET_ISO_8859_1;
265#elif (OS==RISCOS)
266/* RISCOS 3.1 and above CHARSET_RISCOS31 (ISO_8859_1 + extras in 128-159)
267 * RISCOS < 3.1 is ISO_8859_1 !HACK! */
268   return CHARSET_RISCOS31;
269#elif (OS==MSDOS)
270   return CHARSET_DOSCP850;
271#else
272   return CHARSET_ISO_8859_1; /* Look at env var CHARSET ? !HACK! */
273#endif
274}
275
276#if (OS==MSDOS)
277static int
278xlate_dos_cp850(int unicode)
279{
280   switch (unicode) {
281#include "uni2dos.h"
282   }
283   return 0;
284}
285#endif
286
287static int
288add_unicode(int charset, unsigned char *p, int value)
289{
290#ifdef DEBUG
291   fprintf(stderr, "add_unicode(%d, %p, %d)\n", charset, p, value);
292#endif
293   if (value == 0) return 0;
294   switch (charset) {
295   case CHARSET_USASCII:
296      if (value < 128) {
297         *p = value;
298         return 1;
299      }
300      break;
301   case CHARSET_ISO_8859_1:
302#if (OS==RISCOS)
303   case CHARSET_RISCOS31: /* RISC OS 3.1 has a few extras in 128-159 */
304#endif
305      if (value < 256) {
306         *p = value;
307         return 1;
308      }
309#if (OS==RISCOS)
310      /* FIXME: if OS version >= 3.1 handle extras here */
311      /* RISC OS 3.1 (and later) extensions to ISO-8859-1:
312       * \^y = \x86
313       * \^Y = \x85
314       * \^w = \x82
315       * \^W = \x81
316       * \oe = \x9b
317       * \OE = \x9a
318       */
319#endif
320      break;
321#if (OS==MSDOS)
322   case CHARSET_DOSCP850:
323      value = xlate_dos_cp850(value);
324      if (value) {
325         *p = value;
326         return 1;
327      }
328      break;
329#endif
330   }
331   return 0;
332}
333
334/* fall back on looking in the current directory */
335static const char *pth_cfg_files = "";
336
337static int num_msgs = 0;
338static char **msg_array = NULL;
339
340static void
341parse_msg_file(int charset_code)
342{
343   FILE *fh;
344   unsigned char header[20];
345   /*const*/ char *lang;
346   int i;
347   unsigned len;
348   unsigned char *p;
349   
350#ifdef DEBUG
351   fprintf(stderr, "parse_msg_file(%d)\n", charset_code);
352#endif
353
354   lang = getenv("SURVEXLANG");
355#ifdef DEBUG
356   fprintf(stderr, "lang = %p (= \"%s\")\n", lang, lang?lang:"(null)");
357#endif
358   
359   if (!lang || !*lang) {
360      lang = getenv("LANG");
361      if (!lang || !*lang) lang = DEFAULTLANG;
362   }
363#ifdef DEBUG
364   fprintf(stderr, "lang = %p (= \"%s\")\n", lang, lang?lang:"(null)");
365#endif
366
367#if 1
368   /* backward compatibility - FIXME deprecate? */
369   if (strcasecmp(lang, "engi") == 0) {
370      lang = "en";
371   } else if (strcasecmp(lang, "engu") == 0) {
372      lang = "en-us";
373   } else if (strcasecmp(lang, "fren") == 0) {
374      lang = "fr";
375   } else if (strcasecmp(lang, "germ") == 0) {
376      lang = "de";
377   } else if (strcasecmp(lang, "ital") == 0) {
378      lang = "it";
379   } else if (strcasecmp(lang, "span") == 0) {
380      lang = "es";
381   } else if (strcasecmp(lang, "cata") == 0) {
382      lang = "ca";
383   } else if (strcasecmp(lang, "port") == 0) {
384      lang = "pt";
385   }
386#endif
387#ifdef DEBUG
388   fprintf(stderr, "lang = %p (= \"%s\")\n", lang, lang?lang:"(null)");
389#endif
390
391   lang = osstrdup(lang);
392   /* On my RedHat 6.1 Linux box, LANG defaults to en_US - be nice and
393    * handle this... */
394   if (strchr(lang, '_')) {
395      char *under = strchr(lang, '_');
396      *under++ = '-';
397      while (*under) {
398         *under = tolower(*under);
399         under++;
400      }
401   }
402
403   fh = fopenWithPthAndExt(pth_cfg_files, lang, EXT_SVX_MSG, "rb", NULL);
404
405   if (!fh) {
406      /* e.g. if 'en-COCKNEY' is unknown, see if we know 'en' */
407      if (strlen(lang) > 3 && lang[2] == '-') {
408         char lang_generic[3];
409         lang_generic[0] = lang[0];
410         lang_generic[1] = lang[1];
411         lang_generic[2] = '\0';
412         fh = fopenWithPthAndExt(pth_cfg_files, lang_generic, EXT_SVX_MSG,
413                                 "rb", NULL);
414      }
415   }
416
417   if (!fh) {
418      /* no point extracting this error, as it won't get used if file opens */
419      fprintf(STDERR, "Can't open message file '%s' using path '%s'\n",
420              lang, pth_cfg_files);
421      exit(EXIT_FAILURE);
422   }
423
424   if (fread(header, 1, 20, fh) < 20 ||
425       memcmp(header, "Svx\nMsg\r\n\xfe\xff", 12) != 0) {
426      /* no point extracting this error, as it won't get used if file opens */
427      fprintf(STDERR, "Problem with message file '%s'\n", lang);
428      exit(EXIT_FAILURE);
429   }
430
431   if (header[12] != 0) {
432      /* no point extracting this error, as it won't get used if file opens */
433      fprintf(STDERR, "I don't understand this message file version\n");
434      exit(EXIT_FAILURE);
435   }
436
437   num_msgs = (header[14] << 8) | header[15];
438
439   len = 0;
440   for (i = 16; i < 20; i++) len = (len << 8) | header[i];
441
442   p = osmalloc(len);
443   if (fread(p, 1, len, fh) < len) {
444      /* no point extracting this error - it won't get used once file's read */
445      fprintf(STDERR, "Message file truncated?\n");
446      exit(EXIT_FAILURE);
447   }
448   fclose(fh);
449
450#ifdef DEBUG
451   fprintf(stderr, "lang = '%s', num_msgs = %d, len = %d\n", lang, num_msgs, len);
452#endif
453
454   msg_array = osmalloc(sizeof(char *) * num_msgs);
455
456   for (i = 0; i < num_msgs; i++) {
457      unsigned char *to = p;
458      int ch;
459      msg_array[i] = (char *)p;
460      while ((ch = *p++) != 0) {
461         /* A byte in the range 0x80-0xbf or 0xf0-0xff isn't valid in
462          * this state, (0xf0-0xfd mean values > 0xffff) so treat as
463          * literal and try to resync so we cope better when fed
464          * non-utf-8 data.  Similarly we abandon a multibyte sequence
465          * if we hit an invalid character. */
466         if (ch >= 0xc0 && ch < 0xf0) {
467            int ch1 = *p;
468            if ((ch1 & 0xc0) != 0x80) goto resync;
469               
470            if (ch < 0xe0) {
471               /* 2 byte sequence */
472               ch = ((ch & 0x1f) << 6) | (ch1 & 0x3f);
473               p++;
474            } else {
475               /* 3 byte sequence */
476               int ch2 = p[1];
477               if ((ch2 & 0xc0) != 0x80) goto resync;
478               ch = ((ch & 0x1f) << 12) | ((ch1 & 0x3f) << 6) | (ch2 & 0x3f);
479               p += 2;
480            }
481         }
482           
483         resync:
484           
485         if (ch < 127) {
486            *to++ = (char)ch;
487         } else {
488            /* FIXME this rather assumes a 2 byte UTF-8 code never
489             * transliterates to more than 2 characters */
490            to += add_unicode(charset_code, to, ch);
491         }
492      }
493      *to++ = '\0';
494   }
495}
496
497const char *
498msg_cfgpth(void)
499{
500   return pth_cfg_files;
501}
502
503void
504msg_init(const char *argv0)
505{
506   char *p;
507
508#ifdef HAVE_SIGNAL
509   init_signals();
510#endif
511   /* This code *should* be completely bomb-proof even if strcpy
512    * generates a signal
513    */
514   szAppNameCopy = argv0; /* FIXME... */
515   szAppNameCopy = osstrdup(argv0);
516
517   /* Look for env. var. "SURVEXHOME" or the like */
518   p = getenv("SURVEXHOME");
519   if (p && *p) {
520      pth_cfg_files = osstrdup(p);
521#if (OS==UNIX) && defined(DATADIR) && defined(PACKAGE)
522   } else {
523      /* under Unix, we compile in the configured path */
524      pth_cfg_files = DATADIR "/" PACKAGE;
525#else
526   } else if (argv0) {
527      /* else try the path on argv[0] */
528      pth_cfg_files = path_from_fnm(argv0);
529#endif
530   }
531
532   select_charset(default_charset());
533}
534
535/* message may be overwritten by next call (but not in current implementation) */
536extern const char *
537msg(int en)
538{
539   static const char *szBadEn = "???";
540
541   if (!msg_array) {
542      if (en != 1) return szBadEn;
543      /* this should be the only message which can be requested before
544       * the message file is opened and read... */
545      return "Out of memory (couldn't find %ul bytes).\n";
546   }
547
548   if (en < 0 || en >= num_msgs) return szBadEn;
549
550   return msg_array[en];
551}
552
553/* returns persistent copy of message */
554extern const char *
555msgPerm(int en)
556{
557   return msg(en);
558}
559
560void
561v_report(int severity, const char *fnm, int line, int en, va_list ap)
562{
563   if (fnm) {
564      fputs(fnm, STDERR);
565      if (line) fprintf(STDERR, ":%d", line);
566   } else {
567      fputs(szAppNameCopy, STDERR);
568   }   
569   fputs(": ", STDERR);
570
571   if (severity == 0) {
572      fputs(msg(/*warning*/4), STDERR);
573      fputs(": ", STDERR);
574   }
575
576   vfprintf(STDERR, msg(en), ap);
577   fputnl(STDERR);
578   
579   /* FIXME allow "warnings are errors" and/or "errors are fatal" */
580   switch (severity) {
581    case 0:
582      cWarnings++;
583      break;
584    case 1:
585      cErrors++;
586      if (cErrors == 50)
587         fatalerror_in_file(fnm, 0, /*Too many errors - giving up*/19);
588      break;
589    case 2:
590      exit(EXIT_FAILURE);
591   }
592}
593
594void
595warning(int en, ...)
596{
597   va_list ap;
598   va_start(ap, en);
599   v_report(0, NULL, 0, en, ap);
600   va_end(ap);
601}
602
603void
604error(int en, ...)
605{
606   va_list ap;
607   va_start(ap, en);
608   v_report(1, NULL, 0, en, ap);
609   va_end(ap);
610}
611
612void
613fatalerror(int en, ...)
614{
615   va_list ap;
616   va_start(ap, en);
617   v_report(2, NULL, 0, en, ap);
618   va_end(ap);
619}
620
621void
622warning_in_file(const char *fnm, int line, int en, ...)
623{
624   va_list ap;
625   va_start(ap, en);
626   v_report(0, fnm, line, en, ap);
627   va_end(ap);
628}
629
630void
631error_in_file(const char *fnm, int line, int en, ...)
632{
633   va_list ap;
634   va_start(ap, en);
635   v_report(1, fnm, line, en, ap);
636   va_end(ap);
637}
638
639void
640fatalerror_in_file(const char *fnm, int line, int en, ...)
641{
642   va_list ap;
643   va_start(ap, en);
644   v_report(2, fnm, line, en, ap);
645   va_end(ap);
646}
647
648/* Code to support switching character set at runtime (e.g. for a printer
649 * driver to support different character sets on screen and on the printer)
650 */
651typedef struct charset_li {
652   struct charset_li *next;
653   int code;
654   char **msg_array;
655} charset_li;
656
657static charset_li *charset_head = NULL;
658
659static int charset = CHARSET_BAD;
660
661int
662select_charset(int charset_code)
663{
664   int old_charset = charset;
665   charset_li *p;
666
667#ifdef DEBUG
668   fprintf(stderr, "select_charset(%d), old charset = %d\n", charset_code, charset);
669#endif
670   
671   charset = charset_code;
672
673   /* check if we've already parsed messages for new charset */
674   for (p = charset_head; p; p = p->next) {
675#ifdef DEBUG
676      printf("%p: code %d msg_array %p\n", p, p->code, p->msg_array);
677#endif
678      if (p->code == charset) {
679         msg_array = p->msg_array;
680         return old_charset;
681      }
682   }
683
684   /* nope, got to reparse message file */
685   parse_msg_file(charset_code);
686
687   /* add to list */
688   p = osnew(charset_li);
689   p->code = charset;
690   p->msg_array = msg_array;
691   p->next = charset_head;
692   charset_head = p;
693
694   return old_charset;
695}
Note: See TracBrowser for help on using the repository browser.