Committed patch #5524, which allows the message destination to be
[pspp-builds.git] / src / ui / terminal / msg-ui.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000, 2006 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
22 #include "msg-ui.h"
23
24 #include "exit.h"
25 #include "linebreak.h"
26
27 #include <language/line-buffer.h>
28 #include <data/settings.h>
29 #include <libpspp/message.h>
30 #include <errno.h>
31
32 #include "gettext.h"
33 #define _(msgid) gettext (msgid)
34 #define N_(msgid) msgid
35
36 /* Number of errors, warnings reported. */
37 static int error_count;
38 static int warning_count;
39 static const char *error_file;
40
41 static void handle_msg (const struct msg *);
42
43 static FILE *msg_file;
44
45 void 
46 msg_ui_set_error_file (const char *filename)
47 {
48   error_file = filename;
49 }
50
51 void
52 msg_ui_init (void) 
53 {
54   msg_file = stdout;
55
56   if ( error_file ) 
57     {
58       msg_file = fopen (error_file, "a");
59       if ( NULL == msg_file ) 
60         {
61           int err = errno;
62           printf ( _("Cannot open %s (%s). "
63                      "Writing errors to stdout instead.\n"), 
64                    error_file, strerror(err) );
65           msg_file = stdout;
66         }
67     }
68   msg_init (handle_msg, get_msg_location);
69 }
70
71 void
72 msg_ui_done (void) 
73 {
74   msg_done ();
75   fclose (msg_file);
76 }
77
78
79 /* Checks whether we've had so many errors that it's time to quit
80    processing this syntax file. */
81 void
82 check_msg_count (void)
83 {
84   if (!getl_is_interactive ()) 
85     {
86       if (get_errorbreak () && error_count)
87         msg (MN, _("Terminating execution of syntax file due to error."));
88       else if (error_count > get_mxerrs() )
89         msg (MN, _("Errors (%d) exceeds limit (%d)."),
90              error_count, get_mxerrs());
91       else if (error_count + warning_count > get_mxwarns() )
92         msg (MN, _("Warnings (%d) exceed limit (%d)."),
93              error_count + warning_count, get_mxwarns() );
94       else
95         return;
96
97       getl_abort_noninteractive (); 
98     }
99 }
100
101 void
102 reset_msg_count (void) 
103 {
104   error_count = warning_count = 0;
105 }
106
107 bool
108 any_errors (void) 
109 {
110   return error_count > 0;
111 }
112 \f
113 static void dump_message (char *msg, unsigned width, unsigned indent, FILE *);
114 static void dump_line (int line_indent, const char *line, size_t length,
115                        FILE *);
116
117 static void
118 handle_msg (const struct msg *m)
119 {
120   struct category 
121     {
122       bool show_command_name;   /* Show command name with error? */
123       bool show_file_location;  /* Show syntax file location? */
124     };
125
126   static const struct category categories[] = 
127     {
128       {false, false},           /* MSG_GENERAL. */
129       {true, true},             /* MSG_SYNTAX. */
130       {false, true},            /* MSG_DATA. */
131     };
132
133   struct severity 
134     {
135       const char *name;         /* How to identify this severity. */
136       int *count;               /* Number of msgs with this severity so far. */
137     };
138   
139   static struct severity severities[] = 
140     {
141       {N_("error"), &error_count},          /* MSG_ERROR. */
142       {N_("warning"), &warning_count},      /* MSG_WARNING. */
143       {NULL, NULL},                         /* MSG_NOTE. */
144     };
145
146   const struct category *category = &categories[m->category];
147   const struct severity *severity = &severities[m->severity];
148   struct string string = DS_EMPTY_INITIALIZER;
149
150   if (category->show_file_location && m->where.file_name)
151     {
152       ds_put_format (&string, "%s:", m->where.file_name);
153       if (m->where.line_number != -1)
154         ds_put_format (&string, "%d:", m->where.line_number);
155       ds_put_char (&string, ' ');
156     }
157
158   if (severity->name != NULL)
159     ds_put_format (&string, "%s: ", gettext (severity->name));
160   
161   if (severity->count != NULL)
162     ++*severity->count;
163   
164   if (category->show_command_name && msg_get_command_name () != NULL)
165     ds_put_format (&string, "%s: ", msg_get_command_name ());
166
167   ds_put_cstr (&string, m->text);
168
169   if (msg_file != stdout || get_error_routing_to_terminal ())
170     dump_message (ds_cstr (&string), get_viewwidth (), 8, msg_file);
171
172   ds_destroy (&string);
173 }
174
175 /* Divides MSG into lines of WIDTH width for the first line and
176    WIDTH - INDENT width for each succeeding line, and writes the
177    lines to STREAM. */
178 static void
179 dump_message (char *msg, unsigned width, unsigned indent, FILE *stream)
180 {
181   size_t length = strlen (msg);
182   char *string, *breaks;
183   int line_indent;
184   size_t line_start, i;
185
186   /* Allocate temporary buffers.
187      If we can't get memory for them, then just dump the whole
188      message. */
189   string = strdup (msg);
190   breaks = malloc (length);
191   if (string == NULL || breaks == NULL)
192     {
193       free (string);
194       free (breaks);
195       fputs (msg, stream);
196       putc ('\n', stream);
197       return;
198     }
199
200   /* Break into lines. */
201   if (indent > width / 3)
202     indent = width / 3;
203   mbs_width_linebreaks (string, length,
204                         width - indent, -indent, 0,
205                         NULL, locale_charset (), breaks);
206
207   /* Write out lines. */
208   line_start = 0;
209   line_indent = 0;
210   for (i = 0; i < length; i++)
211     switch (breaks[i]) 
212       {
213       case UC_BREAK_POSSIBLE:
214         /* Break before this character,
215            and include this character in the next line. */
216         dump_line (line_indent, &string[line_start], i - line_start, stream);
217         line_start = i;
218         line_indent = indent;
219         break;
220       case UC_BREAK_MANDATORY:
221         /* Break before this character,
222            but don't include this character in the next line
223            (because it'string a new-line). */
224         dump_line (line_indent, &string[line_start], i - line_start, stream);
225         line_start = i + 1;
226         line_indent = indent;
227         break;
228       default:
229         break;
230       }
231   if (line_start < length)
232     dump_line (line_indent, &string[line_start], length - line_start, stream);
233
234   free (string);
235   free (breaks);
236 }
237
238 /* Write LINE_INDENT spaces, the LENGTH characters in LINE, then
239    a new-line to STREAM. */
240 static void
241 dump_line (int line_indent, const char *line, size_t length, FILE *stream)
242 {
243   int i;
244   for (i = 0; i < line_indent; i++)
245     putc (' ', stream);
246   fwrite (line, 1, length, stream);
247   putc ('\n', stream);
248 }
249