1 /* PSPP - computes sample statistics.
2 Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3 Written by Ben Pfaff <blp@gnu.org>.
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.
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.
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., 59 Temple Place - Suite 330, Boston, MA
37 int err_warning_count;
39 int err_already_flagged;
43 /* File locator stack. */
44 static const struct file_locator **file_loc;
45 static int nfile_loc, mfile_loc;
47 /* Fairly common public functions. */
49 /* Writes error message in CLASS, with title TITLE and text FORMAT,
50 formatted with printf, to the standard places. */
52 tmsg (int class, const char *title, const char *format, ...)
56 /* Format the message into BUF. */
60 va_start (args, format);
61 vsnprintf (buf, 1024, format, args);
65 /* Output the message. */
70 err_location (&e.where);
77 /* Writes error message in CLASS, with text FORMAT, formatted with
78 printf, to the standard places. */
80 msg (int class, const char *format, ...)
84 /* Format the message into BUF. */
88 va_start (args, format);
89 vsnprintf (buf, 1024, format, args);
93 /* Output the message. */
98 err_location (&e.where);
105 /* Terminate due to fatal error in input. */
112 fprintf (stderr, "%s: %s\n", pgmname,
113 _("Terminating NOW due to a fatal error!"));
118 /* Terminate unless we're interactive or will go interactive when the
119 file is over with. */
123 if (getl_reading_script)
125 if (getl_interactive)
132 /* File locator stack functions. */
134 /* Pushes F onto the stack of file locations. */
136 err_push_file_locator (const struct file_locator *f)
138 if (nfile_loc >= mfile_loc)
145 file_loc = xrealloc (file_loc, mfile_loc * sizeof *file_loc);
148 file_loc[nfile_loc++] = f;
151 /* Pops F off the stack of file locations.
152 Argument F is only used for verification that that is actually the
153 item on top of the stack. */
155 err_pop_file_locator (const struct file_locator *f)
157 assert (nfile_loc >= 0 && file_loc[nfile_loc - 1] == f);
161 /* Puts the current file and line number in F, or NULL and -1 if
164 err_location (struct file_locator *f)
167 *f = *file_loc[nfile_loc - 1];
169 getl_location (&f->filename, &f->line_number);
172 /* Obscure public functions. */
174 /* Writes a blank line to the error device(s).
175 FIXME: currently a no-op. */
181 /* Checks whether we've had so many errors that it's time to quit
182 processing this syntax file. If so, then take appropriate
185 err_check_count (void)
187 int error_class = getl_interactive ? MM : FE;
189 if (set_errorbreak && err_error_count)
190 msg (error_class, _("Terminating execution of syntax file due to error."));
191 else if (err_error_count > set_mxerrs)
192 msg (error_class, _("Errors (%d) exceeds limit (%d)."),
193 err_error_count, set_mxerrs);
194 else if (err_error_count + err_warning_count > set_mxwarns)
195 msg (error_class, _("Warnings (%d) exceed limit (%d)."),
196 err_error_count + err_warning_count, set_mxwarns);
203 /* Some machines are broken. Compensate. */
205 #define EXIT_SUCCESS 0
209 #define EXIT_FAILURE 1
212 static int terminating;
214 /* Halt-catch-fire. SUCCESS should be nonzero if exiting successfully
215 or zero if not. Despite the name, this is the usual way to finish,
216 successfully or not. */
218 err_hcf (int success)
222 getl_uninitialize ();
226 exit (success ? EXIT_SUCCESS : EXIT_FAILURE);
229 static void puts_stdout (const char *s);
230 static void dump_message (char *errbuf, unsigned indent,
231 void (*func) (const char *), unsigned width);
234 err_vmsg (const struct error *e)
239 ERR_IN_PROCEDURE = 01, /* 1=Display name of current procedure. */
240 ERR_WITH_FILE = 02, /* 1=Display filename and line number. */
243 /* Describes one class of error. */
246 int flags; /* Zero or more of MSG_*. */
247 int *count; /* Counting category. */
248 const char *banner; /* Banner. */
251 static const struct error_class error_classes[ERR_CLASS_COUNT] =
253 {0, NULL, N_("fatal")}, /* FE */
255 {3, &err_error_count, N_("error")}, /* SE */
256 {3, &err_warning_count, N_("warning")}, /* SW */
257 {3, NULL, N_("note")}, /* SM */
259 {0, NULL, N_("installation error")}, /* IE */
260 {2, NULL, N_("installation error")}, /* IS */
262 {2, &err_error_count, N_("error")}, /* DE */
263 {2, &err_warning_count, N_("warning")}, /* DW */
265 {0, &err_error_count, N_("error")}, /* ME */
266 {0, &err_warning_count, N_("warning")}, /* MW */
267 {0, NULL, N_("note")}, /* MM */
273 /* Check verbosity level. */
275 if (((class >> ERR_VERBOSITY_SHIFT) & ERR_VERBOSITY_MASK) > err_verbosity)
277 class &= ERR_CLASS_MASK;
279 assert (class >= 0 && class < ERR_CLASS_COUNT);
280 assert (e->text != NULL);
282 ds_init (NULL, &msg, 64);
283 if (e->where.filename && (error_classes[class].flags & ERR_WITH_FILE))
285 ds_printf (&msg, "%s:", e->where.filename);
286 if (e->where.line_number != -1)
287 ds_printf (&msg, "%d:", e->where.line_number);
288 ds_putchar (&msg, ' ');
291 ds_printf (&msg, "%s: ", gettext (error_classes[class].banner));
294 int *count = error_classes[class].count;
299 if (cur_proc && (error_classes[class].flags & ERR_IN_PROCEDURE))
300 ds_printf (&msg, "%s: ", cur_proc);
303 ds_concat (&msg, e->title);
305 ds_concat (&msg, e->text);
307 /* FIXME: Check set_messages and set_errors to determine where to
308 send errors and messages.
310 Please note that this is not trivial. We have to avoid an
311 infinite loop in reporting errors that originate in the output
313 dump_message (ds_value (&msg), 8, puts_stdout, set_viewwidth);
317 if (e->class == FE && !terminating)
321 /* Private functions. */
324 /* Write S followed by a newline to stderr. */
326 puts_stderr (const char *s)
329 fputc ('\n', stderr);
333 /* Write S followed by a newline to stdout. */
335 puts_stdout (const char *s)
340 /* Returns 1 if C is a `break character', that is, if it is a good
341 place to break a message into lines. */
343 char_is_break (int quote, int c)
345 return ((quote && c == DIR_SEPARATOR)
346 || (!quote && (isspace (c) || c == '-' || c == '/')));
349 /* Returns 1 if C is a break character where the break should be made
350 BEFORE the character. */
352 break_before (int quote, int c)
354 return !quote && isspace (c);
357 /* If C is a break character, returns 1 if the break should be made
358 AFTER the character. Does not return a meaningful result if C is
359 not a break character. */
361 break_after (int quote, int c)
363 return !break_before (quote, c);
366 /* If you want very long words that occur at a bad break point to be
367 broken into two lines even if they're shorter than a whole line by
368 themselves, define as 2/3, or 4/5, or whatever fraction of a whole
369 line you think is necessary in order to consider a word long enough
370 to break into pieces. Otherwise, define as 0. See code to grok
371 the details. Do NOT parenthesize the expression! */
372 #define BREAK_LONG_WORD 0
373 /* #define BREAK_LONG_WORD 2/3 */
374 /* #define BREAK_LONG_WORD 4/5 */
376 /* Divides MSG into lines of WIDTH width for the first line and WIDTH
377 - INDENT width for each succeeding line. Each line is dumped
378 through FUNC, which may do with the string what it will. */
380 dump_message (char *msg, unsigned indent, void (*func) (const char *),
385 /* 1 when at a position inside double quotes ("). */
388 /* Buffer for a single line. */
391 /* If the message is short, just print the full thing. */
392 if (strlen (msg) < width)
398 /* Make sure the indent isn't too big relative to the page width. */
399 if (indent > width / 3)
402 buf = local_alloc (width + 1);
404 /* Advance WIDTH characters into MSG.
405 If that's a valid breakpoint, keep it; otherwise, back up.
407 for (cp = msg; (unsigned) (cp - msg) < width - 1; cp++)
411 if (break_after (quote, (unsigned char) *cp))
413 for (cp--; !char_is_break (quote, (unsigned char) *cp) && cp > msg; cp--)
417 if (break_after (quote, (unsigned char) *cp))
421 if (cp <= msg + width * BREAK_LONG_WORD)
422 for (; cp < msg + width - 1; cp++)
433 /* Repeat above procedure for remaining lines. */
438 /* Advance past whitespace. */
439 while (isspace ((unsigned char) *cp))
444 /* Advance WIDTH - INDENT characters. */
445 for (cp2 = cp; (unsigned) (cp2 - cp) < width - indent && *cp2; cp2++)
449 /* Back up if this isn't a breakpoint. */
451 unsigned w = cp2 - cp;
453 for (cp2--; !char_is_break (quote, (unsigned char) *cp2) && cp2 > cp;
458 if (w == width - indent
459 && (unsigned) (cp2 - cp) <= (width - indent) * BREAK_LONG_WORD)
460 for (; (unsigned) (cp2 - cp) < width - indent && *cp2; cp2++)
465 /* Write out the line. */
466 memset (buf, ' ', indent);
467 memcpy (&buf[indent], cp, cp2 - cp);
468 buf[indent + cp2 - cp] = '\0';