"Fossies" - the Fresh Open Source Software Archive

Member "tin-2.4.1/src/newsrc.c" (12 Oct 2016, 39379 Bytes) of archive /linux/misc/tin-2.4.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "newsrc.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.4.0_vs_2.4.1.

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