|
|
1.1 misho 1: /*
2: * Copyright (C) 2009-2013 Tobias Brunner
3: * Copyright (C) 2006-2008 Martin Willi
4: * HSR Hochschule fuer Technik Rapperswil
5: *
6: * This program is free software; you can redistribute it and/or modify it
7: * under the terms of the GNU General Public License as published by the
8: * Free Software Foundation; either version 2 of the License, or (at your
9: * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10: *
11: * This program is distributed in the hope that it will be useful, but
12: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14: * for more details.
15: */
16:
17: #include "printf_hook.h"
18:
19: #include <utils/utils.h>
20: #include <utils/debug.h>
21: #include <threading/thread_value.h>
22:
23: #include <vstr.h>
24: #include <string.h>
25: #include <unistd.h>
26: #include <errno.h>
27:
28: typedef struct private_printf_hook_t private_printf_hook_t;
29: typedef struct printf_hook_handler_t printf_hook_handler_t;
30:
31: #define PRINTF_BUF_LEN 8192
32: #define ARGS_MAX 3
33:
34: /**
35: * private data of printf_hook
36: */
37: struct private_printf_hook_t {
38:
39: /**
40: * public functions
41: */
42: printf_hook_t public;
43: };
44:
45: /**
46: * struct with information about a registered handler
47: */
48: struct printf_hook_handler_t {
49:
50: /**
51: * callback function
52: */
53: printf_hook_function_t hook;
54:
55: /**
56: * number of arguments
57: */
58: int numargs;
59:
60: /**
61: * types of the arguments, VSTR_TYPE_FMT_*
62: */
63: int argtypes[ARGS_MAX];
64:
65: /**
66: * name required for Vstr
67: */
68: char *name;
69: };
70:
71: /**
72: * Data to pass to a printf hook.
73: */
74: struct printf_hook_data_t {
75:
76: /**
77: * Base to append printf to
78: */
79: Vstr_base *base;
80:
81: /**
82: * Position in base to write to
83: */
84: size_t pos;
85: };
86:
87: /* A-Z | 6 other chars | a-z */
88: static printf_hook_handler_t *printf_hooks[58];
89:
90: #define SPEC_TO_INDEX(spec) ((int)(spec) - (int)'A')
91:
92: /**
93: * These are used below, whenever the public wrapper functions are called before
94: * initialization or after destruction.
95: */
96: #undef vprintf
97: #undef vfprintf
98: #undef vsnprintf
99:
100: /**
101: * Vstr variant of print_in_hook()
102: */
103: size_t print_in_hook(printf_hook_data_t *data, char *fmt, ...)
104: {
105: size_t written;
106: va_list args;
107:
108: va_start(args, fmt);
109: written = vstr_add_vfmt(data->base, data->pos, fmt, args);
110: va_end(args);
111:
112: data->pos += written;
113: return written;
114: }
115:
116: /**
117: * Vstr custom format specifier callback function.
118: */
119: static int custom_fmt_cb(Vstr_base *base, size_t pos, Vstr_fmt_spec *fmt_spec)
120: {
121: int i;
122: const void *args[ARGS_MAX];
123: printf_hook_spec_t spec;
124: printf_hook_handler_t *handler;
125: printf_hook_data_t data = {
126: .base = base,
127: .pos = pos,
128: };
129:
130: handler = printf_hooks[SPEC_TO_INDEX(fmt_spec->name[0])];
131: for (i = 0; i < handler->numargs; i++)
132: {
133: switch (handler->argtypes[i])
134: {
135: case VSTR_TYPE_FMT_INT:
136: args[i] = VSTR_FMT_CB_ARG_PTR(fmt_spec, i);
137: break;
138: case VSTR_TYPE_FMT_PTR_VOID:
139: args[i] = &VSTR_FMT_CB_ARG_PTR(fmt_spec, i);
140: break;
141: }
142: }
143:
144: spec.hash = fmt_spec->fmt_hash;
145: spec.plus = fmt_spec->fmt_plus;
146: spec.minus = fmt_spec->fmt_minus;
147: spec.width = fmt_spec->obj_field_width;
148:
149: handler->hook(&data, &spec, args);
150:
151: return 1;
152: }
153:
154: /**
155: * Add a custom format handler to the given Vstr_conf object
156: */
157: static void vstr_fmt_add_handler(Vstr_conf *conf, printf_hook_handler_t *handler)
158: {
159: int *at;
160:
161: at = handler->argtypes;
162: switch (handler->numargs)
163: {
164: case 1:
165: vstr_fmt_add(conf, handler->name, custom_fmt_cb, at[0],
166: VSTR_TYPE_FMT_END);
167: break;
168: case 2:
169: vstr_fmt_add(conf, handler->name, custom_fmt_cb, at[0],
170: at[1], VSTR_TYPE_FMT_END);
171: break;
172: case 3:
173: vstr_fmt_add(conf, handler->name, custom_fmt_cb, at[0],
174: at[1], at[2], VSTR_TYPE_FMT_END);
175: break;
176: }
177: }
178:
179: /**
180: * Thread specific vstr config
181: */
182: static thread_value_t *vstr_conf = NULL;
183:
184: /**
185: * Create vstr config for current thread
186: */
187: static Vstr_conf *create_vstr_conf()
188: {
189: Vstr_conf *conf;
190: int i;
191:
192: conf = vstr_make_conf();
193: vstr_cntl_conf(conf, VSTR_CNTL_CONF_SET_FMT_CHAR_ESC, '%');
194: vstr_cntl_conf(conf, VSTR_CNTL_CONF_SET_TYPE_GRPALLOC_CACHE,
195: VSTR_TYPE_CNTL_CONF_GRPALLOC_CSTR);
196: vstr_cntl_conf(conf, VSTR_CNTL_CONF_SET_NUM_BUF_SZ, PRINTF_BUF_LEN);
197:
198: for (i = 0; i < countof(printf_hooks); i++)
199: {
200: if (printf_hooks[i])
201: {
202: vstr_fmt_add_handler(conf, printf_hooks[i]);
203: }
204: }
205: return conf;
206: }
207:
208: /**
209: * Get vstr config of current thread
210: */
211: static inline Vstr_conf *get_vstr_conf()
212: {
213: Vstr_conf *conf = NULL;
214:
215: if (vstr_conf)
216: {
217: conf = (Vstr_conf*)vstr_conf->get(vstr_conf);
218: if (!conf)
219: {
220: conf = create_vstr_conf();
221: vstr_conf->set(vstr_conf, conf);
222: }
223: }
224: return conf;
225: }
226:
227: /**
228: * Wrapper functions for printf and alike
229: */
230: int vstr_wrapper_printf(const char *format, ...)
231: {
232: int written;
233: va_list args;
234: va_start(args, format);
235: written = vstr_wrapper_vprintf(format, args);
236: va_end(args);
237: return written;
238: }
239:
240: int vstr_wrapper_fprintf(FILE *stream, const char *format, ...)
241: {
242: int written;
243: va_list args;
244: va_start(args, format);
245: written = vstr_wrapper_vfprintf(stream, format, args);
246: va_end(args);
247: return written;
248: }
249:
250: int vstr_wrapper_sprintf(char *str, const char *format, ...)
251: {
252: int written;
253: va_list args;
254: va_start(args, format);
255: written = vstr_wrapper_vsprintf(str, format, args);
256: va_end(args);
257: return written;
258: }
259:
260: int vstr_wrapper_snprintf(char *str, size_t size, const char *format, ...)
261: {
262: int written;
263: va_list args;
264: va_start(args, format);
265: written = vstr_wrapper_vsnprintf(str, size, format, args);
266: va_end(args);
267: return written;
268: }
269:
270: int vstr_wrapper_asprintf(char **str, const char *format, ...)
271: {
272: int written;
273: va_list args;
274: va_start(args, format);
275: written = vstr_wrapper_vasprintf(str, format, args);
276: va_end(args);
277: return written;
278: }
279:
280: static inline int vstr_wrapper_vprintf_internal(Vstr_conf *conf, FILE *stream,
281: const char *format,
282: va_list args)
283: {
284: struct iovec *iov;
285: int iovcnt, written = 0;
286: Vstr_base *s;
287:
288: s = vstr_make_base(conf);
289: vstr_add_vfmt(s, 0, format, args);
290: if (vstr_export_iovec_ptr_all(s, &iov, &iovcnt))
291: {
292: while (iovcnt--)
293: {
294: if (iov->iov_base)
295: {
296: written += fwrite(iov->iov_base, 1, iov->iov_len, stream);
297: }
298: iov++;
299: }
300: }
301: vstr_free_base(s);
302: return written;
303: }
304:
305: int vstr_wrapper_vprintf(const char *format, va_list args)
306: {
307: Vstr_conf *conf;
308:
309: conf = get_vstr_conf();
310: if (conf)
311: {
312: return vstr_wrapper_vprintf_internal(conf, stdout, format, args);
313: }
314: return vprintf(format, args);
315: }
316:
317: int vstr_wrapper_vfprintf(FILE *stream, const char *format, va_list args)
318: {
319: Vstr_conf *conf;
320:
321: conf = get_vstr_conf();
322: if (conf)
323: {
324: return vstr_wrapper_vprintf_internal(conf, stream, format, args);
325: }
326: return vfprintf(stream, format, args);
327: }
328:
329: static inline int vstr_wrapper_vsnprintf_internal(char *str, size_t size,
330: const char *format,
331: va_list args)
332: {
333: Vstr_conf *conf;
334: Vstr_base *s;
335: int written;
336:
337: conf = get_vstr_conf();
338: if (conf)
339: {
340: s = vstr_make_base(conf);
341: vstr_add_vfmt(s, 0, format, args);
342: written = s->len;
343: vstr_export_cstr_buf(s, 1, s->len, str, (size > 0) ? size : s->len + 1);
344: vstr_free_base(s);
345: return written;
346: }
347: return vsnprintf(str, size, format, args);
348: }
349:
350: int vstr_wrapper_vsprintf(char *str, const char *format, va_list args)
351: {
352: return vstr_wrapper_vsnprintf_internal(str, 0, format, args);
353: }
354:
355: int vstr_wrapper_vsnprintf(char *str, size_t size, const char *format,
356: va_list args)
357: {
358: if (size > 0)
359: {
360: return vstr_wrapper_vsnprintf_internal(str, size, format, args);
361: }
362: return 0;
363: }
364:
365: int vstr_wrapper_vasprintf(char **str, const char *format, va_list args)
366: {
367: size_t len = 100;
368: int written;
369:
370: *str = malloc(len);
371: while (TRUE)
372: {
373: va_list ac;
374: va_copy(ac, args);
375: written = vstr_wrapper_vsnprintf_internal(*str, len, format, ac);
376: va_end(ac);
377: if (written < len)
378: {
379: break;
380: }
381: len = written + 1;
382: *str = realloc(*str, len);
383: }
384: return written;
385: }
386:
387: METHOD(printf_hook_t, add_handler, void,
388: private_printf_hook_t *this, char spec, printf_hook_function_t hook, ...)
389: {
390: int i = -1;
391: bool failed = FALSE;
392: printf_hook_handler_t *handler;
393: printf_hook_argtype_t argtype;
394: va_list args;
395:
396: if (SPEC_TO_INDEX(spec) <= -1 ||
397: SPEC_TO_INDEX(spec) >= countof(printf_hooks))
398: {
399: DBG1(DBG_LIB, "'%c' is not a valid printf hook specifier, "
400: "not registered!", spec);
401: return;
402: }
403:
404: INIT(handler,
405: .hook = hook,
406: );
407:
408: va_start(args, hook);
409: while (!failed)
410: {
411: argtype = va_arg(args, printf_hook_argtype_t);
412: if (argtype == PRINTF_HOOK_ARGTYPE_END)
413: {
414: break;
415: }
416: if (++i >= ARGS_MAX)
417: {
418: DBG1(DBG_LIB, "Too many arguments for printf hook with "
419: "specifier '%c', not registered!", spec);
420: failed = TRUE;
421: break;
422: }
423: switch (argtype)
424: {
425: case PRINTF_HOOK_ARGTYPE_INT:
426: handler->argtypes[i] = VSTR_TYPE_FMT_INT;
427: break;
428: case PRINTF_HOOK_ARGTYPE_POINTER:
429: handler->argtypes[i] = VSTR_TYPE_FMT_PTR_VOID;
430: break;
431: default:
432: DBG1(DBG_LIB, "Invalid printf hook arg type for '%c'", spec);
433: failed = TRUE;
434: break;
435: }
436: }
437: va_end(args);
438:
439: handler->numargs = i + 1;
440:
441: if (!failed && handler->numargs > 0)
442: {
443: Vstr_conf *conf = get_vstr_conf();
444: handler->name = malloc(2);
445: handler->name[0] = spec;
446: handler->name[1] = '\0';
447: vstr_fmt_add_handler(conf, handler);
448: printf_hooks[SPEC_TO_INDEX(spec)] = handler;
449: }
450: else
451: {
452: free(handler);
453: }
454: }
455:
456: METHOD(printf_hook_t, destroy, void,
457: private_printf_hook_t *this)
458: {
459: int i;
460: Vstr_conf *conf;
461: printf_hook_handler_t *handler;
462:
463: conf = get_vstr_conf();
464: for (i = 0; i < countof(printf_hooks); ++i)
465: {
466: handler = printf_hooks[i];
467: if (handler)
468: {
469: vstr_fmt_del(conf, handler->name);
470: free(handler->name);
471: free(handler);
472: }
473: }
474:
475: /* freeing the Vstr_conf of the main thread */
476: vstr_conf->destroy(vstr_conf);
477: vstr_conf = NULL;
478: vstr_exit();
479: free(this);
480: }
481:
482: /*
483: * see header file
484: */
485: printf_hook_t *printf_hook_create()
486: {
487: private_printf_hook_t *this;
488:
489: INIT(this,
490: .public = {
491: .add_handler = _add_handler,
492: .destroy = _destroy,
493: },
494: );
495:
496: memset(printf_hooks, 0, sizeof(printf_hooks));
497:
498: if (!vstr_init())
499: {
500: DBG1(DBG_LIB, "failed to initialize Vstr library!");
501: free(this);
502: return NULL;
503: }
504: vstr_conf = thread_value_create((thread_cleanup_t)vstr_free_conf);
505:
506: return &this->public;
507: }