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