Subject: Re: How to get arbitrary time in shell script?
To: NetBSD User's Discussion List <netbsd-users@NetBSD.ORG>
From: Greg A. Woods <woods@weird.com>
List: netbsd-users
Date: 11/29/2001 12:46:03
[ On Thursday, November 29, 2001 at 10:03:12 (-0000), David Laight wrote: ]
> Subject: Re: How to get arbitrary time in shell script?
>
> [ On Thursday, November 29, 2001 at 07:11:48 (+0100), Christoph Kaegi wrote: ]
> Subject: Re: How to get arbitrary time in shell script?
> > 
> > Maybe it even looks a little bit nicer this way:
> > 
> >        date -r `expr $(date +%s) - 259200`
> 
> One day someone will try hard to depracate '`...`' but they haven't
> suceeded yet.  The '$(...)' form is only understood by some shells,
> in particular the 'standard' bourne shell (the one scripts should be
> written for) doesn't support it.

The '$(...)' form is defined by the IEEE POSIX 1003.2 standard.  Every
compliant/compatible shell must handle it.

> If you are going to do it, do it properly...
> 
>         date -r $(expr $(date +%s) - 3 '*' 24 '*' 60 '*' 60)

Well if you're going to use POSIX shell syntax, then why not all of it?  :-)

	date -r $(($(date +%s) - (3*24*60*60)))

That way you don't need to use "expr", or quote all the silly operators.

Hmmmm.... I guess that would be good if it really worked properly with
all shells.  Unfortunately it seems there's a bug in NetBSD's /bin/sh
arithmetic expression evaluation:

	$ ksh -c 'echo $(($(date +%s) - (3*24*60*60)))'
	1006794103
	$ sh -c 'echo $(($(date +%s) - (3*24*60*60)))'  
	100679410

I thought on first glance this seemed to be a simple string copying bug,
but a little experiment shows it may be deeper than that:

	$ sh -c 'echo $((123456789))'
	123456789
	$ sh -c 'echo $((1234567890))'
	123456789
	$ sh -c 'echo $((12345678901))'
	214748364

Or maybe the latter is simply a combination of the string copying bug
and a limit to the size of integer "sh" can manipulate:

	$ ksh -c 'echo $((123456789))'  
	123456789
	$ ksh -c 'echo $((1234567890))'
	1234567890
	$ ksh -c 'echo $((12345678901))'
	-539222987


Indeed both shells seem to use signed integer arithmetic (I'm assuming
they use C 'int's), but /bin/sh seems to have a fixed length string
buffer in which integers are formatted for output since the addition of
a sign character chopps off another of the least significant digits:

	$ sh -c 'echo $((1 << 32))' 
	1
	$ ksh -c 'echo $((1 << 32))'
	1
	$ sh -c 'echo $((1 << 31))'  
	-21474836
	$ ksh -c 'echo $((1 << 31))'
	-2147483648

I took a quick look at the code, and after a second closer look I found
the following wee error in expand.c in expari():

        result = arith(p+2);
        fmtstr(p, 10, "%d", result);

That '10' should be something a bit bigger, such as maybe even 22 or 23,
which should then accomodate signed 64-bit integers too (the value
should probably be less than 23 unless TEMPSIZE is changed from its
current value of 24).  Changing that '10' to '23' does indeed seem to
fix the problem, at least on a 32-bit machine:

	$ obj/sh -c 'echo $((1 << 31))'
	-2147483648
	$ ksh -c 'echo $((1 << 31))'    
	-2147483648

I guess I should send-pr that fix!  ;-)

-- 
							Greg A. Woods

+1 416 218-0098      VE3TCP      <gwoods@acm.org>     <woods@robohack.ca>
Planix, Inc. <woods@planix.com>;   Secrets of the Weird <woods@weird.com>