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