1 /*
2 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
7 *
8 * See the COPYRIGHT file distributed with this work for additional
9 * information regarding copyright ownership.
10 */
11
12 #include <config.h>
13
14 #include <ctype.h>
15 #include <inttypes.h>
16 #include <stdio.h> /* for sprintf */
17 #include <string.h>
18
19 #define LWRES__PRINT_SOURCE /* Used to get the lwres_print_* prototypes. */
20
21 #include <lwres/stdlib.h>
22 #include <lwres/string.h>
23
24 #include "assert_p.h"
25 #include "print_p.h"
26
27 int
lwres__print_sprintf(char * str,const char * format,...)28 lwres__print_sprintf(char *str, const char *format, ...) {
29 va_list ap;
30
31 va_start(ap, format);
32 vsprintf(str, format, ap);
33 va_end(ap);
34 return (strlen(str));
35 }
36
37 /*
38 * Return length of string that would have been written if not truncated.
39 */
40
41 int
lwres__print_snprintf(char * str,size_t size,const char * format,...)42 lwres__print_snprintf(char *str, size_t size, const char *format, ...) {
43 va_list ap;
44 int ret;
45
46 va_start(ap, format);
47 ret = vsnprintf(str, size, format, ap);
48 va_end(ap);
49 return (ret);
50
51 }
52
53 /*
54 * Return length of string that would have been written if not truncated.
55 */
56
57 int
lwres__print_vsnprintf(char * str,size_t size,const char * format,va_list ap)58 lwres__print_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
59 int h;
60 int l;
61 int q;
62 int z;
63 int alt;
64 int zero;
65 int left;
66 int plus;
67 int space;
68 long long tmpi;
69 unsigned long long tmpui;
70 unsigned long width;
71 unsigned long precision;
72 unsigned int length;
73 char buf[1024];
74 char c;
75 void *v;
76 char *save = str;
77 const char *cp;
78 const char *head;
79 int count = 0;
80 int pad;
81 int zeropad;
82 int dot;
83 double dbl;
84 #ifdef HAVE_LONG_DOUBLE
85 long double ldbl;
86 #endif
87 char fmt[32];
88
89 INSIST(str != NULL);
90 INSIST(format != NULL);
91
92 while (*format != '\0') {
93 if (*format != '%') {
94 if (size > 1U) {
95 *str++ = *format;
96 size--;
97 }
98 count++;
99 format++;
100 continue;
101 }
102 format++;
103
104 /*
105 * Reset flags.
106 */
107 dot = space = plus = left = zero = alt = h = l = q = z = 0;
108 width = precision = 0;
109 head = "";
110 length = pad = zeropad = 0;
111 POST(length);
112
113 do {
114 if (*format == '#') {
115 alt = 1;
116 format++;
117 } else if (*format == '-') {
118 left = 1;
119 zero = 0;
120 format++;
121 } else if (*format == ' ') {
122 if (!plus)
123 space = 1;
124 format++;
125 } else if (*format == '+') {
126 plus = 1;
127 space = 0;
128 format++;
129 } else if (*format == '0') {
130 if (!left)
131 zero = 1;
132 format++;
133 } else
134 break;
135 } while (1);
136
137 /*
138 * Width.
139 */
140 if (*format == '*') {
141 width = va_arg(ap, int);
142 format++;
143 } else if (isdigit((unsigned char)*format)) {
144 char *e;
145 width = strtoul(format, &e, 10);
146 format = e;
147 }
148
149 /*
150 * Precision.
151 */
152 if (*format == '.') {
153 format++;
154 dot = 1;
155 if (*format == '*') {
156 precision = va_arg(ap, int);
157 format++;
158 } else if (isdigit((unsigned char)*format)) {
159 char *e;
160 precision = strtoul(format, &e, 10);
161 format = e;
162 }
163 }
164
165 switch (*format) {
166 case '\0':
167 continue;
168 case '%':
169 if (size > 1U) {
170 *str++ = *format;
171 size--;
172 }
173 count++;
174 break;
175 case 'q':
176 q = 1;
177 format++;
178 goto doint;
179 case 'h':
180 h = 1;
181 format++;
182 goto doint;
183 case 'l':
184 l = 1;
185 format++;
186 if (*format == 'l') {
187 q = 1;
188 format++;
189 }
190 goto doint;
191 case 'z':
192 z = 1;
193 format++;
194 goto doint;
195 case 'n':
196 case 'i':
197 case 'd':
198 case 'o':
199 case 'u':
200 case 'x':
201 case 'X':
202 doint:
203 if (precision != 0U)
204 zero = 0;
205 switch (*format) {
206 case 'n':
207 if (h) {
208 short int *p;
209 p = va_arg(ap, short *);
210 REQUIRE(p != NULL);
211 *p = str - save;
212 } else if (l) {
213 long int *p;
214 p = va_arg(ap, long *);
215 REQUIRE(p != NULL);
216 *p = str - save;
217 } else if (z) {
218 size_t *p;
219 p = va_arg(ap, size_t *);
220 REQUIRE(p != NULL);
221 *p = str - save;
222 } else {
223 int *p;
224 p = va_arg(ap, int *);
225 REQUIRE(p != NULL);
226 *p = str - save;
227 }
228 break;
229 case 'i':
230 case 'd':
231 if (q)
232 tmpi = va_arg(ap, long long int);
233 else if (l)
234 tmpi = va_arg(ap, long int);
235 else if (z)
236 tmpi = va_arg(ap, size_t);
237 else
238 tmpi = va_arg(ap, int);
239 if (tmpi < 0) {
240 head = "-";
241 tmpui = -tmpi;
242 } else {
243 if (plus)
244 head = "+";
245 else if (space)
246 head = " ";
247 else
248 head = "";
249 tmpui = tmpi;
250 }
251 sprintf(buf, "%llu",
252 tmpui);
253 goto printint;
254 case 'o':
255 if (q)
256 tmpui = va_arg(ap,
257 unsigned long long int);
258 else if (l)
259 tmpui = va_arg(ap, long int);
260 else if (z)
261 tmpui = va_arg(ap, size_t);
262 else
263 tmpui = va_arg(ap, int);
264 sprintf(buf,
265 alt ? "%#llo"
266 : "%llo",
267 tmpui);
268 goto printint;
269 case 'u':
270 if (q)
271 tmpui = va_arg(ap,
272 unsigned long long int);
273 else if (l)
274 tmpui = va_arg(ap, unsigned long int);
275 else if (z)
276 tmpui = va_arg(ap, size_t);
277 else
278 tmpui = va_arg(ap, unsigned int);
279 sprintf(buf, "%llu",
280 tmpui);
281 goto printint;
282 case 'x':
283 if (q)
284 tmpui = va_arg(ap,
285 unsigned long long int);
286 else if (l)
287 tmpui = va_arg(ap, unsigned long int);
288 else if (z)
289 tmpui = va_arg(ap, size_t);
290 else
291 tmpui = va_arg(ap, unsigned int);
292 if (alt) {
293 head = "0x";
294 if (precision > 2U)
295 precision -= 2;
296 }
297 sprintf(buf, "%llx",
298 tmpui);
299 goto printint;
300 case 'X':
301 if (q)
302 tmpui = va_arg(ap,
303 unsigned long long int);
304 else if (l)
305 tmpui = va_arg(ap, unsigned long int);
306 else if (z)
307 tmpui = va_arg(ap, size_t);
308 else
309 tmpui = va_arg(ap, unsigned int);
310 if (alt) {
311 head = "0X";
312 if (precision > 2U)
313 precision -= 2;
314 }
315 sprintf(buf, "%llX",
316 tmpui);
317 goto printint;
318 printint:
319 if (precision != 0U || width != 0U) {
320 length = strlen(buf);
321 if (length < precision)
322 zeropad = precision - length;
323 else if (length < width && zero)
324 zeropad = width - length;
325 if (width != 0U) {
326 pad = width - length -
327 zeropad - strlen(head);
328 if (pad < 0)
329 pad = 0;
330 }
331 }
332 count += strlen(head) + strlen(buf) + pad +
333 zeropad;
334 if (!left) {
335 while (pad > 0 && size > 1U) {
336 *str++ = ' ';
337 size--;
338 pad--;
339 }
340 }
341 cp = head;
342 while (*cp != '\0' && size > 1U) {
343 *str++ = *cp++;
344 size--;
345 }
346 while (zeropad > 0 && size > 1U) {
347 *str++ = '0';
348 size--;
349 zeropad--;
350 }
351 cp = buf;
352 while (*cp != '\0' && size > 1U) {
353 *str++ = *cp++;
354 size--;
355 }
356 while (pad > 0 && size > 1U) {
357 *str++ = ' ';
358 size--;
359 pad--;
360 }
361 break;
362 default:
363 break;
364 }
365 break;
366 case 's':
367 cp = va_arg(ap, char *);
368 REQUIRE(cp != NULL);
369
370 if (precision != 0U) {
371 /*
372 * cp need not be NULL terminated.
373 */
374 const char *tp;
375 unsigned long n;
376
377 n = precision;
378 tp = cp;
379 while (n != 0U && *tp != '\0')
380 n--, tp++;
381 length = precision - n;
382 } else {
383 length = strlen(cp);
384 }
385 if (width != 0U) {
386 pad = width - length;
387 if (pad < 0)
388 pad = 0;
389 }
390 count += pad + length;
391 if (!left)
392 while (pad > 0 && size > 1U) {
393 *str++ = ' ';
394 size--;
395 pad--;
396 }
397 if (precision != 0U)
398 while (precision > 0U && *cp != '\0' &&
399 size > 1U) {
400 *str++ = *cp++;
401 size--;
402 precision--;
403 }
404 else
405 while (*cp != '\0' && size > 1U) {
406 *str++ = *cp++;
407 size--;
408 }
409 while (pad > 0 && size > 1U) {
410 *str++ = ' ';
411 size--;
412 pad--;
413 }
414 break;
415 case 'c':
416 c = va_arg(ap, int);
417 if (width > 0U) {
418 count += width;
419 width--;
420 if (left) {
421 *str++ = c;
422 size--;
423 }
424 while (width-- > 0U && size > 1U) {
425 *str++ = ' ';
426 size--;
427 }
428 if (!left && size > 1U) {
429 *str++ = c;
430 size--;
431 }
432 } else {
433 count++;
434 if (size > 1U) {
435 *str++ = c;
436 size--;
437 }
438 }
439 break;
440 case 'p':
441 v = va_arg(ap, void *);
442 sprintf(buf, "%p", v);
443 length = strlen(buf);
444 if (precision > length)
445 zeropad = precision - length;
446 if (width > 0U) {
447 pad = width - length - zeropad;
448 if (pad < 0)
449 pad = 0;
450 }
451 count += length + pad + zeropad;
452 if (!left)
453 while (pad > 0 && size > 1U) {
454 *str++ = ' ';
455 size--;
456 pad--;
457 }
458 cp = buf;
459 if (zeropad > 0 && buf[0] == '0' &&
460 (buf[1] == 'x' || buf[1] == 'X')) {
461 if (size > 1U) {
462 *str++ = *cp++;
463 size--;
464 }
465 if (size > 1U) {
466 *str++ = *cp++;
467 size--;
468 }
469 while (zeropad > 0 && size > 1U) {
470 *str++ = '0';
471 size--;
472 zeropad--;
473 }
474 }
475 while (*cp != '\0' && size > 1U) {
476 *str++ = *cp++;
477 size--;
478 }
479 while (pad > 0 && size > 1U) {
480 *str++ = ' ';
481 size--;
482 pad--;
483 }
484 break;
485
486 case 'D': /*deprecated*/
487 /* cppcheck-suppress literalWithCharPtrCompare */
488 INSIST("use %ld instead of %D" == NULL);
489 break;
490 case 'O': /*deprecated*/
491 /* cppcheck-suppress literalWithCharPtrCompare */
492 INSIST("use %lo instead of %O" == NULL);
493 break;
494 case 'U': /*deprecated*/
495 /* cppcheck-suppress literalWithCharPtrCompare */
496 INSIST("use %lu instead of %U" == NULL);
497 break;
498
499 case 'L':
500 #ifdef HAVE_LONG_DOUBLE
501 l = 1;
502 #else
503 /* cppcheck-suppress literalWithCharPtrCompare */
504 INSIST("long doubles are not supported" == NULL);
505 #endif
506 /* FALLTHROUGH */
507 case 'e':
508 case 'E':
509 case 'f':
510 case 'g':
511 case 'G':
512 if (!dot)
513 precision = 6;
514 /*
515 * IEEE floating point.
516 * MIN 2.2250738585072014E-308
517 * MAX 1.7976931348623157E+308
518 * VAX floating point has a smaller range than IEEE.
519 *
520 * precisions > 324 don't make much sense.
521 * if we cap the precision at 512 we will not
522 * overflow buf.
523 */
524 if (precision > 512U)
525 precision = 512;
526 sprintf(fmt, "%%%s%s.%lu%s%c", alt ? "#" : "",
527 plus ? "+" : space ? " " : "",
528 precision, l ? "L" : "", *format);
529 switch (*format) {
530 case 'e':
531 case 'E':
532 case 'f':
533 case 'g':
534 case 'G':
535 #ifdef HAVE_LONG_DOUBLE
536 if (l) {
537 ldbl = va_arg(ap, long double);
538 sprintf(buf, fmt, ldbl);
539 } else
540 #endif
541 {
542 dbl = va_arg(ap, double);
543 sprintf(buf, fmt, dbl);
544 }
545 length = strlen(buf);
546 if (width > 0U) {
547 pad = width - length;
548 if (pad < 0)
549 pad = 0;
550 }
551 count += length + pad;
552 if (!left)
553 while (pad > 0 && size > 1U) {
554 *str++ = ' ';
555 size--;
556 pad--;
557 }
558 cp = buf;
559 while (*cp != ' ' && size > 1U) {
560 *str++ = *cp++;
561 size--;
562 }
563 while (pad > 0 && size > 1U) {
564 *str++ = ' ';
565 size--;
566 pad--;
567 }
568 break;
569 default:
570 continue;
571 }
572 break;
573 default:
574 continue;
575 }
576 format++;
577 }
578 if (size > 0U)
579 *str = '\0';
580 return (count);
581 }
582