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