source: git/src/message.c @ d698192

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 d698192 was db75e18, checked in by Olly Betts <olly@…>, 27 years ago

check in before major fettle

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

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