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