home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume31 / tin / part12 / kill.c < prev    next >
C/C++ Source or Header  |  1992-07-08  |  13KB  |  576 lines

  1. /*
  2.  *  Project   : tin - a threaded Netnews reader
  3.  *  Module    : kill.c
  4.  *  Author    : I.Lea & J.Robinson
  5.  *  Created   : 01-04-91
  6.  *  Updated   : 20-06-92
  7.  *  Notes     : kill & auto select (hot) articles
  8.  *  Copyright : (c) Copyright 1991-92 by Iain Lea & Jim Robinson
  9.  *              You may  freely  copy or  redistribute  this software,
  10.  *              so  long as there is no profit made from its use, sale
  11.  *              trade or  reproduction.  You may not change this copy-
  12.  *              right notice, and it must be included in any copy made
  13.  */
  14.  
  15. #include    "tin.h"
  16.  
  17. #ifdef NO_REGEX 
  18. char *stars = "";
  19. #else        
  20. char *stars = "*";
  21. #endif
  22.  
  23. #define SET_KILLED(i)        (arts[i].unread = ART_READ, arts[i].killed = 1, num_of_killed_arts++)
  24. #define SET_HOT(i)        (arts[i].hot = 1)
  25. #define IS_READ(i)        (arts[i].unread == ART_READ)
  26. #define IS_KILLED(i)        (arts[i].killed == 1)
  27.  
  28. #define KILL_CHAR    'K'
  29. #define HOT_CHAR    'H'
  30.  
  31. #define K_KILL        0
  32. #define K_HOT        1
  33.  
  34. int kill_level = 1;
  35. int kill_num = 0;
  36. int max_kill;
  37. struct kill_t *killf;
  38.  
  39. /*
  40.  *  read_kill_file - read ~/.tin/kill file contents into kill array
  41.  */
  42.  
  43. int read_kill_file ()
  44. {
  45.     char buf[LEN];
  46.     FILE *fp;
  47.     int n;
  48.     char c;
  49.     unsigned int type;
  50.  
  51.     free_kill_array ();
  52.     
  53.     if ((fp = fopen (killfile, "r")) == NULL) {
  54.         return FALSE;
  55.     }
  56.  
  57.     kill_num=0;
  58.     while (fgets (buf, sizeof buf, fp) != NULL) {
  59.         if (buf[0] == '#') {
  60.             continue;
  61.         }    
  62.         if (kill_num == max_kill-1) {
  63.             expand_kill ();
  64.         }
  65.         n = sscanf(buf, "%d %c", &type, &c);
  66.         if (n == 0) {
  67.             goto corrupt_killfile;
  68.         }    
  69.         if (n > 1 && c == HOT_CHAR) {    /* hot */
  70.              killf[kill_num].kill_how = K_HOT;
  71.         } else {
  72.              killf[kill_num].kill_how = K_KILL;
  73.         }     
  74.         killf[kill_num].kill_type = type;
  75.  
  76.         if (fgets (buf, sizeof buf, fp) == NULL)  {
  77.             goto corrupt_killfile;
  78.         }
  79.         
  80.         killf[kill_num].kill_group = (long) atol (buf);
  81.  
  82.         switch (killf[kill_num].kill_type) {
  83.         case KILL_SUBJ:
  84.             if (fgets (buf, sizeof buf, fp) != NULL) {
  85.                 buf[strlen (buf)-1] = '\0';
  86.                 killf[kill_num].kill_subj = str_dup (buf);
  87.             }
  88.             break;
  89.         case KILL_FROM:
  90.             if (fgets (buf, sizeof buf, fp) != NULL) {
  91.                 buf[strlen (buf)-1] = '\0';
  92.                 killf[kill_num].kill_from = str_dup (buf);
  93.             }
  94.             break;
  95.         case KILL_BOTH:
  96.             if (fgets (buf, sizeof buf, fp) != NULL) {
  97.                 buf[strlen (buf)-1] = '\0';
  98.                 killf[kill_num].kill_subj = str_dup (buf);
  99.             }
  100.             if (fgets (buf, sizeof buf, fp) != NULL) {
  101.                 buf[strlen (buf)-1] = '\0';
  102.                 killf[kill_num].kill_from = str_dup (buf);
  103.             }
  104.             break;
  105.         default:
  106.             goto corrupt_killfile;
  107.         }
  108.         kill_num++;
  109.     }
  110.  
  111.     fclose (fp);
  112.     return (kill_num);
  113.  
  114. corrupt_killfile:
  115.     fclose (fp);
  116.     killf[kill_num].kill_type = 0;
  117.     error_message (txt_corrupt_kill_file, killfile);
  118.     return FALSE;
  119. }
  120.  
  121. /*
  122.  *  write_kill_file - write kill strings to ~/.tin/kill
  123.  */
  124.  
  125. void write_kill_file ()
  126. {
  127.     FILE *fp;
  128.     int i;
  129.     
  130.     if (kill_num == 0 || (fp = fopen (killfile, "w")) == NULL) {
  131.         return;
  132.     }
  133.  
  134.     wait_message (txt_saving);
  135.     fprintf (fp, "# 1st line  1=(Subject: only)  2=(From: only)  3=(Subject: & From:)\n");
  136.     fprintf (fp, "#           %c=(kill) %c=(auto-selection)\n", KILL_CHAR, HOT_CHAR);
  137.     fprintf (fp, "# 2nd line  0=(kill on all newsgroups)  >0=(kill on specific newsgroup)\n");
  138.     for (i=0 ; i < kill_num ; i++) {
  139.         if (killf[i].kill_type == 0 || (killf[i].kill_subj == 0 
  140.             &&  killf[i].kill_from == 0)) 
  141.             continue;
  142.  
  143.         if (killf[i].kill_how == K_KILL) {
  144.             fprintf (fp, "#\n# %03d KILL\n", i+1);
  145.             fprintf (fp, "%d\t%c\n", killf[i].kill_type, KILL_CHAR);
  146.         } else {
  147.             fprintf (fp, "#\n# %03d HOT\n", i+1);
  148.             fprintf (fp, "%d\t%c\n", killf[i].kill_type, HOT_CHAR);
  149.         }    
  150.         fprintf (fp, "%ld\n", killf[i].kill_group);
  151.  
  152.         switch (killf[i].kill_type) {
  153.             case KILL_SUBJ:
  154.                 fprintf (fp, "%s\n", killf[i].kill_subj);
  155.                 break;
  156.             case KILL_FROM:
  157.                 fprintf (fp, "%s\n", killf[i].kill_from);
  158.                 break;
  159.             case KILL_BOTH:
  160.                 fprintf (fp, "%s\n", killf[i].kill_subj);
  161.                 fprintf (fp, "%s\n", killf[i].kill_from);
  162.                 break;
  163.         }
  164.     }
  165.  
  166.     fclose (fp);
  167.     chmod (killfile, 0600);
  168.  
  169.     set_tin_uid_gid ();
  170. }
  171.  
  172. static int get_choice (x, help, prompt, opt1, opt2, opt3, opt4)
  173.     int x;
  174.     char *help, *prompt, *opt1, *opt2, *opt3, *opt4;
  175. {
  176.     int ch, n = 0, i = 0;
  177.     char *argv[4];
  178.     
  179.     if (opt1)
  180.         argv[n++] = opt1;
  181.     if (opt2)
  182.         argv[n++] = opt2;
  183.     if (opt3)
  184.         argv[n++] = opt3;
  185.     if (opt4)
  186.         argv[n++] = opt4;
  187.     assert(n > 0);
  188.  
  189.     if (help)
  190.         show_menu_help (help);
  191.         
  192.     do {
  193.         MoveCursor(x, (int) strlen (prompt));
  194.         fputs (argv[i], stdout);
  195.         fflush (stdout);
  196.         CleartoEOLN (); 
  197.         if ((ch = ReadCh ()) != ' ')
  198.             continue;
  199.         if (++i == n)
  200.             i = 0;
  201.     } while (ch != CR && ch != ESC);
  202.  
  203.     if (ch == ESC)
  204.         return (-1);
  205.     return (i);
  206. }
  207.  
  208. /*
  209.  *  options menu so that the user can dynamically change parameters
  210.  */
  211.  
  212. int kill_art_menu (group_name, index)
  213.     char *group_name;
  214.     int index;
  215. {
  216.     char buf[LEN];
  217.     char text[LEN];
  218.     char kill_from[LEN];
  219.     char kill_subj[LEN];
  220.     char kill_group[LEN];
  221.     char ch_default = 's';
  222.     int ch;
  223.     int counter = 0;
  224.     int killed = TRUE;
  225.     int kill_from_ok = FALSE;
  226.     int kill_subj_ok = FALSE;
  227.     int kill_every_group = FALSE;
  228.     int i;
  229.     int kill_how;
  230.  
  231. #ifdef SIGTSTP
  232.     sigtype_t (*susp)();
  233.     
  234.     susp = (sigtype_t *) 0;
  235.  
  236.     if (do_sigtstp) {
  237.         susp = sigdisp (SIGTSTP, SIG_DFL);
  238.         sigdisp (SIGTSTP, SIG_IGN);
  239.     }
  240. #endif
  241.     
  242.     sprintf (kill_group, "%s only", group_name);
  243.     sprintf (kill_subj, txt_kill_subject, COLS-35, COLS-35, arts[index].subject);
  244.     if (arts[index].name != (char *) 0) {
  245.         sprintf (text, "%s (%s)", arts[index].from, arts[index].name);
  246.     } else {
  247.         strcpy (text, arts[index].from);
  248.     }
  249.     sprintf (kill_from, txt_kill_from, COLS-35, COLS-35, text);
  250.     text[0] = '\0';
  251.     
  252.     ClearScreen ();
  253.  
  254.     center_line (0, TRUE, txt_kill_menu);
  255.     
  256.     MoveCursor (INDEX_TOP, 0);
  257.     printf ("%s\r\n\r\n\r\n", txt_kill_how);
  258.     printf ("%s\r\n\r\n", txt_kill_text);
  259.     printf ("%s\r\n\r\n\r\n", txt_kill_text_type);
  260.     printf ("%s\r\n\r\n", kill_subj);
  261.     printf ("%s\r\n\r\n\r\n", kill_from);
  262.     printf ("%s%s", txt_kill_group, kill_group);
  263.     fflush (stdout);
  264.  
  265.     i = get_choice (INDEX_TOP, txt_help_kill_how, txt_kill_how, 
  266.                "Kill       ", "Auto Select", NULL, NULL);
  267.     if (i == -1) {
  268.         return FALSE;
  269.     }    
  270.     kill_how = (i == 0 ? K_KILL : K_HOT);
  271.  
  272.     show_menu_help (txt_help_kill_text);
  273.     
  274.     if (! prompt_menu_string (INDEX_TOP+3, (int) strlen (txt_kill_text), text)) {
  275.         return FALSE;
  276.     }
  277.  
  278.     if (text[0]) {
  279.         i = get_choice(INDEX_TOP+5, txt_help_kill_text_type, 
  280.                    txt_kill_text_type, "Subject: line only    ", 
  281.                    "From: line only       ", "Subject: & From: lines", 
  282.                    NULL);
  283.         if (i == -1) {
  284.             return FALSE;
  285.         }    
  286.         counter = ((i == 0 ? KILL_SUBJ : (i == 1 ? KILL_FROM : KILL_BOTH)));
  287.     }
  288.  
  289.     if (! text[0]) {
  290.         i = get_choice (INDEX_TOP+8, txt_help_kill_subject, 
  291.                     kill_subj, "Yes", "No ", NULL, NULL);
  292.         if (i == -1) {
  293.             return FALSE;
  294.         } else {
  295.             kill_subj_ok = (i ? FALSE : TRUE);
  296.         }
  297.         i = get_choice (INDEX_TOP+10, txt_help_kill_from, 
  298.                     kill_from, "No ", "Yes", NULL, NULL);
  299.         if (i == -1) {
  300.             return FALSE;
  301.         } else {
  302.             kill_from_ok = (i ? TRUE : FALSE);
  303.         }
  304.     }
  305.  
  306.     if (text[0] || kill_subj_ok || kill_from_ok) {
  307.         i = get_choice (INDEX_TOP+13, txt_help_kill_group, 
  308.                    txt_kill_group, kill_group, "All groups", 
  309.                    NULL, NULL);
  310.         if (i == -1) {
  311.             return FALSE;
  312.         }    
  313.         kill_every_group = (i == 0 ? FALSE : TRUE);
  314.     }
  315.  
  316.     while (1) {
  317.         do {
  318.             sprintf (msg, "%s%c", txt_quit_edit_save_killfile, ch_default);
  319.             wait_message (msg);
  320.             MoveCursor (LINES, (int) strlen (txt_quit_edit_save_killfile));
  321.             if ((ch = ReadCh ()) == CR)
  322.                 ch = ch_default;
  323.         } while (ch != ESC && ch != 'q' && ch != 'e' && ch != 's');
  324.         switch (ch) {
  325.         case 'e':
  326.             start_line_offset = 2;
  327.             invoke_editor (killfile);
  328.             unkill_all_articles ();
  329.             killed_articles = read_kill_file ();
  330.             killed = TRUE;
  331.             goto kill_done;
  332.  
  333.         case 'a':
  334.         case ESC:
  335.             killed = FALSE;
  336.             goto kill_done;
  337.             
  338.         case 's':
  339.             if (kill_num > max_kill-1) {
  340.                 expand_kill ();
  341.             }
  342.  
  343.             killf[kill_num].kill_how = kill_how;
  344.  
  345.             if (text[0]) {
  346.                 sprintf (buf, "%s%s%s", stars, text, stars);
  347.                 switch (counter) {
  348.                 case KILL_SUBJ:
  349.                     killf[kill_num].kill_subj = str_dup (buf);
  350.                     break;
  351.                 case KILL_FROM:
  352.                     killf[kill_num].kill_from = str_dup (buf);
  353.                     break;
  354.                 case KILL_BOTH:
  355.                     killf[kill_num].kill_subj = str_dup (buf);
  356.                     killf[kill_num].kill_from = killf[kill_num].kill_subj; 
  357.                     break;
  358.                 }
  359.                 killf[kill_num].kill_type = counter;
  360.                 if (kill_every_group) {
  361.                     killf[kill_num].kill_group = 0L;
  362.                 } else {
  363.                     killf[kill_num].kill_group = hash_s (group_name);
  364.                 }
  365.                 kill_num++;
  366.             } else {
  367.                 if (kill_subj_ok) {
  368.                     killf[kill_num].kill_type = KILL_SUBJ;
  369.                     sprintf (buf, "%s%s%s", 
  370.                         stars, arts[index].subject, stars);
  371.                     killf[kill_num].kill_subj = str_dup (buf);
  372.                 }
  373.                 if (kill_from_ok) {
  374.                     killf[kill_num].kill_type |= KILL_FROM;
  375.                     if (arts[index].name != (char *) 0) {
  376.                         sprintf (buf, "%s%s (%s)%s", 
  377.                             stars, arts[index].from, arts[index].name, stars);
  378.                     } else {
  379.                         sprintf (buf, "%s%s%s", 
  380.                             stars, arts[index].from, stars);
  381.                     }
  382.                     killf[kill_num].kill_from = str_dup (buf);
  383.                 }
  384.                 if (killf[kill_num].kill_type) {        
  385.                     if (kill_every_group) {
  386.                         killf[kill_num].kill_group= 0L;
  387.                     } else {
  388.                         killf[kill_num].kill_group= hash_s (group_name);
  389.                     }
  390.                     kill_num++;
  391.                 }
  392.             }
  393.             write_kill_file ();
  394.             
  395.         kill_done:
  396.             
  397. #ifdef SIGTSTP
  398.             if (do_sigtstp) {
  399.                 sigdisp (SIGTSTP, susp);
  400.             }
  401. #endif
  402.             return (killed);
  403.         }    
  404.     }
  405.     /* NOTREACHED */
  406. }
  407.  
  408.  
  409. /*
  410.  * We assume that any articles which are tagged as killed are also
  411.  * tagged as being read BECAUSE they were killed. So, we retag
  412.  * them as being unread.
  413.  */
  414.  
  415. int unkill_all_articles ()
  416. {
  417.     int unkilled = FALSE;
  418.     register int i;
  419.  
  420.     for (i=0 ; i < top ; i++) {
  421.         if (arts[i].killed) {
  422.             arts[i].killed = FALSE;
  423.             arts[i].unread = ART_UNREAD;
  424.             unkilled = TRUE;
  425.         }
  426.     }
  427.     num_of_killed_arts = 0;
  428.  
  429.     return (unkilled);
  430. }
  431.  
  432.  
  433. int kill_any_articles (group)
  434.     char *group;
  435. {
  436.     char buf[LEN];
  437.     int killed = FALSE;
  438.     int run_ok = FALSE;
  439.     int is_hot;
  440.     long newsgroup_hash;
  441.     register int i, j;
  442.  
  443.     if (! kill_num) {
  444.         return (killed);
  445.     }
  446.  
  447.     num_of_killed_arts = 0;
  448.     num_of_hot_arts = 0;
  449.  
  450.     newsgroup_hash = hash_s (group);
  451.  
  452.     for (i=0 ; i < kill_num ; i++) {
  453.         if (killf[i].kill_group == 0L ||
  454.             killf[i].kill_group == newsgroup_hash) {
  455.             run_ok = TRUE;    
  456.         }
  457.     }
  458.     if (! run_ok) {
  459.         return (killed);
  460.     }
  461.     if (debug && ! update) {
  462.         wait_message (txt_killing_arts);
  463.     }
  464.     for (i=0 ; i < top ; i++) {
  465.         if (IS_READ(i) && kill_level == 0) {
  466.             continue;
  467.         }    
  468.         for (j=0 ; j < kill_num ; j++) {
  469.             if (killf[j].kill_group != 0L &&
  470.                 killf[j].kill_group != newsgroup_hash)
  471.                 continue;
  472.  
  473.             is_hot = (killf[j].kill_how == K_HOT ? TRUE : FALSE);
  474.             switch (killf[j].kill_type) {
  475.             case KILL_SUBJ:
  476.                 if (STR_MATCH (arts[i].subject, killf[j].kill_subj)) {
  477.                     if (! is_hot) {
  478.                         SET_KILLED(i);
  479.                     } else {
  480.                         SET_HOT(i);
  481.                         if (show_only_unread) {
  482.                             if (arts[i].unread == ART_UNREAD) {
  483.                                 num_of_hot_arts++;
  484.                             }
  485.                         } else {
  486.                             num_of_hot_arts++;
  487.                         }
  488.                     }    
  489.                 }
  490.                 break;
  491.             case KILL_FROM:
  492.                 if (arts[i].name != (char *) 0) {
  493.                     sprintf (buf, "%s (%s)", arts[i].from, arts[i].name);
  494.                 } else {
  495.                     strcpy (buf, arts[i].from);
  496.                 }
  497.                 if (STR_MATCH (buf, killf[j].kill_from)) {
  498.                     if (! is_hot) {
  499.                         SET_KILLED(i);
  500.                     } else {
  501.                         SET_HOT(i);
  502.                         if (show_only_unread) {
  503.                             if (arts[i].unread == ART_UNREAD) {
  504.                                 num_of_hot_arts++;
  505.                             }
  506.                         } else {
  507.                             num_of_hot_arts++;
  508.                         }
  509.                     }    
  510.                 }
  511.                 break;
  512.             case KILL_BOTH:
  513.                 if (STR_MATCH (arts[i].subject, killf[j].kill_subj)) {
  514.                     if (! is_hot) {
  515.                         SET_KILLED(i);
  516.                     } else {
  517.                         SET_HOT(i);
  518.                         if (show_only_unread) {
  519.                             if (arts[i].unread == ART_UNREAD) {
  520.                                 num_of_hot_arts++;
  521.                             }
  522.                         } else {
  523.                             num_of_hot_arts++;
  524.                         }
  525.                     }    
  526.                     break;
  527.                 }
  528.                 if (arts[i].name != (char *) 0) {
  529.                     sprintf (buf, "%s (%s)", arts[i].from, arts[i].name);
  530.                 } else {
  531.                     strcpy (buf, arts[i].from);
  532.                 }
  533.  
  534.                 if (STR_MATCH (buf, killf[j].kill_from)) {
  535.                     if (! is_hot) {
  536.                         SET_KILLED(i);
  537.                     } else {
  538.                         SET_HOT(i);
  539.                         if (show_only_unread) {
  540.                             if (arts[i].unread == ART_UNREAD) {
  541.                                 num_of_hot_arts++;
  542.                             }
  543.                         } else {
  544.                             num_of_hot_arts++;
  545.                         }
  546.                     }    
  547.                 }
  548.                 break;
  549.             }
  550.             if (IS_KILLED(i) || ! killed)
  551.                 killed = TRUE;
  552.         }
  553.     }
  554.     return (killed);
  555. }
  556.  
  557. /*
  558.  * Auto select articles.
  559.  * WARNING - this routinely is presently a kludge. It calls
  560.  * kill_any_articles() which also kills articles. It also always returns
  561.  * true in order to fake out the display code (cause it doesn't know
  562.  * if any articles were actually selected)
  563.  * The correct way to do this is to modify kill_any_articles() to take
  564.  * another arg to specify whether killing, auto-selecting, or both is to be 
  565.  * done, rename it to something else, and then have a new kill_any_articles()
  566.  * and auto_select_articles() call this new routine with the appropriate 
  567.  * arguments.
  568.  */
  569.  
  570. int auto_select_articles (group)
  571.     char *group;
  572. {
  573.     kill_any_articles (group);
  574.     return (TRUE);
  575. }
  576.