409499301e4f68d343a29287672ef8d69e8d2496
[pspp-builds.git] / 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   m.where.first_column = 0;
63   m.where.last_column = 0;
64   va_end (args);
65
66   msg_emit (&m);
67 }
68
69 static struct source_stream *s_stream;
70
71 void
72 msg_init (struct source_stream *ss,  void (*handler) (const struct msg *) )
73 {
74   s_stream = ss;
75   msg_handler = handler;
76 }
77
78 void
79 msg_done (void)
80 {
81 }
82 \f
83 /* Working with messages. */
84
85 /* Duplicate a message */
86 struct msg *
87 msg_dup (const struct msg *m)
88 {
89   struct msg *new_msg;
90
91   new_msg = xmemdup (m, sizeof *m);
92   if (m->where.file_name != NULL)
93     new_msg->where.file_name = xstrdup (m->where.file_name);
94   new_msg->text = xstrdup (m->text);
95
96   return new_msg;
97 }
98
99 /* Frees a message created by msg_dup().
100
101    (Messages not created by msg_dup(), as well as their where.file_name
102    members, are typically not dynamically allocated, so this function should
103    not be used to destroy them.) */
104 void
105 msg_destroy (struct msg *m)
106 {
107   free (m->where.file_name);
108   free (m->text);
109   free (m);
110 }
111
112 char *
113 msg_to_string (const struct msg *m, const char *command_name)
114 {
115   const char *label;
116   struct string s;
117
118   ds_init_empty (&s);
119
120   if (m->category != MSG_C_GENERAL
121       && (m->where.file_name
122           || m->where.line_number > 0
123           || m->where.first_column > 0))
124     {
125       if (m->where.file_name)
126         ds_put_format (&s, "%s", m->where.file_name);
127       if (m->where.line_number > 0)
128         {
129           if (!ds_is_empty (&s))
130             ds_put_byte (&s, ':');
131           ds_put_format (&s, "%d", m->where.line_number);
132         }
133       if (m->where.first_column > 0)
134         {
135           ds_put_format (&s, ".%d", m->where.first_column);
136           if (m->where.last_column > m->where.first_column + 1)
137             ds_put_format (&s, "-%d", m->where.last_column - 1);
138         }
139       ds_put_cstr (&s, ": ");
140     }
141
142   switch (m->severity)
143     {
144     case MSG_S_ERROR:
145       label = _("error");
146       break;
147     case MSG_S_WARNING:
148       label = _("warning");
149       break;
150     case MSG_S_NOTE:
151     default:
152       label = _("note");
153       break;
154     }
155   ds_put_format (&s, "%s: ", label);
156
157   if (m->category == MSG_C_SYNTAX && command_name != NULL)
158     ds_put_format (&s, "%s: ", command_name);
159
160   ds_put_cstr (&s, m->text);
161
162   return ds_cstr (&s);
163 }
164 \f
165
166 /* Number of messages reported, by severity level. */
167 static int counts[MSG_N_SEVERITIES];
168
169 /* True after the maximum number of errors or warnings has been exceeded. */
170 static bool too_many_errors;
171
172 /* True after the maximum number of notes has been exceeded. */
173 static bool too_many_notes;
174
175 /* True iff warnings have been explicitly disabled (MXWARNS = 0) */
176 static bool warnings_off = false;
177
178 /* Checks whether we've had so many errors that it's time to quit
179    processing this syntax file. */
180 bool
181 msg_ui_too_many_errors (void)
182 {
183   return too_many_errors;
184 }
185
186 void
187 msg_ui_disable_warnings (bool x)
188 {
189   warnings_off = x;
190 }
191
192
193 void
194 msg_ui_reset_counts (void)
195 {
196   int i;
197
198   for (i = 0; i < MSG_N_SEVERITIES; i++)
199     counts[i] = 0;
200   too_many_errors = false;
201   too_many_notes = false;
202 }
203
204 bool
205 msg_ui_any_errors (void)
206 {
207   return counts[MSG_S_ERROR] > 0;
208 }
209
210 static void
211 submit_note (char *s)
212 {
213   struct msg m;
214
215   m.category = MSG_C_GENERAL;
216   m.severity = MSG_S_NOTE;
217   m.where.file_name = NULL;
218   m.where.line_number = 0;
219   m.where.first_column = 0;
220   m.where.last_column = 0;
221   m.text = s;
222   msg_handler (&m);
223   free (s);
224 }
225
226
227
228 static void
229 process_msg (const struct msg *m)
230 {
231   int n_msgs, max_msgs;
232
233
234   if (too_many_errors
235       || (too_many_notes && m->severity == MSG_S_NOTE)
236       || (warnings_off && m->severity == MSG_S_WARNING) )
237     return;
238
239   msg_handler (m);
240
241   counts[m->severity]++;
242   max_msgs = settings_get_max_messages (m->severity);
243   n_msgs = counts[m->severity];
244   if (m->severity == MSG_S_WARNING)
245     n_msgs += counts[MSG_S_ERROR];
246   if (n_msgs > max_msgs)
247     {
248       if (m->severity == MSG_S_NOTE)
249         {
250           too_many_notes = true;
251           submit_note (xasprintf (_("Notes (%d) exceed limit (%d).  "
252                                     "Suppressing further notes."),
253                                   n_msgs, max_msgs));
254         }
255       else
256         {
257           too_many_errors = true;
258           if (m->severity == MSG_S_WARNING)
259             submit_note (xasprintf (_("Warnings (%d) exceed limit (%d).  Syntax processing will be halted."),
260                                     n_msgs, max_msgs));
261           else
262             submit_note (xasprintf (_("Errors (%d) exceed limit (%d).  Syntax processing will be halted."),
263                                     n_msgs, max_msgs));
264         }
265     }
266 }
267
268
269 /* Emits M as an error message.
270    Frees allocated data in M. */
271 void
272 msg_emit (struct msg *m)
273 {
274   if ( s_stream && m->where.file_name == NULL )
275     {
276       struct msg_locator loc;
277
278       get_msg_location (s_stream, &loc);
279       m->where.file_name = loc.file_name;
280       m->where.line_number = loc.line_number;
281     }
282   else
283     {
284       m->where.file_name = NULL;
285       m->where.line_number = 0;
286     }
287
288   if (!messages_disabled)
289      process_msg (m);
290
291   free (m->text);
292 }
293
294 /* Disables message output until the next call to msg_enable.  If
295    this function is called multiple times, msg_enable must be
296    called an equal number of times before messages are actually
297    re-enabled. */
298 void
299 msg_disable (void)
300 {
301   messages_disabled++;
302 }
303
304 /* Enables message output that was disabled by msg_disable. */
305 void
306 msg_enable (void)
307 {
308   assert (messages_disabled > 0);
309   messages_disabled--;
310 }
311 \f
312 /* Private functions. */
313
314 void
315 request_bug_report (const char *msg)
316 {
317   fprintf (stderr, "******************************************************\n");
318   fprintf (stderr, "You have discovered a bug in PSPP.  Please report this\n");
319   fprintf (stderr, "to " PACKAGE_BUGREPORT ".  Please include this entire\n");
320   fprintf (stderr, "message, *plus* several lines of output just above it.\n");
321   fprintf (stderr, "For the best chance at having the bug fixed, also\n");
322   fprintf (stderr, "include the syntax file that triggered it and a sample\n");
323   fprintf (stderr, "of any data file used for input.\n");
324   fprintf (stderr, "proximate cause:     %s\n", msg);
325   fprintf (stderr, "version:             %s\n", stat_version);
326   fprintf (stderr, "host_system:         %s\n", host_system);
327   fprintf (stderr, "build_system:        %s\n", build_system);
328   fprintf (stderr, "locale_dir:          %s\n", locale_dir);
329   fprintf (stderr, "compiler version:    %s\n",
330 #ifdef __VERSION__
331            __VERSION__
332 #else
333            "Unknown"
334 #endif
335            );
336   fprintf (stderr, "******************************************************\n");
337 }
338