"Fossies" - the Fresh Open Source Software Archive 
Member "tin-2.6.2/src/search.c" (9 Dec 2022, 19436 Bytes) of package /linux/misc/tin-2.6.2.tar.xz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "search.c" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
2.6.1_vs_2.6.2.
1 /*
2 * Project : tin - a Usenet reader
3 * Module : search.c
4 * Author : I. Lea & R. Skrenta
5 * Created : 1991-04-01
6 * Updated : 2022-08-29
7 * Notes :
8 *
9 * Copyright (c) 1991-2023 Iain Lea <iain@bricbrac.de>, Rich Skrenta <skrenta@pbm.com>
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 *
16 * 1. Redistributions of source code must retain the above copyright notice,
17 * this list of conditions and the following disclaimer.
18 *
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 *
23 * 3. Neither the name of the copyright holder nor the names of its
24 * contributors may be used to endorse or promote products derived from
25 * this software without specific prior written permission.
26 *
27 * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
31 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
38 */
39
40
41 #ifndef TIN_H
42 # include "tin.h"
43 #endif /* !TIN_H */
44
45
46 /*
47 * local prototypes
48 */
49 static char *get_search_pattern(t_bool *forward, t_bool repeat, const char *fwd_msg, const char *bwd_msg, char *def, int which_hist);
50 static int author_search(int i, char *searchbuf);
51 static int body_search(int i, char *searchbuf);
52 static int subject_search(int i, char *searchbuf);
53 static int search_group(t_bool forward, int current_art, char *searchbuff, int (*search_func) (int i, char *searchbuff));
54
55
56 /*
57 * Kludge to maintain some internal state for body search
58 */
59 int srch_lineno = -1;
60 static int total_cnt = 0, curr_cnt = 0;
61
62 /*
63 * Used by article and body search - this saves passing around large numbers
64 * of parameters all the time
65 */
66 static t_bool last_article_search_matched = FALSE;
67 static REGEX_SIZE srch_offsets[2];
68 static REGEX_NOFFSET srch_offsets_size = ARRAY_SIZE(srch_offsets);
69 static struct regex_cache search_regex = REGEX_CACHE_INITIALIZER;
70
71 /*
72 * Used to copy the per regex_cache offsets to the global ones.
73 */
74 static void
75 copy_offsets(
76 REGEX_SIZE *dst,
77 REGEX_NOFFSET dst_size,
78 struct regex_cache *re)
79 {
80 REGEX_NOFFSET i;
81 REGEX_NOFFSET limit = 2 * regex_get_ovector_count(re);
82 REGEX_SIZE *ovector = regex_get_ovector_pointer(re);
83
84 if (limit > dst_size)
85 limit = dst_size;
86
87 for (i = 0; i < limit; i++)
88 dst[i] = ovector[i];
89 }
90
91
92 /*
93 * Obtain the search pattern, save it in the default buffer.
94 * Return NULL if no pattern could be found
95 */
96 static char *
97 get_search_pattern(
98 t_bool *forward,
99 t_bool repeat,
100 const char *fwd_msg,
101 const char *bwd_msg,
102 char *def,
103 int which_hist)
104 {
105 static char tmpbuf[LEN]; /* Hold the last pattern used */
106 static char last_pattern[LEN]; /* last search pattern used; for repeated search */
107 static t_bool last_forward;
108
109 if (repeat) {
110 *forward = last_forward;
111 my_strncpy(def, last_pattern, LEN);
112 } else {
113 snprintf(tmpbuf, sizeof(tmpbuf), (*forward ? fwd_msg : bwd_msg), def);
114
115 if (!prompt_string_default(tmpbuf, def, _(txt_no_search_string), which_hist))
116 return NULL;
117
118 last_forward = *forward;
119 my_strncpy(last_pattern, def, LEN);
120
121 /* HIST_BODY_SEARCH doesn't exist, hence last_search is set directly in search_body() */
122 if (which_hist == HIST_AUTHOR_SEARCH)
123 last_search = *forward ? GLOBAL_SEARCH_AUTHOR_FORWARD : GLOBAL_SEARCH_AUTHOR_BACKWARD;
124 else
125 last_search = *forward ? GLOBAL_SEARCH_SUBJECT_FORWARD : GLOBAL_SEARCH_SUBJECT_BACKWARD;
126 }
127
128 wait_message(0, _(txt_searching));
129
130 stow_cursor();
131
132 #ifdef HAVE_UNICODE_NORMALIZATION
133 /* normalize search pattern */
134 if (IS_LOCAL_CHARSET("UTF-8")) {
135 char *tmp;
136
137 tmp = normalize(def);
138 my_strncpy(def, tmp, LEN);
139 free(tmp);
140 }
141 #endif /* HAVE_UNICODE_NORMALIZATION */
142
143 if (tinrc.wildcard) { /* ie, not wildmat() */
144 strcpy(def, quote_wild_whitespace(def));
145 return def;
146 }
147
148 /*
149 * A gross hack to simulate substrings with wildmat()
150 */
151 /* TODO: somehow use REGEX_FMT here? */
152 snprintf(tmpbuf, sizeof(tmpbuf), "*%s*", def);
153 return tmpbuf;
154 }
155
156
157 /*
158 * called by config.c
159 */
160 enum option_enum
161 search_config(
162 t_bool forward,
163 t_bool repeat,
164 enum option_enum current,
165 enum option_enum last)
166 {
167 char *pattern, *buf;
168 enum option_enum n = current;
169 enum option_enum result = current;
170
171 if (!(pattern = get_search_pattern(&forward, repeat, _(txt_search_forwards), _(txt_search_backwards), tinrc.default_search_config, HIST_CONFIG_SEARCH)))
172 return result;
173
174 if (tinrc.wildcard && !(compile_regex(pattern, &search_regex, REGEX_CASELESS)))
175 return result;
176
177 do {
178 if (n == 0 && !forward)
179 n = last;
180 else {
181 if (n == last && forward)
182 n = 0;
183 else
184 n = (n + (enum option_enum) (forward ? 1 : -1));
185 }
186 /* search only visible options */
187 if (option_is_visible(n)) {
188 #ifdef HAVE_UNICODE_NORMALIZATION
189 if (IS_LOCAL_CHARSET("UTF-8"))
190 buf = normalize(_(option_table[n].txt->opt));
191 else
192 #endif /* HAVE_UNICODE_NORMALIZATION */
193 buf = my_strdup(_(option_table[n].txt->opt));
194
195 if (match_regex(buf, pattern, &search_regex, TRUE)) {
196 result = n;
197 free(buf);
198 break;
199 }
200 free(buf);
201 }
202 } while (n != current);
203
204 clear_message();
205 if (tinrc.wildcard) {
206 regex_cache_destroy(&search_regex);
207 }
208 return result;
209 }
210
211
212 /*
213 * called by save.c (search for attachment) and page.c (search for URL)
214 */
215 int
216 generic_search(
217 t_bool forward,
218 t_bool repeat,
219 int current,
220 int last,
221 int level)
222 {
223 char *pattern;
224 char buf[BUFSIZ];
225 const char *name, *charset;
226 int n = current;
227 int result = current;
228 t_bool found = FALSE;
229 t_part *part;
230 t_url *urlptr;
231 t_posted *phptr;
232
233 if (!(pattern = get_search_pattern(&forward, repeat, _(txt_search_forwards), _(txt_search_backwards), tinrc.default_search_config, HIST_CONFIG_SEARCH)))
234 return result;
235
236 if (tinrc.wildcard && !(compile_regex(pattern, &search_regex, REGEX_CASELESS)))
237 return result;
238
239 do {
240 if (n == 0 && !forward)
241 n = last;
242 else {
243 if (n == last && forward)
244 n = 0;
245 else
246 n += (forward ? 1 : -1);
247 }
248 switch (level) {
249 case ATTACHMENT_LEVEL:
250 part = get_part(n);
251 if (!(name = get_filename(part->params))) {
252 if (!(name = part->description))
253 name = _(txt_attachment_no_name);
254 }
255 charset = get_param(part->params, "charset");
256 snprintf(buf, sizeof(buf), "%s %s/%s %s, %s", name, content_types[part->type], part->subtype, content_encodings[part->encoding], charset ? charset : "");
257 break;
258
259 case POSTED_LEVEL:
260 phptr = find_post_hist(n);
261 snprintf(buf, sizeof(buf), "%s %s %s", phptr->date, phptr->group, phptr->subj);
262 break;
263
264 case URL_LEVEL:
265 urlptr = find_url(n);
266 snprintf(buf, sizeof(buf), "%s", urlptr->url);
267 break;
268
269 default:
270 buf[0] = '\0';
271 break;
272 }
273 if (match_regex(buf, pattern, &search_regex, TRUE)) {
274 result = n;
275 found = TRUE;
276 break;
277 }
278 } while (n != current);
279
280 clear_message();
281 if (tinrc.wildcard) {
282 regex_cache_destroy(&search_regex);
283 }
284 if (!found)
285 info_message(_(txt_no_match));
286
287 return result;
288 }
289
290
291 /*
292 * Search active[] looking for a groupname
293 * Called by select.c
294 * Return index into active of matching groupname or -1
295 */
296 int
297 search_active(
298 t_bool forward,
299 t_bool repeat)
300 {
301 char *buf;
302 char *ptr;
303 char buf2[LEN];
304 int i;
305
306 if (!selmenu.max) {
307 info_message(_(txt_no_groups));
308 return -1;
309 }
310
311 if (!(buf = get_search_pattern(&forward, repeat, _(txt_search_forwards), _(txt_search_backwards), tinrc.default_search_group, HIST_GROUP_SEARCH)))
312 return -1;
313
314 if (tinrc.wildcard && !(compile_regex(buf, &search_regex, REGEX_CASELESS)))
315 return -1;
316
317 i = selmenu.curr;
318
319 do {
320 if (forward) {
321 if (++i >= selmenu.max)
322 i = 0;
323 } else {
324 if (--i < 0)
325 i = selmenu.max - 1;
326 }
327
328 /*
329 * Get the group name & description into buf2
330 */
331 if (show_description && active[my_group[i]].description) {
332 snprintf(buf2, sizeof(buf2), "%s %s", active[my_group[i]].name, active[my_group[i]].description);
333 ptr = buf2;
334 } else
335 ptr = active[my_group[i]].name;
336
337 if (match_regex(ptr, buf, &search_regex, TRUE)) {
338 if (tinrc.wildcard) {
339 regex_cache_destroy(&search_regex);
340 }
341 return i;
342 }
343 } while (i != selmenu.curr);
344
345 if (tinrc.wildcard) {
346 regex_cache_destroy(&search_regex);
347 }
348 info_message(_(txt_no_match));
349 return -1;
350 }
351
352
353 /*
354 * Scan the body of an arts[i] for searchbuf
355 * used only by search_body()
356 * Returns: 1 String found
357 * 0 Not found
358 * -1 User aborted search
359 */
360 static int
361 body_search(
362 int i,
363 char *searchbuf)
364 {
365 static char msg[LEN]; /* show_progress needs a constant message buffer */
366 char *line, *tmp;
367 t_openartinfo artinfo;
368
369 switch (art_open(TRUE, &arts[i], curr_group, &artinfo, FALSE, NULL)) {
370 case ART_ABORT: /* User 'q'uit */
371 art_close(&artinfo);
372 return -1;
373
374 case ART_UNAVAILABLE: /* Treat as string not present */
375 art_close(&artinfo);
376 info_message(_(txt_no_match));
377 return 0;
378 }
379
380 /*
381 * Skip the header - is this right ?
382 */
383 for (i = 0; artinfo.cookl[i].flags & C_HEADER; ++i)
384 ;
385 if (fseek(artinfo.cooked, artinfo.cookl[i].offset, SEEK_SET) != 0) {
386 art_close(&artinfo);
387 return -1;
388 }
389
390 /*
391 * Now search the body
392 */
393 snprintf(msg, sizeof(msg), _(txt_searching_body), ++curr_cnt, total_cnt);
394 show_progress(msg, curr_cnt, total_cnt);
395 while ((tmp = tin_fgets(artinfo.cooked, FALSE)) != NULL) {
396 #ifdef HAVE_UNICODE_NORMALIZATION
397 if (IS_LOCAL_CHARSET("UTF-8"))
398 line = normalize(tmp);
399 else
400 #endif /* HAVE_UNICODE_NORMALIZATION */
401 line = my_strdup(tmp);
402
403 if (tinrc.wildcard) {
404 if (match_regex_ex(line, (int) strlen(line), 0, 0, &search_regex) >= 0) {
405 copy_offsets(srch_offsets, srch_offsets_size, &search_regex);
406 srch_lineno = i;
407 art_close(&pgart); /* Switch the pager over to matched art */
408 pgart = artinfo;
409 #ifdef DEBUG
410 if (debug & DEBUG_MISC)
411 fprintf(stderr, "art_switch(%p = %p)\n", (void *) &pgart, (void *) &artinfo);
412 #endif /* DEBUG */
413 free(line);
414
415 return 1;
416 }
417 } else {
418 if (wildmatpos(line, searchbuf, TRUE, srch_offsets, srch_offsets_size)) {
419 srch_lineno = i;
420 art_close(&pgart); /* Switch the pager over to matched art */
421 pgart = artinfo;
422 free(line);
423 return 1;
424 }
425 }
426 i++;
427 free(line);
428 }
429
430 if (tin_errno != 0) { /* User abort */
431 art_close(&artinfo);
432 return -1;
433 }
434
435 art_close(&artinfo);
436 /* info_message(_(txt_no_match)); */
437 return 0;
438 }
439
440
441 /*
442 * Match searchbuff against the From: information in arts[i]
443 * 1 = found, 0 = not found
444 */
445 static int
446 author_search(
447 int i,
448 char *searchbuf)
449 {
450 char *buf, *tmp;
451
452 if (arts[i].name == NULL)
453 tmp = my_strdup(arts[i].from);
454 else {
455 size_t len = strlen(arts[i].from) + strlen(arts[i].name) + 4;
456
457 tmp = my_malloc(len);
458 snprintf(tmp, len, "%s <%s>", arts[i].name, arts[i].from);
459 }
460
461 #ifdef HAVE_UNICODE_NORMALIZATION
462 if (IS_LOCAL_CHARSET("UTF-8")) {
463 buf = normalize(tmp);
464 free(tmp);
465 } else
466 #endif /* HAVE_UNICODE_NORMALIZATION */
467 buf = tmp;
468
469 if (match_regex(buf, searchbuf, &search_regex, TRUE)) {
470 free(buf);
471 return 1;
472 }
473
474 free(buf);
475 return 0;
476 }
477
478
479 /*
480 * Match searchbuff against the Subject: information in arts[i]
481 * 1 = found, 0 = not found
482 */
483 static int
484 subject_search(
485 int i,
486 char *searchbuf)
487 {
488 char *buf;
489
490 #ifdef HAVE_UNICODE_NORMALIZATION
491 if (IS_LOCAL_CHARSET("UTF-8"))
492 buf = normalize(arts[i].subject);
493 else
494 #endif /* HAVE_UNICODE_NORMALIZATION */
495 buf = my_strdup(arts[i].subject);
496
497 if (match_regex(buf, searchbuf, &search_regex, TRUE)) {
498 free(buf);
499 return 1;
500 }
501
502 free(buf);
503 return 0;
504 }
505
506
507 /*
508 * Returns index into arts[] of matching article or -1
509 */
510 static int
511 search_group(
512 t_bool forward,
513 int current_art,
514 char *searchbuff,
515 int (*search_func) (int i, char *searchbuff))
516 {
517 int i, ret, loop_cnt;
518
519 if (grpmenu.curr < 0) {
520 info_message(_(txt_no_arts));
521 return -1;
522 }
523
524 /*
525 * precompile if we're using full regex
526 */
527 if (tinrc.wildcard && !(compile_regex(searchbuff, &search_regex, REGEX_CASELESS)))
528 return -1;
529
530 i = current_art;
531 loop_cnt = 1;
532
533 do {
534 if (forward) {
535 if ((i = next_response(i)) < 0)
536 i = (int) base[0];
537 } else {
538 if ((i = prev_response(i)) < 0)
539 i = find_response(grpmenu.max - 1, num_of_responses(grpmenu.max - 1));
540 }
541
542 /* Only search displayed articles */
543 if (curr_group->attribute->show_only_unread_arts && arts[i].status != ART_UNREAD)
544 continue;
545
546 ret = search_func(i, searchbuff);
547 if (tinrc.wildcard && (ret == 1 || ret == -1)) {
548 /* we will exit soon, clean up */
549 regex_cache_destroy(&search_regex);
550 }
551 switch (ret) {
552 case 1: /* Found */
553 clear_message();
554 return i;
555
556 case -1: /* User abort */
557 return -1;
558 }
559 #ifdef HAVE_SELECT
560 if (wait_for_input()) /* allow abort */
561 return -1;
562 #endif /* HAVE_SELECT */
563 if (loop_cnt % (MODULO_COUNT_NUM * 20) == 0)
564 show_progress(txt_searching, loop_cnt, top_art);
565 } while (i != current_art && loop_cnt++ <= top_art);
566
567 if (tinrc.wildcard) {
568 regex_cache_destroy(&search_regex);
569 }
570 info_message(_(txt_no_match));
571 return -1;
572 }
573
574
575 /*
576 * Generic entry point to search for fields in arts[]
577 * Returns index into arts[] of matching article or -1
578 */
579 int
580 search(
581 t_function func,
582 int current_art,
583 t_bool repeat)
584 {
585 char *buf;
586 int (*search_func) (int i, char *searchbuff) = author_search;
587 t_bool forward;
588
589 if (func == GLOBAL_SEARCH_SUBJECT_FORWARD || func == GLOBAL_SEARCH_AUTHOR_FORWARD)
590 forward = TRUE;
591 else
592 forward = FALSE;
593
594 switch (func) {
595 case GLOBAL_SEARCH_SUBJECT_FORWARD:
596 case GLOBAL_SEARCH_SUBJECT_BACKWARD:
597 if (!(buf = get_search_pattern(&forward, repeat, _(txt_search_forwards), _(txt_search_backwards), tinrc.default_search_subject, HIST_SUBJECT_SEARCH)))
598 return -1;
599 search_func = subject_search;
600 break;
601
602 case GLOBAL_SEARCH_AUTHOR_FORWARD:
603 case GLOBAL_SEARCH_AUTHOR_BACKWARD:
604 default:
605 if (!(buf = get_search_pattern(&forward, repeat, _(txt_author_search_forwards), _(txt_author_search_backwards), tinrc.default_search_author, HIST_AUTHOR_SEARCH)))
606 return -1;
607 search_func = author_search;
608 break;
609 }
610 return (search_group(forward, current_art, buf, search_func));
611 }
612
613
614 /*
615 * page.c (search current article body)
616 * Return line number that matches or -1
617 * If using regex's return vector of character offsets
618 */
619 int
620 search_article(
621 t_bool forward,
622 t_bool repeat,
623 int start_line,
624 int lines,
625 t_lineinfo *line,
626 int reveal_ctrl_l_lines,
627 FILE *fp)
628 {
629 char *pattern, *ptr, *tmp;
630 int i = start_line;
631 REGEX_SIZE tmp_srch_offsets[2] = {0, 0};
632 t_bool wrap = FALSE;
633 t_bool match = FALSE;
634
635 if (!(pattern = get_search_pattern(&forward, repeat, _(txt_search_forwards), _(txt_search_backwards), tinrc.default_search_art, HIST_ART_SEARCH)))
636 return 0;
637
638 if (tinrc.wildcard && !(compile_regex(pattern, &search_regex, REGEX_CASELESS)))
639 return -1;
640
641 srch_lineno = -1;
642
643 forever {
644 if (i == start_line && wrap)
645 break;
646
647 /*
648 * TODO: consider not searching some line types?
649 * 'B'ody search skips hdrs, '/' inside article does not.
650 */
651 if (fseek(fp, line[i].offset, SEEK_SET) != 0)
652 return -1;
653
654 /* Don't search beyond ^L if hiding is enabled */
655 if ((line[i].flags & C_CTRLL) && i > reveal_ctrl_l_lines)
656 break;
657
658 if ((tmp = tin_fgets(fp, FALSE)) == NULL)
659 return -1;
660
661 if (!forward && last_article_search_matched) {
662 tmp[srch_offsets[0]] = '\0'; /* ignore anything on this line after the beginning of the last match */
663 srch_offsets[1] = 0; /* start backwards search always at the beginning of the line */
664 }
665 last_article_search_matched = FALSE;
666
667 #ifdef HAVE_UNICODE_NORMALIZATION
668 if (IS_LOCAL_CHARSET("UTF-8"))
669 ptr = normalize(tmp);
670 else
671 #endif /* HAVE_UNICODE_NORMALIZATION */
672 ptr = my_strdup(tmp);
673
674 if (tinrc.wildcard) {
675 while (match_regex_ex(ptr, (int) strlen(ptr), srch_offsets[1], REGEX_NOTEMPTY, &search_regex) >= 0) {
676 copy_offsets(srch_offsets, srch_offsets_size, &search_regex);
677 match = TRUE;
678 if (forward)
679 break;
680 else {
681 tmp_srch_offsets[0] = srch_offsets[0];
682 tmp_srch_offsets[1] = srch_offsets[1];
683 }
684 }
685 if (match) {
686 if (!forward) {
687 srch_offsets[0] = tmp_srch_offsets[0];
688 srch_offsets[1] = tmp_srch_offsets[1];
689 }
690 srch_lineno = i;
691 regex_cache_destroy(&search_regex);
692 free(ptr);
693 last_article_search_matched = TRUE;
694 return i;
695 }
696 } else {
697 if (wildmatpos(ptr, pattern, TRUE, srch_offsets, srch_offsets_size)) {
698 srch_lineno = i;
699 free(ptr);
700 last_article_search_matched = TRUE;
701 return i;
702 }
703 }
704 free(ptr);
705
706 if (forward) {
707 if (i >= lines - 1) {
708 i = 0;
709 wrap = TRUE;
710 } else
711 i++;
712 } else {
713 if (i <= 0) {
714 i = lines - 1;
715 wrap = TRUE;
716 } else
717 i--;
718 }
719
720 /* search at the beginning of the line */
721 srch_offsets[1] = 0;
722 }
723
724 info_message(_(txt_no_match));
725 if (tinrc.wildcard) {
726 regex_cache_destroy(&search_regex);
727 }
728 return -1;
729 }
730
731
732 /*
733 * Search the bodies of all the articles in current group
734 * Start the search at the current article
735 * A match will replace the context of the article open in the pager
736 * Save the line # that matched (and the start/end vector for regex)
737 * for later retrieval
738 * Return index in arts[] of article that matched or -1
739 */
740 int
741 search_body(
742 struct t_group *group,
743 int current_art,
744 t_bool repeat)
745 {
746 char *buf;
747 int i;
748 t_bool forward_fake = TRUE;
749
750 if (!(buf = get_search_pattern(
751 &forward_fake, /* we pass a dummy var since body search has no `forward' */
752 repeat,
753 _(txt_search_body),
754 _(txt_search_body),
755 tinrc.default_search_art,
756 HIST_ART_SEARCH
757 ))) return -1;
758
759 last_search = GLOBAL_SEARCH_BODY; /* store last search type for repeated search */
760 total_cnt = curr_cnt = 0; /* Reset global counter of articles done */
761
762 /*
763 * Count up the articles to be processed for the progress meter
764 */
765 if (group->attribute->show_only_unread_arts) {
766 for (i = 0; i < grpmenu.max; i++)
767 total_cnt += new_responses(i);
768 } else {
769 for_each_art(i) {
770 if (!IGNORE_ART(i))
771 total_cnt++;
772 }
773 }
774
775 srch_lineno = -1;
776 return search_group(1, current_art, buf, body_search);
777 }
778
779
780 /*
781 * Return the saved line & start/end info from previous successful
782 * regex search
783 */
784 int
785 get_search_vectors(
786 int *start,
787 int *end)
788 {
789 int i = srch_lineno;
790
791 *start = srch_offsets[0];
792 *end = srch_offsets[1];
793 srch_lineno = -1; /* We can only retrieve this info once */
794 return i;
795 }
796
797
798 /*
799 * Reset offsets so that the next search starts at the beginning of the line.
800 * This function is needed to access srch_offsets from within other modules.
801 */
802 void
803 reset_srch_offsets(
804 void)
805 {
806 srch_offsets[0] = srch_offsets[1] = 0;
807 /*
808 * bwd article search starts at the first line. This kludge avoids bwd
809 * search to match in the first line first.
810 */
811 last_article_search_matched = FALSE;
812 }