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., 51 Franklin Street, Fifth Floor, Boston, MA
39 #define _(msgid) gettext (msgid)
40 #define N_(msgid) msgid
43 int err_warning_count;
45 int err_already_flagged;
49 /* File locator stack. */
50 static const struct file_locator **file_loc;
51 static int nfile_loc, mfile_loc;
53 /* Fairly common public functions. */
55 /* Writes error message in CLASS, with title TITLE and text FORMAT,
56 formatted with printf, to the standard places. */
58 tmsg (int class, const char *title, const char *format, ...)
64 err_location (&e.where);
67 va_start (args, format);
68 err_vmsg (&e, format, args);
72 /* Writes error message in CLASS, with text FORMAT, formatted with
73 printf, to the standard places. */
75 msg (int class, const char *format, ...)
81 err_location (&e.where);
84 va_start (args, format);
85 err_vmsg (&e, format, args);
89 /* Terminate due to fatal error in input. */
96 fprintf (stderr, "%s: %s\n", pgmname,
97 _("Terminating NOW due to a fatal error!"));
102 /* Terminate unless we're interactive or will go interactive when the
103 file is over with. */
107 if (getl_reading_script)
109 if (getl_interactive)
116 /* File locator stack functions. */
118 /* Pushes F onto the stack of file locations. */
120 err_push_file_locator (const struct file_locator *f)
122 if (nfile_loc >= mfile_loc)
129 file_loc = xrealloc (file_loc, mfile_loc * sizeof *file_loc);
132 file_loc[nfile_loc++] = f;
135 /* Pops F off the stack of file locations.
136 Argument F is only used for verification that that is actually the
137 item on top of the stack. */
139 err_pop_file_locator (const struct file_locator *f)
141 assert (nfile_loc >= 0 && file_loc[nfile_loc - 1] == f);
145 /* Puts the current file and line number in F, or NULL and -1 if
148 err_location (struct file_locator *f)
151 *f = *file_loc[nfile_loc - 1];
153 getl_location (&f->filename, &f->line_number);
156 /* Obscure public functions. */
158 /* Writes a blank line to the error device(s).
159 FIXME: currently a no-op. */
165 /* Checks whether we've had so many errors that it's time to quit
166 processing this syntax file. If so, then take appropriate
169 err_check_count (void)
171 int error_class = getl_interactive ? MM : FE;
173 if (get_errorbreak() && err_error_count)
174 msg (error_class, _("Terminating execution of syntax file due to error."));
175 else if (err_error_count > get_mxerrs() )
176 msg (error_class, _("Errors (%d) exceeds limit (%d)."),
177 err_error_count, get_mxerrs());
178 else if (err_error_count + err_warning_count > get_mxwarns() )
179 msg (error_class, _("Warnings (%d) exceed limit (%d)."),
180 err_error_count + err_warning_count, get_mxwarns() );
187 /* Some machines are broken. Compensate. */
189 #define EXIT_SUCCESS 0
193 #define EXIT_FAILURE 1
196 static int terminating;
198 /* Halt-catch-fire. SUCCESS should be nonzero if exiting successfully
199 or zero if not. Despite the name, this is the usual way to finish,
200 successfully or not. */
202 err_hcf (int success)
207 getl_uninitialize ();
211 nfile_loc = mfile_loc = 0;
216 exit (success ? EXIT_SUCCESS : EXIT_FAILURE);
219 static void puts_stdout (const char *s);
220 static void dump_message (char *errbuf, unsigned indent,
221 void (*func) (const char *), unsigned width);
224 err_vmsg (const struct error *e, const char *format, va_list args)
229 ERR_IN_PROCEDURE = 01, /* 1=Display name of current procedure. */
230 ERR_WITH_FILE = 02, /* 1=Display filename and line number. */
233 /* Describes one class of error. */
236 int flags; /* Zero or more of ERR_*. */
237 int *count; /* Counting category. */
238 const char *banner; /* Banner. */
241 static const struct error_class error_classes[ERR_CLASS_COUNT] =
243 {0, NULL, N_("fatal")}, /* FE */
245 {3, &err_error_count, N_("error")}, /* SE */
246 {3, &err_warning_count, N_("warning")}, /* SW */
247 {3, NULL, N_("note")}, /* SM */
249 {0, NULL, N_("installation error")}, /* IE */
250 {2, NULL, N_("installation error")}, /* IS */
252 {2, &err_error_count, N_("error")}, /* DE */
253 {2, &err_warning_count, N_("warning")}, /* DW */
255 {0, &err_error_count, N_("error")}, /* ME */
256 {0, &err_warning_count, N_("warning")}, /* MW */
257 {0, NULL, N_("note")}, /* MM */
263 /* Check verbosity level. */
265 if (((class >> ERR_VERBOSITY_SHIFT) & ERR_VERBOSITY_MASK) > err_verbosity)
267 class &= ERR_CLASS_MASK;
269 assert (class >= 0 && class < ERR_CLASS_COUNT);
270 assert (format != NULL);
273 if (e->where.filename && (error_classes[class].flags & ERR_WITH_FILE))
275 ds_printf (&msg, "%s:", e->where.filename);
276 if (e->where.line_number != -1)
277 ds_printf (&msg, "%d:", e->where.line_number);
281 ds_printf (&msg, "%s: ", gettext (error_classes[class].banner));
284 int *count = error_classes[class].count;
289 if (cur_proc && (error_classes[class].flags & ERR_IN_PROCEDURE))
290 ds_printf (&msg, "%s: ", cur_proc);
293 ds_puts (&msg, e->title);
295 ds_vprintf (&msg, format, args);
297 /* FIXME: Check set_messages and set_errors to determine where to
298 send errors and messages.
300 Please note that this is not trivial. We have to avoid an
301 infinite loop in reporting errors that originate in the output
303 dump_message (ds_c_str (&msg), 8, puts_stdout, get_viewwidth());
307 if (e->class == FE && !terminating)
311 /* Private functions. */
314 /* Write S followed by a newline to stderr. */
316 puts_stderr (const char *s)
319 fputc ('\n', stderr);
323 /* Write S followed by a newline to stdout. */
325 puts_stdout (const char *s)
330 /* Returns 1 if the line must be broken here */
332 compulsory_break(int c)
334 return ( c == '\n' );
337 /* Returns 1 if C is a `break character', that is, if it is a good
338 place to break a message into lines. */
340 char_is_break (int quote, int c)
342 return ((quote && c == DIR_SEPARATOR)
343 || (!quote && (isspace (c) || c == '-' || c == '/')));
346 /* Returns 1 if C is a break character where the break should be made
347 BEFORE the character. */
349 break_before (int quote, int c)
351 return !quote && isspace (c);
354 /* If C is a break character, returns 1 if the break should be made
355 AFTER the character. Does not return a meaningful result if C is
356 not a break character. */
358 break_after (int quote, int c)
360 return !break_before (quote, c);
363 /* If you want very long words that occur at a bad break point to be
364 broken into two lines even if they're shorter than a whole line by
365 themselves, define as 2/3, or 4/5, or whatever fraction of a whole
366 line you think is necessary in order to consider a word long enough
367 to break into pieces. Otherwise, define as 0. See code to grok
368 the details. Do NOT parenthesize the expression! */
369 #define BREAK_LONG_WORD 0
370 /* #define BREAK_LONG_WORD 2/3 */
371 /* #define BREAK_LONG_WORD 4/5 */
373 /* Divides MSG into lines of WIDTH width for the first line and WIDTH
374 - INDENT width for each succeeding line. Each line is dumped
375 through FUNC, which may do with the string what it will. */
377 dump_message (char *msg, unsigned indent, void (*func) (const char *),
382 /* 1 when at a position inside double quotes ("). */
385 /* Buffer for a single line. */
388 /* If the message is short, just print the full thing. */
389 if (strlen (msg) < width)
395 /* Make sure the indent isn't too big relative to the page width. */
396 if (indent > width / 3)
399 buf = local_alloc (width + 2);
401 /* Advance WIDTH characters into MSG.
402 If that's a valid breakpoint, keep it; otherwise, back up.
404 for (cp = msg; (unsigned) (cp - msg) < width - 1 &&
405 ! compulsory_break(*cp); cp++)
409 if (break_after (quote, (unsigned char) *cp))
411 for (cp--; !char_is_break (quote, (unsigned char) *cp) && cp > msg; cp--)
415 if (break_after (quote, (unsigned char) *cp))
419 if (cp <= msg + width * BREAK_LONG_WORD)
420 for (; cp < msg + width - 1; cp++)
432 /* Repeat above procedure for remaining lines. */
435 static int hard_break=0;
440 /* Advance past whitespace. */
442 while ( isspace ((unsigned char) *cp) )
451 /* Advance WIDTH - INDENT characters. */
452 for (cp2 = cp; (unsigned) (cp2 - cp) < width - indent &&
453 *cp2 && !compulsory_break(*cp2); cp2++)
457 if ( compulsory_break(*cp2) )
463 /* Back up if this isn't a breakpoint. */
465 unsigned w = cp2 - cp;
466 if (*cp2 && ! compulsory_break(*cp2) )
467 for (cp2--; !char_is_break (quote, (unsigned char) *cp2) &&
476 if (w == width - indent
477 && (unsigned) (cp2 - cp) <= (width - indent) * BREAK_LONG_WORD)
478 for (; (unsigned) (cp2 - cp) < width - indent && *cp2 ; cp2++)
484 /* Write out the line. */
486 memset (buf, ' ', indent);
487 memcpy (&buf[indent], cp, cp2 - cp);
489 buf[indent + idx + cp2 - cp] = '\0';
499 request_bug_report_and_abort(const char *msg )
502 "******************************************************************\n"
503 "You have discovered a bug in PSPP.\n\n"
504 " Please report this, by sending "
505 "an email to " PACKAGE_BUGREPORT ",\n"
506 "explaining what you were doing when this happened, and including\n"
507 "a sample of your input file which caused it.\n");
510 "Also, please copy the following lines into your bug report:\n\n"
516 "default_config_path: %s\n"
518 "groff_font_path: %s\n"
520 "compiler version: %s\n"
540 fprintf(stderr,"Diagnosis: %s\n",msg);
543 "******************************************************************\n");
549 err_assert_fail(const char *expr, const char *file, int line)
552 snprintf(msg,256,"Assertion failed: %s:%d; (%s)",file,line,expr);
553 request_bug_report_and_abort( msg );