xref: /freebsd/contrib/sendmail/libsmutil/snprintf.c (revision 1b6c76a2fe091c74f08427e6c870851025a9cf67)
1 /*
2  * Copyright (c) 1998-2000 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
15 static char id[] = "@(#)$Id: snprintf.c,v 8.27.16.2 2000/09/17 17:04:24 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 $";*/
43 void	sm_dopr();
44 char	*DoprEnd;
45 int	SnprfOverflow;
46 
47 #if !HASSNPRINTF && !SNPRINTF_IS_BROKEN
48 # define sm_snprintf	snprintf
49 # ifndef luna2
50 #  define sm_vsnprintf	vsnprintf
51 extern int	vsnprintf __P((char *, size_t, const char *, va_list));
52 # endif /* ! luna2 */
53 #endif /* !HASSNPRINTF && !SNPRINTF_IS_BROKEN */
54 
55 /* VARARGS3 */
56 int
57 # ifdef __STDC__
58 sm_snprintf(char *str, size_t count, const char *fmt, ...)
59 # else /* __STDC__ */
60 sm_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 
76 int
77 sm_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 && tTd(57, 2))
90 		dprintf("\nvsnprintf overflow, len = %ld, str = %s",
91 			(long) count, shortenstring(str, MAXSHORTSTR));
92 	return strlen(str);
93 }
94 
95 /*
96  * sm_dopr(): poor man's version of doprintf
97  */
98 
99 void fmtstr __P((char *value, int ljust, int len, int zpad, int maxwidth));
100 void fmtnum __P((long value, int base, int dosign, int ljust, int len, int zpad));
101 void dostr __P(( char * , int ));
102 char *output;
103 void dopr_outch __P(( int c ));
104 int	SyslogErrno;
105 
106 void
107 sm_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 
234 void
235 fmtstr(  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 
261 void
262 fmtnum(  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 
321 void
322 dostr( 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 
333 void
334 dopr_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 
365 char *
366 quad_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 
401 char *
402 shortenstring(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