/* ***************************************************************************** * core/log.c -- libp7 logging utilities. * Copyright (C) 2016-2017 Thomas "Cakeisalie5" Touhey * * This file is part of libp7. * libp7 is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 3.0 of the License, * or (at your option) any later version. * * libp7 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with libp7; if not, see . * ************************************************************************** */ #include #include #include #include #include #define should_log(LEVEL) (log_setting <= (LEVEL)) /* ************************************************************************** */ /* Interact with the log settings at runtime */ /* ************************************************************************** */ /* The log setting */ static p7_loglevel_t log_setting = LOGLEVEL; /** * p7_setlog: * Set the log level at runtime. * * @arg level the level to set. */ p7_define_func(p7_setlog, void, p7_attrs_setlog, p7_loglevel_t level) { log_setting = level; } /** * p7_getlog: * Get the log level at runtime. * * @return the current level. */ p7_define_func(p7_getlog, p7_loglevel_t, p7_attrs_getlog, void) { return (log_setting); } /* ************************************************************************** */ /* Make conversions between settings values and strings, list settings */ /* ************************************************************************** */ /** * p7_loglevel_tostring: * Make a string out of a log level. * * @arg level the log level. * @return the string. */ p7_define_func(p7_loglevel_tostring, const char*, p7_attrs_loglevel_tostring, p7_loglevel_t level) { if (level >= 0 && level < 10) return ("info"); if (level >= 10 && level < 20) return ("warn"); if (level >= 20 && level < 30) return ("error"); if (level >= 30 && level < 40) return ("fatal"); return ("none"); } /** * p7_loglevel_fromstring: * Make a log level out of a string. * * @arg string the string. * @return the log level. */ p7_define_func(p7_loglevel_fromstring, p7_loglevel_t, p7_attrs_loglevel_fromstring, const char *string) { if (!strcmp(string, "info")) return (p7_loglevel_info); if (!strcmp(string, "warn")) return (p7_loglevel_warn); if (!strcmp(string, "error")) return (p7_loglevel_error); if (!strcmp(string, "fatal")) return (p7_loglevel_fatal); return (p7_loglevel_none); } /** * p7_loglevel_list: * List log levels. * * @arg callback the callback. * @arg cookie the callback cookie. */ p7_define_func(p7_loglevel_list, void, p7_attrs_loglevel_list, p7_liststr_t *callback, void *cookie) { (*callback)(cookie, "info"); (*callback)(cookie, "warn"); (*callback)(cookie, "error"); (*callback)(cookie, "fatal"); } /* ************************************************************************** */ /* Log simple messages */ /* ************************************************************************** */ /** * p7_log: * Log a simple message. * * @arg name the handle name. * @arg loglevel the log level. * @arg format the format. * @arg ... the arguments. */ void p7_log(const char *name, int loglevel, const char *format, ...) { va_list args; /* put the prefix */ name = name ? name : ""; if (should_log(loglevel)) fprintf(stderr, "[libp7 %s%s] ", name, p7_loglevel_tostring(loglevel)); /* put the main part */ va_start(args, format); if (should_log(loglevel)) { vfprintf(stderr, format, args); fputc('\n', stderr); } va_end(args); } /* ************************************************************************** */ /* Log memory */ /* ************************************************************************** */ /** * log_mem_hex: * Prints the octal interpretation of a max of two octets. * * @arg s the string where to put it * @arg m the memory zone to print * @arg n the size of the memory zone */ static void log_mem_hex(char *s, const unsigned char *m, size_t n) { size_t l = 0; while (l < 16) { /* put the hex number */ if (n) { p7_putascii((unsigned char*)s, *m++, 2); s += 2; } else { *s++ = ' '; *s++ = ' '; } /* decrement size of the memory zone to go */ n -= !!n; /* go to next character if s is at the ending of a group */ if (l++ % 2) s++; } } /** * log_mem_asc: * Prints the ascii interpretation of a max of two octets. * * @arg s the string where to put it * @arg m the memory zone to print * @arg n the size of the memory zone */ static void log_mem_asc(char *s, const unsigned char *m, size_t n) { size_t l = 0; /* for each byte */ while (n-- && l++ < 16) { /* put the character (or a dot if non printable) */ if (isprint(*m++)) *s++ = *((const char*)m - 1); else *s++ = '.'; } /* put the line ending */ *s++ = '\n'; *s = '\0'; } /** * p7_log_mem: * Print memory zone. * * @arg name the handle name. * @arg loglevel the message log level. * @arg m the memory zone to print. * @arg n the size of the memory zone. */ void p7_log_mem(const char *name, p7_loglevel_t loglevel, const void *m, size_t n) { char linebuf[58]; const unsigned char *p; /* check if the log level is good */ if (!should_log(loglevel)) return ; /* if nothing, print it directly */ name = name ? name : ""; if (!n) { fprintf(stderr, "[libp7 %s%s] (nothing)\n", name, p7_loglevel_tostring(loglevel)); return ; } /* prepare line buffer, and loop-loop-loop-loop-loop */ memcpy(linebuf, "0000 0000 0000 0000 0000 0000 0000 0000 ", 40); for (p = m; n > 0;) { /* fill in ascii-hex part */ log_mem_hex(&linebuf[0], p, n); /* fill in ascii part */ log_mem_asc(&linebuf[40], p, n); /* then print line */ fprintf(stderr, "[libp7 %s%s] ", name, p7_loglevel_tostring(loglevel)); fputs(linebuf, stderr); /* and increment pointer */ p += 16; n -= min(16, n); } }