"Fossies" - the Fresh Open Source Software Archive

Member "tin-2.6.2/src/tags.c" (9 Dec 2022, 9693 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 "tags.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    : tags.c
    4  *  Author    : Jason Faultless <jason@altarstone.com>
    5  *  Created   : 1999-12-06
    6  *  Updated   : 2020-08-04
    7  *  Notes     : Split out from other modules
    8  *
    9  * Copyright (c) 1999-2023 Jason Faultless <jason@altarstone.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 #ifndef TIN_H
   41 #   include "tin.h"
   42 #endif /* !TIN_H */
   43 
   44 /* Local prototypes */
   45 static t_bool parse_range(char *range, int min, int max, int curr, int *range_start, int *range_end);
   46 
   47 int num_of_tagged_arts = 0;
   48 
   49 /*
   50  * Tags all parts of a multipart index if base_index points
   51  * to a multipart message and all its parts can be found.
   52  *
   53  * @param base_index points to one message in a multipart message.
   54  * @return number of messages tagged, or zero on failure
   55  */
   56 int
   57 tag_multipart(
   58     int arts_index)
   59 {
   60     MultiPartInfo *info = NULL;
   61     int i;
   62     int qty;
   63     t_bool untagging = FALSE;
   64 
   65     for_each_art(i) {
   66         if (!global_look_for_multipart(i, '[', ']'))
   67             global_look_for_multipart(i, '(', ')');
   68     }
   69 
   70     qty = global_get_multiparts(arts_index, &info, TRUE);
   71 
   72     /* check for failure... */
   73     if (qty == 0) {
   74         info_message(_(txt_info_not_multipart_message));
   75         return 0;
   76     }
   77     if (qty < 0) {
   78         info_message(_(txt_info_missing_part), -qty);
   79         return 0;
   80     }
   81 
   82     /*
   83      * if any are already tagged, untag 'em
   84      */
   85     for (i = 0; i < qty; ++i) {
   86         if (arts[info[i].arts_index].tagged != 0) {
   87             untagging = TRUE;
   88             while (i < qty)
   89                 untag_article(info[i++].arts_index);
   90         }
   91     }
   92 
   93     /*
   94      * get_multiparts() sorts info by part number,
   95      * so a simple for loop tags in the right order
   96      *
   97      * only tag if we are not untagging
   98      */
   99     if (!untagging) {
  100         for (i = 0; i < qty; ++i)
  101             arts[info[i].arts_index].tagged = ++num_of_tagged_arts;
  102     }
  103 
  104     free(info);
  105 
  106     return qty;
  107 }
  108 
  109 
  110 /*
  111  * Return the highest tag number of any article in thread
  112  * rooted at base[n]
  113  */
  114 int
  115 line_is_tagged(
  116     int n)
  117 {
  118     int code = 0;
  119 
  120     if (curr_group->attribute->thread_articles) {
  121         int i;
  122         for (i = n; i >= 0; i = arts[i].thread) {
  123             if (arts[i].tagged > code)
  124                 code = arts[i].tagged;
  125         }
  126     } else
  127         code = arts[n].tagged;
  128 
  129     return code;
  130 }
  131 
  132 
  133 /*
  134  * Toggle tag status of an article. Returns TRUE if we tagged the article
  135  * FALSE if we untagged it.
  136  */
  137 t_bool
  138 tag_article(
  139     int art)
  140 {
  141     if (arts[art].tagged != 0) {
  142         untag_article(art);
  143         info_message(_(txt_prefix_untagged), txt_article_singular);
  144         return FALSE;
  145     } else {
  146         arts[art].tagged = ++num_of_tagged_arts;
  147         info_message(_(txt_prefix_tagged), txt_article_singular);
  148         return TRUE;
  149     }
  150 }
  151 
  152 
  153 /*
  154  * Remove the tag from an article
  155  * Work through all the threads and decrement the tag counter on all arts
  156  * greater than 'tag', fixup counters
  157  */
  158 void
  159 untag_article(
  160     long art)
  161 {
  162     int i, j;
  163 
  164     for (i = 0; i < grpmenu.max; ++i) {
  165         for_each_art_in_thread(j, i) {
  166             if (arts[j].tagged > arts[art].tagged)
  167                 --arts[j].tagged;
  168         }
  169     }
  170     arts[art].tagged = 0;
  171     --num_of_tagged_arts;
  172 }
  173 
  174 
  175 /*
  176  * Clear tag status of all articles. If articles were untagged, return TRUE
  177  */
  178 t_bool
  179 untag_all_articles(
  180     void)
  181 {
  182     int i;
  183     t_bool untagged = FALSE;
  184 
  185     for_each_art(i) {
  186         if (arts[i].tagged != 0) {
  187             arts[i].tagged = 0;
  188             untagged = TRUE;
  189         }
  190     }
  191     num_of_tagged_arts = 0;
  192 
  193     return untagged;
  194 }
  195 
  196 
  197 /*
  198  * RANGE CODE
  199  */
  200 /*
  201  * Allows user to specify an group/article range that a followup
  202  * command will operate on (eg. catchup articles 1-56) # 1-56 K
  203  * min/max/curr are the lowest/highest and current positions on the
  204  * menu from which this was called; used as defaults if needed
  205  * Return TRUE if a range was successfully read, parsed and set
  206  *
  207  * Allowed syntax is 0123456789-.$ (blanks are ignored):
  208  *   1-23    mark grp/art 1 through 23
  209  *   1-.     mark grp/art 1 through current
  210  *   1-$     mark grp/art 1 through last
  211  *   .-$     mark grp/art current through last
  212  */
  213 t_bool
  214 set_range(
  215     int level,
  216     int min,
  217     int max,
  218     int curr)
  219 {
  220     char *range;
  221     char *prompt;
  222     int artnum;
  223     int i;
  224     int range_min;
  225     int range_max;
  226 
  227     switch (level) {
  228         case SELECT_LEVEL:
  229             range = tinrc.default_range_select;
  230             break;
  231 
  232         case GROUP_LEVEL:
  233             range = tinrc.default_range_group;
  234             break;
  235 
  236         case THREAD_LEVEL:
  237             range = tinrc.default_range_thread;
  238             break;
  239 
  240         default:    /* should no happen */
  241             return FALSE;
  242     }
  243 
  244 #if 0
  245     error_message(2, "Min=[%d] Max=[%d] Cur=[%d] DefRng=[%s]", min, max, curr, range);
  246 #endif /* 0 */
  247     prompt = fmt_string(_(txt_enter_range), range);
  248 
  249     if (!(prompt_string_default(prompt, range, _(txt_range_invalid), HIST_OTHER))) {
  250         free(prompt);
  251         return FALSE;
  252     }
  253     free(prompt);
  254 
  255     /*
  256      * Parse range string
  257      */
  258     if (!parse_range(range, min, max, curr, &range_min, &range_max)) {
  259         info_message(_(txt_range_invalid));
  260         return FALSE;
  261     }
  262 
  263     switch (level) {
  264         case SELECT_LEVEL:
  265             for (i = 0; i < max; i++)           /* Clear existing range */
  266                 active[my_group[i]].inrange = FALSE;
  267 
  268             for (i = range_min - 1; i < range_max; i++)
  269                 active[my_group[i]].inrange = TRUE;
  270             break;
  271 
  272         case GROUP_LEVEL:
  273             for (i = 0; i < max; i++) {         /* Clear existing range */
  274                 for_each_art_in_thread(artnum, i)
  275                     arts[artnum].inrange = FALSE;
  276             }
  277 
  278             for (i = range_min - 1; i < range_max; i++) {
  279                 for_each_art_in_thread(artnum, i)
  280                     arts[artnum].inrange = TRUE;
  281             }
  282             break;
  283 
  284         case THREAD_LEVEL:
  285             /*
  286              * Debatably should clear all of arts[] depending on how you
  287              * interpret the (non)spec
  288              */
  289             for (i = 0; i < grpmenu.max; i++) {         /* Clear existing range */
  290                 for_each_art_in_thread(artnum, i)
  291                     arts[artnum].inrange = FALSE;
  292             }
  293 
  294             i = 1;
  295             for_each_art_in_thread(artnum, thread_basenote) {
  296                 if (i > range_max)
  297                     break;
  298                 if (i >= range_min)
  299                     arts[artnum].inrange = TRUE;
  300                 i++;
  301             }
  302             break;
  303 
  304         default:
  305             return FALSE;
  306             /* NOTREACHED */
  307             break;
  308     }
  309     return TRUE;
  310 }
  311 
  312 
  313 /*
  314  * Parse 'range', return the range start and end values in range_start and range_end
  315  * min/max/curr are used to select defaults when n explicit start/end are given
  316  */
  317 static t_bool
  318 parse_range(
  319     char *range,
  320     int min,
  321     int max,
  322     int curr,
  323     int *range_start,
  324     int *range_end)
  325 {
  326     char *ptr = range;
  327     enum states { FINDMIN, FINDMAX, DONE };
  328     int state = FINDMIN;
  329     t_bool ret = FALSE;
  330 
  331     *range_start = -1;
  332     *range_end = -1;
  333 
  334     while (*ptr && state != DONE) {
  335         if (isdigit((int) *ptr)) {
  336             if (state == FINDMAX) {
  337                 *range_end = atoi(ptr);
  338                 state = DONE;
  339             } else
  340                 *range_start = atoi(ptr);
  341             while (isdigit((int) *ptr))
  342                 ptr++;
  343         } else {
  344             switch (*ptr) {
  345                 case '-':
  346                     state = FINDMAX;
  347                     break;
  348 
  349                 case '.':
  350                     if (state == FINDMAX) {
  351                         *range_end = curr;
  352                         state = DONE;
  353                     } else
  354                         *range_start = curr;
  355                     break;
  356 
  357                 case '$':
  358                     if (state == FINDMAX) {
  359                         *range_end = max;
  360                         state = DONE;
  361                     }
  362                     break;
  363 
  364                 default:
  365                     break;
  366             }
  367             ptr++;
  368         }
  369     }
  370 
  371     if (*range_start >= min && *range_end >= *range_start && *range_end <= max)
  372         ret = TRUE;
  373 
  374     return ret;
  375 }
  376 
  377 
  378 /*
  379  * SELECTED CODE
  380  */
  381 void
  382 do_auto_select_arts(
  383     void)
  384 {
  385     int i;
  386 
  387     for_each_art(i) {
  388         if (arts[i].status == ART_UNREAD && !arts[i].selected) {
  389 #ifdef DEBUG
  390             if (debug & DEBUG_NEWSRC)
  391                 debug_print_comment("group.c: X command");
  392 #endif /* DEBUG */
  393             art_mark(curr_group, &arts[i], ART_READ);
  394             arts[i].zombie = TRUE;
  395         }
  396         if (curr_group->attribute->show_only_unread_arts)
  397             arts[i].keep_in_base = FALSE;
  398     }
  399     if (curr_group->attribute->show_only_unread_arts)
  400         find_base(curr_group);
  401 
  402     grpmenu.curr = 0;
  403     show_group_page();
  404 }
  405 
  406 
  407 /* selection already happened in filter_articles() */
  408 void
  409 undo_auto_select_arts(
  410     void)
  411 {
  412     int i;
  413 
  414     for_each_art(i) {
  415         if (arts[i].status == ART_READ && arts[i].zombie) {
  416 #ifdef DEBUG
  417             if (debug & DEBUG_NEWSRC)
  418                 debug_print_comment("group.c: + command");
  419 #endif /* DEBUG */
  420             art_mark(curr_group, &arts[i], ART_UNREAD);
  421             arts[i].zombie = FALSE;
  422         }
  423     }
  424     if (curr_group->attribute->show_only_unread_arts)
  425         find_base(curr_group);
  426 
  427     grpmenu.curr = 0;   /* do we want this? */
  428     show_group_page();
  429 }
  430 
  431 
  432 void
  433 undo_selections(
  434     void)
  435 {
  436     int i;
  437 
  438     for_each_art(i) {
  439         arts[i].selected = FALSE;
  440         arts[i].zombie = FALSE;
  441     }
  442 }
  443 
  444 
  445 /*
  446  * Return TRUE if there are any selected arts
  447  */
  448 t_bool
  449 arts_selected(
  450     void)
  451 {
  452     int i;
  453 
  454     for_each_art(i) {
  455         if (arts[i].selected)
  456             return TRUE;
  457     }
  458 
  459     return FALSE;
  460 }