/* GNU Mailutils -- a suite of utilities for electronic mail Copyright (C) 1999, 2000, 2001, 2004, 2005, 2007, 2008, 2010, 2011 Free Software Foundation, Inc. This library 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 of the License, or (at your option) any later version. This library 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 this library. If not, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include int mu_debug_line_info; /* Debug messages include source locations */ struct debug_category { char *name; mu_debug_level_t level; int isset; }; static struct debug_category default_cattab[] = { { "all" }, #define __MU_DEBCAT_C_ARRAY #include #undef __MU_DEBCAT_C_ARRAY }; static struct debug_category *cattab = default_cattab; static size_t catcnt = MU_ARRAY_SIZE (default_cattab); static size_t catmax = 0; #define MU_DEFAULT_CATMAX 256; size_t mu_debug_register_category (char *name) { struct debug_category *newtab; size_t n; if (cattab == default_cattab) { /* Reallocate the array */ n = 2 * catcnt; newtab = calloc (n, sizeof (newtab[0])); if (!newtab) { mu_error (_("cannot reallocate debug category table")); return (size_t)-1; } memcpy (newtab, cattab, catcnt * sizeof (cattab[0])); cattab = newtab; catmax = n; } else if (catcnt == catmax) { n = catmax + MU_DEFAULT_CATMAX; newtab = realloc (cattab, n * sizeof (cattab[0])); if (!newtab) { mu_error (_("cannot reallocate debug category table")); return (size_t)-1; } else { cattab = newtab; catmax = n; } } cattab[catcnt].name = name; cattab[catcnt].level = 0; cattab[catcnt].isset = 0; return catcnt++; } size_t mu_debug_next_handle () { return catcnt ? catcnt : 1; } int mu_debug_level_p (mu_debug_handle_t catn, mu_debug_level_t level) { return catn < catcnt && ((cattab[catn].isset ? cattab[catn].level : cattab[0].level) & MU_DEBUG_LEVEL_MASK (level)); } int mu_debug_category_match (mu_debug_handle_t catn, mu_debug_level_t mask) { return catn < catcnt && ((cattab[catn].isset ? cattab[catn].level : cattab[0].level) & mask); } static mu_debug_handle_t find_category (const char *name, size_t len) { mu_debug_handle_t i; for (i = 0; i < catcnt; i++) { if (strlen (cattab[i].name) == len && memcmp (cattab[i].name, name, len) == 0) return i; } return (mu_debug_handle_t)-1; } void mu_debug_enable_category (const char *catname, size_t catlen, mu_debug_level_t level) { mu_debug_handle_t catn = find_category (catname, catlen); if (catn == (mu_debug_handle_t)-1) { mu_error (_("unknown category: %.*s"), (int) catlen, catname); return; } cattab[catn].level = level; cattab[catn].isset = 1; } void mu_debug_disable_category (const char *catname, size_t catlen) { size_t catn = find_category (catname, catlen); if (catn == (mu_debug_handle_t)-1) { mu_error (_("unknown category: %.*s"), (int) catlen, catname); return; } cattab[catn].isset = 0; } int mu_debug_category_level (const char *catname, size_t catlen, mu_debug_level_t *plev) { mu_debug_handle_t catn; if (catname) { catn = find_category (catname, catlen); if (catn == (mu_debug_handle_t)-1) return MU_ERR_NOENT; } else catn = 0; *plev = cattab[catn].level; return 0; } int mu_debug_set_category_level (mu_debug_handle_t catn, mu_debug_level_t level) { if (catn < catcnt) { cattab[catn].isset = 1; cattab[catn].level = level; return 0; } return MU_ERR_NOENT; } int mu_debug_get_category_level (mu_debug_handle_t catn, mu_debug_level_t *plev) { if (catn < catcnt) { if (!cattab[catn].isset) *plev = 0; else *plev = cattab[catn].level; return 0; } return MU_ERR_NOENT; } static char *mu_debug_level_str[] = { "error", "trace0", "trace1", "trace2", "trace3", "trace4", "trace5", "trace6", "trace7", "trace8", "trace9", "prot" }; static int mu_debug_level_from_string (const char *str, mu_debug_level_t *lev, char **endp) { int i; const char *p; char *q; for (i = 0; i < MU_ARRAY_SIZE (mu_debug_level_str); i++) { for (p = str, q = mu_debug_level_str[i]; ; p++, q++) { if (!*q) { if (endp) *endp = (char*) p; *lev = i; return 0; } if (*q != *p) break; } } return MU_ERR_NOENT; } static void parse_spec (const char *spec) { char *q; mu_debug_level_t level; if (mu_isdigit (*spec)) { level = strtoul (spec, &q, 0); if (*q) mu_error (_("%s: wrong debug spec near %s"), spec, q); else { cattab[0].level = level; cattab[0].isset = 1; } return; } if (*spec == '!') mu_debug_disable_category (spec + 1, strlen (spec + 1)); else { char *p; size_t len; p = strchr (spec, '.'); if (p) { struct mu_wordsplit ws; len = p - spec; ws.ws_delim = ","; if (mu_wordsplit (p + 1, &ws, MU_WRDSF_DELIM|MU_WRDSF_WS| MU_WRDSF_NOVAR|MU_WRDSF_NOCMD)) { mu_error (_("cannot split line `%s': %s"), p + 1, mu_wordsplit_strerror (&ws)); return; } else { size_t i; mu_debug_level_t lev = 0; mu_debug_level_t xlev = 0; char *end; for (i = 0; i < ws.ws_wordc; i++) { char *s = ws.ws_wordv[i]; int exact = 0; mu_debug_level_t n; mu_debug_level_t *tgt = &lev; if (*s == '!') { if (i == 0) lev = MU_DEBUG_LEVEL_UPTO (MU_DEBUG_PROT); tgt = &xlev; s++; } if (*s == '=') { exact = 1; s++; } if (mu_debug_level_from_string (s, &n, &end)) { mu_error (_("unknown level `%s'"), s); continue; } else if (*end == '-') { mu_debug_level_t l; s = end + 1; if (mu_debug_level_from_string (s, &l, &end)) { mu_error (_("unknown level `%s'"), s); continue; } else if (*end) { mu_error (_("invalid level: %s"), s); continue; } if (n < l) *tgt |= MU_DEBUG_LEVEL_RANGE (n, l); else *tgt |= MU_DEBUG_LEVEL_RANGE (l, n); } else if (*end) { mu_error (_("invalid level: %s"), s); continue; } else if (exact) *tgt |= MU_DEBUG_LEVEL_MASK (n); else *tgt |= MU_DEBUG_LEVEL_UPTO (n); } level = lev & ~xlev; } } else { len = strlen (spec); level = MU_DEBUG_LEVEL_UPTO (MU_DEBUG_PROT); } mu_debug_enable_category (spec, len, level); } } void mu_debug_parse_spec (const char *spec) { struct mu_wordsplit ws; ws.ws_delim = ";"; if (mu_wordsplit (spec, &ws, MU_WRDSF_DELIM|MU_WRDSF_WS| MU_WRDSF_NOVAR|MU_WRDSF_NOCMD)) { mu_error (_("cannot split line `%s': %s"), spec, mu_wordsplit_strerror (&ws)); } else { size_t i; for (i = 0; i < ws.ws_wordc; i++) parse_spec (ws.ws_wordv[i]); mu_wordsplit_free (&ws); } } void mu_debug_clear_all () { int i; for (i = 0; i < catcnt; i++) cattab[i].isset = 0; } #define _LEVEL_ALL MU_DEBUG_LEVEL_UPTO(MU_DEBUG_PROT) static int name_matches (char **names, char *str) { int i; for (i = 0; names[i]; i++) if (strcmp (names[i], str) == 0) return 1; return 0; } int mu_debug_format_spec (mu_stream_t str, const char *names, int showunset) { int i; size_t cnt = 0; int rc = 0; struct mu_wordsplit ws; if (names) { ws.ws_delim = ";"; if (mu_wordsplit (names, &ws, MU_WRDSF_DELIM|MU_WRDSF_WS| MU_WRDSF_NOVAR|MU_WRDSF_NOCMD)) return errno; } for (i = 0; i < catcnt; i++) { if (names && !name_matches (ws.ws_wordv, cattab[i].name)) continue; if (cattab[i].isset && cattab[i].level) { if (cnt) { rc = mu_stream_printf(str, ";"); if (rc) break; } rc = mu_stream_printf (str, "%s", cattab[i].name); if (rc) break; if (cattab[i].level != _LEVEL_ALL) { mu_debug_level_t j = MU_DEBUG_ERROR, minl, maxl; int delim = '.'; while (1) { /* Find the least bit set */ for (; j <= MU_DEBUG_PROT; j++) if (cattab[i].level & MU_DEBUG_LEVEL_MASK (j)) break; if (j > MU_DEBUG_PROT) break; minl = j; for (; j + 1 <= MU_DEBUG_PROT && cattab[i].level & MU_DEBUG_LEVEL_MASK (j + 1); j++) ; maxl = j++; if (minl == maxl) rc = mu_stream_printf (str, "%c=%s", delim, mu_debug_level_str[minl]); else if (minl == 0) rc = mu_stream_printf (str, "%c%s", delim, mu_debug_level_str[maxl]); else rc = mu_stream_printf (str, "%c%s-%s", delim, mu_debug_level_str[minl], mu_debug_level_str[maxl]); if (rc) break; delim = ','; } } cnt++; } else if (showunset) { if (cnt) { rc = mu_stream_printf(str, ";"); if (rc) break; } rc = mu_stream_printf(str, "!%s", cattab[i].name); if (rc) break; cnt++; } } if (names) mu_wordsplit_free (&ws); return rc; } /* Iterator */ static mu_iterator_t iterator_head; #define ITR_BACKWARDS 0x01 #define ITR_SKIPUNSET 0x02 #define ITR_FINISHED 0x04 struct debug_iterator { size_t pos; int flags; }; static int first (void *owner) { struct debug_iterator *itr = owner; itr->flags &= ~ITR_FINISHED; if (itr->flags & ITR_BACKWARDS) itr->pos = catcnt - 1; else itr->pos = 0; return 0; } static int next (void *owner) { struct debug_iterator *itr = owner; itr->flags &= ~ITR_FINISHED; do { if (itr->flags & ITR_BACKWARDS) { if (itr->pos) itr->pos--; else itr->flags |= ITR_FINISHED; } else { if (itr->pos < catcnt - 1) itr->pos++; else itr->flags |= ITR_FINISHED; } } while ((itr->flags & ITR_SKIPUNSET) && !(itr->flags & ITR_FINISHED) && !cattab[itr->pos].isset); return 0; } static int getitem (void *owner, void **pret, const void **pkey) { struct debug_iterator *itr = owner; *(mu_debug_level_t*) pret = cattab[itr->pos].level; if (pkey) *pkey = cattab[itr->pos].name; return 0; } static int finished_p (void *owner) { struct debug_iterator *itr = owner; return itr->flags & ITR_FINISHED; } static int curitem_p (void *owner, void *item) { struct debug_iterator *itr = owner; return mu_c_strcasecmp (cattab[itr->pos].name, (char *) item) == 0; } static int list_data_dup (void **ptr, void *owner) { *ptr = malloc (sizeof (struct debug_iterator)); if (*ptr == NULL) return ENOMEM; memcpy (*ptr, owner, sizeof (struct debug_iterator)); return 0; } static int list_itrctl (void *owner, enum mu_itrctl_req req, void *arg) { struct debug_iterator *itr = owner; switch (req) { case mu_itrctl_tell: /* Return current position in the object */ if (!arg) return EINVAL; *(size_t*)arg = itr->pos; break; case mu_itrctl_delete: case mu_itrctl_delete_nd: /* Delete current element */ cattab[itr->pos].level = 0; cattab[itr->pos].isset = 0; break; case mu_itrctl_replace: case mu_itrctl_replace_nd: if (!arg) return EINVAL; cattab[itr->pos].level = *(mu_debug_level_t*)arg; break; case mu_itrctl_qry_direction: if (!arg) return EINVAL; else *(int*)arg = itr->flags & ITR_BACKWARDS; break; case mu_itrctl_set_direction: if (!arg) return EINVAL; else itr->flags |= ITR_BACKWARDS; break; default: return ENOSYS; } return 0; } int mu_debug_get_iterator (mu_iterator_t *piterator, int skipunset) { int status; mu_iterator_t iterator; struct debug_iterator *itr; itr = malloc (sizeof *itr); if (!itr) return ENOMEM; itr->pos = 0; itr->flags = skipunset ? ITR_SKIPUNSET : 0; status = mu_iterator_create (&iterator, itr); if (status) { free (itr); return status; } mu_iterator_set_first (iterator, first); mu_iterator_set_next (iterator, next); mu_iterator_set_getitem (iterator, getitem); mu_iterator_set_finished_p (iterator, finished_p); mu_iterator_set_curitem_p (iterator, curitem_p); mu_iterator_set_dup (iterator, list_data_dup); mu_iterator_set_itrctl (iterator, list_itrctl); mu_iterator_attach (&iterator_head, iterator); *piterator = iterator; return 0; } void mu_debug_log (const char *fmt, ...) { va_list ap; mu_diag_init (); va_start (ap, fmt); mu_stream_printf (mu_strerr, "\033s<%d>", MU_LOG_DEBUG); mu_stream_vprintf (mu_strerr, fmt, ap); mu_stream_write (mu_strerr, "\n", 1, NULL); va_end (ap); } void mu_debug_log_begin (const char *fmt, ...) { va_list ap; mu_diag_init (); mu_stream_flush (mu_strerr); va_start (ap, fmt); mu_stream_printf (mu_strerr, "\033s<%d>", MU_LOG_DEBUG); mu_stream_vprintf (mu_strerr, fmt, ap); va_end (ap); } void mu_debug_log_cont (const char *fmt, ...) { va_list ap; va_start (ap, fmt); mu_stream_vprintf (mu_strerr, fmt, ap); va_end (ap); } void mu_debug_log_end (const char *fmt, ...) { va_list ap; va_start (ap, fmt); mu_stream_vprintf (mu_strerr, fmt, ap); mu_stream_write (mu_strerr, "\n", 1, NULL); va_end (ap); } void mu_debug_log_nl () { mu_stream_write (mu_strerr, "\n", 1, NULL); }