| /* |
| * Copyright (C) 2009-2013 Tobias Brunner |
| * Copyright (C) 2006-2008 Martin Willi |
| * HSR Hochschule fuer Technik Rapperswil |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License as published by the |
| * Free Software Foundation; either version 2 of the License, or (at your |
| * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
| * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * for more details. |
| */ |
| |
| #include "printf_hook.h" |
| |
| #include <utils/utils.h> |
| #include <utils/debug.h> |
| |
| #include <stdio.h> |
| #include <stdarg.h> |
| #include <string.h> |
| #include <printf.h> |
| |
| typedef struct private_printf_hook_t private_printf_hook_t; |
| typedef struct printf_hook_handler_t printf_hook_handler_t; |
| |
| /** |
| * private data of printf_hook |
| */ |
| struct private_printf_hook_t { |
| |
| /** |
| * public functions |
| */ |
| printf_hook_t public; |
| }; |
| |
| /** |
| * struct with information about a registered handler |
| */ |
| struct printf_hook_handler_t { |
| |
| /** |
| * callback function |
| */ |
| printf_hook_function_t hook; |
| |
| /** |
| * number of arguments |
| */ |
| int numargs; |
| |
| /** |
| * types of the arguments, PA_* |
| */ |
| int argtypes[3]; |
| }; |
| |
| /** |
| * Data to pass to a printf hook. |
| */ |
| struct printf_hook_data_t { |
| |
| /** |
| * Output FILE stream |
| */ |
| FILE *stream;; |
| }; |
| |
| /* A-Z | 6 other chars | a-z */ |
| static printf_hook_handler_t *printf_hooks[58]; |
| |
| #define SPEC_TO_INDEX(spec) ((int)(spec) - (int)'A') |
| |
| /** |
| * Glibc variant of print_in_hook() |
| */ |
| size_t print_in_hook(printf_hook_data_t *data, char *fmt, ...) |
| { |
| ssize_t written; |
| va_list args; |
| |
| va_start(args, fmt); |
| written = vfprintf(data->stream, fmt, args); |
| va_end(args); |
| |
| if (written < 0) |
| { |
| written = 0; |
| } |
| return written; |
| } |
| |
| /** |
| * Printf hook print function. This is actually of type "printf_function", |
| * however glibc does it typedef to function, but uclibc to a pointer. |
| * So we redefine it here. |
| */ |
| static int custom_print(FILE *stream, const struct printf_info *info, |
| const void *const *args) |
| { |
| printf_hook_spec_t spec; |
| printf_hook_handler_t *handler; |
| printf_hook_data_t data = { |
| .stream = stream, |
| }; |
| |
| handler = printf_hooks[SPEC_TO_INDEX(info->spec)]; |
| spec.hash = info->alt; |
| spec.plus = info->showsign; |
| spec.minus = info->left; |
| spec.width = info->width; |
| |
| return handler->hook(&data, &spec, args); |
| } |
| |
| /** |
| * Printf hook arginfo function, which is actually of type |
| * "printf_arginfo_[size_]function". |
| */ |
| static int custom_arginfo(const struct printf_info *info, size_t n, int *argtypes |
| #ifdef HAVE_PRINTF_SPECIFIER |
| , int *size |
| #endif |
| ) |
| { |
| int i; |
| printf_hook_handler_t *handler; |
| |
| handler = printf_hooks[SPEC_TO_INDEX(info->spec)]; |
| if (handler->numargs <= n) |
| { |
| for (i = 0; i < handler->numargs; ++i) |
| { |
| argtypes[i] = handler->argtypes[i]; |
| } |
| } |
| /* we never set "size", as we have no user defined types */ |
| return handler->numargs; |
| } |
| |
| METHOD(printf_hook_t, add_handler, void, |
| private_printf_hook_t *this, char spec, |
| printf_hook_function_t hook, ...) |
| { |
| int i = -1; |
| bool failed = FALSE; |
| printf_hook_handler_t *handler; |
| printf_hook_argtype_t argtype; |
| va_list args; |
| |
| if (SPEC_TO_INDEX(spec) <= -1 || |
| SPEC_TO_INDEX(spec) >= countof(printf_hooks)) |
| { |
| DBG1(DBG_LIB, "'%c' is not a valid printf hook specifier, " |
| "not registered!", spec); |
| return; |
| } |
| |
| INIT(handler, |
| .hook = hook, |
| ); |
| |
| va_start(args, hook); |
| while (!failed) |
| { |
| argtype = va_arg(args, printf_hook_argtype_t); |
| |
| if (argtype == PRINTF_HOOK_ARGTYPE_END) |
| { |
| break; |
| } |
| if (++i >= countof(handler->argtypes)) |
| { |
| DBG1(DBG_LIB, "Too many arguments for printf hook with " |
| "specifier '%c', not registered!", spec); |
| failed = TRUE; |
| break; |
| } |
| switch (argtype) |
| { |
| case PRINTF_HOOK_ARGTYPE_INT: |
| handler->argtypes[i] = PA_INT; |
| break; |
| case PRINTF_HOOK_ARGTYPE_POINTER: |
| handler->argtypes[i] = PA_POINTER; |
| break; |
| default: |
| DBG1(DBG_LIB, "Invalid printf hook arg type for '%c'", spec); |
| failed = TRUE; |
| break; |
| } |
| } |
| va_end(args); |
| |
| handler->numargs = i + 1; |
| if (!failed && handler->numargs > 0) |
| { |
| # ifdef HAVE_PRINTF_SPECIFIER |
| register_printf_specifier(spec, custom_print, custom_arginfo); |
| # else |
| register_printf_function(spec, custom_print, custom_arginfo); |
| # endif |
| printf_hooks[SPEC_TO_INDEX(spec)] = handler; |
| } |
| else |
| { |
| free(handler); |
| } |
| } |
| |
| METHOD(printf_hook_t, destroy, void, |
| private_printf_hook_t *this) |
| { |
| int i; |
| |
| for (i = 0; i < countof(printf_hooks); i++) |
| { |
| free(printf_hooks[i]); |
| } |
| free(this); |
| } |
| |
| /* |
| * see header file |
| */ |
| printf_hook_t *printf_hook_create() |
| { |
| private_printf_hook_t *this; |
| |
| INIT(this, |
| .public = { |
| .add_handler = _add_handler, |
| .destroy = _destroy, |
| }, |
| ); |
| |
| memset(printf_hooks, 0, sizeof(printf_hooks)); |
| |
| return &this->public; |
| } |