snprintf.c revision 80785
1/*
2 * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
3 *	All rights reserved.
4 * Copyright (c) 1997 Eric P. Allman.  All rights reserved.
5 * Copyright (c) 1988, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * By using this file, you agree to the terms and conditions set
9 * forth in the LICENSE file which can be found at the top level of
10 * the sendmail distribution.
11 *
12 */
13
14#ifndef lint
15static char id[] = "@(#)$Id: snprintf.c,v 8.27.16.4 2001/07/20 04:19:37 gshapiro Exp $";
16#endif /* ! lint */
17
18#include <sendmail.h>
19
20/*
21**  SNPRINTF, VSNPRINT -- counted versions of printf
22**
23**	These versions have been grabbed off the net.  They have been
24**	cleaned up to compile properly and support for .precision and
25**	%lx has been added.
26*/
27
28/**************************************************************
29 * Original:
30 * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
31 * A bombproof version of doprnt (sm_dopr) included.
32 * Sigh.  This sort of thing is always nasty do deal with.  Note that
33 * the version here does not include floating point...
34 *
35 * snprintf() is used instead of sprintf() as it does limit checks
36 * for string length.  This covers a nasty loophole.
37 *
38 * The other functions are there to prevent NULL pointers from
39 * causing nast effects.
40 **************************************************************/
41
42/*static char _id[] = "$OrigId: snprintf.c,v 1.2 1995/10/09 11:19:47 roberto Exp $";*/
43void	sm_dopr();
44char	*DoprEnd;
45int	SnprfOverflow;
46
47#if !HASSNPRINTF && !SNPRINTF_IS_BROKEN
48# define sm_snprintf	snprintf
49# ifndef luna2
50#  define sm_vsnprintf	vsnprintf
51extern int	vsnprintf __P((char *, size_t, const char *, va_list));
52# endif /* ! luna2 */
53#endif /* !HASSNPRINTF && !SNPRINTF_IS_BROKEN */
54
55/* VARARGS3 */
56int
57# ifdef __STDC__
58sm_snprintf(char *str, size_t count, const char *fmt, ...)
59# else /* __STDC__ */
60sm_snprintf(str, count, fmt, va_alist)
61	char *str;
62	size_t count;
63	const char *fmt;
64	va_dcl
65# endif /* __STDC__ */
66{
67	int len;
68	VA_LOCAL_DECL
69
70	VA_START(fmt);
71	len = sm_vsnprintf(str, count, fmt, ap);
72	VA_END;
73	return len;
74}
75
76int
77sm_vsnprintf(str, count, fmt, args)
78	char *str;
79	size_t count;
80	const char *fmt;
81	va_list args;
82{
83	str[0] = 0;
84	DoprEnd = str + count - 1;
85	SnprfOverflow = 0;
86	sm_dopr( str, fmt, args );
87	if (count > 0)
88		DoprEnd[0] = 0;
89	if (SnprfOverflow > 0 && tTd(57, 2))
90		dprintf("\nvsnprintf overflow, len = %ld, str = %s",
91			(long) count, shortenstring(str, MAXSHORTSTR));
92	return strlen(str) + SnprfOverflow;
93}
94
95/*
96 * sm_dopr(): poor man's version of doprintf
97 */
98
99void fmtstr __P((char *value, int ljust, int len, int zpad, int maxwidth));
100void fmtnum __P((long value, int base, int dosign, int ljust, int len, int zpad));
101void dostr __P(( char * , int ));
102char *output;
103void dopr_outch __P(( int c ));
104int	SyslogErrno;
105
106void
107sm_dopr( buffer, format, args )
108       char *buffer;
109       const char *format;
110       va_list args;
111{
112       int ch;
113       long value;
114       int longflag  = 0;
115       int pointflag = 0;
116       int maxwidth  = 0;
117       char *strvalue;
118       int ljust;
119       int len;
120       int zpad;
121#if !HASSTRERROR && !defined(ERRLIST_PREDEFINED)
122	extern char *sys_errlist[];
123	extern int sys_nerr;
124#endif /* !HASSTRERROR && !defined(ERRLIST_PREDEFINED) */
125
126
127       output = buffer;
128       while( (ch = *format++) != '\0' ){
129	       switch( ch ){
130	       case '%':
131		       ljust = len = zpad = maxwidth = 0;
132		       longflag = pointflag = 0;
133	       nextch:
134		       ch = *format++;
135		       switch( ch ){
136		       case 0:
137			       dostr( "**end of format**" , 0);
138			       return;
139		       case '-': ljust = 1; goto nextch;
140		       case '0': /* set zero padding if len not set */
141			       if(len==0 && !pointflag) zpad = '0';
142				/* FALLTHROUGH */
143		       case '1': case '2': case '3':
144		       case '4': case '5': case '6':
145		       case '7': case '8': case '9':
146			       if (pointflag)
147				 maxwidth = maxwidth*10 + ch - '0';
148			       else
149				 len = len*10 + ch - '0';
150			       goto nextch;
151		       case '*':
152			       if (pointflag)
153				 maxwidth = va_arg( args, int );
154			       else
155				 len = va_arg( args, int );
156			       goto nextch;
157		       case '.': pointflag = 1; goto nextch;
158		       case 'l': longflag = 1; goto nextch;
159		       case 'u': case 'U':
160			       /*fmtnum(value,base,dosign,ljust,len,zpad) */
161			       if( longflag ){
162				       value = va_arg( args, long );
163			       } else {
164				       value = va_arg( args, int );
165			       }
166			       fmtnum( value, 10,0, ljust, len, zpad ); break;
167		       case 'o': case 'O':
168			       /*fmtnum(value,base,dosign,ljust,len,zpad) */
169			       if( longflag ){
170				       value = va_arg( args, long );
171			       } else {
172				       value = va_arg( args, int );
173			       }
174			       fmtnum( value, 8,0, ljust, len, zpad ); break;
175		       case 'd': case 'D':
176			       if( longflag ){
177				       value = va_arg( args, long );
178			       } else {
179				       value = va_arg( args, int );
180			       }
181			       fmtnum( value, 10,1, ljust, len, zpad ); break;
182		       case 'x':
183			       if( longflag ){
184				       value = va_arg( args, long );
185			       } else {
186				       value = va_arg( args, int );
187			       }
188			       fmtnum( value, 16,0, ljust, len, zpad ); break;
189		       case 'X':
190			       if( longflag ){
191				       value = va_arg( args, long );
192			       } else {
193				       value = va_arg( args, int );
194			       }
195			       fmtnum( value,-16,0, ljust, len, zpad ); break;
196		       case 's':
197			       strvalue = va_arg( args, char *);
198			       if (maxwidth > 0 || !pointflag) {
199				 if (pointflag && len > maxwidth)
200				   len = maxwidth; /* Adjust padding */
201				 fmtstr( strvalue,ljust,len,zpad, maxwidth);
202			       }
203			       break;
204		       case 'c':
205			       ch = va_arg( args, int );
206			       dopr_outch( ch ); break;
207		       case 'm':
208#if HASSTRERROR
209			       dostr(strerror(SyslogErrno), 0);
210#else /* HASSTRERROR */
211			       if (SyslogErrno < 0 || SyslogErrno >= sys_nerr)
212			       {
213				   dostr("Error ", 0);
214				   fmtnum(SyslogErrno, 10, 0, 0, 0, 0);
215			       }
216			       else
217				   dostr((char *)sys_errlist[SyslogErrno], 0);
218#endif /* HASSTRERROR */
219			       break;
220
221		       case '%': dopr_outch( ch ); continue;
222		       default:
223			       dostr(  "???????" , 0);
224		       }
225		       break;
226	       default:
227		       dopr_outch( ch );
228		       break;
229	       }
230       }
231       *output = 0;
232}
233
234void
235fmtstr(  value, ljust, len, zpad, maxwidth )
236       char *value;
237       int ljust, len, zpad, maxwidth;
238{
239       int padlen, strleng;     /* amount to pad */
240
241       if( value == 0 ){
242	       value = "<NULL>";
243       }
244       for( strleng = 0; value[strleng]; ++ strleng ); /* strlen */
245       if (strleng > maxwidth && maxwidth)
246	 strleng = maxwidth;
247       padlen = len - strleng;
248       if( padlen < 0 ) padlen = 0;
249       if( ljust ) padlen = -padlen;
250       while( padlen > 0 ) {
251	       dopr_outch( ' ' );
252	       --padlen;
253       }
254       dostr( value, maxwidth );
255       while( padlen < 0 ) {
256	       dopr_outch( ' ' );
257	       ++padlen;
258       }
259}
260
261void
262fmtnum(  value, base, dosign, ljust, len, zpad )
263       long value;
264       int base, dosign, ljust, len, zpad;
265{
266       int signvalue = 0;
267       unsigned long uvalue;
268       char convert[20];
269       int place = 0;
270       int padlen = 0; /* amount to pad */
271       int caps = 0;
272
273       /* DEBUGP(("value 0x%x, base %d, dosign %d, ljust %d, len %d, zpad %d\n",
274	       value, base, dosign, ljust, len, zpad )); */
275       uvalue = value;
276       if( dosign ){
277	       if( value < 0 ) {
278		       signvalue = '-';
279		       uvalue = -value;
280	       }
281       }
282       if( base < 0 ){
283	       caps = 1;
284	       base = -base;
285       }
286       do{
287	       convert[place++] =
288		       (caps? "0123456789ABCDEF":"0123456789abcdef")
289			[uvalue % (unsigned)base  ];
290	       uvalue = (uvalue / (unsigned)base );
291       }while(uvalue);
292       convert[place] = 0;
293       padlen = len - place;
294       if( padlen < 0 ) padlen = 0;
295       if( ljust ) padlen = -padlen;
296       /* DEBUGP(( "str '%s', place %d, sign %c, padlen %d\n",
297	       convert,place,signvalue,padlen)); */
298       if( zpad && padlen > 0 ){
299	       if( signvalue ){
300		       dopr_outch( signvalue );
301		       --padlen;
302		       signvalue = 0;
303	       }
304	       while( padlen > 0 ){
305		       dopr_outch( zpad );
306		       --padlen;
307	       }
308       }
309       while( padlen > 0 ) {
310	       dopr_outch( ' ' );
311	       --padlen;
312       }
313       if( signvalue ) dopr_outch( signvalue );
314       while( place > 0 ) dopr_outch( convert[--place] );
315       while( padlen < 0 ){
316	       dopr_outch( ' ' );
317	       ++padlen;
318       }
319}
320
321void
322dostr( str , cut)
323     char *str;
324     int cut;
325{
326  if (cut) {
327    while(*str && cut-- > 0) dopr_outch(*str++);
328  } else {
329    while(*str) dopr_outch(*str++);
330  }
331}
332
333void
334dopr_outch( c )
335       int c;
336{
337#if 0
338       if( iscntrl(c) && c != '\n' && c != '\t' ){
339	       c = '@' + (c & 0x1F);
340	       if( DoprEnd == 0 || output < DoprEnd )
341		       *output++ = '^';
342       }
343#endif /* 0 */
344       if( DoprEnd == 0 || output < DoprEnd )
345	       *output++ = c;
346       else
347		SnprfOverflow++;
348}
349
350/*
351**  QUAD_TO_STRING -- Convert a quad type to a string.
352**
353**	Convert a quad type to a string.  This must be done
354**	separately as %lld/%qd are not supported by snprint()
355**	and adding support would slow down systems which only
356**	emulate the data type.
357**
358**	Parameters:
359**		value -- number to convert to a string.
360**
361**	Returns:
362**		pointer to a string.
363*/
364
365char *
366quad_to_string(value)
367	QUAD_T value;
368{
369	char *formatstr;
370	static char buf[64];
371
372	/*
373	**  Use sprintf() instead of snprintf() since snprintf()
374	**  does not support %qu or %llu.  The buffer is large enough
375	**  to hold the string so there is no danger of buffer
376	**  overflow.
377	*/
378
379#if NEED_PERCENTQ
380	formatstr = "%qu";
381#else /* NEED_PERCENTQ */
382	formatstr = "%llu";
383#endif /* NEED_PERCENTQ */
384	sprintf(buf, formatstr, value);
385	return buf;
386}
387/*
388**  SHORTENSTRING -- return short version of a string
389**
390**	If the string is already short, just return it.  If it is too
391**	long, return the head and tail of the string.
392**
393**	Parameters:
394**		s -- the string to shorten.
395**		m -- the max length of the string (strlen()).
396**
397**	Returns:
398**		Either s or a short version of s.
399*/
400
401char *
402shortenstring(s, m)
403	register const char *s;
404	int m;
405{
406	int l;
407	static char buf[MAXSHORTSTR + 1];
408
409	l = strlen(s);
410	if (l < m)
411		return (char *) s;
412	if (m > MAXSHORTSTR)
413		m = MAXSHORTSTR;
414	else if (m < 10)
415	{
416		if (m < 5)
417		{
418			(void) strlcpy(buf, s, m + 1);
419			return buf;
420		}
421		(void) strlcpy(buf, s, m - 2);
422		(void) strlcat(buf, "...", sizeof buf);
423		return buf;
424	}
425	m = (m - 3) / 2;
426	(void) strlcpy(buf, s, m + 1);
427	(void) strlcat(buf, "...", sizeof buf);
428	(void) strlcat(buf, s + l - m, sizeof buf);
429	return buf;
430}
431