"Fossies" - the Fresh Open Source Software Archive

Member "tin-2.6.2/src/newsrc.c" (9 Dec 2022, 40501 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 "newsrc.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    : newsrc.c
    4  *  Author    : I. Lea & R. Skrenta
    5  *  Created   : 1991-04-01
    6  *  Updated   : 2022-02-19
    7  *  Notes     : ArtCount = (ArtMax - ArtMin) + 1  [could have holes]
    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 #ifndef TCURSES_H
   45 #   include "tcurses.h"
   46 #endif /* !TCURSES_H */
   47 #ifndef TNNTP_H
   48 #   include "tnntp.h"
   49 #endif /* !TNNTP_H */
   50 #ifndef NEWSRC_H
   51 #   include "newsrc.h"
   52 #endif /* !NEWSRC_H */
   53 
   54 static mode_t newsrc_mode = 0;
   55 
   56 /*
   57  * Local prototypes
   58  */
   59 static FILE *open_subscription_fp(void);
   60 static char *parse_newsrc_line(char *line, int *sub);
   61 static char *parse_subseq(struct t_group *group, char *seq, t_artnum *low, t_artnum *high, t_artnum *sum);
   62 static char *parse_get_seq(char *seq, t_artnum *low, t_artnum *high);
   63 static int write_newsrc_line(FILE *fp, char *line);
   64 static t_bool create_newsrc(char *newsrc_file);
   65 static void auto_subscribe_groups(char *newsrc_file);
   66 static void get_subscribe_info(struct t_group *grp);
   67 static void parse_bitmap_seq(struct t_group *group, char *seq);
   68 static void print_bitmap_seq(FILE *fp, struct t_group *group);
   69 
   70 
   71 /*
   72  * Read .newsrc into my_group[]. my_group[] ints point to active[] entries.
   73  * If allgroups is set, then my_group[] is completely overwritten,
   74  * otherwise, groups are appended. Any bogus groups will be handled
   75  * accordingly. Bogus groups will _not_ be subscribed to as a design
   76  * principle.
   77  *
   78  * Returns the number of lines read(useful for a check newsrc >= oldnewsrc)
   79  *  < 0 error
   80  *  >=0 number of lines read
   81  */
   82 signed long int
   83 read_newsrc(
   84     char *newsrc_file,
   85     t_bool allgroups)
   86 {
   87     FILE *fp;
   88     char *grp, *seq;
   89     int sub, i;
   90     signed long line_count = 0;
   91     struct stat statbuf;
   92 
   93     if (allgroups)
   94         selmenu.max = skip_newgroups();
   95 
   96     /*
   97      * make a .newsrc if none exist & auto subscribe to set groups
   98      */
   99     if (stat(newsrc_file, &statbuf) == -1) {
  100         if (!create_newsrc(newsrc_file))
  101             return -1L; /* ouch */
  102         auto_subscribe_groups(newsrc_file);
  103     } else
  104         newsrc_mode = statbuf.st_mode;
  105 
  106     if ((fp = fopen(newsrc_file, "r")) != NULL) {
  107         if (!batch_mode || verbose)
  108             wait_message(0, _(txt_reading_newsrc));
  109 
  110         while ((grp = tin_fgets(fp, FALSE)) != NULL) {
  111             strip_line(grp);
  112             if (*grp == '#' || *grp == '\0')    /* skip comments and empty lines */
  113                 continue;
  114 
  115             line_count++;   /* but count all other lines (incl. bogous ones) */
  116             seq = parse_newsrc_line(grp, &sub);
  117 
  118             if (sub == SUBSCRIBED) {
  119                 if ((i = my_group_add(grp, FALSE)) >= 0) {
  120                     if (!active[my_group[i]].bogus) {
  121                         active[my_group[i]].subscribed = SUB_BOOL(sub);
  122                         parse_bitmap_seq(&active[my_group[i]], seq);
  123                     }
  124                 } else
  125                     process_bogus(grp);
  126             }
  127         }
  128         fclose(fp);
  129         /* If you aborted with 'q', then you get what you get. */
  130 
  131         if (!batch_mode || verbose)
  132             my_fputc('\n', stdout);
  133 
  134         if (!cmd_line && !batch_mode)
  135             clear_message();
  136     }
  137     return line_count;
  138 }
  139 
  140 
  141 /*
  142  * Parse a line from the newsrc file and write it back out with updated
  143  * sequence information. Return number of lines written (ie, 0 or 1)
  144  */
  145 static int
  146 write_newsrc_line(
  147     FILE *fp,
  148     char *line)
  149 {
  150     char *seq;
  151     int sub;
  152     struct t_group *group;
  153 
  154     seq = parse_newsrc_line(line, &sub);
  155 
  156     if (line[0] == '\0' || sub == 0)        /* Insurance against blank line */
  157         return 0;
  158 
  159     if (seq == NULL) {      /* line has no ':' or '!' in it */
  160         if (tinrc.strip_bogus == BOGUS_REMOVE)
  161             wait_message(2, _(txt_remove_bogus), line);
  162         return 0;
  163     }
  164 
  165     /*
  166      * Find the group in active. If we cannot, then junk it if bogus groups
  167      * are set to auto removal. Also check for bogus flag just in case
  168      * strip_bogus was changed since tin started
  169      */
  170     group = group_find(line, FALSE);
  171 
  172     if (tinrc.strip_bogus == BOGUS_REMOVE) {
  173         if (group == NULL || group->bogus) { /* group doesn't exist */
  174             wait_message(2, _(txt_remove_bogus), line);
  175             return 0;
  176         }
  177     }
  178 
  179     if ((group && group->newsrc.present) && (group->subscribed || !tinrc.strip_newsrc)) {
  180         fprintf(fp, "%s%c ", group->name, SUB_CHAR(group->subscribed));
  181         print_bitmap_seq(fp, group);
  182         return 1;
  183     } else {
  184         if (sub == SUBSCRIBED || !tinrc.strip_newsrc) {
  185             fprintf(fp, "%s%c %s\n", line, sub, seq);
  186             return 1;
  187         }
  188     }
  189     return 0;
  190 }
  191 
  192 
  193 /*
  194  * Read in the users newsrc file and write a new file with all the changes
  195  * changes from the current session. If this works, replace the original
  196  * newsrc file.
  197  * Returns < 0 on error
  198  *         >=0 number of lines written
  199  */
  200 signed long int
  201 write_newsrc(
  202     void)
  203 {
  204     FILE *fp_ip;
  205     FILE *fp_op;
  206     char *line;
  207     signed long int tot = 0L;
  208     struct stat note_stat_newsrc;
  209     t_bool write_ok = FALSE;
  210     int err;
  211 
  212     if (no_write)
  213         return 0L;
  214 
  215     if ((fp_ip = fopen(newsrc, "r")) == NULL)
  216         return -1L; /* can't open newsrc */
  217 
  218     /* get size of original newsrc */
  219     if (fstat(fileno(fp_ip), &note_stat_newsrc) != 0) {
  220         fclose(fp_ip);
  221         return -1L; /* can't access newsrc */
  222     }
  223 
  224     if (!note_stat_newsrc.st_size) {
  225         fclose(fp_ip);
  226         return 0L; /* newsrc is empty */
  227     }
  228 
  229     if ((fp_op = fopen(newnewsrc, "w")) != NULL) {
  230         if (newsrc_mode)
  231 #ifdef HAVE_FCHMOD
  232             fchmod(fileno(fp_op), newsrc_mode);
  233 #else
  234 #   ifdef HAVE_CHMOD
  235             chmod(newnewsrc, newsrc_mode);
  236 #   endif /* HAVE_CHMOD */
  237 #endif /* HAVE_FCHMOD */
  238 
  239         while ((line = tin_fgets(fp_ip, FALSE)) != NULL)
  240             tot += write_newsrc_line(fp_op, line);
  241 
  242         /*
  243          * Don't rename if either fclose() fails or ferror() is set
  244          */
  245         if ((err = ferror(fp_op)) || fclose(fp_op)) {
  246             error_message(2, _(txt_filesystem_full), NEWSRC_FILE);
  247             unlink(newnewsrc);
  248             if (err) {
  249                 clearerr(fp_op);
  250                 fclose(fp_op);
  251             }
  252         } else
  253             write_ok = TRUE;
  254     }
  255 
  256     fclose(fp_ip);
  257 
  258     if (tot < 1) {
  259         error_message(2, _(txt_newsrc_nogroups));
  260         unlink(newnewsrc);
  261         return 0L;      /* So we don't get prompted to try again */
  262     }
  263 
  264     if (write_ok)
  265         rename_file(newnewsrc, newsrc);
  266 
  267     return tot;
  268 }
  269 
  270 
  271 /*
  272  * Create a newsrc from active[] groups. Subscribe to all groups.
  273  */
  274 static t_bool
  275 create_newsrc(
  276     char *newsrc_file)
  277 {
  278     FILE *fp;
  279     int i;
  280 
  281     if ((fp = fopen(newsrc_file, "w")) != NULL) {
  282         wait_message(0, _(txt_creating_newsrc));
  283 
  284         for_each_group(i)
  285             fprintf(fp, "%s!\n", active[i].name);
  286 
  287         if ((i = ferror(fp)) || fclose(fp)) {
  288             error_message(2, _(txt_filesystem_full), NEWSRC_FILE);
  289             if (i) {
  290                 clearerr(fp);
  291                 fclose(fp);
  292             }
  293             return FALSE;
  294         }
  295         return TRUE; /* newsrc created */
  296     }
  297     return FALSE;
  298 }
  299 
  300 
  301 /*
  302  * Get a list of default groups to subscribe to
  303  */
  304 static FILE *
  305 open_subscription_fp(
  306     void)
  307 {
  308     if (!read_saved_news) {
  309 #ifdef NNTP_ABLE
  310         if (read_news_via_nntp) {
  311             /* RFC 6048 2.6 */
  312             if (nntp_caps.type == CAPABILITIES && !nntp_caps.list_subscriptions)
  313                 return NULL;
  314             else
  315                 return (nntp_command("LIST SUBSCRIPTIONS", OK_GROUPS, NULL, 0));
  316         } else
  317 #endif /* NNTP_ABLE */
  318             return (fopen(subscriptions_file, "r"));
  319     } else
  320         return NULL;
  321 }
  322 
  323 
  324 /*
  325  * Automatically subscribe user to newsgroups specified in
  326  * NEWSLIBDIR/subscriptions (locally) or same file but from NNTP
  327  * server (LIST SUBSCRIPTIONS) and create .newsrc
  328  */
  329 static void
  330 auto_subscribe_groups(
  331     char *newsrc_file)
  332 {
  333     FILE *fp_newsrc;
  334     FILE *fp_subs;
  335     char *ptr;
  336     int err;
  337 
  338     /*
  339      * If subscription file exists then first unsubscribe to all groups
  340      * and then subscribe to just the auto specified groups.
  341      */
  342     if ((fp_subs = open_subscription_fp()) == NULL)
  343         return;
  344 
  345     if (!batch_mode)
  346         wait_message(0, _(txt_autosubscribing_groups));
  347 
  348     if ((fp_newsrc = fopen(newsrc_file, "w")) == NULL) {
  349         TIN_FCLOSE(fp_subs);
  350         return;
  351     }
  352 
  353     if (newsrc_mode)
  354 #ifdef HAVE_FCHMOD
  355         fchmod(fileno(fp_newsrc), newsrc_mode);
  356 #else
  357 #   ifdef HAVE_CHMOD
  358         chmod(newsrc_file, newsrc_mode);
  359 #   endif /* HAVE_CHMOD */
  360 #endif /* HAVE_FCHMOD */
  361 
  362     /* TODO: test me! */
  363     while ((ptr = tin_fgets(fp_subs, FALSE)) != NULL) {
  364         if (ptr[0] != '#') {
  365             if (group_find(ptr, FALSE) != NULL)
  366                 fprintf(fp_newsrc, "%s:\n", ptr);
  367         }
  368     }
  369 
  370     /* We ignore user 'q'uits here. They will get them next time in any case */
  371 
  372     if ((err = ferror(fp_newsrc)) || fclose(fp_newsrc)) {
  373         error_message(2, _(txt_filesystem_full), NEWSRC_FILE);
  374         if (err) {
  375             clearerr(fp_newsrc);
  376             fclose(fp_newsrc);
  377         }
  378     }
  379 
  380     TIN_FCLOSE(fp_subs);
  381 }
  382 
  383 
  384 /*
  385  * make a backup of users .newsrc in case of the bogie man
  386  */
  387 void
  388 backup_newsrc(
  389     void)
  390 {
  391     char dirbuf[PATH_LEN];
  392     char filebuf[PATH_LEN];
  393     struct stat statbuf;
  394 
  395 #ifdef NNTP_ABLE
  396     if (read_news_via_nntp && !read_saved_news && nntp_tcp_port != IPPORT_NNTP)
  397         snprintf(filebuf, sizeof(filebuf), "%s:%u", nntp_server, nntp_tcp_port);
  398     else
  399 #endif /* NNTP_ABLE */
  400     {
  401         STRCPY(filebuf, quote_space_to_dash(nntp_server));
  402     }
  403     joinpath(dirbuf, sizeof(dirbuf), rcdir, filebuf);
  404     joinpath(filebuf, sizeof(filebuf), dirbuf, OLDNEWSRC_FILE);
  405 
  406     if (stat(dirbuf, &statbuf) == -1) {
  407         if (my_mkdir(dirbuf, (mode_t) (S_IRWXU)) == -1)
  408             /* Can't create directory: Fall back on Homedir */
  409             joinpath(filebuf, sizeof(filebuf), homedir, OLDNEWSRC_FILE);
  410     }
  411 
  412     if (!backup_file(newsrc, filebuf))
  413         error_message(2, _(txt_filesystem_full_backup), NEWSRC_FILE);
  414 }
  415 
  416 
  417 /*
  418  * Find the total, max & min articles number for specified group
  419  * Use nntp GROUP command or read local spool
  420  * Return 0, or -error
  421  */
  422 int
  423 group_get_art_info(
  424     char *tin_spooldir,
  425     char *groupname,
  426     int grouptype,
  427     t_artnum *art_count,
  428     t_artnum *art_max,
  429     t_artnum *art_min)
  430 {
  431     DIR *dir;
  432     DIR_BUF *direntry;
  433     t_artnum artnum;
  434 
  435     if (read_news_via_nntp && grouptype == GROUP_TYPE_NEWS) {
  436 #ifdef NNTP_ABLE
  437         char line[NNTP_STRLEN];
  438 
  439         snprintf(line, sizeof(line), "GROUP %s", groupname);
  440 #   ifdef DEBUG
  441         if ((debug & DEBUG_NNTP) && verbose > 1)
  442             debug_print_file("NNTP", "group_get_art_info %s", line);
  443 #   endif /* DEBUG */
  444         put_server(line);
  445 
  446         switch (get_respcode(line, sizeof(line))) {
  447             case OK_GROUP:
  448                 if (sscanf(line, "%"T_ARTNUM_SFMT" %"T_ARTNUM_SFMT" %"T_ARTNUM_SFMT, art_count, art_min, art_max) != 3) {
  449 #   ifdef DEBUG
  450                     if ((debug & DEBUG_NNTP) && verbose > 1)
  451                         debug_print_file("NNTP", "Invalid response to GROUP command, %s", line);
  452 #   endif /* DEBUG */
  453                 }
  454                 break;
  455 
  456             case ERR_NOGROUP:
  457                 *art_count = T_ARTNUM_CONST(0);
  458                 *art_min = T_ARTNUM_CONST(1);
  459                 *art_max = T_ARTNUM_CONST(0);
  460                 return -ERR_NOGROUP;
  461 
  462             case ERR_ACCESS:
  463                 tin_done(NNTP_ERROR_EXIT, "%s", line);
  464                 /* keep lint quiet: */
  465                 /* NOTREACHED */
  466                 break;
  467 
  468             default:
  469 #   ifdef DEBUG
  470                 if ((debug & DEBUG_NNTP) && verbose > 1)
  471                     debug_print_file("NNTP", "NOT_OK %s", line);
  472 #   endif /* DEBUG */
  473                 return -1;
  474         }
  475 #else
  476         my_fprintf(stderr, _("Unreachable?\n")); /* TODO: -> lang.c */
  477         return 0;
  478 #endif /* NNTP_ABLE */
  479     } else {
  480         char group_path[PATH_LEN];
  481         *art_count = T_ARTNUM_CONST(0);
  482         *art_min = T_ARTNUM_CONST(0);
  483         *art_max = T_ARTNUM_CONST(0);
  484 
  485         make_base_group_path(tin_spooldir, groupname, group_path, sizeof(group_path));
  486 
  487         if ((dir = opendir(group_path)) != NULL) {
  488             while ((direntry = readdir(dir)) != NULL) {
  489                 artnum = atoartnum(direntry->d_name); /* should be '\0' terminated... */
  490                 if (artnum >= T_ARTNUM_CONST(1)) {
  491                     if (artnum > *art_max) {
  492                         *art_max = artnum;
  493                         if (*art_min == T_ARTNUM_CONST(0))
  494                             *art_min = artnum;
  495                     } else if (artnum < *art_min)
  496                         *art_min = artnum;
  497                     (*art_count)++;
  498                 }
  499             }
  500             CLOSEDIR(dir);
  501             if (*art_min == T_ARTNUM_CONST(0))
  502                 *art_min = T_ARTNUM_CONST(1);
  503         } else {
  504             *art_min = T_ARTNUM_CONST(1);
  505             return -1;
  506         }
  507     }
  508 
  509     return 0;
  510 }
  511 
  512 
  513 /*
  514  * Get and fixup (if needed) the counters for a newly subscribed group
  515  */
  516 static void
  517 get_subscribe_info(
  518     struct t_group *grp)
  519 {
  520     t_artnum oldmin = grp->xmin;
  521     t_artnum oldmax = grp->xmax;
  522 
  523     group_get_art_info(grp->spooldir, grp->name, grp->type, &grp->count, &grp->xmax, &grp->xmin);
  524 
  525     if (grp->newsrc.num_unread > grp->count) {
  526 #ifdef DEBUG
  527         if (debug & DEBUG_NEWSRC) { /* TODO: is this the right debug-level? */
  528             my_printf(cCRLF "Unread WRONG %s unread=[%"T_ARTNUM_PFMT"] count=[%"T_ARTNUM_PFMT"]", grp->name, grp->newsrc.num_unread, grp->count);
  529             my_flush();
  530         }
  531 #endif /* DEBUG */
  532         grp->newsrc.num_unread = grp->count;
  533     }
  534 
  535     if (grp->xmin != oldmin || grp->xmax != oldmax) {
  536         expand_bitmap(grp, 0);
  537 #ifdef DEBUG
  538         if (debug & DEBUG_NEWSRC) { /* TODO: is this the right debug-level? */
  539             my_printf(cCRLF "Min/Max DIFF %s old=[%"T_ARTNUM_PFMT"-%"T_ARTNUM_PFMT"] new=[%"T_ARTNUM_PFMT"-%"T_ARTNUM_PFMT"]", grp->name, oldmin, oldmax, grp->xmin, grp->xmax);
  540             my_flush();
  541         }
  542 #endif /* DEBUG */
  543     }
  544 }
  545 
  546 
  547 /*
  548  * Subscribe/unsubscribe to a group in .newsrc.
  549  * This involves rewriting the .newsrc with the new info
  550  * If get_info is set we are allowed to issue NNTP commands if needed
  551  */
  552 void
  553 subscribe(
  554     struct t_group *group,
  555     int sub_state,
  556     t_bool get_info)
  557 {
  558     FILE *fp;
  559     FILE *newfp;
  560     char *line;
  561     char *seq;
  562     int sub;
  563     t_bool found = FALSE;
  564 
  565     if (no_write)
  566         return;
  567 
  568     if ((newfp = fopen(newnewsrc, "w")) == NULL)
  569         return;
  570 
  571     if (newsrc_mode)
  572 #ifdef HAVE_FCHMOD
  573         fchmod(fileno(newfp), newsrc_mode);
  574 #else
  575 #   ifdef HAVE_CHMOD
  576         chmod(newnewsrc, newsrc_mode);
  577 #   endif /* HAVE_CHMOD */
  578 #endif /* HAVE_FCHMOD */
  579 
  580     if ((fp = fopen(newsrc, "r")) != NULL) {
  581         while ((line = tin_fgets(fp, FALSE)) != NULL) {
  582             if ((seq = parse_newsrc_line(line, &sub))) {
  583                 if (STRCMPEQ(line, group->name)) {
  584                     fprintf(newfp, "%s%c %s\n", line, sub_state, seq);
  585                     group->subscribed = SUB_BOOL(sub_state);
  586 
  587                     /* If previously subscribed to in .newsrc, load up any existing information */
  588                     if (sub_state == SUBSCRIBED)
  589                         parse_bitmap_seq(group, seq);
  590 
  591                     found = TRUE;
  592                 } else
  593                     fprintf(newfp, "%s%c %s\n", line, sub, seq);
  594             }
  595         }
  596 
  597         fclose(fp);
  598 
  599         if (!found) {
  600             wait_message(0, _(txt_subscribing));
  601             group->subscribed = SUB_BOOL(sub_state);
  602             if (sub_state == SUBSCRIBED) {
  603                 fprintf(newfp, "%s%c ", group->name, sub_state);
  604                 if (get_info) {
  605                     get_subscribe_info(group);
  606                     print_bitmap_seq(newfp, group);
  607                 } else /* we are not allowed to issue NNTP cmds during AUTOSUBSCRIBE loop */
  608                     fprintf(newfp, "1\n");
  609             } else
  610                 fprintf(newfp, "%s%c\n", group->name, sub_state);
  611         }
  612     }
  613 
  614     if ((sub = ferror(newfp)) || fclose(newfp)) {
  615         error_message(2, _(txt_filesystem_full), NEWSRC_FILE);
  616         if (sub) {
  617             clearerr(newfp);
  618             fclose(newfp);
  619         }
  620         unlink(newnewsrc);
  621     } else
  622         rename_file(newnewsrc, newsrc);
  623 }
  624 
  625 
  626 void
  627 reset_newsrc(
  628     void)
  629 {
  630     FILE *fp;
  631     FILE *newfp;
  632     char *line;
  633     int sub, i;
  634 
  635     if (!no_write && (newfp = fopen(newnewsrc, "w")) != NULL) {
  636         if (newsrc_mode)
  637 #ifdef HAVE_FCHMOD
  638             fchmod(fileno(newfp), newsrc_mode);
  639 #else
  640 #   ifdef HAVE_CHMOD
  641             chmod(newnewsrc, newsrc_mode);
  642 #   endif /* HAVE_CHMOD */
  643 #endif /* HAVE_FCHMOD */
  644 
  645         if ((fp = fopen(newsrc, "r")) != NULL) {
  646             while ((line = tin_fgets(fp, FALSE)) != NULL) {
  647                 (void) parse_newsrc_line(line, &sub);
  648                 fprintf(newfp, "%s%c\n", line, sub);
  649             }
  650             fclose(fp);
  651         }
  652         if ((sub = ferror(newfp)) || fclose(newfp)) {
  653             error_message(2, _(txt_filesystem_full), NEWSRC_FILE);
  654             if (sub) {
  655                 clearerr(newfp);
  656                 fclose(newfp);
  657             }
  658             unlink(newnewsrc);
  659         } else
  660             rename_file(newnewsrc, newsrc);
  661     }
  662 
  663     for (i = 0; i < selmenu.max; i++)
  664         set_default_bitmap(&active[my_group[i]]);
  665 }
  666 
  667 
  668 /*
  669  * Rewrite the newsrc file, without the specified group
  670  */
  671 void
  672 delete_group(
  673     char *group)
  674 {
  675     FILE *fp;
  676     FILE *newfp;
  677     char *line;
  678     char *seq;
  679     int sub;
  680 
  681     if (no_write)
  682         return;
  683 
  684     if ((newfp = fopen(newnewsrc, "w")) != NULL) {
  685         if (newsrc_mode)
  686 #ifdef HAVE_FCHMOD
  687             fchmod(fileno(newfp), newsrc_mode);
  688 #else
  689 #   ifdef HAVE_CHMOD
  690             chmod(newnewsrc, newsrc_mode);
  691 #   endif /* HAVE_CHMOD */
  692 #endif /* HAVE_FCHMOD */
  693 
  694         if ((fp = fopen(newsrc, "r")) != NULL) {
  695             while ((line = tin_fgets(fp, FALSE)) != NULL) {
  696                 if ((seq = parse_newsrc_line(line, &sub))) {
  697                     if (!STRCMPEQ(line, group))
  698                         fprintf(newfp, "%s%c %s\n", line, sub, seq);
  699                 }
  700             }
  701             fclose(fp);
  702         }
  703 
  704         if ((sub = ferror(newfp)) || fclose(newfp)) {
  705             error_message(2, _(txt_filesystem_full), NEWSRC_FILE);
  706             if (sub) {
  707                 clearerr(newfp);
  708                 fclose(newfp);
  709             }
  710             unlink(newnewsrc);
  711         } else
  712             rename_file(newnewsrc, newsrc);
  713     }
  714 }
  715 
  716 
  717 /*
  718  * Mark a group as read
  719  * If art != NULL then we explicitly process each article thus
  720  * catching crossposts as well, otherwise we simply scrub the
  721  * bitmap and adjust the highwater mark.
  722  */
  723 void
  724 grp_mark_read(
  725     struct t_group *group,
  726     struct t_article *art)
  727 {
  728     int i;
  729 
  730 #ifdef DEBUG
  731     if (debug & DEBUG_NEWSRC)
  732         debug_print_comment("c/C command");
  733 #endif /* DEBUG */
  734 
  735     if (art != NULL) {
  736         for_each_art(i)
  737             art_mark(group, &art[i], ART_READ);
  738     } else {
  739         FreeAndNull(group->newsrc.xbitmap);
  740         group->newsrc.xbitlen = 0;
  741         if (group->xmax > group->newsrc.xmax)
  742             group->newsrc.xmax = group->xmax;
  743         group->newsrc.xmin = group->newsrc.xmax + 1;
  744         group->newsrc.num_unread = 0;
  745     }
  746 }
  747 
  748 
  749 void
  750 grp_mark_unread(
  751     struct t_group *group)
  752 {
  753     t_artnum bitlength;
  754     t_bitmap *newbitmap = (t_bitmap *) 0;
  755 
  756 #ifdef DEBUG
  757     if (debug & DEBUG_NEWSRC)
  758         debug_print_comment("Z command");
  759 #endif /* DEBUG */
  760 
  761     group_get_art_info(group->spooldir, group->name, group->type, &group->count, &group->xmax, &group->xmin);
  762 
  763     group->newsrc.num_unread = group->count;
  764     if (group->xmax > group->newsrc.xmax)
  765         group->newsrc.xmax = group->xmax;
  766     if (group->xmin > 0)
  767         group->newsrc.xmin = group->xmin;
  768 
  769     bitlength = MAX(0, group->newsrc.xmax - group->newsrc.xmin + 1);
  770 
  771     if (bitlength > 0)
  772         newbitmap = my_malloc(BITS_TO_BYTES(bitlength));
  773 
  774     FreeIfNeeded(group->newsrc.xbitmap);
  775     group->newsrc.xbitmap = newbitmap;
  776     group->newsrc.xbitlen = bitlength;
  777 
  778     if (bitlength)
  779         NSETRNG1(group->newsrc.xbitmap, T_ARTNUM_CONST(0), bitlength - T_ARTNUM_CONST(1));
  780 
  781 #ifdef DEBUG
  782     if (debug & DEBUG_NEWSRC)
  783         debug_print_bitmap(group, NULL);
  784 #endif /* DEBUG */
  785 }
  786 
  787 
  788 void
  789 thd_mark_read(
  790     struct t_group *group,
  791     long thread)
  792 {
  793     int i;
  794 
  795 #ifdef DEBUG
  796     if (debug & DEBUG_NEWSRC)
  797         debug_print_comment("Mark thread read K command");
  798 #endif /* DEBUG */
  799 
  800     for (i = (int) thread; i >= 0; i = arts[i].thread)
  801         art_mark(group, &arts[i], ART_READ);
  802 }
  803 
  804 
  805 void
  806 thd_mark_unread(
  807     struct t_group *group,
  808     long thread)
  809 {
  810     int i;
  811 
  812 #ifdef DEBUG
  813     if (debug & DEBUG_NEWSRC)
  814         debug_print_comment("Mark thread unread Z command");
  815 #endif /* DEBUG */
  816 
  817     for (i = (int) thread; i >= 0; i = arts[i].thread)
  818         art_mark(group, &arts[i], ART_WILL_RETURN);
  819 }
  820 
  821 
  822 /*
  823  * Parse the newsrc sequence for the specified group
  824  */
  825 static void
  826 parse_bitmap_seq(
  827     struct t_group *group,
  828     char *seq)
  829 {
  830     char *ptr;
  831     t_artnum sum = T_ARTNUM_CONST(0);
  832     t_artnum low = T_ARTNUM_CONST(0);
  833     t_artnum high = T_ARTNUM_CONST(0);
  834     t_artnum min, max;
  835     t_bool gotseq = FALSE;
  836 
  837     /*
  838      * Skip possible non-numeric prefix
  839      */
  840     ptr = seq;
  841     while (ptr && *ptr && (*ptr < '0' || *ptr > '9'))
  842         ptr++;
  843 
  844 #ifdef DEBUG
  845     if (debug & DEBUG_NEWSRC) {
  846         char buf[NEWSRC_LINE];
  847 
  848         snprintf(buf, sizeof(buf), "Parsing [%s%c %.*s]", group->name, SUB_CHAR(group->subscribed), (int) (NEWSRC_LINE - strlen(group->name) - 14), BlankIfNull(ptr));
  849         debug_print_comment(buf);
  850         debug_print_bitmap(group, NULL);
  851     }
  852 #endif /* DEBUG */
  853 
  854     if (ptr) {
  855         gotseq = TRUE;
  856         ptr = parse_get_seq(ptr, &low, &high);
  857 
  858         if (high < group->xmin - 1)
  859             high = group->xmin - 1;
  860 
  861         min = ((low <= 1) ? (high + 1) : 1);
  862 
  863         if (group->xmin > min)
  864             min = group->xmin;
  865 
  866         if (group->xmax > high)
  867             max = group->xmax;
  868         else
  869             max = high;     /* trust newsrc's max */
  870 
  871         FreeAndNull(group->newsrc.xbitmap);
  872         group->newsrc.xmax = max;
  873         group->newsrc.xmin = min;
  874         group->newsrc.xbitlen = (max - min) + 1;
  875         if (group->newsrc.xbitlen > 0) {
  876             group->newsrc.xbitmap = my_malloc(BITS_TO_BYTES(group->newsrc.xbitlen));
  877             NSETRNG1(group->newsrc.xbitmap, T_ARTNUM_CONST(0), group->newsrc.xbitlen - T_ARTNUM_CONST(1));
  878         }
  879 
  880         if (min <= high) {
  881             if (low > min)
  882                 sum = low - min;
  883             else
  884                 low = min;
  885             NSETRNG0(group->newsrc.xbitmap, low - min, high - min);
  886         }
  887 
  888         /*
  889          * Pick up any additional articles/ranges after the first
  890          */
  891         while (*ptr)
  892             ptr = parse_subseq(group, ptr, &low, &high, &sum);
  893     } else {
  894         FreeAndNull(group->newsrc.xbitmap);
  895         group->newsrc.xmax = group->xmax;
  896         if (group->xmin > 0)
  897             group->newsrc.xmin = group->xmin;
  898         else
  899             group->newsrc.xmin = 1;
  900         group->newsrc.xbitlen = (group->newsrc.xmax - group->newsrc.xmin) + 1;
  901         if (group->newsrc.xbitlen > 0) {
  902             group->newsrc.xbitmap = my_malloc(BITS_TO_BYTES(group->newsrc.xbitlen));
  903             NSETRNG1(group->newsrc.xbitmap, T_ARTNUM_CONST(0), group->newsrc.xbitlen - T_ARTNUM_CONST(1));
  904         }
  905 /*
  906 wait_message(2, "BITMAP Grp=[%s] MinMax=[%"T_ARTNUM_PFMT"-%"T_ARTNUM_PFMT"] Len=[%"T_ARTNUM_PFMT"]\n",
  907     group->name, group->xmin, group->xmax, group->newsrc.xbitlen);
  908 */
  909     }
  910 
  911     group->newsrc.present = TRUE;
  912 
  913     if (gotseq) {
  914         if (group->newsrc.xmax > high)
  915             sum += group->newsrc.xmax - high;
  916     } else
  917         sum = (group->count >= 0) ? group->count : ((group->newsrc.xmax - group->newsrc.xmin) + 1);
  918 
  919     group->newsrc.num_unread = sum;
  920 #ifdef DEBUG
  921     if (debug & DEBUG_NEWSRC)
  922         debug_print_bitmap(group, NULL);
  923 #endif /* DEBUG */
  924 }
  925 
  926 
  927 /*
  928  * Parse a subsection of the newsrc sequencer ie., 1-34,23-90,93,97-99
  929  * would parse the sequence if called in a loop in the following way:
  930  *   1st call would parse  1-34 and return 23-90,93,97-99
  931  *   2nd call would parse 23-90 and return 93,97-99
  932  *   3rd call would parse    93 and return 97-99
  933  *   4th call would parse 97-99 and return NULL
  934  */
  935 static char *
  936 parse_subseq(
  937     struct t_group *group,
  938     char *seq,
  939     t_artnum *low,
  940     t_artnum *high,
  941     t_artnum *sum)
  942 {
  943     t_artnum bitmin;
  944     t_artnum bitmax;
  945     t_artnum last_high = *high;
  946 
  947     seq = parse_get_seq(seq, low, high);
  948 
  949     /*
  950      * Bitmap index
  951      */
  952     bitmin = *low - group->newsrc.xmin;
  953 
  954     /*
  955      * check that seq is not out of order
  956      */
  957     if (*low > last_high)
  958         *sum += (*low - last_high) - 1;
  959 
  960     if (*high == *low) {
  961         if (bitmin >= 0) {
  962             if (*high > group->newsrc.xmax) {
  963                 /* We trust .newsrc's max. */
  964                 t_artnum bitlen;
  965                 t_bitmap *newbitmap;
  966 
  967                 group->newsrc.xmax = *high;
  968                 bitlen = group->newsrc.xmax - group->newsrc.xmin + 1;
  969                 newbitmap = my_malloc(BITS_TO_BYTES(bitlen));
  970 
  971                 /* Copy over old bitmap */
  972                 memcpy(newbitmap, group->newsrc.xbitmap, BITS_TO_BYTES(group->newsrc.xbitlen));
  973 
  974                 /* Mark high numbered articles as unread */
  975                 NSETRNG1(newbitmap, group->newsrc.xbitlen, bitlen - 1);
  976 
  977                 free(group->newsrc.xbitmap);
  978                 group->newsrc.xbitmap = newbitmap;
  979                 group->newsrc.xbitlen = bitlen;
  980             }
  981             NSET0(group->newsrc.xbitmap, bitmin);
  982         }
  983     } else if ((*low < *high) && (*high >= group->newsrc.xmin)) {
  984         /*
  985          * Restrict the range to min..max
  986          */
  987         if (bitmin < 0)
  988             bitmin = 0;
  989 
  990         bitmax = *high;
  991 
  992         if (bitmax > group->newsrc.xmax) {
  993             /* We trust .newsrc's max. */
  994             t_artnum bitlen;
  995             t_bitmap *newbitmap;
  996 
  997             group->newsrc.xmax = bitmax;
  998             bitlen = group->newsrc.xmax - group->newsrc.xmin + 1;
  999             newbitmap = my_malloc(BITS_TO_BYTES(bitlen));
 1000 
 1001             /* Copy over old bitmap */
 1002             memcpy(newbitmap, group->newsrc.xbitmap, BITS_TO_BYTES(group->newsrc.xbitlen));
 1003 
 1004             /* Mark high numbered articles as unread */
 1005             NSETRNG1(newbitmap, group->newsrc.xbitlen, bitlen - 1);
 1006 
 1007             free(group->newsrc.xbitmap);
 1008             group->newsrc.xbitmap = newbitmap;
 1009             group->newsrc.xbitlen = bitlen;
 1010         }
 1011 
 1012         bitmax -= group->newsrc.xmin;
 1013 
 1014         /*
 1015          * Fill in the whole range as read
 1016          */
 1017         NSETRNG0(group->newsrc.xbitmap, bitmin, bitmax);
 1018     }
 1019     return seq;
 1020 }
 1021 
 1022 
 1023 static char *
 1024 parse_get_seq(
 1025     char *seq,
 1026     t_artnum *low,
 1027     t_artnum *high)
 1028 {
 1029     *low = strtoartnum(seq, &seq, 10);
 1030 
 1031     if (*seq == '-') {  /* Range of articles */
 1032         seq++;
 1033         *high = strtoartnum(seq, &seq, 10);
 1034     } else  /* Single article */
 1035         *high = *low;
 1036 
 1037     while (*seq && (*seq < '0' || *seq > '9'))
 1038         seq++;
 1039 
 1040     return seq;
 1041 }
 1042 
 1043 
 1044 /*
 1045  * Loop through arts[] array marking state of each article READ/UNREAD
 1046  */
 1047 void
 1048 parse_unread_arts(
 1049     struct t_group *group,
 1050     t_artnum min)
 1051 {
 1052     int i;
 1053     t_artnum unread = T_ARTNUM_CONST(0);
 1054     t_artnum bitmin, bitmax;
 1055     t_bitmap *newbitmap = (t_bitmap *) 0;
 1056 
 1057     bitmin = group->newsrc.xmin;
 1058     bitmax = group->newsrc.xmax;
 1059 
 1060     /*
 1061      * TODO
 1062      * what about group->newsrc.xmax > group->xmax?
 1063      * that should indicate an artnum 'reset' on the server
 1064      * (or using the "wrong" newsrc for that server)
 1065      */
 1066 
 1067     if (group->xmax > group->newsrc.xmax)
 1068         group->newsrc.xmax = group->xmax;
 1069 
 1070     if (group->newsrc.xmax >= bitmin) {
 1071         newbitmap = my_malloc(BITS_TO_BYTES(group->newsrc.xmax - bitmin + 1));
 1072         NSETRNG0(newbitmap, T_ARTNUM_CONST(0), group->newsrc.xmax - bitmin);
 1073     }
 1074 
 1075     /*
 1076      * if getart_limit > 0 preserve read/unread state
 1077      * of all articles below the new minimum
 1078      */
 1079     if (min > 0 && newbitmap) {
 1080         t_artnum j, tmp_bitmax;
 1081 
 1082         tmp_bitmax = (bitmax < min) ? bitmax : min;
 1083         for (j = bitmin; j < tmp_bitmax; j++) {
 1084             if (NTEST(group->newsrc.xbitmap, j - bitmin) != ART_READ)
 1085                 NSET1(newbitmap, j - bitmin);
 1086         }
 1087         while (j < min) {
 1088             NSET1(newbitmap, j - bitmin);
 1089             j++;
 1090         }
 1091     }
 1092 
 1093     for_each_art(i) {
 1094         if (arts[i].artnum < bitmin)
 1095             arts[i].status = ART_READ;
 1096         else if (arts[i].artnum > bitmax)
 1097             arts[i].status = ART_UNREAD;
 1098         else if (NTEST(group->newsrc.xbitmap, arts[i].artnum - bitmin) == ART_READ)
 1099             arts[i].status = ART_READ;
 1100         else
 1101             arts[i].status = ART_UNREAD;
 1102 
 1103         /* TODO: logic correct? */
 1104         if (newbitmap != NULL && arts[i].status == ART_UNREAD && arts[i].artnum >= bitmin) {
 1105 #if 0
 1106         /*
 1107          * check for wrong article numbers in the overview
 1108          *
 1109          * TODO: check disabled as we currently catch the artnum > high_mark
 1110          *       case in read_overview() where we might be able to
 1111          *       fix the broken artnum (via xref:-parsing). currently
 1112          *       we just skip the art there.
 1113          */
 1114             if (arts[i].artnum <= group->xmax)
 1115 #endif /* 0 */
 1116                 NSET1(newbitmap, arts[i].artnum - bitmin);
 1117             unread++;
 1118         }
 1119     }
 1120 
 1121     group->newsrc.xbitlen = group->newsrc.xmax - bitmin + 1;
 1122 
 1123     FreeIfNeeded(group->newsrc.xbitmap);
 1124 
 1125     group->newsrc.xbitmap = newbitmap;
 1126     group->newsrc.num_unread = unread;
 1127 }
 1128 
 1129 
 1130 static void
 1131 print_bitmap_seq(
 1132     FILE *fp,
 1133     struct t_group *group)
 1134 {
 1135     t_artnum artnum;
 1136     t_artnum i;
 1137     t_bool flag = FALSE;
 1138 
 1139 #ifdef DEBUG
 1140     if (debug & DEBUG_NEWSRC) {
 1141         debug_print_comment("print_bitmap_seq()");
 1142         debug_print_bitmap(group, NULL);
 1143     }
 1144 #endif /* DEBUG */
 1145 
 1146     if (group->count == 0 || group->xmin > group->xmax) {
 1147         if (group->newsrc.xmax > 1)
 1148             fprintf(fp, "1-%"T_ARTNUM_PFMT, group->newsrc.xmax);
 1149 
 1150         fprintf(fp, "\n");
 1151         fflush(fp);
 1152 #ifdef DEBUG
 1153         if (debug & DEBUG_NEWSRC)
 1154             debug_print_comment("print_bitmap_seq(): group->count == 0");
 1155 #endif /* DEBUG */
 1156         return;
 1157     }
 1158 
 1159     i = group->newsrc.xmin;
 1160     if (i <= group->newsrc.xmax) {
 1161         forever {
 1162             if (group->newsrc.xbitmap && NTEST(group->newsrc.xbitmap, i - group->newsrc.xmin) == ART_READ) {
 1163                 if (flag) {
 1164                     artnum = i;
 1165                     fprintf(fp, ",%"T_ARTNUM_PFMT, i);
 1166                 } else {
 1167                     artnum = 1;
 1168                     flag = TRUE;
 1169                     fprintf(fp, "1");
 1170                 }
 1171                 while (i < group->newsrc.xmax && NTEST(group->newsrc.xbitmap, (i + 1) - group->newsrc.xmin) == ART_READ)
 1172                     i++;
 1173 
 1174                 if (artnum != i)
 1175                     fprintf(fp, "-%"T_ARTNUM_PFMT, i);
 1176 
 1177             } else if (!flag) {
 1178                 flag = TRUE;
 1179                 if (group->newsrc.xmin > 1) {
 1180                     fprintf(fp, "1");
 1181 
 1182                     if (group->newsrc.xmin > 2)
 1183                         fprintf(fp, "-%"T_ARTNUM_PFMT, group->newsrc.xmin - 1);
 1184 
 1185                 }
 1186             }
 1187             if (group->newsrc.xmax == i)
 1188                 break;
 1189 
 1190             i++;
 1191         }
 1192     }
 1193 
 1194     if (!flag && group->newsrc.xmin > 1) {
 1195         fprintf(fp, "1");
 1196 
 1197         if (group->newsrc.xmin > 2)
 1198             fprintf(fp, "-%"T_ARTNUM_PFMT, group->newsrc.xmin - 1);
 1199 
 1200 #ifdef DEBUG
 1201         if (debug & DEBUG_NEWSRC)
 1202             debug_print_comment("print_bitmap_seq(): !flag && group->newsrc.xmin > 1");
 1203 #endif /* DEBUG */
 1204     }
 1205 
 1206     fprintf(fp, "\n");
 1207     fflush(fp);
 1208 }
 1209 
 1210 
 1211 /*
 1212  * rewrite .newsrc and position group at specified position
 1213  */
 1214 t_bool
 1215 pos_group_in_newsrc(
 1216     struct t_group *group,
 1217     int pos)
 1218 {
 1219     FILE *fp_in = NULL, *fp_out = NULL;
 1220     FILE *fp_sub = NULL, *fp_unsub = NULL;
 1221     char *newsgroup = NULL;
 1222     char *line;
 1223     char filename[PATH_LEN];
 1224     char sub[PATH_LEN];
 1225     char unsub[PATH_LEN];
 1226     int subscribed_pos = 1;
 1227     int err;
 1228     size_t group_len;
 1229     t_bool found = FALSE;
 1230     t_bool newnewsrc_created = FALSE;
 1231     t_bool option_line = FALSE;
 1232     t_bool repositioned = FALSE;
 1233     t_bool ret_code = FALSE;
 1234     t_bool sub_created = FALSE;
 1235     t_bool unsub_created = FALSE;
 1236     t_bool fs_error = FALSE;
 1237 
 1238     if (no_write)
 1239         goto rewrite_group_done;
 1240 
 1241     if ((fp_in = fopen(newsrc, "r")) == NULL)
 1242         goto rewrite_group_done;
 1243 
 1244     if ((fp_out = fopen(newnewsrc, "w")) == NULL)
 1245         goto rewrite_group_done;
 1246 
 1247     newnewsrc_created = TRUE;
 1248 
 1249     if (newsrc_mode)
 1250 #ifdef HAVE_FCHMOD
 1251         fchmod(fileno(fp_out), newsrc_mode);
 1252 #else
 1253 #   ifdef HAVE_CHMOD
 1254         chmod(newnewsrc, newsrc_mode);
 1255 #   endif /* HAVE_CHMOD */
 1256 #endif /* HAVE_FCHMOD */
 1257 
 1258     joinpath(filename, sizeof(filename), tmpdir, ".subrc");
 1259     snprintf(sub, sizeof(sub), "%s.%ld", filename, (long) process_id);
 1260 
 1261     joinpath(filename, sizeof(filename), tmpdir, ".unsubrc");
 1262     snprintf(unsub, sizeof(unsub), "%s.%ld", filename, (long) process_id);
 1263 
 1264     if ((fp_sub = fopen(sub, "w")) == NULL)
 1265         goto rewrite_group_done;
 1266 
 1267     sub_created = TRUE;
 1268 
 1269     if ((fp_unsub = fopen(unsub, "w")) == NULL)
 1270         goto rewrite_group_done;
 1271 
 1272     unsub_created = TRUE;
 1273 
 1274     /*
 1275      * split newsrc into subscribed and unsubscribed to files
 1276      */
 1277     group_len = strlen(group->name);
 1278 
 1279     while ((line = tin_fgets(fp_in, FALSE)) != NULL) {
 1280         if (STRNCMPEQ(group->name, line, group_len) && line[group_len] == SUBSCRIBED) {
 1281             FreeIfNeeded(newsgroup);
 1282             newsgroup = my_strdup(line);        /* Take a copy of this line */
 1283             found = TRUE;
 1284             continue;
 1285         } else if (strchr(line, SUBSCRIBED) != NULL) {
 1286             write_newsrc_line(fp_sub, line);
 1287         } else if (strchr(line, UNSUBSCRIBED) != NULL) {
 1288             write_newsrc_line(fp_unsub, line);
 1289         } else {                                /* options line at beginning of .newsrc */
 1290             fprintf(fp_sub, "%s\n", line);
 1291             option_line = TRUE;
 1292         }
 1293     }
 1294 
 1295     if ((err = ferror(fp_sub)) || fclose(fp_sub)) {
 1296         error_message(2, _(txt_filesystem_full), NEWSRC_FILE);
 1297         if (err) {
 1298             clearerr(fp_sub);
 1299             fclose(fp_sub);
 1300         }
 1301         fs_error = TRUE;
 1302     }
 1303     if ((err = ferror(fp_unsub)) || fclose(fp_unsub)) {
 1304         if (!fs_error) /* avoid repeatd error message */
 1305             error_message(2, _(txt_filesystem_full), NEWSRC_FILE);
 1306         if (err) {
 1307             clearerr(fp_unsub);
 1308             fclose(fp_unsub);
 1309         }
 1310         fs_error = TRUE;
 1311     }
 1312     fp_sub = fp_unsub = NULL;
 1313 
 1314     if (fs_error)
 1315         goto rewrite_group_done;
 1316 
 1317     fclose(fp_in);
 1318     fp_in = NULL;
 1319 
 1320     /*
 1321      * The group to be moved cannot be found, so give up now
 1322      */
 1323     if (!found)
 1324         goto rewrite_group_done;
 1325 
 1326     /*
 1327      * write subscribed groups & repositioned group to newnewsrc
 1328      */
 1329     if ((fp_sub = fopen(sub, "r")) == NULL)
 1330         goto rewrite_group_done;
 1331 
 1332     while ((line = tin_fgets(fp_sub, FALSE)) != NULL) {
 1333         if (option_line) {
 1334             if (strchr(line, SUBSCRIBED) == NULL && strchr(line, UNSUBSCRIBED) == NULL) {
 1335                 fprintf(fp_out, "%s\n", line);
 1336                 continue;
 1337             } else
 1338                 option_line = FALSE;
 1339         }
 1340 
 1341         if (pos == subscribed_pos) {
 1342             write_newsrc_line(fp_out, newsgroup);
 1343             repositioned = TRUE;
 1344         }
 1345 
 1346         fprintf(fp_out, "%s\n", line);
 1347 
 1348         subscribed_pos++;
 1349     }
 1350 
 1351     if (!repositioned)
 1352         write_newsrc_line(fp_out, newsgroup);
 1353 
 1354     /*
 1355      * append unsubscribed groups file to newnewsrc
 1356      */
 1357     if ((fp_unsub = fopen(unsub, "r")) == NULL)
 1358         goto rewrite_group_done;
 1359 
 1360     while ((line = tin_fgets(fp_unsub, FALSE)) != NULL)
 1361         fprintf(fp_out, "%s\n", line);
 1362 
 1363     /*
 1364      * Try and cleanly close out the newnewsrc file
 1365      */
 1366     if ((err = ferror(fp_out)) || fclose(fp_out)) {
 1367         error_message(2, _(txt_filesystem_full), NEWSRC_FILE);
 1368         if (err) {
 1369             clearerr(fp_out);
 1370             fclose(fp_out);
 1371         }
 1372     } else {
 1373         rename_file(newnewsrc, newsrc);
 1374         ret_code = TRUE;
 1375     }
 1376     fp_out = NULL;
 1377     newnewsrc_created = FALSE;
 1378 
 1379 rewrite_group_done:
 1380     if (fp_in != NULL)
 1381         fclose(fp_in);
 1382 
 1383     if (fp_out != NULL)
 1384         fclose(fp_out);
 1385 
 1386     if (fp_sub != NULL)
 1387         fclose(fp_sub);
 1388 
 1389     if (fp_unsub != NULL)
 1390         fclose(fp_unsub);
 1391 
 1392     if (newnewsrc_created)
 1393         unlink(newnewsrc);
 1394 
 1395     if (sub_created)
 1396         unlink(sub);
 1397 
 1398     if (unsub_created)
 1399         unlink(unsub);
 1400 
 1401     FreeIfNeeded(newsgroup);
 1402 
 1403     return ret_code;
 1404 }
 1405 
 1406 
 1407 /*
 1408  * catchup all groups in .newsrc
 1409  */
 1410 void
 1411 catchup_newsrc_file(
 1412     void)
 1413 {
 1414     int i;
 1415     struct t_group *group;
 1416 
 1417     for (i = 0; i < selmenu.max; i++) {
 1418         group = &active[my_group[i]];
 1419         group->newsrc.present = TRUE;
 1420         FreeAndNull(group->newsrc.xbitmap);
 1421         if (group->xmax > group->newsrc.xmax)
 1422             group->newsrc.xmax = group->xmax;
 1423         group->newsrc.xmin = group->newsrc.xmax + 1;
 1424         group->newsrc.num_unread = 0;
 1425         group->newsrc.xbitlen = 0;
 1426     }
 1427 }
 1428 
 1429 
 1430 /*
 1431  * Break down a line of .newsrc file
 1432  * The sequence information [ eg; 1-3,10,12 ] is returned, line is truncated to
 1433  * just the group name and the subscription flag is copied to sub.
 1434  */
 1435 static char *
 1436 parse_newsrc_line(
 1437     char *line,
 1438     int *sub)
 1439 {
 1440     char *ptr, *tmp;
 1441 
 1442     *sub = UNSUBSCRIBED;                /* Default to no entry */
 1443 
 1444     if ((ptr = strpbrk(line, "!:")) == NULL)            /* space|SUBSCRIBED|UNSUBSCRIBED */
 1445         return NULL;
 1446 
 1447     *sub = *ptr;                        /* Save the subscription status */
 1448     tmp = ptr;                          /* Keep this blank for later */
 1449     *(ptr++) = '\0';                    /* Terminate the group name */
 1450 
 1451 #if 0
 1452     if (ptr == NULL)                    /* No seq info, so return a blank */
 1453         return tmp;
 1454 #endif /* 0 */
 1455 
 1456     if ((ptr = strpbrk(ptr, " \t")) == NULL)
 1457         return tmp;
 1458 
 1459     return (ptr + 1);   /* Return pointer to sequence info. At worst this will be \0 */
 1460 }
 1461 
 1462 
 1463 /*
 1464  * expand group->newsrc information if group->xmax is larger than
 1465  * group->newsrc.xmax or min is smaller than group->newsrc.xmin.
 1466  */
 1467 void
 1468 expand_bitmap(
 1469     struct t_group *group,
 1470     t_artnum min)
 1471 {
 1472     t_artnum bitlen;
 1473     t_artnum first;
 1474     t_artnum tmp;
 1475     t_artnum max;
 1476     t_bool need_full_copy = FALSE;
 1477 
 1478     /* calculate new max */
 1479     if (group->newsrc.xmax > group->xmax)
 1480         max = group->newsrc.xmax;
 1481     else
 1482         max = group->xmax;
 1483 
 1484     /* adjust min */
 1485     if (!min)
 1486         min = group->newsrc.xmin;
 1487 
 1488     /* calculate first */
 1489     if (min >= group->newsrc.xmin)
 1490         first = group->newsrc.xmin;
 1491     else
 1492         first = group->newsrc.xmin - ((group->newsrc.xmin - min + (NBITS - 1)) & ~(NBITS - 1));
 1493 
 1494     /* adjust first */
 1495     if (group->newsrc.xmax < first - 1)
 1496         first = first - ((first - (group->newsrc.xmax + 1) + (NBITS - 1)) & ~(NBITS - 1));
 1497 
 1498     /* check first */
 1499     if (first < 1) {
 1500         need_full_copy = TRUE;
 1501         first = 1;
 1502     }
 1503 
 1504     bitlen = max - first + 1;
 1505 
 1506     if (bitlen <= 0) {
 1507         bitlen = 0;
 1508         FreeIfNeeded(group->newsrc.xbitmap);
 1509         group->newsrc.xbitmap = (t_bitmap *) 0;
 1510 #ifdef DEBUG
 1511         if (debug & DEBUG_NEWSRC)
 1512             debug_print_comment("expand_bitmap: group->newsrc.bitlen == 0");
 1513 #endif /* DEBUG */
 1514     } else if (group->newsrc.xbitmap == NULL) {
 1515         group->newsrc.xbitmap = my_malloc(BITS_TO_BYTES(bitlen));
 1516         if (group->newsrc.xmin > first)
 1517             NSETRNG0(group->newsrc.xbitmap, T_ARTNUM_CONST(0), group->newsrc.xmin - first - T_ARTNUM_CONST(1));
 1518         if (bitlen > group->newsrc.xmin - first)
 1519             NSETRNG1(group->newsrc.xbitmap, group->newsrc.xmin - first, bitlen - 1);
 1520 #ifdef DEBUG
 1521         if (debug & DEBUG_NEWSRC)
 1522             debug_print_comment("expand_bitmap: group->newsrc.xbitmap == NULL");
 1523 #endif /* DEBUG */
 1524     } else if (need_full_copy) {
 1525         t_bitmap *newbitmap = my_malloc(BITS_TO_BYTES(bitlen));
 1526 
 1527         /* Copy over old bitmap */
 1528         /* TODO: change to use shift */
 1529         for (tmp = group->newsrc.xmin; tmp <= group->newsrc.xmax; tmp++) {
 1530             if (NTEST(group->newsrc.xbitmap, tmp - group->newsrc.xmin) == ART_READ)
 1531                 NSET0(newbitmap, tmp - first);
 1532             else
 1533                 NSET1(newbitmap, tmp - first);
 1534         }
 1535 
 1536         /* Mark earlier articles as read, updating num_unread */
 1537 
 1538         if (first < group->newsrc.xmin) {
 1539             NSETRNG0(newbitmap, T_ARTNUM_CONST(0), group->newsrc.xmin - first - T_ARTNUM_CONST(1));
 1540         }
 1541 
 1542         for (tmp = group->newsrc.xmin; tmp < min; tmp++) {
 1543             if (NTEST(newbitmap, tmp - first) != ART_READ) {
 1544                 NSET0(newbitmap, tmp - first);
 1545                 if (group->newsrc.num_unread)
 1546                     group->newsrc.num_unread--;
 1547             }
 1548         }
 1549 
 1550         /* Mark high numbered articles as unread */
 1551 
 1552         if (group->newsrc.xmin - first + group->newsrc.xbitlen < bitlen) {
 1553             tmp = group->newsrc.xmin - first + group->newsrc.xbitlen;
 1554             NSETRNG1(newbitmap, tmp, bitlen - 1);
 1555         }
 1556 
 1557         free(group->newsrc.xbitmap);
 1558         group->newsrc.xbitmap = newbitmap;
 1559 #ifdef DEBUG
 1560         if (debug & DEBUG_NEWSRC)
 1561             debug_print_comment("expand_bitmap: group->newsrc.bitlen != (group->max-group->min)+1 and need full copy");
 1562 #endif /* DEBUG */
 1563     } else if (max != group->newsrc.xmax || first != group->newsrc.xmin) {
 1564         t_bitmap *newbitmap;
 1565         newbitmap = my_malloc(BITS_TO_BYTES(bitlen));
 1566 
 1567         /* Copy over old bitmap */
 1568 
 1569         assert((group->newsrc.xmin - first) / NBITS + BITS_TO_BYTES(group->newsrc.xbitlen) <= BITS_TO_BYTES(bitlen));
 1570 
 1571         memcpy(newbitmap + (group->newsrc.xmin - first) / NBITS, group->newsrc.xbitmap, BITS_TO_BYTES(group->newsrc.xbitlen));
 1572 
 1573         /* Mark earlier articles as read, updating num_unread */
 1574 
 1575         if (first < group->newsrc.xmin) {
 1576             NSETRNG0(newbitmap, T_ARTNUM_CONST(0), group->newsrc.xmin - first - T_ARTNUM_CONST(1));
 1577         }
 1578 
 1579         for (tmp = group->newsrc.xmin; tmp < min; tmp++) {
 1580             if (NTEST(newbitmap, tmp - first) != ART_READ) {
 1581                 NSET0(newbitmap, tmp - first);
 1582                 if (group->newsrc.num_unread)
 1583                     group->newsrc.num_unread--;
 1584             }
 1585         }
 1586 
 1587         /* Mark high numbered articles as unread */
 1588 
 1589         if (group->newsrc.xmin - first + group->newsrc.xbitlen < bitlen) {
 1590             tmp = group->newsrc.xmin - first + group->newsrc.xbitlen;
 1591             NSETRNG1(newbitmap, tmp, bitlen - 1);
 1592         }
 1593 
 1594         free(group->newsrc.xbitmap);
 1595         group->newsrc.xbitmap = newbitmap;
 1596 #ifdef DEBUG
 1597         if (debug & DEBUG_NEWSRC)
 1598             debug_print_comment("expand_bitmap: group->newsrc.bitlen != (group->max-group->min)+1");
 1599 #endif /* DEBUG */
 1600     }
 1601     group->newsrc.xmin = first;
 1602     if (group->newsrc.xmax < max)
 1603         group->newsrc.num_unread += max - group->newsrc.xmax;
 1604     group->newsrc.xmax = max;
 1605     group->newsrc.xbitlen = bitlen;
 1606     group->newsrc.present = TRUE;
 1607 }
 1608 
 1609 
 1610 void
 1611 art_mark(
 1612     struct t_group *group,
 1613     struct t_article *art,
 1614     int flag)
 1615 {
 1616     if (art == NULL)
 1617         return;
 1618 
 1619     switch (flag) {
 1620         case ART_READ:
 1621             if (group != NULL) {
 1622                 if (art->artnum >= group->newsrc.xmin && art->artnum <= group->newsrc.xmax)
 1623                     NSET0(group->newsrc.xbitmap, art->artnum - group->newsrc.xmin);
 1624 #ifdef DEBUG
 1625                 if (debug & DEBUG_NEWSRC)
 1626                     debug_print_bitmap(group, art);
 1627 #endif /* DEBUG */
 1628             }
 1629             if ((art->status == ART_UNREAD) || (art->status == ART_WILL_RETURN)) {
 1630                 art_mark_xref_read(art);
 1631 
 1632                 if (group != NULL) {
 1633                     if (group->newsrc.num_unread)
 1634                         group->newsrc.num_unread--;
 1635 
 1636                     if (group->attribute->show_only_unread_arts)
 1637                         art->keep_in_base = TRUE;
 1638                 }
 1639 
 1640                 art->status = ART_READ;
 1641             }
 1642             break;
 1643 
 1644         case ART_UNREAD:
 1645         case ART_WILL_RETURN:
 1646             if (art->status == ART_READ) {
 1647                 if (group != NULL) {
 1648                     group->newsrc.num_unread++;
 1649 
 1650                     if (group->attribute->show_only_unread_arts)
 1651                         art->keep_in_base = FALSE;
 1652                 }
 1653 
 1654                 art->status = flag;
 1655             }
 1656             if (group != NULL) {
 1657                 if (art->artnum < group->newsrc.xmin)
 1658                     expand_bitmap(group, art->artnum);
 1659                 else {
 1660                     NSET1(group->newsrc.xbitmap, art->artnum - group->newsrc.xmin);
 1661 #ifdef DEBUG
 1662                     if (debug & DEBUG_NEWSRC)
 1663                         debug_print_bitmap(group, art);
 1664 #endif /* DEBUG */
 1665                 }
 1666             }
 1667             break;
 1668 
 1669         default:
 1670             break;
 1671     }
 1672 }
 1673 
 1674 
 1675 void
 1676 set_default_bitmap(
 1677     struct t_group *group)
 1678 {
 1679     if (group != NULL) {
 1680         group->newsrc.num_unread = 0;
 1681         group->newsrc.present = FALSE;
 1682 
 1683         FreeIfNeeded(group->newsrc.xbitmap);
 1684 
 1685         group->newsrc.xbitmap = (t_bitmap *) 0;
 1686         group->newsrc.xbitlen = 0;
 1687         if (group->xmin > 0)
 1688             group->newsrc.xmin = group->xmin;
 1689         else
 1690             group->newsrc.xmin = 1;
 1691         group->newsrc.xmax = group->newsrc.xmin - 1;
 1692     }
 1693 }