1
2 static char rcsid[] = "@(#)$Id: pattern.c,v 1.7 1998/02/11 22:02:15 wfp5p Exp $";
3
4 /*******************************************************************************
5 * The Elm Mail System - $Revision: 1.7 $ $State: Exp $
6 *
7 * Copyright (c) 1988-1995 USENET Community Trust
8 * Copyright (c) 1986,1987 Dave Taylor
9 *******************************************************************************
10 * Bug reports, patches, comments, suggestions should be sent to:
11 *
12 * Bill Pemberton, Elm Coordinator
13 * flash@virginia.edu
14 *
15 *******************************************************************************
16 * $Log: pattern.c,v $
17 * Revision 1.7 1998/02/11 22:02:15 wfp5p
18 * Beta 2
19 *
20 * Revision 1.6 1996/05/09 15:51:24 wfp5p
21 * Alpha 10
22 *
23 * Revision 1.5 1996/03/14 17:29:45 wfp5p
24 * Alpha 9
25 *
26 * Revision 1.4 1995/09/29 17:42:21 wfp5p
27 * Alpha 8 (Chip's big changes)
28 *
29 * Revision 1.3 1995/09/11 15:19:23 wfp5p
30 * Alpha 7
31 *
32 * Revision 1.2 1995/06/12 20:33:35 wfp5p
33 * Alpha 2 clean up
34 *
35 * Revision 1.1.1.1 1995/04/19 20:38:37 wfp5p
36 * Initial import of elm 2.4 PL0 as base for elm 2.5.
37 *
38 ******************************************************************************/
39
40 /** General pattern matching for the ELM mailer.
41
42 **/
43
44
45 #include "elm_defs.h"
46 #include "elm_globals.h"
47 #include "s_elm.h"
48 #include <assert.h>
49
50 /* local procedures */
51 static void ask_clear_existing_tags P_((void));
52 static int from_matches();
53 static int subject_matches();
54 static int name_matches();
55 static int alias_matches();
56 static int comment_matches();
57 static int address_matches();
58 static int match_in_message();
59
60 int
meta_match(function)61 meta_match(function)
62 int function;
63 {
64 /** Perform specific function based on whether an entered string
65 matches either the From or Subject lines..
66 Return TRUE if the current message was matched, else FALSE.
67 **/
68
69 int i, count, curtag;
70 char *word_Action, *word_Actioned, *word_actioned;
71 static char pat[SLEN];
72
73 switch (function) {
74 case MATCH_TAG:
75 word_Action = catgets(elm_msg_cat, ElmSet, ElmTag,
76 "Tag");
77 word_Actioned = catgets(elm_msg_cat, ElmSet, ElmCapTagged,
78 "Tagged");
79 word_actioned = catgets(elm_msg_cat, ElmSet, ElmTagged,
80 "tagged");
81 break;
82 case MATCH_DELETE:
83 word_Action = catgets(elm_msg_cat, ElmSet, ElmDelete,
84 "Delete");
85 word_Actioned = catgets(elm_msg_cat, ElmSet, ElmCapMarkDelete,
86 "Marked for deletion");
87 word_actioned = catgets(elm_msg_cat, ElmSet, ElmMarkDelete,
88 "marked for deletion");
89 break;
90 case MATCH_UNDELETE:
91 word_Action = catgets(elm_msg_cat, ElmSet, ElmUndelete,
92 "Undelete");
93 word_Actioned = catgets(elm_msg_cat, ElmSet, ElmCapUndeleted,
94 "Undeleted");
95 word_actioned = catgets(elm_msg_cat, ElmSet, ElmUndeleted,
96 "undeleted");
97 break;
98 default:
99 assert(function == MATCH_TAG || function == MATCH_DELETE
100 || function == MATCH_UNDELETE);
101 return FALSE;
102 }
103
104 PutLine2(LINES-3, strlen(nls_Prompt), catgets(elm_msg_cat, ElmSet,
105 ElmMessagesMatchPattern, "%s %s that match pattern..."),
106 word_Action, nls_items);
107
108 /* clear any existing tags? */
109 if (function == MATCH_TAG)
110 ask_clear_existing_tags();
111
112 PutLine0(LINES-2, 0, catgets(elm_msg_cat, ElmSet, ElmEnterPattern,
113 "Enter pattern: "));
114 if (enter_string(pat, sizeof(pat), -1, -1, ESTR_REPLACE) < 0
115 || pat[0] == '\0') {
116 ClearLine(LINES-3);
117 ClearLine(LINES-2);
118 return FALSE;
119 }
120 strcpy(pat, shift_lower(pat));
121
122 count = 0;
123 curtag = FALSE;
124 if (inalias) {
125
126 for (i = 0; i < num_aliases; i++) {
127
128 if (!name_matches(i, pat) && !alias_matches(i, pat))
129 continue;
130 if (selected && !(aliases[i]->status & VISIBLE))
131 continue;
132
133 switch (function) {
134 case MATCH_DELETE:
135 if (aliases[i]->type & SYSTEM)
136 continue; /* don't delete these!!! */
137 aliases[i]->status |= DELETED;
138 break;
139 case MATCH_UNDELETE:
140 aliases[i]->status &= ~DELETED;
141 break;
142 case MATCH_TAG:
143 aliases[i]->status |= TAGGED;
144 break;
145 }
146
147 show_new_status(i);
148 if (curr_alias == i+1)
149 curtag = TRUE;
150 count++;
151
152 }
153
154
155 } else {
156
157 for (i = 0; i < curr_folder.num_mssgs; i++) {
158
159 if (!from_matches(i, pat) && !subject_matches(i, pat))
160 continue;
161 if (selected && !(curr_folder.headers[i]->status & VISIBLE))
162 continue;
163
164 switch (function) {
165 case MATCH_DELETE:
166 curr_folder.headers[i]->status |= DELETED;
167 break;
168 case MATCH_UNDELETE:
169 curr_folder.headers[i]->status &= ~DELETED;
170 break;
171 case MATCH_TAG:
172 curr_folder.headers[i]->status |= TAGGED;
173 break;
174 }
175
176 show_new_status(i);
177 if (curr_folder.curr_mssg == i+1)
178 curtag++;
179 count++;
180
181 }
182
183 }
184
185 MoveCursor(LINES-3, 0);
186 CleartoEOS();
187 switch (count) {
188 case 0:
189 error2(catgets(elm_msg_cat, ElmSet, ElmNoMatchesNoTags,
190 "No matches. No %s %s."), nls_items, word_actioned);
191 break;
192 case 1:
193 error2(catgets(elm_msg_cat, ElmSet, ElmTaggedMessage,
194 "%s 1 %s."), word_Actioned, nls_item);
195 break;
196 default:
197 error3(catgets(elm_msg_cat, ElmSet, ElmTaggedMessages,
198 "%s %d %s."), word_Actioned, count, nls_items);
199 break;
200 }
201
202 return(curtag);
203 }
204
ask_clear_existing_tags()205 static void ask_clear_existing_tags()
206 {
207 int tagged, i;
208 char tagmsg[SLEN], msg[SLEN];
209
210 tagged = 0;
211 if (inalias) {
212 for (i=0; i < num_aliases; i++) {
213 if (aliases[i]->status & TAGGED)
214 tagged++;
215 }
216 } else {
217 for (i=0; i < curr_folder.num_mssgs; i++) {
218 if (curr_folder.headers[i]->status & TAGGED)
219 tagged++;
220 }
221 }
222
223 if (tagged == 0)
224 return;
225
226 if (tagged > 1) {
227 MCsprintf(tagmsg, catgets(elm_msg_cat, ElmSet, ElmSomeMessagesATagged,
228 "Some %s are already tagged."), nls_items);
229 MCsprintf(msg, catgets(elm_msg_cat, ElmSet, ElmRemoveTags,
230 "%s Remove Tags?"), tagmsg);
231 } else {
232 MCsprintf(tagmsg, catgets(elm_msg_cat, ElmSet, ElmAMessageATagged,
233 "One %s is already tagged."), nls_item);
234 MCsprintf(msg, catgets(elm_msg_cat, ElmSet, ElmRemoveTag,
235 "%s Remove Tag?"), tagmsg);
236 }
237
238 if (enter_yn(msg, TRUE, LINES-2, FALSE)) {
239 if (inalias) {
240 for (i=0; i < num_aliases; i++) {
241 aliases[i]->status &= ~TAGGED;
242 show_new_status(i);
243 }
244 } else {
245 for (i=0; i < curr_folder.num_mssgs; i++) {
246 curr_folder.headers[i]->status &= ~TAGGED;
247 show_new_status(i);
248 }
249 }
250 }
251
252 }
253
254
255 /*
256 * Select message based upon pattern supplied by user.
257 *
258 * Normally, matching is performed on the from and subject lines.
259 * If the user strikes "/" then an alternate pattern is selected,
260 * and matching is performed against the entire message (headers
261 * and body). This makes things a bit twisty, because we need
262 * to get that first character and then decide what to do from
263 * there.
264 *
265 * If a match is found, the selection is updated and TRUE is
266 * displayed. If matching fails then an error is displayed
267 * and FALSE is returned. If the user aborts the match then
268 * we silently return FALSE.
269 */
270 int
pattern_match()271 pattern_match()
272 {
273 char inpbuf[SLEN], *sel_pat;
274 int inp_line, inp_col, anywhere, matched, ch, i;
275 static char hdr_pattern[SLEN];
276 static char body_pattern[SLEN];
277
278 PutLine1(LINES-3, 40, catgets(elm_msg_cat, ElmSet, ElmMatchAnywhere,
279 "/ = Match anywhere in %s."), nls_items);
280 PutLine0(LINES-1, 0, catgets(elm_msg_cat, ElmSet, ElmMatchPattern,
281 "Match pattern: "));
282 GetCursorPos(&inp_line, &inp_col);
283 PutLine0(-1, -1, hdr_pattern);
284 CleartoEOLN();
285
286 ch = ReadCh();
287 switch (ch) {
288
289 case ctrl('D'): /* abort */
290 ClearLine(LINES-1);
291 return FALSE;
292
293 case '\n': /* accept current pattern */
294 case '\r':
295 if (hdr_pattern[0] == '\0') {
296 ClearLine(LINES-1);
297 return FALSE;
298 }
299 sel_pat = hdr_pattern;
300 anywhere = FALSE;
301 break;
302
303 case '/': /* switch patterns and match against entire message */
304 MoveCursor(LINES-3, 40);
305 CleartoEOLN();
306 PutLine1(LINES-1, 0, catgets(elm_msg_cat, ElmSet,
307 ElmMatchPatternInEntire, "Match pattern (in entire %s): "),
308 nls_item);
309 (void) strcpy(inpbuf, body_pattern);
310 if (enter_string(inpbuf, sizeof(inpbuf), -1, -1, ESTR_UPDATE) < 0
311 || inpbuf[0] == '\0') {
312 ClearLine(LINES-1);
313 return FALSE;
314 }
315 sel_pat = strcpy(body_pattern, inpbuf);
316 anywhere = TRUE;
317 break;
318
319 default: /* edit the pattern */
320 UnreadCh(ch); /* push back - let enter_string() deal with it */
321 (void) strfcpy(inpbuf, hdr_pattern, sizeof(inpbuf));
322 if (enter_string(inpbuf, sizeof(inpbuf), inp_line, inp_col,
323 ESTR_UPDATE) < 0 || inpbuf[0] == '\0') {
324 ClearLine(LINES-1);
325 return FALSE;
326 }
327 sel_pat = strcpy(hdr_pattern, inpbuf);
328 anywhere = FALSE;
329 break;
330
331 }
332
333 if (inalias) {
334 for (i = curr_alias; i < num_aliases; i++) {
335 if (!selected || aliases[i]->status & VISIBLE) {
336 matched = name_matches(i, sel_pat) || alias_matches(i, sel_pat);
337 if (! matched && anywhere) { /* Look only if no match yet */
338 matched = comment_matches(i, sel_pat) ||
339 address_matches(i, sel_pat);
340 }
341 if (matched) {
342 curr_alias = i+1;
343 return TRUE;
344 }
345 }
346 }
347 goto not_matched;
348 }
349
350 if (anywhere) {
351 if (match_in_message(sel_pat))
352 return TRUE;
353 goto not_matched;
354 }
355
356 for (i = curr_folder.curr_mssg; i < curr_folder.num_mssgs; i++) {
357 if (!selected || curr_folder.headers[i]->status & VISIBLE) {
358 if (from_matches(i, sel_pat) || subject_matches(i, sel_pat)) {
359 curr_folder.curr_mssg = i+1;
360 return TRUE;
361 }
362 }
363 }
364
365 not_matched:
366 error(catgets(elm_msg_cat, ElmSet, ElmPatternNotFound,
367 "pattern not found!"));
368 return FALSE;
369 }
370
371
372 /*
373 * Local Procedures
374 */
375
376 static int
from_matches(message_number,pat)377 from_matches(message_number, pat)
378 int message_number;
379 char *pat;
380 {
381 return patmatch(pat, curr_folder.headers[message_number]->from, PM_NOCASE|PM_WSFOLD);
382 }
383
384 static int
subject_matches(message_number,pat)385 subject_matches(message_number, pat)
386 int message_number;
387 char *pat;
388 {
389 return patmatch(pat, curr_folder.headers[message_number]->subject, PM_NOCASE|PM_WSFOLD);
390 }
391
392 static int
name_matches(message_number,pat)393 name_matches(message_number, pat)
394 int message_number;
395 char *pat;
396 {
397 return patmatch(pat, aliases[message_number]->name, PM_NOCASE|PM_WSFOLD);
398 }
399
400 static int
alias_matches(message_number,pat)401 alias_matches(message_number, pat)
402 int message_number;
403 char *pat;
404 {
405 return patmatch(pat, aliases[message_number]->alias, PM_NOCASE|PM_WSFOLD);
406 }
407
408 static int
comment_matches(message_number,pat)409 comment_matches(message_number, pat)
410 int message_number;
411 char *pat;
412 {
413 return patmatch(pat, aliases[message_number]->comment, PM_NOCASE|PM_WSFOLD);
414 }
415
416 static int
address_matches(message_number,pat)417 address_matches(message_number, pat)
418 int message_number;
419 char *pat;
420 {
421 char *exp;
422 int dummy;
423
424 exp = get_alias_address(aliases[message_number]->alias, TRUE, &dummy);
425 return (exp != NULL && patmatch(pat, exp, PM_NOCASE|PM_WSFOLD));
426 }
427
428 static int
match_in_message(pat)429 match_in_message(pat)
430 char *pat;
431 {
432 /** Match a string INSIDE a message...starting at the current
433 message read each line and try to find the pattern. As
434 soon as we do, set current and leave!
435 Returns 1 if found, 0 if not
436 **/
437
438 char buffer[VERY_LONG_STRING];
439 int message_number, lines, line, line_len, err;
440
441 message_number = curr_folder.curr_mssg-1;
442
443 error(catgets(elm_msg_cat, ElmSet, ElmSearchingFolderPattern,
444 "Searching folder for pattern..."));
445
446 for ( ; message_number < curr_folder.num_mssgs; message_number++) {
447
448 /* if limited, search only selected messages */
449 if (selected &&
450 !(curr_folder.headers[message_number]->status & VISIBLE))
451 continue;
452
453 if (fseek(curr_folder.fp, curr_folder.headers[message_number]->offset, 0L) == -1) {
454
455 err = errno;
456 dprint(1, (debugfile,
457 "Error: seek %ld bytes into file failed. errno %d (%s)\n",
458 curr_folder.headers[message_number]->offset, err,
459 "match_in_message"));
460 error2(catgets(elm_msg_cat, ElmSet, ElmMatchSeekFailed,
461 "ELM [match] failed looking %ld bytes into file (%s)."),
462 curr_folder.headers[message_number]->offset, strerror(err));
463 return TRUE; /* fake it out to avoid replacing error message */
464 }
465
466 line = 0;
467 lines = curr_folder.headers[message_number]->lines;
468
469 while ((line_len = mail_gets(buffer, VERY_LONG_STRING, curr_folder.fp)) &&
470 line < lines) {
471
472 if(buffer[line_len - 1] == '\n') line++;
473
474 if (patmatch(pat, buffer, PM_NOCASE|PM_WSFOLD)) {
475 curr_folder.curr_mssg = message_number+1;
476 clear_error();
477 return TRUE;
478 }
479 }
480 }
481
482 return FALSE;
483 }
484
485