Fix for Solaris patch and new HP-UX patch
I found an oversight with the patch that switches to PRIdMAX for printing intmax_t. The printf shell builtin actually programmatically constructs format strings by inserting the 'j' length modifier. I have create a quick patch that makes it extract the length modifier from PRIdMAX instead. I'll also send a new version of the HP-UX port patch rebased against this and the latest dash master.
Regards,
Brian Koropoff
PATCH 1/2 Fix additional use of 'j' printf length modifier.
The printf builtin modifies the user's format strings by prefixing integer conversion specifications with the 'j' (intmax_t) length modifier. Since this is not portable, instead prefix them with the length modifier extracted from the PRIdMAX constant.
--- src/bltin/printf.c | 15 ++++++++------- 1 files changed, 8 insertions(+), 7 deletions(-)
src/bltin/printf.c +8 -7
@@ -317,15 +317,16 @@ static char * mklong(const char *str, const char *ch) { char *copy; -size_t len; +size_t len; +size_t pridmax_len =strlen (PRIdMAX); - len = ch - str +3 ; + len = ch - str + pridmax_len;STARTSTACKSTR (copy); - copy =makestrspace (len, copy); -memcpy (copy, str, len -3 ); - copy[len -3 ] ='j' ; - copy[len -2 ] = *ch; - copy[len -1 ] ='\0' ; + copy =makestrspace (len +1 , copy); +memcpy (copy, str, len - pridmax_len); +memcpy (copy + len - pridmax_len, PRIdMAX, pridmax_len -1 ); + copy[len -1 ] = *ch; + copy[len] ='\0' ;return (copy); }
Signature · 1 line
1.7.1
Re: [PATCH 1/2] Fix additional use of 'j' printf length modifier.
Hi,
Quoted 5 lines · Brian Koropoff
The printf builtin modifies the user's format strings by prefixing integer conversion specifications with the 'j' (
intmax_t) length modifier. Since this is not portable, instead prefix them with the length modifier extracted from the PRIdMAX constant.
This assumes PRIdMAX, PRIxMAX, etc all consist of the same prefix before the standard characters. Since the most common definitions are j<usual char>, l<usual char>, q<usual char>, I64<usual char>, and ll<usual char>, that's probably a safe assumption. I wonder why C99 and its predecessors did not use
printf("%"PRIMAX"x\n", val);Oh well. Maybe it would warrant a comment, though?
/* * Replace a string like * * %92.3u * ^ ^--- ch * '-------- str * * with "%92.3" PRIuMAX "". * * Although C99 does not guarantee it, we assume PRIiMAX, * PRIoMAX, PRIuMAX, PRIxMAX, and PRIXMAX are all the same * as PRIdMAX with the final 'd' replaced by the corresponding * character. */
src/bltin/printf.c static char * · line 321
Quoted 9 lines
src/bltin/printf.c +2
-1@@ -317,15 +317,16 @@ static char * mklong(const char *str, const char *ch) { char *copy; -size_t len; +size_t len; +size_t pridmax_len =strlen (PRIdMAX);
I think just using strlen(PRIdMAX) as-is would make it clearer that we are expecting the compiler to inline the "strlen" and provides a reminder of the value, too (i.e., is it 2 or 3 for "jd"?).
src/bltin/printf.c static char * · line 322
Quoted 3 lines
src/bltin/printf.c +1
-1- len = ch - str +3 ; + len = ch - str + pridmax_len;
This changes the meaning of "len" to no longer be the size of the buffer. I suppose that doesn't matter, but...
src/bltin/printf.c static char * · line 328
Quoted 11 lines
src/bltin/printf.c +5
-5STARTSTACKSTR (copy); - copy =makestrspace (len, copy); -memcpy (copy, str, len -3 ); - copy[len -3 ] ='j' ; - copy[len -2 ] = *ch; - copy[len -1 ] ='\0' ; + copy =makestrspace (len +1 , copy); +memcpy (copy, str, len - pridmax_len); +memcpy (copy + len - pridmax_len, PRIdMAX, pridmax_len -1 ); + copy[len -1 ] = *ch; + copy[len] ='\0' ;
... the arithmetic is getting complicated. I think mempcpy could make the intention clearer, like so.
char *p; [...] len = ch - str + strlen(PRIdMAX) + 1; p = copy = makestrspace(len, copy); p = mempcpy(p, str, ch - str); p = mempcpy(p, PRIdMAX, strlen(PRIdMAX) - 1); *p++ = *ch; *p++ = '\0';
Like this, maybe (on top, untested)?
--- src/bltin/printf.c | 23 ++++++++++++++++------- 1 files changed, 16 insertions(+), 7 deletions(-)
src/bltin/printf.c +16 -7
@@ -317,16 +317,25 @@ static char * mklong(const char *str, const char *ch) { char *copy; +char *p;size_t len; -size_t pridmax_len =strlen (PRIdMAX); - len = ch - str + pridmax_len; +/* +* Replace a string like "%92.3u" with "%92.3"PRIuMAX. +* +* Although C99 does not guarantee it, we assume PRIiMAX, +* PRIoMAX, PRIuMAX, PRIxMAX, and PRIXMAX are all the same +* as PRIdMAX with the final 'd' replaced by the corresponding +* character. +*/ + + len = ch - str +strlen (PRIdMAX) +1 ;STARTSTACKSTR (copy); - copy =makestrspace (len +1 , copy); -memcpy (copy, str, len - pridmax_len); -memcpy (copy + len - pridmax_len, PRIdMAX, pridmax_len -1 ); - copy[len -1 ] = *ch; - copy[len] ='\0' ; + p = copy =makestrspace (len, copy); + p =mempcpy (p, str, ch - str); + p =mempcpy (p, PRIdMAX,strlen (PRIdMAX) -1 ); + *p++ = *ch; + *p++ ='\0' ;return (copy); }
Signature · 1 line
1.7.5.rc2
Re: [PATCH 1/2] Fix additional use of 'j' printf length modifier.
Hello,
Quoted 10 lines · Jonathan Nieder
Hi,
Quoted 5 lines · Brian Koropoff
The printf builtin modifies the user's format strings by prefixing integer conversion specifications with the 'j' (
intmax_t) length modifier. Since this is not portable, instead prefix them with the length modifier extracted from the PRIdMAX constant.
Quoted 7 lines
This assumes PRIdMAX, PRIxMAX, etc all consist of the same prefix before the standard characters. Since the most common definitions are j<usual char>, l<usual char>, q<usual char>, I64<usual char>, and ll<usual char>, that's probably a safe assumption. I wonder why C99 and its predecessors did not use
printf("%"PRIMAX"x\n", val);
To be completely safe we could refactor mklong() to take the PRI?MAX string as a parameter and use the correct one at each invocation site. I'm not sure this is warranted until we actually encounter a platform where it makes a difference.
Quoted 62 lines
Oh well. Maybe it would warrant a comment, though?
/* * Replace a string like * * %92.3u * ^ ^--- ch * '-------- str * * with "%92.3" PRIuMAX "". * * Although C99 does not guarantee it, we assume PRIiMAX, * PRIoMAX, PRIuMAX, PRIxMAX, and PRIXMAX are all the same * as PRIdMAX with the final 'd' replaced by the corresponding * character. */src/bltin/printf.c static char * · line 321
Quoted 9 lines
src/bltin/printf.c +2
-1@@ -317,15 +317,16 @@ static char * mklong(const char *str, const char *ch) { char *copy; -size_t len; +size_t len; +size_t pridmax_len =strlen (PRIdMAX);I think just using strlen(PRIdMAX) as-is would make it clearer that we are expecting the compiler to inline the "strlen" and provides a reminder of the value, too (i.e., is it 2 or 3 for "jd"?).
src/bltin/printf.c static char * · line 322
Quoted 3 lines
src/bltin/printf.c +1
-1- len = ch - str +3 ; + len = ch - str + pridmax_len;This changes the meaning of "len" to no longer be the size of the buffer. I suppose that doesn't matter, but...
src/bltin/printf.c static char * · line 328
Quoted 11 lines
src/bltin/printf.c +5
-5STARTSTACKSTR (copy); - copy =makestrspace (len, copy); -memcpy (copy, str, len -3 ); - copy[len -3 ] ='j' ; - copy[len -2 ] = *ch; - copy[len -1 ] ='\0' ; + copy =makestrspace (len +1 , copy); +memcpy (copy, str, len - pridmax_len); +memcpy (copy + len - pridmax_len, PRIdMAX, pridmax_len -1 ); + copy[len -1 ] = *ch; + copy[len] ='\0' ;... the arithmetic is getting complicated. I think mempcpy could make the intention clearer, like so.
char *p; [...] len = ch - str + strlen(PRIdMAX) + 1; p = copy = makestrspace(len, copy); p = mempcpy(p, str, ch - str); p = mempcpy(p, PRIdMAX, strlen(PRIdMAX) - 1); *p++ = *ch; *p++ = '\0';
Quoted 1 line
Like this, maybe (on top, untested)?
I like this, it feels a bit cleaner. Maybe get rid of the len variable now since it's only used once.
-- Brian
Re: [PATCH 1/2] Fix additional use of 'j' printf length modifier.
In reply to Brian KoropoffQuoted 7 lines · Brian Koropoff
The printf builtin modifies the user's format strings by prefixing integer conversion specifications with the 'j' (
intmax_t) length modifier. Since this is not portable, instead prefix them with the length modifier extracted from the PRIdMAX constant.Signed-off-by Brian Koropoff <[email protected]>
I'm sorry but I'm not taking this. %j is part of ISO C99 and POSIX so please fix your build environment instead.
Thanks,
Signature · 3 lines
Email: Herbert Xu <[email protected]> Home Page: http://gondor.apana.org.au/~herbert/ PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
Re: [PATCH 1/2] Fix additional use of 'j' printf length modifier.
Quoted 1 line · Herbert Xu
%j is part of ISO C99 and POSIX
*checks* Oh, so it is! Thanks; it's nice to learn the standard offers more than expected.
Brian: fwiw gnulib printf seems to provide %j, though of course it's a bit more heavyweight. And of course there are alternative C libraries --- e.g., glibc runs on Solaris.
Re: [PATCH 1/2] Fix additional use of 'j' printf length modifier.
Quoted 9 lines · Jonathan Nieder
Quoted 1 line · Herbert Xu
%j is part of ISO C99 and POSIX
*checks* Oh, so it is! Thanks; it's nice to learn the standard offers more than expected.
Brian: fwiw gnulib printf seems to provide %j, though of course it's a bit more heavyweight. And of course there are alternative C libraries --- e.g., glibc runs on Solaris.
C99 also guarantees the presence of imaxdiv, but arith_yacc.c happily #ifdefs around it if the build environment lacks it. I submitted other patches in the same spirit (working around non-compliant vsnprintf) and they were accepted. Would a patch that retains the old code verbatim but #ifdefs in an alternate implementation on legacy systems be more acceptable?
-- Brian
Re: [PATCH 1/2] Fix additional use of 'j' printf length modifier.
Quoted 6 lines · Brian Koropoff
C99 also guarantees the presence of imaxdiv, but
arith_yacc.chappily #ifdefs around it if the build environment lacks it. I submitted other patches in the same spirit (working around non-compliant vsnprintf) and they were accepted. Would a patch that retains the old code verbatim but #ifdefs in an alternate implementation on legacy systems be more acceptable?
I guess so if that's the only thing keeping you from building this on hupx.
Can you also split your other patch up so that each patch does one thing only?
Thanks,
Signature · 3 lines
Email: Herbert Xu <[email protected]> Home Page: http://gondor.apana.org.au/~herbert/ PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
PATCH 2/2 Port to HP-UX
In reply to Brian Koropoff- strtoimax and strtoumax are macros and not functions, so switch to using AC_CHECK_DECLS in configure.ac to find them.
- HP-UX lacks strsignal() and sys_siglist. Check for sys_siglist in configure.ac and #ifdef around it.
- HP-UX's vsnprintf() completely violates the standard by returning -1 (and neglecting to set errno) if the buffer is not large enough rather than returning the size necessary. Work around it by (yuck) reallocating a dynamic buffer until it succeeds so we can return the expected result.
- HP-UX needs _LARGEFILE64_SOURCE to be defined for open64() and friends to be available. This seems to be safe to define everywhere, so do so.
- The nl program doesn't like spaces between flags and their arguments, so remove them in mkbuiltins.
--- configure.ac | 18 +++++++++++++++--- src/mkbuiltins | 2 +- src/output.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/system.c | 2 ++ src/system.h | 4 ++-- 5 files changed, 72 insertions(+), 6 deletions(-)
configure.ac +15 -3
@@ -9,6 +9,8 @@ AC_PROG_CC AC_GNU_SOURCE AC_PROG_YACC +AC_DEFINE([_LARGEFILE64_SOURCE],[1],[Always define]) + AC_MSG_CHECKING([for build system compiler]) if test "$cross_compiling" = yes; then CC_FOR_BUILD=${CC_FOR_BUILD-cc}
@@ -43,7 +45,7 @@ AC_ARG_ENABLE(glob, AS_HELP_STRING(--enable-glob, [Use glob(3) from libc])) dnl Checks for libraries. dnl Checks for header files. -AC_CHECK_HEADERS(alloca.h paths.h) +AC_CHECK_HEADERS(alloca.h paths.h unistd.h signal.h) dnl Check for declarations AC_CHECK_DECL([_PATH_BSHELL],,AC_DEFINE_UNQUOTED([_PATH_BSHELL], "/bin/sh", [Define to system shell path]),[
@@ -84,11 +86,21 @@ AC_CHECK_DECL([PRIdMAX],, #include <inttypes.h> ]) +dnl strtoimax is a macro on some systems +AC_CHECK_DECLS([strtoimax, strtoumax],,,[#include <inttypes.h>]) + +dnl Check for sys_siglist +AC_CHECK_DECLS([sys_siglist], [], [], + [#include <signal.h> + #ifdef HAVE_UNISTD_H + # include <unistd.h> + #endif + ]) + dnl Checks for library functions. AC_CHECK_FUNCS(bsearch faccessat getpwnam getrlimit imaxdiv isalpha killpg \ mempcpy \ - sigsetmask stpcpy strchrnul strsignal strtod strtoimax \ - strtoumax sysconf) + sigsetmask stpcpy strchrnul strsignal strtod sysconf) if test "$enable_fnmatch" = yes; then use_fnmatch=
src/mkbuiltins +1 -1
@@ -97,7 +97,7 @@ cat <<\! */ ! -sed 's/ -[a-z]*//' $temp2 | nl -b a -v 0 | LC_COLLATE=C sort -u -k 3,3 | +sed 's/ -[a-z]*//' $temp2 | nl -ba -v0 | LC_COLLATE=C sort -u -k 3,3 | tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ | awk '{ printf "#define %s (builtincmd + %d)\n", $3, $1}' printf '\n#define NUMBUILTINS %d\n' $(wc -l < $temp2)
src/output.c +52 -0
61 lines not on this page · raw message
src/system.c +2 -0
@@ -92,8 +92,10 @@ char *strsignal(int sig) {static char buf[19 ]; +#if HAVE_DECL_SYS_SIGLIST if ((unsigned )sig <NSIG && sys_siglist[sig])return (char *)sys_siglist[sig]; +#endif fmtstr (buf,sizeof (buf),"Signal %d" , sig);return buf; }
src/system.h +2 -2
@@ -69,11 +69,11 @@ static inline double strtod(const char *nptr, char **endptr) }#endif -#ifndef HAVE_STRTOIMAX +#if !HAVE_DECL_STRTOIMAX #define strtoimax strtoll#endif -#ifndef HAVE_STRTOUMAX +#if !HAVE_DECL_STRTOUMAX #define strtoumax strtoull#endif
Signature · 1 line
1.7.1