"Fossies" - the Fresh Open Source Software Archive

Member "tin-2.4.1/src/search.c" (12 Oct 2016, 18276 Bytes) of package /linux/misc/tin-2.4.1.tar.gz:


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.0_vs_2.4.1.

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