1 /* artsrch.c
2  */
3 /* This software is copyrighted as detailed in the LICENSE file. */
4 
5 
6 #include "EXTERN.h"
7 #include "common.h"
8 #include "list.h"
9 #include "hash.h"
10 #include "ngdata.h"
11 #include "nntpclient.h"
12 #include "datasrc.h"
13 #include "nntp.h"
14 #include "search.h"
15 #include "term.h"
16 #include "util.h"
17 #include "util2.h"
18 #include "intrp.h"
19 #include "cache.h"
20 #include "bits.h"
21 #include "kfile.h"
22 #include "head.h"
23 #include "final.h"
24 #include "ng.h"
25 #include "addng.h"
26 #include "ngstuff.h"
27 #include "artio.h"
28 #include "rthread.h"
29 #include "rt-util.h"
30 #include "rt-select.h"
31 #include "INTERN.h"
32 #include "artsrch.h"
33 
34 void
artsrch_init()35 artsrch_init()
36 {
37 #ifdef ARTSEARCH
38     init_compex(&sub_compex);
39     init_compex(&art_compex);
40 #endif
41 }
42 
43 /* search for an article containing some pattern */
44 
45 #ifdef ARTSEARCH
46 int
art_search(patbuf,patbufsiz,get_cmd)47 art_search(patbuf,patbufsiz,get_cmd)
48 char* patbuf;				/* if patbuf != buf, get_cmd must */
49 int patbufsiz;
50 int get_cmd;				/*   be set to FALSE!!! */
51 {
52     char* pattern;			/* unparsed pattern */
53     register char cmdchr = *patbuf;	/* what kind of search? */
54     register char* s;
55     bool backward = cmdchr == '?' || cmdchr == Ctl('p');
56 					/* direction of search */
57     COMPEX* compex;			/* which compiled expression */
58     char* cmdlst = NULL;		/* list of commands to do */
59     int ret = SRCH_NOTFOUND;		/* assume no commands */
60     int saltaway = 0;			/* store in KILL file? */
61     int howmuch;			/* search scope: subj/from/Hdr/head/art */
62     int srchhdr;			/* header to search if Hdr scope */
63     bool topstart = 0;
64     bool doread;			/* search read articles? */
65     bool foldcase = TRUE;		/* fold upper and lower case? */
66     int ignorethru = 0;			/* should we ignore the thru line? */
67     bool output_level = (!use_threads && gmode != 's');
68     ART_NUM srchfirst;
69 
70     int_count = 0;
71     if (cmdchr == '/' || cmdchr == '?') {	/* normal search? */
72 	if (get_cmd && buf == patbuf)
73 	    if (!finish_command(FALSE))	/* get rest of command */
74 		return SRCH_ABORT;
75 	compex = &art_compex;
76 	if (patbuf[1]) {
77 	    howmuch = ARTSCOPE_SUBJECT;
78 	    srchhdr = SOME_LINE;
79 	    doread = FALSE;
80 	}
81 	else {
82 	    howmuch = art_howmuch;
83 	    srchhdr = art_srchhdr;
84 	    doread = art_doread;
85 	}
86 	s = cpytill(buf,patbuf+1,cmdchr);/* ok to cpy buf+1 to buf */
87 	pattern = buf;
88 	if (*pattern) {
89 	    if (*lastpat)
90 		free(lastpat);
91 	    lastpat = savestr(pattern);
92 	}
93 	if (*s) {			/* modifiers or commands? */
94 	    while (*++s) {
95 		switch (*s) {
96 		case 'f':		/* scan the From line */
97 		    howmuch = ARTSCOPE_FROM;
98 		    break;
99 		case 'H':		/* scan a specific header */
100 		    howmuch = ARTSCOPE_ONEHDR;
101 		    s = cpytill(msg, s+1, ':');
102 		    srchhdr = get_header_num(msg);
103 		    goto loop_break;
104 		case 'h':		/* scan header */
105 		    howmuch = ARTSCOPE_HEAD;
106 		    break;
107 		case 'b':		/* scan body sans signature */
108 		    howmuch = ARTSCOPE_BODY_NOSIG;
109 		    break;
110 		case 'B':		/* scan body */
111 		    howmuch = ARTSCOPE_BODY;
112 		    break;
113 		case 'a':		/* scan article */
114 		    howmuch = ARTSCOPE_ARTICLE;
115 		    break;
116 		case 't':		/* start from the top */
117 		    topstart = TRUE;
118 		    break;
119 		case 'r':		/* scan read articles */
120 		    doread = TRUE;
121 		    break;
122 		case 'K':		/* put into KILL file */
123 		    saltaway = 1;
124 		    break;
125 		case 'c':		/* make search case sensitive */
126 		    foldcase = FALSE;
127 		    break;
128 		case 'I':		/* ignore the killfile thru line */
129 		    ignorethru = 1;
130 		    break;
131 		case 'N':		/* override ignore if -k was used */
132 		    ignorethru = -1;
133 		    break;
134 		default:
135 		    goto loop_break;
136 		}
137 	    }
138 	  loop_break:;
139 	}
140 	while (isspace(*s) || *s == ':') s++;
141 	if (*s) {
142 #ifdef OLD_RN_WAY
143 	    if (*s == 'm' || *s == 'M')
144 #else
145 	    if (*s == 'm')
146 #endif
147 		doread = TRUE;
148 	    if (*s == 'k')		/* grandfather clause */
149 		*s = 'j';
150 	    cmdlst = savestr(s);
151 	    ret = SRCH_DONE;
152 	}
153 	art_howmuch = howmuch;
154 	art_srchhdr = srchhdr;
155 	art_doread = doread;
156 	if (srchahead)
157 	    srchahead = -1;
158     }
159     else {
160 	register char* h;
161 	int saltmode = patbuf[2] == 'g'? 2 : 1;
162 	char *finding_str = patbuf[1] == 'f'? "author" : "subject";
163 
164 	howmuch = patbuf[1] == 'f'? ARTSCOPE_FROM : ARTSCOPE_SUBJECT;
165 	srchhdr = SOME_LINE;
166 	doread = (cmdchr == Ctl('p'));
167 	if (cmdchr == Ctl('n'))
168 	    ret = SRCH_SUBJDONE;
169 	compex = &sub_compex;
170 	pattern = patbuf+1;
171 	if (howmuch == ARTSCOPE_SUBJECT) {
172 	    strcpy(pattern,": *");
173 	    h = pattern + strlen(pattern);
174 	    interp(h,patbufsiz - (h-patbuf),"%\\s");  /* fetch current subject */
175 	}
176 	else {
177 	    h = pattern;
178 	    /*$$ if using thread files, make this "%\\)f" */
179 	    interp(pattern, patbufsiz - 1, "%\\>f");
180 	}
181 	if (cmdchr == 'k' || cmdchr == 'K' || cmdchr == ','
182 	 || cmdchr == '+' || cmdchr == '.' || cmdchr == 's') {
183 	    if (cmdchr != 'k')
184 		saltaway = saltmode;
185 	    ret = SRCH_DONE;
186 	    if (cmdchr == '+') {
187 		cmdlst = savestr("+");
188 		if (!ignorethru && kill_thru_kludge)
189 		    ignorethru = 1;
190 	    }
191 	    else if (cmdchr == '.') {
192 		cmdlst = savestr(".");
193 		if (!ignorethru && kill_thru_kludge)
194 		    ignorethru = 1;
195 	    }
196 	    else if (cmdchr == 's') {
197 		cmdlst = savestr(patbuf);
198 		/*ignorethru = 1;*/
199 	    }
200 	    else {
201 		if (cmdchr == ',')
202 		    cmdlst = savestr(",");
203 		else
204 		    cmdlst = savestr("j");
205 		mark_as_read(article_ptr(art));	/* this article needs to die */
206 	    }
207 	    if (!*h) {
208 #ifdef VERBOSE
209 		IF(verbose)
210 		    sprintf(msg, "Current article has no %s.", finding_str);
211 		ELSE
212 #endif
213 #ifdef TERSE
214 		    sprintf(msg, "Null %s.", finding_str);
215 #endif
216 		errormsg(msg);
217 		ret = SRCH_ABORT;
218 		goto exit;
219 	    }
220 #ifdef VERBOSE
221 	    if (verbose) {
222 		if (cmdchr != '+' && cmdchr != '.')
223 		    printf("\nMarking %s \"%s\" as read.\n",finding_str,h) FLUSH;
224 		else
225 		    printf("\nSelecting %s \"%s\".\n",finding_str,h) FLUSH;
226 		termdown(2);
227 	    }
228 #endif
229 	}
230 	else if (!srchahead)
231 	    srchahead = -1;
232 
233 	{			/* compensate for notesfiles */
234 	    register int i;
235 	    for (i = 24; *h && i--; h++)
236 		if (*h == '\\')
237 		    h++;
238 	    *h = '\0';
239 	}
240 #ifdef DEBUG
241 	if (debug) {
242 	    printf("\npattern = %s\n",pattern) FLUSH;
243 	    termdown(2);
244 	}
245 #endif
246     }
247     if ((s = compile(compex,pattern,TRUE,foldcase)) != NULL) {
248 					/* compile regular expression */
249 	errormsg(s);
250 	ret = SRCH_ABORT;
251 	goto exit;
252     }
253     if (cmdlst && index(cmdlst,'='))
254 	ret = SRCH_ERROR;		/* listing subjects is an error? */
255     if (gmode == 's') {
256 	if (!cmdlst) {
257 	    if (sel_mode == SM_ARTICLE)/* set the selector's default command */
258 		cmdlst = savestr("+");
259 	    else
260 		cmdlst = savestr("++");
261 	}
262 	ret = SRCH_DONE;
263     }
264 #ifdef KILLFILES
265     if (saltaway) {
266 	char saltbuf[LBUFLEN], *f;
267 
268 	s = saltbuf;
269 	f = pattern;
270 	*s++ = '/';
271 	while (*f) {
272 	    if (*f == '/')
273 		*s++ = '\\';
274 	    *s++ = *f++;
275 	}
276 	*s++ = '/';
277 	if (doread)
278 	    *s++ = 'r';
279 	if (!foldcase)
280 	    *s++ = 'c';
281 	if (ignorethru)
282 	    *s++ = (ignorethru == 1 ? 'I' : 'N');
283 	if (howmuch != ARTSCOPE_SUBJECT) {
284 	    *s++ = scopestr[howmuch];
285 	    if (howmuch == ARTSCOPE_ONEHDR) {
286 		safecpy(s,htype[srchhdr].name,LBUFLEN-(s-saltbuf));
287 		s += htype[srchhdr].length;
288 		if (s - saltbuf > LBUFLEN-2)
289 		    s = saltbuf+LBUFLEN-2;
290 	    }
291 	}
292 	*s++ = ':';
293 	if (!cmdlst)
294 	    cmdlst = savestr("j");
295 	safecpy(s,cmdlst,LBUFLEN-(s-saltbuf));
296 	kf_append(saltbuf, saltaway == 2? KF_GLOBAL : KF_LOCAL);
297     }
298 #endif
299     if (get_cmd) {
300 	if (use_threads)
301 	    newline();
302 	else {
303 	    fputs("\nSearching...\n",stdout) FLUSH;
304 	    termdown(2);
305 	}
306 					/* give them something to read */
307     }
308     if (ignorethru == 0 && kill_thru_kludge && cmdlst
309      && (*cmdlst == '+' || *cmdlst == '.'))
310 	ignorethru = 1;
311     srchfirst = doread || sel_rereading? absfirst
312 		      : (mode != 'k' || ignorethru > 0)? firstart : killfirst;
313     if (topstart || art == 0) {
314 	art = lastart+1;
315 	topstart = FALSE;
316     }
317     if (backward) {
318 	if (cmdlst && art <= lastart)
319 	    art++;			/* include current article */
320     }
321     else {
322 	if (art > lastart)
323 	    art = srchfirst-1;
324 	else if (cmdlst && art >= absfirst)
325 	    art--;			/* include current article */
326     }
327     if (srchahead > 0) {
328 	if (!backward)
329 	    art = srchahead - 1;
330 	srchahead = -1;
331     }
332     assert(!cmdlst || *cmdlst);
333     perform_status_init(ngptr->toread);
334     for (;;) {
335 	/* check if we're out of articles */
336 	if (backward? ((art = article_prev(art)) < srchfirst)
337 		    : ((art = article_next(art)) > lastart))
338 	    break;
339 	if (int_count) {
340 	    int_count = 0;
341 	    ret = SRCH_INTR;
342 	    break;
343 	}
344 	artp = article_ptr(art);
345 	if (doread || (!(artp->flags & AF_UNREAD) ^ !sel_rereading)) {
346 	    if (wanted(compex,art,howmuch)) {
347 				    /* does the shoe fit? */
348 		if (!cmdlst)
349 		    return SRCH_FOUND;
350 		if (perform(cmdlst,output_level && page_line == 1) < 0) {
351 		    free(cmdlst);
352 		    return SRCH_INTR;
353 		}
354 	    }
355 	    else if (output_level && !cmdlst && !(art%50)) {
356 		printf("...%ld",(long)art);
357 		fflush(stdout);
358 	    }
359 	}
360 	if (!output_level && page_line == 1)
361 	    perform_status(ngptr->toread, 60 / (howmuch+1));
362     }
363 exit:
364     if (cmdlst)
365 	free(cmdlst);
366     return ret;
367 }
368 #endif /* ARTSEARCH */
369 
370 /* determine if article fits pattern */
371 /* returns TRUE if it exists and fits pattern, FALSE otherwise */
372 
373 #ifdef ARTSEARCH
374 bool
wanted(compex,artnum,scope)375 wanted(compex, artnum, scope)
376 COMPEX* compex;
377 ART_NUM artnum;
378 char_int scope;
379 {
380     ARTICLE* ap = article_find(artnum);
381 
382     if (!ap || !(ap->flags & AF_EXISTS))
383 	return FALSE;
384 
385     switch (scope) {
386       case ARTSCOPE_SUBJECT:
387 	strcpy(buf,"Subject: ");
388 	strncpy(buf+9,fetchsubj(artnum,FALSE),256);
389 #ifdef DEBUG
390 	if (debug & DEB_SEARCH_AHEAD)
391 	    printf("%s\n",buf) FLUSH;
392 #endif
393 	break;
394       case ARTSCOPE_FROM:
395 	strcpy(buf, "From: ");
396 	strncpy(buf+6,fetchfrom(artnum,FALSE),256);
397 	break;
398       case ARTSCOPE_ONEHDR:
399 	untrim_cache = TRUE;
400 	sprintf(buf, "%s: %s", htype[art_srchhdr].name,
401 		prefetchlines(artnum,art_srchhdr,FALSE));
402 	untrim_cache = FALSE;
403 	break;
404       default: {
405 	char* s;
406 	char* nlptr;
407 	char ch;
408 	bool success = FALSE, in_sig = FALSE;
409 	if (scope != ARTSCOPE_BODY && scope != ARTSCOPE_BODY_NOSIG) {
410 	    if (!parseheader(artnum))
411 		return FALSE;
412 	    /* see if it's in the header */
413 	    if (execute(compex,headbuf))	/* does it match? */
414 		return TRUE;			/* say, "Eureka!" */
415 	    if (scope < ARTSCOPE_ARTICLE)
416 		return FALSE;
417 	}
418 	if (parsed_art == artnum) {
419 	    if (!artopen(artnum,htype[PAST_HEADER].minpos))
420 		return FALSE;
421 	}
422 	else {
423 	    if (!artopen(artnum,(ART_POS)0))
424 		return FALSE;
425 	    if (!parseheader(artnum))
426 		return FALSE;
427 	}
428 	/* loop through each line of the article */
429 	seekartbuf(htype[PAST_HEADER].minpos);
430 	while ((s = readartbuf(FALSE)) != NULL) {
431 	    if (scope == ARTSCOPE_BODY_NOSIG && *s == '-' && s[1] == '-'
432 	     && (s[2] == '\n' || (s[2] == ' ' && s[3] == '\n'))) {
433 		if (in_sig && success)
434 		    return TRUE;
435 		in_sig = TRUE;
436 	    }
437 	    if ((nlptr = index(s,'\n')) != NULL) {
438 		ch = *++nlptr;
439 		*nlptr = '\0';
440 	    }
441 	    success = success || execute(compex,s) != NULL;
442 	    if (nlptr)
443 		*nlptr = ch;
444 	    if (success && !in_sig)		/* does it match? */
445 		return TRUE;			/* say, "Eureka!" */
446 	}
447 	return FALSE;			/* out of article, so no match */
448       }
449     }
450     return execute(compex,buf) != NULL;
451 }
452 #endif /* ARTSEARCH */
453