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