1 /*
2 * snprintf.c
3 *
4 * it was snagged from SSH 1.x source
5 *
6 */
7
8 #include "snprintf.h"
9
10 #if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF)
11
12 #include "irc.h"
13 #include "dma.h"
14
15 #define MINUS_FLAG 0x0001
16 #define PLUS_FLAG 0x0002
17 #define SPACE_FLAG 0x0004
18 #define HASH_FLAG 0x0008
19 #define CONV_TO_SHORT 0x0010
20 #define IS_LONG_INT 0x0020
21 #define IS_LONG_DOUBLE 0x0040
22 #define X_UPCASE 0x0080
23 #define IS_NEGATIVE 0x0100
24 #define UNSIGNED_DEC 0x0200
25 #define ZERO_PADDING 0x0400
26
27
28 /* Extract a formatting directive from str. Str must point to a '%'.
29 Returns number of characters used or zero if extraction failed. */
30
31 static int
snprintf_get_directive(const char * str,int * flags,int * width,int * precision,char * format_char,va_list * ap)32 snprintf_get_directive(const char *str, int *flags, int *width,
33 int *precision, char *format_char, va_list *ap)
34 {
35 int length, value;
36 const char *orig_str = str;
37
38 *flags = 0;
39 *width = 0;
40 *precision = 0;
41 *format_char = (char)0;
42
43 if (*str == '%')
44 {
45 /* Get the flags */
46 str++;
47 while (*str == '-' || *str == '+' || *str == ' '
48 || *str == '#' || *str == '0')
49 {
50 switch (*str)
51 {
52 case '-':
53 *flags |= MINUS_FLAG;
54 break;
55 case '+':
56 *flags |= PLUS_FLAG;
57 break;
58 case ' ':
59 *flags |= SPACE_FLAG;
60 break;
61 case '#':
62 *flags |= HASH_FLAG;
63 break;
64 case '0':
65 *flags |= ZERO_PADDING;
66 break;
67 }
68 str++;
69 }
70
71 /* Don't pad left-justified numbers withs zeros */
72 if ((*flags & MINUS_FLAG) && (*flags & ZERO_PADDING))
73 *flags &= ~ZERO_PADDING;
74
75 /* Is width field present? */
76 if (isdigit(*str))
77 {
78 for (value = 0; *str && isdigit(*str); str++)
79 value = 10 * value + *str - '0';
80 *width = value;
81 }
82 else
83 if (*str == '*')
84 {
85 *width = va_arg(*ap, int);
86 str++;
87 }
88
89 /* Is the precision field present? */
90 if (*str == '.')
91 {
92 str++;
93 if (isdigit(*str))
94 {
95 for (value = 0; *str && isdigit(*str); str++)
96 value = 10 * value + *str - '0';
97 *precision = value;
98 }
99 else
100 if (*str == '*')
101 {
102 *precision = va_arg(*ap, int);
103 str++;
104 }
105 else
106 *precision = 0;
107 }
108
109 /* Get the optional type character */
110 if (*str == 'h')
111 {
112 *flags |= CONV_TO_SHORT;
113 str++;
114 }
115 else
116 {
117 if (*str == 'l')
118 {
119 *flags |= IS_LONG_INT;
120 str++;
121 }
122 else
123 {
124 if (*str == 'L')
125 {
126 *flags |= IS_LONG_DOUBLE;
127 str++;
128 }
129 }
130 }
131
132 /* Get and check the formatting character */
133
134 *format_char = *str;
135 str++;
136 length = str - orig_str;
137
138 switch (*format_char)
139 {
140 case 'i': case 'd': case 'o': case 'u': case 'x': case 'X':
141 case 'f': case 'e': case 'E': case 'g': case 'G':
142 case 'c': case 's': case 'p': case 'n':
143 if (*format_char == 'X')
144 *flags |= X_UPCASE;
145 if (*format_char == 'o')
146 *flags |= UNSIGNED_DEC;
147 return length;
148
149 default:
150 return 0;
151 }
152 }
153 else
154 {
155 return 0;
156 }
157 }
158
159 /* Convert a integer from unsigned long int representation
160 to string representation. This will insert prefixes if needed
161 (leading zero for octal and 0x or 0X for hexadecimal) and
162 will write at most buf_size characters to buffer.
163 tmp_buf is used because we want to get correctly truncated
164 results.
165 */
166
167 static int
snprintf_convert_ulong(char * buffer,size_t buf_size,int base,char * digits,unsigned long int ulong_val,int flags,int width,int precision)168 snprintf_convert_ulong(char *buffer, size_t buf_size, int base, char *digits,
169 unsigned long int ulong_val, int flags, int width,
170 int precision)
171 {
172 int tmp_buf_len = 100 + width, len;
173 char *tmp_buf, *tmp_buf_ptr, prefix[2];
174 tmp_buf = (char *)dma_Malloc(tmp_buf_len);
175
176 prefix[0] = '\0';
177 prefix[1] = '\0';
178
179 /* Make tmp_buf_ptr point just past the last char of buffer */
180 tmp_buf_ptr = tmp_buf + tmp_buf_len;
181
182 /* Main conversion loop */
183 do
184 {
185 *--tmp_buf_ptr = digits[ulong_val % base];
186 ulong_val /= base;
187 precision--;
188 }
189 while ((ulong_val != 0 || precision > 0) && tmp_buf_ptr > tmp_buf);
190
191 /* Get the prefix */
192 if (!(flags & IS_NEGATIVE))
193 {
194 if (base == 16 && (flags & HASH_FLAG))
195 {
196 if (flags && X_UPCASE)
197 {
198 prefix[0] = 'x';
199 prefix[1] = '0';
200 }
201 else
202 {
203 prefix[0] = 'X';
204 prefix[1] = '0';
205 }
206 }
207 if (base == 8 && (flags & HASH_FLAG))
208 prefix[0] = '0';
209
210 if (base == 10 && !(flags & UNSIGNED_DEC) && (flags & PLUS_FLAG))
211 prefix[0] = '+';
212 else
213 if (base == 10 && !(flags & UNSIGNED_DEC) && (flags & SPACE_FLAG))
214 prefix[0] = ' ';
215 }
216 else
217 prefix[0] = '-';
218
219 if (prefix[0] != '\0' && tmp_buf_ptr > tmp_buf)
220 {
221 *--tmp_buf_ptr = prefix[0];
222 if (prefix[1] != '\0' && tmp_buf_ptr > tmp_buf)
223 *--tmp_buf_ptr = prefix[1];
224 }
225
226 len = (tmp_buf + tmp_buf_len) - tmp_buf_ptr;
227
228 if (len <= buf_size)
229 {
230 if (len < width)
231 {
232 if (width > (tmp_buf_ptr - tmp_buf))
233 width = (tmp_buf_ptr - tmp_buf);
234 if (flags & MINUS_FLAG)
235 {
236 memcpy(buffer, tmp_buf_ptr, len);
237 memset(buffer + len, (flags & ZERO_PADDING)?'0':' ',
238 width - len);
239 len = width;
240 }
241 else
242 {
243 memset(buffer, (flags & ZERO_PADDING)?'0':' ',
244 width - len);
245 memcpy(buffer + width - len, tmp_buf_ptr, len);
246 len = width;
247 }
248 }
249 else
250 {
251 memcpy(buffer, tmp_buf_ptr, len);
252 }
253 dma_Free(&tmp_buf);
254 return len;
255 }
256 else
257 {
258 memcpy(buffer, tmp_buf_ptr, buf_size);
259 dma_Free(&tmp_buf);
260 return buf_size;
261 }
262 }
263
264 #ifndef KERNEL
265
266 static int
snprintf_convert_float(char * buffer,size_t buf_size,double dbl_val,int flags,int width,int precision,char format_char)267 snprintf_convert_float(char *buffer, size_t buf_size,
268 double dbl_val, int flags, int width,
269 int precision, char format_char)
270 {
271 char print_buf[160], print_buf_len = 0;
272 char format_str[80], *format_str_ptr;
273
274 format_str_ptr = format_str;
275
276 if (width > 155) width = 155;
277 if (precision <= 0)
278 precision = 6;
279 if (precision > 120)
280 precision = 120;
281
282 /* Construct the formatting string and let system's sprintf
283 do the real work. */
284
285 *format_str_ptr++ = '%';
286
287 if (flags & MINUS_FLAG)
288 *format_str_ptr++ = '-';
289 if (flags & PLUS_FLAG)
290 *format_str_ptr++ = '+';
291 if (flags & SPACE_FLAG)
292 *format_str_ptr++ = ' ';
293 if (flags & ZERO_PADDING)
294 *format_str_ptr++ = '0';
295 if (flags & HASH_FLAG)
296 *format_str_ptr++ = '#';
297
298 sprintf(format_str_ptr, "%d.%d", width, precision);
299 format_str_ptr += strlen(format_str_ptr);
300
301 if (flags & IS_LONG_DOUBLE)
302 *format_str_ptr++ = 'L';
303 *format_str_ptr++ = format_char;
304 *format_str_ptr++ = '\0';
305
306 sprintf(print_buf, format_str, dbl_val);
307 print_buf_len = strlen(print_buf);
308
309 if (print_buf_len > buf_size)
310 print_buf_len = buf_size;
311 strncpy(buffer, print_buf, print_buf_len);
312 return print_buf_len;
313 }
314
315 #endif /* KERNEL */
316
317
318 #ifndef HAVE_SNPRINTF
319
320 int
snprintf(char * str,size_t size,const char * format,...)321 snprintf(char *str, size_t size, const char *format, ...)
322 {
323 int ret;
324 va_list ap;
325 va_start(ap, format);
326 ret = vsnprintf(str, size, format, ap);
327 va_end(ap);
328
329 return ret;
330 }
331
332 #endif
333
334
335 /*
336 * if we don't have vsnprintf, include it
337 */
338
339 #ifndef HAVE_VSNPRINTF
340
341 int
vsnprintf(char * str,size_t size,const char * format,va_list ap)342 vsnprintf(char *str, size_t size, const char *format, va_list ap)
343 {
344 int status, left = (int)size - 1;
345 const char *format_ptr = format;
346 int flags, width, precision, i;
347 char format_char, *orig_str = str;
348 int *int_ptr;
349 long int long_val;
350 unsigned long int ulong_val;
351 char *str_val;
352 #ifndef KERNEL
353 double dbl_val;
354 #endif /* KERNEL */
355
356 flags = 0;
357 while (format_ptr < format + strlen(format))
358 {
359 if (*format_ptr == '%')
360 {
361 if (format_ptr[1] == '%' && left > 0)
362 {
363 *str++ = '%';
364 left--;
365 format_ptr += 2;
366 }
367 else
368 {
369 if (left <= 0)
370 {
371 *str = '\0';
372 return size;
373 }
374 else
375 {
376 status = snprintf_get_directive(format_ptr, &flags, &width,
377 &precision, &format_char,
378 &ap);
379 if (status == 0)
380 {
381 *str = '\0';
382 return 0;
383 }
384 else
385 {
386 format_ptr += status;
387 /* Print a formatted argument */
388 switch (format_char)
389 {
390 case 'i': case 'd':
391 /* Convert to unsigned long int before
392 actual conversion to string */
393 if (flags & IS_LONG_INT)
394 long_val = va_arg(ap, long int);
395 else
396 long_val = (long int) va_arg(ap, int);
397
398 if (long_val < 0)
399 {
400 ulong_val = (unsigned long int) -long_val;
401 flags |= IS_NEGATIVE;
402 }
403 else
404 {
405 ulong_val = (unsigned long int) long_val;
406 }
407 status = snprintf_convert_ulong(str, left, 10,
408 "0123456789",
409 ulong_val, flags,
410 width, precision);
411 str += status;
412 left -= status;
413 break;
414
415 case 'x':
416 if (flags & IS_LONG_INT)
417 ulong_val = va_arg(ap, unsigned long int);
418 else
419 ulong_val =
420 (unsigned long int) va_arg(ap, unsigned int);
421
422 status = snprintf_convert_ulong(str, left, 16,
423 "0123456789abcdef",
424 ulong_val, flags,
425 width, precision);
426 str += status;
427 left -= status;
428 break;
429
430 case 'X':
431 if (flags & IS_LONG_INT)
432 ulong_val = va_arg(ap, unsigned long int);
433 else
434 ulong_val =
435 (unsigned long int) va_arg(ap, unsigned int);
436
437 status = snprintf_convert_ulong(str, left, 16,
438 "0123456789ABCDEF",
439 ulong_val, flags,
440 width, precision);
441 str += status;
442 left -= status;
443 break;
444
445 case 'o':
446 if (flags & IS_LONG_INT)
447 ulong_val = va_arg(ap, unsigned long int);
448 else
449 ulong_val =
450 (unsigned long int) va_arg(ap, unsigned int);
451
452 status = snprintf_convert_ulong(str, left, 8,
453 "01234567",
454 ulong_val, flags,
455 width, precision);
456 str += status;
457 left -= status;
458 break;
459
460 case 'u':
461 if (flags & IS_LONG_INT)
462 ulong_val = va_arg(ap, unsigned long int);
463 else
464 ulong_val =
465 (unsigned long int) va_arg(ap, unsigned int);
466
467 status = snprintf_convert_ulong(str, left, 10,
468 "0123456789",
469 ulong_val, flags,
470 width, precision);
471 str += status;
472 left -= status;
473 break;
474
475 case 'p':
476 break;
477
478 case 'c':
479 if (flags & IS_LONG_INT)
480 ulong_val = va_arg(ap, unsigned long int);
481 else
482 ulong_val =
483 (unsigned long int) va_arg(ap, unsigned int);
484 *str++ = (unsigned char)ulong_val;
485 left--;
486 break;
487
488 case 's':
489 str_val = va_arg(ap, char *);
490
491 if (str_val == NULL)
492 str_val = "(null)";
493
494 if (precision == 0)
495 precision = strlen(str_val);
496 else
497 {
498 if (memchr(str_val, 0, precision) != NULL)
499 precision = strlen(str_val);
500 }
501 if (precision > left)
502 precision = left;
503
504 if (width > left)
505 width = left;
506 if (width < precision)
507 width = precision;
508 i = width - precision;
509
510 if (flags & MINUS_FLAG)
511 {
512 strncpy(str, str_val, precision);
513 memset(str + precision,
514 (flags & ZERO_PADDING)?'0':' ', i);
515 }
516 else
517 {
518 memset(str, (flags & ZERO_PADDING)?'0':' ', i);
519 strncpy(str + i, str_val, precision);
520 }
521 str += width;
522 left -= width;
523 break;
524
525 case 'n':
526 int_ptr = va_arg(ap, int *);
527 *int_ptr = str - orig_str;
528 break;
529
530 #ifndef KERNEL
531 case 'f': case 'e': case 'E': case 'g': case 'G':
532 if (flags & IS_LONG_DOUBLE)
533 dbl_val = (double) va_arg(ap, long double);
534 else
535 dbl_val = va_arg(ap, double);
536 status =
537 snprintf_convert_float(str, left, dbl_val, flags,
538 width, precision,
539 format_char);
540 str += status;
541 left -= status;
542 break;
543 #endif /* KERNEL */
544
545 default:
546 break;
547 }
548 }
549 }
550 }
551 }
552 else
553 {
554 if (left > 0)
555 {
556 *str++ = *format_ptr++;
557 left--;
558 }
559 else
560 {
561 *str = '\0';
562 return size;
563 }
564 }
565 }
566 *str = '\0';
567 return size - left - 1;
568 }
569
570 #endif
571
572 #endif
573