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