Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/ldns/compat/snprintf.c
39483 views
1
/* snprintf - compatibility implementation of snprintf, vsnprintf
2
*
3
* Copyright (c) 2013, NLnet Labs. All rights reserved.
4
*
5
* This software is open source.
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions
9
* are met:
10
*
11
* Redistributions of source code must retain the above copyright notice,
12
* this list of conditions and the following disclaimer.
13
*
14
* Redistributions in binary form must reproduce the above copyright notice,
15
* this list of conditions and the following disclaimer in the documentation
16
* and/or other materials provided with the distribution.
17
*
18
* Neither the name of the NLNET LABS nor the names of its contributors may
19
* be used to endorse or promote products derived from this software without
20
* specific prior written permission.
21
*
22
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
28
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
29
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
*/
34
35
#include <ldns/config.h>
36
#include <stdio.h>
37
#include <ctype.h>
38
#include <string.h>
39
#include <stdarg.h>
40
#include <stdlib.h>
41
#include <errno.h>
42
#ifdef HAVE_STDINT_H
43
#include <stdint.h>
44
#endif
45
46
/* for test */
47
/* #define SNPRINTF_TEST 1 */
48
#ifdef SNPRINTF_TEST
49
#define snprintf my_snprintf
50
#define vsnprintf my_vsnprintf
51
#endif /* SNPRINTF_TEST */
52
53
int snprintf(char* str, size_t size, const char* format, ...);
54
int vsnprintf(char* str, size_t size, const char* format, va_list arg);
55
56
/**
57
* Very portable snprintf implementation, limited in functionality,
58
* esp. for %[capital] %[nonportable] and so on. Reduced float functionality,
59
* mostly in formatting and range (e+-16), for %f and %g.
60
*
61
* %s, %d, %u, %i, %x, %c, %n and %% are fully supported.
62
* This includes width, precision, flags 0- +, and *(arg for wid,prec).
63
* %f, %g, %m, %p have reduced support, support for wid,prec,flags,*, but
64
* less floating point range, no %e formatting for %g.
65
*/
66
int snprintf(char* str, size_t size, const char* format, ...)
67
{
68
int r;
69
va_list args;
70
va_start(args, format);
71
r = vsnprintf(str, size, format, args);
72
va_end(args);
73
return r;
74
}
75
76
/** add padding to string */
77
static void
78
print_pad(char** at, size_t* left, int* ret, char p, int num)
79
{
80
while(num--) {
81
if(*left > 1) {
82
*(*at)++ = p;
83
(*left)--;
84
}
85
(*ret)++;
86
}
87
}
88
89
/** get negative symbol, 0 if none */
90
static char
91
get_negsign(int negative, int plus, int space)
92
{
93
if(negative)
94
return '-';
95
if(plus)
96
return '+';
97
if(space)
98
return ' ';
99
return 0;
100
}
101
102
#define PRINT_DEC_BUFSZ 32 /* 20 is enough for 64 bit decimals */
103
/** print decimal into buffer, returns length */
104
static int
105
print_dec(char* buf, int max, unsigned int value)
106
{
107
int i = 0;
108
if(value == 0) {
109
if(max > 0) {
110
buf[0] = '0';
111
i = 1;
112
}
113
} else while(value && i < max) {
114
buf[i++] = '0' + value % 10;
115
value /= 10;
116
}
117
return i;
118
}
119
120
/** print long decimal into buffer, returns length */
121
static int
122
print_dec_l(char* buf, int max, unsigned long value)
123
{
124
int i = 0;
125
if(value == 0) {
126
if(max > 0) {
127
buf[0] = '0';
128
i = 1;
129
}
130
} else while(value && i < max) {
131
buf[i++] = '0' + value % 10;
132
value /= 10;
133
}
134
return i;
135
}
136
137
/** print long decimal into buffer, returns length */
138
static int
139
print_dec_ll(char* buf, int max, unsigned long long value)
140
{
141
int i = 0;
142
if(value == 0) {
143
if(max > 0) {
144
buf[0] = '0';
145
i = 1;
146
}
147
} else while(value && i < max) {
148
buf[i++] = '0' + value % 10;
149
value /= 10;
150
}
151
return i;
152
}
153
154
/** print hex into buffer, returns length */
155
static int
156
print_hex(char* buf, int max, unsigned int value)
157
{
158
const char* h = "0123456789abcdef";
159
int i = 0;
160
if(value == 0) {
161
if(max > 0) {
162
buf[0] = '0';
163
i = 1;
164
}
165
} else while(value && i < max) {
166
buf[i++] = h[value & 0x0f];
167
value >>= 4;
168
}
169
return i;
170
}
171
172
/** print long hex into buffer, returns length */
173
static int
174
print_hex_l(char* buf, int max, unsigned long value)
175
{
176
const char* h = "0123456789abcdef";
177
int i = 0;
178
if(value == 0) {
179
if(max > 0) {
180
buf[0] = '0';
181
i = 1;
182
}
183
} else while(value && i < max) {
184
buf[i++] = h[value & 0x0f];
185
value >>= 4;
186
}
187
return i;
188
}
189
190
/** print long long hex into buffer, returns length */
191
static int
192
print_hex_ll(char* buf, int max, unsigned long long value)
193
{
194
const char* h = "0123456789abcdef";
195
int i = 0;
196
if(value == 0) {
197
if(max > 0) {
198
buf[0] = '0';
199
i = 1;
200
}
201
} else while(value && i < max) {
202
buf[i++] = h[value & 0x0f];
203
value >>= 4;
204
}
205
return i;
206
}
207
208
/** copy string into result, reversed */
209
static void
210
spool_str_rev(char** at, size_t* left, int* ret, const char* buf, int len)
211
{
212
int i = len;
213
while(i) {
214
if(*left > 1) {
215
*(*at)++ = buf[--i];
216
(*left)--;
217
} else --i;
218
(*ret)++;
219
}
220
}
221
222
/** copy string into result */
223
static void
224
spool_str(char** at, size_t* left, int* ret, const char* buf, int len)
225
{
226
int i;
227
for(i=0; i<len; i++) {
228
if(*left > 1) {
229
*(*at)++ = buf[i];
230
(*left)--;
231
}
232
(*ret)++;
233
}
234
}
235
236
/** print number formatted */
237
static void
238
print_num(char** at, size_t* left, int* ret, int minw, int precision,
239
int prgiven, int zeropad, int minus, int plus, int space,
240
int zero, int negative, char* buf, int len)
241
{
242
int w = len; /* excludes minus sign */
243
char s = get_negsign(negative, plus, space);
244
if(minus) {
245
/* left adjust the number into the field, space padding */
246
/* calc numw = [sign][zeroes][number] */
247
int numw = w;
248
if(precision == 0 && zero) numw = 0;
249
if(numw < precision) numw = precision;
250
if(s) numw++;
251
252
/* sign */
253
if(s) print_pad(at, left, ret, s, 1);
254
255
/* number */
256
if(precision == 0 && zero) {
257
/* "" for the number */
258
} else {
259
if(w < precision)
260
print_pad(at, left, ret, '0', precision - w);
261
spool_str_rev(at, left, ret, buf, len);
262
}
263
/* spaces */
264
if(numw < minw)
265
print_pad(at, left, ret, ' ', minw - numw);
266
} else {
267
/* pad on the left of the number */
268
/* calculate numw has width of [sign][zeroes][number] */
269
int numw = w;
270
if(precision == 0 && zero) numw = 0;
271
if(numw < precision) numw = precision;
272
if(!prgiven && zeropad && numw < minw) numw = minw;
273
else if(s) numw++;
274
275
/* pad with spaces */
276
if(numw < minw)
277
print_pad(at, left, ret, ' ', minw - numw);
278
/* print sign (and one less zeropad if so) */
279
if(s) {
280
print_pad(at, left, ret, s, 1);
281
numw--;
282
}
283
/* pad with zeroes */
284
if(w < numw)
285
print_pad(at, left, ret, '0', numw - w);
286
if(precision == 0 && zero)
287
return;
288
/* print the characters for the value */
289
spool_str_rev(at, left, ret, buf, len);
290
}
291
}
292
293
/** print %d and %i */
294
static void
295
print_num_d(char** at, size_t* left, int* ret, int value,
296
int minw, int precision, int prgiven, int zeropad, int minus,
297
int plus, int space)
298
{
299
char buf[PRINT_DEC_BUFSZ];
300
int negative = (value < 0);
301
int zero = (value == 0);
302
int len = print_dec(buf, (int)sizeof(buf),
303
(unsigned int)(negative?-value:value));
304
print_num(at, left, ret, minw, precision, prgiven, zeropad, minus,
305
plus, space, zero, negative, buf, len);
306
}
307
308
/** print %ld and %li */
309
static void
310
print_num_ld(char** at, size_t* left, int* ret, long value,
311
int minw, int precision, int prgiven, int zeropad, int minus,
312
int plus, int space)
313
{
314
char buf[PRINT_DEC_BUFSZ];
315
int negative = (value < 0);
316
int zero = (value == 0);
317
int len = print_dec_l(buf, (int)sizeof(buf),
318
(unsigned long)(negative?-value:value));
319
print_num(at, left, ret, minw, precision, prgiven, zeropad, minus,
320
plus, space, zero, negative, buf, len);
321
}
322
323
/** print %lld and %lli */
324
static void
325
print_num_lld(char** at, size_t* left, int* ret, long long value,
326
int minw, int precision, int prgiven, int zeropad, int minus,
327
int plus, int space)
328
{
329
char buf[PRINT_DEC_BUFSZ];
330
int negative = (value < 0);
331
int zero = (value == 0);
332
int len = print_dec_ll(buf, (int)sizeof(buf),
333
(unsigned long long)(negative?-value:value));
334
print_num(at, left, ret, minw, precision, prgiven, zeropad, minus,
335
plus, space, zero, negative, buf, len);
336
}
337
338
/** print %u */
339
static void
340
print_num_u(char** at, size_t* left, int* ret, unsigned int value,
341
int minw, int precision, int prgiven, int zeropad, int minus,
342
int plus, int space)
343
{
344
char buf[PRINT_DEC_BUFSZ];
345
int negative = 0;
346
int zero = (value == 0);
347
int len = print_dec(buf, (int)sizeof(buf), value);
348
print_num(at, left, ret, minw, precision, prgiven, zeropad, minus,
349
plus, space, zero, negative, buf, len);
350
}
351
352
/** print %lu */
353
static void
354
print_num_lu(char** at, size_t* left, int* ret, unsigned long value,
355
int minw, int precision, int prgiven, int zeropad, int minus,
356
int plus, int space)
357
{
358
char buf[PRINT_DEC_BUFSZ];
359
int negative = 0;
360
int zero = (value == 0);
361
int len = print_dec_l(buf, (int)sizeof(buf), value);
362
print_num(at, left, ret, minw, precision, prgiven, zeropad, minus,
363
plus, space, zero, negative, buf, len);
364
}
365
366
/** print %llu */
367
static void
368
print_num_llu(char** at, size_t* left, int* ret, unsigned long long value,
369
int minw, int precision, int prgiven, int zeropad, int minus,
370
int plus, int space)
371
{
372
char buf[PRINT_DEC_BUFSZ];
373
int negative = 0;
374
int zero = (value == 0);
375
int len = print_dec_ll(buf, (int)sizeof(buf), value);
376
print_num(at, left, ret, minw, precision, prgiven, zeropad, minus,
377
plus, space, zero, negative, buf, len);
378
}
379
380
/** print %x */
381
static void
382
print_num_x(char** at, size_t* left, int* ret, unsigned int value,
383
int minw, int precision, int prgiven, int zeropad, int minus,
384
int plus, int space)
385
{
386
char buf[PRINT_DEC_BUFSZ];
387
int negative = 0;
388
int zero = (value == 0);
389
int len = print_hex(buf, (int)sizeof(buf), value);
390
print_num(at, left, ret, minw, precision, prgiven, zeropad, minus,
391
plus, space, zero, negative, buf, len);
392
}
393
394
/** print %lx */
395
static void
396
print_num_lx(char** at, size_t* left, int* ret, unsigned long value,
397
int minw, int precision, int prgiven, int zeropad, int minus,
398
int plus, int space)
399
{
400
char buf[PRINT_DEC_BUFSZ];
401
int negative = 0;
402
int zero = (value == 0);
403
int len = print_hex_l(buf, (int)sizeof(buf), value);
404
print_num(at, left, ret, minw, precision, prgiven, zeropad, minus,
405
plus, space, zero, negative, buf, len);
406
}
407
408
/** print %llx */
409
static void
410
print_num_llx(char** at, size_t* left, int* ret, unsigned long long value,
411
int minw, int precision, int prgiven, int zeropad, int minus,
412
int plus, int space)
413
{
414
char buf[PRINT_DEC_BUFSZ];
415
int negative = 0;
416
int zero = (value == 0);
417
int len = print_hex_ll(buf, (int)sizeof(buf), value);
418
print_num(at, left, ret, minw, precision, prgiven, zeropad, minus,
419
plus, space, zero, negative, buf, len);
420
}
421
422
/** print %llp */
423
static void
424
print_num_llp(char** at, size_t* left, int* ret, void* value,
425
int minw, int precision, int prgiven, int zeropad, int minus,
426
int plus, int space)
427
{
428
char buf[PRINT_DEC_BUFSZ];
429
int negative = 0;
430
int zero = (value == 0);
431
#if defined(UINTPTR_MAX) && defined(UINT32_MAX) && (UINTPTR_MAX == UINT32_MAX)
432
/* avoid warning about upcast on 32bit systems */
433
unsigned long long llvalue = (unsigned long)value;
434
#else
435
unsigned long long llvalue = (unsigned long long)value;
436
#endif
437
int len = print_hex_ll(buf, (int)sizeof(buf), llvalue);
438
if(zero) {
439
buf[0]=')';
440
buf[1]='l';
441
buf[2]='i';
442
buf[3]='n';
443
buf[4]='(';
444
len = 5;
445
} else {
446
/* put '0x' in front of the (reversed) buffer result */
447
if(len < PRINT_DEC_BUFSZ)
448
buf[len++] = 'x';
449
if(len < PRINT_DEC_BUFSZ)
450
buf[len++] = '0';
451
}
452
print_num(at, left, ret, minw, precision, prgiven, zeropad, minus,
453
plus, space, zero, negative, buf, len);
454
}
455
456
#define PRINT_FLOAT_BUFSZ 64 /* xx.yy with 20.20 about the max */
457
/** spool remainder after the decimal point to buffer, in reverse */
458
static int
459
print_remainder(char* buf, int max, double r, int prec)
460
{
461
unsigned long long cap = 1;
462
unsigned long long value;
463
int len, i;
464
if(prec > 19) prec = 19; /* max we can do */
465
if(max < prec) return 0;
466
for(i=0; i<prec; i++) {
467
cap *= 10;
468
}
469
r *= (double)cap;
470
value = (unsigned long long)r;
471
/* see if we need to round up */
472
if(((unsigned long long)((r - (double)value)*10.0)) >= 5) {
473
value++;
474
/* that might carry to numbers before the comma, if so,
475
* just ignore that rounding. failure because 64bitprintout */
476
if(value >= cap)
477
value = cap-1;
478
}
479
len = print_dec_ll(buf, max, value);
480
while(len < prec) { /* pad with zeroes, e.g. if 0.0012 */
481
buf[len++] = '0';
482
}
483
if(len < max)
484
buf[len++] = '.';
485
return len;
486
}
487
488
/** spool floating point to buffer */
489
static int
490
print_float(char* buf, int max, double value, int prec)
491
{
492
/* as xxx.xxx if prec==0, no '.', with prec decimals after . */
493
/* no conversion for NAN and INF, because we do not want to require
494
linking with -lm. */
495
/* Thus, the conversions use 64bit integers to convert the numbers,
496
* which makes 19 digits before and after the decimal point the max */
497
unsigned long long whole = (unsigned long long)value;
498
double remain = value - (double)whole;
499
int len = 0;
500
if(prec != 0)
501
len = print_remainder(buf, max, remain, prec);
502
len += print_dec_ll(buf+len, max-len, whole);
503
return len;
504
}
505
506
/** print %f */
507
static void
508
print_num_f(char** at, size_t* left, int* ret, double value,
509
int minw, int precision, int prgiven, int zeropad, int minus,
510
int plus, int space)
511
{
512
char buf[PRINT_FLOAT_BUFSZ];
513
int negative = (value < 0);
514
int zero = 0;
515
int len;
516
if(!prgiven) precision = 6;
517
len = print_float(buf, (int)sizeof(buf), negative?-value:value,
518
precision);
519
print_num(at, left, ret, minw, 1, 0, zeropad, minus,
520
plus, space, zero, negative, buf, len);
521
}
522
523
/* rudimentary %g support */
524
static int
525
print_float_g(char* buf, int max, double value, int prec)
526
{
527
unsigned long long whole = (unsigned long long)value;
528
double remain = value - (double)whole;
529
int before = 0;
530
int len = 0;
531
532
/* number of digits before the decimal point */
533
while(whole > 0) {
534
before++;
535
whole /= 10;
536
}
537
whole = (unsigned long long)value;
538
539
if(prec > before && remain != 0.0) {
540
/* see if the last decimals are zero, if so, skip them */
541
len = print_remainder(buf, max, remain, prec-before);
542
while(len > 0 && buf[0]=='0') {
543
memmove(buf, buf+1, --len);
544
}
545
}
546
len += print_dec_ll(buf+len, max-len, whole);
547
return len;
548
}
549
550
551
/** print %g */
552
static void
553
print_num_g(char** at, size_t* left, int* ret, double value,
554
int minw, int precision, int prgiven, int zeropad, int minus,
555
int plus, int space)
556
{
557
char buf[PRINT_FLOAT_BUFSZ];
558
int negative = (value < 0);
559
int zero = 0;
560
int len;
561
if(!prgiven) precision = 6;
562
if(precision == 0) precision = 1;
563
len = print_float_g(buf, (int)sizeof(buf), negative?-value:value,
564
precision);
565
print_num(at, left, ret, minw, 1, 0, zeropad, minus,
566
plus, space, zero, negative, buf, len);
567
}
568
569
570
/** strnlen (compat implementation) */
571
static int
572
my_strnlen(const char* s, int max)
573
{
574
int i;
575
for(i=0; i<max; i++)
576
if(s[i]==0)
577
return i;
578
return max;
579
}
580
581
/** print %s */
582
static void
583
print_str(char** at, size_t* left, int* ret, char* s,
584
int minw, int precision, int prgiven, int minus)
585
{
586
int w;
587
/* with prec: no more than x characters from this string, stop at 0 */
588
if(prgiven)
589
w = my_strnlen(s, precision);
590
else w = (int)strlen(s); /* up to the nul */
591
if(w < minw && !minus)
592
print_pad(at, left, ret, ' ', minw - w);
593
spool_str(at, left, ret, s, w);
594
if(w < minw && minus)
595
print_pad(at, left, ret, ' ', minw - w);
596
}
597
598
/** print %c */
599
static void
600
print_char(char** at, size_t* left, int* ret, int c,
601
int minw, int minus)
602
{
603
if(1 < minw && !minus)
604
print_pad(at, left, ret, ' ', minw - 1);
605
print_pad(at, left, ret, c, 1);
606
if(1 < minw && minus)
607
print_pad(at, left, ret, ' ', minw - 1);
608
}
609
610
611
/**
612
* Print to string.
613
* str: string buffer for result. result will be null terminated.
614
* size: size of the buffer. null is put inside buffer.
615
* format: printf format string.
616
* arg: '...' arguments to print.
617
* returns number of characters. a null is printed after this.
618
* return number of bytes that would have been written
619
* if the buffer had been large enough.
620
*
621
* supported format specifiers:
622
* %s, %u, %d, %x, %i, %f, %g, %c, %p, %n.
623
* length: l, ll (for d, u, x).
624
* precision: 6.6d (for d, u, x)
625
* %f, %g precisions, 0.3f
626
* %20s, '.*s'
627
* and %%.
628
*/
629
int vsnprintf(char* str, size_t size, const char* format, va_list arg)
630
{
631
char* at = str;
632
size_t left = size;
633
int ret = 0;
634
const char* fmt = format;
635
int conv, minw, precision, prgiven, zeropad, minus, plus, space, length;
636
while(*fmt) {
637
/* copy string before % */
638
while(*fmt && *fmt!='%') {
639
if(left > 1) {
640
*at++ = *fmt++;
641
left--;
642
} else fmt++;
643
ret++;
644
}
645
646
/* see if we are at end */
647
if(!*fmt) break;
648
649
/* fetch next argument % designation from format string */
650
fmt++; /* skip the '%' */
651
652
/********************************/
653
/* get the argument designation */
654
/********************************/
655
/* we must do this vararg stuff inside this function for
656
* portability. Hence, get_designation, and print_designation
657
* are not their own functions. */
658
659
/* printout designation:
660
* conversion specifier: x, d, u, s, c, n, m, p
661
* flags: # not supported
662
* 0 zeropad (on the left)
663
* - left adjust (right by default)
664
* ' ' printspace for positive number (in - position).
665
* + alwayssign
666
* fieldwidth: [1-9][0-9]* minimum field width.
667
* if this is * then type int next argument specifies the minwidth.
668
* if this is negative, the - flag is set (with positive width).
669
* precision: period[digits]*, %.2x.
670
* if this is * then type int next argument specifies the precision.
671
* just '.' or negative value means precision=0.
672
* this is mindigits to print for d, i, u, x
673
* this is aftercomma digits for f
674
* this is max number significant digits for g
675
* maxnumber characters to be printed for s
676
* length: 0-none (int), 1-l (long), 2-ll (long long)
677
* notsupported: hh (char), h (short), L (long double), q, j, z, t
678
* Does not support %m$ and *m$ argument designation as array indices.
679
* Does not support %#x
680
*
681
*/
682
minw = 0;
683
precision = 1;
684
prgiven = 0;
685
zeropad = 0;
686
minus = 0;
687
plus = 0;
688
space = 0;
689
length = 0;
690
691
/* get flags in any order */
692
for(;;) {
693
if(*fmt == '0')
694
zeropad = 1;
695
else if(*fmt == '-')
696
minus = 1;
697
else if(*fmt == '+')
698
plus = 1;
699
else if(*fmt == ' ')
700
space = 1;
701
else break;
702
fmt++;
703
}
704
705
/* field width */
706
if(*fmt == '*') {
707
fmt++; /* skip char */
708
minw = va_arg(arg, int);
709
if(minw < 0) {
710
minus = 1;
711
minw = -minw;
712
}
713
} else while(*fmt >= '0' && *fmt <= '9') {
714
minw = minw*10 + (*fmt++)-'0';
715
}
716
717
/* precision */
718
if(*fmt == '.') {
719
fmt++; /* skip period */
720
prgiven = 1;
721
precision = 0;
722
if(*fmt == '*') {
723
fmt++; /* skip char */
724
precision = va_arg(arg, int);
725
if(precision < 0)
726
precision = 0;
727
} else while(*fmt >= '0' && *fmt <= '9') {
728
precision = precision*10 + (*fmt++)-'0';
729
}
730
}
731
732
/* length */
733
if(*fmt == 'l') {
734
fmt++; /* skip char */
735
length = 1;
736
if(*fmt == 'l') {
737
fmt++; /* skip char */
738
length = 2;
739
}
740
}
741
742
/* get the conversion */
743
if(!*fmt) conv = 0;
744
else conv = *fmt++;
745
746
/***********************************/
747
/* print that argument designation */
748
/***********************************/
749
switch(conv) {
750
case 'i':
751
case 'd':
752
if(length == 0)
753
print_num_d(&at, &left, &ret, va_arg(arg, int),
754
minw, precision, prgiven, zeropad, minus, plus, space);
755
else if(length == 1)
756
print_num_ld(&at, &left, &ret, va_arg(arg, long),
757
minw, precision, prgiven, zeropad, minus, plus, space);
758
else if(length == 2)
759
print_num_lld(&at, &left, &ret,
760
va_arg(arg, long long),
761
minw, precision, prgiven, zeropad, minus, plus, space);
762
break;
763
case 'u':
764
if(length == 0)
765
print_num_u(&at, &left, &ret,
766
va_arg(arg, unsigned int),
767
minw, precision, prgiven, zeropad, minus, plus, space);
768
else if(length == 1)
769
print_num_lu(&at, &left, &ret,
770
va_arg(arg, unsigned long),
771
minw, precision, prgiven, zeropad, minus, plus, space);
772
else if(length == 2)
773
print_num_llu(&at, &left, &ret,
774
va_arg(arg, unsigned long long),
775
minw, precision, prgiven, zeropad, minus, plus, space);
776
break;
777
case 'x':
778
if(length == 0)
779
print_num_x(&at, &left, &ret,
780
va_arg(arg, unsigned int),
781
minw, precision, prgiven, zeropad, minus, plus, space);
782
else if(length == 1)
783
print_num_lx(&at, &left, &ret,
784
va_arg(arg, unsigned long),
785
minw, precision, prgiven, zeropad, minus, plus, space);
786
else if(length == 2)
787
print_num_llx(&at, &left, &ret,
788
va_arg(arg, unsigned long long),
789
minw, precision, prgiven, zeropad, minus, plus, space);
790
break;
791
case 's':
792
print_str(&at, &left, &ret, va_arg(arg, char*),
793
minw, precision, prgiven, minus);
794
break;
795
case 'c':
796
print_char(&at, &left, &ret, va_arg(arg, int),
797
minw, minus);
798
break;
799
case 'n':
800
*va_arg(arg, int*) = ret;
801
break;
802
case 'm':
803
print_str(&at, &left, &ret, strerror(errno),
804
minw, precision, prgiven, minus);
805
break;
806
case 'p':
807
print_num_llp(&at, &left, &ret, va_arg(arg, void*),
808
minw, precision, prgiven, zeropad, minus, plus, space);
809
break;
810
case '%':
811
print_pad(&at, &left, &ret, '%', 1);
812
break;
813
case 'f':
814
print_num_f(&at, &left, &ret, va_arg(arg, double),
815
minw, precision, prgiven, zeropad, minus, plus, space);
816
break;
817
case 'g':
818
print_num_g(&at, &left, &ret, va_arg(arg, double),
819
minw, precision, prgiven, zeropad, minus, plus, space);
820
break;
821
/* unknown */
822
default:
823
case 0: break;
824
}
825
}
826
827
/* zero terminate */
828
if(left > 0)
829
*at = 0;
830
return ret;
831
}
832
833
#ifdef SNPRINTF_TEST
834
835
/** do tests */
836
#undef snprintf
837
#define DOTEST(bufsz, result, retval, ...) do { \
838
char buf[bufsz]; \
839
printf("now test %s\n", #__VA_ARGS__); \
840
int r=my_snprintf(buf, sizeof(buf), __VA_ARGS__); \
841
if(r != retval || strcmp(buf, result) != 0) { \
842
printf("error test(%s) was \"%s\":%d\n", \
843
""#bufsz", "#result", "#retval", "#__VA_ARGS__, \
844
buf, r); \
845
exit(1); \
846
} \
847
r=snprintf(buf, sizeof(buf), __VA_ARGS__); \
848
if(r != retval || strcmp(buf, result) != 0) { \
849
printf("error test(%s) differs with system, \"%s\":%d\n", \
850
""#bufsz", "#result", "#retval", "#__VA_ARGS__, \
851
buf, r); \
852
exit(1); \
853
} \
854
printf("test(\"%s\":%d) passed\n", buf, r); \
855
} while(0);
856
857
/** test program */
858
int main(void)
859
{
860
int x = 0;
861
862
/* bufsize, expectedstring, expectedretval, snprintf arguments */
863
DOTEST(1024, "hello", 5, "hello");
864
DOTEST(1024, "h", 1, "h");
865
/* warning from gcc for format string, but it does work
866
* DOTEST(1024, "", 0, ""); */
867
868
DOTEST(3, "he", 5, "hello");
869
DOTEST(1, "", 7, "%d", 7823089);
870
871
/* test positive numbers */
872
DOTEST(1024, "0", 1, "%d", 0);
873
DOTEST(1024, "1", 1, "%d", 1);
874
DOTEST(1024, "9", 1, "%d", 9);
875
DOTEST(1024, "15", 2, "%d", 15);
876
DOTEST(1024, "ab15cd", 6, "ab%dcd", 15);
877
DOTEST(1024, "167", 3, "%d", 167);
878
DOTEST(1024, "7823089", 7, "%d", 7823089);
879
DOTEST(1024, " 12", 3, "%3d", 12);
880
DOTEST(1024, "012", 3, "%.3d", 12);
881
DOTEST(1024, "012", 3, "%3.3d", 12);
882
DOTEST(1024, "012", 3, "%03d", 12);
883
DOTEST(1024, " 012", 4, "%4.3d", 12);
884
DOTEST(1024, "", 0, "%.0d", 0);
885
886
/* test negative numbers */
887
DOTEST(1024, "-1", 2, "%d", -1);
888
DOTEST(1024, "-12", 3, "%3d", -12);
889
DOTEST(1024, " -2", 3, "%3d", -2);
890
DOTEST(1024, "-012", 4, "%.3d", -12);
891
DOTEST(1024, "-012", 4, "%3.3d", -12);
892
DOTEST(1024, "-012", 4, "%4.3d", -12);
893
DOTEST(1024, " -012", 5, "%5.3d", -12);
894
DOTEST(1024, "-12", 3, "%03d", -12);
895
DOTEST(1024, "-02", 3, "%03d", -2);
896
DOTEST(1024, "-15", 3, "%d", -15);
897
DOTEST(1024, "-7307", 5, "%d", -7307);
898
DOTEST(1024, "-12 ", 5, "%-5d", -12);
899
DOTEST(1024, "-00012", 6, "%-.5d", -12);
900
901
/* test + and space flags */
902
DOTEST(1024, "+12", 3, "%+d", 12);
903
DOTEST(1024, " 12", 3, "% d", 12);
904
905
/* test %u */
906
DOTEST(1024, "12", 2, "%u", 12);
907
DOTEST(1024, "0", 1, "%u", 0);
908
DOTEST(1024, "4294967295", 10, "%u", 0xffffffff);
909
910
/* test %x */
911
DOTEST(1024, "0", 1, "%x", 0);
912
DOTEST(1024, "c", 1, "%x", 12);
913
DOTEST(1024, "12ab34cd", 8, "%x", 0x12ab34cd);
914
915
/* test %llu, %lld */
916
DOTEST(1024, "18446744073709551615", 20, "%llu",
917
(long long)0xffffffffffffffff);
918
DOTEST(1024, "-9223372036854775808", 20, "%lld",
919
(long long)0x8000000000000000);
920
DOTEST(1024, "9223372036854775808", 19, "%llu",
921
(long long)0x8000000000000000);
922
923
/* test %s */
924
DOTEST(1024, "hello", 5, "%s", "hello");
925
DOTEST(1024, " hello", 10, "%10s", "hello");
926
DOTEST(1024, "hello ", 10, "%-10s", "hello");
927
DOTEST(1024, "he", 2, "%.2s", "hello");
928
DOTEST(1024, " he", 4, "%4.2s", "hello");
929
DOTEST(1024, " h", 4, "%4.2s", "h");
930
931
/* test %c */
932
DOTEST(1024, "a", 1, "%c", 'a');
933
/* warning from gcc for format string, but it does work
934
DOTEST(1024, " a", 5, "%5c", 'a');
935
DOTEST(1024, "a", 1, "%.0c", 'a'); */
936
937
/* test %n */
938
DOTEST(1024, "hello", 5, "hello%n", &x);
939
if(x != 5) { printf("the %%n failed\n"); exit(1); }
940
941
/* test %m */
942
errno = 0;
943
DOTEST(1024, "Success", 7, "%m");
944
945
/* test %p */
946
DOTEST(1024, "0x10", 4, "%p", (void*)0x10);
947
DOTEST(1024, "(nil)", 5, "%p", (void*)0x0);
948
949
/* test %% */
950
DOTEST(1024, "%", 1, "%%");
951
952
/* test %f */
953
DOTEST(1024, "0.000000", 8, "%f", 0.0);
954
DOTEST(1024, "0.00", 4, "%.2f", 0.0);
955
/* differs, "-0.00" DOTEST(1024, "0.00", 4, "%.2f", -0.0); */
956
DOTEST(1024, "234.00", 6, "%.2f", 234.005);
957
DOTEST(1024, "8973497.1246", 12, "%.4f", 8973497.12456);
958
DOTEST(1024, "-12.000000", 10, "%f", -12.0);
959
DOTEST(1024, "6", 1, "%.0f", 6.0);
960
961
DOTEST(1024, "6", 1, "%g", 6.0);
962
DOTEST(1024, "6.1", 3, "%g", 6.1);
963
DOTEST(1024, "6.15", 4, "%g", 6.15);
964
965
/* These format strings are from the code of NSD, Unbound, ldns */
966
967
DOTEST(1024, "abcdef", 6, "%s", "abcdef");
968
DOTEST(1024, "005", 3, "%03u", 5);
969
DOTEST(1024, "12345", 5, "%03u", 12345);
970
DOTEST(1024, "5", 1, "%d", 5);
971
DOTEST(1024, "(nil)", 5, "%p", NULL);
972
DOTEST(1024, "12345", 5, "%ld", (long)12345);
973
DOTEST(1024, "12345", 5, "%lu", (long)12345);
974
DOTEST(1024, " 12345", 12, "%12u", (unsigned)12345);
975
DOTEST(1024, "12345", 5, "%u", (unsigned)12345);
976
DOTEST(1024, "12345", 5, "%llu", (unsigned long long)12345);
977
DOTEST(1024, "12345", 5, "%x", 0x12345);
978
DOTEST(1024, "12345", 5, "%llx", (long long)0x12345);
979
DOTEST(1024, "012345", 6, "%6.6d", 12345);
980
DOTEST(1024, "012345", 6, "%6.6u", 12345);
981
DOTEST(1024, "1234.54", 7, "%g", 1234.54);
982
DOTEST(1024, "123456789.54", 12, "%.12g", 123456789.54);
983
DOTEST(1024, "3456789123456.54", 16, "%.16g", 3456789123456.54);
984
/* %24g does not work with 24 digits, not enough accuracy,
985
* the first 16 digits are correct */
986
DOTEST(1024, "12345", 5, "%3.3d", 12345);
987
DOTEST(1024, "000", 3, "%3.3d", 0);
988
DOTEST(1024, "001", 3, "%3.3d", 1);
989
DOTEST(1024, "012", 3, "%3.3d", 12);
990
DOTEST(1024, "-012", 4, "%3.3d", -12);
991
DOTEST(1024, "he", 2, "%.2s", "hello");
992
DOTEST(1024, "helloworld", 10, "%s%s", "hello", "world");
993
DOTEST(1024, "he", 2, "%.*s", 2, "hello");
994
DOTEST(1024, " hello", 7, "%*s", 7, "hello");
995
DOTEST(1024, "hello ", 7, "%*s", -7, "hello");
996
DOTEST(1024, "0", 1, "%c", '0');
997
DOTEST(1024, "A", 1, "%c", 'A');
998
DOTEST(1024, "", 1, "%c", 0);
999
DOTEST(1024, "\010", 1, "%c", 8);
1000
DOTEST(1024, "%", 1, "%%");
1001
DOTEST(1024, "0a", 2, "%02x", 0x0a);
1002
DOTEST(1024, "bd", 2, "%02x", 0xbd);
1003
DOTEST(1024, "12", 2, "%02ld", (long)12);
1004
DOTEST(1024, "02", 2, "%02ld", (long)2);
1005
DOTEST(1024, "02", 2, "%02u", (unsigned)2);
1006
DOTEST(1024, "765432", 6, "%05u", (unsigned)765432);
1007
DOTEST(1024, "10.234", 6, "%0.3f", 10.23421);
1008
DOTEST(1024, "123456.234", 10, "%0.3f", 123456.23421);
1009
DOTEST(1024, "123456789.234", 13, "%0.3f", 123456789.23421);
1010
DOTEST(1024, "123456.23", 9, "%.2f", 123456.23421);
1011
DOTEST(1024, "123456", 6, "%.0f", 123456.23421);
1012
DOTEST(1024, "0123", 4, "%.4x", 0x0123);
1013
DOTEST(1024, "00000123", 8, "%.8x", 0x0123);
1014
DOTEST(1024, "ffeb0cde", 8, "%.8x", 0xffeb0cde);
1015
DOTEST(1024, " 987654321", 10, "%10lu", (unsigned long)987654321);
1016
DOTEST(1024, " 987654321", 12, "%12lu", (unsigned long)987654321);
1017
DOTEST(1024, "987654321", 9, "%i", 987654321);
1018
DOTEST(1024, "-87654321", 9, "%i", -87654321);
1019
DOTEST(1024, "hello ", 16, "%-16s", "hello");
1020
DOTEST(1024, " ", 16, "%-16s", "");
1021
DOTEST(1024, "a ", 16, "%-16s", "a");
1022
DOTEST(1024, "foobarfoobar ", 16, "%-16s", "foobarfoobar");
1023
DOTEST(1024, "foobarfoobarfoobar", 18, "%-16s", "foobarfoobarfoobar");
1024
1025
/* combined expressions */
1026
DOTEST(1024, "foo 1.0 size 512 edns", 21,
1027
"foo %s size %d %s%s", "1.0", 512, "", "edns");
1028
DOTEST(15, "foo 1.0 size 5", 21,
1029
"foo %s size %d %s%s", "1.0", 512, "", "edns");
1030
DOTEST(1024, "packet 1203ceff id", 18,
1031
"packet %2.2x%2.2x%2.2x%2.2x id", 0x12, 0x03, 0xce, 0xff);
1032
DOTEST(1024, "/tmp/testbound_123abcd.tmp", 26, "/tmp/testbound_%u%s%s.tmp", 123, "ab", "cd");
1033
1034
return 0;
1035
}
1036
#endif /* SNPRINTF_TEST */
1037
1038