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