Paul Eggert <eggert <at> CS.UCLA.EDU> writes: > > Man, I wish there were an error() variant that took a va_list. > > That'd be a nice thing to add -- could you propose it? ... > Initially I suppose this stuff should be written so that the new > interface is exported only in gnulib mode. >
I checked with glibc, and our error.c is already pretty far removed from theirs, so it wasn't worth my time trying to sync them. Here's my proposal for a va_list variant of error. OK to check in? Note that it does affect the semantics of anyone using the error_print_progname callback - previously, error would not supply a space and now it does. But that is the only way I could make error_at_line consistent with GNU Coding Standards. > While you're > at it, perhaps you could make the new interface reentrant, so that it > doesn't use the global variables. (As long as we're wishing....) > For now, I'd rather get verror approved. But reentrency should just be a matter of defining something like: verror_r (int status, int errnum, void (*print_progname) (void), unsigned int *count, char const *format, va_list args); then doing: verror(...) { verror_r (status, errnum, error_print_prognam, &error_message_count, format, args); } I also wonder whether we should add: [v]error_at_col (int status, int errnum, char const *file, size_t line, size_t col, char const *format, ...); where if col is nonzero, we output "program:file:line:col: message". Note that I would use size_t (or should it be off_t?), and not the int used by error_at_line, since with large file offsets, int is insufficient for line and column numbers on worst-case input files. 2006-08-01 Eric Blake <[EMAIL PROTECTED]> * error.c (error_print_progname): In order to comply with GNU Coding Standards as used by both error and error_at_line, this must end with a colon, not space. (error): Add space after calling error_print_progname. (error_at_line): Call exit even when no message is printed. Ensure spacing is consistent whether file/line pair is printed or not. Avoid calling strcmp on NULL parameter. (verror, verror_at_line): New functions. * error.h (verror, verror_at_line): Add prototypes. Index: lib/error.c =================================================================== RCS file: /sources/gnulib/gnulib/lib/error.c,v retrieving revision 1.43 diff -u -r1.43 error.c --- lib/error.c 14 May 2005 06:03:58 -0000 1.43 +++ lib/error.c 1 Aug 2006 18:07:02 -0000 @@ -1,5 +1,5 @@ /* Error handler for noninteractive utilities - Copyright (C) 1990-1998, 2000-2003, 2004 Free Software Foundation, Inc. + Copyright (C) 1990-1998, 2000-2003, 2004, 2006 Free Software Foundation, Inc. This file is part of the GNU C Library. This program is free software; you can redistribute it and/or modify @@ -46,9 +46,10 @@ # define _(String) String #endif -/* If NULL, error will flush stdout, then print on stderr the program - name, a colon and a space. Otherwise, error will call this - function without parameters instead. */ +/* If NULL, print the contents of program_name and a colon as the program + name portion of error and error_at_line. Otherwise, error will flush + stdout then call this function without parameters. If this + callback prints to stderr, it should end output with a colon. */ void (*error_print_progname) (void); /* This variable is incremented each time `error' is called. */ @@ -68,7 +69,7 @@ extern void __error_at_line (int status, int errnum, const char *file_name, unsigned int line_number, const char *message, ...) - __attribute__ ((__format__ (__printf__, 5, 6)));; + __attribute__ ((__format__ (__printf__, 5, 6))); # define error __error # define error_at_line __error_at_line @@ -181,15 +182,26 @@ } -/* Print the program name and error message MESSAGE, which is a printf-style - format string with optional args. - If ERRNUM is nonzero, print its corresponding system error message. - Exit with status STATUS if it is nonzero. */ +/* Flush stdout, then print the program name as controlled by + error_print_progname, then a space, and the message with + `fprintf (stderr, FORMAT, ...)'. + GNU Coding Standards suggest that FORMAT start with lower case, and end + without a period. FORMAT should not end with a newline. + If ERRNUM is nonzero, follow the message with ": " and strerror (ERRNUM). + If STATUS is nonzero, terminate the program with `exit (STATUS)'. */ void error (int status, int errnum, const char *message, ...) { va_list args; + va_start (args, message); + verror (status, errnum, message, args); +} + +/* Like error, but take a va_list ARGS instead. */ +void +verror (int status, int errnum, const char *message, va_list args) +{ #if defined _LIBC && defined __libc_ptf_call /* We do not want this call to be cut short by a thread cancellation. Therefore disable cancellation for now. */ @@ -203,7 +215,15 @@ _IO_flockfile (stderr); #endif if (error_print_progname) - (*error_print_progname) (); + { + (*error_print_progname) (); +#if _LIBC + if (_IO_fwide (stderr, 0) > 0) + putwc (L' ', stderr); + else +#endif + putc (' ', stderr); + } else { #if _LIBC @@ -214,7 +234,6 @@ fprintf (stderr, "%s: ", program_name); } - va_start (args, message); error_tail (status, errnum, message, args); #ifdef _LIBC @@ -226,25 +245,43 @@ } /* Sometimes we want to have at most one error per line. This - variable controls whether this mode is selected or not. */ + variable controls whether this mode is selected or not. If non-zero, + error_at_line will be silent for a repeat location, but will still + exit if status is non-zero. */ int error_one_per_line; +/* Like error, with the addition that if FNAME is not NULL, the file and + line number of the error are printed after the program name. */ void error_at_line (int status, int errnum, const char *file_name, unsigned int line_number, const char *message, ...) { va_list args; - if (error_one_per_line) + va_start (args, message); + verror_at_line (status, errnum, file_name, line_number, message, args); +} + +/* Like error_at_line, but take a va_list ARGS instead. */ +void +verror_at_line (int status, int errnum, const char *file_name, + unsigned int line_number, const char *message, va_list args) +{ + if (error_one_per_line && file_name) { static const char *old_file_name; static unsigned int old_line_number; if (old_line_number == line_number + && old_file_name && (file_name == old_file_name || strcmp (old_file_name, file_name) == 0)) - /* Simply return and print nothing. */ - return; + { + /* A message has already been printed for this line. */ + if (status) + exit (status); + return; + } old_file_name = file_name; old_line_number = line_number; @@ -268,7 +305,7 @@ { #if _LIBC if (_IO_fwide (stderr, 0) > 0) - __fwprintf (stderr, L"%s: ", program_name); + __fwprintf (stderr, L"%s:", program_name); else #endif fprintf (stderr, "%s:", program_name); @@ -283,8 +320,16 @@ #endif fprintf (stderr, "%s:%d: ", file_name, line_number); } + else + { +#if _LIBC + if (_IO_fwide (stderr, 0) > 0) + putwc (L' ', stderr); + else +#endif + putc (' ', stderr); + } - va_start (args, message); error_tail (status, errnum, message, args); #ifdef _LIBC Index: lib/error.h =================================================================== RCS file: /sources/gnulib/gnulib/lib/error.h,v retrieving revision 1.20 diff -u -r1.20 error.h --- lib/error.h 14 May 2005 06:03:58 -0000 1.20 +++ lib/error.h 1 Aug 2006 18:07:02 -0000 @@ -1,5 +1,5 @@ /* Declaration for error-reporting function - Copyright (C) 1995, 1996, 1997, 2003 Free Software Foundation, Inc. + Copyright (C) 1995, 1996, 1997, 2003, 2006 Free Software Foundation, Inc. This file is part of the GNU C Library. This program is free software; you can redistribute it and/or modify @@ -32,31 +32,56 @@ # endif #endif +#include <stdarg.h> + #ifdef __cplusplus extern "C" { #endif -/* Print a message with `fprintf (stderr, FORMAT, ...)'; - if ERRNUM is nonzero, follow it with ": " and strerror (ERRNUM). +/* Flush stdout, then print the program name as controlled by + error_print_progname, then a space, and the message with + `fprintf (stderr, FORMAT, ...)'. + GNU Coding Standards suggest that FORMAT start with lower case, and end + without a period. FORMAT should not end with a newline. + If ERRNUM is nonzero, follow the message with ": " and strerror (ERRNUM). If STATUS is nonzero, terminate the program with `exit (STATUS)'. */ extern void error (int __status, int __errnum, const char *__format, ...) __attribute__ ((__format__ (__printf__, 3, 4))); +/* Like error, but take a va_list ARGS instead. */ + +extern void verror (int __status, int __errnum, const char *__format, + va_list __args) + __attribute__ ((__format__ (__printf__, 3, 0))); + +/* Like error, with the addition that if FNAME is not NULL, the file and + line number of the error are printed after the program name. */ + extern void error_at_line (int __status, int __errnum, const char *__fname, unsigned int __lineno, const char *__format, ...) __attribute__ ((__format__ (__printf__, 5, 6))); -/* If NULL, error will flush stdout, then print on stderr the program - name, a colon and a space. Otherwise, error will call this - function without parameters instead. */ +/* Like error_at_line, but take a va_list ARGS instead. */ + +extern void verror_at_line (int __status, int __errnum, const char *__fname, + unsigned int __lineno, const char *__format, + va_list __args) + __attribute__ ((__format__ (__printf__, 5, 0))); + +/* If NULL, print the contents of program_name and a colon as the program + name portion of error and error_at_line. Otherwise, error will flush + stdout then call this function without parameters. If this + callback prints to stderr, it should end output with a colon. */ extern void (*error_print_progname) (void); /* This variable is incremented each time `error' is called. */ extern unsigned int error_message_count; /* Sometimes we want to have at most one error per line. This - variable controls whether this mode is selected or not. */ + variable controls whether this mode is selected or not. If non-zero, + error_at_line will be silent for a repeat location, but will still + exit if status is non-zero. */ extern int error_one_per_line; #ifdef __cplusplus