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