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