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