]>
Commit | Line | Data |
---|---|---|
db7ef624 | 1 | /* |
d25ce370 | 2 | * Copyright (C) 2009 Tobias Brunner |
552cc11b | 3 | * Copyright (C) 2006-2008 Martin Willi |
db7ef624 MW |
4 | * 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 | ||
552cc11b | 19 | #include <utils.h> |
d25ce370 TB |
20 | #include <debug.h> |
21 | ||
22 | #include <stdio.h> | |
23 | #include <stdarg.h> | |
24 | #include <string.h> | |
db7ef624 | 25 | |
552cc11b | 26 | typedef struct private_printf_hook_t private_printf_hook_t; |
d25ce370 TB |
27 | typedef struct printf_hook_handler_t printf_hook_handler_t; |
28 | ||
29 | #define PRINTF_BUF_LEN 8192 | |
30 | #define ARGS_MAX 3 | |
2f5914a3 | 31 | |
db7ef624 | 32 | /** |
552cc11b | 33 | * private data of printf_hook |
db7ef624 | 34 | */ |
552cc11b | 35 | struct private_printf_hook_t { |
db7ef624 | 36 | |
552cc11b MW |
37 | /** |
38 | * public functions | |
39 | */ | |
40 | printf_hook_t public; | |
41 | }; | |
db7ef624 | 42 | |
d25ce370 TB |
43 | /** |
44 | * struct with information about a registered handler | |
45 | */ | |
46 | struct printf_hook_handler_t { | |
7daf5226 | 47 | |
d25ce370 TB |
48 | /** |
49 | * callback function | |
50 | */ | |
51 | printf_hook_function_t hook; | |
7daf5226 | 52 | |
d25ce370 TB |
53 | /** |
54 | * number of arguments | |
55 | */ | |
56 | int numargs; | |
7daf5226 | 57 | |
d25ce370 TB |
58 | /** |
59 | * types of the arguments | |
60 | */ | |
61 | int argtypes[ARGS_MAX]; | |
62 | ||
bf45d6dd | 63 | #ifdef USE_VSTR |
d25ce370 TB |
64 | /** |
65 | * name required for Vstr | |
66 | */ | |
67 | char *name; | |
68 | #endif | |
69 | }; | |
70 | ||
71 | /* A-Z | 6 other chars | a-z */ | |
72 | #define NUM_HANDLERS 58 | |
73 | static printf_hook_handler_t *printf_hooks[NUM_HANDLERS]; | |
74 | ||
75 | #define SPEC_TO_INDEX(spec) ((int)(spec) - (int)'A') | |
76 | #define IS_VALID_SPEC(spec) (SPEC_TO_INDEX(spec) > -1 && SPEC_TO_INDEX(spec) < NUM_HANDLERS) | |
77 | ||
f6bbcec3 MW |
78 | #if !defined(USE_VSTR) && \ |
79 | (defined(HAVE_PRINTF_FUNCTION) || defined(HAVE_PRINTF_SPECIFIER)) | |
d25ce370 TB |
80 | |
81 | /** | |
82 | * Printf hook print function. This is actually of type "printf_function", | |
83 | * however glibc does it typedef to function, but uclibc to a pointer. | |
84 | * So we redefine it here. | |
85 | */ | |
86 | static int custom_print(FILE *stream, const struct printf_info *info, | |
87 | const void *const *args) | |
88 | { | |
89 | int written; | |
90 | char buf[PRINTF_BUF_LEN]; | |
91 | printf_hook_spec_t spec; | |
92 | printf_hook_handler_t *handler = printf_hooks[SPEC_TO_INDEX(info->spec)]; | |
7daf5226 | 93 | |
d25ce370 TB |
94 | spec.hash = info->alt; |
95 | spec.minus = info->left; | |
96 | spec.width = info->width; | |
7daf5226 | 97 | |
d25ce370 TB |
98 | written = handler->hook(buf, sizeof(buf), &spec, args); |
99 | if (written > 0) | |
100 | { | |
c5c96963 | 101 | ignore_result(fwrite(buf, 1, written, stream)); |
d25ce370 TB |
102 | } |
103 | return written; | |
104 | } | |
105 | ||
106 | /** | |
107 | * Printf hook arginfo function, which is actually of type | |
f6bbcec3 | 108 | * "printf_arginfo_[size_]function". |
d25ce370 | 109 | */ |
f6bbcec3 MW |
110 | static int custom_arginfo(const struct printf_info *info, size_t n, int *argtypes |
111 | #ifdef HAVE_PRINTF_SPECIFIER | |
112 | , int *size | |
113 | #endif | |
114 | ) | |
d25ce370 TB |
115 | { |
116 | int i; | |
117 | printf_hook_handler_t *handler = printf_hooks[SPEC_TO_INDEX(info->spec)]; | |
7daf5226 | 118 | |
d25ce370 TB |
119 | if (handler->numargs <= n) |
120 | { | |
121 | for (i = 0; i < handler->numargs; ++i) | |
122 | { | |
123 | argtypes[i] = handler->argtypes[i]; | |
124 | } | |
125 | } | |
f6bbcec3 | 126 | /* we never set "size", as we have no user defined types */ |
d25ce370 TB |
127 | return handler->numargs; |
128 | } | |
129 | ||
130 | #else | |
131 | ||
132 | #include <errno.h> | |
133 | #include <unistd.h> /* for STDOUT_FILENO */ | |
134 | ||
135 | /** | |
136 | * Vstr custom format specifier callback function. | |
137 | */ | |
138 | static int custom_fmt_cb(Vstr_base *base, size_t pos, Vstr_fmt_spec *fmt_spec) | |
139 | { | |
140 | int i, written; | |
141 | char buf[PRINTF_BUF_LEN]; | |
142 | const void *args[ARGS_MAX]; | |
143 | printf_hook_spec_t spec; | |
144 | printf_hook_handler_t *handler = printf_hooks[SPEC_TO_INDEX(fmt_spec->name[0])]; | |
7daf5226 | 145 | |
d25ce370 TB |
146 | for (i = 0; i < handler->numargs; i++) |
147 | { | |
148 | switch(handler->argtypes[i]) | |
149 | { | |
150 | case PRINTF_HOOK_ARGTYPE_INT: | |
151 | args[i] = VSTR_FMT_CB_ARG_PTR(fmt_spec, i); | |
152 | break; | |
153 | case PRINTF_HOOK_ARGTYPE_POINTER: | |
154 | args[i] = &VSTR_FMT_CB_ARG_PTR(fmt_spec, i); | |
155 | break; | |
156 | } | |
157 | } | |
7daf5226 | 158 | |
d25ce370 TB |
159 | spec.hash = fmt_spec->fmt_hash; |
160 | spec.minus = fmt_spec->fmt_minus; | |
161 | spec.width = fmt_spec->fmt_field_width; | |
7daf5226 | 162 | |
d25ce370 TB |
163 | written = handler->hook(buf, sizeof(buf), &spec, args); |
164 | if (written > 0) | |
165 | { | |
166 | vstr_add_buf(base, pos, buf, written); | |
167 | } | |
168 | return TRUE; | |
169 | } | |
170 | ||
171 | /** | |
d24a74c5 | 172 | * Add a custom format handler to the given Vstr_conf object |
d25ce370 TB |
173 | */ |
174 | static void vstr_fmt_add_handler(Vstr_conf *conf, printf_hook_handler_t *handler) | |
175 | { | |
176 | int *at = handler->argtypes; | |
177 | switch(handler->numargs) | |
178 | { | |
179 | case 1: | |
180 | vstr_fmt_add(conf, handler->name, custom_fmt_cb, at[0], VSTR_TYPE_FMT_END); | |
181 | break; | |
182 | case 2: | |
183 | vstr_fmt_add(conf, handler->name, custom_fmt_cb, at[0], at[1], VSTR_TYPE_FMT_END); | |
184 | break; | |
185 | case 3: | |
186 | vstr_fmt_add(conf, handler->name, custom_fmt_cb, at[0], at[1], at[2], VSTR_TYPE_FMT_END); | |
187 | break; | |
188 | } | |
189 | } | |
190 | ||
191 | /** | |
192 | * Management of thread-specific Vstr_conf objects | |
193 | */ | |
194 | #include <pthread.h> | |
195 | ||
196 | static pthread_key_t vstr_conf_key; | |
197 | static pthread_once_t vstr_conf_key_once = PTHREAD_ONCE_INIT; | |
198 | ||
199 | static void init_vstr_conf_key(void) | |
200 | { | |
201 | pthread_key_create(&vstr_conf_key, (void*)vstr_free_conf); | |
202 | } | |
203 | ||
204 | static Vstr_conf *create_vstr_conf() | |
205 | { | |
206 | int i; | |
207 | Vstr_conf *conf = vstr_make_conf(); | |
208 | vstr_cntl_conf(conf, VSTR_CNTL_CONF_SET_FMT_CHAR_ESC, '%'); | |
209 | vstr_cntl_conf(conf, VSTR_CNTL_CONF_SET_TYPE_GRPALLOC_CACHE, | |
210 | VSTR_TYPE_CNTL_CONF_GRPALLOC_CSTR); | |
211 | vstr_cntl_conf(conf, VSTR_CNTL_CONF_SET_NUM_BUF_SZ, PRINTF_BUF_LEN); | |
212 | for (i = 0; i < NUM_HANDLERS; ++i) | |
213 | { | |
214 | printf_hook_handler_t *handler = printf_hooks[i]; | |
215 | if (handler) | |
216 | { | |
217 | vstr_fmt_add_handler(conf, handler); | |
218 | } | |
219 | } | |
220 | return conf; | |
221 | } | |
222 | ||
223 | static inline Vstr_conf *get_vstr_conf() | |
224 | { | |
225 | Vstr_conf *conf; | |
226 | pthread_once(&vstr_conf_key_once, init_vstr_conf_key); | |
227 | conf = (Vstr_conf*)pthread_getspecific(vstr_conf_key); | |
228 | if (!conf) | |
229 | { | |
230 | conf = create_vstr_conf(); | |
231 | pthread_setspecific(vstr_conf_key, conf); | |
232 | } | |
233 | return conf; | |
234 | } | |
235 | ||
236 | /** | |
237 | * Wrapper functions for printf and alike | |
238 | */ | |
239 | int vstr_wrapper_printf(const char *format, ...) | |
240 | { | |
241 | int written; | |
242 | va_list args; | |
243 | va_start(args, format); | |
244 | written = vstr_wrapper_vprintf(format, args); | |
245 | va_end(args); | |
246 | return written; | |
247 | } | |
248 | int vstr_wrapper_fprintf(FILE *stream, const char *format, ...) | |
249 | { | |
250 | int written; | |
251 | va_list args; | |
252 | va_start(args, format); | |
253 | written = vstr_wrapper_vfprintf(stream, format, args); | |
254 | va_end(args); | |
255 | return written; | |
256 | } | |
257 | int vstr_wrapper_sprintf(char *str, const char *format, ...) | |
258 | { | |
259 | int written; | |
260 | va_list args; | |
261 | va_start(args, format); | |
262 | written = vstr_wrapper_vsprintf(str, format, args); | |
263 | va_end(args); | |
264 | return written; | |
265 | } | |
266 | int vstr_wrapper_snprintf(char *str, size_t size, const char *format, ...) | |
267 | { | |
268 | int written; | |
269 | va_list args; | |
270 | va_start(args, format); | |
271 | written = vstr_wrapper_vsnprintf(str, size, format, args); | |
272 | va_end(args); | |
273 | return written; | |
274 | } | |
275 | static inline int vstr_wrapper_vprintf_internal(int fd, const char *format, | |
276 | va_list args) | |
277 | { | |
278 | int written; | |
279 | Vstr_conf *conf = get_vstr_conf(); | |
280 | Vstr_base *s = vstr_make_base(conf); | |
281 | vstr_add_vfmt(s, 0, format, args); | |
282 | written = s->len; | |
283 | while (s->len) | |
284 | { | |
285 | if (!vstr_sc_write_fd(s, 1, s->len, fd, NULL)) | |
286 | { | |
287 | if (errno != EAGAIN && errno != EINTR) | |
288 | { | |
289 | written -= s->len; | |
290 | break; | |
291 | } | |
292 | } | |
293 | } | |
294 | vstr_free_base(s); | |
295 | return written; | |
296 | } | |
297 | int vstr_wrapper_vprintf(const char *format, va_list args) | |
298 | { | |
299 | return vstr_wrapper_vprintf_internal(STDOUT_FILENO, format, args); | |
300 | } | |
301 | int vstr_wrapper_vfprintf(FILE *stream, const char *format, va_list args) | |
302 | { | |
303 | return vstr_wrapper_vprintf_internal(fileno(stream), format, args); | |
304 | } | |
305 | static inline int vstr_wrapper_vsnprintf_internal(char *str, size_t size, | |
306 | const char *format, | |
307 | va_list args) | |
308 | { | |
309 | int written; | |
310 | Vstr_conf *conf = get_vstr_conf(); | |
311 | Vstr_base *s = vstr_make_base(conf); | |
312 | vstr_add_vfmt(s, 0, format, args); | |
313 | written = s->len; | |
314 | vstr_export_cstr_buf(s, 1, s->len, str, (size > 0) ? size : s->len + 1); | |
315 | vstr_free_base(s); | |
316 | return written; | |
317 | } | |
318 | int vstr_wrapper_vsprintf(char *str, const char *format, va_list args) | |
319 | { | |
320 | return vstr_wrapper_vsnprintf_internal(str, 0, format, args); | |
321 | } | |
322 | int vstr_wrapper_vsnprintf(char *str, size_t size, const char *format, | |
323 | va_list args) | |
324 | { | |
325 | return (size > 0) ? vstr_wrapper_vsnprintf_internal(str, size, format, args) : 0; | |
326 | } | |
327 | ||
328 | #endif | |
329 | ||
db7ef624 | 330 | /** |
552cc11b | 331 | * Implementation of printf_hook_t.add_handler. |
db7ef624 | 332 | */ |
d25ce370 TB |
333 | static void add_handler(private_printf_hook_t *this, char spec, |
334 | printf_hook_function_t hook, ...) | |
db7ef624 | 335 | { |
d25ce370 TB |
336 | int i = -1; |
337 | printf_hook_handler_t *handler; | |
338 | printf_hook_argtype_t argtype; | |
339 | va_list args; | |
7daf5226 | 340 | |
d25ce370 TB |
341 | if (!IS_VALID_SPEC(spec)) |
342 | { | |
343 | DBG1("'%c' is not a valid printf hook specifier, not registered!", spec); | |
344 | return; | |
345 | } | |
7daf5226 | 346 | |
d24a74c5 | 347 | handler = malloc_thing(printf_hook_handler_t); |
d25ce370 | 348 | handler->hook = hook; |
7daf5226 | 349 | |
d25ce370 TB |
350 | va_start(args, hook); |
351 | while ((argtype = va_arg(args, printf_hook_argtype_t)) != PRINTF_HOOK_ARGTYPE_END) | |
352 | { | |
353 | if (++i >= ARGS_MAX) | |
354 | { | |
355 | DBG1("Too many arguments for printf hook with specifier '%c', not registered!", spec); | |
356 | va_end(args); | |
357 | free(handler); | |
358 | return; | |
359 | } | |
360 | handler->argtypes[i] = argtype; | |
361 | } | |
362 | va_end(args); | |
7daf5226 | 363 | |
d25ce370 | 364 | handler->numargs = i + 1; |
7daf5226 | 365 | |
d25ce370 TB |
366 | if (handler->numargs > 0) |
367 | { | |
f6bbcec3 MW |
368 | #if !defined(USE_VSTR) && \ |
369 | (defined(HAVE_PRINTF_FUNCTION) || defined(HAVE_PRINTF_SPECIFIER)) | |
370 | # ifdef HAVE_PRINTF_SPECIFIER | |
371 | register_printf_specifier(spec, custom_print, custom_arginfo); | |
372 | # else | |
d25ce370 | 373 | register_printf_function(spec, custom_print, custom_arginfo); |
f6bbcec3 | 374 | # endif |
d25ce370 TB |
375 | #else |
376 | Vstr_conf *conf = get_vstr_conf(); | |
377 | handler->name = malloc(2); | |
378 | handler->name[0] = spec; | |
379 | handler->name[1] = '\0'; | |
380 | vstr_fmt_add_handler(conf, handler); | |
381 | #endif | |
382 | printf_hooks[SPEC_TO_INDEX(spec)] = handler; | |
383 | } | |
384 | else | |
385 | { | |
386 | free(handler); | |
387 | } | |
db7ef624 MW |
388 | } |
389 | ||
390 | /** | |
552cc11b | 391 | * Implementation of printf_hook_t.destroy |
db7ef624 | 392 | */ |
552cc11b | 393 | static void destroy(private_printf_hook_t *this) |
db7ef624 | 394 | { |
d25ce370 | 395 | int i; |
bf45d6dd | 396 | #ifdef USE_VSTR |
d25ce370 TB |
397 | Vstr_conf *conf = get_vstr_conf(); |
398 | #endif | |
7daf5226 | 399 | |
d25ce370 TB |
400 | for (i = 0; i < NUM_HANDLERS; ++i) |
401 | { | |
402 | printf_hook_handler_t *handler = printf_hooks[i]; | |
403 | if (handler) | |
404 | { | |
bf45d6dd | 405 | #ifdef USE_VSTR |
d25ce370 TB |
406 | vstr_fmt_del(conf, handler->name); |
407 | free(handler->name); | |
408 | #endif | |
409 | free(handler); | |
410 | } | |
411 | } | |
7daf5226 | 412 | |
bf45d6dd | 413 | #ifdef USE_VSTR |
d25ce370 TB |
414 | /* freeing the Vstr_conf of the main thread */ |
415 | pthread_key_delete(vstr_conf_key); | |
416 | vstr_free_conf(conf); | |
417 | vstr_exit(); | |
418 | #endif | |
552cc11b | 419 | free(this); |
db7ef624 | 420 | } |
ee614711 | 421 | |
552cc11b MW |
422 | /* |
423 | * see header file | |
ee614711 | 424 | */ |
552cc11b | 425 | printf_hook_t *printf_hook_create() |
ee614711 | 426 | { |
552cc11b | 427 | private_printf_hook_t *this = malloc_thing(private_printf_hook_t); |
7daf5226 | 428 | |
d25ce370 | 429 | this->public.add_handler = (void(*)(printf_hook_t*, char, printf_hook_function_t, ...))add_handler; |
552cc11b | 430 | this->public.destroy = (void(*)(printf_hook_t*))destroy; |
7daf5226 | 431 | |
d25ce370 | 432 | memset(printf_hooks, 0, sizeof(printf_hooks)); |
7daf5226 | 433 | |
bf45d6dd | 434 | #ifdef USE_VSTR |
d25ce370 TB |
435 | if (!vstr_init()) |
436 | { | |
437 | DBG1("failed to initialize Vstr library!"); | |
438 | free(this); | |
439 | return NULL; | |
440 | } | |
441 | #endif | |
7daf5226 | 442 | |
552cc11b | 443 | return &this->public; |
ee614711 MW |
444 | } |
445 |