tech-userlevel archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Adding %m conversion to the printf(3) family
Hi List
Ours gcc recently grew changes to warn that printf %m is only allowed in
syslog(3) like functions.
This is problematic because if we compile with -Wmissing-format-attribute gcc
will warn us that our syslog wrapper function should use sysloglike, but when
we make the change it when warns us that because of the functions used
internally it should be printflike. ie, it cannot make up it's own mind which
to use.
Instead of trying to make gcc work better, I propose that our libc printf(3)
family of functions instead support %m. There are sound technical reasons why
this is a good thing:
1) POSIX syslog(3) requires %m on top of the printf(3) standard
As such printf(3) cannot grow a conflicting %m conversion
printf(3) standard allows for undefined conversions so we're not breaking any
standard by doing this
2) The alternative of using %s and strerror(3) is problematic in portable
applications for these reasons
* strerror(3) isn't guaranteed to be threadsafe (it is on NetBSD)
* strerror_r(3) isn't portable thanks to glibc and others having a different
signature
* strerror_r(3) has a horrible interface anyway as you don't know the size
of the buffer you need and need to loop through until you get no errors.
* strerror_l(3) isn't well supported (recently added to NetBSD but seems to
be lacking a man page)
3) Code size and binary size are reduced.
Not only is the libc size reduced, but applications like dhcpcd which have a
syslog(3) wrapper can as well. dhcpcd's binary size reduction is similar to
libc's at 1.5k on amd64. When dhcpcd first changing it's error reporting to use
%m instead of strerror, that saved another 2k as well again promoting smaller
code.
Attached is a patch to demonstrate this, including a man page update to
clearly state that %m is not recommended in portable applications.
I expect there to be some heated debate over this. To those who disagree with
this proposal, I expect you to supply a working solution to the problem noted
at the top of this email. A valid solution could be reverting the gcc change
and keeping the status quo.
Roy
Index: gen/syslog.c
===================================================================
RCS file: /cvsroot/src/lib/libc/gen/syslog.c,v
retrieving revision 1.54
diff -u -r1.54 syslog.c
--- gen/syslog.c 18 Sep 2014 13:58:20 -0000 1.54
+++ gen/syslog.c 23 Oct 2015 20:56:36 -0000
@@ -213,16 +213,16 @@
static const char BRCOSP[] = "]: ";
static const char CRLF[] = "\r\n";
size_t cnt, prlen, tries;
- char ch, *p, *t;
+ char *p;
struct timeval tv;
struct tm tmnow;
time_t now;
- int fd, saved_errno;
+ int fd;
#define TBUF_LEN 2048
#define FMT_LEN 1024
#define MAXTRIES 10
- char tbuf[TBUF_LEN], fmt_cpy[FMT_LEN], fmt_cat[FMT_LEN] = "";
- size_t tbuf_left, fmt_left, msgsdlen;
+ char tbuf[TBUF_LEN], fmt_cat[FMT_LEN] = "";
+ size_t tbuf_left, msgsdlen;
char *fmt = fmt_cat;
int signal_safe = pri & LOG_SIGNAL_SAFE;
struct iovec iov[7]; /* prog + [ + pid + ]: + fmt + crlf */
@@ -242,8 +242,6 @@
if (!(LOG_MASK(LOG_PRI(pri)) & data->log_mask))
return;
- saved_errno = errno;
-
/* Set default facility if none specified. */
if ((pri & LOG_FACMASK) == 0)
pri |= data->log_fac;
@@ -372,42 +370,10 @@
strlcat(fmt_cat, msgfmt, FMT_LEN);
}
- /*
- * We wouldn't need this mess if printf handled %m, or if
- * strerror() had been invented before syslog().
- */
- for (t = fmt_cpy, fmt_left = FMT_LEN; (ch = *fmt) != '\0'; ++fmt) {
- if (ch == '%' && fmt[1] == 'm') {
- char ebuf[128];
- ++fmt;
- if (signal_safe ||
- strerror_r(saved_errno, ebuf, sizeof(ebuf)))
- prlen = snprintf_ss(t, fmt_left, "Error %d",
- saved_errno);
- else
- prlen = snprintf_ss(t, fmt_left, "%s", ebuf);
- if (prlen >= fmt_left)
- prlen = fmt_left - 1;
- t += prlen;
- fmt_left -= prlen;
- } else if (ch == '%' && fmt[1] == '%' && fmt_left > 2) {
- *t++ = '%';
- *t++ = '%';
- fmt++;
- fmt_left -= 2;
- } else {
- if (fmt_left > 1) {
- *t++ = ch;
- fmt_left--;
- }
- }
- }
- *t = '\0';
-
if (signal_safe)
- prlen = vsnprintf_ss(p, tbuf_left, fmt_cpy, ap);
+ prlen = vsnprintf_ss(p, tbuf_left, fmt, ap);
else
- prlen = vsnprintf(p, tbuf_left, fmt_cpy, ap);
+ prlen = vsnprintf(p, tbuf_left, fmt, ap);
if (data->log_stat & (LOG_PERROR|LOG_CONS)) {
iov[iovcnt].iov_base = p + msgsdlen;
Index: stdio/printf.3
===================================================================
RCS file: /cvsroot/src/lib/libc/stdio/printf.3,v
retrieving revision 1.64
diff -u -r1.64 printf.3
--- stdio/printf.3 29 Sep 2014 14:58:33 -0000 1.64
+++ stdio/printf.3 23 Oct 2015 20:56:41 -0000
@@ -33,7 +33,7 @@
.\"
.\" @(#)printf.3 8.1 (Berkeley) 6/4/93
.\"
-.Dd September 29, 2014
+.Dd October 23, 2015
.Dt PRINTF 3
.Os
.Sh NAME
@@ -719,6 +719,13 @@
.Vt "int *"
(or variant) pointer argument.
No argument is converted.
+.It Cm m
+Print the current error message.
+(As denoted by the global variable
+.Va errno ;
+see
+.Xr strerror 3 . )
+No argument is converted.
.It Cm %
A
.Ql %
@@ -836,6 +843,17 @@
.Fn vsnprintf
functions conform to
.St -isoC-99 .
+.Pp
+The
+.Cm \&%m
+conversion format is not standard,
+it is only included here because
+.Xr syslog 3
+requires it.
+Portable programs should use
+.Cm \&%s
+and an argument of
+.Fn strerror errno .
.Sh HISTORY
The functions
.Fn snprintf
Index: stdio/vfwprintf.c
===================================================================
RCS file: /cvsroot/src/lib/libc/stdio/vfwprintf.c,v
retrieving revision 1.34
diff -u -r1.34 vfwprintf.c
--- stdio/vfwprintf.c 20 Jan 2014 14:11:03 -0000 1.34
+++ stdio/vfwprintf.c 23 Oct 2015 20:56:44 -0000
@@ -690,6 +690,7 @@
int nextarg; /* 1-based argument index */
va_list orgap; /* original argument pointer */
CHAR_T *convbuf; /* multibyte to wide conversion result */
+ int saved_errno; /* save errorno for %m */
/*
* Choose PADSIZE to trade efficiency vs. size. If larger printf
@@ -822,6 +823,8 @@
_DIAGASSERT(fp != NULL);
_DIAGASSERT(fmt0 != NULL);
+ saved_errno = errno;
+
_SET_ORIENTATION(fp, -1);
thousands_sep = '\0';
@@ -1282,6 +1285,9 @@
}
break;
#endif /* !NO_FLOATING_POINT */
+ case 'm':
+ result = (CHAR_T *)strerror(saved_errno);
+ goto print_result;
case 'n':
/*
* Assignment-like behavior is specified if the
@@ -1353,6 +1359,7 @@
}
}
+print_result:
if (prec >= 0) {
/*
* can't use STRLEN; can only look for the
Home |
Main Index |
Thread Index |
Old Index