1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2003-2021 Free Software Foundation, Inc.
3
4 GNU Mailutils is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 GNU Mailutils 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
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
16
17 /* MH pick command */
18
19 #include <mh.h>
20 #include <regex.h>
21 #include <pick.h>
22 #include <pick-gram.h>
23
24 static char prog_doc[] = N_("Search for messages by content");
25 static char args_doc[] = N_("[--COMPONENT PATTERN]... [MSGLIST]");
26
27 static int public_option = 1;
28 static int zero_option = 0;
29
30 static int list = 1;
31 static int seq_flags = 0; /* Create public sequences;
32 Do not zero the sequence before addition */
33 static mu_list_t seq_list; /* List of sequence names to operate upon */
34
35 static mu_list_t lexlist; /* List of input tokens */
36
37 static mu_msgset_t picked_message_uids;
38
39 static void
add_component_name(struct mu_parseopt * po,struct mu_option * opt,char const * arg)40 add_component_name (struct mu_parseopt *po, struct mu_option *opt,
41 char const *arg)
42 {
43 pick_add_token (&lexlist, T_COMP, arg);
44 }
45
46 static void
add_component_pattern(struct mu_parseopt * po,struct mu_option * opt,char const * arg)47 add_component_pattern (struct mu_parseopt *po, struct mu_option *opt,
48 char const *arg)
49 {
50 pick_add_token (&lexlist, T_STRING, arg);
51 }
52
53 static void
add_cflags(struct mu_parseopt * po,struct mu_option * opt,char const * arg)54 add_cflags (struct mu_parseopt *po, struct mu_option *opt,
55 char const *arg)
56 {
57 pick_add_token (&lexlist, T_CFLAGS, arg);
58 }
59
60 static void
add_comp_match(struct mu_parseopt * po,struct mu_option * opt,char const * arg)61 add_comp_match (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
62 {
63 pick_add_token (&lexlist, T_COMP, opt->opt_default);
64 pick_add_token (&lexlist, T_STRING, arg);
65 }
66
67 static void
add_datefield(struct mu_parseopt * po,struct mu_option * opt,char const * arg)68 add_datefield (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
69 {
70 pick_add_token (&lexlist, T_DATEFIELD, arg);
71 }
72
73 static void
add_after(struct mu_parseopt * po,struct mu_option * opt,char const * arg)74 add_after (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
75 {
76 pick_add_token (&lexlist, T_AFTER, NULL);
77 pick_add_token (&lexlist, T_STRING, arg);
78 }
79
80 static void
add_before(struct mu_parseopt * po,struct mu_option * opt,char const * arg)81 add_before (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
82 {
83 pick_add_token (&lexlist, T_BEFORE, NULL);
84 pick_add_token (&lexlist, T_STRING, arg);
85 }
86
87 static void
add_and(struct mu_parseopt * po,struct mu_option * opt,char const * arg)88 add_and (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
89 {
90 pick_add_token (&lexlist, T_AND, NULL);
91 }
92
93 static void
add_or(struct mu_parseopt * po,struct mu_option * opt,char const * arg)94 add_or (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
95 {
96 pick_add_token (&lexlist, T_OR, NULL);
97 }
98
99 static void
add_not(struct mu_parseopt * po,struct mu_option * opt,char const * arg)100 add_not (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
101 {
102 pick_add_token (&lexlist, T_NOT, NULL);
103 }
104
105 static void
add_lbrace(struct mu_parseopt * po,struct mu_option * opt,char const * arg)106 add_lbrace (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
107 {
108 pick_add_token (&lexlist, T_LBRACE, NULL);
109 }
110
111 static void
add_rbrace(struct mu_parseopt * po,struct mu_option * opt,char const * arg)112 add_rbrace (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
113 {
114 pick_add_token (&lexlist, T_RBRACE, NULL);
115 }
116
117 static void
add_to_sequence(struct mu_parseopt * po,struct mu_option * opt,char const * arg)118 add_to_sequence (struct mu_parseopt *po, struct mu_option *opt,
119 char const *arg)
120 {
121 if (!seq_list && mu_list_create (&seq_list))
122 {
123 mu_error (_("cannot create sequence list"));
124 exit (1);
125 }
126 mu_list_append (seq_list, mu_strdup (arg));
127 list = 0;
128 }
129
130 static struct mu_option options[] = {
131 MU_OPTION_GROUP (N_("Search patterns")),
132 { "component", 0, N_("FIELD"), MU_OPTION_DEFAULT,
133 N_("search the named header field"),
134 mu_c_string, NULL, add_component_name },
135 { "pattern", 0, N_("STRING"), MU_OPTION_DEFAULT,
136 N_("set pattern to look for"),
137 mu_c_string, NULL, add_component_pattern },
138 { "search", 0, NULL, MU_OPTION_ALIAS },
139 { "cflags", 0, N_("STRING"), MU_OPTION_DEFAULT,
140 N_("flags controlling the type of regular expressions. "
141 "STRING must consist of one or more of the following letters: "
142 "B=basic, E=extended, I=ignore case, C=case sensitive. "
143 "Default is \"EI\". The flags remain in effect until the next "
144 "occurrence of --cflags option. The option must occur right "
145 "before --pattern or --component option (or its alias)."),
146 mu_c_string, NULL, add_cflags },
147 { "cc", 0, N_("STRING"), MU_OPTION_DEFAULT,
148 N_("search for Cc matching STRING"),
149 mu_c_string, NULL, add_comp_match, "cc" },
150 { "date", 0, N_("STRING"), MU_OPTION_DEFAULT,
151 N_("search for Date matching STRING"),
152 mu_c_string, NULL, add_comp_match, "date" },
153 { "from", 0, N_("STRING"), MU_OPTION_DEFAULT,
154 N_("search for From matching STRING"),
155 mu_c_string, NULL, add_comp_match, "from" },
156 { "subject", 0, N_("STRING"), MU_OPTION_DEFAULT,
157 N_("search for Subject matching STRING"),
158 mu_c_string, NULL, add_comp_match, "subject" },
159 { "to", 0, N_("STRING"), MU_OPTION_DEFAULT,
160 N_("search for To matching STRING"),
161 mu_c_string, NULL, add_comp_match, "to" },
162
163 MU_OPTION_GROUP (N_("Date constraint operations")),
164 { "datefield", 0, N_("STRING"), MU_OPTION_DEFAULT,
165 N_("search in the named date header field (default is `Date:')"),
166 mu_c_string, NULL, add_datefield },
167 { "after", 0, N_("DATE"), MU_OPTION_DEFAULT,
168 N_("match messages after the given date"),
169 mu_c_string, NULL, add_after },
170 { "before", 0, N_("DATE"), MU_OPTION_DEFAULT,
171 N_("match messages before the given date"),
172 mu_c_string, NULL, add_before },
173
174 MU_OPTION_GROUP (N_("Logical operations and grouping")),
175 { "and", 0, NULL, MU_OPTION_DEFAULT,
176 N_("logical AND (default)"),
177 mu_c_string, NULL, add_and },
178 { "or", 0, NULL, MU_OPTION_DEFAULT,
179 N_("logical OR"),
180 mu_c_string, NULL, add_or },
181 { "not", 0, NULL, MU_OPTION_DEFAULT,
182 N_("logical NOT"),
183 mu_c_string, NULL, add_not },
184 { "lbrace", 0, NULL, MU_OPTION_DEFAULT,
185 N_("open group"),
186 mu_c_string, NULL, add_lbrace },
187 { "(", 0, NULL, MU_OPTION_ALIAS },
188 { "rbrace", 0, NULL, MU_OPTION_DEFAULT,
189 N_("close group"),
190 mu_c_string, NULL, add_rbrace },
191 { ")", 0, NULL, MU_OPTION_ALIAS },
192
193 MU_OPTION_GROUP (N_("Operations over the selected messages")),
194 { "list", 0, NULL, MU_OPTION_DEFAULT,
195 N_("list the numbers of the selected messages (default)"),
196 mu_c_bool, &list },
197 { "sequence", 0, N_("NAME"), MU_OPTION_DEFAULT,
198 N_("add matching messages to the given sequence"),
199 mu_c_string, NULL, add_to_sequence },
200 { "public", 0, NULL, MU_OPTION_DEFAULT,
201 N_("create public sequence"),
202 mu_c_bool, &public_option },
203 { "zero", 0, NULL, MU_OPTION_DEFAULT,
204 N_("empty the sequence before adding messages"),
205 mu_c_bool, &zero_option },
206
207 MU_OPTION_END
208
209 };
210
211 static int
pick_message(size_t num,mu_message_t msg,void * data)212 pick_message (size_t num, mu_message_t msg, void *data)
213 {
214 if (pick_eval (msg))
215 {
216 mh_message_number (msg, &num);
217 if (list)
218 printf ("%s\n", mu_umaxtostr (0, num));
219 if (picked_message_uids)
220 mu_msgset_add_range (picked_message_uids, num, num, MU_MSGSET_UID);
221 }
222 return 0;
223 }
224
225
226 static int
action_add(void * item,void * data)227 action_add (void *item, void *data)
228 {
229 mu_mailbox_t mbox = data;
230 mh_seq_add (mbox, (char *)item, picked_message_uids, seq_flags);
231 return 0;
232 }
233
234 int
main(int argc,char ** argv)235 main (int argc, char **argv)
236 {
237 int i, status;
238 mu_mailbox_t mbox;
239 mu_msgset_t msgset;
240 int argv_alloc = 0;
241
242 /* Expand eventual --COMPONENT NAME pairs */
243 for (i = 1; i < argc;)
244 {
245 if (strncmp (argv[i], "--", 2) == 0 && argv[i][3] && i + 1 < argc)
246 {
247 size_t n = argc + 2;
248 if (argv_alloc)
249 argv = mu_realloc (argv, (n + 1) * sizeof (argv[0]));
250 else
251 {
252 char **p = mu_calloc (n + 1, sizeof p[0]);
253 memcpy (p, argv, (argc + 1) * sizeof argv[0]);
254 argv = p;
255 argv_alloc = 1;
256 }
257
258 memmove (argv + i + 4, argv + i + 2,
259 (argc - i - 1) * sizeof argv[0]);
260 argv[i + 3] = argv[i + 1];
261 argv[i + 2] = mu_strdup ("-pattern");
262 argv[i + 1] = mu_strdup (argv[i] + 2);
263 argv[i] = mu_strdup ("-component");
264 argc = n;
265 i += 4;
266 }
267 else
268 i++;
269 }
270
271 mh_getopt (&argc, &argv, options, MH_GETOPT_DEFAULT_FOLDER,
272 args_doc, prog_doc, NULL);
273
274 if (pick_parse (lexlist))
275 return 1;
276
277 seq_flags = (public_option ? 0 : SEQ_PRIVATE) | (zero_option ? SEQ_ZERO : 0);
278
279 mbox = mh_open_folder (mh_current_folder (),
280 seq_list ? MU_STREAM_RDWR : MU_STREAM_READ);
281
282 if (seq_list)
283 mu_msgset_create (&picked_message_uids, NULL, MU_MSGSET_UID);
284
285 mh_msgset_parse (&msgset, mbox, argc, argv, "all");
286 status = mu_msgset_foreach_message (msgset, pick_message, NULL);
287
288 if (picked_message_uids)
289 mu_list_foreach (seq_list, action_add, mbox);
290
291 mh_global_save_state ();
292 mu_mailbox_close (mbox);
293 mu_mailbox_destroy (&mbox);
294 return status;
295 }
296
297