1 /*   tsprintf.c
2 * ===========================================================================
3 *
4 *                            PUBLIC DOMAIN NOTICE
5 *               National Center for Biotechnology Information
6 *
7 *  This software/database is a "United States Government Work" under the
8 *  terms of the United States Copyright Act.  It was written as part of
9 *  the author's official duties as a United States Government employee and
10 *  thus cannot be copyrighted.  This software/database is freely available
11 *  to the public for use. The National Library of Medicine and the U.S.
12 *  Government have not placed any restriction on its use or reproduction.
13 *
14 *  Although all reasonable efforts have been taken to ensure the accuracy
15 *  and reliability of the software and data, the NLM and the U.S.
16 *  Government do not and cannot warrant the performance or results that
17 *  may be obtained by using this software or data. The NLM and the U.S.
18 *  Government disclaim all warranties, express or implied, including
19 *  warranties of performance, merchantability or fitness for any particular
20 *  purpose.
21 *
22 *  Please cite the author in any work or product based on this material.
23 *
24 * ===========================================================================
25 *
26 * File Name:  tsprintf.c
27 *
28 * Author:  Denis Vakatov
29 *
30 * Version Creation Date:   07/10/96
31 *
32 * $Revision: 6.12 $
33 *
34 * File Description:
35 *   	Memory- and MT-safe "sprintf()"
36 *
37 * Modifications:
38 * --------------------------------------------------------------------------
39 *
40 * $Log: tsprintf.c,v $
41 * Revision 6.12  2007/08/02 17:59:11  kans
42 * do_div needed to take Int8 n argument
43 *
44 * Revision 6.11  2007/08/02 16:14:31  ucko
45 * Support "ll"-prefixed arguments (assumed to be [U]Int8).
46 *
47 * Revision 6.10  2003/12/12 23:28:25  dondosha
48 * Correction for Opteron, at suggestion from Nicolas Joly
49 *
50 * Revision 6.9  2002/03/11 16:55:43  ivanov
51 * Fixed fp_count() -- error with round-up numbers
52 *
53 * Revision 6.8  2001/04/17 13:49:55  beloslyu
54 * changes for Linux PPC, contributed by Gary Bader <gary.bader@utoronto.ca>
55 *
56 * Revision 6.7  2001/03/23 14:04:14  beloslyu
57 * fix the name of strnlen to my_strnlen. It appears IBM's AIX has it's own
58 * function with this name
59 *
60 * Revision 6.6  2000/12/28 21:25:14  vakatov
61 * Comment fixed
62 *
63 * Revision 6.5  2000/12/28 21:16:44  vakatov
64 * va_args():  use "int" to fetch a "short int" argument
65 *
66 * Revision 6.4  2000/07/19 20:54:48  vakatov
67 * minor cleanup
68 *
69 * Revision 6.3  1998/01/28 15:57:24  vakatov
70 * Nlm_TSPrintfArgs():  always ignore the "vsprintf()"'s return value;
71 * count the resultant string length using StrLen
72 *
73 * Revision 6.2  1997/12/04 22:05:35  vakatov
74 * Check for NULL string arg;  cut the output instead of crashing the program
75 *
76 * Revision 6.1  1997/11/26 21:26:33  vakatov
77 * Fixed errors and warnings issued by C and C++ (GNU and Sun) compilers
78 *
79 * Revision 6.0  1997/08/25 18:17:40  madden
80 * Revision changed to 6.0
81 *
82 * Revision 1.5  1997/07/15 16:51:48  vakatov
83 * vsprintf_count_args() -- allow "fmt" be NULL(just return 0, don't crash)
84 *
85 * Revision 1.4  1996/12/03 21:48:33  vakatov
86 * Adopted for 32-bit MS-Windows DLLs
87 *
88 * Revision 1.3  1996/07/23  16:23:20  epstein
89 * fix for non-SYSV UNIX systems (e.g., SunOS 4)
90 *
91 * Revision 1.2  1996/07/22  15:27:31  vakatov
92 * Fixed "%c"-formatting bug
93 *
94 * Revision 1.1  1996/07/16  20:02:06  vakatov
95 * Initial revision
96 *
97 * ==========================================================================
98 */
99 
100 
101 #include <string.h>
102 #include <ctype.h>
103 #include <math.h>
104 
105 #include <ncbi.h>
106 
107 #include <tsprintf.h>
108 
109 #ifndef va_copy
110 # ifdef __va_copy
111 #  define va_copy __va_copy
112 # else
113 #  define va_copy(d,s) ((d) = (s))
114 # endif
115 #endif
116 
117 /***********************************************************************
118  *  INTERNAL
119  ***********************************************************************/
120 
my_strnlen(const char * s,size_t count)121 static size_t my_strnlen(const char * s, size_t count)
122 {
123   const char *sc;
124 
125   for (sc = s; count-- && *sc != '\0'; ++sc)
126     /* nothing */;
127   return sc - s;
128 }
129 
130 
131 /* we use this so that we can do without the ctype library */
132 #define is_digit(c)	((c) >= '0' && (c) <= '9')
133 
skip_atoi(const char ** s)134 static int skip_atoi(const char **s)
135 {
136   int i=0;
137 
138   while (is_digit(**s))
139     i = i*10 + *((*s)++) - '0';
140   return i;
141 }
142 
143 
144 #define ZEROPAD	1		/* pad with zero */
145 #define SIGNED	2		/* unsigned/signed long */
146 #define PLUS	4		/* show plus */
147 #define SPACE	8		/* space if plus */
148 #define LEFT	16		/* left justified */
149 #define SPECIAL	32		/* 0x */
150 #define LARGE	64		/* use 'ABCDEF' instead of 'abcdef' */
151 
152 
do_div(Nlm_Int8 * n,int base)153 static int do_div(Nlm_Int8 *n, int base)
154   {
155     int res = ((unsigned long) *n) % (unsigned) base;
156     *n =      ((unsigned long) *n) / (unsigned) base;
157     return res;
158   }
159 
160 
fp_count(double fp,char type,int size,int precision,int flags)161 static int fp_count(double fp, char type, int size, int precision, int flags)
162 {
163   char    xx_type  =  (char)tolower( type );
164   double  fp_abs   =  (fp > 0) ? fp : -fp;
165   double  power10  =  (fp_abs ? log10( fp_abs ) : 0.0);
166   int     counter;
167 
168   if (precision < 0)
169     precision = 6;
170 
171   {{
172   switch ( xx_type )
173     {
174     case 'e':
175       counter = 1 + 1 + precision + 5;
176       break;
177     case 'f':
178       counter = (power10 > 0.0 ? (int)power10 + 1 : 0) + 1 + 1 + precision;
179       break;
180     case 'g':
181       {
182         int e_count = 1 + precision + 5;
183         int f_count = 1 + precision +
184           ((power10 < 0.0) ? (int)(-power10) + 2 : 0);
185         counter = (f_count < e_count) ? f_count : e_count;
186         break;
187       }
188     default:
189       return 0;
190     }
191   }}
192 
193   if (precision == 0)
194     counter--;
195 
196   if (((fp > 0)  &&  (flags & (PLUS|SPACE)))  ||  (fp < 0))
197     counter++;
198 
199   return ((counter > size) ? counter : size);
200 }
201 
202 
number_count(Nlm_Int8 num,int base,int size,int precision,int type)203 static int number_count(Nlm_Int8 num, int base, int size, int precision,
204                         int type)
205 {
206   int counter = 0;
207   int i       = 0;
208 
209   if (type & LEFT)
210     type &= ~ZEROPAD;
211   if (base < 2 || base > 36)
212     return 0;
213 
214   if ((type & (PLUS|SPACE))  &&  (num >= 0))
215     {
216       counter++;
217       size--;
218     }
219 
220   if ((type & SIGNED)  &&  (num < 0))
221     {
222       num = -num;
223       counter++;
224       size--;
225     }
226 
227   if (type & SPECIAL) {
228     if (base == 16)
229       size -= 2;
230     else if (base == 8)
231       size--;
232   }
233 
234   if (num == 0)
235     i++;
236   else while (num != 0)
237     {
238       do_div(&num, base);
239       i++;
240     }
241 
242   if (i > precision)
243     precision = i;
244   size -= precision;
245   if (!(type&(ZEROPAD+LEFT)))
246     while (size-- > 0)
247       counter++;
248   if (type & SPECIAL) {
249     if (base==8)
250       counter++;
251     else if (base==16)
252       counter += 2;
253   }
254 
255   if (!(type & LEFT)) {
256     while (size-- > 0)
257       counter++;
258   }
259   while (i < precision--)
260     counter++;
261   while (i-- > 0)
262     counter++;
263   while (size-- > 0)
264     counter++;
265 
266   return counter;
267 }
268 
269 
vsprintf_count_args(const Char PNTR fmt,va_list args,size_t * cut_fmt)270 static size_t vsprintf_count_args(const Char PNTR fmt, va_list args,
271                                   size_t *cut_fmt)
272 {
273   size_t counter = 0;
274 
275   const Char PNTR start_fmt = fmt;
276   Nlm_Uint8 num;
277   int base;
278   int flags;         /* flags to number() */
279   int field_width;   /* width of output field */
280   int precision;     /* min. # of digits for integers; max
281 			number of chars for from string */
282   int qualifier;     /* 'h', 'l', 'L', or 'q' ("ll") for integer fields */
283 
284   *cut_fmt = 0;
285 
286   if ( !fmt )
287     return 0;
288 
289   for ( ;  *fmt;  fmt++)
290     {
291       if (*fmt != '%')
292         {
293           counter++;
294           continue;
295         }
296 
297       /* process flags */
298       flags = 0;
299     repeat:
300       ++fmt;		/* this also skips first '%' */
301       switch (*fmt) {
302       case '-': flags |= LEFT; goto repeat;
303       case '+': flags |= PLUS; goto repeat;
304       case ' ': flags |= SPACE; goto repeat;
305       case '#': flags |= SPECIAL; goto repeat;
306       case '0': flags |= ZEROPAD; goto repeat;
307       }
308 
309       /* get field width */
310       field_width = -1;
311       if (is_digit(*fmt))
312         field_width = skip_atoi(&fmt);
313       else if (*fmt == '*') {
314         ++fmt;
315         /* it's the next argument */
316         field_width = va_arg(args, int);
317         if (field_width < 0) {
318           field_width = -field_width;
319           flags |= LEFT;
320         }
321       }
322 
323       /* get the precision */
324       precision = -1;
325       if (*fmt == '.') {
326         ++fmt;
327         if (is_digit(*fmt))
328           precision = skip_atoi(&fmt);
329         else if (*fmt == '*') {
330           ++fmt;
331           /* it's the next argument */
332           precision = va_arg(args, int);
333         }
334         if (precision < 0)
335           precision = 0;
336       }
337 
338       /* get the conversion qualifier */
339       qualifier = -1;
340       if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
341         qualifier = *fmt;
342         ++fmt;
343         if (qualifier == 'l' && *fmt == 'l') {
344           qualifier = 'q';
345           ++fmt;
346         }
347       }
348 
349       /* default base */
350       base = 10;
351 
352       switch (*fmt) {
353       case 'c':
354         (void)va_arg(args, int);
355         if (field_width > 0)
356           counter += field_width;
357         else
358           counter++;
359         continue;
360 
361       case 's':
362         {
363           int len;
364           const char *s = va_arg(args, const char *);
365 #ifdef OS_UNIX_PPCLINUX
366 /*
367  *  comment from Gary Bader <gary.bader@utoronto.ca>
368  *	LinuxPPC has problems with using variable arguments in certain cases.
369  *	I'm not completely sure if this is necessary, but it works
370  */
371 			counter += 8;   /*add 8 for PPCLINUX*/
372 #endif
373           if ( !s ) {
374             while (*fmt != '%')
375               fmt--;
376             *cut_fmt = fmt - start_fmt;
377             va_end( args );
378             return counter;
379           }
380           len = my_strnlen(s, precision);
381           if (len < field_width)
382             len = field_width;
383           counter += len;
384         }
385         continue;
386 
387       case 'p':
388         if (field_width == -1) {
389           field_width = 2 * sizeof(void *);
390           flags |= ZEROPAD;
391         }
392         counter += number_count((unsigned long) va_arg(args, void *), 16,
393 			  	field_width, precision, flags);
394         continue;
395 
396       case 'n':
397         if (qualifier == 'l') {
398           long * ip = va_arg(args, long *);
399           *ip = (long)counter;
400         } else if (qualifier == 'q') {
401           Nlm_Uint8 * ip = va_arg(args, Nlm_Uint8 *);
402           *ip = (Nlm_Uint8)counter;
403         } else {
404           int * ip = va_arg(args, int *);
405           *ip = (int)counter;
406         }
407         continue;
408 
409       case 'f':
410       case 'F':
411       case 'e':
412       case 'E':
413       case 'g':
414       case 'G':
415         counter += fp_count(va_arg(args, double), *fmt,
416                             field_width, precision, flags);
417         continue;
418 
419 
420         /* integer number formats - set up the flags and "break" */
421       case 'o':
422         base = 8;
423         break;
424 
425       case 'X':
426       case 'x':
427         base = 16;
428         break;
429 
430       case 'd':
431       case 'i':
432         flags |= SIGNED;
433       case 'u':
434         break;
435 
436       default:
437         if (*fmt != '%')
438           counter++;
439         if (*fmt)
440           counter++;
441         else
442           --fmt;
443         continue;
444       }
445 
446       if (qualifier == 'l')
447         if (flags & SIGNED)
448           num = va_arg(args, long);
449         else
450           num = va_arg(args, unsigned long);
451       else if (qualifier == 'q')
452         num = va_arg(args, Nlm_Uint8);
453       else if (flags & SIGNED) /* plain or 'h' -- short promotes to int */
454         num = va_arg(args, int);
455       else
456         num = va_arg(args, unsigned int);
457       counter += number_count(num, base, field_width, precision, flags);
458     }
459 
460   va_end( args );
461   return counter;
462 }
463 
464 
465 /***********************************************************************
466  *  EXTERNAL
467  ***********************************************************************/
468 
469 #ifdef VAR_ARGS
Nlm_TSPrintf(fmt,va_alist)470 NLM_EXTERN const Char PNTR Nlm_TSPrintf(fmt, va_alist)
471 const Char PNTR fmt;
472 va_dcl
473 #else
474 NLM_EXTERN const Char PNTR Nlm_TSPrintf(const Char PNTR fmt, ...)
475 #endif
476 {
477   va_list args;
478   const Char PNTR str = NULL;
479 
480 #ifdef VAR_ARGS
481   va_start(args);
482 #else
483   va_start(args, fmt);
484 #endif
485 
486   str = Nlm_TSPrintfArgs(fmt, args);
487 
488   va_end(args);
489   return str;
490 }
491 
492 
Nlm_TSPrintfArgs(const Char PNTR fmt,va_list args)493 NLM_EXTERN const Char PNTR Nlm_TSPrintfArgs(const Char PNTR fmt, va_list args)
494 {
495   size_t  parsed_size;
496   int     n_written;
497   CharPtr temp_buf;
498   size_t  cut_fmt;
499   char   *x_fmt;
500   va_list save;
501 
502 #ifdef OS_UNIX_PPCLINUX
503 /*
504  *  comment from Gary Bader <gary.bader@utoronto.ca>
505  *	LinuxPPC has problems with using variable arguments in certain cases.
506  *	I'm not completely sure if this is necessary, but it works
507  */
508   parsed_size = 2048;
509   cut_fmt = 0;
510 #else
511   va_copy(save, args);
512   parsed_size = vsprintf_count_args(fmt, save, &cut_fmt);
513   va_end(save);
514   if (parsed_size == 0)
515     return NULL;
516 #endif
517 
518   temp_buf = Nlm_GetScratchBuffer(parsed_size + 1);
519   if (temp_buf == NULL)
520     return NULL;
521 
522   if ( cut_fmt ) {
523     if ( !(x_fmt = (char *)Malloc(cut_fmt+1)) )
524       return NULL;
525     MemCpy(x_fmt, fmt, cut_fmt);
526     x_fmt[cut_fmt] = '\0';
527   }
528   else
529     x_fmt = (char *)fmt;
530 
531   temp_buf[0] = '\0';
532   vsprintf(temp_buf, x_fmt, args);
533 
534   n_written = StrLen(temp_buf);
535   if (n_written > 0  &&  (size_t)n_written > parsed_size)
536     abort();
537 
538   if ( cut_fmt )
539     Free(x_fmt);
540 
541   return temp_buf;
542 }
543 
544 
545 #ifdef TEST_MODULE_TSPRINTF
546 
547 /***********************************************************************
548  *  TEST
549  ***********************************************************************/
550 
551 #include <stdio.h>
552 
553 
554 #ifdef VAR_ARGS
vsprintf_count(cut_fmt,fmt,va_alist)555 static size_t vsprintf_count(cut_fmt, fmt, va_alist)
556 size_t *cut_fmt;
557 const char *fmt;
558 va_dcl
559 #else
560 static size_t vsprintf_count(size_t *cut_fmt, const Char PNTR fmt, ...)
561 #endif
562 {
563   va_list args;
564   size_t  counter = 0;
565 
566 #ifdef VAR_ARGS
567   va_start(args);
568 #else
569   va_start(args, fmt);
570 #endif
571 
572   counter = vsprintf_count_args(fmt, args, cut_fmt);
573 
574   va_end(args);
575   return counter;
576 }
577 
578 
579 #define test(n,t,v) \
580 {{ \
581   size_t cut_fmt; \
582   int    n_actual; \
583   char  *x_fmt; \
584   char   xx_fmt[1024]; \
585   size_t n_parsed = vsprintf_count(&cut_fmt, fmt[n][t], v);\
586   if (n_parsed  &&  !cut_fmt)  { x_fmt = (char *)fmt[n][t]; } \
587   else { ASSERT( cut_fmt < sizeof(xx_fmt) ); \
588     MemCpy(xx_fmt, fmt[n][t], cut_fmt);  xx_fmt[cut_fmt] = '\0'; x_fmt = xx_fmt; /* printf("::%d  '%s'  '%s'\n", (int)cut_fmt, xx_fmt, fmt[n][t]); */ } \
589   n_actual = sprintf(str, x_fmt, v);\
590   if ((long)n_parsed != (long)n_actual) {\
591     printf("fmt='%s', parsed= %d,\t actual= %d:\t '%s'\n",\
592            fmt[n][t], (int)n_parsed, n_actual, str);\
593     if ((long)n_parsed < (long)n_actual)\
594       abort(); \
595   } \
596   Nlm_ErrPostEx(SEV_WARNING, 7, 3, fmt[n][t], v); \
597 }}
598 
599 
TEST__vsprintf_count(void)600 static Int2 TEST__vsprintf_count( void )
601 {
602   char str[8182];
603   int    i = 11000;
604   long   l = 222222000;
605   float  f = (float)-0.01234567890123456;/*(float)339348798353465673479834573.33385873457487657643583475874e10;*/
606   double d = -0.01234567890123456;/*444439837983783875378.44493759837895639853746365387698374E+100;*/
607   static const char s  [] = "01234567890123456789012345678901234567890";
608   static const char sz [] = "";
609   const  char       *sNULL = NULL;
610   static const char *fmt[10][5] =
611   {
612     "i = %3d",    "l = %5ld",    "f = %8.3f",   "d = %8.3g",   "s = '%s'",
613     "i = % d",    "l = %ld",     "f = %f",      "d = %g",      "s = '%s'",
614     "i = %8.0d",  "l = %-8.0ld", "f = %20.0f",  "d = %20.0g",  "s = '%3s'",
615     "i = %11.3d", "l = %11.3ld", "f = %33.3f",  "d = %33.3g",  "s = '%s'",
616     "i = %-5.5d", "l = %5.5ld",  "f = %10.10f", "d = %10.10g", "s = '%20s'",
617     "",           "l = %+5.1ld", "f = %-9.7f",  "d = %5.10g",  "s = '%s20.3'",
618     "i = %12.6d", "l = %15ld",   "f = %10.5f",  "d = %44.10g", "s = '%19s'",
619     "i = %10.9d", "l = %15.5ld", "f = %22.10f", "d = %16.8g",  "s = '%s'",
620     "i = %6.7d",  "l = %20.9ld", "f = %0.10f",  "d = %22g",    "%8s = s",
621     "i = %11.2d", "l = %+2.7ld", "f = %8.7f",   "d = %33g",    "s = '%-6s'"
622   };
623 
624   size_t n;
625   int iii;
626 
627   {{
628     static char *ptr = "eeeeeee";
629     ErrPostEx(SEV_WARNING, 7, 3, "%c", *ptr);
630   }}
631 
632   for (iii = 0;  iii < 100;  iii++)
633     {
634       for (n = 0;  n < sizeof( fmt ) / sizeof(const char *) / 5;  n++)
635         {
636           printf("Test: %ld\n", (long) n);
637           i = -i;
638           l = -l;
639           f = -f;
640           d = -d;
641           test(n,0,i);
642           test(n,1,l);
643           test(n,2,f);
644           test(n,3,d);
645           test(n,4,s);
646         }
647 /*      scanf("%s", str);*/
648 
649       i = (int)    ((l - f + 100) * d);
650       l = (long)   ((i - d * cos( f ) + 10) * l);
651       f = (float)  ((i - l + f + 888) * sin( d ) + i);
652       d = (double) ((cos( d ) + sin( i )) * f / (l * l + 1000 + f * cos( f )));
653     }
654 
655 
656   printf("\nTest ZERO:\n");
657   i = 0;
658   l = 0;
659   f = (float)0;
660   d = 0;
661   for (n = 0;  n < sizeof( fmt ) / sizeof(const char *) / 5;  n++)
662     {
663       printf("Test: %ld\n", (long) n);
664       test(n,0,i);
665       test(n,1,l);
666       test(n,2,f);
667       test(n,3,d);
668       test(n,4,sz);
669       test(n,4,sNULL);
670     }
671   scanf("%s", str);
672 
673   return 0;
674 }
675 
676 
main()677 int main()
678 {
679   return TEST__vsprintf_count();
680 }
681 
682 #endif  /* TEST_MODULE_TSPRINTF */
683