Subject: Re: bug#10782: zero padding does not work in case of strings
From: Eric Blake <[email-masked]>
To: Praveen A <[email-masked]>
Cc: [email-masked]
Date: Fri, 10 Feb 2012 12:29:53 -0700
List-Id: <debbugs-submit.debbugs.gnu.org>

tag 10782 notabug
thanks

On 02/10/2012 11:52 AM, Praveen A wrote:
> $ printf %04d 12
> 0012$ printf %4d 12
>   12$ printf %04s 12
>   12$ printf %4s 12
>   12$

Thanks for the report.  In printf(3), POSIX states the use of the 0
modifier with %s is undefined:

http://pubs.opengroup.org/onlinepubs/9699919799/functions/printf.html

"0    For d , i , o , u , x , X , a , A , e , E , f , F , g , and G
conversion specifiers.... For other conversions, the behavior is undefined."

Note that glibc silently ignores %04x, and gcc correctly warns about it
being fishy:

$ cat foo.c
#include <stdio.h>
int main() { printf("%04s\n", "hi"); return 0; }
$ gcc -o foo foo.c -Wall
foo.c: In function ‘main’:
foo.c:2:1: warning: '0' flag used with ‘%s’ gnu_printf format [-Wformat]
$ ./foo
  hi

> 
> It would be very useful if it works for strings too (I found this when
> trying to zero pad hex values in an ipv6 address). Now it requires an
> additional sed find and replace to get the desired result.

Note that you didn't even test coreutils - you tested the built-in
printf of your shell.  Coreutils intentionally errors out on this
undefined behavior, to make it obvious that there is no sane documented
way to implement it:

$ /usr/bin/printf %04s 12
/usr/bin/printf: %04s: invalid conversion specification

Implementing %04s in coreutils, but not also in glibc, bash, dash, ksh,
and so forth, all at the same time, would just cause user confusion due
to the inconsistencies; plus, you would have to wait for those fixes to
propagate into your machine before you could rely on it.  Rather, you
should patch your script to use %d or %x or other tricks in a manner
that works today:

# Use $(()) to convert hex to decimal, then 0-pad it back out to hex
$ printf %04x $((0x12))
0012

# given an arbitrary string in $1, add 0-padding if needed to extend
# it to at least 4 characters
$ f() {
  case ${#1} in
    0|1|2|3) printf %0$((4-${#1}))d%s 0 "$1" ;;
    *) printf %s "$1" ;;
  esac
}
$ f 12
0012

As such, I'm closing this bug report as something we won't change.

-- 
Eric Blake   [email-masked]    +1-919-301-3266
Libvirt virtualization library http://libvirt.org