home *** CD-ROM | disk | FTP | other *** search
- /*
- * (c) Copyright 1990, Kim Fabricius Storm. All rights reserved.
- *
- * .newsrc parsing and update.
- */
-
- #include "config.h"
- #include "options.h"
- #include "regexp.h"
- #include "term.h"
- #include "articles.h"
-
- #define TR(arg) printf arg
-
- import char *news_lib_directory, *db_directory;
- import char *pname;
-
- import int verbose;
- import int silent;
-
- export int keep_rc_backup = 1;
- export char *bak_suffix = ".bak";
-
- export int no_update = 0;
- export int use_selections = 1;
- export int quick_unread_count = 1; /* make a quick count of unread art. */
- export int newsrc_update_freq = 1; /* how often to write .newsrc */
- export char *newsrc_file = NULL;
-
- #define RCX_NEVER 0 /* ignore missing groups */
- #define RCX_HEAD 1 /* prepend missing groups to .newsrc when read */
- #define RCX_TAIL 2 /* append missing groups to .newsrc when read */
- #define RCX_TIME 3 /* append NEW groups as they arrive */
- #define RCX_TIME_CONF 4 /* append NEW groups with confirmation */
- #define RCX_RNLAST 5 /* .rnlast compatible functionality */
-
- export int new_group_action = RCX_TIME; /* append new groups to .newsrc */
- export int keep_unsubscribed = 1; /* keep unsubscribed groups in .newsrc */
- export int keep_unsub_long = 0; /* keep unread in unsubscribed groups */
-
- export int tidy_newsrc = 0; /* remove obsolete groups from .newsrc */
-
- export int auto_junk_seen = 1; /* junk seen articles ... */
- export int conf_junk_seen = 0; /* ... if confirmed by user ... */
- export int retain_seen_status = 0; /* ... or remember seen articles. */
-
- export long unread_articles; /* estimate of unread articles */
- export int unread_groups;
-
- export group_header *rc_sequence = NULL;
-
- static char *sel_path = NULL;
-
-
- /* delimitors on newsrc lines */
-
- #define RC_SUBSCR ':' /* subscription to group */
- #define RC_UNSUBSCR '!' /* no subscription to group */
-
- #define RC_DELIM ',' /* separator on rc lines */
- #define RC_RANGE '-' /* range */
-
- /* delimitors on select lines */
-
- #define SEL_RANGE '-' /* range */
- #define SEL_SELECT ',' /* following articles are selected */
- #define SEL_LEAVE '+' /* following articles are left over */
- #define SEL_SEEN ';' /* following articles are seen */
- #define SEL_UNREAD '~' /* in digests */
- #define SEL_DIGEST '(' /* start digest list */
- #define SEL_END_DIGEST ')' /* end digest list */
- #define SEL_NEW '&' /* new group (group.name&nnn) */
-
- #define END_OF_LIST 10000000L /* Greater than any article number */
-
- /* line buffers */
-
- #define RC_LINE_MAX 8192
-
- static char rcbuf[RC_LINE_MAX];
- static char selbuf[RC_LINE_MAX];
-
- static group_header *rc_seq_tail = NULL;
-
- static int newsrc_update_count = 0, select_update_count = 0;
-
- #define DM_NEWSRC 0
- #define DM_SELECT 1
- #define DM_ORIG_NEWSRC 2
- #define DM_ORIG_SELECT 3
-
- static dump_file(path, mode)
- char *path;
- int mode;
- {
- FILE *f = NULL;
- register group_header *gh;
- char *line;
-
- Loop_Groups_Newsrc(gh) {
- switch (mode) {
- case DM_NEWSRC:
- if (tidy_newsrc) {
- if ((gh->master_flag & M_VALID) == 0)
- continue;
- if (!keep_unsubscribed && (gh->group_flag & G_UNSUBSCRIBED))
- continue;
- }
- line = gh->newsrc_line;
- break;
- case DM_SELECT:
- if (tidy_newsrc && (gh->master_flag & M_VALID) == 0) continue;
- if (gh->group_flag & G_UNSUBSCRIBED) continue;
- line = gh->select_line;
- break;
- case DM_ORIG_NEWSRC:
- line = gh->newsrc_orig;
- break;
- case DM_ORIG_SELECT:
- line = gh->select_orig;
- break;
- }
- if (line == NULL) continue;
- if (f == NULL)
- f = open_file(path, OPEN_CREATE|MUST_EXIST);
- fputs(line, f);
- }
- if (f != NULL) fclose(f);
- }
-
-
- static dump_newsrc()
- {
- char bak[FILENAME];
- static int first = 1;
-
- if (no_update) return;
- if (++newsrc_update_count < newsrc_update_freq) return;
-
- if (first && keep_rc_backup) {
- sprintf(bak, "%s%s", newsrc_file, bak_suffix);
- dump_file(bak, DM_ORIG_NEWSRC);
- first = 0;
- }
-
- dump_file(newsrc_file, DM_NEWSRC);
-
- newsrc_update_count = 0;
- }
-
- static dump_select()
- {
- char bak[FILENAME];
- static int first = 1;
-
- if (no_update) return;
- if (++select_update_count < newsrc_update_freq) return;
-
- if (first && keep_rc_backup) {
- sprintf(bak, "%s%s", sel_path, bak_suffix);
- dump_file(bak, DM_ORIG_SELECT);
- first = 0;
- }
-
- dump_file(sel_path, DM_SELECT);
-
- select_update_count = 0;
- }
-
- #define RN_LAST_GROUP_READ 0
- #define RN_LAST_TIME_RUN 1
- #define RN_LAST_ACTIVE_SIZE 2
- #define RN_LAST_CREATION_TIME 3
- #define RN_LAST_NEW_GROUP 4
- #define RN_ACTIVE_TIMES_OFFSET 5
-
- #define MAX_RNLAST_LINE 6
-
- static char *rnlast_line[MAX_RNLAST_LINE];
- static char *rnlast_path;
-
- static time_t get_last_new()
- {
- FILE *lf = NULL;
- char buf[FILENAME];
- register int i;
-
- if (new_group_action == RCX_RNLAST) {
- rnlast_path = home_relative(".rnlast");
- lf = open_file(rnlast_path, OPEN_READ);
- if (lf == NULL) goto no_file;
-
- for (i = 0; i < MAX_RNLAST_LINE; i++) {
- if (fgets(buf, FILENAME, lf) == NULL) break;
- rnlast_line[i] = copy_str(buf);
- }
- if (i != MAX_RNLAST_LINE) {
- printf(".rnlast only supported with active.times patches\n");
- sleep(3);
- new_group_action = RCX_TIME_CONF;
- goto no_file;
- }
- fclose(lf);
- return (time_t)atol(rnlast_line[RN_LAST_CREATION_TIME]);
- }
-
- lf = open_file(relative(nn_directory, "LAST"), OPEN_READ);
- if (lf == NULL) goto no_file;
- if (fgets(buf, FILENAME, lf) == NULL) goto no_file;
-
- fclose(lf);
- return (time_t)atol(buf);
-
- no_file:
- if (lf != NULL) fclose(lf);
- return (time_t)(-1);
- }
-
- static update_last_new(lastg)
- group_header *lastg;
- {
- FILE *lf = NULL;
- register int i;
- struct stat st;
-
- if (new_group_action == RCX_RNLAST) {
- lf = open_file(rnlast_path, OPEN_CREATE|MUST_EXIST);
- fputs(rnlast_line[RN_LAST_GROUP_READ], lf); /* as good as any */
- fprintf(lf, "%ld\n", (long)cur_time()); /* RN_LAST_TIME_RUN */
- fprintf(lf, "%ld\n", (long)master.last_size); /* RN_LAST_ACTIVE_SIZE */
-
- fprintf(lf, "%ld\n", (long)lastg->creation_time); /* RN_LAST_CREATION_TIME */
- fprintf(lf, "%s\n",lastg->group_name); /* RN_LAST_NEW_GROUP */
-
- if (stat(relative(news_lib_directory, "active.times"), &st) == 0)
- fprintf(lf, "%ld\n", (long)st.st_size);
- else /* can't be perfect -- don't update */
- fputs(rnlast_line[RN_ACTIVE_TIMES_OFFSET], lf);
- for (i = 0; i < MAX_RNLAST_LINE; i++) freeobj(rnlast_line[i]);
- freeobj(rnlast_path);
- } else {
- lf = open_file(relative(nn_directory, "LAST"), OPEN_CREATE|MUST_EXIST);
- fprintf(lf, "%ld\n%s\n", (long)lastg->creation_time, lastg->group_name);
- }
-
- fclose(lf);
- }
-
- static article_number get_last_article(gh)
- group_header *gh;
- {
- register char *line;
-
- if ((line = gh->newsrc_line) == NULL) return -1;
-
- line += gh->group_name_length+1;
- while (*line && isspace(*line)) line++;
- if (*line == NUL) return -1;
-
- if (line[0] == '1') {
- if (line[1] == RC_RANGE)
- return atol(line+2);
- if (!isdigit(line[1])) return 1;
- }
- return 0;
- }
-
-
- visit_rc_file()
- {
- FILE *rc, *sel;
- register group_header *gh;
- int subscr;
- register char *bp;
- register int c;
- char bak[FILENAME];
- time_t last_new_group = 0, rc_age, newsrc_age;
- group_header *last_new_gh = NULL;
-
- if (newsrc_file == NULL)
- newsrc_file = home_relative(".newsrc");
-
- sel_path = mk_file_name(nn_directory, "select");
-
- Loop_Groups_Header(gh) {
- gh->newsrc_line = NULL;
- gh->newsrc_orig = NULL;
- gh->select_line = NULL;
- gh->select_orig = NULL;
- }
-
- if (rc_age = file_exist(relative(nn_directory, "rc"), (char *)NULL)) {
- if (who_am_i != I_AM_NN)
- user_error("A release 6.3 rc file exists. Run nn to upgrade");
-
- sprintf(bak, "%s/upgrade_rc", lib_directory);
-
- if ((newsrc_age = file_exist(newsrc_file, (char *)NULL)) == 0) {
- display_file("adm.upgrade1", CLEAR_DISPLAY);
- } else {
- if (rc_age + 60 > newsrc_age) {
- /* rc file is newest (or .newsrc does not exist) */
- display_file("adm.upgrade2", CLEAR_DISPLAY);
- prompt("Convert rc file to .newsrc now? ");
- if (yes(1) <= 0) nn_exit(0);
- } else {
- /* .newsrc file is newest */
- display_file("adm.upgrade3", CLEAR_DISPLAY);
- prompt("Use current .newsrc file? ");
- if (yes(1) > 0) {
- strcat(bak, " n");
- } else {
- display_file("adm.upgrade4", CLEAR_DISPLAY);
- prompt("Convert rc file to .newsrc? ");
- if (yes(1) <= 0) {
- printf("Then you will have to upgrade manually\n");
- nn_exit(0);
- }
- }
- }
- }
-
- printf("\r\n\n");
- system(bak);
- any_key(prompt_line);
- }
-
- rc = open_file(newsrc_file, OPEN_READ);
- if (rc == NULL) goto new_user;
-
- while (fgets(rcbuf, RC_LINE_MAX, rc) != NULL) {
- gh = NULL;
- subscr = 0;
- for (bp = rcbuf; (c = *bp); bp++) {
- if (isspace(c)) break; /* not a valid line */
-
- if (c == RC_UNSUBSCR || c == RC_SUBSCR) {
- subscr = (c == RC_SUBSCR);
- *bp = NUL;
- gh = lookup(rcbuf);
- if (gh == NULL) {
- gh = newobj(group_header, 1);
- gh->group_name = copy_str(rcbuf); /* invalid group! */
- }
- *bp = c;
- break;
- }
- }
-
- if (gh == NULL) {
- gh = newobj(group_header, 1);
- gh->group_flag |= G_FAKED;
- gh->master_flag |= M_VALID;
- }
-
- if (rc_seq_tail == NULL)
- rc_sequence = rc_seq_tail = gh;
- else {
- rc_seq_tail->newsrc_seq = gh;
- rc_seq_tail = gh;
- }
-
- gh->newsrc_orig = gh->newsrc_line = copy_str(rcbuf);
- if (gh->group_flag & G_FAKED)
- gh->group_name = gh->newsrc_line;
- else
- if (!subscr)
- gh->group_flag |= G_UNSUBSCRIBED;
- }
- fclose(rc);
-
- new_user:
- rc = NULL;
- Loop_Groups_Header(gh) {
- if (gh->master_flag & M_IGNORE_GROUP) continue;
- if (gh->group_flag & G_UNSUBSCRIBED) continue;
- if (gh->newsrc_line == NULL) {
- char buf[FILENAME];
-
- /* NEW GROUP - ADD TO NEWSRC AS APPROPRIATE */
-
- if (new_group_action == RCX_NEVER) {
- gh->group_flag |= G_DONE; /* will not enter sequence */
- continue;
- }
-
- switch (new_group_action) {
- case RCX_NEVER:
- /* no not add new groups */
- gh->group_flag |= G_DONE;
- continue;
-
- case RCX_HEAD:
- /* insert at top */
- gh->newsrc_seq = rc_sequence;
- rc_sequence = gh;
- break;
-
-
- case RCX_TIME:
- case RCX_TIME_CONF:
- case RCX_RNLAST:
- if (last_new_group == 0)
- last_new_group = get_last_new();
-
- if (gh->creation_time <= last_new_group) {
- /* old groups not in .newsrc are unsubscribed */
- gh->group_flag |= G_UNSUBSCRIBED;
- continue;
- }
-
- if (last_new_gh == NULL || last_new_gh->creation_time <= gh->creation_time)
- last_new_gh = gh;
-
- if (new_group_action != RCX_TIME) {
- printf("\nNew group: %s -- append to .newsrc? (y)");
- if (yes(0) <= 0) continue;
- }
- sprintf(buf, "%s:\n", gh->group_name);
- /* to avoid fooling the LAST mechanism, we must fake */
- /* that the group was also in the original .newsrc */
-
- gh->newsrc_orig = gh->newsrc_line = copy_str(buf);
- newsrc_update_count++;
-
- /* fall thru */
-
- case RCX_TAIL:
- /* insert at bottom */
- if (rc_seq_tail == NULL)
- rc_sequence = rc_seq_tail = gh;
- else {
- rc_seq_tail->newsrc_seq = gh;
- rc_seq_tail = gh;
- }
- break;
- }
-
- gh->last_article = -1;
- } else
- gh->last_article = get_last_article(gh);
-
- if (gh->last_article < 0) {
- gh->group_flag |= G_NEW;
- gh->last_article = gh->first_db_article - 1;
- } else
- if (gh->first_db_article > gh->last_article)
- gh->last_article = gh->first_db_article - 1;
-
- if (gh->last_article < 0) gh->last_article = 0;
- gh->first_article = gh->last_article;
- }
-
- if (rc_seq_tail)
- rc_seq_tail->newsrc_seq = NULL;
-
- if (last_new_gh != NULL)
- update_last_new(last_new_gh);
-
- if (!use_selections) return;
-
- sel = open_file(sel_path, OPEN_READ);
- if (sel == NULL) return;
-
- while (fgets(selbuf, RC_LINE_MAX, sel) != NULL) {
- gh = NULL;
- for (bp = selbuf; (c = *bp); bp++)
- if (c == SP || c == SEL_NEW) break;
-
- if (c == NUL) continue;
- *bp = NUL;
- gh = lookup(selbuf);
- if (gh == NULL) continue;
- *bp = c;
- if (c == SEL_NEW) gh->group_flag |= G_NEW;
- gh->select_orig = gh->select_line = copy_str(selbuf);
- }
- fclose(sel);
- }
-
- /*
- * prepare to use newsrc & select information for a specific group
- */
-
- static char *rc_p; /* pointer into newsrc_line */
- static article_number rc_min; /* current newsrc range min */
- static article_number rc_max; /* current newsrc range max */
- static char rc_delim; /* delimiter character */
-
- static char *sel_p; /* pointer into select_line */
- static char *sel_initp; /* rc_p after initialization */
- static article_number sel_min; /* current select range min */
- static article_number sel_max; /* current select range max */
- static article_number sel_digest; /* current digest */
- static attr_type sel_type; /* current select range type */
- static char sel_delim; /* delimiter character */
-
-
- use_newsrc(gh, use_orig)
- register group_header *gh;
- int use_orig;
- {
- /* TR( ("===%s===", gh->group_name) );*/
-
- if (use_orig) {
- rc_p = gh->newsrc_orig;
- sel_p = gh->select_orig;
- } else {
- rc_p = gh->newsrc_line;
- sel_p = gh->select_line;
- }
-
- if (rc_p == NULL) {
- rc_min = rc_max = END_OF_LIST;
- } else {
- rc_min = rc_max = -1;
- rc_delim = SP;
- rc_p += gh->group_name_length + 1;
- }
-
- sel_digest = 0;
- if (sel_p == NULL) {
- sel_min = sel_max = END_OF_LIST;
- } else {
- sel_p += gh->group_name_length + 1;
- sel_min = sel_max = -1;
- sel_delim = SP;
- }
- }
- /*
- #define TRC(wh) TR( ("r%d>%-8.8s< %ld %ld %ld %c\n", wh, p ? p : "***", n, rc_min, rc_max, rc_delim) )
- #define TSEL(wh) TR( ("s%d>%-8.8s< %ld %ld %ld %c\n", wh, p ? p : "***", n, sel_min, sel_max, sel_delim) )
- */
- #define TRC(wh)
- #define TSEL(wh)
-
- attr_type test_article(ah)
- register article_header *ah;
- {
- register char *p;
- register int c;
- register int32 n = ah->a_number, x;
-
- while (n > rc_max) {
- /* get next interval from newsrc line */
- rc_min = -1;
- x = 0;
- p = rc_p;
- TRC(1);
-
- if (*p == RC_DELIM) p++;
- if (*p == NUL || *p == NL)
- rc_min = rc_max = END_OF_LIST;
- else {
- for ( ; (c = *p) && c != RC_DELIM && c != NL; p++) {
- if (c == RC_RANGE) {
- if (rc_min < 0)
- rc_min = x;
- else
- msg("syntax error in rc file");
- x = 0;
- continue;
- }
-
- if (isascii(*p) && isdigit(*p))
- x = x*10 + c - '0';
- }
- rc_max = x;
- if (rc_min < 0) rc_min = x;
- rc_p = p;
- }
- }
- TRC(2);
-
- if (n >= rc_min && n <= rc_max) return A_READ;
-
- p = sel_p;
- if (sel_digest != 0) {
- if (n == sel_digest && (ah->flag & A_DIGEST)) {
- if (*sel_p == SEL_END_DIGEST) return A_READ;
- n = ah->fpos;
- } else {
- if (n < sel_digest) return 0;
- while (*p && *p++ != SEL_END_DIGEST);
- sel_digest = 0;
- sel_min = sel_max = -1;
- }
- }
-
- while (n > sel_max) {
- sel_min = -1;
- sel_type = A_SELECT;
- x = 0;
- TSEL(3);
-
- for (;;) {
- switch (*p) {
- case SEL_SELECT:
- sel_type = A_SELECT;
- p++;
- continue;
- case SEL_LEAVE:
- sel_type = A_LEAVE;
- p++;
- continue;
- case SEL_SEEN:
- sel_type = A_SEEN;
- p++;
- continue;
- case SEL_UNREAD:
- sel_type = 0;
- p++;
- continue;
- case SEL_DIGEST:
- while (*p && *p++ != SEL_END_DIGEST);
- continue;
- case SEL_END_DIGEST:
- if (sel_digest) {
- if (sel_digest == ah->a_number) {
- sel_p = p;
- return A_READ;
- }
- sel_digest = 0;
- }
- p++;
- sel_type = A_SELECT;
- continue;
- default:
- break;
- }
- break;
- }
-
- if (*p == NUL || *p == NL) {
- sel_min = sel_max = END_OF_LIST;
- break;
- }
-
- for ( ; c = *p ; p++ ) {
- switch (c) {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- x = x*10 + c - '0';
- continue;
-
- case SEL_SELECT:
- case SEL_LEAVE:
- case SEL_SEEN:
- case SEL_UNREAD:
- break;
-
- case SEL_RANGE:
- if (sel_min < 0)
- sel_min = x;
- else
- msg("syntax error in sel file");
- x = 0;
- continue;
-
- case SEL_DIGEST:
- n = ah->a_number;
- if (n > x) {
- while (*p && (*p++ != SEL_END_DIGEST));
- x = -1;
- break;
- }
- p++;
- sel_digest = x;
- if (n < sel_digest) {
- sel_p = p;
- return 0;
- }
- n = ah->fpos;
- x = -1;
- break;
-
- case NL:
- if (sel_digest == 0) break;
- /* fall thru */
- case SEL_END_DIGEST:
- if (sel_digest == ah->a_number) {
- sel_p = p;
- return (ah->fpos == x) ? sel_type : A_READ;
- }
- sel_digest = 0;
- x = -1;
- break;
- }
- break;
- }
- sel_max = x;
- if (sel_min < 0) sel_min = x;
- sel_p = p;
- }
-
- if (n >= sel_min && n <= sel_max) return sel_type;
-
- if (sel_digest) return A_READ; /* only read articles are not listed */
-
- return 0; /* unread, unseen, unselected */
- }
-
- /*
- * We only mark the articles that should remain unread
- */
-
- /*VARARGS*/
- static append(va_alist)
- va_dcl
- {
- int x;
- register char *p;
- char **pp, *fmt;
- use_vararg;
-
- start_vararg;
- x = va_arg1(int);
- pp = x ? &sel_p : &rc_p;
- p = *pp;
- if (p > (x ? &selbuf[RC_LINE_MAX - 16] : &rcbuf[RC_LINE_MAX - 16])) {
- msg("%s line too long", x ? "select" : ".newsrc");
- end_vararg;
- return;
- }
- fmt = va_arg2(char *);
- vsprintf(p, fmt, va_args3toN);
- end_vararg;
-
- while (*p) p++;
- *p = NL;
- p[1] = NUL;
- *pp = p;
- }
-
- static append_range(pp, delim, rmin, rmax)
- int pp;
- char delim;
- article_number rmin, rmax;
- {
- if (rmin == rmax)
- append(pp, "%c%ld", delim, (long)rmin);
- else
- append(pp, "%c%ld%c%ld", delim, (long)rmin, RC_RANGE, (long)rmax);
- }
-
- static int32 mark_counter;
-
- static begin_rc_update(gh)
- register group_header *gh;
- {
- add_unread(gh, -1);
- mark_counter = 0;
-
- rc_p = rcbuf;
- rc_min = 1;
- append(0, "%s%c", gh->group_name,
- gh->group_flag & G_UNSUBSCRIBED ? RC_UNSUBSCR : RC_SUBSCR);
- rc_delim = SP;
- sel_p = selbuf;
- sel_min = 0;
- sel_max = 0;
- sel_digest = 0;
- sel_delim = SP;
- append(1, "%s%c", gh->group_name,
- (gh->group_flag & G_NEW) ? SEL_NEW : SP);
- /* sel_initp == sep_p => empty list */
- sel_initp = (gh->group_flag & G_NEW) ? NULL : sel_p;
- }
-
- static end_rc_update(gh)
- register group_header *gh;
- {
- if (rc_min <= gh->last_db_article)
- append_range(0, rc_delim, rc_min, gh->last_db_article);
-
- if (gh->newsrc_line != NULL && strcmp(rcbuf, gh->newsrc_line)) {
- if (gh->newsrc_orig != gh->newsrc_line)
- freeobj(gh->newsrc_line);
- gh->newsrc_line = NULL;
- }
-
- if (gh->newsrc_line == NULL) {
- gh->newsrc_line = copy_str(rcbuf);
- dump_newsrc();
- }
-
- if (sel_digest)
- append(1, "%c", SEL_END_DIGEST);
- else
- if (sel_min)
- append_range(1, sel_delim, sel_min, sel_max);
-
- if (gh->select_line) {
- if (strcmp(selbuf, gh->select_line) == 0) goto out;
- } else
- if (sel_p == sel_initp) goto out;
-
- if (gh->select_line && gh->select_orig != gh->select_line)
- freeobj(gh->select_line);
-
- gh->select_line = (sel_p == sel_initp) ? NULL : copy_str(selbuf);
- dump_select();
-
- out:
- if ((gh->last_article = get_last_article(gh)) < 0)
- gh->last_article = 0;
-
- gh->group_flag |= G_READ; /* should not call update_group again */
- if (mark_counter > 0) {
- gh->unread_count = mark_counter;
- add_unread(gh, 0);
- }
- }
-
- static mark_article(ah, how)
- register article_header *ah;
- attr_type how;
- {
- register article_number anum;
- char delim;
-
- switch (how) {
- case A_SELECT:
- delim = SEL_SELECT;
- break;
- case A_SEEN:
- delim = SEL_SEEN;
- break;
- case A_LEAVE:
- case A_LEAVE_NEXT:
- delim = SEL_LEAVE;
- break;
- case 0:
- delim = SEL_UNREAD;
- break;
- }
-
- mark_counter++;
- anum = ah->a_number;
-
- if (rc_min < anum) {
- append_range(0, rc_delim, rc_min, anum - 1);
- rc_delim = RC_DELIM;
-
- if ((ah->flag & A_DIGEST) == 0
- && sel_min && delim == sel_delim && sel_max == (rc_min - 1))
- sel_max = anum - 1; /* expand select range over read articles */
- }
- rc_min = anum + 1;
-
- if (ah->flag & A_DIGEST) {
- if (sel_digest != anum) {
- if (sel_digest) {
- append(1, "%c", SEL_END_DIGEST);
- } else
- if (sel_min) {
- append_range(1, sel_delim, sel_min, sel_max);
- sel_min = 0;
- }
- append(1, "%c%ld%c", SEL_SELECT, (long)anum, SEL_DIGEST);
- sel_digest = anum;
- }
-
- append(1, "%c%ld", delim, (long)ah->fpos);
- return;
- }
-
- if (sel_digest) {
- append(1, "%c", SEL_END_DIGEST);
- sel_digest = 0;
- }
-
- if (sel_min) {
- if (delim != sel_delim || delim == SEL_UNREAD) {
- append_range(1, sel_delim, sel_min, sel_max);
- sel_delim = delim;
- if (delim == SEL_UNREAD)
- sel_min = 0;
- else
- sel_min = anum;
- } else
- sel_max = anum;
- } else
- if (delim != SEL_UNREAD) {
- sel_min = sel_max = anum;
- sel_delim = delim;
- }
- }
-
- flush_newsrc()
- {
- newsrc_update_freq = 0;
- if (select_update_count) dump_select();
- if (newsrc_update_count) dump_newsrc();
- }
-
- restore_bak()
- {
- if (no_update)
- return 1;
-
- prompt("Are you sure? ");
- if (!yes(1)) return 0;
-
- dump_file(newsrc_file, DM_ORIG_NEWSRC);
-
- prompt("Restore selections? ");
- if (yes(1)) dump_file(sel_path, DM_ORIG_SELECT);
-
- no_update = 1; /* so current group is not updated */
- return 1;
- }
-
- /*
- * Update .newsrc for one group.
- * sort_articles(0) MUST HAVE BEEN CALLED BEFORE USE.
- */
-
- update_rc(gh)
- register group_header *gh;
- {
- register article_header *ah, **ahp;
- register article_number art;
- register int junk_seen = 0;
-
- if (gh->group_flag & (G_FOLDER | G_FAKED)) return;
-
- begin_rc_update(gh);
-
- for (ahp = articles, art = 0; art < n_articles; ahp++, art++) {
- ah = *ahp;
- if (ah->a_group != NULL && ah->a_group != gh) continue;
-
- switch (ah->attr) {
- case A_READ:
- case A_KILL:
- continue;
-
- case A_LEAVE:
- case A_LEAVE_NEXT:
- case A_SELECT:
- mark_article(ah, ah->attr);
- continue;
-
- case A_SEEN:
- if (junk_seen == 0) {
- junk_seen = -1;
- if (auto_junk_seen) {
- if (conf_junk_seen) {
- prompt("\1Junk seen articles\1 ");
- if (yes(0) > 0) junk_seen = 1;
- } else
- junk_seen = 1;
- }
- }
- if (junk_seen > 0) continue;
- mark_article(ah, (attr_type)(retain_seen_status ? A_SEEN : 0));
- continue;
-
- case A_AUTO_SELECT:
- default:
- mark_article(ah, (attr_type)0);
- continue;
- }
- }
-
- end_rc_update(gh);
- }
-
- update_rc_all(gh, unsub)
- register group_header *gh;
- int unsub;
- {
- if (unsub) {
- gh->group_flag &= ~G_NEW;
- gh->group_flag |= G_UNSUBSCRIBED;
-
- if (!keep_unsubscribed) {
- add_unread(gh, -1);
- if (gh->newsrc_line != NULL && gh->newsrc_orig != gh->newsrc_line)
- freeobj(gh->newsrc_line);
- gh->newsrc_line = NULL;
- return;
- }
-
- if (keep_unsub_long) {
- update_rc(gh);
- return;
- }
- }
-
- begin_rc_update(gh);
- end_rc_update(gh);
- }
-
- add_to_newsrc(gh)
- group_header *gh;
- {
- gh->group_flag &= ~G_UNSUBSCRIBED;
-
- if (gh->newsrc_seq != NULL || gh == rc_seq_tail) {
- update_rc(gh);
- return;
- }
-
- rc_seq_tail->newsrc_seq = gh;
- rc_seq_tail = gh;
- if (gh->last_db_article > 0)
- sprintf(rcbuf, "%s: %s%ld\n", gh->group_name,
- gh->last_db_article > 1 ? "1-" : "",
- (long)gh->last_db_article);
- else
- sprintf(rcbuf, "%s:\n", gh->group_name);
- gh->newsrc_line = copy_str(rcbuf);
- dump_newsrc();
- }
-
- int32 restore_rc(gh, last)
- register group_header *gh;
- article_number last;
- {
- register article_number *numtab, n;
- register attr_type *attrtab, attr;
- register int32 at, atmax;
- article_header ahdr;
- int32 count;
-
- if (last > gh->last_db_article) return 0;
-
- if (gh->unread_count <= 0) {
- /* no unread articles to account for -- quick update */
- n = gh->last_db_article; /* fake for end_rc_update */
- gh->last_db_article = last;
- begin_rc_update(gh);
- end_rc_update(gh);
- gh->last_db_article = n;
- add_unread(gh, 1); /* not done by end_rc_update bec. mark_counter==0 */
- return gh->unread_count;
- }
-
- /* there are unread articles in the group */
- /* we must truncate rc&select lines to retain older unread articles */
-
- atmax = at = 0;
- numtab = NULL;
- attrtab = NULL;
-
- use_newsrc(gh, 0);
- ahdr.flag = 0;
- count = gh->unread_count;
-
- for (n = gh->last_article + 1; n <= last; n++) {
- if (rc_min == END_OF_LIST) {
- /* current & rest is unread */
- last = n - 1;
- break;
- }
- ahdr.a_number = n;
- if ((attr = test_article(&ahdr)) == A_READ) continue;
- if (at >= atmax) {
- atmax += 100;
- numtab = resizeobj(numtab, article_number, atmax);
- attrtab = resizeobj(attrtab, attr_type, atmax);
- }
- numtab[at] = n;
- attrtab[at] = attr;
- at++;
- }
-
- begin_rc_update(gh);
- while (--at >= 0) {
- ahdr.a_number = *numtab++;
- mark_article(&ahdr, *attrtab++);
- }
- for (n = last+1; n <= gh->last_db_article; n++) {
- ahdr.a_number = n;
- mark_article(&ahdr, (attr_type)0);
- }
- end_rc_update(gh);
- return gh->unread_count - count;
- }
-
- restore_unread(gh)
- register group_header *gh;
- {
- if (gh->select_line != gh->select_orig) {
- if (gh->select_line != NULL) freeobj(gh->select_line);
- gh->select_line = gh->select_orig;
- dump_select();
- }
-
- if (gh->newsrc_orig == gh->newsrc_line) return 0;
-
- add_unread(gh, -1);
- if (gh->newsrc_line != NULL) freeobj(gh->newsrc_line);
- gh->newsrc_line = gh->newsrc_orig;
- gh->last_article = gh->first_article;
- dump_newsrc();
-
- add_unread(gh, 1);
-
- return 1;
- }
-
-
- count_unread_articles()
- {
- register group_header *gh;
- long n;
-
- unread_articles = 0;
- unread_groups = 0;
-
- Loop_Groups_Sequence(gh) {
- gh->unread_count = 0;
-
- if (gh->master_flag & M_NO_DIRECTORY) continue;
-
- if (gh->last_db_article > gh->last_article) {
- n = unread_articles;
- add_unread(gh, 1);
- }
-
- if ((gh->group_flag & G_COUNTED) == 0) continue;
- if (verbose)
- printf("%6d %s\n", unread_articles - n, gh->group_name);
- }
- }
-
-
- prt_unread(format)
- register char *format;
- {
- if (format == NULL) {
- printf("No News (is good news)\n");
- return;
- }
-
- while (*format) {
- if (*format != '%') {
- putchar(*format++);
- continue;
- }
- format++;
- switch (*format++) {
- case 'u':
- printf("%ld unread article%s", unread_articles, plural((long)unread_articles));
- continue;
- case 'g':
- printf("%d group%s", unread_groups, plural((long)unread_groups));
- continue;
- case 'i':
- printf(unread_articles == 1 ? "is" : "are");
- continue;
- case 'U':
- printf("%ld", unread_articles);
- continue;
- case 'G':
- printf("%d", unread_groups);
- continue;
- }
- }
- }
-
-
- add_unread(gh, mode)
- group_header *gh;
- int mode; /* +1 => count + add, 0 => gh->unread_count, -1 => subtract */
- {
- int32 old_count;
- article_header ahdr;
-
- old_count = gh->unread_count;
-
- if (mode == 0) goto add_directly;
-
- if (gh->group_flag & G_COUNTED) {
- unread_articles -= gh->unread_count;
- unread_groups --;
- gh->unread_count = 0;
- gh->group_flag &= ~G_COUNTED;
- }
-
- if (mode < 0) goto out;
-
- if (quick_unread_count)
- gh->unread_count = gh->last_db_article - gh->last_article;
- else {
- use_newsrc(gh, 0);
- ahdr.flag = 0;
- for (ahdr.a_number = gh->last_article + 1;
- ahdr.a_number <= gh->last_db_article;
- ahdr.a_number++) {
- if (rc_min == END_OF_LIST) {
- gh->unread_count += gh->last_db_article - ahdr.a_number + 1;
- break;
- }
- if (test_article(&ahdr) != A_READ)
- gh->unread_count++;
- }
- }
-
- add_directly:
- if (gh->unread_count <= 0) {
- gh->unread_count = 0;
- goto out;
- }
-
- if (gh->group_flag & G_UNSUBSCRIBED) goto out;
-
- unread_articles += gh->unread_count;
- unread_groups++;
- gh->group_flag |= G_COUNTED;
-
- out:
- return old_count != gh->unread_count;
- }
-
- /*
- * nngrep
- */
-
- static int
- grep_all = 0,
- grep_new = 0,
- grep_not_sequence = 0,
- grep_pending = 0,
- grep_read = 0,
- grep_sequence = 0,
- grep_unsub = 0,
- grep_long = 0,
- grep_patterns;
-
- Option_Description(grep_options) {
- 'a', Bool_Option(grep_all),
- 'i', Bool_Option(grep_not_sequence),
- 'n', Bool_Option(grep_new),
- 'p', Bool_Option(grep_pending),
- 'r', Bool_Option(grep_read),
- 's', Bool_Option(grep_sequence),
- 'u', Bool_Option(grep_unsub),
- 'l', Bool_Option(grep_long),
- '\0',
- };
-
- opt_nngrep(argc, argv)
- int argc;
- char *argv[];
- {
- grep_patterns =
- parse_options(argc, argv, (char *)NULL, grep_options, " pattern...");
- }
-
- do_grep(pat)
- char **pat;
- {
- register group_header *gh;
- register regexp **re;
- register int i;
- int header = 1;
-
- re = newobj(regexp *, grep_patterns);
- for (i = 0; i < grep_patterns; i++)
- re[i] = regcomp(pat[i]);
-
- Loop_Groups_Sorted(gh) {
- if (gh->master_flag & M_IGNORE_GROUP) continue;
-
- if (grep_pending && gh->unread_count <= 0) continue;
- if (grep_read && gh->unread_count > 0) continue;
- if (grep_sequence && (gh->group_flag & G_SEQUENCE) == 0) continue;
- if (grep_not_sequence && (gh->group_flag & G_SEQUENCE)) continue;
- if (grep_new && (gh->group_flag & G_NEW) == 0) continue;
- if (!grep_all) {
- if (grep_unsub && (gh->group_flag & G_UNSUBSCRIBED) == 0) continue;
- if (!grep_unsub && (gh->group_flag & G_UNSUBSCRIBED)) continue;
- }
-
- if (grep_patterns > 0) {
- for (i = 0; i < grep_patterns; i++)
- if (regexec(re[i], gh->group_name)) break;
- if (i == grep_patterns) continue;
- }
-
- if (grep_long) {
- if (header)
- printf("SUBSCR NEW UNREAD SEQUENCE GROUP\n");
- header = 0;
-
- printf(" %s %s ",
- (gh->group_flag & G_UNSUBSCRIBED) ? "no " : "yes",
- (gh->group_flag & G_NEW) ? "yes" : "no ");
-
- if (gh->unread_count > 0)
- printf("%6d ", gh->unread_count);
- else
- printf(" ");
- if (gh->group_flag & G_SEQUENCE)
- printf(" %4d ", gh->preseq_index);
- else
- printf(" ");
- }
-
- printf("%s\n", gh->group_name);
- }
- }
-
-
- /*
- * nntidy
- */
-
- static int
- tidy_unsubscribed = 0, /* truncate lines for unsub groups*/
- tidy_remove_unsub = 0, /* remove lines for unsub groups*/
- tidy_sequence = 0, /* remove groups not in sequence */
- tidy_ignored = 0, /* remove G_IGN groups */
- tidy_crap = 0, /* remove unrecognized lines */
- tidy_all = 0; /* all of the above */
-
- Option_Description(tidy_options) {
- 'N', Bool_Option(no_update),
- 'Q', Bool_Option(silent),
- 'v', Bool_Option(verbose),
- 'a', Bool_Option(tidy_all),
- 'c', Bool_Option(tidy_crap),
- 'i', Bool_Option(tidy_ignored),
- 'r', Bool_Option(tidy_remove_unsub),
- 's', Bool_Option(tidy_sequence),
- 'u', Bool_Option(tidy_unsubscribed),
- '\0',
- };
-
- opt_nntidy(argc, argv)
- int argc;
- char *argv[];
- {
- return parse_options(argc, argv, (char *)NULL,
- tidy_options, " [group]...");
- }
-
- do_tidy_newsrc()
- {
- register group_header *gh;
- int changed;
- char *why;
-
- /* visit_rc_file has been called. */
-
- keep_rc_backup = 1;
- bak_suffix = ".tidy";
-
- tidy_newsrc = 0;
- changed = 0;
-
- if (tidy_all)
- tidy_sequence = tidy_ignored = tidy_crap = tidy_unsubscribed = 1;
-
- newsrc_update_freq = 9999;
-
- Loop_Groups_Newsrc(gh) {
- if ((gh->master_flag & M_VALID) == 0) {
- why = "Unknown group: ";
- goto delete;
- }
- if (tidy_sequence && (gh->group_flag & G_SEQUENCE) == 0) {
- why = "Not in sequence: ";
- goto delete;
- }
- if (tidy_ignored && (gh->master_flag & M_IGNORE_GROUP)) {
- why = "Ignored group: ";
- goto delete;
- }
- if (tidy_crap && (gh->group_flag & G_FAKED)) {
- why = "Crap in .newsrc: ";
- goto delete;
- }
- if (tidy_remove_unsub && (gh->group_flag & G_UNSUBSCRIBED)) {
- if (gh->group_flag & G_FAKED) continue;
- why = "Unsubscribed: ";
- goto delete;
- }
-
- if (tidy_unsubscribed && (gh->group_flag & G_UNSUBSCRIBED)) {
- if (gh->group_flag & G_FAKED) continue;
-
- begin_rc_update(gh);
- gh->last_db_article = 0;
- end_rc_update(gh);
-
- if (gh->newsrc_line != gh->newsrc_orig) {
- why = "Truncated: ";
- goto change;
- }
- }
- if (verbose) {
- why = "Ok: ";
- goto report;
- }
- continue;
-
- delete:
- gh->newsrc_line = NULL;
- gh->select_line = NULL;
-
- change:
- changed = 1;
-
- report:
- if (!silent) printf("%s%s\n", why, gh->group_name);
- }
-
- if (changed) {
- newsrc_update_freq = 0;
- dump_newsrc();
- dump_select();
- printf("NOTICE: Original files are saved with %s extention\n", bak_suffix);
- }
- }
-
- /*
- * nngoback
- */
-
- static int
- goback_interact = 0, /* interactive nngoback */
- goback_days = -1,
- goback_alsounsub = 0; /* unsubscribed groups also */
-
- Option_Description(goback_options) {
- 'N', Bool_Option(no_update),
- 'Q', Bool_Option(silent),
- 'd', Int_Option(goback_days),
- 'i', Bool_Option(goback_interact),
- 'u', Bool_Option(goback_alsounsub),
- 'v', Bool_Option(verbose),
- '\0',
- };
-
- opt_nngoback(argc, argvp)
- int argc;
- char ***argvp;
- {
- int n;
-
- n = parse_options(argc, *argvp, (char *)NULL, goback_options,
- " days [groups]...");
-
- if (goback_days < 0) {
- if (n == 0 || !isdigit((*argvp)[1][0])) {
- fprintf(stderr, "usage: %s [-NQvi] days [groups]...\n", pname);
- nn_exit(1);
- }
- goback_days = atoi((*argvp)[1]);
- n--;
- ++*argvp;
- }
- return n;
- }
-
- do_goback()
- {
- char back_act[FILENAME];
- FILE *ba;
- register group_header *gh;
- int32 count, total;
- int groups, y;
-
- sprintf(back_act, "%s/active.%d", db_directory, goback_days);
- if ((ba = open_file(back_act, OPEN_READ)) == NULL) {
- fprintf(stderr, "Cannot go back %d days\n", goback_days);
- nn_exit(1);
- }
-
- read_active_file(ba, (FILE *)NULL);
-
- fclose(ba);
-
- /* visit_rc_file has been called. */
-
- keep_rc_backup = 1;
- bak_suffix = ".goback";
- newsrc_update_freq = 9999;
- quick_unread_count = 0;
- total = groups = 0;
-
- if (goback_interact) {
- init_term();
- raw();
- }
-
- Loop_Groups_Sequence(gh) {
- if ((gh->master_flag & M_VALID) == 0) continue;
- if (!goback_alsounsub && (gh->group_flag & G_UNSUBSCRIBED)) continue;
-
- add_unread(gh, 1);
-
- count = restore_rc(gh, gh->last_a_article);
- if (count > 0) {
- if (goback_interact) {
- printf("%s + %ld ? (y) ", gh->group_name, (long)count); fl;
- y = yes(0);
- putchar(CR); putchar(NL);
- switch (y) {
- case 1:
- break;
- case 0:
- gh->newsrc_line = gh->newsrc_orig;
- gh->select_line = gh->select_orig;
- continue;
- case -1:
- if (total > 0) {
- printf("\nSave changes sofar? (n) "); fl;
- if (yes(1) <= 0) nn_exit(0);
- }
- goto out;
- }
- } else
- if (verbose)
- printf("%5ld\t%s\n", (long)count, gh->group_name);
-
- total += count;
- groups++;
- }
- }
-
- out:
-
- if (total == 0) {
- printf("No articles marked\n");
- return;
- }
-
- flush_newsrc();
-
- if (verbose) putchar(NL);
- if (!silent)
- printf("%ld article%s marked unread in %d group%s\n",
- (long)total, plural((long)total),
- groups, plural((long)groups));
- }
-
- /* fake this for read_active_file */
-
- group_header *add_new_group(name)
- char *name;
- {
- return NULL;
- }
-