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