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