NeoMutt  2025-12-11-1039-g550ac6
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
parse.c
Go to the documentation of this file.
1
22
28
29#include "config.h"
30#include <stdbool.h>
31#include <stdint.h>
32#include <stdio.h>
33#include "mutt/lib.h"
34#include "config/lib.h"
35#include "core/lib.h"
36#include "commands/lib.h"
37#include "compmbox/lib.h"
38#include "expando/lib.h"
39#include "index/lib.h"
40#include "parse/lib.h"
41#include "pattern/lib.h"
42#include "globals.h"
43#include "hook.h"
44#include "module_data.h"
45#include "muttlib.h"
46
47extern const struct ExpandoDefinition IndexFormatDef[];
48extern const struct ExpandoDefinition CompressFormatDef[];
49
57enum CommandResult parse_charset_hook(const struct Command *cmd, struct Buffer *line,
58 const struct ParseContext *pc, struct ParseError *pe)
59{
60 struct Buffer *err = pe->message;
61
62 if (!MoreArgs(line))
63 {
64 buf_printf(err, _("%s: too few arguments"), cmd->name);
65 return MUTT_CMD_WARNING;
66 }
67
68 struct Buffer *alias = buf_pool_get();
69 struct Buffer *charset = buf_pool_get();
71
72 if (parse_extract_token(alias, line, TOKEN_NONE) < 0)
73 goto done;
74 if (parse_extract_token(charset, line, TOKEN_NONE) < 0)
75 goto done;
76
77 const enum LookupType type = (cmd->id == CMD_ICONV_HOOK) ? MUTT_LOOKUP_ICONV :
79
80 if (buf_is_empty(alias) || buf_is_empty(charset))
81 {
82 buf_printf(err, _("%s: too few arguments"), cmd->name);
84 }
85 else if (MoreArgs(line))
86 {
87 buf_printf(err, _("%s: too many arguments"), cmd->name);
88 buf_reset(line); // clean up buffer to avoid a mess with further rcfile processing
90 }
91 else if (mutt_ch_lookup_add(type, buf_string(alias), buf_string(charset), err))
92 {
94 }
95
96done:
97 buf_pool_release(&alias);
98 buf_pool_release(&charset);
99
100 return rc;
101}
102
111enum CommandResult parse_global_hook(const struct Command *cmd, struct Buffer *line,
112 const struct ParseContext *pc, struct ParseError *pe)
113{
114 struct Buffer *err = pe->message;
115
117 struct Hook *hook = NULL;
119
120 struct Buffer *command = buf_pool_get();
121
122 // TOKEN_SPACE allows the command to contain whitespace, without quoting
123 parse_extract_token(command, line, TOKEN_SPACE);
124
125 if (buf_is_empty(command))
126 {
127 buf_printf(err, _("%s: too few arguments"), cmd->name);
128 rc = MUTT_CMD_WARNING;
129 goto cleanup;
130 }
131
132 if (MoreArgs(line))
133 {
134 buf_printf(err, _("%s: too many arguments"), cmd->name);
135 rc = MUTT_CMD_WARNING;
136 goto cleanup;
137 }
138
139 /* check to make sure that a matching Hook doesn't already exist */
140 TAILQ_FOREACH(hook, &mod_data->hooks, entries)
141 {
142 /* Ignore duplicate global Hooks */
143 if ((hook->id == cmd->id) && mutt_str_equal(hook->command, buf_string(command)))
144 {
145 rc = MUTT_CMD_SUCCESS;
146 goto cleanup;
147 }
148 }
149
150 hook = hook_new();
151 hook->id = cmd->id;
152 hook->command = buf_strdup(command);
154 hook->pattern = NULL;
155 hook->regex.pattern = NULL;
156 hook->regex.regex = NULL;
157 hook->regex.pat_not = false;
158 hook->expando = NULL;
159
160 TAILQ_INSERT_TAIL(&mod_data->hooks, hook, entries);
161 rc = MUTT_CMD_SUCCESS;
162
163cleanup:
164 buf_pool_release(&command);
165 return rc;
166}
167
177enum CommandResult parse_pattern_hook(const struct Command *cmd, struct Buffer *line,
178 const struct ParseContext *pc, struct ParseError *pe)
179{
180 struct Buffer *err = pe->message;
181
183 struct Hook *hook = NULL;
185 bool pat_not = false;
186 struct PatternList *pat = NULL;
187
188 struct Buffer *command = buf_pool_get();
189 struct Buffer *pattern = buf_pool_get();
190
191 if (*line->dptr == '!')
192 {
193 line->dptr++;
194 SKIPWS(line->dptr);
195 pat_not = true;
196 }
197
198 parse_extract_token(pattern, line, TOKEN_NONE);
199
200 if (!MoreArgs(line))
201 {
202 buf_printf(err, _("%s: too few arguments"), cmd->name);
203 rc = MUTT_CMD_WARNING;
204 goto cleanup;
205 }
206
207 // TOKEN_SPACE allows the command to contain whitespace, without quoting
208 parse_extract_token(command, line, TOKEN_SPACE);
209
210 if (buf_is_empty(command))
211 {
212 buf_printf(err, _("%s: too few arguments"), cmd->name);
213 rc = MUTT_CMD_WARNING;
214 goto cleanup;
215 }
216
217 if (MoreArgs(line))
218 {
219 buf_printf(err, _("%s: too many arguments"), cmd->name);
220 rc = MUTT_CMD_WARNING;
221 goto cleanup;
222 }
223
224 const char *const c_default_hook = cs_subset_string(NeoMutt->sub, "default_hook");
225 if (c_default_hook)
226 {
227 mutt_check_simple(pattern, c_default_hook);
228 }
229
230 /* check to make sure that a matching hook doesn't already exist */
231 TAILQ_FOREACH(hook, &mod_data->hooks, entries)
232 {
233 if ((hook->id == cmd->id) && (hook->regex.pat_not == pat_not) &&
234 mutt_str_equal(buf_string(pattern), hook->regex.pattern))
235 {
236 /* these hooks allow multiple commands with the same pattern,
237 * so if we've already seen this pattern/command pair,
238 * just ignore it instead of creating a duplicate */
239 if (mutt_str_equal(hook->command, buf_string(command)))
240 {
241 rc = MUTT_CMD_SUCCESS;
242 goto cleanup;
243 }
244 }
245 }
246
247 PatternCompFlags comp_flags;
248 if (cmd->id == CMD_SEND2_HOOK)
249 comp_flags = MUTT_PC_SEND_MODE_SEARCH;
250 else if (cmd->id == CMD_SEND_HOOK)
251 comp_flags = MUTT_PC_NONE;
252 else
253 comp_flags = MUTT_PC_FULL_MSG;
254
255 struct MailboxView *mv_cur = get_current_mailbox_view();
256 pat = mutt_pattern_comp(mv_cur, buf_string(pattern), comp_flags, err);
257 if (!pat)
258 goto cleanup;
259
260 hook = hook_new();
261 hook->id = cmd->id;
262 hook->command = buf_strdup(command);
264 hook->pattern = pat;
266 hook->regex.regex = NULL;
267 hook->regex.pat_not = pat_not;
268 hook->expando = NULL;
269
270 TAILQ_INSERT_TAIL(&mod_data->hooks, hook, entries);
271 rc = MUTT_CMD_SUCCESS;
272
273cleanup:
274 buf_pool_release(&command);
276 return rc;
277}
278
289 struct Buffer *pattern, bool pat_not, struct Buffer *err)
290{
292 struct Hook *hook = NULL;
293 struct PatternList *pat = NULL;
294
295 /* check to make sure that a matching hook doesn't already exist */
296 TAILQ_FOREACH(hook, &mod_data->hooks, entries)
297 {
298 if ((hook->id == id) && (hook->regex.pat_not == pat_not) &&
299 mutt_str_equal(buf_string(pattern), hook->regex.pattern))
300 {
301 // Update an existing hook
302 FREE(&hook->command);
303 hook->command = buf_strdup(mailbox);
304 FREE(&hook->source_file);
306
307 expando_free(&hook->expando);
308 hook->expando = expando_parse(buf_string(mailbox), IndexFormatDef, err);
309
310 return MUTT_CMD_SUCCESS;
311 }
312 }
313
314 PatternCompFlags comp_flags;
315 if (id == CMD_FCC_HOOK)
316 comp_flags = MUTT_PC_NONE;
317 else
318 comp_flags = MUTT_PC_FULL_MSG;
319
320 struct MailboxView *mv_cur = get_current_mailbox_view();
321 pat = mutt_pattern_comp(mv_cur, buf_string(pattern), comp_flags, err);
322 if (!pat)
323 return MUTT_CMD_ERROR;
324
325 struct Expando *exp = expando_parse(buf_string(mailbox), IndexFormatDef, err);
326
327 hook = hook_new();
328 hook->id = id;
329 hook->command = buf_strdup(mailbox);
331 hook->pattern = pat;
332 hook->regex.pattern = buf_strdup(pattern);
333 hook->regex.regex = NULL;
334 hook->regex.pat_not = pat_not;
335 hook->expando = exp;
336
337 TAILQ_INSERT_TAIL(&mod_data->hooks, hook, entries);
338 return MUTT_CMD_SUCCESS;
339}
340
349enum CommandResult parse_mailbox_hook(const struct Command *cmd, struct Buffer *line,
350 const struct ParseContext *pc, struct ParseError *pe)
351{
352 struct Buffer *err = pe->message;
353
355 bool pat_not = false;
356
357 struct Buffer *pattern = buf_pool_get();
358 struct Buffer *mailbox = buf_pool_get();
359
360 if (*line->dptr == '!')
361 {
362 line->dptr++;
363 SKIPWS(line->dptr);
364 pat_not = true;
365 }
366
367 parse_extract_token(pattern, line, TOKEN_NONE);
368
369 if (!MoreArgs(line))
370 {
371 buf_printf(err, _("%s: too few arguments"), cmd->name);
372 rc = MUTT_CMD_WARNING;
373 goto cleanup;
374 }
375
376 parse_extract_token(mailbox, line, TOKEN_NONE);
377
378 if (buf_is_empty(mailbox))
379 {
380 buf_printf(err, _("%s: too few arguments"), cmd->name);
381 rc = MUTT_CMD_WARNING;
382 goto cleanup;
383 }
384
385 if (MoreArgs(line))
386 {
387 buf_printf(err, _("%s: too many arguments"), cmd->name);
388 rc = MUTT_CMD_WARNING;
389 goto cleanup;
390 }
391
392 const char *const c_default_hook = cs_subset_string(NeoMutt->sub, "default_hook");
393 if (c_default_hook)
394 {
395 mutt_check_simple(pattern, c_default_hook);
396 }
397
398 expand_path(mailbox, false);
399
400 if ((cmd->id == CMD_FCC_HOOK) || (cmd->id == CMD_FCC_SAVE_HOOK))
401 {
402 rc = add_mailbox_hook(CMD_FCC_HOOK, mailbox, pattern, pat_not, err);
403 if (rc != MUTT_CMD_SUCCESS)
404 goto cleanup;
405 }
406
407 if ((cmd->id == CMD_SAVE_HOOK) || (cmd->id == CMD_FCC_SAVE_HOOK))
408 {
409 rc = add_mailbox_hook(CMD_SAVE_HOOK, mailbox, pattern, pat_not, err);
410 if (rc != MUTT_CMD_SUCCESS)
411 goto cleanup;
412 }
413
414 rc = MUTT_CMD_SUCCESS;
415
416cleanup:
417 buf_pool_release(&pattern);
418 buf_pool_release(&mailbox);
419 return rc;
420}
421
428enum CommandResult parse_regex_hook(const struct Command *cmd, struct Buffer *line,
429 const struct ParseContext *pc, struct ParseError *pe)
430{
431 struct Buffer *err = pe->message;
432
434 struct Hook *hook = NULL;
436 bool pat_not = false;
437 regex_t *rx = NULL;
438
439 struct Buffer *regex = buf_pool_get();
440 struct Buffer *command = buf_pool_get();
441
442 if (*line->dptr == '!')
443 {
444 line->dptr++;
445 SKIPWS(line->dptr);
446 pat_not = true;
447 }
448
449 parse_extract_token(regex, line, TOKEN_NONE);
450
451 if (!MoreArgs(line))
452 {
453 buf_printf(err, _("%s: too few arguments"), cmd->name);
454 rc = MUTT_CMD_WARNING;
455 goto cleanup;
456 }
457
458 parse_extract_token(command, line, TOKEN_SPACE);
459
460 if (buf_is_empty(command))
461 {
462 buf_printf(err, _("%s: too few arguments"), cmd->name);
463 rc = MUTT_CMD_WARNING;
464 goto cleanup;
465 }
466
467 if (MoreArgs(line))
468 {
469 buf_printf(err, _("%s: too many arguments"), cmd->name);
470 rc = MUTT_CMD_WARNING;
471 goto cleanup;
472 }
473
474 /* check to make sure that a matching hook doesn't already exist */
475 TAILQ_FOREACH(hook, &mod_data->hooks, entries)
476 {
477 if ((hook->id == cmd->id) && (hook->regex.pat_not == pat_not) &&
478 mutt_str_equal(buf_string(regex), hook->regex.pattern))
479 {
480 // Ignore duplicate hooks
481 if (mutt_str_equal(hook->command, buf_string(command)))
482 {
483 rc = MUTT_CMD_SUCCESS;
484 goto cleanup;
485 }
486 }
487 }
488
489 /* Hooks not allowing full patterns: Check syntax of regex */
490 rx = MUTT_MEM_CALLOC(1, regex_t);
491 int rc2 = REG_COMP(rx, buf_string(regex), 0);
492 if (rc2 != 0)
493 {
494 regerror(rc2, rx, err->data, err->dsize);
495 buf_fix_dptr(err);
496 FREE(&rx);
497 goto cleanup;
498 }
499
500 hook = hook_new();
501 hook->id = cmd->id;
502 hook->command = buf_strdup(command);
504 hook->pattern = NULL;
505 hook->regex.pattern = buf_strdup(regex);
506 hook->regex.regex = rx;
507 hook->regex.pat_not = pat_not;
508 hook->expando = NULL;
509
510 TAILQ_INSERT_TAIL(&mod_data->hooks, hook, entries);
511 rc = MUTT_CMD_SUCCESS;
512
513cleanup:
514 buf_pool_release(&regex);
515 buf_pool_release(&command);
516 return rc;
517}
518
525enum CommandResult parse_folder_hook(const struct Command *cmd, struct Buffer *line,
526 const struct ParseContext *pc, struct ParseError *pe)
527{
528 struct Buffer *err = pe->message;
529
531 struct Hook *hook = NULL;
533 bool pat_not = false;
534 bool use_regex = true;
535 regex_t *rx = NULL;
536
537 struct Buffer *regex = buf_pool_get();
538 struct Buffer *command = buf_pool_get();
539
540 if (*line->dptr == '!')
541 {
542 line->dptr++;
543 SKIPWS(line->dptr);
544 pat_not = true;
545 }
546
547 parse_extract_token(regex, line, TOKEN_NONE);
548 if (mutt_str_equal(buf_string(regex), "-noregex"))
549 {
550 use_regex = false;
551 if (!MoreArgs(line))
552 {
553 buf_printf(err, _("%s: too few arguments"), cmd->name);
554 rc = MUTT_CMD_WARNING;
555 goto cleanup;
556 }
557 parse_extract_token(regex, line, TOKEN_NONE);
558 }
559
560 if (!MoreArgs(line))
561 {
562 buf_printf(err, _("%s: too few arguments"), cmd->name);
563 rc = MUTT_CMD_WARNING;
564 goto cleanup;
565 }
566
567 parse_extract_token(command, line, TOKEN_SPACE);
568
569 if (buf_is_empty(command))
570 {
571 buf_printf(err, _("%s: too few arguments"), cmd->name);
572 rc = MUTT_CMD_WARNING;
573 goto cleanup;
574 }
575
576 if (MoreArgs(line))
577 {
578 buf_printf(err, _("%s: too many arguments"), cmd->name);
579 rc = MUTT_CMD_WARNING;
580 goto cleanup;
581 }
582
583 /* Accidentally using the ^ mailbox shortcut in the .neomuttrc is a
584 * common mistake */
585 if ((buf_at(regex, 0) == '^') && !CurrentFolder)
586 {
587 buf_strcpy(err, _("current mailbox shortcut '^' is unset"));
588 goto cleanup;
589 }
590
591 struct Buffer *tmp = buf_pool_get();
592 buf_copy(tmp, regex);
593 expand_path(tmp, use_regex);
594
595 /* Check for other mailbox shortcuts that expand to the empty string.
596 * This is likely a mistake too */
597 if (buf_is_empty(tmp) && !buf_is_empty(regex))
598 {
599 buf_strcpy(err, _("mailbox shortcut expanded to empty regex"));
600 buf_pool_release(&tmp);
601 goto cleanup;
602 }
603
604 if (use_regex)
605 {
606 buf_copy(regex, tmp);
607 }
608 else
609 {
611 }
612 buf_pool_release(&tmp);
613
614 /* check to make sure that a matching hook doesn't already exist */
615 TAILQ_FOREACH(hook, &mod_data->hooks, entries)
616 {
617 if ((hook->id == cmd->id) && (hook->regex.pat_not == pat_not) &&
618 mutt_str_equal(buf_string(regex), hook->regex.pattern))
619 {
620 // Ignore duplicate hooks
621 if (mutt_str_equal(hook->command, buf_string(command)))
622 {
623 rc = MUTT_CMD_SUCCESS;
624 goto cleanup;
625 }
626 }
627 }
628
629 /* Hooks not allowing full patterns: Check syntax of regex */
630 rx = MUTT_MEM_CALLOC(1, regex_t);
631 int rc2 = REG_COMP(rx, buf_string(regex), 0);
632 if (rc2 != 0)
633 {
634 regerror(rc2, rx, err->data, err->dsize);
635 buf_fix_dptr(err);
636 FREE(&rx);
637 goto cleanup;
638 }
639
640 hook = hook_new();
641 hook->id = cmd->id;
642 hook->command = buf_strdup(command);
644 hook->pattern = NULL;
645 hook->regex.pattern = buf_strdup(regex);
646 hook->regex.regex = rx;
647 hook->regex.pat_not = pat_not;
648 hook->expando = NULL;
649
650 TAILQ_INSERT_TAIL(&mod_data->hooks, hook, entries);
651 rc = MUTT_CMD_SUCCESS;
652
653cleanup:
654 buf_pool_release(&regex);
655 buf_pool_release(&command);
656 return rc;
657}
658
666enum CommandResult parse_crypt_hook(const struct Command *cmd, struct Buffer *line,
667 const struct ParseContext *pc, struct ParseError *pe)
668{
669 struct Buffer *err = pe->message;
670
672 struct Hook *hook = NULL;
674 bool pat_not = false;
675 regex_t *rx = NULL;
676
677 struct Buffer *regex = buf_pool_get();
678 struct Buffer *keyid = buf_pool_get();
679
680 if (*line->dptr == '!')
681 {
682 line->dptr++;
683 SKIPWS(line->dptr);
684 pat_not = true;
685 }
686
687 parse_extract_token(regex, line, TOKEN_NONE);
688
689 if (!MoreArgs(line))
690 {
691 buf_printf(err, _("%s: too few arguments"), cmd->name);
692 rc = MUTT_CMD_WARNING;
693 goto cleanup;
694 }
695
696 parse_extract_token(keyid, line, TOKEN_NONE);
697
698 if (buf_is_empty(keyid))
699 {
700 buf_printf(err, _("%s: too few arguments"), cmd->name);
701 rc = MUTT_CMD_WARNING;
702 goto cleanup;
703 }
704
705 if (MoreArgs(line))
706 {
707 buf_printf(err, _("%s: too many arguments"), cmd->name);
708 rc = MUTT_CMD_WARNING;
709 goto cleanup;
710 }
711
712 /* check to make sure that a matching hook doesn't already exist */
713 TAILQ_FOREACH(hook, &mod_data->hooks, entries)
714 {
715 if ((hook->id == cmd->id) && (hook->regex.pat_not == pat_not) &&
716 mutt_str_equal(buf_string(regex), hook->regex.pattern))
717 {
718 // Ignore duplicate hooks
719 if (mutt_str_equal(hook->command, buf_string(keyid)))
720 {
721 rc = MUTT_CMD_SUCCESS;
722 goto cleanup;
723 }
724 }
725 }
726
727 /* Hooks not allowing full patterns: Check syntax of regex */
728 rx = MUTT_MEM_CALLOC(1, regex_t);
729 int rc2 = REG_COMP(rx, buf_string(regex), REG_ICASE);
730 if (rc2 != 0)
731 {
732 regerror(rc2, rx, err->data, err->dsize);
733 buf_fix_dptr(err);
734 FREE(&rx);
735 goto cleanup;
736 }
737
738 hook = hook_new();
739 hook->id = cmd->id;
740 hook->command = buf_strdup(keyid);
742 hook->pattern = NULL;
743 hook->regex.pattern = buf_strdup(regex);
744 hook->regex.regex = rx;
745 hook->regex.pat_not = pat_not;
746 hook->expando = NULL;
747
748 TAILQ_INSERT_TAIL(&mod_data->hooks, hook, entries);
749 rc = MUTT_CMD_SUCCESS;
750
751cleanup:
752 buf_pool_release(&regex);
753 buf_pool_release(&keyid);
754 return rc;
755}
756
763enum CommandResult parse_mbox_hook(const struct Command *cmd, struct Buffer *line,
764 const struct ParseContext *pc, struct ParseError *pe)
765{
766 struct Buffer *err = pe->message;
767
769 struct Hook *hook = NULL;
771 bool pat_not = false;
772 bool use_regex = true;
773 regex_t *rx = NULL;
774
775 struct Buffer *regex = buf_pool_get();
776 struct Buffer *command = buf_pool_get();
777
778 if (*line->dptr == '!')
779 {
780 line->dptr++;
781 SKIPWS(line->dptr);
782 pat_not = true;
783 }
784
785 parse_extract_token(regex, line, TOKEN_NONE);
786 if (mutt_str_equal(buf_string(regex), "-noregex"))
787 {
788 use_regex = false;
789 if (!MoreArgs(line))
790 {
791 buf_printf(err, _("%s: too few arguments"), cmd->name);
792 rc = MUTT_CMD_WARNING;
793 goto cleanup;
794 }
795 parse_extract_token(regex, line, TOKEN_NONE);
796 }
797
798 if (!MoreArgs(line))
799 {
800 buf_printf(err, _("%s: too few arguments"), cmd->name);
801 rc = MUTT_CMD_WARNING;
802 goto cleanup;
803 }
804
805 parse_extract_token(command, line, TOKEN_NONE);
806
807 if (buf_is_empty(command))
808 {
809 buf_printf(err, _("%s: too few arguments"), cmd->name);
810 rc = MUTT_CMD_WARNING;
811 goto cleanup;
812 }
813
814 if (MoreArgs(line))
815 {
816 buf_printf(err, _("%s: too many arguments"), cmd->name);
817 rc = MUTT_CMD_WARNING;
818 goto cleanup;
819 }
820
821 /* Accidentally using the ^ mailbox shortcut in the .neomuttrc is a
822 * common mistake */
823 if ((buf_at(regex, 0) == '^') && !CurrentFolder)
824 {
825 buf_strcpy(err, _("current mailbox shortcut '^' is unset"));
826 goto cleanup;
827 }
828
829 struct Buffer *tmp = buf_pool_get();
830 buf_copy(tmp, regex);
831 expand_path(tmp, use_regex);
832
833 /* Check for other mailbox shortcuts that expand to the empty string.
834 * This is likely a mistake too */
835 if (buf_is_empty(tmp) && !buf_is_empty(regex))
836 {
837 buf_strcpy(err, _("mailbox shortcut expanded to empty regex"));
838 buf_pool_release(&tmp);
839 goto cleanup;
840 }
841
842 if (use_regex)
843 {
844 buf_copy(regex, tmp);
845 }
846 else
847 {
849 }
850 buf_pool_release(&tmp);
851
852 expand_path(command, false);
853
854 /* check to make sure that a matching hook doesn't already exist */
855 TAILQ_FOREACH(hook, &mod_data->hooks, entries)
856 {
857 if ((hook->id == cmd->id) && (hook->regex.pat_not == pat_not) &&
858 mutt_str_equal(buf_string(regex), hook->regex.pattern))
859 {
860 // Update an existing hook
861 FREE(&hook->command);
862 hook->command = buf_strdup(command);
863 FREE(&hook->source_file);
865
866 expando_free(&hook->expando);
867 hook->expando = expando_parse(buf_string(command), IndexFormatDef, err);
868
869 rc = MUTT_CMD_SUCCESS;
870 goto cleanup;
871 }
872 }
873
874 /* Hooks not allowing full patterns: Check syntax of regex */
875 rx = MUTT_MEM_CALLOC(1, regex_t);
876 int rc2 = REG_COMP(rx, buf_string(regex), 0);
877 if (rc2 != 0)
878 {
879 regerror(rc2, rx, err->data, err->dsize);
880 buf_fix_dptr(err);
881 FREE(&rx);
882 goto cleanup;
883 }
884
885 struct Expando *exp = expando_parse(buf_string(command), IndexFormatDef, err);
886
887 hook = hook_new();
888 hook->id = cmd->id;
889 hook->command = buf_strdup(command);
891 hook->pattern = NULL;
892 hook->regex.pattern = buf_strdup(regex);
893 hook->regex.regex = rx;
894 hook->regex.pat_not = pat_not;
895 hook->expando = exp;
896
897 TAILQ_INSERT_TAIL(&mod_data->hooks, hook, entries);
898 rc = MUTT_CMD_SUCCESS;
899
900cleanup:
901 buf_pool_release(&regex);
902 buf_pool_release(&command);
903 return rc;
904}
905
914enum CommandResult parse_compress_hook(const struct Command *cmd, struct Buffer *line,
915 const struct ParseContext *pc, struct ParseError *pe)
916{
917 struct Buffer *err = pe->message;
918
920 struct Hook *hook = NULL;
921 struct Expando *exp = NULL;
923 bool pat_not = false;
924 regex_t *rx = NULL;
925
926 struct Buffer *regex = buf_pool_get();
927 struct Buffer *command = buf_pool_get();
928
929 if (*line->dptr == '!')
930 {
931 line->dptr++;
932 SKIPWS(line->dptr);
933 pat_not = true;
934 }
935
936 parse_extract_token(regex, line, TOKEN_NONE);
937
938 if (!MoreArgs(line))
939 {
940 buf_printf(err, _("%s: too few arguments"), cmd->name);
941 rc = MUTT_CMD_WARNING;
942 goto cleanup;
943 }
944
945 // TOKEN_SPACE allows the command to contain whitespace, without quoting
946 parse_extract_token(command, line, TOKEN_SPACE);
947
948 if (buf_is_empty(command))
949 {
950 buf_printf(err, _("%s: too few arguments"), cmd->name);
951 rc = MUTT_CMD_WARNING;
952 goto cleanup;
953 }
954
955 if (MoreArgs(line))
956 {
957 buf_printf(err, _("%s: too many arguments"), cmd->name);
958 rc = MUTT_CMD_WARNING;
959 goto cleanup;
960 }
961
962 exp = expando_parse(buf_string(command), CompressFormatDef, err);
963 if (!exp)
964 goto cleanup;
965
968 {
969 buf_strcpy(err, _("badly formatted command string"));
970 goto cleanup;
971 }
972
973 /* check to make sure that a matching hook doesn't already exist */
974 TAILQ_FOREACH(hook, &mod_data->hooks, entries)
975 {
976 if ((hook->id == cmd->id) && (hook->regex.pat_not == pat_not) &&
977 mutt_str_equal(buf_string(regex), hook->regex.pattern))
978 {
979 // Update an existing hook
980 FREE(&hook->command);
981 hook->command = buf_strdup(command);
982 FREE(&hook->source_file);
984 expando_free(&hook->expando);
985 hook->expando = exp;
986 exp = NULL;
987
988 rc = MUTT_CMD_SUCCESS;
989 goto cleanup;
990 }
991 }
992
993 /* Hooks not allowing full patterns: Check syntax of regex */
994 rx = MUTT_MEM_CALLOC(1, regex_t);
995 int rc2 = REG_COMP(rx, buf_string(regex), 0);
996 if (rc2 != 0)
997 {
998 regerror(rc2, rx, err->data, err->dsize);
999 buf_fix_dptr(err);
1000 FREE(&rx);
1001 goto cleanup;
1002 }
1003
1004 hook = hook_new();
1005 hook->id = cmd->id;
1006 hook->command = buf_strdup(command);
1008 hook->pattern = NULL;
1009 hook->regex.pattern = buf_strdup(regex);
1010 hook->regex.regex = rx;
1011 hook->regex.pat_not = pat_not;
1012 hook->expando = exp;
1013 exp = NULL;
1014
1015 TAILQ_INSERT_TAIL(&mod_data->hooks, hook, entries);
1016 rc = MUTT_CMD_SUCCESS;
1017
1018cleanup:
1019 expando_free(&exp);
1020 buf_pool_release(&regex);
1021 buf_pool_release(&command);
1022 return rc;
1023}
1024
1032void mutt_delete_hooks(struct HookList *hooks, enum CommandId id)
1033{
1034 struct Hook *h = NULL;
1035 struct Hook *tmp = NULL;
1036
1037 TAILQ_FOREACH_SAFE(h, hooks, entries, tmp)
1038 {
1039 if ((id == CMD_NONE) || (id == h->id))
1040 {
1041 TAILQ_REMOVE(hooks, h, entries);
1042 hook_free(&h);
1043 }
1044 }
1045}
1046
1050static void idxfmt_hashelem_free(int type, void *obj, intptr_t data)
1051{
1052 struct HookList *hl = obj;
1053 struct Hook *h = NULL;
1054 struct Hook *tmp = NULL;
1055
1056 TAILQ_FOREACH_SAFE(h, hl, entries, tmp)
1057 {
1058 TAILQ_REMOVE(hl, h, entries);
1059 hook_free(&h);
1060 }
1061
1062 FREE(&hl);
1063}
1064
1069void delete_idxfmt_hooks(struct HashTable **idx_fmt_hooks)
1070{
1071 mutt_hash_free(idx_fmt_hooks);
1072}
1073
1080enum CommandResult parse_index_hook(const struct Command *cmd, struct Buffer *line,
1081 const struct ParseContext *pc, struct ParseError *pe)
1082{
1083 struct Buffer *err = pe->message;
1084
1085 if (!MoreArgs(line))
1086 {
1087 buf_printf(err, _("%s: too few arguments"), cmd->name);
1088 return MUTT_CMD_WARNING;
1089 }
1090
1092 enum CommandResult rc = MUTT_CMD_ERROR;
1093 bool pat_not = false;
1094
1095 struct Buffer *name = buf_pool_get();
1096 struct Buffer *pattern = buf_pool_get();
1097 struct Buffer *fmt = buf_pool_get();
1098 struct Expando *exp = NULL;
1099
1100 if (!mod_data->idx_fmt_hooks)
1101 {
1104 }
1105
1106 parse_extract_token(name, line, TOKEN_NONE);
1107 struct HookList *hl = mutt_hash_find(mod_data->idx_fmt_hooks, buf_string(name));
1108
1109 if (*line->dptr == '!')
1110 {
1111 line->dptr++;
1112 SKIPWS(line->dptr);
1113 pat_not = true;
1114 }
1115 parse_extract_token(pattern, line, TOKEN_NONE);
1116
1117 if (!MoreArgs(line))
1118 {
1119 buf_printf(err, _("%s: too few arguments"), cmd->name);
1120 goto out;
1121 }
1122 parse_extract_token(fmt, line, TOKEN_NONE);
1123
1124 exp = expando_parse(buf_string(fmt), IndexFormatDef, err);
1125 if (!buf_is_empty(err))
1126 goto out;
1127
1128 if (MoreArgs(line))
1129 {
1130 buf_printf(err, _("%s: too many arguments"), cmd->name);
1131 goto out;
1132 }
1133
1134 const char *const c_default_hook = cs_subset_string(NeoMutt->sub, "default_hook");
1135 if (c_default_hook)
1136 mutt_check_simple(pattern, c_default_hook);
1137
1138 /* check to make sure that a matching hook doesn't already exist */
1139 struct Hook *hook = NULL;
1140 if (hl)
1141 {
1142 TAILQ_FOREACH(hook, hl, entries)
1143 {
1144 // Update an existing hook
1145 if ((hook->regex.pat_not == pat_not) &&
1147 {
1148 expando_free(&hook->expando);
1149 hook->expando = exp;
1150 exp = NULL;
1151 rc = MUTT_CMD_SUCCESS;
1152 goto out;
1153 }
1154 }
1155 }
1156
1157 /* MUTT_PC_PATTERN_DYNAMIC sets so that date ranges are regenerated during
1158 * matching. This of course is slower, but index-format-hook is commonly
1159 * used for date ranges, and they need to be evaluated relative to "now", not
1160 * the hook compilation time. */
1161 struct MailboxView *mv_cur = get_current_mailbox_view();
1162 struct PatternList *pat = mutt_pattern_comp(mv_cur, buf_string(pattern),
1164 err);
1165 if (!pat)
1166 goto out;
1167
1168 hook = hook_new();
1169 hook->id = CMD_INDEX_FORMAT_HOOK;
1170 hook->command = NULL;
1172 hook->pattern = pat;
1173 hook->regex.pattern = buf_strdup(pattern);
1174 hook->regex.regex = NULL;
1175 hook->regex.pat_not = pat_not;
1176 hook->expando = exp;
1177 exp = NULL;
1178
1179 if (!hl)
1180 {
1181 hl = MUTT_MEM_CALLOC(1, struct HookList);
1182 TAILQ_INIT(hl);
1183 mutt_hash_insert(mod_data->idx_fmt_hooks, buf_string(name), hl);
1184 }
1185
1186 TAILQ_INSERT_TAIL(hl, hook, entries);
1187 rc = MUTT_CMD_SUCCESS;
1188
1189out:
1190 buf_pool_release(&name);
1191 buf_pool_release(&pattern);
1192 buf_pool_release(&fmt);
1193 expando_free(&exp);
1194
1195 return rc;
1196}
1197
1204enum CommandResult parse_unhook(const struct Command *cmd, struct Buffer *line,
1205 const struct ParseContext *pc, struct ParseError *pe)
1206{
1207 struct Buffer *err = pe->message;
1208
1209 if (!MoreArgs(line))
1210 {
1211 buf_printf(err, _("%s: too few arguments"), cmd->name);
1212 return MUTT_CMD_WARNING;
1213 }
1214
1216 struct Buffer *token = buf_pool_get();
1218
1219 while (MoreArgs(line))
1220 {
1221 parse_extract_token(token, line, TOKEN_NONE);
1222 if (mutt_str_equal("*", buf_string(token)))
1223 {
1224 if (mod_data->current_hook_id != CMD_NONE)
1225 {
1226 buf_addstr(err, _("unhook: Can't do unhook * from within a hook"));
1227 goto done;
1228 }
1229 mutt_delete_hooks(&mod_data->hooks, CMD_NONE);
1232 break;
1233 }
1234 else
1235 {
1236 const struct Command *hook = command_find_by_name(&NeoMutt->commands,
1237 buf_string(token));
1238 if (!hook)
1239 {
1240 buf_printf(err, _("unhook: unknown hook type: %s"), buf_string(token));
1241 rc = MUTT_CMD_ERROR;
1242 goto done;
1243 }
1244
1245 if ((hook->id == CMD_CHARSET_HOOK) || (hook->id == CMD_ICONV_HOOK))
1246 {
1248 }
1249 else if ((mod_data->current_hook_id == hook->id) ||
1250 ((hook->id == CMD_FCC_SAVE_HOOK) &&
1251 ((mod_data->current_hook_id == CMD_FCC_HOOK) ||
1252 (mod_data->current_hook_id == CMD_SAVE_HOOK))))
1253 {
1254 buf_printf(err, _("unhook: Can't delete a %s from within a %s"),
1255 buf_string(token), buf_string(token));
1256 rc = MUTT_CMD_WARNING;
1257 goto done;
1258 }
1259 else if (hook->id == CMD_INDEX_FORMAT_HOOK)
1260 {
1262 }
1263 else if (hook->id == CMD_FCC_SAVE_HOOK)
1264 {
1267 }
1268 else
1269 {
1270 mutt_delete_hooks(&mod_data->hooks, hook->id);
1271 }
1272 }
1273 }
1274
1275 rc = MUTT_CMD_SUCCESS;
1276
1277done:
1278 buf_pool_release(&token);
1279 return rc;
1280}
const struct ExpandoDefinition IndexFormatDef[]
Expando definitions.
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition buffer.c:168
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:89
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition buffer.c:298
void buf_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition buffer.c:189
char buf_at(const struct Buffer *buf, size_t offset)
Return the character at the given offset.
Definition buffer.c:674
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:233
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:401
size_t buf_copy(struct Buffer *dst, const struct Buffer *src)
Copy a Buffer's contents to another Buffer.
Definition buffer.c:607
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition buffer.c:577
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
CommandId
ID of Command.
Definition command.h:61
@ CMD_SEND_HOOK
:send-hook
Definition command.h:110
@ CMD_FCC_SAVE_HOOK
:fcc-save-hook
Definition command.h:79
@ CMD_ICONV_HOOK
:iconv-hook
Definition command.h:85
@ CMD_INDEX_FORMAT_HOOK
:index-format-hook
Definition command.h:89
@ CMD_FCC_HOOK
:fcc-hook
Definition command.h:78
@ CMD_SEND2_HOOK
:send2-hook
Definition command.h:109
@ CMD_CHARSET_HOOK
:charset-hook
Definition command.h:72
@ CMD_NONE
No Command.
Definition command.h:62
@ CMD_SAVE_HOOK
:save-hook
Definition command.h:107
CommandResult
Error codes for command_t parse functions.
Definition command.h:37
@ MUTT_CMD_SUCCESS
Success: Command worked.
Definition command.h:40
@ MUTT_CMD_ERROR
Error: Can't help the user.
Definition command.h:38
@ MUTT_CMD_WARNING
Warning: Help given to the user.
Definition command.h:39
const struct Command * command_find_by_name(const struct CommandArray *ca, const char *name)
Find a NeoMutt Command by its name.
Definition commands.c:145
NeoMutt Commands.
struct PatternList * mutt_pattern_comp(struct MailboxView *mv, const char *s, PatternCompFlags flags, struct Buffer *err)
Create a Pattern.
Definition compile.c:964
const struct ExpandoDefinition CompressFormatDef[]
Expando definitions.
Definition compress.c:89
Compressed mbox local mailbox type.
@ ED_CMP_FROM
'from' path
Definition lib.h:51
@ ED_CMP_TO
'to' path
Definition lib.h:52
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition helpers.c:291
Convenience wrapper for the config headers.
Convenience wrapper for the core headers.
@ ED_COMPRESS
Compress ED_CMP_ ExpandoDataCompress.
Definition domain.h:40
const struct ExpandoNode * expando_find_node(const struct Expando *exp, int did, int uid)
Find a Node in an Expando tree.
Definition expando.c:109
struct Expando * expando_parse(const char *str, const struct ExpandoDefinition *defs, struct Buffer *err)
Parse an Expando string.
Definition expando.c:124
void expando_free(struct Expando **ptr)
Free an Expando object.
Definition expando.c:61
Parse Expando string.
int parse_extract_token(struct Buffer *dest, struct Buffer *line, TokenFlags flags)
Extract one token from a string.
Definition extract.c:49
#define MoreArgs(buf)
Definition extract.h:32
@ TOKEN_SPACE
Don't treat whitespace as a term.
Definition extract.h:53
@ TOKEN_NONE
No flags are set.
Definition extract.h:50
int mutt_file_sanitize_regex(struct Buffer *dest, const char *src)
Escape any regex-magic characters in a string.
Definition file.c:624
char * CurrentFolder
Currently selected mailbox.
Definition globals.c:38
Global variables.
enum CommandResult parse_pattern_hook(const struct Command *cmd, struct Buffer *line, const struct ParseContext *pc, struct ParseError *pe)
Parse pattern-based Hook commands - Implements Command::parse() -.
Definition parse.c:177
enum CommandResult parse_folder_hook(const struct Command *cmd, struct Buffer *line, const struct ParseContext *pc, struct ParseError *pe)
Parse folder hook command - Implements Command::parse() -.
Definition parse.c:525
enum CommandResult parse_compress_hook(const struct Command *cmd, struct Buffer *line, const struct ParseContext *pc, struct ParseError *pe)
Parse compress hook commands - Implements Command::parse() -.
Definition parse.c:914
enum CommandResult parse_regex_hook(const struct Command *cmd, struct Buffer *line, const struct ParseContext *pc, struct ParseError *pe)
Parse regex-based hook command - Implements Command::parse() -.
Definition parse.c:428
enum CommandResult parse_unhook(const struct Command *cmd, struct Buffer *line, const struct ParseContext *pc, struct ParseError *pe)
Parse the unhook command - Implements Command::parse() -.
Definition parse.c:1204
enum CommandResult parse_crypt_hook(const struct Command *cmd, struct Buffer *line, const struct ParseContext *pc, struct ParseError *pe)
Parse crypt hook commands - Implements Command::parse() -.
Definition parse.c:666
enum CommandResult parse_mailbox_hook(const struct Command *cmd, struct Buffer *line, const struct ParseContext *pc, struct ParseError *pe)
Parse mailbox pattern hook commands - Implements Command::parse() -.
Definition parse.c:349
enum CommandResult parse_charset_hook(const struct Command *cmd, struct Buffer *line, const struct ParseContext *pc, struct ParseError *pe)
Parse charset Hook commands - Implements Command::parse() -.
Definition parse.c:57
enum CommandResult parse_mbox_hook(const struct Command *cmd, struct Buffer *line, const struct ParseContext *pc, struct ParseError *pe)
Parse mbox hook command - Implements Command::parse() -.
Definition parse.c:763
enum CommandResult parse_global_hook(const struct Command *cmd, struct Buffer *line, const struct ParseContext *pc, struct ParseError *pe)
Parse global Hook commands - Implements Command::parse() -.
Definition parse.c:111
enum CommandResult parse_index_hook(const struct Command *cmd, struct Buffer *line, const struct ParseContext *pc, struct ParseError *pe)
Parse the index format hook command - Implements Command::parse() -.
Definition parse.c:1080
static void idxfmt_hashelem_free(int type, void *obj, intptr_t data)
Free our hash table data - Implements hash_hdata_free_t -.
Definition parse.c:1050
struct HashElem * mutt_hash_insert(struct HashTable *table, const char *strkey, void *data)
Add a new element to the Hash Table (with string keys).
Definition hash.c:338
void * mutt_hash_find(const struct HashTable *table, const char *strkey)
Find the HashElem data in a Hash Table element using a key.
Definition hash.c:365
struct HashTable * mutt_hash_new(size_t num_elems, HashFlags flags)
Create a new Hash Table (with string keys).
Definition hash.c:262
void mutt_hash_set_destructor(struct HashTable *table, hash_hdata_free_t fn, intptr_t fn_data)
Set the destructor for a Hash Table.
Definition hash.c:304
void mutt_hash_free(struct HashTable **ptr)
Free a hash table.
Definition hash.c:460
@ MUTT_HASH_STRDUP_KEYS
make a copy of the keys
Definition hash.h:117
void hook_free(struct Hook **ptr)
Free a Hook.
Definition hook.c:47
struct Hook * hook_new(void)
Create a Hook.
Definition hook.c:71
User-defined Hooks.
Hooks private Module data.
void mutt_delete_hooks(struct HookList *hooks, enum CommandId id)
Delete matching hooks.
Definition parse.c:1032
enum CommandResult add_mailbox_hook(enum CommandId id, struct Buffer *mailbox, struct Buffer *pattern, bool pat_not, struct Buffer *err)
Add a Mailbox Hook.
Definition parse.c:288
void delete_idxfmt_hooks(struct HashTable **idx_fmt_hooks)
Delete all the index-format-hooks.
Definition parse.c:1069
GUI manage the main index (list of emails).
struct MailboxView * get_current_mailbox_view(void)
Get the current Mailbox view.
Definition index.c:692
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
#define MUTT_MEM_CALLOC(n, type)
Definition memory.h:52
@ MODULE_ID_HOOKS
ModuleHooks, Hook Commands
Definition module_api.h:71
void mutt_ch_lookup_remove(void)
Remove all the character set lookups.
Definition charset.c:528
bool mutt_ch_lookup_add(enum LookupType type, const char *pat, const char *replace, struct Buffer *err)
Add a new character set lookup.
Definition charset.c:496
LookupType
Types of character set lookups.
Definition charset.h:61
@ MUTT_LOOKUP_ICONV
Character set conversion.
Definition charset.h:63
@ MUTT_LOOKUP_CHARSET
Alias for another character set.
Definition charset.h:62
Convenience wrapper for the library headers.
#define _(a)
Definition message.h:28
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:666
void expand_path(struct Buffer *buf, bool regex)
Create the canonical path.
Definition muttlib.c:122
Some miscellaneous functions.
void * neomutt_get_module_data(struct NeoMutt *n, enum ModuleId id)
Get the private data for a Module.
Definition neomutt.c:666
Text parsing functions.
Match patterns to emails.
uint8_t PatternCompFlags
Definition lib.h:78
void mutt_check_simple(struct Buffer *s, const char *simple)
Convert a simple search into a real request.
Definition pattern.c:91
@ MUTT_PC_PATTERN_DYNAMIC
Enable runtime date range evaluation.
Definition lib.h:75
@ MUTT_PC_NONE
No flags are set.
Definition lib.h:73
@ MUTT_PC_SEND_MODE_SEARCH
Allow send-mode body searching.
Definition lib.h:76
@ MUTT_PC_FULL_MSG
Enable body and header matching.
Definition lib.h:74
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
#define TAILQ_FOREACH(var, head, field)
Definition queue.h:782
#define TAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition queue.h:792
#define TAILQ_INIT(head)
Definition queue.h:822
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition queue.h:866
#define TAILQ_REMOVE(head, elm, field)
Definition queue.h:901
#define REG_COMP(preg, regex, cflags)
Compile a regular expression.
Definition regex3.h:49
char * mutt_get_sourced_cwd(void)
Get the current file path that is being parsed.
Definition source.c:317
#define SKIPWS(ch)
Definition string2.h:52
String manipulation buffer.
Definition buffer.h:36
char * dptr
Current read/write position.
Definition buffer.h:38
size_t dsize
Length of data.
Definition buffer.h:39
char * data
Pointer to data.
Definition buffer.h:37
const char * name
Name of the Command.
Definition command.h:162
enum CommandId id
ID of the Command.
Definition command.h:163
Definition of a format string.
Definition definition.h:49
Parsed Expando trees.
Definition expando.h:41
A Hash Table.
Definition hash.h:99
A list of user hooks.
Definition hook.h:33
struct PatternList * pattern
Used for fcc,save,send-hook.
Definition hook.h:38
struct Regex regex
Regular expression.
Definition hook.h:35
char * command
Filename, command or pattern to execute.
Definition hook.h:36
struct Expando * expando
Used for format hooks.
Definition hook.h:39
enum CommandId id
Hook CommandId, e.g. CMD_FOLDER_HOOK.
Definition hook.h:34
char * source_file
Used for relative-directory source.
Definition hook.h:37
Hooks private Module data.
Definition module_data.h:33
struct HashTable * idx_fmt_hooks
All Index Format hooks.
Definition module_data.h:36
struct HookList hooks
All simple hooks, e.g. CMD_FOLDER_HOOK.
Definition module_data.h:35
int current_hook_id
The ID of the Hook currently being executed.
Definition module_data.h:37
View of a Mailbox.
Definition mview.h:40
struct Mailbox * mailbox
Current Mailbox.
Definition mview.h:51
char * pattern
Limit pattern string.
Definition mview.h:42
Container for Accounts, Notifications.
Definition neomutt.h:41
struct CommandArray commands
NeoMutt commands.
Definition neomutt.h:53
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
Context for config parsing (history/backtrace).
Definition pcontext.h:34
Detailed error information from config parsing.
Definition perror.h:34
struct Buffer * message
Error message.
Definition perror.h:35
char * pattern
printable version
Definition regex3.h:86
bool pat_not
do not match
Definition regex3.h:88
regex_t * regex
compiled expression
Definition regex3.h:87