message: Consistently initialize locator; use 0 for "no line number".
[pspp] / src / libpspp / message.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2006, 2009, 2010 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include "message.h"
20 #include "msg-locator.h"
21
22 #include <assert.h>
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28
29 #include <libpspp/str.h>
30 #include <libpspp/version.h>
31 #include <data/settings.h>
32
33 #include "gl/progname.h"
34 #include "gl/xalloc.h"
35 #include "gl/xvasprintf.h"
36
37 #include "gettext.h"
38 #define _(msgid) gettext (msgid)
39
40 /* Message handler as set by msg_init(). */
41 static void (*msg_handler)  (const struct msg *);
42
43 /* Disables emitting messages if positive. */
44 static int messages_disabled;
45
46 /* Public functions. */
47
48 /* Writes error message in CLASS, with text FORMAT, formatted with
49    printf, to the standard places. */
50 void
51 msg (enum msg_class class, const char *format, ...)
52 {
53   struct msg m;
54   va_list args;
55
56   m.category = msg_class_to_category (class);
57   m.severity = msg_class_to_severity (class);
58   va_start (args, format);
59   m.text = xvasprintf (format, args);
60   m.where.file_name = NULL;
61   m.where.line_number = 0;
62   va_end (args);
63
64   msg_emit (&m);
65 }
66
67 static struct source_stream *s_stream;
68
69 void
70 msg_init (struct source_stream *ss,  void (*handler) (const struct msg *) )
71 {
72   s_stream = ss;
73   msg_handler = handler;
74 }
75
76 void
77 msg_done (void)
78 {
79 }
80 \f
81 /* Working with messages. */
82
83 /* Duplicate a message */
84 struct msg *
85 msg_dup (const struct msg *m)
86 {
87   struct msg *new_msg;
88
89   new_msg = xmemdup (m, sizeof *m);
90   if (m->where.file_name != NULL)
91     new_msg->where.file_name = xstrdup (m->where.file_name);
92   new_msg->text = xstrdup (m->text);
93
94   return new_msg;
95 }
96
97 /* Frees a message created by msg_dup().
98
99    (Messages not created by msg_dup(), as well as their where.file_name
100    members, are typically not dynamically allocated, so this function should
101    not be used to destroy them.) */
102 void
103 msg_destroy (struct msg *m)
104 {
105   free (m->where.file_name);
106   free (m->text);
107   free (m);
108 }
109
110 char *
111 msg_to_string (const struct msg *m, const char *command_name)
112 {
113   const char *label;
114   struct string s;
115
116   ds_init_empty (&s);
117
118   if (m->category != MSG_C_GENERAL
119       && (m->where.file_name || m->where.line_number != -1))
120     {
121       if (m->where.file_name)
122         ds_put_format (&s, "%s:", m->where.file_name);
123       if (m->where.line_number > 0)
124         ds_put_format (&s, "%d:", m->where.line_number);
125       ds_put_char (&s, ' ');
126     }
127
128   switch (m->severity)
129     {
130     case MSG_S_ERROR:
131       label = _("error");
132       break;
133     case MSG_S_WARNING:
134       label = _("warning");
135       break;
136     case MSG_S_NOTE:
137     default:
138       label = _("note");
139       break;
140     }
141   ds_put_format (&s, "%s: ", label);
142
143   if (m->category == MSG_C_SYNTAX && command_name != NULL)
144     ds_put_format (&s, "%s: ", command_name);
145
146   ds_put_cstr (&s, m->text);
147
148   return ds_cstr (&s);
149 }
150 \f
151
152 /* Number of messages reported, by severity level. */
153 static int counts[MSG_N_SEVERITIES];
154
155 /* True after the maximum number of errors or warnings has been exceeded. */
156 static bool too_many_errors;
157
158 /* True after the maximum number of notes has been exceeded. */
159 static bool too_many_notes;
160
161 /* True iff warnings have been explicitly disabled (MXWARNS = 0) */
162 static bool warnings_off = false;
163
164 /* Checks whether we've had so many errors that it's time to quit
165    processing this syntax file. */
166 bool
167 msg_ui_too_many_errors (void)
168 {
169   return too_many_errors;
170 }
171
172 void
173 msg_ui_disable_warnings (bool x)
174 {
175   warnings_off = x;
176 }
177
178
179 void
180 msg_ui_reset_counts (void)
181 {
182   int i;
183
184   for (i = 0; i < MSG_N_SEVERITIES; i++)
185     counts[i] = 0;
186   too_many_errors = false;
187   too_many_notes = false;
188 }
189
190 bool
191 msg_ui_any_errors (void)
192 {
193   return counts[MSG_S_ERROR] > 0;
194 }
195
196 static void
197 submit_note (char *s)
198 {
199   struct msg m;
200
201   m.category = MSG_C_GENERAL;
202   m.severity = MSG_S_NOTE;
203   m.where.file_name = NULL;
204   m.where.line_number = 0;
205   m.text = s;
206   msg_handler (&m);
207   free (s);
208 }
209
210
211
212 static void
213 process_msg (const struct msg *m)
214 {
215   int n_msgs, max_msgs;
216
217
218   if (too_many_errors
219       || (too_many_notes && m->severity == MSG_S_NOTE)
220       || (warnings_off && m->severity == MSG_S_WARNING) )
221     return;
222
223   msg_handler (m);
224
225   counts[m->severity]++;
226   max_msgs = settings_get_max_messages (m->severity);
227   n_msgs = counts[m->severity];
228   if (m->severity == MSG_S_WARNING)
229     n_msgs += counts[MSG_S_ERROR];
230   if (n_msgs > max_msgs)
231     {
232       if (m->severity == MSG_S_NOTE)
233         {
234           too_many_notes = true;
235           submit_note (xasprintf (_("Notes (%d) exceed limit (%d).  "
236                                     "Suppressing further notes."),
237                                   n_msgs, max_msgs));
238         }
239       else
240         {
241           too_many_errors = true;
242           if (m->severity == MSG_S_WARNING)
243             submit_note (xasprintf (_("Warnings (%d) exceed limit (%d).  Syntax processing will be halted."),
244                                     n_msgs, max_msgs));
245           else
246             submit_note (xasprintf (_("Errors (%d) exceed limit (%d).  Syntax processing will be halted."),
247                                     n_msgs, max_msgs));
248         }
249     }
250 }
251
252
253 /* Emits M as an error message.
254    Frees allocated data in M. */
255 void
256 msg_emit (struct msg *m)
257 {
258   if ( s_stream )
259     get_msg_location (s_stream, &m->where);
260   else
261     {
262       m->where.file_name = NULL;
263       m->where.line_number = 0;
264     }
265
266   if (!messages_disabled)
267      process_msg (m);
268
269   free (m->text);
270 }
271
272 /* Disables message output until the next call to msg_enable.  If
273    this function is called multiple times, msg_enable must be
274    called an equal number of times before messages are actually
275    re-enabled. */
276 void
277 msg_disable (void)
278 {
279   messages_disabled++;
280 }
281
282 /* Enables message output that was disabled by msg_disable. */
283 void
284 msg_enable (void)
285 {
286   assert (messages_disabled > 0);
287   messages_disabled--;
288 }
289 \f
290 /* Private functions. */
291
292 void
293 request_bug_report_and_abort (const char *msg)
294 {
295   fprintf (stderr, "******************************************************\n");
296   fprintf (stderr, "You have discovered a bug in PSPP.  Please report this\n");
297   fprintf (stderr, "to " PACKAGE_BUGREPORT ".  Please include this entire\n");
298   fprintf (stderr, "message, *plus* several lines of output just above it.\n");
299   fprintf (stderr, "For the best chance at having the bug fixed, also\n");
300   fprintf (stderr, "include the syntax file that triggered it and a sample\n");
301   fprintf (stderr, "of any data file used for input.\n");
302   fprintf (stderr, "proximate cause:     %s\n", msg);
303   fprintf (stderr, "version:             %s\n", stat_version);
304   fprintf (stderr, "host_system:         %s\n", host_system);
305   fprintf (stderr, "build_system:        %s\n", build_system);
306   fprintf (stderr, "locale_dir:          %s\n", locale_dir);
307   fprintf (stderr, "compiler version:    %s\n",
308 #ifdef __VERSION__
309            __VERSION__
310 #else
311            "Unknown"
312 #endif
313            );
314   fprintf (stderr, "******************************************************\n");
315
316   _exit (EXIT_FAILURE);
317 }
318