Add support for tab-completion when selecting by rule
[alpine.git] / alpine / pattern.c
blob0d6bfe765440acca75fe69125ce978388e6d7ff2
1 /*
2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2007 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 #include <system.h>
16 #include <general.h>
18 #include "../c-client/c-client.h"
20 #include "../pith/osdep/canaccess.h"
21 #include "../pith/osdep/color.h"
22 #include "../pith/osdep/pipe.h"
24 #include "../pith/charconv/utf8.h"
25 #include "../pith/charconv/filesys.h"
27 #include "../pith/status.h"
28 #include "../pith/pipe.h"
29 #include "../pith/debug.h"
30 #include "../pith/detach.h"
32 #include "pipe.h"
33 #include "pattern.h"
34 #include "signal.h"
37 /* Internal prototypes */
38 int test_message_with_cmd(MAILSTREAM *, long, char *, long, int *);
42 * pattern_filter_command - filter given message
44 void
45 pattern_filter_command(char **cat_cmds, SEARCHSET *srchset, MAILSTREAM *stream, long cat_lim, INTVL_S *cat)
47 char **l;
48 int exitval, i;
49 SEARCHSET *s;
50 MESSAGECACHE *mc;
51 char *cmd = NULL;
52 char *just_arg0 = NULL;
53 char *cmd_start, *cmd_end;
54 int just_one;
56 if(!(cat_cmds && cat_cmds[0]))
57 return;
59 just_one = !(cat_cmds[1]);
61 /* find the first command that exists on this host */
62 for(l = cat_cmds; l && *l; l++){
63 cmd = cpystr(*l);
64 removing_quotes(cmd);
65 if(cmd){
66 for(cmd_start = cmd;
67 *cmd_start && isspace(*cmd_start); cmd_start++)
70 for(cmd_end = cmd_start+1;
71 *cmd_end && !isspace(*cmd_end); cmd_end++)
74 just_arg0 = (char *) fs_get((cmd_end-cmd_start+1)
75 * sizeof(char));
76 strncpy(just_arg0, cmd_start, cmd_end - cmd_start);
77 just_arg0[cmd_end - cmd_start] = '\0';
80 if(valid_filter_command(&just_arg0))
81 break;
82 else{
83 if(just_one){
84 if(can_access(just_arg0, ACCESS_EXISTS) != 0)
85 q_status_message1(SM_ORDER, 0, 3,
86 "\"%s\" does not exist",
87 just_arg0);
88 else
89 q_status_message1(SM_ORDER, 0, 3,
90 "\"%s\" is not executable",
91 just_arg0);
94 if(just_arg0)
95 fs_give((void **) &just_arg0);
96 if(cmd)
97 fs_give((void **) &cmd);
101 if(!just_arg0 && !just_one)
102 q_status_message(SM_ORDER, 0, 3,
103 "None of the category cmds exists and is executable");
106 * If category_cmd isn't executable, it isn't a match.
108 if(!just_arg0 || !cmd){
110 * If we couldn't run the pipe command,
111 * we declare no match
113 for(s = srchset; s; s = s->next)
114 for(i = s->first; i <= s->last; i++)
115 if(i > 0L && stream && i <= stream->nmsgs
116 && (mc=mail_elt(stream, i)) && mc->searched)
117 mc->searched = NIL;
119 else
120 for(s = srchset; s; s = s->next)
121 for(i = s->first; i <= s->last; i++)
122 if(i > 0L && stream && i <= stream->nmsgs
123 && (mc=mail_elt(stream, i)) && mc->searched){
126 * If there was an error, or the exitval is out of
127 * range, then it is not a match.
128 * Default range is (0,0),
129 * which is right for matching
130 * bogofilter spam.
132 if(test_message_with_cmd(stream, i, cmd,
133 cat_lim, &exitval) != 0)
134 mc->searched = NIL;
136 /* test exitval */
137 if(mc->searched){
138 INTVL_S *iv;
140 if(cat){
141 for(iv = cat; iv; iv = iv->next)
142 if((long) exitval >= iv->imin
143 && (long) exitval <= iv->imax)
144 break;
146 if(!iv)
147 mc->searched = NIL; /* not in any interval */
149 else{
150 /* default to interval containing only zero */
151 if(exitval != 0)
152 mc->searched = NIL;
157 if(just_arg0)
158 fs_give((void **) &just_arg0);
160 if(cmd)
161 fs_give((void **) &cmd);
167 * Returns 0 if ok, -1 if not ok.
168 * If ok then exitval contains the exit value of the cmd.
171 test_message_with_cmd(MAILSTREAM *stream, long int msgno, char *cmd,
172 long char_limit, /* limit testing to this many chars from body */
173 int *exitval)
175 PIPE_S *tpipe;
176 gf_io_t pc;
177 int status = 0, flags, err = 0;
178 char *resultfile = NULL, *pipe_err;
180 if(cmd && cmd[0]){
182 flags = PIPE_WRITE | PIPE_NOSHELL | PIPE_STDERR | PIPE_NONEWMAIL;
184 dprint((7, "test_message_with_cmd(msgno=%ld cmd=%s)\n",
185 msgno, cmd));
187 if((tpipe = cmd_pipe_open(cmd, &resultfile, flags, &pc))){
189 prime_raw_pipe_getc(stream, msgno, char_limit, FT_PEEK);
190 gf_filter_init();
191 gf_link_filter(gf_nvtnl_local, NULL);
192 if((pipe_err = gf_pipe(raw_pipe_getc, pc)) != NULL){
193 q_status_message1(SM_ORDER|SM_DING, 3, 3,
194 "Internal Error: %.200s", pipe_err);
195 err++;
199 * Don't call new_mail in close_system_pipe because we're probably
200 * already here from new_mail and we don't want to get loopy.
202 status = close_system_pipe(&tpipe, exitval, pipe_callback);
205 * This is a place where the command can put its output, which we
206 * are not interested in.
208 if(resultfile){
209 our_unlink(resultfile);
210 fs_give((void **) &resultfile);
213 return((err || status) ? -1 : 0);
217 return(-1);