NeoMutt  2025-12-11-694-ga89709
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 "muttlib.h"
45
46extern const struct ExpandoDefinition IndexFormatDef[];
47
50
52struct HashTable *IdxFmtHooks = NULL;
53
56
64enum CommandResult parse_charset_hook(const struct Command *cmd, struct Buffer *line,
65 const struct ParseContext *pc, struct ParseError *pe)
66{
67 struct Buffer *err = pe->message;
68
69 if (!MoreArgs(line))
70 {
71 buf_printf(err, _("%s: too few arguments"), cmd->name);
72 return MUTT_CMD_WARNING;
73 }
74
75 struct Buffer *alias = buf_pool_get();
76 struct Buffer *charset = buf_pool_get();
78
79 if (parse_extract_token(alias, line, TOKEN_NO_FLAGS) < 0)
80 goto done;
81 if (parse_extract_token(charset, line, TOKEN_NO_FLAGS) < 0)
82 goto done;
83
84 const enum LookupType type = (cmd->id == CMD_ICONV_HOOK) ? MUTT_LOOKUP_ICONV :
86
87 if (buf_is_empty(alias) || buf_is_empty(charset))
88 {
89 buf_printf(err, _("%s: too few arguments"), cmd->name);
91 }
92 else if (MoreArgs(line))
93 {
94 buf_printf(err, _("%s: too many arguments"), cmd->name);
95 buf_reset(line); // clean up buffer to avoid a mess with further rcfile processing
97 }
98 else if (mutt_ch_lookup_add(type, buf_string(alias), buf_string(charset), err))
99 {
100 rc = MUTT_CMD_SUCCESS;
101 }
102
103done:
104 buf_pool_release(&alias);
105 buf_pool_release(&charset);
106
107 return rc;
108}
109
118enum CommandResult parse_global_hook(const struct Command *cmd, struct Buffer *line,
119 const struct ParseContext *pc, struct ParseError *pe)
120{
121 struct Buffer *err = pe->message;
122
123 struct Hook *hook = NULL;
125
126 struct Buffer *command = buf_pool_get();
127
128 // TOKEN_SPACE allows the command to contain whitespace, without quoting
129 parse_extract_token(command, line, TOKEN_SPACE);
130
131 if (buf_is_empty(command))
132 {
133 buf_printf(err, _("%s: too few arguments"), cmd->name);
134 rc = MUTT_CMD_WARNING;
135 goto cleanup;
136 }
137
138 if (MoreArgs(line))
139 {
140 buf_printf(err, _("%s: too many arguments"), cmd->name);
141 rc = MUTT_CMD_WARNING;
142 goto cleanup;
143 }
144
145 /* check to make sure that a matching Hook doesn't already exist */
146 TAILQ_FOREACH(hook, &Hooks, entries)
147 {
148 /* Ignore duplicate global Hooks */
149 if ((hook->id == cmd->id) && mutt_str_equal(hook->command, buf_string(command)))
150 {
151 rc = MUTT_CMD_SUCCESS;
152 goto cleanup;
153 }
154 }
155
156 hook = hook_new();
157 hook->id = cmd->id;
158 hook->command = buf_strdup(command);
160 hook->pattern = NULL;
161 hook->regex.pattern = NULL;
162 hook->regex.regex = NULL;
163 hook->regex.pat_not = false;
164 hook->expando = NULL;
165
166 TAILQ_INSERT_TAIL(&Hooks, hook, entries);
167 rc = MUTT_CMD_SUCCESS;
168
169cleanup:
170 buf_pool_release(&command);
171 return rc;
172}
173
183enum CommandResult parse_pattern_hook(const struct Command *cmd, struct Buffer *line,
184 const struct ParseContext *pc, struct ParseError *pe)
185{
186 struct Buffer *err = pe->message;
187
188 struct Hook *hook = NULL;
190 bool pat_not = false;
191 struct PatternList *pat = NULL;
192
193 struct Buffer *command = buf_pool_get();
194 struct Buffer *pattern = buf_pool_get();
195
196 if (*line->dptr == '!')
197 {
198 line->dptr++;
199 SKIPWS(line->dptr);
200 pat_not = true;
201 }
202
203 parse_extract_token(pattern, line, TOKEN_NO_FLAGS);
204
205 if (!MoreArgs(line))
206 {
207 buf_printf(err, _("%s: too few arguments"), cmd->name);
208 rc = MUTT_CMD_WARNING;
209 goto cleanup;
210 }
211
212 // TOKEN_SPACE allows the command to contain whitespace, without quoting
213 parse_extract_token(command, line, TOKEN_SPACE);
214
215 if (buf_is_empty(command))
216 {
217 buf_printf(err, _("%s: too few arguments"), cmd->name);
218 rc = MUTT_CMD_WARNING;
219 goto cleanup;
220 }
221
222 if (MoreArgs(line))
223 {
224 buf_printf(err, _("%s: too many arguments"), cmd->name);
225 rc = MUTT_CMD_WARNING;
226 goto cleanup;
227 }
228
229 const char *const c_default_hook = cs_subset_string(NeoMutt->sub, "default_hook");
230 if (c_default_hook)
231 {
232 mutt_check_simple(pattern, c_default_hook);
233 }
234
235 /* check to make sure that a matching hook doesn't already exist */
236 TAILQ_FOREACH(hook, &Hooks, entries)
237 {
238 if ((hook->id == cmd->id) && (hook->regex.pat_not == pat_not) &&
239 mutt_str_equal(buf_string(pattern), hook->regex.pattern))
240 {
241 /* these hooks allow multiple commands with the same pattern,
242 * so if we've already seen this pattern/command pair,
243 * just ignore it instead of creating a duplicate */
244 if (mutt_str_equal(hook->command, buf_string(command)))
245 {
246 rc = MUTT_CMD_SUCCESS;
247 goto cleanup;
248 }
249 }
250 }
251
252 PatternCompFlags comp_flags;
253 if (cmd->id == CMD_SEND2_HOOK)
254 comp_flags = MUTT_PC_SEND_MODE_SEARCH;
255 else if (cmd->id == CMD_SEND_HOOK)
256 comp_flags = MUTT_PC_NO_FLAGS;
257 else
258 comp_flags = MUTT_PC_FULL_MSG;
259
260 struct MailboxView *mv_cur = get_current_mailbox_view();
261 pat = mutt_pattern_comp(mv_cur, buf_string(pattern), comp_flags, err);
262 if (!pat)
263 goto cleanup;
264
265 hook = hook_new();
266 hook->id = cmd->id;
267 hook->command = buf_strdup(command);
269 hook->pattern = pat;
271 hook->regex.regex = NULL;
272 hook->regex.pat_not = pat_not;
273 hook->expando = NULL;
274
275 TAILQ_INSERT_TAIL(&Hooks, hook, entries);
276 rc = MUTT_CMD_SUCCESS;
277
278cleanup:
279 buf_pool_release(&command);
281 return rc;
282}
283
294 struct Buffer *pattern, bool pat_not, struct Buffer *err)
295{
296 struct Hook *hook = NULL;
297 struct PatternList *pat = NULL;
298
299 /* check to make sure that a matching hook doesn't already exist */
300 TAILQ_FOREACH(hook, &Hooks, entries)
301 {
302 if ((hook->id == id) && (hook->regex.pat_not == pat_not) &&
303 mutt_str_equal(buf_string(pattern), hook->regex.pattern))
304 {
305 // Update an existing hook
306 FREE(&hook->command);
307 hook->command = buf_strdup(mailbox);
308 FREE(&hook->source_file);
310
311 expando_free(&hook->expando);
312 hook->expando = expando_parse(buf_string(mailbox), IndexFormatDef, err);
313
314 return MUTT_CMD_SUCCESS;
315 }
316 }
317
318 PatternCompFlags comp_flags;
319 if (id == CMD_FCC_HOOK)
320 comp_flags = MUTT_PC_NO_FLAGS;
321 else
322 comp_flags = MUTT_PC_FULL_MSG;
323
324 struct MailboxView *mv_cur = get_current_mailbox_view();
325 pat = mutt_pattern_comp(mv_cur, buf_string(pattern), comp_flags, err);
326 if (!pat)
327 return MUTT_CMD_ERROR;
328
329 struct Expando *exp = expando_parse(buf_string(mailbox), IndexFormatDef, err);
330
331 hook = hook_new();
332 hook->id = id;
333 hook->command = buf_strdup(mailbox);
335 hook->pattern = pat;
336 hook->regex.pattern = buf_strdup(pattern);
337 hook->regex.regex = NULL;
338 hook->regex.pat_not = pat_not;
339 hook->expando = exp;
340
341 TAILQ_INSERT_TAIL(&Hooks, hook, entries);
342 return MUTT_CMD_SUCCESS;
343}
344
353enum CommandResult parse_mailbox_hook(const struct Command *cmd, struct Buffer *line,
354 const struct ParseContext *pc, struct ParseError *pe)
355{
356 struct Buffer *err = pe->message;
357
359 bool pat_not = false;
360
361 struct Buffer *pattern = buf_pool_get();
362 struct Buffer *mailbox = buf_pool_get();
363
364 if (*line->dptr == '!')
365 {
366 line->dptr++;
367 SKIPWS(line->dptr);
368 pat_not = true;
369 }
370
371 parse_extract_token(pattern, line, TOKEN_NO_FLAGS);
372
373 if (!MoreArgs(line))
374 {
375 buf_printf(err, _("%s: too few arguments"), cmd->name);
376 rc = MUTT_CMD_WARNING;
377 goto cleanup;
378 }
379
380 parse_extract_token(mailbox, line, TOKEN_NO_FLAGS);
381
382 if (buf_is_empty(mailbox))
383 {
384 buf_printf(err, _("%s: too few arguments"), cmd->name);
385 rc = MUTT_CMD_WARNING;
386 goto cleanup;
387 }
388
389 if (MoreArgs(line))
390 {
391 buf_printf(err, _("%s: too many arguments"), cmd->name);
392 rc = MUTT_CMD_WARNING;
393 goto cleanup;
394 }
395
396 const char *const c_default_hook = cs_subset_string(NeoMutt->sub, "default_hook");
397 if (c_default_hook)
398 {
399 mutt_check_simple(pattern, c_default_hook);
400 }
401
402 expand_path(mailbox, false);
403
404 if ((cmd->id == CMD_FCC_HOOK) || (cmd->id == CMD_FCC_SAVE_HOOK))
405 {
406 rc = add_mailbox_hook(CMD_FCC_HOOK, mailbox, pattern, pat_not, err);
407 if (rc != MUTT_CMD_SUCCESS)
408 goto cleanup;
409 }
410
411 if ((cmd->id == CMD_SAVE_HOOK) || (cmd->id == CMD_FCC_SAVE_HOOK))
412 {
413 rc = add_mailbox_hook(CMD_SAVE_HOOK, mailbox, pattern, pat_not, err);
414 if (rc != MUTT_CMD_SUCCESS)
415 goto cleanup;
416 }
417
418 rc = MUTT_CMD_SUCCESS;
419
420cleanup:
421 buf_pool_release(&pattern);
422 buf_pool_release(&mailbox);
423 return rc;
424}
425
432enum CommandResult parse_regex_hook(const struct Command *cmd, struct Buffer *line,
433 const struct ParseContext *pc, struct ParseError *pe)
434{
435 struct Buffer *err = pe->message;
436
437 struct Hook *hook = NULL;
439 bool pat_not = false;
440 regex_t *rx = NULL;
441
442 struct Buffer *regex = buf_pool_get();
443 struct Buffer *command = buf_pool_get();
444
445 if (*line->dptr == '!')
446 {
447 line->dptr++;
448 SKIPWS(line->dptr);
449 pat_not = true;
450 }
451
453
454 if (!MoreArgs(line))
455 {
456 buf_printf(err, _("%s: too few arguments"), cmd->name);
457 rc = MUTT_CMD_WARNING;
458 goto cleanup;
459 }
460
461 parse_extract_token(command, line, TOKEN_SPACE);
462
463 if (buf_is_empty(command))
464 {
465 buf_printf(err, _("%s: too few arguments"), cmd->name);
466 rc = MUTT_CMD_WARNING;
467 goto cleanup;
468 }
469
470 if (MoreArgs(line))
471 {
472 buf_printf(err, _("%s: too many arguments"), cmd->name);
473 rc = MUTT_CMD_WARNING;
474 goto cleanup;
475 }
476
477 /* check to make sure that a matching hook doesn't already exist */
478 TAILQ_FOREACH(hook, &Hooks, entries)
479 {
480 if ((hook->id == cmd->id) && (hook->regex.pat_not == pat_not) &&
481 mutt_str_equal(buf_string(regex), hook->regex.pattern))
482 {
483 // Ignore duplicate hooks
484 if (mutt_str_equal(hook->command, buf_string(command)))
485 {
486 rc = MUTT_CMD_SUCCESS;
487 goto cleanup;
488 }
489 }
490 }
491
492 /* Hooks not allowing full patterns: Check syntax of regex */
493 rx = MUTT_MEM_CALLOC(1, regex_t);
494 int rc2 = REG_COMP(rx, buf_string(regex), 0);
495 if (rc2 != 0)
496 {
497 regerror(rc2, rx, err->data, err->dsize);
498 buf_fix_dptr(err);
499 FREE(&rx);
500 goto cleanup;
501 }
502
503 hook = hook_new();
504 hook->id = cmd->id;
505 hook->command = buf_strdup(command);
507 hook->pattern = NULL;
508 hook->regex.pattern = buf_strdup(regex);
509 hook->regex.regex = rx;
510 hook->regex.pat_not = pat_not;
511 hook->expando = NULL;
512
513 TAILQ_INSERT_TAIL(&Hooks, hook, entries);
514 rc = MUTT_CMD_SUCCESS;
515
516cleanup:
517 buf_pool_release(&regex);
518 buf_pool_release(&command);
519 return rc;
520}
521
528enum CommandResult parse_folder_hook(const struct Command *cmd, struct Buffer *line,
529 const struct ParseContext *pc, struct ParseError *pe)
530{
531 struct Buffer *err = pe->message;
532
533 struct Hook *hook = NULL;
535 bool pat_not = false;
536 bool use_regex = true;
537 regex_t *rx = NULL;
538
539 struct Buffer *regex = buf_pool_get();
540 struct Buffer *command = buf_pool_get();
541
542 if (*line->dptr == '!')
543 {
544 line->dptr++;
545 SKIPWS(line->dptr);
546 pat_not = true;
547 }
548
550 if (mutt_str_equal(buf_string(regex), "-noregex"))
551 {
552 use_regex = false;
553 if (!MoreArgs(line))
554 {
555 buf_printf(err, _("%s: too few arguments"), cmd->name);
556 rc = MUTT_CMD_WARNING;
557 goto cleanup;
558 }
560 }
561
562 if (!MoreArgs(line))
563 {
564 buf_printf(err, _("%s: too few arguments"), cmd->name);
565 rc = MUTT_CMD_WARNING;
566 goto cleanup;
567 }
568
569 parse_extract_token(command, line, TOKEN_SPACE);
570
571 if (buf_is_empty(command))
572 {
573 buf_printf(err, _("%s: too few arguments"), cmd->name);
574 rc = MUTT_CMD_WARNING;
575 goto cleanup;
576 }
577
578 if (MoreArgs(line))
579 {
580 buf_printf(err, _("%s: too many arguments"), cmd->name);
581 rc = MUTT_CMD_WARNING;
582 goto cleanup;
583 }
584
585 /* Accidentally using the ^ mailbox shortcut in the .neomuttrc is a
586 * common mistake */
587 if ((buf_at(regex, 0) == '^') && !CurrentFolder)
588 {
589 buf_strcpy(err, _("current mailbox shortcut '^' is unset"));
590 goto cleanup;
591 }
592
593 struct Buffer *tmp = buf_pool_get();
594 buf_copy(tmp, regex);
595 expand_path(tmp, use_regex);
596
597 /* Check for other mailbox shortcuts that expand to the empty string.
598 * This is likely a mistake too */
599 if (buf_is_empty(tmp) && !buf_is_empty(regex))
600 {
601 buf_strcpy(err, _("mailbox shortcut expanded to empty regex"));
602 buf_pool_release(&tmp);
603 goto cleanup;
604 }
605
606 if (use_regex)
607 {
608 buf_copy(regex, tmp);
609 }
610 else
611 {
613 }
614 buf_pool_release(&tmp);
615
616 /* check to make sure that a matching hook doesn't already exist */
617 TAILQ_FOREACH(hook, &Hooks, entries)
618 {
619 if ((hook->id == cmd->id) && (hook->regex.pat_not == pat_not) &&
620 mutt_str_equal(buf_string(regex), hook->regex.pattern))
621 {
622 // Ignore duplicate hooks
623 if (mutt_str_equal(hook->command, buf_string(command)))
624 {
625 rc = MUTT_CMD_SUCCESS;
626 goto cleanup;
627 }
628 }
629 }
630
631 /* Hooks not allowing full patterns: Check syntax of regex */
632 rx = MUTT_MEM_CALLOC(1, regex_t);
633 int rc2 = REG_COMP(rx, buf_string(regex), 0);
634 if (rc2 != 0)
635 {
636 regerror(rc2, rx, err->data, err->dsize);
637 buf_fix_dptr(err);
638 FREE(&rx);
639 goto cleanup;
640 }
641
642 hook = hook_new();
643 hook->id = cmd->id;
644 hook->command = buf_strdup(command);
646 hook->pattern = NULL;
647 hook->regex.pattern = buf_strdup(regex);
648 hook->regex.regex = rx;
649 hook->regex.pat_not = pat_not;
650 hook->expando = NULL;
651
652 TAILQ_INSERT_TAIL(&Hooks, hook, entries);
653 rc = MUTT_CMD_SUCCESS;
654
655cleanup:
656 buf_pool_release(&regex);
657 buf_pool_release(&command);
658 return rc;
659}
660
668enum CommandResult parse_crypt_hook(const struct Command *cmd, struct Buffer *line,
669 const struct ParseContext *pc, struct ParseError *pe)
670{
671 struct Buffer *err = pe->message;
672
673 struct Hook *hook = NULL;
675 bool pat_not = false;
676 regex_t *rx = NULL;
677
678 struct Buffer *regex = buf_pool_get();
679 struct Buffer *keyid = buf_pool_get();
680
681 if (*line->dptr == '!')
682 {
683 line->dptr++;
684 SKIPWS(line->dptr);
685 pat_not = true;
686 }
687
689
690 if (!MoreArgs(line))
691 {
692 buf_printf(err, _("%s: too few arguments"), cmd->name);
693 rc = MUTT_CMD_WARNING;
694 goto cleanup;
695 }
696
698
699 if (buf_is_empty(keyid))
700 {
701 buf_printf(err, _("%s: too few arguments"), cmd->name);
702 rc = MUTT_CMD_WARNING;
703 goto cleanup;
704 }
705
706 if (MoreArgs(line))
707 {
708 buf_printf(err, _("%s: too many arguments"), cmd->name);
709 rc = MUTT_CMD_WARNING;
710 goto cleanup;
711 }
712
713 /* check to make sure that a matching hook doesn't already exist */
714 TAILQ_FOREACH(hook, &Hooks, entries)
715 {
716 if ((hook->id == cmd->id) && (hook->regex.pat_not == pat_not) &&
717 mutt_str_equal(buf_string(regex), hook->regex.pattern))
718 {
719 // Ignore duplicate hooks
720 if (mutt_str_equal(hook->command, buf_string(keyid)))
721 {
722 rc = MUTT_CMD_SUCCESS;
723 goto cleanup;
724 }
725 }
726 }
727
728 /* Hooks not allowing full patterns: Check syntax of regex */
729 rx = MUTT_MEM_CALLOC(1, regex_t);
730 int rc2 = REG_COMP(rx, buf_string(regex), REG_ICASE);
731 if (rc2 != 0)
732 {
733 regerror(rc2, rx, err->data, err->dsize);
734 buf_fix_dptr(err);
735 FREE(&rx);
736 goto cleanup;
737 }
738
739 hook = hook_new();
740 hook->id = cmd->id;
741 hook->command = buf_strdup(keyid);
743 hook->pattern = NULL;
744 hook->regex.pattern = buf_strdup(regex);
745 hook->regex.regex = rx;
746 hook->regex.pat_not = pat_not;
747 hook->expando = NULL;
748
749 TAILQ_INSERT_TAIL(&Hooks, hook, entries);
750 rc = MUTT_CMD_SUCCESS;
751
752cleanup:
753 buf_pool_release(&regex);
754 buf_pool_release(&keyid);
755 return rc;
756}
757
764enum CommandResult parse_mbox_hook(const struct Command *cmd, struct Buffer *line,
765 const struct ParseContext *pc, struct ParseError *pe)
766{
767 struct Buffer *err = pe->message;
768
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
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 }
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_NO_FLAGS);
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, &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(&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
919 struct Hook *hook = NULL;
921 bool pat_not = false;
922 regex_t *rx = NULL;
923
924 struct Buffer *regex = buf_pool_get();
925 struct Buffer *command = buf_pool_get();
926
927 if (*line->dptr == '!')
928 {
929 line->dptr++;
930 SKIPWS(line->dptr);
931 pat_not = true;
932 }
933
935
936 if (!MoreArgs(line))
937 {
938 buf_printf(err, _("%s: too few arguments"), cmd->name);
939 rc = MUTT_CMD_WARNING;
940 goto cleanup;
941 }
942
943 // TOKEN_SPACE allows the command to contain whitespace, without quoting
944 parse_extract_token(command, line, TOKEN_SPACE);
945
946 if (buf_is_empty(command))
947 {
948 buf_printf(err, _("%s: too few arguments"), cmd->name);
949 rc = MUTT_CMD_WARNING;
950 goto cleanup;
951 }
952
953 if (MoreArgs(line))
954 {
955 buf_printf(err, _("%s: too many arguments"), cmd->name);
956 rc = MUTT_CMD_WARNING;
957 goto cleanup;
958 }
959
960 if (mutt_comp_valid_command(buf_string(command)) == 0)
961 {
962 buf_strcpy(err, _("badly formatted command string"));
963 goto cleanup;
964 }
965
966 /* check to make sure that a matching hook doesn't already exist */
967 TAILQ_FOREACH(hook, &Hooks, entries)
968 {
969 if ((hook->id == cmd->id) && (hook->regex.pat_not == pat_not) &&
970 mutt_str_equal(buf_string(regex), hook->regex.pattern))
971 {
972 // Update an existing hook
973 FREE(&hook->command);
974 hook->command = buf_strdup(command);
975 FREE(&hook->source_file);
977
978 rc = MUTT_CMD_SUCCESS;
979 goto cleanup;
980 }
981 }
982
983 /* Hooks not allowing full patterns: Check syntax of regex */
984 rx = MUTT_MEM_CALLOC(1, regex_t);
985 int rc2 = REG_COMP(rx, buf_string(regex), 0);
986 if (rc2 != 0)
987 {
988 regerror(rc2, rx, err->data, err->dsize);
989 buf_fix_dptr(err);
990 FREE(&rx);
991 goto cleanup;
992 }
993
994 hook = hook_new();
995 hook->id = cmd->id;
996 hook->command = buf_strdup(command);
998 hook->pattern = NULL;
999 hook->regex.pattern = buf_strdup(regex);
1000 hook->regex.regex = rx;
1001 hook->regex.pat_not = pat_not;
1002 hook->expando = NULL;
1003
1004 TAILQ_INSERT_TAIL(&Hooks, hook, entries);
1005 rc = MUTT_CMD_SUCCESS;
1006
1007cleanup:
1008 buf_pool_release(&regex);
1009 buf_pool_release(&command);
1010 return rc;
1011}
1012
1020{
1021 struct Hook *h = NULL;
1022 struct Hook *tmp = NULL;
1023
1024 TAILQ_FOREACH_SAFE(h, &Hooks, entries, tmp)
1025 {
1026 if ((id == CMD_NONE) || (id == h->id))
1027 {
1028 TAILQ_REMOVE(&Hooks, h, entries);
1029 hook_free(&h);
1030 }
1031 }
1032}
1033
1037static void idxfmt_hashelem_free(int type, void *obj, intptr_t data)
1038{
1039 struct HookList *hl = obj;
1040 struct Hook *h = NULL;
1041 struct Hook *tmp = NULL;
1042
1043 TAILQ_FOREACH_SAFE(h, hl, entries, tmp)
1044 {
1045 TAILQ_REMOVE(hl, h, entries);
1046 hook_free(&h);
1047 }
1048
1049 FREE(&hl);
1050}
1051
1055static void delete_idxfmt_hooks(void)
1056{
1058}
1059
1066enum CommandResult parse_index_hook(const struct Command *cmd, struct Buffer *line,
1067 const struct ParseContext *pc, struct ParseError *pe)
1068{
1069 struct Buffer *err = pe->message;
1070
1071 if (!MoreArgs(line))
1072 {
1073 buf_printf(err, _("%s: too few arguments"), cmd->name);
1074 return MUTT_CMD_WARNING;
1075 }
1076
1077 enum CommandResult rc = MUTT_CMD_ERROR;
1078 bool pat_not = false;
1079
1080 struct Buffer *name = buf_pool_get();
1081 struct Buffer *pattern = buf_pool_get();
1082 struct Buffer *fmt = buf_pool_get();
1083 struct Expando *exp = NULL;
1084
1085 if (!IdxFmtHooks)
1086 {
1089 }
1090
1092 struct HookList *hl = mutt_hash_find(IdxFmtHooks, buf_string(name));
1093
1094 if (*line->dptr == '!')
1095 {
1096 line->dptr++;
1097 SKIPWS(line->dptr);
1098 pat_not = true;
1099 }
1100 parse_extract_token(pattern, line, TOKEN_NO_FLAGS);
1101
1102 if (!MoreArgs(line))
1103 {
1104 buf_printf(err, _("%s: too few arguments"), cmd->name);
1105 goto out;
1106 }
1108
1109 exp = expando_parse(buf_string(fmt), IndexFormatDef, err);
1110 if (!exp)
1111 goto out;
1112
1113 if (MoreArgs(line))
1114 {
1115 buf_printf(err, _("%s: too many arguments"), cmd->name);
1116 goto out;
1117 }
1118
1119 const char *const c_default_hook = cs_subset_string(NeoMutt->sub, "default_hook");
1120 if (c_default_hook)
1121 mutt_check_simple(pattern, c_default_hook);
1122
1123 /* check to make sure that a matching hook doesn't already exist */
1124 struct Hook *hook = NULL;
1125 if (hl)
1126 {
1127 TAILQ_FOREACH(hook, hl, entries)
1128 {
1129 // Update an existing hook
1130 if ((hook->regex.pat_not == pat_not) &&
1132 {
1133 expando_free(&hook->expando);
1134 hook->expando = exp;
1135 exp = NULL;
1136 rc = MUTT_CMD_SUCCESS;
1137 goto out;
1138 }
1139 }
1140 }
1141
1142 /* MUTT_PC_PATTERN_DYNAMIC sets so that date ranges are regenerated during
1143 * matching. This of course is slower, but index-format-hook is commonly
1144 * used for date ranges, and they need to be evaluated relative to "now", not
1145 * the hook compilation time. */
1146 struct MailboxView *mv_cur = get_current_mailbox_view();
1147 struct PatternList *pat = mutt_pattern_comp(mv_cur, buf_string(pattern),
1149 err);
1150 if (!pat)
1151 goto out;
1152
1153 hook = hook_new();
1154 hook->id = CMD_INDEX_FORMAT_HOOK;
1155 hook->command = NULL;
1157 hook->pattern = pat;
1158 hook->regex.pattern = buf_strdup(pattern);
1159 hook->regex.regex = NULL;
1160 hook->regex.pat_not = pat_not;
1161 hook->expando = exp;
1162 exp = NULL;
1163
1164 if (!hl)
1165 {
1166 hl = MUTT_MEM_CALLOC(1, struct HookList);
1167 TAILQ_INIT(hl);
1169 }
1170
1171 TAILQ_INSERT_TAIL(hl, hook, entries);
1172 rc = MUTT_CMD_SUCCESS;
1173
1174out:
1175 buf_pool_release(&name);
1176 buf_pool_release(&pattern);
1177 buf_pool_release(&fmt);
1178 expando_free(&exp);
1179
1180 return rc;
1181}
1182
1189enum CommandResult parse_unhook(const struct Command *cmd, struct Buffer *line,
1190 const struct ParseContext *pc, struct ParseError *pe)
1191{
1192 struct Buffer *err = pe->message;
1193
1194 if (!MoreArgs(line))
1195 {
1196 buf_printf(err, _("%s: too few arguments"), cmd->name);
1197 return MUTT_CMD_WARNING;
1198 }
1199
1200 struct Buffer *token = buf_pool_get();
1202
1203 while (MoreArgs(line))
1204 {
1205 parse_extract_token(token, line, TOKEN_NO_FLAGS);
1206 if (mutt_str_equal("*", buf_string(token)))
1207 {
1208 if (CurrentHookId != CMD_NONE)
1209 {
1210 buf_addstr(err, _("unhook: Can't do unhook * from within a hook"));
1211 goto done;
1212 }
1216 }
1217 else
1218 {
1219 const struct Command *hook = command_find_by_name(&NeoMutt->commands,
1220 buf_string(token));
1221 if (!hook)
1222 {
1223 buf_printf(err, _("unhook: unknown hook type: %s"), buf_string(token));
1224 rc = MUTT_CMD_ERROR;
1225 goto done;
1226 }
1227
1228 if ((hook->id == CMD_CHARSET_HOOK) || (hook->id == CMD_ICONV_HOOK))
1229 {
1231 rc = MUTT_CMD_SUCCESS;
1232 goto done;
1233 }
1234 if (CurrentHookId == hook->id)
1235 {
1236 buf_printf(err, _("unhook: Can't delete a %s from within a %s"),
1237 buf_string(token), buf_string(token));
1238 rc = MUTT_CMD_WARNING;
1239 goto done;
1240 }
1241 if (hook->id == CMD_INDEX_FORMAT_HOOK)
1243 else
1244 mutt_delete_hooks(hook->id);
1245 }
1246 }
1247
1248 rc = MUTT_CMD_SUCCESS;
1249
1250done:
1251 buf_pool_release(&token);
1252 return rc;
1253}
const struct ExpandoDefinition IndexFormatDef[]
Expando definitions.
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition buffer.c:161
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:76
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition buffer.c:291
void buf_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition buffer.c:182
char buf_at(const struct Buffer *buf, size_t offset)
Return the character at the given offset.
Definition buffer.c:668
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:226
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:395
size_t buf_copy(struct Buffer *dst, const struct Buffer *src)
Copy a Buffer's contents to another Buffer.
Definition buffer.c:601
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition buffer.c:571
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:58
@ CMD_SEND_HOOK
:send-hook
Definition command.h:107
@ CMD_FCC_SAVE_HOOK
:fcc-save-hook
Definition command.h:76
@ CMD_ICONV_HOOK
:iconv-hook
Definition command.h:82
@ CMD_INDEX_FORMAT_HOOK
:index-format-hook
Definition command.h:86
@ CMD_FCC_HOOK
:fcc-hook
Definition command.h:75
@ CMD_SEND2_HOOK
:send2-hook
Definition command.h:106
@ CMD_CHARSET_HOOK
:charset-hook
Definition command.h:69
@ CMD_NONE
No Command.
Definition command.h:59
@ CMD_SAVE_HOOK
:save-hook
Definition command.h:104
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:171
NeoMutt Commands.
struct PatternList * mutt_pattern_comp(struct MailboxView *mv, const char *s, PatternCompFlags flags, struct Buffer *err)
Create a Pattern.
Definition compile.c:954
int mutt_comp_valid_command(const char *cmd)
Is this command string allowed?
Definition compress.c:387
Compressed mbox local mailbox type.
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.
struct Expando * expando_parse(const char *str, const struct ExpandoDefinition *defs, struct Buffer *err)
Parse an Expando string.
Definition expando.c:81
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 TOKEN_SPACE
Don't treat whitespace as a term.
Definition extract.h:48
#define MoreArgs(buf)
Definition extract.h:31
#define TOKEN_NO_FLAGS
No flags are set.
Definition extract.h:45
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:183
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:528
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:432
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:1189
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:668
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:353
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:64
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:764
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:118
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:1066
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:1037
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:337
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:364
struct HashTable * mutt_hash_new(size_t num_elems, HashFlags flags)
Create a new Hash Table (with string keys)
Definition hash.c:261
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:303
void mutt_hash_free(struct HashTable **ptr)
Free a hash table.
Definition hash.c:459
#define MUTT_HASH_STRDUP_KEYS
make a copy of the keys
Definition hash.h:113
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.
enum CommandId CurrentHookId
The ID of the Hook currently being executed, e.g. CMD_SAVE_HOOK.
Definition parse.c:55
struct HashTable * IdxFmtHooks
All Index Format hooks.
Definition parse.c:52
void mutt_delete_hooks(enum CommandId id)
Delete matching hooks.
Definition parse.c:1019
static void delete_idxfmt_hooks(void)
Delete all the index-format-hooks.
Definition parse.c:1055
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:293
struct HookList Hooks
All simple hooks, e.g. CMD_FOLDER_HOOK.
Definition parse.c:49
GUI manage the main index (list of emails)
struct MailboxView * get_current_mailbox_view(void)
Get the current Mailbox view.
Definition index.c:689
#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
void mutt_ch_lookup_remove(void)
Remove all the character set lookups.
Definition charset.c:527
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:495
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:665
void expand_path(struct Buffer *buf, bool regex)
Create the canonical path.
Definition muttlib.c:122
Some miscellaneous functions.
Text parsing functions.
Match patterns to emails.
#define MUTT_PC_SEND_MODE_SEARCH
Allow send-mode body searching.
Definition lib.h:72
uint8_t PatternCompFlags
Flags for mutt_pattern_comp(), e.g. MUTT_PC_FULL_MSG.
Definition lib.h:68
#define MUTT_PC_FULL_MSG
Enable body and header matching.
Definition lib.h:70
void mutt_check_simple(struct Buffer *s, const char *simple)
Convert a simple search into a real request.
Definition pattern.c:107
#define MUTT_PC_NO_FLAGS
No flags are set.
Definition lib.h:69
#define MUTT_PC_PATTERN_DYNAMIC
Enable runtime date range evaluation.
Definition lib.h:71
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 TAILQ_HEAD_INITIALIZER(head)
Definition queue.h:694
#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:314
#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:159
enum CommandId id
ID of the Command.
Definition command.h:160
Definition of a format string.
Definition definition.h:43
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
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