bsd-snprintf.c revision 98937
194334Sgshapiro/**************************************************************
290792Sgshapiro * Original:
390792Sgshapiro * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
490792Sgshapiro * A bombproof version of doprnt (dopr) included.
590792Sgshapiro * Sigh.  This sort of thing is always nasty do deal with.  Note that
690792Sgshapiro * the version here does not include floating point...
790792Sgshapiro *
894334Sgshapiro * snprintf() is used instead of sprintf() as it does limit checks
990792Sgshapiro * for string length.  This covers a nasty loophole.
1090792Sgshapiro *
1190792Sgshapiro * The other functions are there to prevent NULL pointers from
1290792Sgshapiro * causing nast effects.
1390792Sgshapiro *
1490792Sgshapiro * More Recently:
1590792Sgshapiro *  Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
1690792Sgshapiro *  This was ugly.  It is still ugly.  I opted out of floating point
1790792Sgshapiro *  numbers, but the formatter understands just about everything
1890792Sgshapiro *  from the normal C string format, at least as far as I can tell from
1990792Sgshapiro *  the Solaris 2.5 printf(3S) man page.
2090792Sgshapiro *
2190792Sgshapiro *  Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
2290792Sgshapiro *    Ok, added some minimal floating point support, which means this
2390792Sgshapiro *    probably requires libm on most operating systems.  Don't yet
2490792Sgshapiro *    support the exponent (e,E) and sigfig (g,G).  Also, fmtint()
2590792Sgshapiro *    was pretty badly broken, it just wasn't being exercised in ways
2690792Sgshapiro *    which showed it, so that's been fixed.  Also, formated the code
2790792Sgshapiro *    to mutt conventions, and removed dead code left over from the
2890792Sgshapiro *    original.  Also, there is now a builtin-test, just compile with:
2990792Sgshapiro *           gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
3090792Sgshapiro *    and run snprintf for results.
3190792Sgshapiro *
3290792Sgshapiro *  Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
3390792Sgshapiro *    The PGP code was using unsigned hexadecimal formats.
3490792Sgshapiro *    Unfortunately, unsigned formats simply didn't work.
3590792Sgshapiro *
3690792Sgshapiro *  Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
3790792Sgshapiro *    The original code assumed that both snprintf() and vsnprintf() were
3890792Sgshapiro *    missing.  Some systems only have snprintf() but not vsnprintf(), so
3990792Sgshapiro *    the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
4090792Sgshapiro *
4190792Sgshapiro *  Ben Lindstrom <mouring@eviladmin.org> 09/27/00 for OpenSSH
4290792Sgshapiro *    Welcome to the world of %lld and %qd support.  With other
4390792Sgshapiro *    long long support.  This is needed for sftp-server to work
4490792Sgshapiro *    right.
4590792Sgshapiro *
4690792Sgshapiro *  Ben Lindstrom <mouring@eviladmin.org> 02/12/01 for OpenSSH
4790792Sgshapiro *    Removed all hint of VARARGS stuff and banished it to the void,
4890792Sgshapiro *    and did a bit of KNF style work to make things a bit more
4990792Sgshapiro *    acceptable.  Consider stealing from mutt or enlightenment.
5090792Sgshapiro **************************************************************/
5190792Sgshapiro
5290792Sgshapiro#include "includes.h"
5390792Sgshapiro
5490792SgshapiroRCSID("$Id: bsd-snprintf.c,v 1.5 2001/02/25 23:20:41 mouring Exp $");
5590792Sgshapiro
5690792Sgshapiro#if defined(BROKEN_SNPRINTF)		/* For those with broken snprintf() */
5790792Sgshapiro# undef HAVE_SNPRINTF
5890792Sgshapiro# undef HAVE_VSNPRINTF
5990792Sgshapiro#endif
6090792Sgshapiro
6190792Sgshapiro#if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF)
6290792Sgshapiro
6390792Sgshapirostatic void
6490792Sgshapirodopr(char *buffer, size_t maxlen, const char *format, va_list args);
6590792Sgshapiro
6690792Sgshapirostatic void
6790792Sgshapirofmtstr(char *buffer, size_t *currlen, size_t maxlen, char *value, int flags,
6890792Sgshapiro       int min, int max);
6990792Sgshapiro
7090792Sgshapirostatic void
7190792Sgshapirofmtint(char *buffer, size_t *currlen, size_t maxlen, long value, int base,
7290792Sgshapiro       int min, int max, int flags);
7390792Sgshapiro
7490792Sgshapirostatic void
7590792Sgshapirofmtfp(char *buffer, size_t *currlen, size_t maxlen, long double fvalue,
7690792Sgshapiro      int min, int max, int flags);
7790792Sgshapiro
7890792Sgshapirostatic void
7990792Sgshapirodopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c);
8090792Sgshapiro
8190792Sgshapiro/*
8290792Sgshapiro * dopr(): poor man's version of doprintf
8390792Sgshapiro */
8490792Sgshapiro
8590792Sgshapiro/* format read states */
8690792Sgshapiro#define DP_S_DEFAULT 0
8790792Sgshapiro#define DP_S_FLAGS   1
8890792Sgshapiro#define DP_S_MIN     2
8990792Sgshapiro#define DP_S_DOT     3
9090792Sgshapiro#define DP_S_MAX     4
9190792Sgshapiro#define DP_S_MOD     5
9290792Sgshapiro#define DP_S_CONV    6
9390792Sgshapiro#define DP_S_DONE    7
9490792Sgshapiro
9590792Sgshapiro/* format flags - Bits */
9690792Sgshapiro#define DP_F_MINUS 	(1 << 0)
9790792Sgshapiro#define DP_F_PLUS  	(1 << 1)
9890792Sgshapiro#define DP_F_SPACE 	(1 << 2)
9990792Sgshapiro#define DP_F_NUM   	(1 << 3)
10090792Sgshapiro#define DP_F_ZERO  	(1 << 4)
10190792Sgshapiro#define DP_F_UP    	(1 << 5)
10290792Sgshapiro#define DP_F_UNSIGNED 	(1 << 6)
10390792Sgshapiro
10490792Sgshapiro/* Conversion Flags */
10590792Sgshapiro#define DP_C_SHORT     1
10690792Sgshapiro#define DP_C_LONG      2
10790792Sgshapiro#define DP_C_LDOUBLE   3
10890792Sgshapiro#define DP_C_LONG_LONG 4
10990792Sgshapiro
11090792Sgshapiro#define char_to_int(p) (p - '0')
11190792Sgshapiro#define abs_val(p) (p < 0 ? -p : p)
11290792Sgshapiro
11390792Sgshapiro
11490792Sgshapirostatic void
11590792Sgshapirodopr(char *buffer, size_t maxlen, const char *format, va_list args)
11690792Sgshapiro{
11790792Sgshapiro	char *strvalue;
11890792Sgshapiro	char ch;
11990792Sgshapiro	long value;
12090792Sgshapiro	long double fvalue;
12190792Sgshapiro	int min = 0;
12290792Sgshapiro	int max = -1;
12390792Sgshapiro	int state = DP_S_DEFAULT;
12490792Sgshapiro	int flags = 0;
12590792Sgshapiro	int cflags = 0;
12690792Sgshapiro	size_t currlen = 0;
12790792Sgshapiro
12890792Sgshapiro	ch = *format++;
12990792Sgshapiro
13090792Sgshapiro	while (state != DP_S_DONE) {
13190792Sgshapiro		if ((ch == '\0') || (currlen >= maxlen))
13290792Sgshapiro			state = DP_S_DONE;
13390792Sgshapiro
13490792Sgshapiro		switch(state) {
13590792Sgshapiro			case DP_S_DEFAULT:
13690792Sgshapiro				if (ch == '%')
13790792Sgshapiro					state = DP_S_FLAGS;
13890792Sgshapiro				else
13990792Sgshapiro					dopr_outch(buffer, &currlen, maxlen, ch);
14090792Sgshapiro				ch = *format++;
14190792Sgshapiro				break;
14290792Sgshapiro			case DP_S_FLAGS:
14390792Sgshapiro				switch (ch) {
14494334Sgshapiro					case '-':
14594334Sgshapiro						flags |= DP_F_MINUS;
14694334Sgshapiro						ch = *format++;
14794334Sgshapiro						break;
14894334Sgshapiro					case '+':
14994334Sgshapiro						flags |= DP_F_PLUS;
15090792Sgshapiro						ch = *format++;
15190792Sgshapiro						break;
15290792Sgshapiro					case ' ':
15390792Sgshapiro						flags |= DP_F_SPACE;
15490792Sgshapiro						ch = *format++;
15590792Sgshapiro						break;
15690792Sgshapiro					case '#':
15790792Sgshapiro						flags |= DP_F_NUM;
15890792Sgshapiro						ch = *format++;
15990792Sgshapiro						break;
16090792Sgshapiro					case '0':
16190792Sgshapiro						flags |= DP_F_ZERO;
16290792Sgshapiro						ch = *format++;
16390792Sgshapiro						break;
16490792Sgshapiro					default:
16590792Sgshapiro						state = DP_S_MIN;
16690792Sgshapiro						break;
16790792Sgshapiro				}
16890792Sgshapiro				break;
16990792Sgshapiro			case DP_S_MIN:
17090792Sgshapiro				if (isdigit((unsigned char)ch)) {
17190792Sgshapiro					min = 10*min + char_to_int (ch);
17290792Sgshapiro					ch = *format++;
17390792Sgshapiro				} else if (ch == '*') {
17490792Sgshapiro					min = va_arg (args, int);
17590792Sgshapiro					ch = *format++;
17690792Sgshapiro					state = DP_S_DOT;
17790792Sgshapiro				} else
17890792Sgshapiro					state = DP_S_DOT;
17990792Sgshapiro				break;
18090792Sgshapiro			case DP_S_DOT:
18190792Sgshapiro				if (ch == '.') {
18290792Sgshapiro					state = DP_S_MAX;
18390792Sgshapiro					ch = *format++;
18490792Sgshapiro				} else
18590792Sgshapiro					state = DP_S_MOD;
18690792Sgshapiro				break;
18790792Sgshapiro			case DP_S_MAX:
18890792Sgshapiro				if (isdigit((unsigned char)ch)) {
18990792Sgshapiro					if (max < 0)
19090792Sgshapiro						max = 0;
19190792Sgshapiro					max = 10*max + char_to_int(ch);
19290792Sgshapiro					ch = *format++;
19390792Sgshapiro				} else if (ch == '*') {
19490792Sgshapiro					max = va_arg (args, int);
19590792Sgshapiro					ch = *format++;
19690792Sgshapiro					state = DP_S_MOD;
19790792Sgshapiro				} else
19890792Sgshapiro					state = DP_S_MOD;
19990792Sgshapiro				break;
20090792Sgshapiro			case DP_S_MOD:
20190792Sgshapiro				switch (ch) {
20290792Sgshapiro					case 'h':
20390792Sgshapiro						cflags = DP_C_SHORT;
20490792Sgshapiro						ch = *format++;
20590792Sgshapiro						break;
20690792Sgshapiro					case 'l':
20790792Sgshapiro						cflags = DP_C_LONG;
20890792Sgshapiro						ch = *format++;
20990792Sgshapiro						if (ch == 'l') {
21090792Sgshapiro							cflags = DP_C_LONG_LONG;
21190792Sgshapiro							ch = *format++;
21290792Sgshapiro						}
21390792Sgshapiro						break;
21490792Sgshapiro					case 'q':
21590792Sgshapiro						cflags = DP_C_LONG_LONG;
21690792Sgshapiro						ch = *format++;
21790792Sgshapiro						break;
21890792Sgshapiro					case 'L':
21990792Sgshapiro						cflags = DP_C_LDOUBLE;
22090792Sgshapiro						ch = *format++;
22190792Sgshapiro						break;
22290792Sgshapiro					default:
22390792Sgshapiro						break;
22490792Sgshapiro				}
22590792Sgshapiro				state = DP_S_CONV;
22690792Sgshapiro				break;
22790792Sgshapiro			case DP_S_CONV:
22890792Sgshapiro				switch (ch) {
22990792Sgshapiro					case 'd':
23090792Sgshapiro					case 'i':
23190792Sgshapiro						if (cflags == DP_C_SHORT)
232							value = va_arg(args, int);
233						else if (cflags == DP_C_LONG)
234							value = va_arg(args, long int);
235						else if (cflags == DP_C_LONG_LONG)
236							value = va_arg (args, long long);
237						else
238							value = va_arg (args, int);
239						fmtint(buffer, &currlen, maxlen, value, 10, min, max, flags);
240						break;
241					case 'o':
242						flags |= DP_F_UNSIGNED;
243						if (cflags == DP_C_SHORT)
244							value = va_arg(args, unsigned int);
245						else if (cflags == DP_C_LONG)
246							value = va_arg(args, unsigned long int);
247						else if (cflags == DP_C_LONG_LONG)
248							value = va_arg(args, unsigned long long);
249						else
250							value = va_arg(args, unsigned int);
251						fmtint(buffer, &currlen, maxlen, value, 8, min, max, flags);
252						break;
253					case 'u':
254						flags |= DP_F_UNSIGNED;
255						if (cflags == DP_C_SHORT)
256							value = va_arg(args, unsigned int);
257						else if (cflags == DP_C_LONG)
258							value = va_arg(args, unsigned long int);
259						else if (cflags == DP_C_LONG_LONG)
260							value = va_arg(args, unsigned long long);
261						else
262							value = va_arg(args, unsigned int);
263						fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
264						break;
265					case 'X':
266						flags |= DP_F_UP;
267					case 'x':
268						flags |= DP_F_UNSIGNED;
269						if (cflags == DP_C_SHORT)
270							value = va_arg(args, unsigned int);
271						else if (cflags == DP_C_LONG)
272							value = va_arg(args, unsigned long int);
273						else if (cflags == DP_C_LONG_LONG)
274							value = va_arg(args, unsigned long long);
275						else
276							value = va_arg(args, unsigned int);
277						fmtint(buffer, &currlen, maxlen, value, 16, min, max, flags);
278						break;
279					case 'f':
280						if (cflags == DP_C_LDOUBLE)
281							fvalue = va_arg(args, long double);
282						else
283							fvalue = va_arg(args, double);
284						/* um, floating point? */
285						fmtfp(buffer, &currlen, maxlen, fvalue, min, max, flags);
286						break;
287					case 'E':
288						flags |= DP_F_UP;
289					case 'e':
290						if (cflags == DP_C_LDOUBLE)
291							fvalue = va_arg(args, long double);
292						else
293							fvalue = va_arg(args, double);
294						break;
295					case 'G':
296						flags |= DP_F_UP;
297					case 'g':
298						if (cflags == DP_C_LDOUBLE)
299							fvalue = va_arg(args, long double);
300						else
301							fvalue = va_arg(args, double);
302						break;
303					case 'c':
304						dopr_outch(buffer, &currlen, maxlen, va_arg(args, int));
305						break;
306					case 's':
307						strvalue = va_arg(args, char *);
308						if (max < 0)
309							max = maxlen; /* ie, no max */
310						fmtstr(buffer, &currlen, maxlen, strvalue, flags, min, max);
311						break;
312					case 'p':
313						strvalue = va_arg(args, void *);
314						fmtint(buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
315						break;
316					case 'n':
317						if (cflags == DP_C_SHORT) {
318							short int *num;
319							num = va_arg(args, short int *);
320							*num = currlen;
321						} else if (cflags == DP_C_LONG) {
322							long int *num;
323							num = va_arg(args, long int *);
324							*num = currlen;
325						} else if (cflags == DP_C_LONG_LONG) {
326							long long *num;
327							num = va_arg(args, long long *);
328							*num = currlen;
329						} else {
330							int *num;
331							num = va_arg(args, int *);
332							*num = currlen;
333						}
334						break;
335					case '%':
336						dopr_outch(buffer, &currlen, maxlen, ch);
337						break;
338					case 'w': /* not supported yet, treat as next char */
339						ch = *format++;
340						break;
341					default: /* Unknown, skip */
342					break;
343				}
344				ch = *format++;
345				state = DP_S_DEFAULT;
346				flags = cflags = min = 0;
347				max = -1;
348				break;
349			case DP_S_DONE:
350				break;
351			default: /* hmm? */
352				break; /* some picky compilers need this */
353		}
354	}
355	if (currlen < maxlen - 1)
356		buffer[currlen] = '\0';
357	else
358		buffer[maxlen - 1] = '\0';
359}
360
361static void
362fmtstr(char *buffer, size_t *currlen, size_t maxlen,
363       char *value, int flags, int min, int max)
364{
365	int padlen, strln;     /* amount to pad */
366	int cnt = 0;
367
368	if (value == 0)
369		value = "<NULL>";
370
371	for (strln = 0; value[strln]; ++strln); /* strlen */
372	padlen = min - strln;
373	if (padlen < 0)
374		padlen = 0;
375	if (flags & DP_F_MINUS)
376		padlen = -padlen; /* Left Justify */
377
378	while ((padlen > 0) && (cnt < max)) {
379		dopr_outch(buffer, currlen, maxlen, ' ');
380		--padlen;
381		++cnt;
382	}
383	while (*value && (cnt < max)) {
384		dopr_outch(buffer, currlen, maxlen, *value++);
385		++cnt;
386	}
387	while ((padlen < 0) && (cnt < max)) {
388		dopr_outch(buffer, currlen, maxlen, ' ');
389		++padlen;
390		++cnt;
391	}
392}
393
394/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
395
396static void
397fmtint(char *buffer, size_t *currlen, size_t maxlen,
398       long value, int base, int min, int max, int flags)
399{
400	unsigned long uvalue;
401	char convert[20];
402	int signvalue = 0;
403	int place = 0;
404	int spadlen = 0; /* amount to space pad */
405	int zpadlen = 0; /* amount to zero pad */
406	int caps = 0;
407
408	if (max < 0)
409		max = 0;
410
411	uvalue = value;
412
413	if (!(flags & DP_F_UNSIGNED)) {
414		if (value < 0) {
415			signvalue = '-';
416			uvalue = -value;
417		} else if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
418			signvalue = '+';
419		else if (flags & DP_F_SPACE)
420			signvalue = ' ';
421	}
422
423	if (flags & DP_F_UP)
424		caps = 1; /* Should characters be upper case? */
425
426	do {
427		convert[place++] =
428			(caps? "0123456789ABCDEF":"0123456789abcdef")
429			[uvalue % (unsigned)base];
430		uvalue = (uvalue / (unsigned)base );
431	} while (uvalue && (place < 20));
432	if (place == 20)
433		place--;
434	convert[place] = 0;
435
436	zpadlen = max - place;
437	spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
438	if (zpadlen < 0)
439		zpadlen = 0;
440	if (spadlen < 0)
441		spadlen = 0;
442	if (flags & DP_F_ZERO) {
443		zpadlen = MAX(zpadlen, spadlen);
444		spadlen = 0;
445	}
446	if (flags & DP_F_MINUS)
447		spadlen = -spadlen; /* Left Justifty */
448
449
450	/* Spaces */
451	while (spadlen > 0) {
452		dopr_outch(buffer, currlen, maxlen, ' ');
453		--spadlen;
454	}
455
456	/* Sign */
457	if (signvalue)
458		dopr_outch(buffer, currlen, maxlen, signvalue);
459
460	/* Zeros */
461	if (zpadlen > 0) {
462		while (zpadlen > 0) {
463			dopr_outch(buffer, currlen, maxlen, '0');
464			--zpadlen;
465		}
466	}
467
468	/* Digits */
469	while (place > 0)
470		dopr_outch(buffer, currlen, maxlen, convert[--place]);
471
472	/* Left Justified spaces */
473	while (spadlen < 0) {
474		dopr_outch (buffer, currlen, maxlen, ' ');
475		++spadlen;
476	}
477}
478
479static long double
480pow10(int exp)
481{
482	long double result = 1;
483
484	while (exp) {
485		result *= 10;
486		exp--;
487	}
488
489	return result;
490}
491
492static long
493round(long double value)
494{
495	long intpart = value;
496
497	value -= intpart;
498	if (value >= 0.5)
499		intpart++;
500
501	return intpart;
502}
503
504static void
505fmtfp(char *buffer, size_t *currlen, size_t maxlen, long double fvalue,
506      int min, int max, int flags)
507{
508	char iconvert[20];
509	char fconvert[20];
510	int signvalue = 0;
511	int iplace = 0;
512	int fplace = 0;
513	int padlen = 0; /* amount to pad */
514	int zpadlen = 0;
515	int caps = 0;
516	long intpart;
517	long fracpart;
518	long double ufvalue;
519
520	/*
521	 * AIX manpage says the default is 0, but Solaris says the default
522	 * is 6, and sprintf on AIX defaults to 6
523	 */
524	if (max < 0)
525		max = 6;
526
527	ufvalue = abs_val(fvalue);
528
529	if (fvalue < 0)
530		signvalue = '-';
531	else if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
532		signvalue = '+';
533	else if (flags & DP_F_SPACE)
534		signvalue = ' ';
535
536	intpart = ufvalue;
537
538	/*
539	 * Sorry, we only support 9 digits past the decimal because of our
540	 * conversion method
541	 */
542	if (max > 9)
543		max = 9;
544
545	/* We "cheat" by converting the fractional part to integer by
546	 * multiplying by a factor of 10
547	 */
548	fracpart = round((pow10 (max)) * (ufvalue - intpart));
549
550	if (fracpart >= pow10 (max)) {
551		intpart++;
552		fracpart -= pow10 (max);
553	}
554
555	/* Convert integer part */
556	do {
557		iconvert[iplace++] =
558		  (caps? "0123456789ABCDEF":"0123456789abcdef")[intpart % 10];
559		intpart = (intpart / 10);
560	} while(intpart && (iplace < 20));
561	if (iplace == 20)
562		iplace--;
563	iconvert[iplace] = 0;
564
565	/* Convert fractional part */
566	do {
567		fconvert[fplace++] =
568		  (caps? "0123456789ABCDEF":"0123456789abcdef")[fracpart % 10];
569		fracpart = (fracpart / 10);
570	} while(fracpart && (fplace < 20));
571	if (fplace == 20)
572		fplace--;
573	fconvert[fplace] = 0;
574
575	/* -1 for decimal point, another -1 if we are printing a sign */
576	padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
577	zpadlen = max - fplace;
578	if (zpadlen < 0)
579		zpadlen = 0;
580	if (padlen < 0)
581		padlen = 0;
582	if (flags & DP_F_MINUS)
583		padlen = -padlen; /* Left Justifty */
584
585	if ((flags & DP_F_ZERO) && (padlen > 0)) {
586		if (signvalue) {
587			dopr_outch(buffer, currlen, maxlen, signvalue);
588			--padlen;
589			signvalue = 0;
590		}
591		while (padlen > 0) {
592			dopr_outch(buffer, currlen, maxlen, '0');
593			--padlen;
594		}
595	}
596	while (padlen > 0) {
597		dopr_outch(buffer, currlen, maxlen, ' ');
598		--padlen;
599	}
600	if (signvalue)
601		dopr_outch(buffer, currlen, maxlen, signvalue);
602
603	while (iplace > 0)
604		dopr_outch(buffer, currlen, maxlen, iconvert[--iplace]);
605
606	/*
607	 * Decimal point.  This should probably use locale to find the correct
608	 * char to print out.
609	 */
610	dopr_outch(buffer, currlen, maxlen, '.');
611
612	while (fplace > 0)
613		dopr_outch(buffer, currlen, maxlen, fconvert[--fplace]);
614
615	while (zpadlen > 0) {
616		dopr_outch(buffer, currlen, maxlen, '0');
617		--zpadlen;
618	}
619
620	while (padlen < 0) {
621		dopr_outch(buffer, currlen, maxlen, ' ');
622		++padlen;
623	}
624}
625
626static void
627dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c)
628{
629	if (*currlen < maxlen)
630		buffer[(*currlen)++] = c;
631}
632#endif /* !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) */
633
634#ifndef HAVE_VSNPRINTF
635int
636vsnprintf(char *str, size_t count, const char *fmt, va_list args)
637{
638	str[0] = 0;
639	dopr(str, count, fmt, args);
640
641	return(strlen(str));
642}
643#endif /* !HAVE_VSNPRINTF */
644
645#ifndef HAVE_SNPRINTF
646int
647snprintf(char *str,size_t count,const char *fmt,...)
648{
649	va_list ap;
650
651	va_start(ap, fmt);
652	(void) vsnprintf(str, count, fmt, ap);
653	va_end(ap);
654
655	return(strlen(str));
656}
657
658#ifdef TEST_SNPRINTF
659int
660main(void)
661{
662#define LONG_STRING 1024
663	char buf1[LONG_STRING];
664	char buf2[LONG_STRING];
665	char *fp_fmt[] = {
666		"%-1.5f",
667		"%1.5f",
668		"%123.9f",
669		"%10.5f",
670		"% 10.5f",
671		"%+22.9f",
672		"%+4.9f",
673		"%01.3f",
674		"%4f",
675		"%3.1f",
676		"%3.2f",
677		NULL
678	};
679	double fp_nums[] = {
680		-1.5,
681		134.21,
682		91340.2,
683		341.1234,
684		0203.9,
685		0.96,
686		0.996,
687		0.9996,
688		1.996,
689		4.136,
690		0
691	};
692	char *int_fmt[] = {
693		"%-1.5d",
694		"%1.5d",
695		"%123.9d",
696		"%5.5d",
697		"%10.5d",
698		"% 10.5d",
699		"%+22.33d",
700		"%01.3d",
701		"%4d",
702		"%lld",
703		"%qd",
704		NULL
705	};
706	long long int_nums[] = { -1, 134, 91340, 341, 0203, 0, 9999999 };
707	int x, y;
708	int fail = 0;
709	int num = 0;
710
711	printf("Testing snprintf format codes against system sprintf...\n");
712
713	for (x = 0; fp_fmt[x] != NULL ; x++) {
714		for (y = 0; fp_nums[y] != 0 ; y++) {
715			snprintf(buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]);
716			sprintf (buf2, fp_fmt[x], fp_nums[y]);
717			if (strcmp (buf1, buf2)) {
718				printf("snprintf doesn't match Format: %s\n\t"
719                                       "snprintf = %s\n\tsprintf  = %s\n",
720					fp_fmt[x], buf1, buf2);
721				fail++;
722			}
723			num++;
724		}
725	}
726	for (x = 0; int_fmt[x] != NULL ; x++) {
727		for (y = 0; int_nums[y] != 0 ; y++) {
728			snprintf(buf1, sizeof (buf1), int_fmt[x], int_nums[y]);
729			sprintf(buf2, int_fmt[x], int_nums[y]);
730			if (strcmp (buf1, buf2)) {
731				printf("snprintf doesn't match Format: %s\n\t"
732				       "snprintf = %s\n\tsprintf  = %s\n",
733					int_fmt[x], buf1, buf2);
734				fail++;
735			}
736			num++;
737		}
738	}
739	printf("%d tests failed out of %d.\n", fail, num);
740	return(0);
741}
742#endif /* SNPRINTF_TEST */
743
744#endif /* !HAVE_SNPRINTF */
745