3c91f73266ef4848f5a66931322e32ce43e9b2d0
[pspp-builds.git] / src / error.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    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 Street, Fifth Floor, Boston, MA
18    02110-1301, USA. */
19
20 #include <config.h>
21 #include "error.h"
22 #include <ctype.h>
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include "alloc.h"
27 #include "command.h"
28 #include "getline.h"
29 #include "glob.h"
30 #include "lexer.h"
31 #include "main.h"
32 #include "output.h"
33 #include "settings.h"
34 #include "str.h"
35 #include "var.h"
36 #include "version.h"
37
38 int err_error_count;
39 int err_warning_count;
40
41 int err_already_flagged;
42
43 int err_verbosity;
44
45 /* File locator stack. */
46 static const struct file_locator **file_loc;
47 static int nfile_loc, mfile_loc;
48 \f
49 /* Fairly common public functions. */
50
51 /* Writes error message in CLASS, with title TITLE and text FORMAT,
52    formatted with printf, to the standard places. */
53 void
54 tmsg (int class, const char *title, const char *format, ...)
55 {
56   struct error e;
57   va_list args;
58
59   e.class = class;
60   err_location (&e.where);
61   e.title = title;
62
63   va_start (args, format);
64   err_vmsg (&e, format, args);
65   va_end (args);
66 }
67
68 /* Writes error message in CLASS, with text FORMAT, formatted with
69    printf, to the standard places. */
70 void
71 msg (int class, const char *format, ...)
72 {
73   struct error e;
74   va_list args;
75
76   e.class = class;
77   err_location (&e.where);
78   e.title = NULL;
79
80   va_start (args, format);
81   err_vmsg (&e, format, args);
82   va_end (args);
83 }
84
85 /* Terminate due to fatal error in input. */
86 void
87 err_failure (void)
88 {
89   fflush (stdout);
90   fflush (stderr);
91
92   fprintf (stderr, "%s: %s\n", pgmname,
93            _("Terminating NOW due to a fatal error!"));
94
95   err_hcf (0);
96 }
97
98 /* Terminate unless we're interactive or will go interactive when the
99    file is over with. */
100 void
101 err_cond_fail (void)
102 {
103   if (getl_reading_script)
104     {
105       if (getl_interactive)
106         getl_close_all ();
107       else
108         err_failure ();
109     }
110 }
111 \f
112 /* File locator stack functions. */
113
114 /* Pushes F onto the stack of file locations. */
115 void
116 err_push_file_locator (const struct file_locator *f)
117 {
118   if (nfile_loc >= mfile_loc)
119     {
120       if (mfile_loc == 0)
121         mfile_loc = 8;
122       else
123         mfile_loc *= 2;
124
125       file_loc = xrealloc (file_loc, mfile_loc * sizeof *file_loc);
126     }
127
128   file_loc[nfile_loc++] = f;
129 }
130
131 /* Pops F off the stack of file locations.
132    Argument F is only used for verification that that is actually the
133    item on top of the stack. */
134 void
135 err_pop_file_locator (const struct file_locator *f)
136 {
137   assert (nfile_loc >= 0 && file_loc[nfile_loc - 1] == f);
138   nfile_loc--;
139 }
140
141 /* Puts the current file and line number in F, or NULL and -1 if
142    none. */
143 void
144 err_location (struct file_locator *f)
145 {
146   if (nfile_loc)
147     *f = *file_loc[nfile_loc - 1];
148   else
149     getl_location (&f->filename, &f->line_number);
150 }
151 \f
152 /* Obscure public functions. */
153
154 /* Writes a blank line to the error device(s).
155    FIXME: currently a no-op. */
156 void
157 err_break (void)
158 {
159 }
160
161 /* Checks whether we've had so many errors that it's time to quit
162    processing this syntax file.  If so, then take appropriate
163    action. */
164 void
165 err_check_count (void)
166 {
167   int error_class = getl_interactive ? MM : FE;
168
169   if (get_errorbreak() && err_error_count)
170     msg (error_class, _("Terminating execution of syntax file due to error."));
171   else if (err_error_count > get_mxerrs() )
172     msg (error_class, _("Errors (%d) exceeds limit (%d)."),
173          err_error_count, get_mxerrs());
174   else if (err_error_count + err_warning_count > get_mxwarns() )
175     msg (error_class, _("Warnings (%d) exceed limit (%d)."),
176          err_error_count + err_warning_count, get_mxwarns() );
177   else
178     return;
179
180   getl_close_all ();
181 }
182
183 /* Some machines are broken.  Compensate. */
184 #ifndef EXIT_SUCCESS
185 #define EXIT_SUCCESS 0
186 #endif
187
188 #ifndef EXIT_FAILURE
189 #define EXIT_FAILURE 1
190 #endif
191
192 static int terminating;
193
194 /* Halt-catch-fire.  SUCCESS should be nonzero if exiting successfully
195    or zero if not.  Despite the name, this is the usual way to finish,
196    successfully or not. */
197 void
198 err_hcf (int success)
199 {
200   terminating = 1;
201
202   lex_done();
203   getl_uninitialize ();
204
205   free(file_loc);
206   file_loc = NULL;
207   
208   outp_done ();
209   done_glob();
210
211
212   exit (success ? EXIT_SUCCESS : EXIT_FAILURE);
213 }
214
215 static void puts_stdout (const char *s);
216 static void dump_message (char *errbuf, unsigned indent,
217                           void (*func) (const char *), unsigned width);
218
219 void
220 err_vmsg (const struct error *e, const char *format, va_list args)
221 {
222   /* Class flags. */
223   enum
224     {
225       ERR_IN_PROCEDURE = 01,    /* 1=Display name of current procedure. */
226       ERR_WITH_FILE = 02,       /* 1=Display filename and line number. */
227     };
228
229   /* Describes one class of error. */
230   struct error_class
231     {
232       int flags;                /* Zero or more of ERR_*. */
233       int *count;               /* Counting category. */
234       const char *banner;       /* Banner. */
235     };
236
237   static const struct error_class error_classes[ERR_CLASS_COUNT] =
238     {
239       {0, NULL, N_("fatal")},                   /* FE */
240
241       {3, &err_error_count, N_("error")},       /* SE */
242       {3, &err_warning_count, N_("warning")},   /* SW */
243       {3, NULL, N_("note")},                    /* SM */
244
245       {0, NULL, N_("installation error")},      /* IE */
246       {2, NULL, N_("installation error")},      /* IS */
247
248       {2, &err_error_count, N_("error")},       /* DE */
249       {2, &err_warning_count, N_("warning")},   /* DW */
250
251       {0, &err_error_count, N_("error")},       /* ME */
252       {0, &err_warning_count, N_("warning")},   /* MW */
253       {0, NULL, N_("note")},                    /* MM */
254     };
255
256   struct string msg;
257   int class;
258
259   /* Check verbosity level. */
260   class = e->class;
261   if (((class >> ERR_VERBOSITY_SHIFT) & ERR_VERBOSITY_MASK) > err_verbosity)
262     return;
263   class &= ERR_CLASS_MASK;
264   
265   assert (class >= 0 && class < ERR_CLASS_COUNT);
266   assert (format != NULL);
267   
268   ds_init (&msg, 64);
269   if (e->where.filename && (error_classes[class].flags & ERR_WITH_FILE))
270     {
271       ds_printf (&msg, "%s:", e->where.filename);
272       if (e->where.line_number != -1)
273         ds_printf (&msg, "%d:", e->where.line_number);
274       ds_putc (&msg, ' ');
275     }
276
277   ds_printf (&msg, "%s: ", gettext (error_classes[class].banner));
278   
279   {
280     int *count = error_classes[class].count;
281     if (count)
282       (*count)++;
283   }
284   
285   if (cur_proc && (error_classes[class].flags & ERR_IN_PROCEDURE))
286     ds_printf (&msg, "%s: ", cur_proc);
287
288   if (e->title)
289     ds_puts (&msg, e->title);
290
291   ds_vprintf (&msg, format, args);
292
293   /* FIXME: Check set_messages and set_errors to determine where to
294      send errors and messages.
295
296      Please note that this is not trivial.  We have to avoid an
297      infinite loop in reporting errors that originate in the output
298      section. */
299   dump_message (ds_c_str (&msg), 8, puts_stdout, get_viewwidth());
300
301   ds_destroy (&msg);
302
303   if (e->class == FE && !terminating)
304     err_hcf (0);
305 }
306 \f
307 /* Private functions. */
308
309 #if 0
310 /* Write S followed by a newline to stderr. */
311 static void
312 puts_stderr (const char *s)
313 {
314   fputs (s, stderr);
315   fputc ('\n', stderr);
316 }
317 #endif
318
319 /* Write S followed by a newline to stdout. */
320 static void
321 puts_stdout (const char *s)
322 {
323   puts (s);
324 }
325
326 /* Returns 1 if the line must be broken here */
327 static int
328 compulsory_break(int c)
329 {
330   return ( c == '\n' );
331 }
332
333 /* Returns 1 if C is a `break character', that is, if it is a good
334    place to break a message into lines. */
335 static inline int
336 char_is_break (int quote, int c)
337 {
338   return ((quote && c == DIR_SEPARATOR)
339           || (!quote && (isspace (c) || c == '-' || c == '/'))); 
340 }
341
342 /* Returns 1 if C is a break character where the break should be made
343    BEFORE the character. */
344 static inline int
345 break_before (int quote, int c)
346 {
347   return !quote && isspace (c);
348 }
349
350 /* If C is a break character, returns 1 if the break should be made
351    AFTER the character.  Does not return a meaningful result if C is
352    not a break character. */
353 static inline int
354 break_after (int quote, int c)
355 {
356   return !break_before (quote, c);
357 }
358
359 /* If you want very long words that occur at a bad break point to be
360    broken into two lines even if they're shorter than a whole line by
361    themselves, define as 2/3, or 4/5, or whatever fraction of a whole
362    line you think is necessary in order to consider a word long enough
363    to break into pieces.  Otherwise, define as 0.  See code to grok
364    the details.  Do NOT parenthesize the expression!  */
365 #define BREAK_LONG_WORD 0
366 /* #define BREAK_LONG_WORD 2/3 */
367 /* #define BREAK_LONG_WORD 4/5 */
368
369 /* Divides MSG into lines of WIDTH width for the first line and WIDTH
370    - INDENT width for each succeeding line.  Each line is dumped
371    through FUNC, which may do with the string what it will. */
372 static void
373 dump_message (char *msg, unsigned indent, void (*func) (const char *),
374               unsigned width)
375 {
376   char *cp;
377
378   /* 1 when at a position inside double quotes ("). */
379   int quote = 0;
380
381   /* Buffer for a single line. */
382   char *buf;
383
384   /* If the message is short, just print the full thing. */
385   if (strlen (msg) < width)
386     {
387       func (msg);
388       return;
389     }
390
391   /* Make sure the indent isn't too big relative to the page width. */
392   if (indent > width / 3)
393     indent = width / 3;
394   
395   buf = local_alloc (width + 2);
396
397   /* Advance WIDTH characters into MSG.
398      If that's a valid breakpoint, keep it; otherwise, back up.
399      Output the line. */
400   for (cp = msg; (unsigned) (cp - msg) < width - 1 && 
401          ! compulsory_break(*cp); cp++)
402     if (*cp == '"')
403       quote ^= 1;
404
405   if (break_after (quote, (unsigned char) *cp))
406     {
407       for (cp--; !char_is_break (quote, (unsigned char) *cp) && cp > msg; cp--)
408         if (*cp == '"')
409           quote ^= 1;
410       
411       if (break_after (quote, (unsigned char) *cp))
412         cp++;
413     }
414
415   if (cp <= msg + width * BREAK_LONG_WORD)
416     for (; cp < msg + width - 1; cp++)
417       if (*cp == '"')
418         quote ^= 1;
419   
420   {
421     int c = *cp;
422     *cp = '\0';
423     func (msg);
424     *cp = c;
425   }
426
427
428   /* Repeat above procedure for remaining lines. */
429   for (;;)
430     {
431       static int hard_break=0;
432
433       int idx=0;
434       char *cp2;
435
436       /* Advance past whitespace. */
437       if (! hard_break ) 
438         while ( isspace ((unsigned char) *cp) )
439           cp++;
440       else
441         cp++;
442
443       if (*cp == 0)
444           break; 
445
446
447       /* Advance WIDTH - INDENT characters. */
448       for (cp2 = cp; (unsigned) (cp2 - cp) < width - indent && 
449              *cp2 && !compulsory_break(*cp2);  cp2++)
450         if (*cp2 == '"')
451           quote ^= 1;
452       
453       if ( compulsory_break(*cp2) )
454         hard_break = 1;
455       else
456         hard_break = 0;
457
458
459       /* Back up if this isn't a breakpoint. */
460       {
461         unsigned w = cp2 - cp;
462         if (*cp2 && ! compulsory_break(*cp2) )
463         for (cp2--; !char_is_break (quote, (unsigned char) *cp2) && 
464                cp2 > cp;
465                cp2--)
466           {
467
468             if (*cp2 == '"')
469               quote ^= 1;
470           }
471
472         if (w == width - indent
473             && (unsigned) (cp2 - cp) <= (width - indent) * BREAK_LONG_WORD)
474           for (; (unsigned) (cp2 - cp) < width - indent && *cp2 ; cp2++)
475             if (*cp2 == '"')
476               quote ^= 1;
477       }
478
479       
480       /* Write out the line. */
481
482       memset (buf, ' ', indent);
483       memcpy (&buf[indent], cp, cp2 - cp);
484
485       buf[indent + idx + cp2 - cp] = '\0';
486       func (buf);
487       cp = cp2;
488     }
489
490   local_free (buf);
491 }
492
493
494 void 
495 request_bug_report_and_abort(const char *msg )
496 {
497   fprintf(stderr,
498           "******************************************************************\n"
499           "You have discovered a bug in PSPP.\n\n"
500           "  Please report this, by sending "
501           "an email to " PACKAGE_BUGREPORT ",\n"
502           "explaining what you were doing when this happened, and including\n"
503           "a sample of your input file which caused it.\n");
504
505   fprintf(stderr,
506           "Also, please copy the following lines into your bug report:\n\n"
507           "bare_version:        %s\n" 
508           "version:             %s\n"
509           "stat_version:        %s\n"
510           "host_system:         %s\n"
511           "build_system:        %s\n"
512           "default_config_path: %s\n"
513           "include_path:        %s\n"
514           "groff_font_path:     %s\n"
515           "locale_dir:          %s\n"
516           "compiler version:    %s\n"
517           ,
518
519           bare_version,         
520           version,
521           stat_version,
522           host_system,        
523           build_system,
524           default_config_path,
525           include_path, 
526           groff_font_path,
527           locale_dir,
528 #ifdef __VERSION__
529           __VERSION__
530 #else
531           "Unknown"
532 #endif
533           );     
534
535   if ( msg )
536     fprintf(stderr,"Diagnosis: %s\n",msg);
537
538   fprintf(stderr,
539     "******************************************************************\n");
540
541   abort();
542 }
543
544 void 
545 err_assert_fail(const char *expr, const char *file, int line)
546 {
547   char msg[256];
548   snprintf(msg,256,"Assertion failed: %s:%d; (%s)",file,line,expr);
549   request_bug_report_and_abort( msg );
550 }