]> git.sur5r.net Git - openldap/blob - libraries/liblutil/utils.c
Remove unused variables
[openldap] / libraries / liblutil / utils.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1998-2007 The OpenLDAP Foundation.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted only as authorized by the OpenLDAP
9  * Public License.
10  *
11  * A copy of this license is available in the file LICENSE in the
12  * top-level directory of the distribution or, alternatively, at
13  * <http://www.OpenLDAP.org/license.html>.
14  */
15
16 #include "portable.h"
17
18 #include <stdio.h>
19 #include <ac/stdlib.h>
20 #include <ac/string.h>
21 #include <ac/ctype.h>
22 #include <ac/unistd.h>
23 #include <ac/time.h>
24 #include <ac/errno.h>
25 #ifdef HAVE_IO_H
26 #include <io.h>
27 #endif
28 #ifdef HAVE_FCNTL_H
29 #include <fcntl.h>
30 #endif
31 #ifdef _WIN32
32 #include <windows.h>
33 #endif
34
35 #include "lutil.h"
36 #include "ldap_defaults.h"
37 #include "ldap_pvt.h"
38 #include "lber_pvt.h"
39
40 #ifdef HAVE_EBCDIC
41 int _trans_argv = 1;
42 #endif
43
44 #ifdef _WIN32
45 /* Some Windows versions accept both forward and backslashes in
46  * directory paths, but we always use backslashes when generating
47  * and parsing...
48  */
49 void lutil_slashpath( char *path )
50 {
51         char *c, *p;
52
53         p = path;
54         while (( c=strchr( p, '/' ))) {
55                 *c++ = '\\';
56                 p = c;
57         }
58 }
59 #endif
60
61 char* lutil_progname( const char* name, int argc, char *argv[] )
62 {
63         char *progname;
64
65         if(argc == 0) {
66                 return (char *)name;
67         }
68
69 #ifdef HAVE_EBCDIC
70         if (_trans_argv) {
71                 int i;
72                 for (i=0; i<argc; i++) __etoa(argv[i]);
73                 _trans_argv = 0;
74         }
75 #endif
76         LUTIL_SLASHPATH( argv[0] );
77         progname = strrchr ( argv[0], *LDAP_DIRSEP );
78         progname = progname ? &progname[1] : argv[0];
79         return progname;
80 }
81
82 #if 0
83 size_t lutil_gentime( char *s, size_t smax, const struct tm *tm )
84 {
85         size_t ret;
86 #ifdef HAVE_EBCDIC
87 /* We've been compiling in ASCII so far, but we want EBCDIC now since
88  * strftime only understands EBCDIC input.
89  */
90 #pragma convlit(suspend)
91 #endif
92         ret = strftime( s, smax, "%Y%m%d%H%M%SZ", tm );
93 #ifdef HAVE_EBCDIC
94 #pragma convlit(resume)
95         __etoa( s );
96 #endif
97         return ret;
98 }
99 #endif
100
101 size_t lutil_localtime( char *s, size_t smax, const struct tm *tm, long delta )
102 {
103         size_t  ret;
104         char    *p;
105
106         if ( smax < 16 ) {      /* YYYYmmddHHMMSSZ */
107                 return 0;
108         }
109
110 #ifdef HAVE_EBCDIC
111 /* We've been compiling in ASCII so far, but we want EBCDIC now since
112  * strftime only understands EBCDIC input.
113  */
114 #pragma convlit(suspend)
115 #endif
116         ret = strftime( s, smax, "%Y%m%d%H%M%SZ", tm );
117 #ifdef HAVE_EBCDIC
118 #pragma convlit(resume)
119         __etoa( s );
120 #endif
121         if ( delta == 0 || ret == 0 ) {
122                 return ret;
123         }
124
125         if ( smax < 20 ) {      /* YYYYmmddHHMMSS+HHMM */
126                 return 0;
127         }
128
129         p = s + 14;
130
131         if ( delta < 0 ) {
132                 p[ 0 ] = '-';
133                 delta = -delta;
134         } else {
135                 p[ 0 ] = '+';
136         }
137         p++;
138
139         snprintf( p, smax - 15, "%02ld%02ld", delta / 3600,
140                         ( delta % 3600 ) / 60 );
141
142         return ret + 5;
143 }
144
145 int lutil_tm2time( struct lutil_tm *tm, struct lutil_timet *tt )
146 {
147         static int moffset[12] = {
148                 0, 31, 59, 90, 120,
149                 151, 181, 212, 243,
150                 273, 304, 334 }; 
151         int sec;
152
153         tt->tt_usec = tm->tm_usec;
154
155         /* special case 0000/01/01+00:00:00 is returned as zero */
156         if ( tm->tm_year == -1900 && tm->tm_mon == 0 && tm->tm_mday == 1 &&
157                 tm->tm_hour == 0 && tm->tm_min == 0 && tm->tm_sec == 0 ) {
158                 tt->tt_sec = 0;
159                 tt->tt_gsec = 0;
160                 return 0;
161         }
162
163         /* tm->tm_year is years since 1900 */
164         /* calculate days from years since 1970 (epoch) */ 
165         tt->tt_sec = tm->tm_year - 70; 
166         tt->tt_sec *= 365L; 
167
168         /* count leap days in preceding years */ 
169         tt->tt_sec += ((tm->tm_year -69) >> 2); 
170
171         /* calculate days from months */ 
172         tt->tt_sec += moffset[tm->tm_mon]; 
173
174         /* add in this year's leap day, if any */ 
175         if (((tm->tm_year & 3) == 0) && (tm->tm_mon > 1)) { 
176                 tt->tt_sec ++; 
177         } 
178
179         /* add in days in this month */ 
180         tt->tt_sec += (tm->tm_mday - 1); 
181
182         /* this function can handle a range of about 17408 years... */
183         /* 86400 seconds in a day, divided by 128 = 675 */
184         tt->tt_sec *= 675;
185
186         /* move high 7 bits into tt_gsec */
187         tt->tt_gsec = tt->tt_sec >> 25;
188         tt->tt_sec -= tt->tt_gsec << 25;
189
190         /* get hours */ 
191         sec = tm->tm_hour; 
192
193         /* convert to minutes */ 
194         sec *= 60L; 
195         sec += tm->tm_min; 
196
197         /* convert to seconds */ 
198         sec *= 60L; 
199         sec += tm->tm_sec; 
200         
201         /* add remaining seconds */
202         tt->tt_sec <<= 7;
203         tt->tt_sec += sec;
204
205         /* return success */
206         return 0; 
207 }
208
209 int lutil_parsetime( char *atm, struct lutil_tm *tm )
210 {
211         while (atm && tm) {
212                 char *ptr = atm;
213                 unsigned i, fracs;
214
215                 /* Is the stamp reasonably long? */
216                 for (i=0; isdigit((unsigned char) atm[i]); i++);
217                 if (i < sizeof("00000101000000")-1)
218                         break;
219
220                 /*
221                  * parse the time into a struct tm
222                  */
223                 /* 4 digit year to year - 1900 */
224                 tm->tm_year = *ptr++ - '0';
225                 tm->tm_year *= 10; tm->tm_year += *ptr++ - '0';
226                 tm->tm_year *= 10; tm->tm_year += *ptr++ - '0';
227                 tm->tm_year *= 10; tm->tm_year += *ptr++ - '0';
228                 tm->tm_year -= 1900;
229                 /* month 01-12 to 0-11 */
230                 tm->tm_mon = *ptr++ - '0';
231                 tm->tm_mon *=10; tm->tm_mon += *ptr++ - '0';
232                 if (tm->tm_mon < 1 || tm->tm_mon > 12) break;
233                 tm->tm_mon--;
234
235                 /* day of month 01-31 */
236                 tm->tm_mday = *ptr++ - '0';
237                 tm->tm_mday *=10; tm->tm_mday += *ptr++ - '0';
238                 if (tm->tm_mday < 1 || tm->tm_mday > 31) break;
239
240                 /* Hour 00-23 */
241                 tm->tm_hour = *ptr++ - '0';
242                 tm->tm_hour *=10; tm->tm_hour += *ptr++ - '0';
243                 if (tm->tm_hour < 0 || tm->tm_hour > 23) break;
244
245                 /* Minute 00-59 */
246                 tm->tm_min = *ptr++ - '0';
247                 tm->tm_min *=10; tm->tm_min += *ptr++ - '0';
248                 if (tm->tm_min < 0 || tm->tm_min > 59) break;
249
250                 /* Second 00-61 */
251                 tm->tm_sec = *ptr++ - '0';
252                 tm->tm_sec *=10; tm->tm_sec += *ptr++ - '0';
253                 if (tm->tm_sec < 0 || tm->tm_sec > 61) break;
254
255                 /* Fractions of seconds */
256                 if ( *ptr == '.' ) {
257                         ptr++;
258                         for (i = 0, fracs = 0; isdigit((unsigned char) *ptr); ) {
259                                 i*=10; i+= *ptr++ - '0';
260                                 fracs++;
261                         }
262                         tm->tm_usec = i;
263                         if (i) {
264                                 for (i = fracs; i<6; i++)
265                                         tm->tm_usec *= 10;
266                         }
267                 }
268
269                 /* Must be UTC */
270                 if (*ptr != 'Z') break;
271
272                 return 0;
273         }
274         return -1;
275 }
276
277 /* return a broken out time, with microseconds
278  * Must be mutex-protected.
279  */
280 #ifdef _WIN32
281 /* Windows SYSTEMTIME only has 10 millisecond resolution, so we
282  * also need to use a high resolution timer to get microseconds.
283  * This is pretty clunky.
284  */
285 void
286 lutil_gettime( struct lutil_tm *tm )
287 {
288         static LARGE_INTEGER cFreq;
289         static LARGE_INTEGER prevCount;
290         static int subs;
291         static int offset;
292         LARGE_INTEGER count;
293         SYSTEMTIME st;
294
295         GetSystemTime( &st );
296         QueryPerformanceCounter( &count );
297
298         /* We assume Windows has at least a vague idea of
299          * when a second begins. So we align our microsecond count
300          * with the Windows millisecond count using this offset.
301          * We retain the submillisecond portion of our own count.
302          */
303         if ( !cFreq.QuadPart ) {
304                 long long t;
305                 int usec;
306                 QueryPerformanceFrequency( &cFreq );
307
308                 t = count.QuadPart * 1000000;
309                 t /= cFreq.QuadPart;
310                 usec = t % 10000000;
311                 usec /= 1000;
312                 offset = ( usec - st.wMilliseconds ) * 1000;
313         }
314
315         /* It shouldn't ever go backwards, but multiple CPUs might
316          * be able to hit in the same tick.
317          */
318         if ( count.QuadPart <= prevCount.QuadPart ) {
319                 subs++;
320         } else {
321                 subs = 0;
322                 prevCount = count;
323         }
324
325         tm->tm_usub = subs;
326
327         /* convert to microseconds */
328         count.QuadPart *= 1000000;
329         count.QuadPart /= cFreq.QuadPart;
330         count.QuadPart -= offset;
331
332         tm->tm_usec = count.QuadPart % 1000000;
333
334         /* any difference larger than microseconds is
335          * already reflected in st
336          */
337
338         tm->tm_sec = st.wSecond;
339         tm->tm_min = st.wMinute;
340         tm->tm_hour = st.wHour;
341         tm->tm_mday = st.wDay;
342         tm->tm_mon = st.wMonth - 1;
343         tm->tm_year = st.wYear - 1900;
344 }
345 #else
346 void
347 lutil_gettime( struct lutil_tm *ltm )
348 {
349         struct timeval tv;
350         static struct timeval prevTv;
351         static int subs;
352
353 #ifdef HAVE_GMTIME_R
354         struct tm tm_buf;
355 #endif
356         struct tm *tm;
357         time_t t;
358
359         gettimeofday( &tv, NULL );
360         t = tv.tv_sec;
361
362         if ( tv.tv_sec < prevTv.tv_sec
363                 || ( tv.tv_sec == prevTv.tv_sec && tv.tv_usec == prevTv.tv_usec )) {
364                 subs++;
365         } else {
366                 subs = 0;
367                 prevTv = tv;
368         }
369
370         ltm->tm_usub = subs;
371
372 #ifdef HAVE_GMTIME_R
373         tm = gmtime_r( &t, &tm_buf );
374 #else
375         tm = gmtime( &t );
376 #endif
377
378         ltm->tm_sec = tm->tm_sec;
379         ltm->tm_min = tm->tm_min;
380         ltm->tm_hour = tm->tm_hour;
381         ltm->tm_mday = tm->tm_mday;
382         ltm->tm_mon = tm->tm_mon;
383         ltm->tm_year = tm->tm_year;
384         ltm->tm_usec = tv.tv_usec;
385 }
386 #endif
387
388 /* strcopy is like strcpy except it returns a pointer to the trailing NUL of
389  * the result string. This allows fast construction of catenated strings
390  * without the overhead of strlen/strcat.
391  */
392 char *
393 lutil_strcopy(
394         char *a,
395         const char *b
396 )
397 {
398         if (!a || !b)
399                 return a;
400         
401         while ((*a++ = *b++)) ;
402         return a-1;
403 }
404
405 /* strncopy is like strcpy except it returns a pointer to the trailing NUL of
406  * the result string. This allows fast construction of catenated strings
407  * without the overhead of strlen/strcat.
408  */
409 char *
410 lutil_strncopy(
411         char *a,
412         const char *b,
413         size_t n
414 )
415 {
416         if (!a || !b || n == 0)
417                 return a;
418         
419         while ((*a++ = *b++) && n-- > 0) ;
420         return a-1;
421 }
422
423 #ifndef HAVE_MKSTEMP
424 int mkstemp( char * template )
425 {
426 #ifdef HAVE_MKTEMP
427         return open ( mktemp ( template ), O_RDWR|O_CREAT|O_EXCL, 0600 );
428 #else
429         return -1;
430 #endif
431 }
432 #endif
433
434 #ifdef _MSC_VER
435 struct dirent {
436         char *d_name;
437 };
438 typedef struct DIR {
439         HANDLE dir;
440         struct dirent data;
441         int first;
442         char buf[MAX_PATH+1];
443 } DIR;
444 DIR *opendir( char *path )
445 {
446         char tmp[32768];
447         int len = strlen(path);
448         DIR *d;
449         HANDLE h;
450         WIN32_FIND_DATA data;
451         
452         if (len+3 >= sizeof(tmp))
453                 return NULL;
454
455         strcpy(tmp, path);
456         tmp[len++] = '\\';
457         tmp[len++] = '*';
458         tmp[len] = '\0';
459
460         h = FindFirstFile( tmp, &data );
461         
462         if ( h == INVALID_HANDLE_VALUE )
463                 return NULL;
464
465         d = ber_memalloc( sizeof(DIR) );
466         if ( !d )
467                 return NULL;
468         d->dir = h;
469         d->data.d_name = d->buf;
470         d->first = 1;
471         strcpy(d->data.d_name, data.cFileName);
472         return d;
473 }
474 struct dirent *readdir(DIR *dir)
475 {
476         WIN32_FIND_DATA data;
477
478         if (dir->first) {
479                 dir->first = 0;
480         } else {
481                 if (!FindNextFile(dir->dir, &data))
482                         return NULL;
483                 strcpy(dir->data.d_name, data.cFileName);
484         }
485         return &dir->data;
486 }
487 void closedir(DIR *dir)
488 {
489         FindClose(dir->dir);
490         ber_memfree(dir);
491 }
492 #endif
493
494 /*
495  * Memory Reverse Search
496  */
497 void *
498 lutil_memrchr(const void *b, int c, size_t n)
499 {
500         if (n != 0) {
501                 const unsigned char *s, *bb = b, cc = c;
502
503                 for ( s = bb + n; s > bb; ) {
504                         if ( *--s == cc ) {
505                                 return (void *) s;
506                         }
507                 }
508         }
509
510         return NULL;
511 }
512
513 int
514 lutil_atoix( int *v, const char *s, int x )
515 {
516         char            *next;
517         long            i;
518
519         assert( s != NULL );
520         assert( v != NULL );
521
522         i = strtol( s, &next, x );
523         if ( next == s || next[ 0 ] != '\0' ) {
524                 return -1;
525         }
526
527         if ( (long)(int)i != i ) {
528                 return 1;
529         }
530
531         *v = (int)i;
532
533         return 0;
534 }
535
536 int
537 lutil_atoux( unsigned *v, const char *s, int x )
538 {
539         char            *next;
540         unsigned long   u;
541
542         assert( s != NULL );
543         assert( v != NULL );
544
545         /* strtoul() has an odd interface */
546         if ( s[ 0 ] == '-' ) {
547                 return -1;
548         }
549
550         u = strtoul( s, &next, x );
551         if ( next == s || next[ 0 ] != '\0' ) {
552                 return -1;
553         }
554
555         if ( (unsigned long)(unsigned)u != u ) {
556                 return 1;
557         }
558
559         *v = u;
560
561         return 0;
562 }
563
564 int
565 lutil_atolx( long *v, const char *s, int x )
566 {
567         char            *next;
568         long            l;
569
570         assert( s != NULL );
571         assert( v != NULL );
572
573         l = strtol( s, &next, x );
574         if ( next == s || next[ 0 ] != '\0' ) {
575                 return -1;
576         }
577
578         *v = l;
579
580         return 0;
581 }
582
583 int
584 lutil_atoulx( unsigned long *v, const char *s, int x )
585 {
586         char            *next;
587         unsigned long   ul;
588
589         assert( s != NULL );
590         assert( v != NULL );
591
592         /* strtoul() has an odd interface */
593         if ( s[ 0 ] == '-' ) {
594                 return -1;
595         }
596
597         ul = strtoul( s, &next, x );
598         if ( next == s || next[ 0 ] != '\0' ) {
599                 return -1;
600         }
601
602         *v = ul;
603
604         return 0;
605 }
606
607 /* Multiply an integer by 100000000 and add new */
608 typedef struct lutil_int_decnum {
609         unsigned char *buf;
610         int bufsiz;
611         int beg;
612         int len;
613 } lutil_int_decnum;
614
615 #define FACTOR1 (100000000&0xffff)
616 #define FACTOR2 (100000000>>16)
617
618 static void
619 scale( int new, lutil_int_decnum *prev, unsigned char *tmp )
620 {
621         int i, j;
622         unsigned char *in = prev->buf+prev->beg;
623         unsigned int part;
624         unsigned char *out = tmp + prev->bufsiz - prev->len;
625
626         memset( tmp, 0, prev->bufsiz );
627         if ( prev->len ) {
628                 for ( i = prev->len-1; i>=0; i-- ) {
629                         part = in[i] * FACTOR1;
630                         for ( j = i; part; j-- ) {
631                                 part += out[j];
632                                 out[j] = part & 0xff;
633                                 part >>= 8;
634                         }
635                         part = in[i] * FACTOR2;
636                         for ( j = i-2; part; j-- ) {
637                                 part += out[j];
638                                 out[j] = part & 0xff;
639                                 part >>= 8;
640                         }
641                 }
642                 j++;
643                 prev->beg += j;
644                 prev->len -= j;
645         }
646
647         out = tmp + prev->bufsiz;
648         i = 0;
649         do {
650                 i--;
651                 new += out[i];
652                 out[i] = new & 0xff;
653                 new >>= 8;
654         } while ( new );
655         i = -i;
656         if ( prev->len < i ) {
657                 prev->beg = prev->bufsiz - i;
658                 prev->len = i;
659         }
660         AC_MEMCPY( prev->buf+prev->beg, tmp+prev->beg, prev->len );
661 }
662
663 /* Convert unlimited length decimal or hex string to binary.
664  * Output buffer must be provided, bv_len must indicate buffer size
665  * Hex input can be "0x1234" or "'1234'H"
666  *
667  * Temporarily modifies the input string.
668  *
669  * Note: High bit of binary form is always the sign bit. If the number
670  * is supposed to be positive but has the high bit set, a zero byte
671  * is prepended. It is assumed that this has already been handled on
672  * any hex input.
673  */
674 int
675 lutil_str2bin( struct berval *in, struct berval *out, void *ctx )
676 {
677         char *pin, *pout, ctmp;
678         char *end;
679         long l;
680         int i, chunk, len, rc = 0, hex = 0;
681         if ( !out || !out->bv_val || out->bv_len < in->bv_len )
682                 return -1;
683
684         pout = out->bv_val;
685         /* Leading "0x" for hex input */
686         if ( in->bv_len > 2 && in->bv_val[0] == '0' &&
687                 ( in->bv_val[1] == 'x' || in->bv_val[1] == 'X' ) )
688         {
689                 len = in->bv_len - 2;
690                 pin = in->bv_val + 2;
691                 hex = 1;
692         } else if ( in->bv_len > 3 && in->bv_val[0] == '\'' &&
693                 in->bv_val[in->bv_len-2] == '\'' &&
694                 in->bv_val[in->bv_len-1] == 'H' )
695         {
696                 len = in->bv_len - 3;
697                 pin = in->bv_val + 1;
698                 hex = 1;
699         }
700         if ( hex ) {
701 #define HEXMAX  (2 * sizeof(long))
702                 /* Convert a longword at a time, but handle leading
703                  * odd bytes first
704                  */
705                 chunk = len & (HEXMAX-1);
706                 if ( !chunk )
707                         chunk = HEXMAX;
708
709                 while ( len ) {
710                         ctmp = pin[chunk];
711                         pin[chunk] = '\0';
712                         errno = 0;
713                         l = strtol( pin, &end, 16 );
714                         pin[chunk] = ctmp;
715                         if ( errno )
716                                 return -1;
717                         chunk++;
718                         chunk >>= 1;
719                         for ( i = chunk; i>=0; i-- ) {
720                                 pout[i] = l & 0xff;
721                                 l >>= 8;
722                         }
723                         pin += chunk;
724                         pout += sizeof(long);
725                         len -= chunk;
726                         chunk = HEXMAX;
727                 }
728                 out->bv_len = pout + len - out->bv_val;
729         } else {
730         /* Decimal */
731                 char tmpbuf[64], *tmp;
732                 lutil_int_decnum num;
733                 int neg = 0;
734
735                 len = in->bv_len;
736                 pin = in->bv_val;
737                 num.buf = (unsigned char *)out->bv_val;
738                 num.bufsiz = out->bv_len;
739                 num.beg = num.bufsiz-1;
740                 num.len = 0;
741                 if ( pin[0] == '-' ) {
742                         neg = 0xff;
743                         len--;
744                         pin++;
745                 }
746
747 #define DECMAX  8       /* 8 digits at a time */
748
749                 /* tmp must be at least as large as outbuf */
750                 if ( out->bv_len > sizeof(tmpbuf)) {
751                         tmp = ber_memalloc_x( out->bv_len, ctx );
752                 } else {
753                         tmp = tmpbuf;
754                 }
755                 chunk = len & (DECMAX-1);
756                 if ( !chunk )
757                         chunk = DECMAX;
758
759                 while ( len ) {
760                         ctmp = pin[chunk];
761                         pin[chunk] = '\0';
762                         errno = 0;
763                         l = strtol( pin, &end, 10 );
764                         pin[chunk] = ctmp;
765                         if ( errno ) {
766                                 rc = -1;
767                                 goto decfail;
768                         }
769                         scale( l, &num, (unsigned char *)tmp );
770                         pin += chunk;
771                         len -= chunk;
772                         chunk = DECMAX;
773                 }
774                 /* Negate the result */
775                 if ( neg ) {
776                         unsigned char *ptr;
777
778                         ptr = num.buf+num.beg;
779
780                         /* flip all bits */
781                         for ( i=0; i<num.len; i++ )
782                                 ptr[i] ^= 0xff;
783
784                         /* add 1, with carry - overflow handled below */
785                         while ( i-- && ! (ptr[i] = (ptr[i] + 1) & 0xff )) ;
786                 }
787                 /* Prepend sign byte if wrong sign bit */
788                 if (( num.buf[num.beg] ^ neg ) & 0x80 ) {
789                         num.beg--;
790                         num.len++;
791                         num.buf[num.beg] = neg;
792                 }
793                 if ( num.beg )
794                         AC_MEMCPY( num.buf, num.buf+num.beg, num.len );
795                 out->bv_len = num.len;
796 decfail:
797                 if ( tmp != tmpbuf ) {
798                         ber_memfree_x( tmp, ctx );
799                 }
800         }
801         return rc;
802 }
803
804 static  char            time_unit[] = "dhms";
805
806 /* Used to parse and unparse time intervals, not timestamps */
807 int
808 lutil_parse_time(
809         const char      *in,
810         unsigned long   *tp )
811 {
812         unsigned long   t = 0;
813         char            *s,
814                         *next;
815         int             sofar = -1,
816                         scale[] = { 86400, 3600, 60, 1 };
817
818         *tp = 0;
819
820         for ( s = (char *)in; s[ 0 ] != '\0'; ) {
821                 unsigned long   u;
822                 char            *what;
823
824                 /* strtoul() has an odd interface */
825                 if ( s[ 0 ] == '-' ) {
826                         return -1;
827                 }
828
829                 u = strtoul( s, &next, 10 );
830                 if ( next == s ) {
831                         return -1;
832                 }
833
834                 if ( next[ 0 ] == '\0' ) {
835                         /* assume seconds */
836                         t += u;
837                         break;
838                 }
839
840                 what = strchr( time_unit, next[ 0 ] );
841                 if ( what == NULL ) {
842                         return -1;
843                 }
844
845                 if ( what - time_unit <= sofar ) {
846                         return -1;
847                 }
848
849                 sofar = what - time_unit;
850                 t += u * scale[ sofar ];
851
852                 s = &next[ 1 ];
853         }
854
855         *tp = t;
856         return 0;
857 }
858
859 int
860 lutil_unparse_time(
861         char                    *buf,
862         size_t                  buflen,
863         unsigned long           t )
864 {
865         int             len, i;
866         unsigned long   v[ 4 ];
867         char            *ptr = buf;
868
869         v[ 0 ] = t/86400;
870         v[ 1 ] = (t%86400)/3600;
871         v[ 2 ] = (t%3600)/60;
872         v[ 3 ] = t%60;
873
874         for ( i = 0; i < 4; i++ ) {
875                 if ( v[i] > 0 || ( i == 3 && ptr == buf ) ) {
876                         len = snprintf( ptr, buflen, "%lu%c", v[ i ], time_unit[ i ] );
877                         if ( len < 0 || (unsigned)len >= buflen ) {
878                                 return -1;
879                         }
880                         buflen -= len;
881                         ptr += len;
882                 }
883         }
884
885         return 0;
886 }
887