"Fossies" - the Fresh Open Source Software Archive

Member "tin-2.4.5/src/newsrc.c" (1 Dec 2020, 40342 Bytes) of package /linux/misc/tin-2.4.5.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.4.4_vs_2.4.5.

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