8c4c447393131391b2d6a8ec5fb03ef1ca216b61
[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 "linebreak.h"
34 #include "progname.h"
35
36 #include "gettext.h"
37 #define _(msgid) gettext (msgid)
38 #define N_(msgid) msgid
39
40 int err_error_count;
41 int err_warning_count;
42
43 int err_already_flagged;
44
45 int err_verbosity;
46
47 static char *command_name;
48 \f
49 /* Public functions. */
50
51 /* Writes error message in CLASS, with title TITLE and text FORMAT,
52    formatted with printf, to the standard places. */
53 void
54 tmsg (enum msg_class class, const char *title, const char *format, ...)
55 {
56   struct error e;
57   va_list args;
58
59   e.category = msg_class_to_category (class);
60   e.severity = msg_class_to_severity (class);
61   err_location (&e.where);
62   e.title = title;
63
64   va_start (args, format);
65   err_vmsg (&e, format, args);
66   va_end (args);
67 }
68
69 /* Writes error message in CLASS, with text FORMAT, formatted with
70    printf, to the standard places. */
71 void
72 msg (enum msg_class class, const char *format, ...)
73 {
74   struct error e;
75   va_list args;
76
77   e.category = msg_class_to_category (class);
78   e.severity = msg_class_to_severity (class);
79   err_location (&e.where);
80   e.title = NULL;
81
82   va_start (args, format);
83   err_vmsg (&e, format, args);
84   va_end (args);
85 }
86
87 /* Writes MESSAGE formatted with printf, to stderr, if the
88    verbosity level is at least LEVEL. */
89 void
90 verbose_msg (int level, const char *format, ...)
91 {
92   if (err_verbosity >= level)
93     {
94       va_list args;
95   
96       va_start (args, format);
97       fprintf (stderr, "%s: ", program_name);
98       vfprintf (stderr, format, args);
99       putc ('\n', stderr);
100       va_end (args);
101     }
102 }
103
104 /* Checks whether we've had so many errors that it's time to quit
105    processing this syntax file. */
106 void
107 err_check_count (void)
108 {
109   if (get_errorbreak() && err_error_count)
110     msg (MN, _("Terminating execution of syntax file due to error."));
111   else if (err_error_count > get_mxerrs() )
112     msg (MN, _("Errors (%d) exceeds limit (%d)."),
113          err_error_count, get_mxerrs());
114   else if (err_error_count + err_warning_count > get_mxwarns() )
115     msg (MN, _("Warnings (%d) exceed limit (%d)."),
116          err_error_count + err_warning_count, get_mxwarns() );
117   else
118     return;
119
120   getl_abort_noninteractive ();
121 }
122
123 /* Some machines are broken.  Compensate. */
124 #ifndef EXIT_SUCCESS
125 #define EXIT_SUCCESS 0
126 #endif
127
128 #ifndef EXIT_FAILURE
129 #define EXIT_FAILURE 1
130 #endif
131
132 static void puts_stdout (int line_indent, const char *line, size_t length);
133 static void dump_message (char *msg,
134                           void (*func) (int line_indent,
135                                         const char *line, size_t length),
136                           unsigned width, unsigned indent);
137
138 void
139 err_done (void) 
140 {
141   lex_done();
142   getl_uninitialize ();
143   readln_uninitialize();
144 }
145
146 void
147 err_vmsg (const struct error *e, const char *format, va_list args)
148 {
149   struct category 
150     {
151       bool show_command_name;   /* Show command name with error? */
152       bool show_file_location;  /* Show syntax file location? */
153     };
154
155   static const struct category categories[] = 
156     {
157       {false, false},           /* MSG_GENERAL. */
158       {true, true},             /* MSG_SYNTAX. */
159       {false, true},            /* MSG_DATA. */
160     };
161
162   struct severity 
163     {
164       const char *name;         /* How to identify this severity. */
165       int *count;               /* Number of msgs with this severity so far. */
166     };
167   
168   static struct severity severities[] = 
169     {
170       {N_("error"), &err_error_count},          /* MSG_ERROR. */
171       {N_("warning"), &err_warning_count},      /* MSG_WARNING. */
172       {NULL, NULL},                             /* MSG_NOTE. */
173     };
174
175   const struct category *category = &categories[e->category];
176   const struct severity *severity = &severities[e->severity];
177   struct string msg = DS_INITIALIZER;
178
179   if (category->show_file_location && e->where.file_name)
180     {
181       ds_printf (&msg, "%s:", e->where.file_name);
182       if (e->where.line_number != -1)
183         ds_printf (&msg, "%d:", e->where.line_number);
184       ds_putc (&msg, ' ');
185     }
186
187   if (severity->name != NULL)
188     ds_printf (&msg, "%s: ", gettext (severity->name));
189   
190   if (severity->count != NULL)
191     ++*severity->count;
192   
193   if (category->show_command_name && command_name != NULL)
194     ds_printf (&msg, "%s: ", command_name);
195
196   if (e->title)
197     ds_puts (&msg, e->title);
198
199   ds_vprintf (&msg, format, args);
200
201   /* FIXME: Check set_messages and set_errors to determine where to
202      send errors and messages. */
203   dump_message (ds_c_str (&msg), puts_stdout, get_viewwidth (), 8);
204
205   ds_destroy (&msg);
206 }
207 \f
208 /* Private functions. */
209
210 /* Write LINE_INDENT spaces, the LENGTH characters in LINE, then
211    a new-line to stdout. */
212 static void puts_stdout (int line_indent,
213                          const char *line, size_t length)
214 {
215   int i;
216   for (i = 0; i < line_indent; i++)
217     putchar (' ');
218   fwrite (line, 1, length, stdout);
219   putchar ('\n');
220 }
221
222 /* Divides MSG into lines of WIDTH width for the first line and
223    WIDTH - INDENT width for each succeeding line.  Each line is
224    passed to FUNC as a null-terminated string (no new-line
225    character is included in the string). */
226 static void
227 dump_message (char *msg,
228               void (*func) (int line_indent, const char *line, size_t length),
229               unsigned width, unsigned indent)
230 {
231   size_t length = strlen (msg);
232   char *string, *breaks;
233   int line_indent;
234   size_t line_start, i;
235
236   /* Allocate temporary buffers.
237      If we can't get memory for them, then just dump the whole
238      message. */
239   string = strdup (msg);
240   breaks = malloc (length);
241   if (string == NULL || breaks == NULL)
242     {
243       free (string);
244       free (breaks);
245       func (0, msg, length);
246       return;
247     }
248
249   /* Break into lines. */
250   if (indent > width / 3)
251     indent = width / 3;
252   mbs_width_linebreaks (string, length,
253                         width - indent, -indent, 0,
254                         NULL, locale_charset (), breaks);
255
256   /* Pass lines to FUNC. */
257   line_start = 0;
258   line_indent = 0;
259   for (i = 0; i < length; i++)
260     switch (breaks[i]) 
261       {
262       case UC_BREAK_POSSIBLE:
263         /* Break before this character,
264            and include this character in the next line. */
265         func (line_indent, &string[line_start], i - line_start);
266         line_start = i;
267         line_indent = indent;
268         break;
269       case UC_BREAK_MANDATORY:
270         /* Break before this character,
271            but don't include this character in the next line
272            (because it'string a new-line). */
273         func (line_indent, &string[line_start], i - line_start);
274         line_start = i + 1;
275         line_indent = indent;
276         break;
277       default:
278         break;
279       }
280   if (line_start < length)
281     func (line_indent, &string[line_start], length - line_start);
282
283   free (string);
284   free (breaks);
285 }
286
287 /* Sets COMMAND_NAME as the command name included in some kinds
288    of error messages. */
289 void
290 err_set_command_name (const char *command_name_) 
291 {
292   free (command_name);
293   command_name = command_name_ ? xstrdup (command_name_) : NULL;
294 }
295
296 void 
297 request_bug_report_and_abort(const char *msg )
298 {
299   fprintf(stderr,
300           "******************************************************************\n"
301           "You have discovered a bug in PSPP.\n\n"
302           "  Please report this, by sending "
303           "an email to " PACKAGE_BUGREPORT ",\n"
304           "explaining what you were doing when this happened, and including\n"
305           "a sample of your input file which caused it.\n");
306
307   fprintf(stderr,
308           "Also, please copy the following lines into your bug report:\n\n"
309           "bare_version:        %s\n" 
310           "version:             %s\n"
311           "stat_version:        %s\n"
312           "host_system:         %s\n"
313           "build_system:        %s\n"
314           "default_config_path: %s\n"
315           "include_path:        %s\n"
316           "groff_font_path:     %s\n"
317           "locale_dir:          %s\n"
318           "compiler version:    %s\n"
319           ,
320
321           bare_version,         
322           version,
323           stat_version,
324           host_system,        
325           build_system,
326           default_config_path,
327           include_path, 
328           groff_font_path,
329           locale_dir,
330 #ifdef __VERSION__
331           __VERSION__
332 #else
333           "Unknown"
334 #endif
335           );     
336
337   if ( msg )
338     fprintf(stderr,"Diagnosis: %s\n",msg);
339
340   fprintf(stderr,
341     "******************************************************************\n");
342
343   abort();
344 }
345
346 void 
347 err_assert_fail(const char *expr, const char *file, int line)
348 {
349   char msg[256];
350   snprintf(msg,256,"Assertion failed: %s:%d; (%s)",file,line,expr);
351   request_bug_report_and_abort( msg );
352 }
353