home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume22 / nn6.4 / part05 / newsrc.c next >
Encoding:
C/C++ Source or Header  |  1990-06-07  |  33.9 KB  |  1,558 lines

  1. /*
  2.  *    (c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
  3.  *
  4.  *    .newsrc parsing and update.
  5.  */
  6.  
  7. #include "config.h"
  8. #include "options.h"
  9. #include "regexp.h"
  10. #include "term.h"
  11. #include "articles.h"
  12.  
  13. #define TR(arg) printf arg
  14.  
  15. import char *news_lib_directory, *db_directory;
  16. import char *pname;
  17.  
  18. import int  verbose;
  19. import int  silent;
  20.  
  21. export int  keep_rc_backup = 1;
  22. export char *bak_suffix = ".bak";
  23.  
  24. export int  no_update = 0;
  25. export int  use_selections = 1;
  26. export int  quick_unread_count = 1; /* make a quick count of unread art. */
  27. export int  newsrc_update_freq = 1; /* how often to write .newsrc */
  28. export char *newsrc_file = NULL;
  29.  
  30. #define RCX_NEVER    0 /* ignore missing groups */
  31. #define RCX_HEAD    1 /* prepend missing groups to .newsrc when read */
  32. #define    RCX_TAIL    2 /* append missing groups to .newsrc when read */
  33. #define RCX_TIME    3 /* append NEW groups as they arrive */
  34. #define RCX_TIME_CONF    4 /* append NEW groups with confirmation */
  35. #define RCX_RNLAST    5 /* .rnlast compatible functionality */
  36.  
  37. export int  new_group_action = RCX_TIME; /* append new groups to .newsrc */
  38. export int  keep_unsubscribed = 1; /* keep unsubscribed groups in .newsrc */
  39. export int  keep_unsub_long = 0; /* keep unread in unsubscribed groups */
  40.  
  41. export int  tidy_newsrc = 0;       /* remove obsolete groups from .newsrc */
  42.  
  43. export int  auto_junk_seen = 1;    /* junk seen articles ... */
  44. export int  conf_junk_seen = 0; /* ... if confirmed by user ... */
  45. export int  retain_seen_status = 0; /* ... or remember seen articles. */
  46.  
  47. export long unread_articles;    /* estimate of unread articles */
  48. export int  unread_groups;
  49.  
  50. export group_header *rc_sequence = NULL;
  51.  
  52. static char *sel_path = NULL;
  53.  
  54.  
  55. /* delimitors on newsrc lines */
  56.  
  57. #define RC_SUBSCR    ':'    /* subscription to group */
  58. #define RC_UNSUBSCR    '!'    /* no subscription to group */
  59.  
  60. #define RC_DELIM    ','    /* separator on rc lines */
  61. #define RC_RANGE    '-'    /* range */
  62.  
  63. /* delimitors on select lines */
  64.  
  65. #define SEL_RANGE    '-'    /* range */
  66. #define SEL_SELECT    ','    /* following articles are selected */
  67. #define SEL_LEAVE    '+'    /* following articles are left over */
  68. #define SEL_SEEN    ';'    /* following articles are seen */
  69. #define SEL_UNREAD    '~'    /* in digests */
  70. #define SEL_DIGEST    '('    /* start digest list */
  71. #define SEL_END_DIGEST    ')'    /* end digest list */
  72. #define SEL_NEW        '&'    /* new group (group.name&nnn) */
  73.  
  74. #define END_OF_LIST    10000000L /* Greater than any article number */
  75.  
  76. /* line buffers */
  77.  
  78. #define RC_LINE_MAX    8192
  79.  
  80. static char rcbuf[RC_LINE_MAX];
  81. static char selbuf[RC_LINE_MAX];
  82.  
  83. static group_header *rc_seq_tail = NULL;
  84.  
  85. static int newsrc_update_count = 0, select_update_count = 0;
  86.  
  87. #define DM_NEWSRC    0
  88. #define DM_SELECT    1
  89. #define DM_ORIG_NEWSRC    2
  90. #define DM_ORIG_SELECT    3
  91.  
  92. static dump_file(path, mode)
  93. char *path;
  94. int mode;
  95. {
  96.     FILE *f = NULL;
  97.     register group_header *gh;
  98.     char *line;
  99.  
  100.     Loop_Groups_Newsrc(gh) {
  101.     switch (mode) {
  102.      case DM_NEWSRC:
  103.         if (tidy_newsrc) {
  104.         if ((gh->master_flag & M_VALID) == 0)
  105.             continue;
  106.         if (!keep_unsubscribed && (gh->group_flag & G_UNSUBSCRIBED))
  107.             continue;
  108.         }
  109.         line = gh->newsrc_line;
  110.         break;
  111.      case DM_SELECT:
  112.         if (tidy_newsrc && (gh->master_flag & M_VALID) == 0) continue;
  113.         if (gh->group_flag & G_UNSUBSCRIBED) continue;
  114.         line = gh->select_line;
  115.         break;
  116.      case DM_ORIG_NEWSRC:
  117.         line = gh->newsrc_orig;
  118.         break;
  119.      case DM_ORIG_SELECT:
  120.         line = gh->select_orig;
  121.         break;
  122.     }
  123.     if (line == NULL) continue;
  124.     if (f == NULL)
  125.         f = open_file(path, OPEN_CREATE|MUST_EXIST);
  126.     fputs(line, f);
  127.     }
  128.     if (f != NULL) fclose(f);
  129. }
  130.  
  131.  
  132. static dump_newsrc()
  133. {
  134.     char bak[FILENAME];
  135.     static int first = 1;
  136.  
  137.     if (no_update) return;
  138.     if (++newsrc_update_count < newsrc_update_freq) return;
  139.  
  140.     if (first && keep_rc_backup) {
  141.     sprintf(bak, "%s%s", newsrc_file, bak_suffix);
  142.     dump_file(bak, DM_ORIG_NEWSRC);
  143.     first = 0;
  144.     }
  145.  
  146.     dump_file(newsrc_file, DM_NEWSRC);
  147.  
  148.     newsrc_update_count = 0;
  149. }
  150.  
  151. static dump_select()
  152. {
  153.     char bak[FILENAME];
  154.     static int first = 1;
  155.  
  156.     if (no_update) return;
  157.     if (++select_update_count < newsrc_update_freq) return;
  158.  
  159.     if (first && keep_rc_backup) {
  160.     sprintf(bak, "%s%s", sel_path, bak_suffix);
  161.     dump_file(bak, DM_ORIG_SELECT);
  162.     first = 0;
  163.     }
  164.  
  165.     dump_file(sel_path, DM_SELECT);
  166.  
  167.     select_update_count = 0;
  168. }
  169.  
  170. #define RN_LAST_GROUP_READ    0
  171. #define RN_LAST_TIME_RUN    1
  172. #define RN_LAST_ACTIVE_SIZE    2
  173. #define RN_LAST_CREATION_TIME    3
  174. #define RN_LAST_NEW_GROUP    4
  175. #define RN_ACTIVE_TIMES_OFFSET    5
  176.  
  177. #define MAX_RNLAST_LINE 6
  178.  
  179. static char *rnlast_line[MAX_RNLAST_LINE];
  180. static char *rnlast_path;
  181.  
  182. static time_t get_last_new()
  183. {
  184.     FILE *lf = NULL;
  185.     char buf[FILENAME];
  186.     register int i;
  187.  
  188.     if (new_group_action == RCX_RNLAST) {
  189.     rnlast_path = home_relative(".rnlast");
  190.     lf = open_file(rnlast_path, OPEN_READ);
  191.     if (lf == NULL) goto no_file;
  192.  
  193.     for (i = 0; i < MAX_RNLAST_LINE; i++) {
  194.         if (fgets(buf, FILENAME, lf) == NULL) break;
  195.         rnlast_line[i] = copy_str(buf);
  196.     }
  197.     if (i != MAX_RNLAST_LINE) {
  198.         printf(".rnlast only supported with active.times patches\n");
  199.         sleep(3);
  200.         new_group_action = RCX_TIME_CONF;
  201.         goto no_file;
  202.     }
  203.     fclose(lf);
  204.     return (time_t)atol(rnlast_line[RN_LAST_CREATION_TIME]);
  205.     }
  206.  
  207.     lf = open_file(relative(nn_directory, "LAST"), OPEN_READ);
  208.     if (lf == NULL) goto no_file;
  209.     if (fgets(buf, FILENAME, lf) == NULL) goto no_file;
  210.  
  211.     fclose(lf);
  212.     return (time_t)atol(buf);
  213.  
  214.  no_file:
  215.     if (lf != NULL) fclose(lf);
  216.     return (time_t)(-1);
  217. }
  218.  
  219. static update_last_new(lastg)
  220. group_header *lastg;
  221. {
  222.     FILE *lf = NULL;
  223.     register int i;
  224.     struct stat st;
  225.  
  226.     if (new_group_action == RCX_RNLAST) {
  227.     lf = open_file(rnlast_path, OPEN_CREATE|MUST_EXIST);
  228.     fputs(rnlast_line[RN_LAST_GROUP_READ], lf);    /* as good as any */
  229.     fprintf(lf, "%ld\n", (long)cur_time()); /* RN_LAST_TIME_RUN */
  230.     fprintf(lf, "%ld\n", (long)master.last_size); /* RN_LAST_ACTIVE_SIZE */
  231.  
  232.     fprintf(lf, "%ld\n", (long)lastg->creation_time); /* RN_LAST_CREATION_TIME */
  233.     fprintf(lf, "%s\n",lastg->group_name); /* RN_LAST_NEW_GROUP */
  234.  
  235.     if (stat(relative(news_lib_directory, "active.times"), &st) == 0)
  236.         fprintf(lf, "%ld\n", (long)st.st_size);
  237.     else /* can't be perfect -- don't update */
  238.         fputs(rnlast_line[RN_ACTIVE_TIMES_OFFSET], lf);
  239.     for (i = 0; i < MAX_RNLAST_LINE; i++) freeobj(rnlast_line[i]);
  240.     freeobj(rnlast_path);
  241.     } else {
  242.     lf = open_file(relative(nn_directory, "LAST"), OPEN_CREATE|MUST_EXIST);
  243.     fprintf(lf, "%ld\n%s\n", (long)lastg->creation_time, lastg->group_name);
  244.     }
  245.  
  246.     fclose(lf);
  247. }
  248.  
  249. static article_number get_last_article(gh)
  250. group_header *gh;
  251. {
  252.     register char *line;
  253.  
  254.     if ((line = gh->newsrc_line) == NULL) return -1;
  255.  
  256.     line += gh->group_name_length+1;
  257.     while (*line && isspace(*line)) line++;
  258.     if (*line == NUL) return -1;
  259.  
  260.     if (line[0] == '1') {
  261.     if (line[1] == RC_RANGE)
  262.         return atol(line+2);
  263.     if (!isdigit(line[1])) return 1;
  264.     }
  265.     return 0;
  266. }
  267.  
  268.  
  269. visit_rc_file()
  270. {
  271.     FILE *rc, *sel;
  272.     register group_header *gh;
  273.     int subscr;
  274.     register char *bp;
  275.     register int c;
  276.     char bak[FILENAME];
  277.     time_t last_new_group = 0, rc_age, newsrc_age;
  278.     group_header *last_new_gh = NULL;
  279.  
  280.     if (newsrc_file == NULL)
  281.     newsrc_file = home_relative(".newsrc");
  282.  
  283.     sel_path = mk_file_name(nn_directory, "select");
  284.  
  285.     Loop_Groups_Header(gh) {
  286.     gh->newsrc_line = NULL;
  287.     gh->newsrc_orig = NULL;
  288.     gh->select_line = NULL;
  289.     gh->select_orig = NULL;
  290.     }
  291.  
  292.     if (rc_age = file_exist(relative(nn_directory, "rc"), (char *)NULL)) {
  293.     if (who_am_i != I_AM_NN)
  294.         user_error("A release 6.3 rc file exists. Run nn to upgrade");
  295.  
  296.     sprintf(bak, "%s/upgrade_rc", lib_directory);
  297.  
  298.     if ((newsrc_age = file_exist(newsrc_file, (char *)NULL)) == 0) {
  299.         display_file("adm.upgrade1", CLEAR_DISPLAY);
  300.     } else {
  301.         if (rc_age + 60 > newsrc_age) {
  302.         /* rc file is newest (or .newsrc does not exist) */
  303.         display_file("adm.upgrade2", CLEAR_DISPLAY);
  304.         prompt("Convert rc file to .newsrc now? ");
  305.         if (yes(1) <= 0) nn_exit(0);
  306.         } else {
  307.         /* .newsrc file is newest */
  308.         display_file("adm.upgrade3", CLEAR_DISPLAY);
  309.         prompt("Use current .newsrc file? ");
  310.         if (yes(1) > 0) {
  311.             strcat(bak, " n");
  312.         } else {
  313.             display_file("adm.upgrade4", CLEAR_DISPLAY);
  314.             prompt("Convert rc file to .newsrc? ");
  315.             if (yes(1) <= 0) {
  316.             printf("Then you will have to upgrade manually\n");
  317.             nn_exit(0);
  318.             }
  319.         }
  320.         }
  321.     }
  322.  
  323.     printf("\r\n\n");
  324.     system(bak);
  325.     any_key(prompt_line);
  326.     }
  327.  
  328.     rc = open_file(newsrc_file, OPEN_READ);
  329.     if (rc == NULL) goto new_user;
  330.  
  331.     while (fgets(rcbuf, RC_LINE_MAX, rc) != NULL) {
  332.     gh = NULL;
  333.     subscr = 0;
  334.     for (bp = rcbuf; (c = *bp); bp++) {
  335.         if (isspace(c)) break;    /* not a valid line */
  336.  
  337.         if (c == RC_UNSUBSCR || c == RC_SUBSCR) {
  338.         subscr = (c == RC_SUBSCR);
  339.         *bp = NUL;
  340.         gh = lookup(rcbuf);
  341.         if (gh == NULL) {
  342.             gh = newobj(group_header, 1);
  343.             gh->group_name = copy_str(rcbuf); /* invalid group! */
  344.         }
  345.         *bp = c;
  346.         break;
  347.         }
  348.     }
  349.  
  350.     if (gh == NULL) {
  351.         gh = newobj(group_header, 1);
  352.         gh->group_flag |= G_FAKED;
  353.         gh->master_flag |= M_VALID;
  354.     }
  355.  
  356.     if (rc_seq_tail == NULL)
  357.         rc_sequence = rc_seq_tail = gh;
  358.     else {
  359.         rc_seq_tail->newsrc_seq = gh;
  360.         rc_seq_tail = gh;
  361.     }
  362.  
  363.     gh->newsrc_orig = gh->newsrc_line = copy_str(rcbuf);
  364.     if (gh->group_flag & G_FAKED)
  365.         gh->group_name = gh->newsrc_line;
  366.     else
  367.         if (!subscr)
  368.         gh->group_flag |= G_UNSUBSCRIBED;
  369.     }
  370.     fclose(rc);
  371.  
  372.  new_user:
  373.     rc = NULL;
  374.     Loop_Groups_Header(gh) {
  375.     if (gh->master_flag & M_IGNORE_GROUP) continue;
  376.     if (gh->group_flag & G_UNSUBSCRIBED) continue;
  377.     if (gh->newsrc_line == NULL) {
  378.         char buf[FILENAME];
  379.  
  380.         /* NEW GROUP - ADD TO NEWSRC AS APPROPRIATE */
  381.  
  382.         if (new_group_action == RCX_NEVER) {
  383.         gh->group_flag |= G_DONE; /* will not enter sequence */
  384.         continue;
  385.         }
  386.  
  387.         switch (new_group_action) {
  388.          case RCX_NEVER:
  389.         /* no not add new groups */
  390.         gh->group_flag |= G_DONE;
  391.         continue;
  392.  
  393.          case RCX_HEAD:
  394.         /* insert at top */
  395.         gh->newsrc_seq = rc_sequence;
  396.         rc_sequence = gh;
  397.         break;
  398.  
  399.  
  400.          case RCX_TIME:
  401.          case RCX_TIME_CONF:
  402.          case RCX_RNLAST:
  403.         if (last_new_group == 0)
  404.             last_new_group = get_last_new();
  405.  
  406.         if (gh->creation_time <= last_new_group) {
  407.             /* old groups not in .newsrc are unsubscribed */
  408.             gh->group_flag |= G_UNSUBSCRIBED;
  409.             continue;
  410.         }
  411.  
  412.         if (last_new_gh == NULL || last_new_gh->creation_time <= gh->creation_time)
  413.             last_new_gh = gh;
  414.  
  415.         if (new_group_action != RCX_TIME) {
  416.             printf("\nNew group: %s -- append to .newsrc? (y)");
  417.             if (yes(0) <= 0) continue;
  418.         }
  419.         sprintf(buf, "%s:\n", gh->group_name);
  420.         /* to avoid fooling the LAST mechanism, we must fake */
  421.         /* that the group was also in the original .newsrc */
  422.  
  423.         gh->newsrc_orig = gh->newsrc_line = copy_str(buf);
  424.         newsrc_update_count++;
  425.  
  426.         /* fall thru */
  427.  
  428.          case RCX_TAIL:
  429.         /* insert at bottom */
  430.         if (rc_seq_tail == NULL)
  431.             rc_sequence = rc_seq_tail = gh;
  432.         else {
  433.             rc_seq_tail->newsrc_seq = gh;
  434.             rc_seq_tail = gh;
  435.         }
  436.         break;
  437.         }
  438.  
  439.         gh->last_article = -1;
  440.     } else
  441.         gh->last_article = get_last_article(gh);
  442.  
  443.     if (gh->last_article < 0) {
  444.         gh->group_flag |= G_NEW;
  445.         gh->last_article = gh->first_db_article - 1;
  446.     } else
  447.         if (gh->first_db_article > gh->last_article)
  448.         gh->last_article = gh->first_db_article - 1;
  449.  
  450.     if (gh->last_article < 0) gh->last_article = 0;
  451.     gh->first_article = gh->last_article;
  452.     }
  453.  
  454.     if (rc_seq_tail)
  455.     rc_seq_tail->newsrc_seq = NULL;
  456.  
  457.     if (last_new_gh != NULL)
  458.     update_last_new(last_new_gh);
  459.  
  460.     if (!use_selections) return;
  461.  
  462.     sel = open_file(sel_path, OPEN_READ);
  463.     if (sel == NULL) return;
  464.  
  465.     while (fgets(selbuf, RC_LINE_MAX, sel) != NULL) {
  466.     gh = NULL;
  467.     for (bp = selbuf; (c = *bp); bp++)
  468.         if (c == SP || c == SEL_NEW) break;
  469.  
  470.     if (c == NUL) continue;
  471.     *bp = NUL;
  472.     gh = lookup(selbuf);
  473.     if (gh == NULL) continue;
  474.     *bp = c;
  475.     if (c == SEL_NEW) gh->group_flag |= G_NEW;
  476.     gh->select_orig = gh->select_line = copy_str(selbuf);
  477.     }
  478.     fclose(sel);
  479. }
  480.  
  481. /*
  482.  * prepare to use newsrc & select information for a specific group
  483.  */
  484.  
  485. static char *rc_p;        /* pointer into newsrc_line */
  486. static article_number rc_min;    /* current newsrc range min */
  487. static article_number rc_max;    /* current newsrc range max */
  488. static char rc_delim;        /* delimiter character */
  489.  
  490. static char *sel_p;        /* pointer into select_line */
  491. static char *sel_initp;        /* rc_p after initialization */
  492. static article_number sel_min;    /* current select range min */
  493. static article_number sel_max;    /* current select range max */
  494. static article_number sel_digest; /* current digest */
  495. static attr_type sel_type;    /* current select range type */
  496. static char sel_delim;        /* delimiter character */
  497.  
  498.  
  499. use_newsrc(gh, use_orig)
  500. register group_header *gh;
  501. int use_orig;
  502. {
  503. /*    TR( ("===%s===", gh->group_name) );*/
  504.  
  505.     if (use_orig) {
  506.     rc_p = gh->newsrc_orig;
  507.     sel_p = gh->select_orig;
  508.     } else {
  509.     rc_p = gh->newsrc_line;
  510.     sel_p = gh->select_line;
  511.     }
  512.  
  513.     if (rc_p == NULL) {
  514.     rc_min = rc_max = END_OF_LIST;
  515.     } else {
  516.     rc_min = rc_max = -1;
  517.     rc_delim = SP;
  518.     rc_p += gh->group_name_length + 1;
  519.     }
  520.  
  521.     sel_digest = 0;
  522.     if (sel_p == NULL) {
  523.     sel_min = sel_max = END_OF_LIST;
  524.     } else {
  525.     sel_p += gh->group_name_length + 1;
  526.     sel_min = sel_max = -1;
  527.     sel_delim = SP;
  528.     }
  529. }
  530. /*
  531. #define TRC(wh)  TR( ("r%d>%-8.8s< %ld %ld %ld %c\n", wh, p ? p : "***", n, rc_min, rc_max, rc_delim) )
  532. #define TSEL(wh) TR( ("s%d>%-8.8s< %ld %ld %ld %c\n", wh, p ? p : "***", n, sel_min, sel_max, sel_delim) )
  533. */
  534. #define TRC(wh)
  535. #define TSEL(wh)
  536.  
  537. attr_type test_article(ah)
  538. register article_header *ah;
  539. {
  540.     register char *p;
  541.     register int c;
  542.     register int32 n = ah->a_number, x;
  543.  
  544.     while (n > rc_max) {
  545.     /* get next interval from newsrc line */
  546.     rc_min = -1;
  547.     x = 0;
  548.     p = rc_p;
  549.     TRC(1);
  550.  
  551.     if (*p == RC_DELIM) p++;
  552.     if (*p == NUL || *p == NL)
  553.         rc_min = rc_max = END_OF_LIST;
  554.     else {
  555.         for ( ; (c = *p) && c != RC_DELIM && c != NL; p++) {
  556.         if (c == RC_RANGE) {
  557.             if (rc_min < 0)
  558.             rc_min = x;
  559.             else
  560.             msg("syntax error in rc file");
  561.             x = 0;
  562.             continue;
  563.         }
  564.  
  565.         if (isascii(*p) && isdigit(*p))
  566.             x = x*10 + c - '0';
  567.         }
  568.         rc_max = x;
  569.         if (rc_min < 0) rc_min = x;
  570.         rc_p = p;
  571.     }
  572.     }
  573.     TRC(2);
  574.  
  575.     if (n >= rc_min && n <= rc_max) return A_READ;
  576.  
  577.     p = sel_p;
  578.     if (sel_digest != 0) {
  579.     if (n == sel_digest && (ah->flag & A_DIGEST)) {
  580.         if (*sel_p == SEL_END_DIGEST) return A_READ;
  581.         n = ah->fpos;
  582.     } else {
  583.         if (n < sel_digest) return 0;
  584.         while (*p && *p++ != SEL_END_DIGEST);
  585.         sel_digest = 0;
  586.         sel_min = sel_max = -1;
  587.     }
  588.     }
  589.  
  590.     while (n > sel_max) {
  591.     sel_min = -1;
  592.     sel_type = A_SELECT;
  593.     x = 0;
  594.     TSEL(3);
  595.  
  596.     for (;;) {
  597.         switch (*p) {
  598.          case SEL_SELECT:
  599.         sel_type = A_SELECT;
  600.         p++;
  601.         continue;
  602.          case SEL_LEAVE:
  603.         sel_type = A_LEAVE;
  604.         p++;
  605.         continue;
  606.          case SEL_SEEN:
  607.         sel_type = A_SEEN;
  608.         p++;
  609.         continue;
  610.          case SEL_UNREAD:
  611.         sel_type = 0;
  612.         p++;
  613.         continue;
  614.          case SEL_DIGEST:
  615.         while (*p && *p++ != SEL_END_DIGEST);
  616.         continue;
  617.          case SEL_END_DIGEST:
  618.         if (sel_digest) {
  619.             if (sel_digest == ah->a_number) {
  620.             sel_p = p;
  621.             return A_READ;
  622.             }
  623.             sel_digest = 0;
  624.         }
  625.         p++;
  626.         sel_type = A_SELECT;
  627.         continue;
  628.          default:
  629.         break;
  630.         }
  631.         break;
  632.     }
  633.  
  634.     if (*p == NUL || *p == NL) {
  635.         sel_min = sel_max = END_OF_LIST;
  636.         break;
  637.     }
  638.  
  639.     for ( ; c = *p ; p++ ) {
  640.         switch (c) {
  641.          case '0':
  642.          case '1':
  643.          case '2':
  644.          case '3':
  645.          case '4':
  646.          case '5':
  647.          case '6':
  648.          case '7':
  649.          case '8':
  650.          case '9':
  651.         x = x*10 + c - '0';
  652.         continue;
  653.  
  654.          case SEL_SELECT:
  655.          case SEL_LEAVE:
  656.          case SEL_SEEN:
  657.          case SEL_UNREAD:
  658.         break;
  659.  
  660.          case SEL_RANGE:
  661.         if (sel_min < 0)
  662.             sel_min = x;
  663.         else
  664.             msg("syntax error in sel file");
  665.         x = 0;
  666.         continue;
  667.  
  668.          case SEL_DIGEST:
  669.         n = ah->a_number;
  670.         if (n > x) {
  671.             while (*p && (*p++ != SEL_END_DIGEST));
  672.             x = -1;
  673.             break;
  674.         }
  675.         p++;
  676.         sel_digest = x;
  677.         if (n < sel_digest) {
  678.             sel_p = p;
  679.             return 0;
  680.         }
  681.         n = ah->fpos;
  682.         x = -1;
  683.         break;
  684.  
  685.          case NL:
  686.         if (sel_digest == 0) break;
  687.         /* fall thru */
  688.          case SEL_END_DIGEST:
  689.         if (sel_digest == ah->a_number) {
  690.             sel_p = p;
  691.             return (ah->fpos == x) ? sel_type : A_READ;
  692.         }
  693.         sel_digest = 0;
  694.         x = -1;
  695.         break;
  696.         }
  697.         break;
  698.     }
  699.     sel_max = x;
  700.     if (sel_min < 0) sel_min = x;
  701.     sel_p = p;
  702.     }
  703.  
  704.     if (n >= sel_min && n <= sel_max) return sel_type;
  705.  
  706.     if (sel_digest) return A_READ; /* only read articles are not listed */
  707.  
  708.     return 0;    /* unread, unseen, unselected */
  709. }
  710.  
  711. /*
  712.  * We only mark the articles that should remain unread
  713.  */
  714.  
  715. /*VARARGS*/
  716. static append(va_alist)
  717. va_dcl
  718. {
  719.     int x;
  720.     register char *p;
  721.     char **pp, *fmt;
  722.     use_vararg;
  723.  
  724.     start_vararg;
  725.     x = va_arg1(int);
  726.     pp = x ? &sel_p : &rc_p;
  727.     p = *pp;
  728.     if (p > (x ? &selbuf[RC_LINE_MAX - 16] : &rcbuf[RC_LINE_MAX - 16])) {
  729.     msg("%s line too long", x ? "select" : ".newsrc");
  730.     end_vararg;
  731.     return;
  732.     }
  733.     fmt = va_arg2(char *);
  734.     vsprintf(p, fmt, va_args3toN);
  735.     end_vararg;
  736.  
  737.     while (*p) p++;
  738.     *p = NL;
  739.     p[1] = NUL;
  740.     *pp = p;
  741. }
  742.  
  743. static append_range(pp, delim, rmin, rmax)
  744. int pp;
  745. char delim;
  746. article_number rmin, rmax;
  747. {
  748.     if (rmin == rmax)
  749.     append(pp, "%c%ld", delim, (long)rmin);
  750.     else
  751.     append(pp, "%c%ld%c%ld", delim, (long)rmin, RC_RANGE, (long)rmax);
  752. }
  753.  
  754. static int32 mark_counter;
  755.  
  756. static begin_rc_update(gh)
  757. register group_header *gh;
  758. {
  759.     add_unread(gh, -1);
  760.     mark_counter = 0;
  761.  
  762.     rc_p = rcbuf;
  763.     rc_min = 1;
  764.     append(0, "%s%c", gh->group_name,
  765.        gh->group_flag & G_UNSUBSCRIBED ? RC_UNSUBSCR : RC_SUBSCR);
  766.     rc_delim = SP;
  767.     sel_p = selbuf;
  768.     sel_min = 0;
  769.     sel_max = 0;
  770.     sel_digest = 0;
  771.     sel_delim = SP;
  772.     append(1, "%s%c", gh->group_name,
  773.        (gh->group_flag & G_NEW) ? SEL_NEW : SP);
  774.     /* sel_initp == sep_p => empty list */
  775.     sel_initp = (gh->group_flag & G_NEW) ? NULL : sel_p;
  776. }
  777.  
  778. static end_rc_update(gh)
  779. register group_header *gh;
  780. {
  781.     if (rc_min <= gh->last_db_article)
  782.     append_range(0, rc_delim, rc_min, gh->last_db_article);
  783.  
  784.     if (gh->newsrc_line != NULL && strcmp(rcbuf, gh->newsrc_line)) {
  785.     if (gh->newsrc_orig != gh->newsrc_line)
  786.         freeobj(gh->newsrc_line);
  787.     gh->newsrc_line = NULL;
  788.     }
  789.  
  790.     if (gh->newsrc_line == NULL) {
  791.     gh->newsrc_line = copy_str(rcbuf);
  792.     dump_newsrc();
  793.     }
  794.  
  795.     if (sel_digest)
  796.     append(1, "%c", SEL_END_DIGEST);
  797.     else
  798.     if (sel_min)
  799.         append_range(1, sel_delim, sel_min, sel_max);
  800.  
  801.     if (gh->select_line) {
  802.     if (strcmp(selbuf, gh->select_line) == 0) goto out;
  803.     } else
  804.         if (sel_p == sel_initp) goto out;
  805.  
  806.     if (gh->select_line && gh->select_orig != gh->select_line)
  807.     freeobj(gh->select_line);
  808.  
  809.     gh->select_line = (sel_p == sel_initp) ? NULL : copy_str(selbuf);
  810.     dump_select();
  811.  
  812.  out:
  813.     if ((gh->last_article = get_last_article(gh)) < 0)
  814.     gh->last_article = 0;
  815.  
  816.     gh->group_flag |= G_READ;    /* should not call update_group again */
  817.     if (mark_counter > 0) {
  818.     gh->unread_count = mark_counter;
  819.     add_unread(gh, 0);
  820.     }
  821. }
  822.  
  823. static mark_article(ah, how)
  824. register article_header *ah;
  825. attr_type how;
  826. {
  827.     register article_number anum;
  828.     char delim;
  829.  
  830.     switch (how) {
  831.      case A_SELECT:
  832.     delim = SEL_SELECT;
  833.     break;
  834.      case A_SEEN:
  835.     delim = SEL_SEEN;
  836.     break;
  837.      case A_LEAVE:
  838.      case A_LEAVE_NEXT:
  839.     delim = SEL_LEAVE;
  840.     break;
  841.      case 0:
  842.     delim = SEL_UNREAD;
  843.     break;
  844.     }
  845.  
  846.     mark_counter++;
  847.     anum = ah->a_number;
  848.  
  849.     if (rc_min < anum) {
  850.     append_range(0, rc_delim, rc_min, anum - 1);
  851.     rc_delim = RC_DELIM;
  852.  
  853.     if ((ah->flag & A_DIGEST) == 0
  854.         && sel_min && delim == sel_delim && sel_max == (rc_min - 1))
  855.         sel_max = anum - 1;    /* expand select range over read articles */
  856.     }
  857.     rc_min = anum + 1;
  858.  
  859.     if (ah->flag & A_DIGEST) {
  860.     if (sel_digest != anum) {
  861.         if (sel_digest) {
  862.         append(1, "%c", SEL_END_DIGEST);
  863.         } else
  864.         if (sel_min) {
  865.             append_range(1, sel_delim, sel_min, sel_max);
  866.             sel_min = 0;
  867.         }
  868.         append(1, "%c%ld%c", SEL_SELECT, (long)anum, SEL_DIGEST);
  869.         sel_digest = anum;
  870.     }
  871.  
  872.     append(1, "%c%ld", delim, (long)ah->fpos);
  873.     return;
  874.     }
  875.  
  876.     if (sel_digest) {
  877.     append(1, "%c", SEL_END_DIGEST);
  878.     sel_digest = 0;
  879.     }
  880.  
  881.     if (sel_min) {
  882.     if (delim != sel_delim || delim == SEL_UNREAD) {
  883.         append_range(1, sel_delim, sel_min, sel_max);
  884.         sel_delim = delim;
  885.         if (delim == SEL_UNREAD)
  886.         sel_min = 0;
  887.         else
  888.         sel_min = anum;
  889.     } else
  890.         sel_max = anum;
  891.     } else
  892.     if (delim != SEL_UNREAD) {
  893.         sel_min = sel_max = anum;
  894.         sel_delim = delim;
  895.     }
  896. }
  897.  
  898. flush_newsrc()
  899. {
  900.     newsrc_update_freq = 0;
  901.     if (select_update_count) dump_select();
  902.     if (newsrc_update_count) dump_newsrc();
  903. }
  904.  
  905. restore_bak()
  906. {
  907.     if (no_update)
  908.     return 1;
  909.  
  910.     prompt("Are you sure? ");
  911.     if (!yes(1)) return 0;
  912.  
  913.     dump_file(newsrc_file, DM_ORIG_NEWSRC);
  914.  
  915.     prompt("Restore selections? ");
  916.     if (yes(1)) dump_file(sel_path, DM_ORIG_SELECT);
  917.  
  918.     no_update = 1;    /* so current group is not updated */
  919.     return 1;
  920. }
  921.  
  922. /*
  923.  *    Update .newsrc for one group.
  924.  *    sort_articles(0) MUST HAVE BEEN CALLED BEFORE USE.
  925.  */
  926.  
  927. update_rc(gh)
  928. register group_header *gh;
  929. {
  930.     register article_header *ah, **ahp;
  931.     register article_number art;
  932.     register int junk_seen = 0;
  933.  
  934.     if (gh->group_flag & (G_FOLDER | G_FAKED)) return;
  935.  
  936.     begin_rc_update(gh);
  937.  
  938.     for (ahp = articles, art = 0; art < n_articles; ahp++, art++) {
  939.     ah = *ahp;
  940.     if (ah->a_group != NULL && ah->a_group != gh) continue;
  941.  
  942.     switch (ah->attr) {
  943.      case A_READ:
  944.      case A_KILL:
  945.         continue;
  946.  
  947.      case A_LEAVE:
  948.      case A_LEAVE_NEXT:
  949.      case A_SELECT:
  950.         mark_article(ah, ah->attr);
  951.         continue;
  952.  
  953.      case A_SEEN:
  954.         if (junk_seen == 0) {
  955.         junk_seen = -1;
  956.         if (auto_junk_seen) {
  957.             if (conf_junk_seen) {
  958.             prompt("\1Junk seen articles\1 ");
  959.             if (yes(0) > 0) junk_seen = 1;
  960.             } else
  961.             junk_seen = 1;
  962.         }
  963.         }
  964.         if (junk_seen > 0) continue;
  965.         mark_article(ah, (attr_type)(retain_seen_status ? A_SEEN : 0));
  966.         continue;
  967.  
  968.      case A_AUTO_SELECT:
  969.      default:
  970.         mark_article(ah, (attr_type)0);
  971.         continue;
  972.     }
  973.     }
  974.  
  975.     end_rc_update(gh);
  976. }
  977.  
  978. update_rc_all(gh, unsub)
  979. register group_header *gh;
  980. int unsub;
  981. {
  982.     if (unsub) {
  983.     gh->group_flag &= ~G_NEW;
  984.     gh->group_flag |= G_UNSUBSCRIBED;
  985.  
  986.     if (!keep_unsubscribed) {
  987.         add_unread(gh, -1);
  988.         if (gh->newsrc_line != NULL && gh->newsrc_orig != gh->newsrc_line)
  989.         freeobj(gh->newsrc_line);
  990.         gh->newsrc_line = NULL;
  991.         return;
  992.     }
  993.     
  994.     if (keep_unsub_long) {
  995.         update_rc(gh);
  996.         return;
  997.     }
  998.     }
  999.  
  1000.     begin_rc_update(gh);
  1001.     end_rc_update(gh);
  1002. }
  1003.  
  1004. add_to_newsrc(gh)
  1005. group_header *gh;
  1006. {
  1007.     gh->group_flag &= ~G_UNSUBSCRIBED;
  1008.  
  1009.     if (gh->newsrc_seq != NULL || gh == rc_seq_tail) {
  1010.     update_rc(gh);
  1011.     return;
  1012.     }
  1013.     
  1014.     rc_seq_tail->newsrc_seq = gh;
  1015.     rc_seq_tail = gh;
  1016.     if (gh->last_db_article > 0)
  1017.     sprintf(rcbuf, "%s: %s%ld\n", gh->group_name,
  1018.         gh->last_db_article > 1 ? "1-" : "",
  1019.         (long)gh->last_db_article);
  1020.     else
  1021.     sprintf(rcbuf, "%s:\n", gh->group_name);
  1022.     gh->newsrc_line = copy_str(rcbuf);
  1023.     dump_newsrc();
  1024. }
  1025.  
  1026. int32 restore_rc(gh, last)
  1027. register group_header *gh;
  1028. article_number last;
  1029. {
  1030.     register article_number *numtab, n;
  1031.     register attr_type *attrtab, attr;
  1032.     register int32 at, atmax;
  1033.     article_header ahdr;
  1034.     int32 count;
  1035.  
  1036.     if (last > gh->last_db_article) return 0;
  1037.  
  1038.     if (gh->unread_count <= 0) {
  1039.     /* no unread articles to account for -- quick update */
  1040.     n = gh->last_db_article;    /* fake for end_rc_update */
  1041.     gh->last_db_article = last;
  1042.     begin_rc_update(gh);
  1043.     end_rc_update(gh);
  1044.     gh->last_db_article = n;
  1045.     add_unread(gh, 1); /* not done by end_rc_update bec. mark_counter==0 */
  1046.     return gh->unread_count;
  1047.     }
  1048.  
  1049.     /* there are unread articles in the group */
  1050.     /* we must truncate rc&select lines to retain older unread articles */
  1051.  
  1052.     atmax = at = 0;
  1053.     numtab = NULL;
  1054.     attrtab = NULL;
  1055.     
  1056.     use_newsrc(gh, 0);
  1057.     ahdr.flag = 0;
  1058.     count = gh->unread_count;
  1059.  
  1060.     for (n = gh->last_article + 1; n <= last; n++) {
  1061.     if (rc_min == END_OF_LIST) {
  1062.         /* current & rest is unread */
  1063.         last = n - 1;
  1064.         break;
  1065.     }
  1066.     ahdr.a_number = n;
  1067.     if ((attr = test_article(&ahdr)) == A_READ) continue;
  1068.     if (at >= atmax) {
  1069.         atmax += 100;
  1070.         numtab = resizeobj(numtab, article_number, atmax);
  1071.         attrtab = resizeobj(attrtab, attr_type, atmax);
  1072.     }
  1073.     numtab[at] = n;
  1074.     attrtab[at] = attr;
  1075.     at++;
  1076.     }
  1077.  
  1078.     begin_rc_update(gh);
  1079.     while (--at >= 0) {
  1080.     ahdr.a_number = *numtab++;
  1081.     mark_article(&ahdr, *attrtab++);
  1082.     }
  1083.     for (n = last+1; n <= gh->last_db_article; n++) {
  1084.     ahdr.a_number = n;
  1085.     mark_article(&ahdr, (attr_type)0);
  1086.     }
  1087.     end_rc_update(gh);
  1088.     return gh->unread_count - count;
  1089. }
  1090.  
  1091. restore_unread(gh)
  1092. register group_header *gh;
  1093. {
  1094.     if (gh->select_line != gh->select_orig) {
  1095.     if (gh->select_line != NULL) freeobj(gh->select_line);
  1096.     gh->select_line = gh->select_orig;
  1097.     dump_select();
  1098.     }
  1099.  
  1100.     if (gh->newsrc_orig == gh->newsrc_line) return 0;
  1101.  
  1102.     add_unread(gh, -1);
  1103.     if (gh->newsrc_line != NULL) freeobj(gh->newsrc_line);
  1104.     gh->newsrc_line = gh->newsrc_orig;
  1105.     gh->last_article = gh->first_article;
  1106.     dump_newsrc();
  1107.  
  1108.     add_unread(gh, 1);
  1109.  
  1110.     return 1;
  1111. }
  1112.  
  1113.  
  1114. count_unread_articles()
  1115. {
  1116.     register group_header *gh;
  1117.     long n;
  1118.  
  1119.     unread_articles = 0;
  1120.     unread_groups = 0;
  1121.  
  1122.     Loop_Groups_Sequence(gh) {
  1123.     gh->unread_count = 0;
  1124.  
  1125.     if (gh->master_flag & M_NO_DIRECTORY) continue;
  1126.  
  1127.     if (gh->last_db_article > gh->last_article) {
  1128.         n = unread_articles;
  1129.         add_unread(gh, 1);
  1130.     }
  1131.     
  1132.     if ((gh->group_flag & G_COUNTED) == 0) continue;
  1133.     if (verbose)
  1134.         printf("%6d %s\n", unread_articles - n, gh->group_name);
  1135.     }
  1136. }
  1137.  
  1138.  
  1139. prt_unread(format)
  1140. register char *format;
  1141. {
  1142.     if (format == NULL) {
  1143.     printf("No News (is good news)\n");
  1144.     return;
  1145.     }
  1146.  
  1147.     while (*format) {
  1148.     if (*format != '%') {
  1149.         putchar(*format++);
  1150.         continue;
  1151.     }
  1152.     format++;
  1153.     switch (*format++) {
  1154.      case 'u':
  1155.         printf("%ld unread article%s", unread_articles, plural((long)unread_articles));
  1156.         continue;
  1157.      case 'g':
  1158.         printf("%d group%s", unread_groups, plural((long)unread_groups));
  1159.         continue;
  1160.      case 'i':
  1161.         printf(unread_articles == 1 ? "is" : "are");
  1162.         continue;
  1163.      case 'U':
  1164.         printf("%ld", unread_articles);
  1165.         continue;
  1166.      case 'G':
  1167.         printf("%d", unread_groups);
  1168.         continue;
  1169.     }
  1170.     }
  1171. }
  1172.  
  1173.  
  1174. add_unread(gh, mode)
  1175. group_header *gh;
  1176. int mode;    /* +1 => count + add, 0 => gh->unread_count, -1 => subtract */
  1177. {
  1178.     int32 old_count;
  1179.     article_header ahdr;
  1180.  
  1181.     old_count = gh->unread_count;
  1182.  
  1183.     if (mode == 0) goto add_directly;
  1184.  
  1185.     if (gh->group_flag & G_COUNTED) {
  1186.     unread_articles -= gh->unread_count;
  1187.     unread_groups --;
  1188.     gh->unread_count = 0;
  1189.     gh->group_flag &= ~G_COUNTED;
  1190.     }
  1191.  
  1192.     if (mode < 0) goto out;
  1193.  
  1194.     if (quick_unread_count)
  1195.     gh->unread_count = gh->last_db_article - gh->last_article;
  1196.     else {
  1197.     use_newsrc(gh, 0);
  1198.     ahdr.flag = 0;
  1199.     for (ahdr.a_number = gh->last_article + 1;
  1200.          ahdr.a_number <= gh->last_db_article;
  1201.          ahdr.a_number++) {
  1202.         if (rc_min == END_OF_LIST) {
  1203.         gh->unread_count += gh->last_db_article - ahdr.a_number + 1;
  1204.         break;
  1205.         }
  1206.         if (test_article(&ahdr) != A_READ)
  1207.         gh->unread_count++;
  1208.     }
  1209.     }
  1210.  
  1211.  add_directly:
  1212.     if (gh->unread_count <= 0) {
  1213.     gh->unread_count = 0;
  1214.     goto out;
  1215.     }
  1216.  
  1217.     if (gh->group_flag & G_UNSUBSCRIBED) goto out;
  1218.  
  1219.     unread_articles += gh->unread_count;
  1220.     unread_groups++;
  1221.     gh->group_flag |= G_COUNTED;
  1222.     
  1223.  out:
  1224.     return old_count != gh->unread_count;
  1225. }
  1226.  
  1227. /*
  1228.  *    nngrep
  1229.  */
  1230.  
  1231. static int
  1232.     grep_all = 0,
  1233.     grep_new = 0,
  1234.     grep_not_sequence = 0,
  1235.     grep_pending = 0,
  1236.     grep_read = 0,
  1237.     grep_sequence = 0,
  1238.     grep_unsub = 0,
  1239.     grep_long = 0,
  1240.     grep_patterns;
  1241.  
  1242. Option_Description(grep_options) {
  1243.     'a', Bool_Option(grep_all),
  1244.     'i', Bool_Option(grep_not_sequence),
  1245.     'n', Bool_Option(grep_new),
  1246.     'p', Bool_Option(grep_pending),
  1247.     'r', Bool_Option(grep_read),
  1248.     's', Bool_Option(grep_sequence),
  1249.     'u', Bool_Option(grep_unsub),
  1250.     'l', Bool_Option(grep_long),
  1251.     '\0',
  1252. };
  1253.  
  1254. opt_nngrep(argc, argv)
  1255. int argc;
  1256. char *argv[];
  1257. {
  1258.     grep_patterns =
  1259.     parse_options(argc, argv, (char *)NULL, grep_options, " pattern...");
  1260. }
  1261.  
  1262. do_grep(pat)
  1263. char **pat;
  1264. {
  1265.     register group_header *gh;
  1266.     register regexp **re;
  1267.     register int i;
  1268.     int header = 1;
  1269.  
  1270.     re = newobj(regexp *, grep_patterns);
  1271.     for (i = 0; i < grep_patterns; i++)
  1272.     re[i] = regcomp(pat[i]);
  1273.  
  1274.     Loop_Groups_Sorted(gh) {
  1275.     if (gh->master_flag & M_IGNORE_GROUP) continue;
  1276.  
  1277.     if (grep_pending && gh->unread_count <= 0) continue;
  1278.     if (grep_read && gh->unread_count > 0) continue;
  1279.     if (grep_sequence && (gh->group_flag & G_SEQUENCE) == 0) continue;
  1280.     if (grep_not_sequence && (gh->group_flag & G_SEQUENCE)) continue;
  1281.     if (grep_new && (gh->group_flag & G_NEW) == 0) continue;
  1282.     if (!grep_all) {
  1283.         if (grep_unsub && (gh->group_flag & G_UNSUBSCRIBED) == 0) continue;
  1284.         if (!grep_unsub && (gh->group_flag & G_UNSUBSCRIBED)) continue;
  1285.     }
  1286.  
  1287.     if (grep_patterns > 0) {
  1288.         for (i = 0; i < grep_patterns; i++)
  1289.         if (regexec(re[i], gh->group_name)) break;
  1290.         if (i == grep_patterns) continue;
  1291.     }
  1292.  
  1293.     if (grep_long) {
  1294.         if (header)
  1295.         printf("SUBSCR NEW UNREAD SEQUENCE GROUP\n");
  1296.         header = 0;
  1297.  
  1298.         printf(" %s   %s ",
  1299.            (gh->group_flag & G_UNSUBSCRIBED) ? "no " : "yes",
  1300.            (gh->group_flag & G_NEW) ? "yes" : "no ");
  1301.  
  1302.         if (gh->unread_count > 0)
  1303.         printf("%6d ", gh->unread_count);
  1304.         else
  1305.         printf("       ");
  1306.         if (gh->group_flag & G_SEQUENCE)
  1307.         printf("  %4d   ", gh->preseq_index);
  1308.         else
  1309.         printf("         ");
  1310.     }
  1311.  
  1312.     printf("%s\n", gh->group_name);
  1313.     }
  1314. }
  1315.  
  1316.  
  1317. /*
  1318.  *    nntidy
  1319.  */
  1320.  
  1321. static int
  1322.     tidy_unsubscribed = 0,    /* truncate lines for unsub groups*/
  1323.     tidy_remove_unsub = 0,    /* remove lines for unsub groups*/
  1324.     tidy_sequence = 0,        /* remove groups not in sequence */
  1325.     tidy_ignored = 0,        /* remove G_IGN groups */
  1326.     tidy_crap = 0,        /* remove unrecognized lines */
  1327.     tidy_all = 0;        /* all of the above */
  1328.  
  1329. Option_Description(tidy_options) {
  1330.     'N', Bool_Option(no_update),
  1331.     'Q', Bool_Option(silent),
  1332.     'v', Bool_Option(verbose),
  1333.     'a', Bool_Option(tidy_all),
  1334.     'c', Bool_Option(tidy_crap),
  1335.     'i', Bool_Option(tidy_ignored),
  1336.     'r', Bool_Option(tidy_remove_unsub),
  1337.     's', Bool_Option(tidy_sequence),
  1338.     'u', Bool_Option(tidy_unsubscribed),
  1339.     '\0',
  1340. };
  1341.  
  1342. opt_nntidy(argc, argv)
  1343. int argc;
  1344. char *argv[];
  1345. {
  1346.     return parse_options(argc, argv, (char *)NULL, 
  1347.              tidy_options, " [group]...");
  1348. }
  1349.  
  1350. do_tidy_newsrc()
  1351. {
  1352.     register group_header *gh;
  1353.     int changed;
  1354.     char *why;
  1355.  
  1356.     /* visit_rc_file has been called. */
  1357.  
  1358.     keep_rc_backup = 1;
  1359.     bak_suffix = ".tidy";
  1360.  
  1361.     tidy_newsrc = 0;
  1362.     changed = 0;
  1363.  
  1364.     if (tidy_all)
  1365.     tidy_sequence = tidy_ignored = tidy_crap = tidy_unsubscribed = 1;
  1366.  
  1367.     newsrc_update_freq = 9999;
  1368.  
  1369.     Loop_Groups_Newsrc(gh) {
  1370.     if ((gh->master_flag & M_VALID) == 0) {
  1371.         why = "Unknown group:   ";
  1372.         goto delete;
  1373.     }
  1374.     if (tidy_sequence && (gh->group_flag & G_SEQUENCE) == 0) {
  1375.         why = "Not in sequence: ";
  1376.         goto delete;
  1377.     }
  1378.     if (tidy_ignored && (gh->master_flag & M_IGNORE_GROUP)) {
  1379.         why = "Ignored group:   ";
  1380.         goto delete;
  1381.     }
  1382.     if (tidy_crap && (gh->group_flag & G_FAKED)) {
  1383.         why = "Crap in .newsrc: ";
  1384.         goto delete;
  1385.     }
  1386.     if (tidy_remove_unsub && (gh->group_flag & G_UNSUBSCRIBED)) {
  1387.         if (gh->group_flag & G_FAKED) continue;
  1388.         why = "Unsubscribed:    ";
  1389.         goto delete;
  1390.     }
  1391.  
  1392.     if (tidy_unsubscribed && (gh->group_flag & G_UNSUBSCRIBED)) {
  1393.         if (gh->group_flag & G_FAKED) continue;
  1394.  
  1395.         begin_rc_update(gh);
  1396.         gh->last_db_article = 0;
  1397.         end_rc_update(gh);
  1398.  
  1399.         if (gh->newsrc_line != gh->newsrc_orig) {
  1400.         why = "Truncated:       ";
  1401.         goto change;
  1402.         }
  1403.     }
  1404.     if (verbose) {
  1405.         why = "Ok:              ";
  1406.         goto report;
  1407.     }
  1408.     continue;
  1409.  
  1410.      delete:
  1411.     gh->newsrc_line = NULL;
  1412.     gh->select_line = NULL;
  1413.  
  1414.      change:
  1415.     changed = 1;
  1416.  
  1417.      report:
  1418.     if (!silent) printf("%s%s\n", why, gh->group_name);
  1419.     }
  1420.  
  1421.     if (changed) {
  1422.     newsrc_update_freq = 0;
  1423.     dump_newsrc();
  1424.     dump_select();
  1425.     printf("NOTICE: Original files are saved with %s extention\n", bak_suffix);
  1426.     }
  1427. }
  1428.  
  1429. /*
  1430.  *    nngoback
  1431.  */
  1432.  
  1433. static int
  1434.     goback_interact = 0, /* interactive nngoback */
  1435.     goback_days = -1,
  1436.     goback_alsounsub = 0; /* unsubscribed groups also */
  1437.  
  1438. Option_Description(goback_options) {
  1439.     'N', Bool_Option(no_update),
  1440.     'Q', Bool_Option(silent),
  1441.     'd', Int_Option(goback_days),
  1442.     'i', Bool_Option(goback_interact),
  1443.     'u', Bool_Option(goback_alsounsub),
  1444.     'v', Bool_Option(verbose),
  1445.     '\0',
  1446. };
  1447.  
  1448. opt_nngoback(argc, argvp)
  1449. int argc;
  1450. char ***argvp;
  1451. {
  1452.     int n;
  1453.     
  1454.     n = parse_options(argc, *argvp, (char *)NULL, goback_options,
  1455.               " days [groups]...");
  1456.  
  1457.     if (goback_days < 0) {
  1458.     if (n == 0 || !isdigit((*argvp)[1][0])) {
  1459.         fprintf(stderr, "usage: %s [-NQvi] days [groups]...\n", pname);
  1460.         nn_exit(1);
  1461.     }
  1462.     goback_days = atoi((*argvp)[1]);
  1463.     n--;
  1464.     ++*argvp;
  1465.     }
  1466.     return n;
  1467. }
  1468.  
  1469. do_goback()
  1470. {
  1471.     char back_act[FILENAME];
  1472.     FILE *ba;
  1473.     register group_header *gh;
  1474.     int32 count, total;
  1475.     int groups, y;
  1476.     
  1477.     sprintf(back_act, "%s/active.%d", db_directory, goback_days);
  1478.     if ((ba = open_file(back_act, OPEN_READ)) == NULL) {
  1479.     fprintf(stderr, "Cannot go back %d days\n", goback_days);
  1480.     nn_exit(1);
  1481.     }
  1482.  
  1483.     read_active_file(ba, (FILE *)NULL);
  1484.  
  1485.     fclose(ba);
  1486.  
  1487.     /* visit_rc_file has been called. */
  1488.  
  1489.     keep_rc_backup = 1;
  1490.     bak_suffix = ".goback";
  1491.     newsrc_update_freq = 9999;
  1492.     quick_unread_count = 0;
  1493.     total = groups = 0;
  1494.  
  1495.     if (goback_interact) {
  1496.     init_term();
  1497.     raw();
  1498.     }
  1499.  
  1500.     Loop_Groups_Sequence(gh) {
  1501.     if ((gh->master_flag & M_VALID) == 0) continue;
  1502.     if (!goback_alsounsub && (gh->group_flag & G_UNSUBSCRIBED)) continue;
  1503.  
  1504.     add_unread(gh, 1);
  1505.  
  1506.     count = restore_rc(gh, gh->last_a_article);
  1507.     if (count > 0) {
  1508.         if (goback_interact) {
  1509.         printf("%s + %ld ?  (y) ", gh->group_name, (long)count); fl;
  1510.         y = yes(0);
  1511.         putchar(CR); putchar(NL);
  1512.         switch (y) {
  1513.          case 1:
  1514.             break;
  1515.          case 0:
  1516.             gh->newsrc_line = gh->newsrc_orig;
  1517.             gh->select_line = gh->select_orig;
  1518.             continue;
  1519.          case -1:
  1520.             if (total > 0) {
  1521.             printf("\nSave changes sofar? (n) "); fl;
  1522.             if (yes(1) <= 0) nn_exit(0);
  1523.             }
  1524.             goto out;
  1525.         }
  1526.         } else
  1527.         if (verbose)
  1528.             printf("%5ld\t%s\n", (long)count, gh->group_name);
  1529.  
  1530.         total += count;
  1531.         groups++;
  1532.     }
  1533.     }
  1534.  
  1535.  out:
  1536.  
  1537.     if (total == 0) {
  1538.     printf("No articles marked\n");
  1539.     return;
  1540.     }
  1541.  
  1542.     flush_newsrc();
  1543.  
  1544.     if (verbose) putchar(NL);
  1545.     if (!silent)
  1546.     printf("%ld article%s marked unread in %d group%s\n",
  1547.            (long)total, plural((long)total),
  1548.            groups, plural((long)groups));
  1549. }
  1550.  
  1551. /* fake this for read_active_file */
  1552.  
  1553. group_header *add_new_group(name)
  1554. char *name;
  1555. {
  1556.     return NULL;
  1557. }
  1558.