Changed include paths to be explicitly specified in the #include directive.
[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       {0, NULL, N_("installation error")},      /* IE */
148       {2, NULL, N_("installation error")},      /* IS */
149
150       {2, &err_error_count, N_("error")},       /* DE */
151       {2, &err_warning_count, N_("warning")},   /* DW */
152
153       {0, &err_error_count, N_("error")},       /* ME */
154       {0, &err_warning_count, N_("warning")},   /* MW */
155       {0, NULL, N_("note")},                    /* MM */
156     };
157
158   struct string msg;
159   int class;
160
161   /* Check verbosity level. */
162   class = e->class;
163   if (((class >> ERR_VERBOSITY_SHIFT) & ERR_VERBOSITY_MASK) > err_verbosity)
164     return;
165   class &= ERR_CLASS_MASK;
166   
167   assert (class >= 0 && class < ERR_CLASS_COUNT);
168   assert (format != NULL);
169   
170   ds_init (&msg, 64);
171   if (e->where.filename && (error_classes[class].flags & ERR_WITH_FILE))
172     {
173       ds_printf (&msg, "%s:", e->where.filename);
174       if (e->where.line_number != -1)
175         ds_printf (&msg, "%d:", e->where.line_number);
176       ds_putc (&msg, ' ');
177     }
178
179   ds_printf (&msg, "%s: ", gettext (error_classes[class].banner));
180   
181   {
182     int *count = error_classes[class].count;
183     if (count)
184       (*count)++;
185   }
186   
187   if (command_name != NULL && (error_classes[class].flags & ERR_IN_PROCEDURE))
188     ds_printf (&msg, "%s: ", command_name);
189
190   if (e->title)
191     ds_puts (&msg, e->title);
192
193   ds_vprintf (&msg, format, args);
194
195   /* FIXME: Check set_messages and set_errors to determine where to
196      send errors and messages.
197
198      Please note that this is not trivial.  We have to avoid an
199      infinite loop in reporting errors that originate in the output
200      section. */
201   dump_message (ds_c_str (&msg), 8, puts_stdout, get_viewwidth());
202
203   ds_destroy (&msg);
204 }
205 \f
206 /* Private functions. */
207
208 #if 0
209 /* Write S followed by a newline to stderr. */
210 static void
211 puts_stderr (const char *s)
212 {
213   fputs (s, stderr);
214   fputc ('\n', stderr);
215 }
216 #endif
217
218 /* Write S followed by a newline to stdout. */
219 static void
220 puts_stdout (const char *s)
221 {
222   puts (s);
223 }
224
225 /* Returns 1 if the line must be broken here */
226 static int
227 compulsory_break(int c)
228 {
229   return ( c == '\n' );
230 }
231
232 /* Returns 1 if C is a `break character', that is, if it is a good
233    place to break a message into lines. */
234 static inline int
235 char_is_break (int quote, int c)
236 {
237   return ((quote && c == DIR_SEPARATOR)
238           || (!quote && (isspace (c) || c == '-' || c == '/'))); 
239 }
240
241 /* Returns 1 if C is a break character where the break should be made
242    BEFORE the character. */
243 static inline int
244 break_before (int quote, int c)
245 {
246   return !quote && isspace (c);
247 }
248
249 /* If C is a break character, returns 1 if the break should be made
250    AFTER the character.  Does not return a meaningful result if C is
251    not a break character. */
252 static inline int
253 break_after (int quote, int c)
254 {
255   return !break_before (quote, c);
256 }
257
258 /* If you want very long words that occur at a bad break point to be
259    broken into two lines even if they're shorter than a whole line by
260    themselves, define as 2/3, or 4/5, or whatever fraction of a whole
261    line you think is necessary in order to consider a word long enough
262    to break into pieces.  Otherwise, define as 0.  See code to grok
263    the details.  Do NOT parenthesize the expression!  */
264 #define BREAK_LONG_WORD 0
265 /* #define BREAK_LONG_WORD 2/3 */
266 /* #define BREAK_LONG_WORD 4/5 */
267
268 /* Divides MSG into lines of WIDTH width for the first line and WIDTH
269    - INDENT width for each succeeding line.  Each line is dumped
270    through FUNC, which may do with the string what it will. */
271 static void
272 dump_message (char *msg, unsigned indent, void (*func) (const char *),
273               unsigned width)
274 {
275   char *cp;
276
277   /* 1 when at a position inside double quotes ("). */
278   int quote = 0;
279
280   /* Buffer for a single line. */
281   char *buf;
282
283   /* If the message is short, just print the full thing. */
284   if (strlen (msg) < width)
285     {
286       func (msg);
287       return;
288     }
289
290   /* Make sure the indent isn't too big relative to the page width. */
291   if (indent > width / 3)
292     indent = width / 3;
293   
294   buf = local_alloc (width + 2);
295
296   /* Advance WIDTH characters into MSG.
297      If that's a valid breakpoint, keep it; otherwise, back up.
298      Output the line. */
299   for (cp = msg; (unsigned) (cp - msg) < width - 1 && 
300          ! compulsory_break(*cp); cp++)
301     if (*cp == '"')
302       quote ^= 1;
303
304   if (break_after (quote, (unsigned char) *cp))
305     {
306       for (cp--; !char_is_break (quote, (unsigned char) *cp) && cp > msg; cp--)
307         if (*cp == '"')
308           quote ^= 1;
309       
310       if (break_after (quote, (unsigned char) *cp))
311         cp++;
312     }
313
314   if (cp <= msg + width * BREAK_LONG_WORD)
315     for (; cp < msg + width - 1; cp++)
316       if (*cp == '"')
317         quote ^= 1;
318   
319   {
320     int c = *cp;
321     *cp = '\0';
322     func (msg);
323     *cp = c;
324   }
325
326
327   /* Repeat above procedure for remaining lines. */
328   for (;;)
329     {
330       static int hard_break=0;
331
332       int idx=0;
333       char *cp2;
334
335       /* Advance past whitespace. */
336       if (! hard_break ) 
337         while ( isspace ((unsigned char) *cp) )
338           cp++;
339       else
340         cp++;
341
342       if (*cp == 0)
343           break; 
344
345
346       /* Advance WIDTH - INDENT characters. */
347       for (cp2 = cp; (unsigned) (cp2 - cp) < width - indent && 
348              *cp2 && !compulsory_break(*cp2);  cp2++)
349         if (*cp2 == '"')
350           quote ^= 1;
351       
352       if ( compulsory_break(*cp2) )
353         hard_break = 1;
354       else
355         hard_break = 0;
356
357
358       /* Back up if this isn't a breakpoint. */
359       {
360         unsigned w = cp2 - cp;
361         if (*cp2 && ! compulsory_break(*cp2) )
362         for (cp2--; !char_is_break (quote, (unsigned char) *cp2) && 
363                cp2 > cp;
364                cp2--)
365           {
366
367             if (*cp2 == '"')
368               quote ^= 1;
369           }
370
371         if (w == width - indent
372             && (unsigned) (cp2 - cp) <= (width - indent) * BREAK_LONG_WORD)
373           for (; (unsigned) (cp2 - cp) < width - indent && *cp2 ; cp2++)
374             if (*cp2 == '"')
375               quote ^= 1;
376       }
377
378       
379       /* Write out the line. */
380
381       memset (buf, ' ', indent);
382       memcpy (&buf[indent], cp, cp2 - cp);
383
384       buf[indent + idx + cp2 - cp] = '\0';
385       func (buf);
386       cp = cp2;
387     }
388
389   local_free (buf);
390 }
391
392 /* Sets COMMAND_NAME as the command name included in some kinds
393    of error messages. */
394 void
395 err_set_command_name (const char *command_name_) 
396 {
397   free (command_name);
398   command_name = command_name_ ? xstrdup (command_name_) : NULL;
399 }
400
401 void 
402 request_bug_report_and_abort(const char *msg )
403 {
404   fprintf(stderr,
405           "******************************************************************\n"
406           "You have discovered a bug in PSPP.\n\n"
407           "  Please report this, by sending "
408           "an email to " PACKAGE_BUGREPORT ",\n"
409           "explaining what you were doing when this happened, and including\n"
410           "a sample of your input file which caused it.\n");
411
412   fprintf(stderr,
413           "Also, please copy the following lines into your bug report:\n\n"
414           "bare_version:        %s\n" 
415           "version:             %s\n"
416           "stat_version:        %s\n"
417           "host_system:         %s\n"
418           "build_system:        %s\n"
419           "default_config_path: %s\n"
420           "include_path:        %s\n"
421           "groff_font_path:     %s\n"
422           "locale_dir:          %s\n"
423           "compiler version:    %s\n"
424           ,
425
426           bare_version,         
427           version,
428           stat_version,
429           host_system,        
430           build_system,
431           default_config_path,
432           include_path, 
433           groff_font_path,
434           locale_dir,
435 #ifdef __VERSION__
436           __VERSION__
437 #else
438           "Unknown"
439 #endif
440           );     
441
442   if ( msg )
443     fprintf(stderr,"Diagnosis: %s\n",msg);
444
445   fprintf(stderr,
446     "******************************************************************\n");
447
448   abort();
449 }
450
451 void 
452 err_assert_fail(const char *expr, const char *file, int line)
453 {
454   char msg[256];
455   snprintf(msg,256,"Assertion failed: %s:%d; (%s)",file,line,expr);
456   request_bug_report_and_abort( msg );
457 }
458