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