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