printf-hook-builtin: Add a new "builtin" backend using its own printf() routines
[strongswan.git] / src / libstrongswan / utils / printf_hook / printf_hook_builtin.c
1 /*
2 * Copyright (C) 2013 Martin Willi
3 * Copyright (C) 2013 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 /**
17 * Copyright (C) 2002-2006 H. Peter Anvin
18 *
19 * Permission is hereby granted, free of charge, to any person obtaining a copy
20 * of this software and associated documentation files (the "Software"), to deal
21 * in the Software without restriction, including without limitation the rights
22 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23 * copies of the Software, and to permit persons to whom the Software is
24 * furnished to do so, subject to the following conditions:
25 *
26 * The above copyright notice and this permission notice shall be included in
27 * all copies or substantial portions of the Software.
28 *
29 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
35 * THE SOFTWARE.
36 */
37
38 #include "printf_hook.h"
39
40 #include <utils/utils.h>
41 #include <utils/debug.h>
42 #include <collections/hashtable.h>
43
44 #include <inttypes.h>
45 #include <limits.h>
46 #include <stdio.h>
47 #include <stdarg.h>
48 #include <string.h>
49
50 #define PRINTF_BUF_LEN 8192
51 #define ARGS_MAX 3
52
53 typedef struct private_printf_hook_t private_printf_hook_t;
54 typedef struct printf_hook_handler_t printf_hook_handler_t;
55
56 /**
57 * private data of printf_hook
58 */
59 struct private_printf_hook_t {
60
61 /**
62 * public functions
63 */
64 printf_hook_t public;
65 };
66
67 /**
68 * struct with information about a registered handler
69 */
70 struct printf_hook_handler_t {
71
72 /**
73 * callback function
74 */
75 printf_hook_function_t hook;
76
77 /**
78 * number of arguments
79 */
80 int numargs;
81
82 /**
83 * types of the arguments
84 */
85 int argtypes[ARGS_MAX];
86 };
87
88 /**
89 * Data to pass to a printf hook.
90 */
91 struct printf_hook_data_t {
92
93 /**
94 * Output buffer
95 */
96 char *q;
97
98 /**
99 * Remaining bytes in q
100 */
101 size_t n;
102 };
103
104 /**
105 * Registered hooks (char => printf_hook_handler_t)
106 */
107 static hashtable_t *hooks;
108
109 /**
110 * builtin-printf variant of print_in_hook()
111 */
112 size_t print_in_hook(printf_hook_data_t *data, char *fmt, ...)
113 {
114 int written;
115 va_list args;
116
117 va_start(args, fmt);
118 written = builtin_vsnprintf(data->q, data->n, fmt, args);
119 va_end(args);
120
121 if (written > data->n)
122 {
123 written = data->n;
124 }
125 data->q += written;
126 data->n += written;
127 return written;
128 }
129
130 METHOD(printf_hook_t, add_handler, void,
131 private_printf_hook_t *this, char spec, printf_hook_function_t hook, ...)
132 {
133 int i = -1;
134 bool failed = FALSE;
135 printf_hook_handler_t *handler;
136 printf_hook_argtype_t argtype;
137 va_list args;
138
139 INIT(handler,
140 .hook = hook,
141 );
142
143 va_start(args, hook);
144 while (!failed)
145 {
146 argtype = va_arg(args, printf_hook_argtype_t);
147
148 if (argtype == PRINTF_HOOK_ARGTYPE_END)
149 {
150 break;
151 }
152 if (++i >= countof(handler->argtypes))
153 {
154 DBG1(DBG_LIB, "Too many arguments for printf hook with "
155 "specifier '%c', not registered!", spec);
156 failed = TRUE;
157 break;
158 }
159 handler->argtypes[i] = argtype;
160 }
161 va_end(args);
162
163 handler->numargs = i + 1;
164 if (!failed && handler->numargs > 0)
165 {
166 free(hooks->put(hooks, (void*)(uintptr_t)spec, handler));
167 }
168 else
169 {
170 free(handler);
171 }
172 }
173
174 /**
175 * Printf format modifier flags
176 */
177 typedef enum {
178 FL_ZERO = 0x01,
179 FL_MINUS = 0x02,
180 FL_PLUS = 0x04,
181 FL_TICK = 0x08,
182 FL_SPACE = 0x10,
183 FL_HASH = 0x20,
184 FL_SIGNED = 0x40,
185 FL_UPPER = 0x80,
186 } bpf_flag_t;
187
188 /**
189 * Size of format string arguments
190 */
191 typedef enum {
192 RNK_CHAR = -2,
193 RNK_SHORT = -1,
194 RNK_INT = 0,
195 RNK_LONG = 1,
196 RNK_LONGLONG = 2,
197
198 RNK_INTMAX = RNK_LONGLONG,
199 RNK_SIZE_T = RNK_LONG,
200 RNK_PTRDIFF_T = RNK_LONG,
201
202 RNK_MIN = RNK_CHAR,
203 RNK_MAX = RNK_LONGLONG,
204 } bpf_rank_t;
205
206 /**
207 * Printf specifier Parser state
208 */
209 typedef enum {
210 /* Ground state */
211 ST_NORMAL,
212 /* Special flags */
213 ST_FLAGS,
214 /* Field width */
215 ST_WIDTH,
216 /* Field precision */
217 ST_PREC,
218 /* Length or conversion modifiers */
219 ST_MODIFIERS,
220 } bpf_state_t;
221
222 #define EMIT(x) ({ if (o<n){*q++ = (x);} o++; })
223
224 /**
225 * Write an integer argument to q, using flags, base, width and precision
226 */
227 static size_t format_int(char *q, size_t n, uintmax_t val, bpf_flag_t flags,
228 int base, int width, int prec)
229 {
230 static const char lcdigits[] = "0123456789abcdef";
231 static const char ucdigits[] = "0123456789ABCDEF";
232 char *qq;
233 size_t o = 0, oo;
234 const char *digits;
235 uintmax_t tmpval;
236 int minus = 0;
237 int ndigits = 0, nchars;
238 int tickskip, b4tick;
239
240 /* Select type of digits */
241 digits = (flags & FL_UPPER) ? ucdigits : lcdigits;
242
243 /* If signed, separate out the minus */
244 if (flags & FL_SIGNED && (intmax_t) val < 0)
245 {
246 minus = 1;
247 val = (uintmax_t) (-(intmax_t) val);
248 }
249
250 /* Count the number of digits needed. This returns zero for 0. */
251 tmpval = val;
252 while (tmpval)
253 {
254 tmpval /= base;
255 ndigits++;
256 }
257
258 /* Adjust ndigits for size of output */
259 if (flags & FL_HASH && base == 8)
260 {
261 if (prec < ndigits + 1)
262 {
263 prec = ndigits + 1;
264 }
265 }
266
267 if (ndigits < prec)
268 {
269 /* Mandatory number padding */
270 ndigits = prec;
271 }
272 else if (val == 0)
273 {
274 /* Zero still requires space */
275 ndigits = 1;
276 }
277
278 /* For ', figure out what the skip should be */
279 if (flags & FL_TICK)
280 {
281 if (base == 16)
282 {
283 tickskip = 4;
284 }
285 else
286 {
287 tickskip = 3;
288 }
289 }
290 else
291 {
292 /* No tick marks */
293 tickskip = ndigits;
294 }
295
296 /* Tick marks aren't digits, but generated by the number converter */
297 ndigits += (ndigits - 1) / tickskip;
298
299 /* Now compute the number of nondigits */
300 nchars = ndigits;
301
302 if (minus || (flags & (FL_PLUS | FL_SPACE)))
303 {
304 /* Need space for sign */
305 nchars++;
306 }
307 if ((flags & FL_HASH) && base == 16)
308 {
309 /* Add 0x for hex */
310 nchars += 2;
311 }
312
313 /* Emit early space padding */
314 if (!(flags & (FL_MINUS | FL_ZERO)) && width > nchars)
315 {
316 while (width > nchars)
317 {
318 EMIT(' ');
319 width--;
320 }
321 }
322
323 /* Emit nondigits */
324 if (minus)
325 {
326 EMIT('-');
327 }
328 else if (flags & FL_PLUS)
329 {
330 EMIT('+');
331 }
332 else if (flags & FL_SPACE)
333 {
334 EMIT(' ');
335 }
336
337 if ((flags & FL_HASH) && base == 16)
338 {
339 EMIT('0');
340 EMIT((flags & FL_UPPER) ? 'X' : 'x');
341 }
342
343 /* Emit zero padding */
344 if ((flags & (FL_MINUS | FL_ZERO)) == FL_ZERO && width > ndigits)
345 {
346 while (width > nchars)
347 {
348 EMIT('0');
349 width--;
350 }
351 }
352
353 /* Generate the number. This is done from right to left. */
354 /* Advance the pointer to end of number */
355 q += ndigits;
356 o += ndigits;
357 /* Temporary values */
358 qq = q;
359 oo = o;
360
361 b4tick = tickskip;
362 while (ndigits > 0)
363 {
364 if (!b4tick--)
365 {
366 qq--;
367 oo--;
368 ndigits--;
369 if (oo < n)
370 {
371 *qq = '_';
372 }
373 b4tick = tickskip - 1;
374 }
375 qq--;
376 oo--;
377 ndigits--;
378 if (oo < n)
379 {
380 *qq = digits[val % base];
381 }
382 val /= base;
383 }
384
385 /* Emit late space padding */
386 while ((flags & FL_MINUS) && width > nchars)
387 {
388 EMIT(' ');
389 width--;
390 }
391
392 return o;
393 }
394
395 int builtin_vsnprintf(char *buffer, size_t n, const char *format, va_list ap)
396 {
397 const char *p = format;
398 char ch;
399 char *q = buffer;
400 /* Number of characters output */
401 size_t o = 0;
402 uintmax_t val = 0;
403 /* Default rank */
404 int rank = RNK_INT;
405 int width = 0;
406 int prec = -1;
407 int base;
408 size_t sz;
409 bpf_flag_t flags = 0;
410 bpf_state_t state = ST_NORMAL;
411 /* %s string argument */
412 const char *sarg;
413 /* %c char argument */
414 char carg;
415 /* String length */
416 int slen;
417
418 while ((ch = *p++))
419 {
420 switch (state)
421 {
422 case ST_NORMAL:
423 {
424 if (ch == '%')
425 {
426 state = ST_FLAGS;
427 flags = 0;
428 rank = RNK_INT;
429 width = 0;
430 prec = -1;
431 }
432 else
433 {
434 EMIT(ch);
435 }
436 break;
437 }
438 case ST_FLAGS:
439 {
440 switch (ch)
441 {
442 case '-':
443 flags |= FL_MINUS;
444 break;
445 case '+':
446 flags |= FL_PLUS;
447 break;
448 case '\'':
449 flags |= FL_TICK;
450 break;
451 case ' ':
452 flags |= FL_SPACE;
453 break;
454 case '#':
455 flags |= FL_HASH;
456 break;
457 case '0':
458 flags |= FL_ZERO;
459 break;
460 default:
461 state = ST_WIDTH;
462 /* Process this character again */
463 p--;
464 break;
465 }
466 break;
467 }
468 case ST_WIDTH:
469 {
470 if (ch >= '0' && ch <= '9')
471 {
472 width = width * 10 + (ch - '0');
473 }
474 else if (ch == '*')
475 {
476 width = va_arg(ap, int);
477 if (width < 0)
478 {
479 width = -width;
480 flags |= FL_MINUS;
481 }
482 }
483 else if (ch == '.')
484 {
485 /* Precision given */
486 prec = 0;
487 state = ST_PREC;
488 }
489 else
490 {
491 state = ST_MODIFIERS;
492 /* Process this character again */
493 p--;
494 }
495 break;
496 }
497 case ST_PREC:
498 {
499 if (ch >= '0' && ch <= '9')
500 {
501 prec = prec * 10 + (ch - '0');
502 }
503 else if (ch == '*')
504 {
505 prec = va_arg(ap, int);
506 if (prec < 0)
507 {
508 prec = -1;
509 }
510 }
511 else
512 {
513 state = ST_MODIFIERS;
514 /* Process this character again */
515 p--;
516 }
517 break;
518 }
519 case ST_MODIFIERS:
520 {
521 switch (ch)
522 {
523 /* Length modifiers - nonterminal sequences */
524 case 'h':
525 rank--;
526 break;
527 case 'l':
528 rank++;
529 break;
530 case 'j':
531 rank = RNK_INTMAX;
532 break;
533 case 'z':
534 rank = RNK_SIZE_T;
535 break;
536 case 't':
537 rank = RNK_PTRDIFF_T;
538 break;
539 case 'L':
540 case 'q':
541 rank += 2;
542 break;
543 default:
544 {
545 /* Output modifiers - terminal sequences */
546
547 /* Next state will be normal */
548 state = ST_NORMAL;
549
550 /* Canonicalize rank */
551 if (rank < RNK_MIN)
552 {
553 rank = RNK_MIN;
554 }
555 else if (rank > RNK_MAX)
556 {
557 rank = RNK_MAX;
558 }
559
560 switch (ch)
561 {
562 case 'P':
563 {
564 /* Upper case pointer */
565 flags |= FL_UPPER;
566 /* fall through */
567 }
568 case 'p':
569 {
570 /* Pointer */
571 base = 16;
572 prec = (CHAR_BIT*sizeof(void *)+3)/4;
573 flags |= FL_HASH;
574 val = (uintmax_t)(uintptr_t)
575 va_arg(ap, void *);
576 goto is_integer;
577 }
578 case 'd':
579 case 'i':
580 {
581 /* Signed decimal output */
582 base = 10;
583 flags |= FL_SIGNED;
584 switch (rank)
585 {
586 case RNK_CHAR:
587 /* Yes, all these casts are
588 needed... */
589 val = (uintmax_t)(intmax_t)(signed char)
590 va_arg(ap, signed int);
591 break;
592 case RNK_SHORT:
593 val = (uintmax_t)(intmax_t)(signed short)
594 va_arg(ap, signed int);
595 break;
596 case RNK_INT:
597 val = (uintmax_t)(intmax_t)
598 va_arg(ap, signed int);
599 break;
600 case RNK_LONG:
601 val = (uintmax_t)(intmax_t)
602 va_arg(ap, signed long);
603 break;
604 case RNK_LONGLONG:
605 val = (uintmax_t)(intmax_t)
606 va_arg(ap, signed long long);
607 break;
608 }
609 goto is_integer;
610 }
611 case 'o':
612 {
613 /* Octal */
614 base = 8;
615 goto is_unsigned;
616 }
617 case 'u':
618 {
619 /* Unsigned decimal */
620 base = 10;
621 goto is_unsigned;
622 }
623 case 'X':
624 {
625 /* Upper case hexadecimal */
626 flags |= FL_UPPER;
627 /* fall through */
628 }
629 case 'x':
630 {
631 /* Hexadecimal */
632 base = 16;
633 goto is_unsigned;
634 }
635 is_unsigned:
636 {
637 switch (rank) {
638 case RNK_CHAR:
639 val = (uintmax_t)(unsigned char)
640 va_arg(ap, unsigned int);
641 break;
642 case RNK_SHORT:
643 val = (uintmax_t)(unsigned short)
644 va_arg(ap, unsigned int);
645 break;
646 case RNK_INT:
647 val = (uintmax_t)
648 va_arg(ap, unsigned int);
649 break;
650 case RNK_LONG:
651 val = (uintmax_t)
652 va_arg(ap, unsigned long);
653 break;
654 case RNK_LONGLONG:
655 val = (uintmax_t)
656 va_arg(ap, unsigned long long);
657 break;
658 }
659 goto is_integer;
660 }
661 is_integer:
662 {
663 sz = format_int(q, (o < n) ? n - o : 0,
664 val, flags, base, width, prec);
665 q += sz;
666 o += sz;
667 break;
668 }
669 case 'c':
670 {
671 /* Character */
672 carg = (char)va_arg(ap, int);
673 sarg = &carg;
674 slen = 1;
675 goto is_string;
676 }
677 case 's':
678 {
679 /* String */
680 sarg = va_arg(ap, const char *);
681 sarg = sarg ? sarg : "(null)";
682 slen = strlen(sarg);
683 goto is_string;
684 }
685 is_string:
686 {
687 char sch;
688 int i;
689
690 if (prec != -1 && slen > prec)
691 {
692 slen = prec;
693 }
694
695 if (width > slen && !(flags & FL_MINUS))
696 {
697 char pad = (flags & FL_ZERO) ? '0' : ' ';
698 while (width > slen)
699 {
700 EMIT(pad);
701 width--;
702 }
703 }
704 for (i = slen; i; i--)
705 {
706 sch = *sarg++;
707 EMIT(sch);
708 }
709 if (width > slen && (flags & FL_MINUS))
710 {
711 while (width > slen)
712 {
713 EMIT(' ');
714 width--;
715 }
716 }
717 break;
718 }
719 case 'n':
720 {
721 /* Output the number of characters written */
722 switch (rank)
723 {
724 case RNK_CHAR:
725 *va_arg(ap, signed char *) = o;
726 break;
727 case RNK_SHORT:
728 *va_arg(ap, signed short *) = o;
729 break;
730 case RNK_INT:
731 *va_arg(ap, signed int *) = o;
732 break;
733 case RNK_LONG:
734 *va_arg(ap, signed long *) = o;
735 break;
736 case RNK_LONGLONG:
737 *va_arg(ap, signed long long *) = o;
738 break;
739 }
740 break;
741 }
742 default:
743 {
744 printf_hook_handler_t *handler;
745
746 handler = hooks->get(hooks, (void*)(uintptr_t)ch);
747 if (handler)
748 {
749 const void *args[ARGS_MAX];
750 int i, iargs[ARGS_MAX];
751 void *pargs[ARGS_MAX];
752 printf_hook_spec_t spec = {
753 .hash = flags & FL_HASH,
754 .plus = flags & FL_PLUS,
755 .minus = flags & FL_MINUS,
756 .width = width,
757 };
758 printf_hook_data_t data = {
759 .q = q,
760 .n = (o < n) ? n - o : 0,
761 };
762
763 for (i = 0; i < handler->numargs; i++)
764 {
765 switch (handler->argtypes[i])
766 {
767 case PRINTF_HOOK_ARGTYPE_INT:
768 iargs[i] = va_arg(ap, int);
769 args[i] = &iargs[i];
770 break;
771 case PRINTF_HOOK_ARGTYPE_POINTER:
772 pargs[i] = va_arg(ap, void*);
773 args[i] = &pargs[i];
774 break;
775 }
776 }
777 sz = handler->hook(&data, &spec, args);
778 q += sz;
779 o += sz;
780 }
781 else
782 {
783 /* Anything else, including % */
784 EMIT(ch);
785 }
786 break;
787 }
788 }
789 }
790 }
791 }
792 }
793 }
794
795 /* Null-terminate the string */
796 if (o < n)
797 {
798 /* No overflow */
799 *q = '\0';
800 }
801 else if (n > 0)
802 {
803 /* Overflow - terminate at end of buffer */
804 buffer[n - 1] = '\0';
805 }
806 return o;
807 }
808
809 int builtin_printf(const char *format, ...)
810 {
811 int written;
812 va_list args;
813
814 va_start(args, format);
815 written = builtin_vprintf(format, args);
816 va_end(args);
817
818 return written;
819 }
820
821 int builtin_fprintf(FILE *stream, const char *format, ...)
822 {
823 int written;
824 va_list args;
825
826 va_start(args, format);
827 written = builtin_vfprintf(stream, format, args);
828 va_end(args);
829
830 return written;
831 }
832
833 int builtin_sprintf(char *str, const char *format, ...)
834 {
835 int written;
836 va_list args;
837
838 va_start(args, format);
839 written = builtin_vsnprintf(str, ~(size_t)0, format, args);
840 va_end(args);
841
842 return written;
843 }
844
845 int builtin_snprintf(char *str, size_t size, const char *format, ...)
846 {
847 int written;
848 va_list args;
849
850 va_start(args, format);
851 written = builtin_vsnprintf(str, size, format, args);
852 va_end(args);
853
854 return written;
855 }
856
857 int builtin_asprintf(char **str, const char *format, ...)
858 {
859 int written;
860 va_list args;
861
862 va_start(args, format);
863 written = builtin_vasprintf(str, format, args);
864 va_end(args);
865
866 return written;
867 }
868
869 int builtin_vprintf(const char *format, va_list ap)
870 {
871 return builtin_vfprintf(stdout, format, ap);
872 }
873
874 int builtin_vfprintf(FILE *stream, const char *format, va_list ap)
875 {
876 char buf[PRINTF_BUF_LEN];
877 int len;
878
879 len = builtin_vsnprintf(buf, sizeof(buf), format, ap);
880 return fwrite(buf, 1, len, stream);
881 }
882
883 int builtin_vsprintf(char *str, const char *format, va_list ap)
884 {
885 return builtin_vsnprintf(str, ~(size_t)0, format, ap);
886 }
887
888 int builtin_vasprintf(char **str, const char *format, va_list ap)
889 {
890 char buf[PRINTF_BUF_LEN];
891 int len;
892
893 len = builtin_vsnprintf(buf, sizeof(buf), format, ap);
894 *str = strdup(buf);
895 return len;
896 }
897
898 METHOD(printf_hook_t, destroy, void,
899 private_printf_hook_t *this)
900 {
901 enumerator_t *enumerator;
902 printf_hook_handler_t *handler;
903
904 enumerator = hooks->create_enumerator(hooks);
905 while (enumerator->enumerate(enumerator, NULL, &handler))
906 {
907 free(handler);
908 }
909 enumerator->destroy(enumerator);
910
911 hooks->destroy(hooks);
912
913 free(this);
914 }
915
916 /*
917 * see header file
918 */
919 printf_hook_t *printf_hook_create()
920 {
921 private_printf_hook_t *this;
922
923 INIT(this,
924 .public = {
925 .add_handler = _add_handler,
926 .destroy = _destroy,
927 },
928 );
929
930 hooks = hashtable_create(hashtable_hash_ptr, hashtable_equals_ptr, 8);
931
932 return &this->public;
933 }