source: git/src/message.c @ e05bdb8

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 e05bdb8 was a420b49, checked in by Olly Betts <olly@…>, 26 years ago

0.90 beta 2

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

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