1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 1999-2021 Free Software Foundation, Inc.
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 3 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General
15 Public License along with this library. If not, see
16 <http://www.gnu.org/licenses/>. */
17
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21
22 #include <errno.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26
27 #include <mailutils/errno.h>
28 #include <mailutils/error.h>
29 #include <mailutils/nls.h>
30 #include <mailutils/cctype.h>
31 #include <mailutils/wordsplit.h>
32 #include <mailutils/stream.h>
33 #include <mailutils/stdstream.h>
34 #include <mailutils/iterator.h>
35 #include <mailutils/cstr.h>
36 #include <mailutils/io.h>
37
38 int mu_debug_line_info; /* Debug messages include source locations */
39
40 struct debug_category
41 {
42 char *name;
43 mu_debug_level_t level;
44 int isset;
45 };
46
47 static struct debug_category default_cattab[] = {
48 { "all" },
49 #define __MU_DEBCAT_C_ARRAY
50 #include <mailutils/sys/debcat.h>
51 #undef __MU_DEBCAT_C_ARRAY
52 };
53
54 static struct debug_category *cattab = default_cattab;
55 static size_t catcnt = MU_ARRAY_SIZE (default_cattab);
56 static size_t catmax = 0;
57 #define MU_DEFAULT_CATMAX 256;
58
59 size_t
mu_debug_register_category(char * name)60 mu_debug_register_category (char *name)
61 {
62 struct debug_category *newtab;
63 size_t n;
64
65 if (cattab == default_cattab)
66 {
67 /* Reallocate the array */
68 n = 2 * catcnt;
69
70 newtab = calloc (n, sizeof (newtab[0]));
71 if (!newtab)
72 {
73 mu_error (_("cannot reallocate debug category table"));
74 return (size_t)-1;
75 }
76 memcpy (newtab, cattab, catcnt * sizeof (cattab[0]));
77 cattab = newtab;
78 catmax = n;
79 }
80 else if (catcnt == catmax)
81 {
82 n = catmax + MU_DEFAULT_CATMAX;
83 newtab = realloc (cattab, n * sizeof (cattab[0]));
84 if (!newtab)
85 {
86 mu_error (_("cannot reallocate debug category table"));
87 return (size_t)-1;
88 }
89 else
90 {
91 cattab = newtab;
92 catmax = n;
93 }
94 }
95 cattab[catcnt].name = name;
96 cattab[catcnt].level = 0;
97 cattab[catcnt].isset = 0;
98 return catcnt++;
99 }
100
101 size_t
mu_debug_next_handle()102 mu_debug_next_handle ()
103 {
104 return catcnt ? catcnt : 1;
105 }
106
107 int
mu_debug_level_p(mu_debug_handle_t catn,mu_debug_level_t level)108 mu_debug_level_p (mu_debug_handle_t catn, mu_debug_level_t level)
109 {
110 return catn < catcnt
111 && ((cattab[catn].isset ? cattab[catn].level : cattab[0].level) &
112 MU_DEBUG_LEVEL_MASK (level));
113 }
114
115 int
mu_debug_category_match(mu_debug_handle_t catn,mu_debug_level_t mask)116 mu_debug_category_match (mu_debug_handle_t catn, mu_debug_level_t mask)
117 {
118 return catn < catcnt
119 && ((cattab[catn].isset ? cattab[catn].level : cattab[0].level) & mask);
120 }
121
122 static mu_debug_handle_t
find_category(const char * name,size_t len)123 find_category (const char *name, size_t len)
124 {
125 mu_debug_handle_t i;
126
127 for (i = 0; i < catcnt; i++)
128 {
129 if (strlen (cattab[i].name) == len &&
130 memcmp (cattab[i].name, name, len) == 0)
131 return i;
132 }
133 return (mu_debug_handle_t)-1;
134 }
135
136 void
mu_debug_enable_category(const char * catname,size_t catlen,mu_debug_level_t level)137 mu_debug_enable_category (const char *catname, size_t catlen,
138 mu_debug_level_t level)
139 {
140 mu_debug_handle_t catn = find_category (catname, catlen);
141 if (catn == (mu_debug_handle_t)-1)
142 {
143 mu_error (_("unknown category: %.*s"), (int) catlen, catname);
144 return;
145 }
146 cattab[catn].level = level;
147 cattab[catn].isset = 1;
148 }
149
150 void
mu_debug_disable_category(const char * catname,size_t catlen)151 mu_debug_disable_category (const char *catname, size_t catlen)
152 {
153 size_t catn = find_category (catname, catlen);
154 if (catn == (mu_debug_handle_t)-1)
155 {
156 mu_error (_("unknown category: %.*s"), (int) catlen, catname);
157 return;
158 }
159 cattab[catn].isset = 0;
160 }
161
162 int
mu_debug_category_level(const char * catname,size_t catlen,mu_debug_level_t * plev)163 mu_debug_category_level (const char *catname, size_t catlen,
164 mu_debug_level_t *plev)
165 {
166 mu_debug_handle_t catn;
167
168 if (catname)
169 {
170 catn = find_category (catname, catlen);
171 if (catn == (mu_debug_handle_t)-1)
172 return MU_ERR_NOENT;
173 }
174 else
175 catn = 0;
176 *plev = cattab[catn].level;
177 return 0;
178 }
179
180 int
mu_debug_set_category_level(mu_debug_handle_t catn,mu_debug_level_t level)181 mu_debug_set_category_level (mu_debug_handle_t catn, mu_debug_level_t level)
182 {
183 if (catn < catcnt)
184 {
185 cattab[catn].isset = 1;
186 cattab[catn].level = level;
187 return 0;
188 }
189 return MU_ERR_NOENT;
190 }
191
192 int
mu_debug_get_category_level(mu_debug_handle_t catn,mu_debug_level_t * plev)193 mu_debug_get_category_level (mu_debug_handle_t catn, mu_debug_level_t *plev)
194 {
195 if (catn < catcnt)
196 {
197 if (!cattab[catn].isset)
198 *plev = 0;
199 else
200 *plev = cattab[catn].level;
201 return 0;
202 }
203 return MU_ERR_NOENT;
204 }
205
206 static char *mu_debug_level_str[] = {
207 "error",
208 "trace0",
209 "trace1",
210 "trace2",
211 "trace3",
212 "trace4",
213 "trace5",
214 "trace6",
215 "trace7",
216 "trace8",
217 "trace9",
218 "prot"
219 };
220
221 static int
mu_debug_level_from_string(const char * str,mu_debug_level_t * lev,char ** endp)222 mu_debug_level_from_string (const char *str, mu_debug_level_t *lev,
223 char **endp)
224 {
225 int i;
226 const char *p;
227 char *q;
228
229 for (i = 0; i < MU_ARRAY_SIZE (mu_debug_level_str); i++)
230 {
231 for (p = str, q = mu_debug_level_str[i]; ; p++, q++)
232 {
233 if (!*q)
234 {
235 if (endp)
236 *endp = (char*) p;
237 *lev = i;
238 return 0;
239 }
240 if (*q != *p)
241 break;
242 }
243 }
244 return MU_ERR_NOENT;
245 }
246
247 static void
parse_spec(const char * spec)248 parse_spec (const char *spec)
249 {
250 char *q;
251 mu_debug_level_t level;
252
253 if (mu_isdigit (*spec))
254 {
255 level = strtoul (spec, &q, 0);
256 if (*q)
257 mu_error (_("%s: wrong debug spec near %s"), spec, q);
258 else
259 {
260 cattab[0].level = level;
261 cattab[0].isset = 1;
262 }
263 return;
264 }
265
266 if (*spec == '!')
267 mu_debug_disable_category (spec + 1, strlen (spec + 1));
268 else
269 {
270 char *p;
271 size_t len;
272
273 p = strchr (spec, '.');
274 if (p)
275 {
276 struct mu_wordsplit ws;
277
278 len = p - spec;
279 ws.ws_delim = ",";
280 if (mu_wordsplit (p + 1, &ws,
281 MU_WRDSF_DELIM|MU_WRDSF_WS|
282 MU_WRDSF_NOVAR|MU_WRDSF_NOCMD))
283 {
284 mu_error (_("cannot split line `%s': %s"), p + 1,
285 mu_wordsplit_strerror (&ws));
286 return;
287 }
288 else
289 {
290 size_t i;
291 mu_debug_level_t lev = 0;
292 mu_debug_level_t xlev = 0;
293 char *end;
294
295 for (i = 0; i < ws.ws_wordc; i++)
296 {
297 char *s = ws.ws_wordv[i];
298 int exact = 0;
299 mu_debug_level_t n;
300 mu_debug_level_t *tgt = &lev;
301
302 if (*s == '!')
303 {
304 if (i == 0)
305 lev = MU_DEBUG_LEVEL_UPTO (MU_DEBUG_PROT);
306 tgt = &xlev;
307 s++;
308 }
309 if (*s == '=')
310 {
311 exact = 1;
312 s++;
313 }
314
315 if (mu_debug_level_from_string (s, &n, &end))
316 {
317 mu_error (_("unknown level `%s'"), s);
318 continue;
319 }
320 else if (*end == '-')
321 {
322 mu_debug_level_t l;
323
324 s = end + 1;
325 if (mu_debug_level_from_string (s, &l, &end))
326 {
327 mu_error (_("unknown level `%s'"), s);
328 continue;
329 }
330 else if (*end)
331 {
332 mu_error (_("invalid level: %s"), s);
333 continue;
334 }
335 if (n < l)
336 *tgt |= MU_DEBUG_LEVEL_RANGE (n, l);
337 else
338 *tgt |= MU_DEBUG_LEVEL_RANGE (l, n);
339 }
340 else if (*end)
341 {
342 mu_error (_("invalid level: %s"), s);
343 continue;
344 }
345 else if (exact)
346 *tgt |= MU_DEBUG_LEVEL_MASK (n);
347 else
348 *tgt |= MU_DEBUG_LEVEL_UPTO (n);
349 }
350
351 level = lev & ~xlev;
352 }
353 }
354 else
355 {
356 len = strlen (spec);
357 level = MU_DEBUG_LEVEL_UPTO (MU_DEBUG_PROT);
358 }
359 mu_debug_enable_category (spec, len, level);
360 }
361 }
362
363 void
mu_debug_parse_spec(const char * spec)364 mu_debug_parse_spec (const char *spec)
365 {
366 struct mu_wordsplit ws;
367
368 ws.ws_delim = ";";
369 if (mu_wordsplit (spec, &ws,
370 MU_WRDSF_DELIM|MU_WRDSF_WS|
371 MU_WRDSF_NOVAR|MU_WRDSF_NOCMD))
372 {
373 mu_error (_("cannot split line `%s': %s"), spec,
374 mu_wordsplit_strerror (&ws));
375 }
376 else
377 {
378 size_t i;
379
380 for (i = 0; i < ws.ws_wordc; i++)
381 parse_spec (ws.ws_wordv[i]);
382 mu_wordsplit_free (&ws);
383 }
384 }
385
386 void
mu_debug_clear_all()387 mu_debug_clear_all ()
388 {
389 int i;
390
391 for (i = 0; i < catcnt; i++)
392 cattab[i].isset = 0;
393 }
394
395 #define _LEVEL_ALL MU_DEBUG_LEVEL_UPTO(MU_DEBUG_PROT)
396
397 static int
name_matches(char ** names,char * str)398 name_matches (char **names, char *str)
399 {
400 int i;
401
402 for (i = 0; names[i]; i++)
403 if (strcmp (names[i], str) == 0)
404 return 1;
405 return 0;
406 }
407
408 int
mu_debug_format_spec(mu_stream_t str,const char * names,int showunset)409 mu_debug_format_spec (mu_stream_t str, const char *names, int showunset)
410 {
411 int i;
412 size_t cnt = 0;
413 int rc = 0;
414 struct mu_wordsplit ws;
415
416 if (names)
417 {
418 ws.ws_delim = ";";
419 if (mu_wordsplit (names, &ws,
420 MU_WRDSF_DELIM|MU_WRDSF_WS|
421 MU_WRDSF_NOVAR|MU_WRDSF_NOCMD))
422 return errno;
423 }
424
425 for (i = 0; i < catcnt; i++)
426 {
427 if (names && !name_matches (ws.ws_wordv, cattab[i].name))
428 continue;
429
430 if (cattab[i].isset && cattab[i].level)
431 {
432 if (cnt)
433 {
434 rc = mu_stream_printf(str, ";");
435 if (rc)
436 break;
437 }
438 rc = mu_stream_printf (str, "%s", cattab[i].name);
439 if (rc)
440 break;
441 if (cattab[i].level != _LEVEL_ALL)
442 {
443 mu_debug_level_t j = MU_DEBUG_ERROR, minl, maxl;
444 int delim = '.';
445
446 while (1)
447 {
448 /* Find the least bit set */
449 for (; j <= MU_DEBUG_PROT; j++)
450 if (cattab[i].level & MU_DEBUG_LEVEL_MASK (j))
451 break;
452
453 if (j > MU_DEBUG_PROT)
454 break;
455
456 minl = j;
457
458 for (; j + 1 <= MU_DEBUG_PROT &&
459 cattab[i].level & MU_DEBUG_LEVEL_MASK (j + 1);
460 j++)
461 ;
462
463 maxl = j++;
464
465 if (minl == maxl)
466 rc = mu_stream_printf (str, "%c=%s", delim,
467 mu_debug_level_str[minl]);
468 else if (minl == 0)
469 rc = mu_stream_printf (str, "%c%s", delim,
470 mu_debug_level_str[maxl]);
471 else
472 rc = mu_stream_printf (str, "%c%s-%s", delim,
473 mu_debug_level_str[minl],
474 mu_debug_level_str[maxl]);
475 if (rc)
476 break;
477 delim = ',';
478 }
479 }
480 cnt++;
481 }
482 else if (showunset)
483 {
484 if (cnt)
485 {
486 rc = mu_stream_printf(str, ";");
487 if (rc)
488 break;
489 }
490 rc = mu_stream_printf(str, "!%s", cattab[i].name);
491 if (rc)
492 break;
493 cnt++;
494 }
495 }
496 if (names)
497 mu_wordsplit_free (&ws);
498 return rc;
499 }
500
501
502 /* Iterator */
503
504 static mu_iterator_t iterator_head;
505
506 #define ITR_BACKWARDS 0x01
507 #define ITR_SKIPUNSET 0x02
508 #define ITR_FINISHED 0x04
509
510 struct debug_iterator
511 {
512 size_t pos;
513 int flags;
514 };
515
516 static int
first(void * owner)517 first (void *owner)
518 {
519 struct debug_iterator *itr = owner;
520 itr->flags &= ~ITR_FINISHED;
521 if (itr->flags & ITR_BACKWARDS)
522 itr->pos = catcnt - 1;
523 else
524 itr->pos = 0;
525 return 0;
526 }
527
528 static int
next(void * owner)529 next (void *owner)
530 {
531 struct debug_iterator *itr = owner;
532 itr->flags &= ~ITR_FINISHED;
533 do
534 {
535 if (itr->flags & ITR_BACKWARDS)
536 {
537 if (itr->pos)
538 itr->pos--;
539 else
540 itr->flags |= ITR_FINISHED;
541 }
542 else
543 {
544 if (itr->pos < catcnt - 1)
545 itr->pos++;
546 else
547 itr->flags |= ITR_FINISHED;
548 }
549 }
550 while ((itr->flags & ITR_SKIPUNSET) &&
551 !(itr->flags & ITR_FINISHED) &&
552 !cattab[itr->pos].isset);
553
554 return 0;
555 }
556
557 static int
getitem(void * owner,void ** pret,const void ** pkey)558 getitem (void *owner, void **pret, const void **pkey)
559 {
560 struct debug_iterator *itr = owner;
561 *(mu_debug_level_t*) pret = cattab[itr->pos].level;
562 if (pkey)
563 *pkey = cattab[itr->pos].name;
564 return 0;
565 }
566
567 static int
finished_p(void * owner)568 finished_p (void *owner)
569 {
570 struct debug_iterator *itr = owner;
571 return itr->flags & ITR_FINISHED;
572 }
573
574 static int
delitem(void * owner,void * item)575 delitem (void *owner, void *item)
576 {
577 struct debug_iterator *itr = owner;
578 return mu_c_strcasecmp (cattab[itr->pos].name, (char *) item) == 0 ?
579 MU_ITR_DELITEM_NEXT : MU_ITR_DELITEM_NOTHING;
580 }
581
582 static int
list_data_dup(void ** ptr,void * owner)583 list_data_dup (void **ptr, void *owner)
584 {
585 *ptr = malloc (sizeof (struct debug_iterator));
586 if (*ptr == NULL)
587 return ENOMEM;
588 memcpy (*ptr, owner, sizeof (struct debug_iterator));
589 return 0;
590 }
591
592 static int
list_itrctl(void * owner,enum mu_itrctl_req req,void * arg)593 list_itrctl (void *owner, enum mu_itrctl_req req, void *arg)
594 {
595 struct debug_iterator *itr = owner;
596
597 switch (req)
598 {
599 case mu_itrctl_tell:
600 /* Return current position in the object.
601 NOTE: Positions are 1-based.
602 */
603 if (!arg)
604 return EINVAL;
605 *(size_t*)arg = itr->pos + 1;
606 break;
607
608 case mu_itrctl_delete:
609 case mu_itrctl_delete_nd:
610 /* Delete current element */
611 cattab[itr->pos].level = 0;
612 cattab[itr->pos].isset = 0;
613 break;
614
615 case mu_itrctl_replace:
616 case mu_itrctl_replace_nd:
617 if (!arg)
618 return EINVAL;
619 cattab[itr->pos].level = *(mu_debug_level_t*)arg;
620 break;
621
622 case mu_itrctl_qry_direction:
623 if (!arg)
624 return EINVAL;
625 else
626 *(int*)arg = itr->flags & ITR_BACKWARDS;
627 break;
628
629 case mu_itrctl_set_direction:
630 if (!arg)
631 return EINVAL;
632 else
633 itr->flags |= ITR_BACKWARDS;
634 break;
635
636 case mu_itrctl_count:
637 if (!arg)
638 return EINVAL;
639 *(size_t*)arg = catcnt;
640 break;
641
642 default:
643 return ENOSYS;
644 }
645 return 0;
646 }
647
648 int
mu_debug_get_iterator(mu_iterator_t * piterator,int skipunset)649 mu_debug_get_iterator (mu_iterator_t *piterator, int skipunset)
650 {
651 int status;
652 mu_iterator_t iterator;
653 struct debug_iterator *itr;
654
655 itr = malloc (sizeof *itr);
656 if (!itr)
657 return ENOMEM;
658 itr->pos = 0;
659 itr->flags = skipunset ? ITR_SKIPUNSET : 0;
660 status = mu_iterator_create (&iterator, itr);
661 if (status)
662 {
663 free (itr);
664 return status;
665 }
666
667 mu_iterator_set_first (iterator, first);
668 mu_iterator_set_next (iterator, next);
669 mu_iterator_set_getitem (iterator, getitem);
670 mu_iterator_set_finished_p (iterator, finished_p);
671 mu_iterator_set_delitem (iterator, delitem);
672 mu_iterator_set_dup (iterator, list_data_dup);
673 mu_iterator_set_itrctl (iterator, list_itrctl);
674
675 mu_iterator_attach (&iterator_head, iterator);
676
677 *piterator = iterator;
678 return 0;
679 }
680
681
682 void
mu_debug_log(const char * fmt,...)683 mu_debug_log (const char *fmt, ...)
684 {
685 va_list ap;
686 char *buf = NULL;
687 size_t buflen = 0;
688 size_t n;
689 int rc;
690
691 mu_diag_init ();
692 va_start (ap, fmt);
693 rc = mu_vasnprintf (&buf, &buflen, fmt, ap);
694 va_end (ap);
695 if (rc == 0)
696 {
697 size_t i;
698 int nl = 0;
699 for (i = 0; buf[i]; i += n)
700 {
701 n = strcspn (buf + i, "\n");
702 if ((nl = buf[i + n]))
703 ++n;
704 mu_stream_printf (mu_strerr, "\033s<%d>", MU_LOG_DEBUG);
705 mu_stream_write (mu_strerr, buf + i, n, NULL);
706 }
707 if (!nl)
708 mu_stream_write (mu_strerr, "\n", 1, NULL);
709 }
710 free (buf);
711 }
712
713 void
mu_debug_log_begin(const char * fmt,...)714 mu_debug_log_begin (const char *fmt, ...)
715 {
716 va_list ap;
717
718 mu_diag_init ();
719 mu_stream_flush (mu_strerr);
720 va_start (ap, fmt);
721 mu_stream_printf (mu_strerr, "\033s<%d>", MU_LOG_DEBUG);
722 mu_stream_vprintf (mu_strerr, fmt, ap);
723 va_end (ap);
724 }
725
726 void
mu_debug_log_cont(const char * fmt,...)727 mu_debug_log_cont (const char *fmt, ...)
728 {
729 va_list ap;
730
731 va_start (ap, fmt);
732 mu_stream_vprintf (mu_strerr, fmt, ap);
733 va_end (ap);
734 }
735
736 void
mu_debug_log_end(const char * fmt,...)737 mu_debug_log_end (const char *fmt, ...)
738 {
739 va_list ap;
740
741 va_start (ap, fmt);
742 mu_stream_vprintf (mu_strerr, fmt, ap);
743 mu_stream_write (mu_strerr, "\n", 1, NULL);
744 va_end (ap);
745 }
746
747 void
mu_debug_log_nl()748 mu_debug_log_nl ()
749 {
750 mu_stream_write (mu_strerr, "\n", 1, NULL);
751 }
752