]> andersk Git - openssh.git/blame - openbsd-compat/bsd-snprintf.c
- (djm) Move PAM session initialisation until after fork in sshd. Patch
[openssh.git] / openbsd-compat / bsd-snprintf.c
CommitLineData
2d9a148e 1/**************************************************************
2 * Original:
3 * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
4 * A bombproof version of doprnt (dopr) included.
5 * Sigh. This sort of thing is always nasty do deal with. Note that
6 * the version here does not include floating point...
dad3b556 7 *
2d9a148e 8 * snprintf() is used instead of sprintf() as it does limit checks
9 * for string length. This covers a nasty loophole.
dad3b556 10 *
2d9a148e 11 * The other functions are there to prevent NULL pointers from
12 * causing nast effects.
dad3b556 13 *
2d9a148e 14 * More Recently:
15 * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
16 * This was ugly. It is still ugly. I opted out of floating point
17 * numbers, but the formatter understands just about everything
18 * from the normal C string format, at least as far as I can tell from
19 * the Solaris 2.5 printf(3S) man page.
20 *
21 * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
22 * Ok, added some minimal floating point support, which means this
23 * probably requires libm on most operating systems. Don't yet
24 * support the exponent (e,E) and sigfig (g,G). Also, fmtint()
25 * was pretty badly broken, it just wasn't being exercised in ways
26 * which showed it, so that's been fixed. Also, formated the code
27 * to mutt conventions, and removed dead code left over from the
28 * original. Also, there is now a builtin-test, just compile with:
29 * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
30 * and run snprintf for results.
31 *
32 * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
33 * The PGP code was using unsigned hexadecimal formats.
34 * Unfortunately, unsigned formats simply didn't work.
35 *
36 * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
37 * The original code assumed that both snprintf() and vsnprintf() were
38 * missing. Some systems only have snprintf() but not vsnprintf(), so
39 * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
40 *
f1312c76 41 * Ben Lindstrom <mouring@eviladmin.org> 09/27/00 for OpenSSH
c73b8e3a 42 * Welcome to the world of %lld and %qd support. With other
43 * long long support. This is needed for sftp-server to work
44 * right.
f1312c76 45 *
46 * Ben Lindstrom <mouring@eviladmin.org> 02/12/01 for OpenSSH
47 * Removed all hint of VARARGS stuff and banished it to the void,
48 * and did a bit of KNF style work to make things a bit more
49 * acceptable. Consider stealing from mutt or enlightenment.
2d9a148e 50 **************************************************************/
dad3b556 51
0b202697 52#include "includes.h"
53
54RCSID("$Id$");
dad3b556 55
2d9a148e 56#if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF)
dad3b556 57
f1312c76 58static void
59dopr(char *buffer, size_t maxlen, const char *format, va_list args);
60
61static void
62fmtstr(char *buffer, size_t *currlen, size_t maxlen, char *value, int flags,
63 int min, int max);
64
65static void
66fmtint(char *buffer, size_t *currlen, size_t maxlen, long value, int base,
67 int min, int max, int flags);
68
69static void
70fmtfp(char *buffer, size_t *currlen, size_t maxlen, long double fvalue,
71 int min, int max, int flags);
2d9a148e 72
73/*
74 * dopr(): poor man's version of doprintf
75 */
76
77/* format read states */
78#define DP_S_DEFAULT 0
79#define DP_S_FLAGS 1
80#define DP_S_MIN 2
81#define DP_S_DOT 3
82#define DP_S_MAX 4
83#define DP_S_MOD 5
84#define DP_S_CONV 6
85#define DP_S_DONE 7
86
87/* format flags - Bits */
88#define DP_F_MINUS (1 << 0)
89#define DP_F_PLUS (1 << 1)
90#define DP_F_SPACE (1 << 2)
91#define DP_F_NUM (1 << 3)
92#define DP_F_ZERO (1 << 4)
93#define DP_F_UP (1 << 5)
94#define DP_F_UNSIGNED (1 << 6)
95
96/* Conversion Flags */
c73b8e3a 97#define DP_C_SHORT 1
98#define DP_C_LONG 2
99#define DP_C_LDOUBLE 3
100#define DP_C_LONG_LONG 4
2d9a148e 101
102#define char_to_int(p) (p - '0')
f1312c76 103#define abs_val(p) (p < 0 ? -p : p)
2d9a148e 104
f1312c76 105
106static void
107dopr(char *buffer, size_t maxlen, const char *format, va_list args)
2d9a148e 108{
f1312c76 109 char *strvalue;
110 char ch;
111 long value;
112 long double fvalue;
113 int min = 0;
114 int max = -1;
115 int state = DP_S_DEFAULT;
116 int flags = 0;
117 int cflags = 0;
118 size_t currlen = 0;
2d9a148e 119
2d9a148e 120 ch = *format++;
f1312c76 121
122 while (state != DP_S_DONE) {
123 if ((ch == '\0') || (currlen >= maxlen))
124 state = DP_S_DONE;
125
126 switch(state) {
127 case DP_S_DEFAULT:
128 if (ch == '%')
129 state = DP_S_FLAGS;
130 else
131 dopr_outch(buffer, &currlen, maxlen, ch);
132 ch = *format++;
133 break;
134 case DP_S_FLAGS:
135 switch (ch) {
136 case '-':
137 flags |= DP_F_MINUS;
138 ch = *format++;
139 break;
140 case '+':
141 flags |= DP_F_PLUS;
142 ch = *format++;
143 break;
144 case ' ':
145 flags |= DP_F_SPACE;
146 ch = *format++;
147 break;
148 case '#':
149 flags |= DP_F_NUM;
150 ch = *format++;
151 break;
152 case '0':
153 flags |= DP_F_ZERO;
154 ch = *format++;
155 break;
156 default:
157 state = DP_S_MIN;
158 break;
159 }
160 break;
161 case DP_S_MIN:
162 if (isdigit((unsigned char)ch)) {
163 min = 10*min + char_to_int (ch);
164 ch = *format++;
165 } else if (ch == '*') {
166 min = va_arg (args, int);
167 ch = *format++;
168 state = DP_S_DOT;
169 } else
170 state = DP_S_DOT;
171 break;
172 case DP_S_DOT:
173 if (ch == '.') {
174 state = DP_S_MAX;
175 ch = *format++;
176 } else
177 state = DP_S_MOD;
178 break;
179 case DP_S_MAX:
180 if (isdigit((unsigned char)ch)) {
181 if (max < 0)
182 max = 0;
183 max = 10*max + char_to_int(ch);
184 ch = *format++;
185 } else if (ch == '*') {
186 max = va_arg (args, int);
187 ch = *format++;
188 state = DP_S_MOD;
189 } else
190 state = DP_S_MOD;
191 break;
192 case DP_S_MOD:
193 switch (ch) {
194 case 'h':
195 cflags = DP_C_SHORT;
196 ch = *format++;
197 break;
198 case 'l':
199 cflags = DP_C_LONG;
200 ch = *format++;
201 if (ch == 'l') {
202 cflags = DP_C_LONG_LONG;
203 ch = *format++;
204 }
205 break;
206 case 'q':
207 cflags = DP_C_LONG_LONG;
208 ch = *format++;
209 break;
210 case 'L':
211 cflags = DP_C_LDOUBLE;
212 ch = *format++;
213 break;
214 default:
215 break;
216 }
217 state = DP_S_CONV;
218 break;
219 case DP_S_CONV:
220 switch (ch) {
221 case 'd':
222 case 'i':
223 if (cflags == DP_C_SHORT)
224 value = va_arg(args, int);
225 else if (cflags == DP_C_LONG)
226 value = va_arg(args, long int);
227 else if (cflags == DP_C_LONG_LONG)
228 value = va_arg (args, long long);
229 else
230 value = va_arg (args, int);
231 fmtint(buffer, &currlen, maxlen, value, 10, min, max, flags);
232 break;
233 case 'o':
234 flags |= DP_F_UNSIGNED;
235 if (cflags == DP_C_SHORT)
236 value = va_arg(args, unsigned int);
237 else if (cflags == DP_C_LONG)
238 value = va_arg(args, unsigned long int);
239 else if (cflags == DP_C_LONG_LONG)
240 value = va_arg(args, unsigned long long);
241 else
242 value = va_arg(args, unsigned int);
243 fmtint(buffer, &currlen, maxlen, value, 8, min, max, flags);
244 break;
245 case 'u':
246 flags |= DP_F_UNSIGNED;
247 if (cflags == DP_C_SHORT)
248 value = va_arg(args, unsigned int);
249 else if (cflags == DP_C_LONG)
250 value = va_arg(args, unsigned long int);
251 else if (cflags == DP_C_LONG_LONG)
252 value = va_arg(args, unsigned long long);
253 else
254 value = va_arg(args, unsigned int);
255 fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
256 break;
257 case 'X':
258 flags |= DP_F_UP;
259 case 'x':
260 flags |= DP_F_UNSIGNED;
261 if (cflags == DP_C_SHORT)
262 value = va_arg(args, unsigned int);
263 else if (cflags == DP_C_LONG)
264 value = va_arg(args, unsigned long int);
265 else if (cflags == DP_C_LONG_LONG)
266 value = va_arg(args, unsigned long long);
267 else
268 value = va_arg(args, unsigned int);
269 fmtint(buffer, &currlen, maxlen, value, 16, min, max, flags);
270 break;
271 case 'f':
272 if (cflags == DP_C_LDOUBLE)
273 fvalue = va_arg(args, long double);
274 else
275 fvalue = va_arg(args, double);
276 /* um, floating point? */
277 fmtfp(buffer, &currlen, maxlen, fvalue, min, max, flags);
278 break;
279 case 'E':
280 flags |= DP_F_UP;
281 case 'e':
282 if (cflags == DP_C_LDOUBLE)
283 fvalue = va_arg(args, long double);
284 else
285 fvalue = va_arg(args, double);
286 break;
287 case 'G':
288 flags |= DP_F_UP;
289 case 'g':
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 'c':
296 dopr_outch(buffer, &currlen, maxlen, va_arg(args, int));
297 break;
298 case 's':
299 strvalue = va_arg(args, char *);
300 if (max < 0)
301 max = maxlen; /* ie, no max */
302 fmtstr(buffer, &currlen, maxlen, strvalue, flags, min, max);
303 break;
304 case 'p':
305 strvalue = va_arg(args, void *);
306 fmtint(buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
307 break;
308 case 'n':
309 if (cflags == DP_C_SHORT) {
310 short int *num;
311 num = va_arg(args, short int *);
312 *num = currlen;
313 } else if (cflags == DP_C_LONG) {
314 long int *num;
315 num = va_arg(args, long int *);
316 *num = currlen;
317 } else if (cflags == DP_C_LONG_LONG) {
318 long long *num;
319 num = va_arg(args, long long *);
320 *num = currlen;
321 } else {
322 int *num;
323 num = va_arg(args, int *);
324 *num = currlen;
325 }
326 break;
327 case '%':
328 dopr_outch(buffer, &currlen, maxlen, ch);
329 break;
330 case 'w': /* not supported yet, treat as next char */
331 ch = *format++;
332 break;
333 default: /* Unknown, skip */
334 break;
335 }
336 ch = *format++;
337 state = DP_S_DEFAULT;
338 flags = cflags = min = 0;
339 max = -1;
340 break;
341 case DP_S_DONE:
342 break;
343 default: /* hmm? */
344 break; /* some picky compilers need this */
345 }
346 }
347 if (currlen < maxlen - 1)
348 buffer[currlen] = '\0';
2d9a148e 349 else
f1312c76 350 buffer[maxlen - 1] = '\0';
2d9a148e 351}
352
f1312c76 353static void
354fmtstr(char *buffer, size_t *currlen, size_t maxlen,
355 char *value, int flags, int min, int max)
2d9a148e 356{
f1312c76 357 int padlen, strln; /* amount to pad */
358 int cnt = 0;
2d9a148e 359
f1312c76 360 if (value == 0)
361 value = "<NULL>";
362
363 for (strln = 0; value[strln]; ++strln); /* strlen */
364 padlen = min - strln;
365 if (padlen < 0)
366 padlen = 0;
367 if (flags & DP_F_MINUS)
368 padlen = -padlen; /* Left Justify */
369
370 while ((padlen > 0) && (cnt < max)) {
371 dopr_outch(buffer, currlen, maxlen, ' ');
372 --padlen;
373 ++cnt;
374 }
375 while (*value && (cnt < max)) {
376 dopr_outch(buffer, currlen, maxlen, *value++);
377 ++cnt;
378 }
379 while ((padlen < 0) && (cnt < max)) {
380 dopr_outch(buffer, currlen, maxlen, ' ');
381 ++padlen;
382 ++cnt;
383 }
2d9a148e 384}
385
386/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
387
f1312c76 388static void
389fmtint(char *buffer, size_t *currlen, size_t maxlen,
390 long value, int base, int min, int max, int flags)
2d9a148e 391{
f1312c76 392 unsigned long uvalue;
393 char convert[20];
394 int signvalue = 0;
395 int place = 0;
396 int spadlen = 0; /* amount to space pad */
397 int zpadlen = 0; /* amount to zero pad */
398 int caps = 0;
2d9a148e 399
f1312c76 400 if (max < 0)
401 max = 0;
402
403 uvalue = value;
404
405 if (!(flags & DP_F_UNSIGNED)) {
406 if (value < 0) {
407 signvalue = '-';
408 uvalue = -value;
409 } else if (flags & DP_F_PLUS) /* Do a sign (+/i) */
410 signvalue = '+';
411 else if (flags & DP_F_SPACE)
412 signvalue = ' ';
413 }
2d9a148e 414
f1312c76 415 if (flags & DP_F_UP)
416 caps = 1; /* Should characters be upper case? */
417
418 do {
419 convert[place++] =
420 (caps? "0123456789ABCDEF":"0123456789abcdef")
421 [uvalue % (unsigned)base];
422 uvalue = (uvalue / (unsigned)base );
423 } while (uvalue && (place < 20));
424 if (place == 20)
425 place--;
426 convert[place] = 0;
427
428 zpadlen = max - place;
429 spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
430 if (zpadlen < 0)
431 zpadlen = 0;
432 if (spadlen < 0)
433 spadlen = 0;
434 if (flags & DP_F_ZERO) {
435 zpadlen = MAX(zpadlen, spadlen);
436 spadlen = 0;
437 }
438 if (flags & DP_F_MINUS)
439 spadlen = -spadlen; /* Left Justifty */
440
441
442 /* Spaces */
443 while (spadlen > 0) {
444 dopr_outch(buffer, currlen, maxlen, ' ');
445 --spadlen;
446 }
447
448 /* Sign */
449 if (signvalue)
450 dopr_outch(buffer, currlen, maxlen, signvalue);
451
452 /* Zeros */
453 if (zpadlen > 0) {
454 while (zpadlen > 0) {
455 dopr_outch(buffer, currlen, maxlen, '0');
456 --zpadlen;
457 }
458 }
459
460 /* Digits */
461 while (place > 0)
462 dopr_outch(buffer, currlen, maxlen, convert[--place]);
2d9a148e 463
f1312c76 464 /* Left Justified spaces */
465 while (spadlen < 0) {
466 dopr_outch (buffer, currlen, maxlen, ' ');
467 ++spadlen;
468 }
b6019d68 469}
b6019d68 470
f1312c76 471static long double
472pow10(int exp)
dad3b556 473{
f1312c76 474 long double result = 1;
2d9a148e 475
f1312c76 476 while (exp) {
477 result *= 10;
478 exp--;
479 }
2d9a148e 480
f1312c76 481 return result;
dad3b556 482}
483
f1312c76 484static long
485round(long double value)
dad3b556 486{
f1312c76 487 long intpart = value;
2d9a148e 488
f1312c76 489 value -= intpart;
490 if (value >= 0.5)
491 intpart++;
2d9a148e 492
f1312c76 493 return intpart;
dad3b556 494}
495
f1312c76 496static void
497fmtfp(char *buffer, size_t *currlen, size_t maxlen, long double fvalue,
498 int min, int max, int flags)
dad3b556 499{
f1312c76 500 char iconvert[20];
501 char fconvert[20];
502 int signvalue = 0;
503 int iplace = 0;
504 int fplace = 0;
505 int padlen = 0; /* amount to pad */
506 int zpadlen = 0;
507 int caps = 0;
508 long intpart;
509 long fracpart;
510 long double ufvalue;
2d9a148e 511
f1312c76 512 /*
513 * AIX manpage says the default is 0, but Solaris says the default
514 * is 6, and sprintf on AIX defaults to 6
515 */
516 if (max < 0)
517 max = 6;
518
519 ufvalue = abs_val(fvalue);
520
521 if (fvalue < 0)
522 signvalue = '-';
523 else if (flags & DP_F_PLUS) /* Do a sign (+/i) */
524 signvalue = '+';
525 else if (flags & DP_F_SPACE)
526 signvalue = ' ';
527
528 intpart = ufvalue;
529
530 /*
531 * Sorry, we only support 9 digits past the decimal because of our
532 * conversion method
533 */
534 if (max > 9)
535 max = 9;
536
537 /* We "cheat" by converting the fractional part to integer by
538 * multiplying by a factor of 10
539 */
540 fracpart = round((pow10 (max)) * (ufvalue - intpart));
541
542 if (fracpart >= pow10 (max)) {
543 intpart++;
544 fracpart -= pow10 (max);
545 }
546
547 /* Convert integer part */
548 do {
549 iconvert[iplace++] =
550 (caps? "0123456789ABCDEF":"0123456789abcdef")[intpart % 10];
551 intpart = (intpart / 10);
552 } while(intpart && (iplace < 20));
553 if (iplace == 20)
554 iplace--;
555 iconvert[iplace] = 0;
556
557 /* Convert fractional part */
558 do {
559 fconvert[fplace++] =
560 (caps? "0123456789ABCDEF":"0123456789abcdef")[fracpart % 10];
561 fracpart = (fracpart / 10);
562 } while(fracpart && (fplace < 20));
563 if (fplace == 20)
564 fplace--;
565 fconvert[fplace] = 0;
566
567 /* -1 for decimal point, another -1 if we are printing a sign */
568 padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
569 zpadlen = max - fplace;
570 if (zpadlen < 0)
571 zpadlen = 0;
572 if (padlen < 0)
573 padlen = 0;
574 if (flags & DP_F_MINUS)
575 padlen = -padlen; /* Left Justifty */
576
577 if ((flags & DP_F_ZERO) && (padlen > 0)) {
578 if (signvalue) {
579 dopr_outch(buffer, currlen, maxlen, signvalue);
580 --padlen;
581 signvalue = 0;
582 }
583 while (padlen > 0) {
584 dopr_outch(buffer, currlen, maxlen, '0');
585 --padlen;
586 }
587 }
588 while (padlen > 0) {
589 dopr_outch(buffer, currlen, maxlen, ' ');
590 --padlen;
591 }
592 if (signvalue)
593 dopr_outch(buffer, currlen, maxlen, signvalue);
594
595 while (iplace > 0)
596 dopr_outch(buffer, currlen, maxlen, iconvert[--iplace]);
597
598 /*
599 * Decimal point. This should probably use locale to find the correct
600 * char to print out.
601 */
602 dopr_outch(buffer, currlen, maxlen, '.');
603
604 while (fplace > 0)
605 dopr_outch(buffer, currlen, maxlen, fconvert[--fplace]);
606
607 while (zpadlen > 0) {
608 dopr_outch(buffer, currlen, maxlen, '0');
609 --zpadlen;
610 }
611
612 while (padlen < 0) {
613 dopr_outch(buffer, currlen, maxlen, ' ');
614 ++padlen;
615 }
2d9a148e 616}
617
f1312c76 618static void
619dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c)
2d9a148e 620{
f1312c76 621 if (*currlen < maxlen)
622 buffer[(*currlen)++] = c;
dad3b556 623}
2d9a148e 624#endif /* !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) */
dad3b556 625
2d9a148e 626#ifndef HAVE_VSNPRINTF
f1312c76 627int
628vsnprintf(char *str, size_t count, const char *fmt, va_list args)
dad3b556 629{
f1312c76 630 str[0] = 0;
631 dopr(str, count, fmt, args);
632
633 return(strlen(str));
dad3b556 634}
2d9a148e 635#endif /* !HAVE_VSNPRINTF */
dad3b556 636
2d9a148e 637#ifndef HAVE_SNPRINTF
f1312c76 638int
639snprintf(char *str,size_t count,const char *fmt,...)
847e8865 640{
f1312c76 641 va_list ap;
642
643 va_start(ap, fmt);
644 (void) vsnprintf(str, count, fmt, ap);
645 va_end(ap);
646
647 return(strlen(str));
2d9a148e 648}
649
650#ifdef TEST_SNPRINTF
f1312c76 651int
652main(void)
2d9a148e 653{
f1312c76 654#define LONG_STRING 1024
655 char buf1[LONG_STRING];
656 char buf2[LONG_STRING];
657 char *fp_fmt[] = {
658 "%-1.5f",
659 "%1.5f",
660 "%123.9f",
661 "%10.5f",
662 "% 10.5f",
663 "%+22.9f",
664 "%+4.9f",
665 "%01.3f",
666 "%4f",
667 "%3.1f",
668 "%3.2f",
669 NULL
670 };
671 double fp_nums[] = {
672 -1.5,
673 134.21,
674 91340.2,
675 341.1234,
676 0203.9,
677 0.96,
678 0.996,
679 0.9996,
680 1.996,
681 4.136,
682 0
683 };
684 char *int_fmt[] = {
685 "%-1.5d",
686 "%1.5d",
687 "%123.9d",
688 "%5.5d",
689 "%10.5d",
690 "% 10.5d",
691 "%+22.33d",
692 "%01.3d",
693 "%4d",
694 "%lld",
695 "%qd",
696 NULL
697 };
698 long long int_nums[] = { -1, 134, 91340, 341, 0203, 0, 9999999 };
699 int x, y;
700 int fail = 0;
701 int num = 0;
702
703 printf("Testing snprintf format codes against system sprintf...\n");
704
705 for (x = 0; fp_fmt[x] != NULL ; x++) {
706 for (y = 0; fp_nums[y] != 0 ; y++) {
707 snprintf(buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]);
708 sprintf (buf2, fp_fmt[x], fp_nums[y]);
709 if (strcmp (buf1, buf2)) {
710 printf("snprintf doesn't match Format: %s\n\t"
711 "snprintf = %s\n\tsprintf = %s\n",
712 fp_fmt[x], buf1, buf2);
713 fail++;
714 }
715 num++;
716 }
717 }
718 for (x = 0; int_fmt[x] != NULL ; x++) {
719 for (y = 0; int_nums[y] != 0 ; y++) {
720 snprintf(buf1, sizeof (buf1), int_fmt[x], int_nums[y]);
721 sprintf(buf2, int_fmt[x], int_nums[y]);
722 if (strcmp (buf1, buf2)) {
723 printf("snprintf doesn't match Format: %s\n\t"
724 "snprintf = %s\n\tsprintf = %s\n",
725 int_fmt[x], buf1, buf2);
726 fail++;
727 }
728 num++;
729 }
730 }
731 printf("%d tests failed out of %d.\n", fail, num);
732 return(0);
847e8865 733}
2d9a148e 734#endif /* SNPRINTF_TEST */
847e8865 735
2d9a148e 736#endif /* !HAVE_SNPRINTF */
This page took 0.311072 seconds and 5 git commands to generate.