"Fossies" - the Fresh Open Source Software Archive

Member "tin-2.4.4/src/search.c" (20 Nov 2019, 18412 Bytes) of package /linux/misc/tin-2.4.4.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.4.3_vs_2.4.4.

    1 /*
    2  *  Project   : tin - a Usenet reader
    3  *  Module    : search.c
    4  *  Author    : I. Lea & R. Skrenta
    5  *  Created   : 1991-04-01
    6  *  Updated   : 2018-01-29
    7  *  Notes     :
    8  *
    9  * Copyright (c) 1991-2020 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 int srch_offsets[6];
   67 static int srch_offsets_size = ARRAY_SIZE(srch_offsets);
   68 static struct regex_cache search_regex = { NULL, NULL };
   69 
   70 
   71 /*
   72  * Obtain the search pattern, save it in the default buffer.
   73  * Return NULL if no pattern could be found
   74  */
   75 static char *
   76 get_search_pattern(
   77     t_bool *forward,
   78     t_bool repeat,
   79     const char *fwd_msg,
   80     const char *bwd_msg,
   81     char *def,
   82     int which_hist)
   83 {
   84     static char tmpbuf[LEN];    /* Hold the last pattern used */
   85     static char last_pattern[LEN];  /* last search pattern used; for repeated search */
   86     static t_bool last_forward;
   87 
   88     if (repeat) {
   89         *forward = last_forward;
   90         my_strncpy(def, last_pattern, LEN);
   91     } else {
   92         snprintf(tmpbuf, sizeof(tmpbuf), (*forward ? fwd_msg : bwd_msg), def);
   93 
   94         if (!prompt_string_default(tmpbuf, def, _(txt_no_search_string), which_hist))
   95             return NULL;
   96 
   97         last_forward = *forward;
   98         my_strncpy(last_pattern, def, LEN);
   99 
  100         /* HIST_BODY_SEARCH doesn't exist, hence last_search is set directly in search_body() */
  101         if (which_hist == HIST_AUTHOR_SEARCH)
  102             last_search = *forward ? GLOBAL_SEARCH_AUTHOR_FORWARD : GLOBAL_SEARCH_AUTHOR_BACKWARD;
  103         else
  104             last_search = *forward ? GLOBAL_SEARCH_SUBJECT_FORWARD : GLOBAL_SEARCH_SUBJECT_BACKWARD;
  105     }
  106 
  107     wait_message(0, _(txt_searching));
  108 
  109     stow_cursor();
  110 
  111 #ifdef HAVE_UNICODE_NORMALIZATION
  112     /* normalize search pattern */
  113     if (IS_LOCAL_CHARSET("UTF-8")) {
  114         char *tmp;
  115 
  116         tmp = normalize(def);
  117         my_strncpy(def, tmp, LEN);
  118         free(tmp);
  119     }
  120 #endif /* HAVE_UNICODE_NORMALIZATION */
  121 
  122     if (tinrc.wildcard) {           /* ie, not wildmat() */
  123         strcpy(def, quote_wild_whitespace(def));
  124         return def;
  125     }
  126 
  127     /*
  128      * A gross hack to simulate substrings with wildmat()
  129      */
  130 /* TODO: somehow use REGEX_FMT here? */
  131     snprintf(tmpbuf, sizeof(tmpbuf), "*%s*", def);
  132     return tmpbuf;
  133 }
  134 
  135 
  136 /*
  137  * called by config.c
  138  */
  139 enum option_enum
  140 search_config(
  141     t_bool forward,
  142     t_bool repeat,
  143     enum option_enum current,
  144     enum option_enum last)
  145 {
  146     char *pattern, *buf;
  147     enum option_enum n = current;
  148     enum option_enum result = current;
  149 
  150     if (!(pattern = get_search_pattern(&forward, repeat, _(txt_search_forwards), _(txt_search_backwards), tinrc.default_search_config, HIST_CONFIG_SEARCH)))
  151         return result;
  152 
  153     if (tinrc.wildcard && !(compile_regex(pattern, &search_regex, PCRE_CASELESS)))
  154         return result;
  155 
  156     do {
  157         if (n == 0 && !forward)
  158             n = last;
  159         else {
  160             if (n == last && forward)
  161                 n = 0;
  162             else
  163                 n += (forward ? 1 : -1);
  164         }
  165         /* search only visible options */
  166         if (option_is_visible(n)) {
  167 #ifdef HAVE_UNICODE_NORMALIZATION
  168             if (IS_LOCAL_CHARSET("UTF-8"))
  169                 buf = normalize(_(option_table[n].txt->opt));
  170             else
  171 #endif /* HAVE_UNICODE_NORMALIZATION */
  172                 buf = my_strdup(_(option_table[n].txt->opt));
  173 
  174             if (match_regex(buf, pattern, &search_regex, TRUE)) {
  175                 result = n;
  176                 free(buf);
  177                 break;
  178             }
  179             free(buf);
  180         }
  181     } while (n != current);
  182 
  183     clear_message();
  184     if (tinrc.wildcard) {
  185         FreeAndNull(search_regex.re);
  186         FreeAndNull(search_regex.extra);
  187     }
  188     return result;
  189 }
  190 
  191 
  192 /*
  193  * called by save.c (search for attachment) and page.c (search for URL)
  194  */
  195 int
  196 generic_search(
  197     t_bool forward,
  198     t_bool repeat,
  199     int current,
  200     int last,
  201     int level)
  202 {
  203     char *pattern;
  204     char buf[BUFSIZ];
  205     const char *name, *charset;
  206     int n = current;
  207     int result = current;
  208     t_bool found = FALSE;
  209     t_part *part;
  210     t_url *urlptr;
  211 
  212     if (!(pattern = get_search_pattern(&forward, repeat, _(txt_search_forwards), _(txt_search_backwards), tinrc.default_search_config, HIST_CONFIG_SEARCH)))
  213         return result;
  214 
  215     if (tinrc.wildcard && !(compile_regex(pattern, &search_regex, PCRE_CASELESS)))
  216         return result;
  217 
  218     do {
  219         if (n == 0 && !forward)
  220             n = last;
  221         else {
  222             if (n == last && forward)
  223                 n = 0;
  224             else
  225                 n += (forward ? 1 : -1);
  226         }
  227         switch (level) {
  228             case ATTACHMENT_LEVEL:
  229                 part = get_part(n);
  230                 if (!(name = get_filename(part->params))) {
  231                     if (!(name = part->description))
  232                         name = _(txt_attachment_no_name);
  233                 }
  234                 charset = get_param(part->params, "charset");
  235                 snprintf(buf, sizeof(buf), "%s %s/%s %s, %s", name, content_types[part->type], part->subtype, content_encodings[part->encoding], charset ? charset : "");
  236                 break;
  237 
  238             case URL_LEVEL:
  239                 urlptr = find_url(n);
  240                 snprintf(buf, sizeof(buf), "%s", urlptr->url);
  241                 break;
  242 
  243             default:
  244                 buf[0] = '\0';
  245                 break;
  246         }
  247         if (match_regex(buf, pattern, &search_regex, TRUE)) {
  248             result = n;
  249             found = TRUE;
  250             break;
  251         }
  252     } while (n != current);
  253 
  254     clear_message();
  255     if (tinrc.wildcard) {
  256         FreeAndNull(search_regex.re);
  257         FreeAndNull(search_regex.extra);
  258     }
  259     if (!found)
  260         info_message(_(txt_no_match));
  261 
  262     return result;
  263 }
  264 
  265 
  266 /*
  267  * Search active[] looking for a groupname
  268  * Called by select.c
  269  * Return index into active of matching groupname or -1
  270  */
  271 int
  272 search_active(
  273     t_bool forward,
  274     t_bool repeat)
  275 {
  276     char *buf;
  277     char *ptr;
  278     char buf2[LEN];
  279     int i;
  280 
  281     if (!selmenu.max) {
  282         info_message(_(txt_no_groups));
  283         return -1;
  284     }
  285 
  286     if (!(buf = get_search_pattern(&forward, repeat, _(txt_search_forwards), _(txt_search_backwards), tinrc.default_search_group, HIST_GROUP_SEARCH)))
  287         return -1;
  288 
  289     if (tinrc.wildcard && !(compile_regex(buf, &search_regex, PCRE_CASELESS)))
  290         return -1;
  291 
  292     i = selmenu.curr;
  293 
  294     do {
  295         if (forward) {
  296             if (++i >= selmenu.max)
  297                 i = 0;
  298         } else {
  299             if (--i < 0)
  300                 i = selmenu.max - 1;
  301         }
  302 
  303         /*
  304          * Get the group name & description into buf2
  305          */
  306         if (show_description && active[my_group[i]].description) {
  307             snprintf(buf2, sizeof(buf2), "%s %s", active[my_group[i]].name, active[my_group[i]].description);
  308             ptr = buf2;
  309         } else
  310             ptr = active[my_group[i]].name;
  311 
  312         if (match_regex(ptr, buf, &search_regex, TRUE)) {
  313             if (tinrc.wildcard) {
  314                 FreeAndNull(search_regex.re);
  315                 FreeAndNull(search_regex.extra);
  316             }
  317             return i;
  318         }
  319     } while (i != selmenu.curr);
  320 
  321     if (tinrc.wildcard) {
  322         FreeAndNull(search_regex.re);
  323         FreeAndNull(search_regex.extra);
  324     }
  325     info_message(_(txt_no_match));
  326     return -1;
  327 }
  328 
  329 
  330 /*
  331  * Scan the body of an arts[i] for searchbuf
  332  * used only by search_body()
  333  * Returns: 1   String found
  334  *          0   Not found
  335  *          -1  User aborted search
  336  */
  337 static int
  338 body_search(
  339     int i,
  340     char *searchbuf)
  341 {
  342     static char msg[LEN];   /* show_progress needs a constant message buffer */
  343     char *line, *tmp;
  344     t_openartinfo artinfo;
  345 
  346     switch (art_open(TRUE, &arts[i], curr_group, &artinfo, FALSE, NULL)) {
  347         case ART_ABORT:                 /* User 'q'uit */
  348             art_close(&artinfo);
  349             return -1;
  350 
  351         case ART_UNAVAILABLE:           /* Treat as string not present */
  352             art_close(&artinfo);
  353             info_message(_(txt_no_match));
  354             return 0;
  355     }
  356 
  357     /*
  358      * Skip the header - is this right ?
  359      */
  360     for (i = 0; artinfo.cookl[i].flags & C_HEADER; ++i)
  361         ;
  362     if (fseek(artinfo.cooked, artinfo.cookl[i].offset, SEEK_SET) != 0) {
  363         art_close(&artinfo);
  364         return -1;
  365     }
  366 
  367     /*
  368      * Now search the body
  369      */
  370     snprintf(msg, sizeof(msg), _(txt_searching_body), ++curr_cnt, total_cnt);
  371     show_progress(msg, curr_cnt, total_cnt);
  372     while ((tmp = tin_fgets(artinfo.cooked, FALSE)) != NULL) {
  373 #ifdef HAVE_UNICODE_NORMALIZATION
  374         if (IS_LOCAL_CHARSET("UTF-8"))
  375             line = normalize(tmp);
  376         else
  377 #endif /* HAVE_UNICODE_NORMALIZATION */
  378             line = my_strdup(tmp);
  379 
  380         if (tinrc.wildcard) {
  381             if (pcre_exec(search_regex.re, search_regex.extra, line, strlen(line), 0, 0, srch_offsets, srch_offsets_size) != PCRE_ERROR_NOMATCH) {
  382                 srch_lineno = i;
  383                 art_close(&pgart);      /* Switch the pager over to matched art */
  384                 pgart = artinfo;
  385 #ifdef DEBUG
  386                 if (debug & DEBUG_MISC)
  387                     fprintf(stderr, "art_switch(%p = %p)\n", (void *) &pgart, (void *) &artinfo);
  388 #endif /* DEBUG */
  389                 free(line);
  390 
  391                 return 1;
  392             }
  393         } else {
  394             if (wildmatpos(line, searchbuf, TRUE, srch_offsets, srch_offsets_size)) {
  395                 srch_lineno = i;
  396                 art_close(&pgart);      /* Switch the pager over to matched art */
  397                 pgart = artinfo;
  398                 free(line);
  399                 return 1;
  400             }
  401         }
  402         i++;
  403         free(line);
  404     }
  405 
  406     if (tin_errno != 0) {           /* User abort */
  407         art_close(&artinfo);
  408         return -1;
  409     }
  410 
  411     art_close(&artinfo);
  412 /*  info_message(_(txt_no_match)); */
  413     return 0;
  414 }
  415 
  416 
  417 /*
  418  * Match searchbuff against the From: information in arts[i]
  419  * 1 = found, 0 = not found
  420  */
  421 static int
  422 author_search(
  423     int i,
  424     char *searchbuf)
  425 {
  426     char *buf, *tmp;
  427 
  428     if (arts[i].name == NULL)
  429         tmp = my_strdup(arts[i].from);
  430     else {
  431         size_t len = strlen(arts[i].from) + strlen(arts[i].name) + 4;
  432 
  433         tmp = my_malloc(len);
  434         snprintf(tmp, len, "%s <%s>", arts[i].name, arts[i].from);
  435     }
  436 
  437 #ifdef HAVE_UNICODE_NORMALIZATION
  438     if (IS_LOCAL_CHARSET("UTF-8")) {
  439         buf = normalize(tmp);
  440         free(tmp);
  441     } else
  442 #endif /* HAVE_UNICODE_NORMALIZATION */
  443         buf = tmp;
  444 
  445     if (match_regex(buf, searchbuf, &search_regex, TRUE)) {
  446         free(buf);
  447         return 1;
  448     }
  449 
  450     free(buf);
  451     return 0;
  452 }
  453 
  454 
  455 /*
  456  * Match searchbuff against the Subject: information in arts[i]
  457  * 1 = found, 0 = not found
  458  */
  459 static int
  460 subject_search(
  461     int i,
  462     char *searchbuf)
  463 {
  464     char *buf;
  465 
  466 #ifdef HAVE_UNICODE_NORMALIZATION
  467     if (IS_LOCAL_CHARSET("UTF-8"))
  468         buf = normalize(arts[i].subject);
  469     else
  470 #endif /* HAVE_UNICODE_NORMALIZATION */
  471         buf = my_strdup(arts[i].subject);
  472 
  473     if (match_regex(buf, searchbuf, &search_regex, TRUE)) {
  474         free(buf);
  475         return 1;
  476     }
  477 
  478     free(buf);
  479     return 0;
  480 }
  481 
  482 
  483 /*
  484  * Returns index into arts[] of matching article or -1
  485  */
  486 static int
  487 search_group(
  488     t_bool forward,
  489     int current_art,
  490     char *searchbuff,
  491     int (*search_func) (int i, char *searchbuff))
  492 {
  493     int i, ret;
  494 
  495     if (grpmenu.curr < 0) {
  496         info_message(_(txt_no_arts));
  497         return -1;
  498     }
  499 
  500     /*
  501      * precompile if we're using full regex
  502      */
  503     if (tinrc.wildcard && !(compile_regex(searchbuff, &search_regex, PCRE_CASELESS)))
  504         return -1;
  505 
  506     i = current_art;
  507 
  508     do {
  509         if (forward) {
  510             if ((i = next_response(i)) < 0)
  511                 i = base[0];
  512         } else {
  513             if ((i = prev_response(i)) < 0)
  514                 i = find_response(grpmenu.max - 1, num_of_responses(grpmenu.max - 1));
  515         }
  516 
  517         /* Only search displayed articles */
  518         if (curr_group->attribute->show_only_unread_arts && arts[i].status != ART_UNREAD)
  519             continue;
  520 
  521         ret = search_func(i, searchbuff);
  522         if (tinrc.wildcard && (ret == 1 || ret == -1)) {
  523             /* we will exit soon, clean up */
  524             FreeAndNull(search_regex.re);
  525             FreeAndNull(search_regex.extra);
  526         }
  527         switch (ret) {
  528             case 1:                             /* Found */
  529                 clear_message();
  530                 return i;
  531 
  532             case -1:                            /* User abort */
  533                 return -1;
  534         }
  535     } while (i != current_art);
  536 
  537     if (tinrc.wildcard) {
  538         FreeAndNull(search_regex.re);
  539         FreeAndNull(search_regex.extra);
  540     }
  541     info_message(_(txt_no_match));
  542     return -1;
  543 }
  544 
  545 
  546 /*
  547  * Generic entry point to search for fields in arts[]
  548  * Returns index into arts[] of matching article or -1
  549  */
  550 int
  551 search(
  552     t_function func,
  553     int current_art,
  554     t_bool repeat)
  555 {
  556     char *buf = NULL;
  557     int (*search_func) (int i, char *searchbuff) = author_search;
  558     t_bool forward;
  559 
  560     if (func == GLOBAL_SEARCH_SUBJECT_FORWARD || func == GLOBAL_SEARCH_AUTHOR_FORWARD)
  561         forward = TRUE;
  562     else
  563         forward = FALSE;
  564 
  565     switch (func) {
  566         case GLOBAL_SEARCH_SUBJECT_FORWARD:
  567         case GLOBAL_SEARCH_SUBJECT_BACKWARD:
  568             if (!(buf = get_search_pattern(&forward, repeat, _(txt_search_forwards), _(txt_search_backwards), tinrc.default_search_subject, HIST_SUBJECT_SEARCH)))
  569                 return -1;
  570             search_func = subject_search;
  571             break;
  572 
  573         case GLOBAL_SEARCH_AUTHOR_FORWARD:
  574         case GLOBAL_SEARCH_AUTHOR_BACKWARD:
  575         default:
  576             if (!(buf = get_search_pattern(&forward, repeat, _(txt_author_search_forwards), _(txt_author_search_backwards), tinrc.default_search_author, HIST_AUTHOR_SEARCH)))
  577                 return -1;
  578             search_func = author_search;
  579             break;
  580     }
  581     return (search_group(forward, current_art, buf, search_func));
  582 }
  583 
  584 
  585 /*
  586  * page.c (search current article body)
  587  * Return line number that matches or -1
  588  * If using regex's return vector of character offsets
  589  */
  590 int
  591 search_article(
  592     t_bool forward,
  593     t_bool repeat,
  594     int start_line,
  595     int lines,
  596     t_lineinfo *line,
  597     int reveal_ctrl_l_lines,
  598     FILE *fp)
  599 {
  600     char *pattern, *ptr, *tmp;
  601     int i = start_line;
  602     int tmp_srch_offsets[2] = {0, 0};
  603     t_bool wrap = FALSE;
  604     t_bool match = FALSE;
  605 
  606     if (!(pattern = get_search_pattern(&forward, repeat, _(txt_search_forwards), _(txt_search_backwards), tinrc.default_search_art, HIST_ART_SEARCH)))
  607         return 0;
  608 
  609     if (tinrc.wildcard && !(compile_regex(pattern, &search_regex, PCRE_CASELESS)))
  610         return -1;
  611 
  612     srch_lineno = -1;
  613 
  614     forever {
  615         if (i == start_line && wrap)
  616             break;
  617 
  618         /*
  619          * TODO: consider not searching some line types?
  620          * 'B'ody search skips hdrs, '/' inside article does not.
  621          */
  622         if (fseek(fp, line[i].offset, SEEK_SET) != 0)
  623             return -1;
  624 
  625         /* Don't search beyond ^L if hiding is enabled */
  626         if ((line[i].flags & C_CTRLL) && i > reveal_ctrl_l_lines)
  627             break;
  628 
  629         if ((tmp = tin_fgets(fp, FALSE)) == NULL)
  630             return -1;
  631         if (!forward && srch_offsets[0] >= 0) {
  632             tmp[srch_offsets[0]] = '\0';    /* ignore anything on this line after the last match */
  633             srch_offsets[1] = 0;    /* start backwards search at the beginning of the line */
  634         }
  635 
  636 #ifdef HAVE_UNICODE_NORMALIZATION
  637         if (IS_LOCAL_CHARSET("UTF-8"))
  638             ptr = normalize(tmp);
  639         else
  640 #endif /* HAVE_UNICODE_NORMALIZATION */
  641             ptr = my_strdup(tmp);
  642 
  643         if (tinrc.wildcard) {
  644             while (pcre_exec(search_regex.re, search_regex.extra, ptr, strlen(ptr), srch_offsets[1], 0, srch_offsets, srch_offsets_size) != PCRE_ERROR_NOMATCH) {
  645                 match = TRUE;
  646                 if (forward)
  647                     break;
  648                 else {
  649                     tmp_srch_offsets[0] = srch_offsets[0];
  650                     tmp_srch_offsets[1] = srch_offsets[1];
  651                 }
  652             }
  653             if (match) {
  654                 if (!forward) {
  655                     srch_offsets[0] = tmp_srch_offsets[0];
  656                     srch_offsets[1] = tmp_srch_offsets[1];
  657                 }
  658                 srch_lineno = i;
  659                 FreeAndNull(search_regex.re);
  660                 FreeAndNull(search_regex.extra);
  661                 free(ptr);
  662                 return i;
  663             }
  664         } else {
  665             if (wildmatpos(ptr, pattern, TRUE, srch_offsets, srch_offsets_size)) {
  666                 srch_lineno = i;
  667                 free(ptr);
  668                 return i;
  669             }
  670         }
  671         free(ptr);
  672 
  673         if (forward) {
  674             if (i >= lines - 1) {
  675                 i = 0;
  676                 wrap = TRUE;
  677             } else
  678                 i++;
  679         } else {
  680             if (i <= 0) {
  681                 i = lines - 1;
  682                 wrap = TRUE;
  683             } else
  684                 i--;
  685         }
  686 
  687         /* search at the beginning of the line */
  688         srch_offsets[1] = 0;
  689     }
  690 
  691     info_message(_(txt_no_match));
  692     if (tinrc.wildcard) {
  693         FreeAndNull(search_regex.re);
  694         FreeAndNull(search_regex.extra);
  695     }
  696     return -1;
  697 }
  698 
  699 
  700 /*
  701  * Search the bodies of all the articles in current group
  702  * Start the search at the current article
  703  * A match will replace the context of the article open in the pager
  704  * Save the line # that matched (and the start/end vector for regex)
  705  * for later retrieval
  706  * Return index in arts[] of article that matched or -1
  707  */
  708 int
  709 search_body(
  710     struct t_group *group,
  711     int current_art,
  712     t_bool repeat)
  713 {
  714     char *buf;
  715     int i;
  716     t_bool forward_fake = TRUE;
  717 
  718     if (!(buf = get_search_pattern(
  719             &forward_fake,              /* we pass a dummy var since body search has no `forward' */
  720             repeat,
  721             _(txt_search_body),
  722             _(txt_search_body),
  723             tinrc.default_search_art,
  724             HIST_ART_SEARCH
  725     ))) return -1;
  726 
  727     last_search = GLOBAL_SEARCH_BODY;   /* store last search type for repeated search */
  728     total_cnt = curr_cnt = 0;           /* Reset global counter of articles done */
  729 
  730     /*
  731      * Count up the articles to be processed for the progress meter
  732      */
  733     if (group->attribute->show_only_unread_arts) {
  734         for (i = 0; i < grpmenu.max; i++)
  735             total_cnt += new_responses(i);
  736     } else {
  737         for_each_art(i) {
  738             if (!IGNORE_ART(i))
  739                 total_cnt++;
  740         }
  741     }
  742 
  743     srch_lineno = -1;
  744     return search_group(1, current_art, buf, body_search);
  745 }
  746 
  747 
  748 /*
  749  * Return the saved line & start/end info from previous successful
  750  * regex search
  751  */
  752 int
  753 get_search_vectors(
  754     int *start,
  755     int *end)
  756 {
  757     int i = srch_lineno;
  758 
  759     *start = srch_offsets[0];
  760     *end = srch_offsets[1];
  761     srch_lineno = -1;           /* We can only retrieve this info once */
  762     return i;
  763 }
  764 
  765 
  766 /*
  767  * Reset offsets so that the next search starts at the beginning of the line.
  768  * This function is needed to access srch_offsets from within other modules.
  769  */
  770 void
  771 reset_srch_offsets(
  772     void)
  773 {
  774     srch_offsets[0] = srch_offsets[1] = 0;
  775 }