Rewrite expression code.
[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., 59 Temple Place - Suite 330, Boston, MA
18    02111-1307, 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   outp_done ();
207   done_glob();
208
209
210   exit (success ? EXIT_SUCCESS : EXIT_FAILURE);
211 }
212
213 static void puts_stdout (const char *s);
214 static void dump_message (char *errbuf, unsigned indent,
215                           void (*func) (const char *), unsigned width);
216
217 void
218 err_vmsg (const struct error *e, const char *format, va_list args)
219 {
220   /* Class flags. */
221   enum
222     {
223       ERR_IN_PROCEDURE = 01,    /* 1=Display name of current procedure. */
224       ERR_WITH_FILE = 02,       /* 1=Display filename and line number. */
225     };
226
227   /* Describes one class of error. */
228   struct error_class
229     {
230       int flags;                /* Zero or more of ERR_*. */
231       int *count;               /* Counting category. */
232       const char *banner;       /* Banner. */
233     };
234
235   static const struct error_class error_classes[ERR_CLASS_COUNT] =
236     {
237       {0, NULL, N_("fatal")},                   /* FE */
238
239       {3, &err_error_count, N_("error")},       /* SE */
240       {3, &err_warning_count, N_("warning")},   /* SW */
241       {3, NULL, N_("note")},                    /* SM */
242
243       {0, NULL, N_("installation error")},      /* IE */
244       {2, NULL, N_("installation error")},      /* IS */
245
246       {2, &err_error_count, N_("error")},       /* DE */
247       {2, &err_warning_count, N_("warning")},   /* DW */
248
249       {0, &err_error_count, N_("error")},       /* ME */
250       {0, &err_warning_count, N_("warning")},   /* MW */
251       {0, NULL, N_("note")},                    /* MM */
252     };
253
254   struct string msg;
255   int class;
256
257   /* Check verbosity level. */
258   class = e->class;
259   if (((class >> ERR_VERBOSITY_SHIFT) & ERR_VERBOSITY_MASK) > err_verbosity)
260     return;
261   class &= ERR_CLASS_MASK;
262   
263   assert (class >= 0 && class < ERR_CLASS_COUNT);
264   assert (format != NULL);
265   
266   ds_init (&msg, 64);
267   if (e->where.filename && (error_classes[class].flags & ERR_WITH_FILE))
268     {
269       ds_printf (&msg, "%s:", e->where.filename);
270       if (e->where.line_number != -1)
271         ds_printf (&msg, "%d:", e->where.line_number);
272       ds_putc (&msg, ' ');
273     }
274
275   ds_printf (&msg, "%s: ", gettext (error_classes[class].banner));
276   
277   {
278     int *count = error_classes[class].count;
279     if (count)
280       (*count)++;
281   }
282   
283   if (cur_proc && (error_classes[class].flags & ERR_IN_PROCEDURE))
284     ds_printf (&msg, "%s: ", cur_proc);
285
286   if (e->title)
287     ds_puts (&msg, e->title);
288
289   ds_vprintf (&msg, format, args);
290
291   /* FIXME: Check set_messages and set_errors to determine where to
292      send errors and messages.
293
294      Please note that this is not trivial.  We have to avoid an
295      infinite loop in reporting errors that originate in the output
296      section. */
297   dump_message (ds_c_str (&msg), 8, puts_stdout, get_viewwidth());
298
299   ds_destroy (&msg);
300
301   if (e->class == FE && !terminating)
302     err_hcf (0);
303 }
304 \f
305 /* Private functions. */
306
307 #if 0
308 /* Write S followed by a newline to stderr. */
309 static void
310 puts_stderr (const char *s)
311 {
312   fputs (s, stderr);
313   fputc ('\n', stderr);
314 }
315 #endif
316
317 /* Write S followed by a newline to stdout. */
318 static void
319 puts_stdout (const char *s)
320 {
321   puts (s);
322 }
323
324 /* Returns 1 if the line must be broken here */
325 static int
326 compulsory_break(int c)
327 {
328   return ( c == '\n' );
329 }
330
331 /* Returns 1 if C is a `break character', that is, if it is a good
332    place to break a message into lines. */
333 static inline int
334 char_is_break (int quote, int c)
335 {
336   return ((quote && c == DIR_SEPARATOR)
337           || (!quote && (isspace (c) || c == '-' || c == '/'))); 
338 }
339
340 /* Returns 1 if C is a break character where the break should be made
341    BEFORE the character. */
342 static inline int
343 break_before (int quote, int c)
344 {
345   return !quote && isspace (c);
346 }
347
348 /* If C is a break character, returns 1 if the break should be made
349    AFTER the character.  Does not return a meaningful result if C is
350    not a break character. */
351 static inline int
352 break_after (int quote, int c)
353 {
354   return !break_before (quote, c);
355 }
356
357 /* If you want very long words that occur at a bad break point to be
358    broken into two lines even if they're shorter than a whole line by
359    themselves, define as 2/3, or 4/5, or whatever fraction of a whole
360    line you think is necessary in order to consider a word long enough
361    to break into pieces.  Otherwise, define as 0.  See code to grok
362    the details.  Do NOT parenthesize the expression!  */
363 #define BREAK_LONG_WORD 0
364 /* #define BREAK_LONG_WORD 2/3 */
365 /* #define BREAK_LONG_WORD 4/5 */
366
367 /* Divides MSG into lines of WIDTH width for the first line and WIDTH
368    - INDENT width for each succeeding line.  Each line is dumped
369    through FUNC, which may do with the string what it will. */
370 static void
371 dump_message (char *msg, unsigned indent, void (*func) (const char *),
372               unsigned width)
373 {
374   char *cp;
375
376   /* 1 when at a position inside double quotes ("). */
377   int quote = 0;
378
379   /* Buffer for a single line. */
380   char *buf;
381
382   /* If the message is short, just print the full thing. */
383   if (strlen (msg) < width)
384     {
385       func (msg);
386       return;
387     }
388
389   /* Make sure the indent isn't too big relative to the page width. */
390   if (indent > width / 3)
391     indent = width / 3;
392   
393   buf = local_alloc (width + 2);
394
395   /* Advance WIDTH characters into MSG.
396      If that's a valid breakpoint, keep it; otherwise, back up.
397      Output the line. */
398   for (cp = msg; (unsigned) (cp - msg) < width - 1 && 
399          ! compulsory_break(*cp); cp++)
400     if (*cp == '"')
401       quote ^= 1;
402
403   if (break_after (quote, (unsigned char) *cp))
404     {
405       for (cp--; !char_is_break (quote, (unsigned char) *cp) && cp > msg; cp--)
406         if (*cp == '"')
407           quote ^= 1;
408       
409       if (break_after (quote, (unsigned char) *cp))
410         cp++;
411     }
412
413   if (cp <= msg + width * BREAK_LONG_WORD)
414     for (; cp < msg + width - 1; cp++)
415       if (*cp == '"')
416         quote ^= 1;
417   
418   {
419     int c = *cp;
420     *cp = '\0';
421     func (msg);
422     *cp = c;
423   }
424
425
426   /* Repeat above procedure for remaining lines. */
427   for (;;)
428     {
429       static int hard_break=0;
430
431       int idx=0;
432       char *cp2;
433
434       /* Advance past whitespace. */
435       if (! hard_break ) 
436         while ( isspace ((unsigned char) *cp) )
437           cp++;
438       else
439         cp++;
440
441       if (*cp == 0)
442           break; 
443
444
445       /* Advance WIDTH - INDENT characters. */
446       for (cp2 = cp; (unsigned) (cp2 - cp) < width - indent && 
447              *cp2 && !compulsory_break(*cp2);  cp2++)
448         if (*cp2 == '"')
449           quote ^= 1;
450       
451       if ( compulsory_break(*cp2) )
452         hard_break = 1;
453       else
454         hard_break = 0;
455
456
457       /* Back up if this isn't a breakpoint. */
458       {
459         unsigned w = cp2 - cp;
460         if (*cp2 && ! compulsory_break(*cp2) )
461         for (cp2--; !char_is_break (quote, (unsigned char) *cp2) && 
462                cp2 > cp;
463                cp2--)
464           {
465
466             if (*cp2 == '"')
467               quote ^= 1;
468           }
469
470         if (w == width - indent
471             && (unsigned) (cp2 - cp) <= (width - indent) * BREAK_LONG_WORD)
472           for (; (unsigned) (cp2 - cp) < width - indent && *cp2 ; cp2++)
473             if (*cp2 == '"')
474               quote ^= 1;
475       }
476
477       
478       /* Write out the line. */
479
480       memset (buf, ' ', indent);
481       memcpy (&buf[indent], cp, cp2 - cp);
482
483       buf[indent + idx + cp2 - cp] = '\0';
484       func (buf);
485       cp = cp2;
486     }
487
488   local_free (buf);
489 }
490
491
492 void 
493 request_bug_report_and_abort(const char *msg )
494 {
495   fprintf(stderr,
496           "******************************************************************\n"
497           "You have discovered a bug in PSPP.\n\n"
498           "  Please report this, by sending "
499           "an email to " PACKAGE_BUGREPORT ",\n"
500           "explaining what you were doing when this happened, and including\n"
501           "a sample of your input file which caused it.\n");
502
503   fprintf(stderr,
504           "Also, please copy the following lines into your bug report:\n\n"
505           "bare_version:        %s\n" 
506           "version:             %s\n"
507           "stat_version:        %s\n"
508           "host_system:         %s\n"
509           "build_system:        %s\n"
510           "default_config_path: %s\n"
511           "include_path:        %s\n"
512           "groff_font_path:     %s\n"
513           "locale_dir:          %s\n",
514
515           bare_version,         
516           version,
517           stat_version,
518           host_system,        
519           build_system,
520           default_config_path,
521           include_path, 
522           groff_font_path,
523           locale_dir);     
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 }