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