printf bad ?

Peter plp-ysDPMY98cNQDDBjDh4tngg at public.gmane.org
Fri Mar 18 22:57:39 UTC 2005


On Fri, 18 Mar 2005, Henry Spencer wrote:

> On Fri, 18 Mar 2005, Peter wrote:
>> printf "%05d" 08
>> it says 'invalid number' for any number of 0's before the 8. Why does it
>> think it's octal ?!
>
> Because the syntax of the arguments (after the format string) to the
> printf command is defined -- in the POSIX standard -- to be the same as
> that of C constants, with some minor extensions, and in C, a leading 0
> means an octal number.  (The fact that the FSF's documentation for the
> printf command fails to discuss this is a documentation bug.)

Point taken.

>> printf "%05d" 10#08
>> -bash: printf: 10#08: invalid number
>>
>> (according to the bash manual that notation should force decimal - it
>> does not - not in quotes and not in ticks and not with a
>> backslash-escaped '#').
>
> That notation is for the $(( )) construct, the shell's built-in expression
> evaluation; it doesn't apply everywhere.  If you try this:
>
> 	printf "%05d" $((10#08))
>
> you'll find that it works.

Yes it does. The explanation for the b#n syntax is in the 'Arithmetic 
Evaluation' section of the bash manpage but I failed to realize it 
applies only in that context (I would expect anything being treated as a 
number, and not escaped, to be subjected to this expansion - but there 
may be cases where this may not be good).

>> I wrote a small C program to test atoi, strtod and strtoul and the
>> latter is broken (is it ?). E.g.:
>> plp at plp:~/tc$ ./1 08
>> atoi: 8
>> strtod: 8.000000
>> strtoul: 0
>
> Does your program check to see whether strtoul() actually consumed the
> whole string?  If you specified 0 as the "base" argument of strtoul(),
> then it follows the C rules, so a leading 0x means hex, a leading 0
> followed by a valid octal digit is octal, and otherwise the 0 is the
> whole number and there's some trash following it.

strtoul consumed the 0 and returned a pointer to the 8, but without 
rising any error, as expected. That being said, why does the printf 
shell command raise an error with the same parameters ? Does it delimit 
on the token 08 and then notice that strtoul returned a pointer to 
inside the token ?

> No.  Quoth "man strtoul" (after a discussion of possible leading white
> space and sign):
>
>       If base  is  zero  or  16,  the
>       string may then include a `0x' prefix, and the number will
>       be read in base 16; otherwise, a zero base is taken as  10
>       (decimal)  unless the next character is `0', in which case
>       it is taken as 8 (octal).
>
> In other words, with a zero base, a leading 0 not followed by x means
> octal, period.

Right, I missed that.

>> It should look at the
>> whole string and match the highest base it can, starting from the last
>> character that is not convertible with any base, imho.
>
> That is an interesting notion, but it is *not* the specification for
> strtoul().  You don't have to guess how these things work; it's
> documented.

Yes, but it's painful imho. Is there a well-known workaround to make it 
work with 'automatic' base recognition as I suggested ? One way would be 
to strip leading 0's if they are followed by a character that may be a 
digit in any of the bases strtoul accepts. e.g. something like:

 	// chars that do not introduce a 0%c... base escape and are not octal
 	#define STRTOUL_CHARS \
 		"089abcdefghijklmnopqrstuvwyzABCDEFGHIJKLMNOPQRSTUVWYZ"
 	char *my_strtoul(char *a, char **z, int b)
 	{
 	    if(!b)
 	        while( *a && *(a+1) && (*a=='0') &&
 	            (!strchr(STRTOUL_CHARS,*(a+1))) )
 	                ++a;
  	    return(strtoul(a,z,b));
 	}

This would make 008 a decimal number and 00a a number in base 11. If 
someone needs octal he shall call my_strtoul with argument 3 == 8.

There are other ways to write this (and it may be incorrect - not 
tested!). The code above will probably force octal numbers to be read as 
something else (decimal probably) so it cannot coexist with default 
strtoul but it fixes my problem (I think).

thanks,
Peter
--
The Toronto Linux Users Group.      Meetings: http://tlug.ss.org
TLUG requests: Linux topics, No HTML, wrap text below 80 columns
How to UNSUBSCRIBE: http://tlug.ss.org/subscribe.shtml





More information about the Legacy mailing list