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