home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume22 / nn6.4 / part11 / kill.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-06-07  |  21.3 KB  |  967 lines

  1. /*
  2.  *    (c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
  3.  *
  4.  *    Kill file handling
  5.  */
  6.  
  7. #include "config.h"
  8. #include "term.h"
  9. #include "regexp.h"
  10.  
  11. export int killed_articles;
  12.  
  13. char KILL_FILE[] =     "kill";
  14. char COMPILED_KILL[] =    "KILL.COMP";
  15.  
  16. #define COMP_KILL_MAGIC    0x4b694c6f    /* KiLo */
  17.  
  18. /*
  19.  * kill flags
  20.  */
  21.  
  22. #define COMP_KILL_ENTRY        0x80000000
  23.  
  24. #define GROUP_REGEXP        0x01000000
  25. #define GROUP_REGEXP_HDR    0x02000000
  26.  
  27. #define AND_MATCH        0x00020000
  28. #define OR_MATCH        0x00010000
  29.  
  30. #define    KILL_CASE_MATCH        0x00000100
  31. #define KILL_ON_REGEXP        0x00000200
  32. #define KILL_UNLESS_MATCH    0x00000400
  33.  
  34. #define    AUTO_KILL        0x00000001
  35. #define AUTO_SELECT        0x00000002
  36. #define ON_SUBJECT        0x00000004
  37. #define    ON_SENDER        0x00000008
  38. #define ON_FOLLOW_UP        0x00000010
  39. #define ON_ANY_REFERENCES    0x00000020
  40.  
  41. /*
  42.  * external flag representation
  43.  */
  44.  
  45. #define    EXT_AUTO_KILL        '!'
  46. #define EXT_AUTO_SELECT        '+'
  47. #define EXT_KILL_UNLESS_MATCH    '~'
  48. #define EXT_ON_FOLLOW_UP    '>'
  49. #define EXT_ON_ANY_REFERENCES    'a'
  50. #define EXT_ON_SUBJECT        's'
  51. #define    EXT_ON_SENDER        'n'
  52. #define    EXT_KILL_CASE_MATCH    '='
  53. #define EXT_KILL_ON_REGEXP    '/'
  54. #define EXT_AND_MATCH        '&'
  55. #define EXT_OR_MATCH        '|'
  56.  
  57. /*
  58.  * period = nnn DAYS
  59.  */
  60.  
  61. #define    DAYS    * 24 * 60 * 60
  62.  
  63.  
  64. /*
  65.  * kill_article
  66.  *
  67.  *    return 1 to kill article, 0 to include it
  68.  */
  69.  
  70. typedef struct kill_list_entry {
  71.     flag_type kill_flag;
  72.     char *kill_pattern;
  73.     regexp *kill_regexp;
  74.     struct kill_list_entry *next_kill;
  75. } kill_list_entry;
  76.  
  77. static kill_list_entry *kill_tab;
  78. static char *kill_patterns;
  79.  
  80. static kill_list_entry *global_kill_list = NULL;
  81. static kill_list_entry latest_kl_entry;
  82.  
  83. typedef struct {
  84.     regexp *group_regexp;
  85.     kill_list_entry *kill_entry;
  86. } kill_group_regexp;
  87.  
  88. static kill_group_regexp *group_regexp_table = NULL;
  89. static int regexp_table_size = 0;
  90. static kill_list_entry *regexp_kill_list = NULL;
  91. static group_header *current_kill_group = NULL;
  92.  
  93. /*
  94.  *    Build regexp_kill_list for current_group
  95.  */
  96.  
  97. static build_regexp_kill()
  98. {
  99.     register kill_group_regexp *tb;
  100.     register int n, used_last;
  101.     register char *name;
  102.  
  103.     regexp_kill_list = NULL;
  104.     current_kill_group = current_group;
  105.     name = current_group->group_name;
  106.     used_last = 0;        /* get AND_MATCH/OR_MATCH for free */
  107.  
  108.     for (n = regexp_table_size, tb = group_regexp_table; --n >= 0; tb++) {
  109.     if (tb->group_regexp != NULL) {
  110.         used_last = 0;
  111.         if (!regexec(tb->group_regexp, name)) continue;
  112.     } else
  113.         if (!used_last) continue;
  114.  
  115.     tb->kill_entry->next_kill = regexp_kill_list;
  116.     regexp_kill_list = tb->kill_entry;
  117.     used_last = 1;
  118.     }
  119. }
  120.  
  121. /*
  122.  *    execute kill patterns on article
  123.  */
  124.  
  125. static kill_list_entry *exec_kill(kl, ah, unlessp, do_kill, do_select)
  126. register kill_list_entry *kl;
  127. register article_header *ah;
  128. int *unlessp, do_kill, do_select;
  129. {
  130.     register flag_type flag;
  131.     register char *string;
  132.  
  133.     for ( ; kl != NULL; kl = kl->next_kill) {
  134.     flag = kl->kill_flag;
  135.  
  136.     if (do_select && (flag & AUTO_SELECT) == 0) goto failed;
  137.     if (do_kill && (flag & AUTO_KILL) == 0) goto failed;
  138.  
  139.     if (flag & KILL_UNLESS_MATCH)
  140.         *unlessp = 1;
  141.  
  142.     if (flag & ON_ANY_REFERENCES) {
  143.         if (ah->replies & 0x7f) goto match;
  144.         goto failed;
  145.     }
  146.  
  147.     if (flag & ON_SUBJECT) {
  148.         if (flag & ON_FOLLOW_UP) {
  149.         if ((ah->replies & 0x80) == 0) goto failed;
  150.         }
  151.         string = ah->subject;
  152.     } else
  153.         string = ah->sender;
  154.  
  155.     if (flag & KILL_CASE_MATCH) {
  156.         if (flag & KILL_ON_REGEXP) {
  157.         if (regexec(kl->kill_regexp, string)) goto match;
  158.         } else
  159.         if (strcmp(kl->kill_pattern, string) == 0) goto match;
  160.     } else
  161.     if (flag & KILL_ON_REGEXP) {
  162.         if (regexec_fold(kl->kill_regexp, string)) goto match;
  163.     } else
  164.         if (strmatch_fold(kl->kill_pattern, string)) goto match;
  165.  
  166.      failed:
  167.     if ((flag & AND_MATCH) == 0) continue;
  168.  
  169.     do            /* skip next */
  170.         kl = kl->next_kill;
  171.     while (kl && (kl->kill_flag & AND_MATCH));
  172.     if (kl) continue;
  173.     break;
  174.  
  175.      match:
  176.     if (flag & AND_MATCH) continue;
  177.     break;
  178.     }
  179.     return kl;
  180. }
  181.  
  182.  
  183. kill_article(ah)
  184. article_header *ah;
  185. {
  186.     register kill_list_entry *kl;
  187.     int unless_match = 0;
  188.  
  189.     kl = exec_kill((kill_list_entry *)(current_group->kill_list), ah,
  190.            &unless_match, 0, 0);
  191.     if (kl == NULL && group_regexp_table != NULL) {
  192.     if (current_kill_group != current_group) build_regexp_kill();
  193.     kl = exec_kill(regexp_kill_list, ah, &unless_match, 0, 0);
  194.     }
  195.     if (kl == NULL)
  196.     kl = exec_kill(global_kill_list, ah, &unless_match, 0, 0);
  197.  
  198.     if (kl != NULL) {
  199.     if (kl->kill_flag & AUTO_KILL) {
  200.         killed_articles++;
  201.         return 1;
  202.     }
  203.  
  204.     if (kl->kill_flag & AUTO_SELECT)
  205.         ah->attr = A_AUTO_SELECT;
  206.     return 0;
  207.     }
  208.  
  209.     if (unless_match) {
  210.     killed_articles++;
  211.     return 1;
  212.     }
  213.  
  214.     return 0;
  215. }
  216.  
  217.  
  218. auto_select_article(ah, do_select)
  219. article_header *ah;
  220. int do_select;
  221. {
  222.     register kill_list_entry *kl;
  223.     int dummy;
  224.  
  225.     if (do_select == 1) {
  226.     kl = ah->a_group ? (kill_list_entry *)(ah->a_group->kill_list) :
  227.         (kill_list_entry *)(current_group->kill_list);
  228.     kl = exec_kill(kl, ah, &dummy, !do_select, do_select);
  229.     if (kl == NULL && group_regexp_table != NULL) {
  230.         if (current_kill_group != current_group) build_regexp_kill();
  231.         kl = exec_kill(regexp_kill_list, ah, &dummy, !do_select, do_select);
  232.     }
  233.     if (kl == NULL)
  234.         kl = exec_kill(global_kill_list, ah, &dummy, !do_select, do_select);
  235.     } else {
  236.     kl = exec_kill(&latest_kl_entry, ah, &dummy, !do_select, do_select);
  237.     }
  238.  
  239.     if (kl == NULL) return 0;
  240.  
  241.     if (!do_select) killed_articles++;
  242.     return 1;
  243. }
  244.  
  245.  
  246. static fput_pattern(p, f)
  247. register char *p;
  248. register FILE *f;
  249. {
  250.     register char c;
  251.  
  252.     while (c = *p++) {
  253.     if (c == ':' || c == '\\') putc('\\', f);
  254.     putc(c, f);
  255.     }
  256. }
  257.  
  258. static char *get_pattern(p, lenp, more)
  259. register char *p;
  260. int *lenp, more;
  261. {
  262.     register char c, *q, *start;
  263.  
  264.     start = q = p;
  265.     while (c = *p++) {
  266.     if (c == '\\') {
  267.         c = *p++;
  268.         if (c != ':' && c != '\\') *q++ = '\\';
  269.         *q++ = c;
  270.         continue;
  271.     }
  272.     if (more) {
  273.         if (c == ':') break;
  274.         if (c == NL) return NULL;
  275.     } else
  276.         if (c == NL) break;
  277.  
  278.     *q++ = c;
  279.     }
  280.  
  281.     if (c == NUL) return NULL;
  282.  
  283.     *q++ = NUL;
  284.     *lenp = q - start;
  285.     return p;
  286. }
  287.  
  288. enter_kill_file(gh, pattern, flag, days)
  289. group_header *gh;
  290. char *pattern;
  291. register flag_type flag;
  292. int days;
  293. {
  294.     FILE *killf;
  295.     register kill_list_entry *kl;
  296.     regexp *re;
  297.     char *str;
  298.  
  299.     str = copy_str(pattern);
  300.  
  301.     if ((flag & KILL_CASE_MATCH) == 0)
  302.     fold_string(str);
  303.  
  304.     if (flag & KILL_ON_REGEXP) {
  305.     re = regcomp(pattern);
  306.     if (re == NULL) return;
  307.     } else
  308.     re = NULL;
  309.  
  310.     killf = open_file(relative(nn_directory, "kill"), OPEN_APPEND);
  311.     if (killf == NULL) {
  312.     msg("cannot create kill file");
  313.     return;
  314.     }
  315.  
  316.     if (days >= 0) {
  317.     if (days == 0) days = 30;
  318.     fprintf(killf, "%lu:", (long)(cur_time() + days DAYS));
  319.     }
  320.  
  321.     if (gh) fputs(gh->group_name, killf);
  322.     fputc(':', killf);
  323.  
  324.     if (flag & KILL_UNLESS_MATCH) fputc(EXT_KILL_UNLESS_MATCH, killf);
  325.     if (flag & AUTO_KILL) fputc(EXT_AUTO_KILL, killf);
  326.     if (flag & AUTO_SELECT) fputc(EXT_AUTO_SELECT, killf);
  327.     if (flag & ON_FOLLOW_UP) fputc(EXT_ON_FOLLOW_UP, killf);
  328.     if (flag & ON_ANY_REFERENCES) fputc(EXT_ON_ANY_REFERENCES, killf);
  329.     if (flag & ON_SENDER) fputc(EXT_ON_SENDER, killf);
  330.     if (flag & ON_SUBJECT) fputc(EXT_ON_SUBJECT, killf);
  331.     if (flag & KILL_CASE_MATCH) fputc(EXT_KILL_CASE_MATCH, killf);
  332.     if (flag & KILL_ON_REGEXP) fputc(EXT_KILL_ON_REGEXP, killf);
  333.     fputc(':', killf);
  334.  
  335.     fput_pattern(pattern, killf);
  336.     fputc(NL, killf);
  337.  
  338.     fclose(killf);
  339.     rm_kill_file();
  340.  
  341.     kl = newobj(kill_list_entry, 1);
  342.  
  343.     latest_kl_entry.kill_pattern = kl->kill_pattern = str;
  344.     latest_kl_entry.kill_regexp = kl->kill_regexp = re;
  345.     latest_kl_entry.kill_flag = kl->kill_flag = flag;
  346.     latest_kl_entry.next_kill = NULL;
  347.  
  348.     if (gh) {
  349.     kl->next_kill = (kill_list_entry *)(gh->kill_list);
  350.     gh->kill_list = (char *)kl;
  351.     } else {
  352.     kl->next_kill = global_kill_list;
  353.     global_kill_list = kl;
  354.     }
  355. }
  356.  
  357.  
  358. typedef struct {
  359.     group_number    ck_group;
  360.     flag_type        ck_flag;
  361.     long        ck_pattern_index;
  362. } comp_kill_entry;
  363.  
  364. typedef struct {
  365.     long        ckh_magic;
  366.     time_t        ckh_db_check;
  367.     off_t        ckh_pattern_offset;
  368.     long        ckh_pattern_size;
  369.     long        ckh_entries;
  370.     long        ckh_regexp_size;
  371. } comp_kill_header;
  372.  
  373.  
  374. kill_menu(ah)
  375. article_header *ah;
  376. {
  377.     int days;
  378.     register flag_type flag;
  379.     char *mode1, *mode2;
  380.     char *pattern, *dflt, *days_str, buffer[512];
  381.     extern article_header *get_menu_article();
  382.     group_header *gh;
  383.  
  384.     prompt("\1AUTO\1 (k)ill or (s)elect (CR => Kill subject 1 month) ");
  385.     switch (get_c()) {
  386.      case CR:
  387.      case NL:
  388.     if (ah == NULL) {
  389.         ah = get_menu_article();
  390.         if (ah == NULL) return -1;
  391.     }
  392.  
  393.     strcpy(buffer, ah->subject);
  394.     enter_kill_file(current_group, buffer,
  395.             AUTO_KILL | ON_SUBJECT | KILL_CASE_MATCH, 30);
  396.     msg("DONE");
  397.     return 1;
  398.  
  399.      case 'k':
  400.      case 'K':
  401.      case '!':
  402.     flag = AUTO_KILL;
  403.     mode1 = "KILL";
  404.     break;
  405.      case 's':
  406.      case 'S':
  407.      case '+':
  408.     flag = AUTO_SELECT;
  409.     mode1 = "SELECT";
  410.     break;
  411.      default:
  412.     return -1;
  413.     }
  414.  
  415.     prompt("\1AUTO %s\1 on (s)ubject or (n)ame  (s)", mode1);
  416.  
  417.     dflt = NULL;
  418.     switch (get_c()) {
  419.      case 'n':
  420.      case 'N':
  421.     flag |= ON_SENDER;
  422.     if (ah) dflt = ah->sender;
  423.     mode2 = "Name";
  424.     break;
  425.      case 's':
  426.      case 'S':
  427.      case SP:
  428.      case CR:
  429.      case NL:
  430.     flag |= ON_SUBJECT;
  431.     if (ah) dflt = ah->subject;
  432.     mode2 = "Subject";
  433.     break;
  434.      default:
  435.     return -1;
  436.     }
  437.  
  438.     prompt("\1%s %s:\1 (%=/) ", mode1, mode2);
  439.  
  440.     pattern = get_s(dflt, NONE, "%=/", NULL_FCT);
  441.     if (pattern == NULL) return -1;
  442.     if (*pattern == NUL || *pattern == '%' || *pattern == '=') {
  443.     if (dflt && *dflt)
  444.         pattern = dflt;
  445.     else {
  446.         if ((ah = get_menu_article()) == NULL) return -1;
  447.         pattern = (flag & ON_SUBJECT) ? ah->subject : ah->sender;
  448.     }
  449.     flag |= KILL_CASE_MATCH;
  450.     } else
  451.     if (*pattern == '/') {
  452.         prompt("\1%s %s\1 (regexp): ", mode1, mode2);
  453.  
  454.         pattern = get_s(NONE, NONE, NONE, NULL_FCT);
  455.         if (pattern == NULL || *pattern == NUL) return -1;
  456.         flag |= KILL_ON_REGEXP;
  457.     }
  458.  
  459.     strcpy(buffer, pattern);
  460.     pattern = buffer;
  461.  
  462.     prompt("\1%s\1 in (g)roup '%s' or in (a)ll groups  (g)",
  463.        mode1, current_group->group_name);
  464.  
  465.     switch (get_c()) {
  466.       case 'g':
  467.       case 'G':
  468.       case SP:
  469.       case CR:
  470.       case NL:
  471.      gh = current_group;
  472.      break;
  473.       case 'A':
  474.       case 'a':
  475.      gh = NULL;
  476.      break;
  477.       default:
  478.      return -1;
  479.      }
  480.  
  481.     prompt("\1Lifetime of entry in days\1 (p)ermanent  (30) ");
  482.     days_str = get_s(" 30 days", NONE, "pP", NULL_FCT);
  483.     if (days_str == NULL) return -1;
  484.  
  485.     if (*days_str == NUL) {
  486.         days_str = "30 days";
  487.     days = 30;
  488.     } else if (*days_str == 'p' || *days_str == 'P') {
  489.     days_str = "perm";
  490.     days = -1;
  491.     } else if (isdigit(*days_str)) {
  492.     days = atoi(days_str);
  493.     sprintf(days_str, "%d days", days);
  494.     } else {
  495.     ding();
  496.     return -1;
  497.     }
  498.  
  499.     prompt("\1CONFIRM\1 %s %s %s%s: %-.35s%s ",
  500.        mode1, mode2, days_str,
  501.        (flag & KILL_CASE_MATCH) ? " exact" :
  502.        (flag & KILL_ON_REGEXP) ? " regexp" : "",
  503.        pattern, strlen(pattern) > 35 ? "..." : "");
  504.     if (yes(0) <= 0) return -1;
  505.  
  506.     enter_kill_file(gh, pattern, flag, days);
  507.  
  508.     return (flag & AUTO_KILL) ? 1 : 0;
  509. }
  510.  
  511. static compile_kill_file()
  512. {
  513.     FILE *killf, *compf, *patternf, *dropf;
  514.     comp_kill_header header;
  515.     comp_kill_entry  entry;
  516.     time_t now, age;
  517.     off_t cur_line_start;
  518.     char line[512];
  519.     register char *cp, *np;
  520.     register int c;
  521.     group_header *gh;
  522.     flag_type flag, fields[10];
  523.     extern char *temp_file;
  524.     int any_errors, nfield, nf, len;
  525.  
  526.     any_errors = 0;
  527.     header.ckh_entries = header.ckh_regexp_size = 0;
  528.  
  529.     killf = open_file(relative(nn_directory, KILL_FILE),
  530.               OPEN_READ | DONT_CREATE);
  531.     if (killf == NULL) return 0;
  532.  
  533.     compf = open_file(relative(nn_directory, COMPILED_KILL), OPEN_CREATE);
  534.     if (compf == NULL) goto err1;
  535.  
  536.     new_temp_file();
  537.     if ((patternf = open_file(temp_file, OPEN_CREATE)) == NULL)
  538.     goto err2;
  539.  
  540.     dropf = NULL;
  541.  
  542.     printf("\nCompiling kill file\n");
  543.  
  544.     fseek(compf, (off_t)sizeof(header), 0);
  545.  
  546.     now = cur_time();
  547.  
  548.  next_entry:
  549.  
  550.     for (;;) {
  551.     cur_line_start = ftell(killf);
  552.  
  553.     if (fgets(line, 512, killf) == NULL) break;
  554.  
  555.     cp = line;
  556.     while (*cp && isascii(*cp) && isspace(*cp)) cp++;
  557.     if (*cp == NUL || *cp == '#' || !isascii(*cp)) continue;
  558.  
  559.     if ((np = strchr(cp, ':')) == NULL) goto bad_entry;
  560.  
  561.     /* optional "age:" */
  562.  
  563.     if (np != cp && isdigit(*cp)) {
  564.         *np++ = NUL;
  565.         age = (time_t)atol(cp);
  566.         if (age < now) goto drop_entry;
  567.         cp = np;
  568.         if ((np = strchr(cp, ':')) == NULL) goto bad_entry;
  569.     }
  570.  
  571.     /* "group-name:"  or "/regexp:" or ":" for all groups */
  572.  
  573.     flag = COMP_KILL_ENTRY;
  574.  
  575.     if (np == cp) {
  576.         entry.ck_group = -1;
  577.         np++;
  578.     } else {
  579.         *np++ = NUL;
  580.         if (*cp == '/') {
  581.         entry.ck_group = (long)ftell(patternf);
  582.         cp++;
  583.         len = strlen(cp) + 1;
  584.         if (fwrite(cp, sizeof(char), len, patternf) != len)
  585.             goto err3;
  586.         flag |= GROUP_REGEXP | GROUP_REGEXP_HDR ;
  587.         header.ckh_regexp_size++;
  588.         } else {
  589.         if ((gh = lookup(cp)) == NULL) {
  590.             printf("Unknown group in kill file: %s\n", cp);
  591.             any_errors++;
  592.             goto drop_entry;
  593.         }
  594.         entry.ck_group = gh->group_num;
  595.         }
  596.     }
  597.  
  598.     /* flags */
  599.  
  600.     cp = np;
  601.     nfield = 0;
  602.  
  603.     for (;;) {
  604.         switch (*cp++) {
  605.          case EXT_AND_MATCH:
  606.          case EXT_OR_MATCH:
  607.         fields[nfield++] = flag;
  608.         flag &= ~(AND_MATCH | ON_SUBJECT | ON_SENDER |
  609.               KILL_CASE_MATCH | KILL_ON_REGEXP |
  610.               GROUP_REGEXP_HDR);
  611.         flag |= (cp[-1] == EXT_AND_MATCH) ? AND_MATCH : OR_MATCH;
  612.         continue;
  613.          case EXT_AUTO_KILL:
  614.         flag |= AUTO_KILL;
  615.         continue;
  616.          case EXT_AUTO_SELECT:
  617.         flag |= AUTO_SELECT;
  618.         continue;
  619.          case EXT_ON_FOLLOW_UP:
  620.         flag |= ON_FOLLOW_UP;
  621.         continue;
  622.          case EXT_ON_ANY_REFERENCES:
  623.         flag |= ON_ANY_REFERENCES;
  624.         continue;
  625.          case EXT_ON_SUBJECT:
  626.         flag |= ON_SUBJECT;
  627.         continue;
  628.          case EXT_ON_SENDER:
  629.         flag |= ON_SENDER;
  630.         continue;
  631.          case EXT_KILL_CASE_MATCH:
  632.         flag |= KILL_CASE_MATCH;
  633.         continue;
  634.          case EXT_KILL_UNLESS_MATCH:
  635.         flag |= KILL_UNLESS_MATCH;
  636.         continue;
  637.          case EXT_KILL_ON_REGEXP:
  638.         flag |= KILL_ON_REGEXP;
  639.         continue;
  640.          case ':':
  641.         break;
  642.          case NL:
  643.         goto bad_entry;
  644.          default:
  645.         printf("Ignored flag '%c' in kill file\n", cp[-1]);
  646.         any_errors++;
  647.         continue;
  648.         }
  649.         break;
  650.     }
  651.  
  652.     fields[nfield++] = flag;
  653.  
  654.     for (nf = 0; --nfield >= 0; nf++) {
  655.         entry.ck_flag = flag = fields[nf];
  656.         np = cp;
  657.         if ((cp = get_pattern(np, &len, nfield)) == NULL) goto bad_entry;
  658.  
  659.         if ((flag & KILL_CASE_MATCH) == 0)
  660.         fold_string(np);
  661.  
  662.         entry.ck_pattern_index = ftell(patternf);
  663.  
  664.         if (fwrite((char *)&entry, sizeof(entry), 1, compf) != 1)
  665.         goto err3;
  666.  
  667.         if (fwrite(np, sizeof(char), len, patternf) != len)
  668.         goto err3;
  669.  
  670.         header.ckh_entries++;
  671.     }
  672.     }
  673.  
  674.     header.ckh_pattern_size = ftell(patternf);
  675.  
  676.     fclose(patternf);
  677.     patternf = open_file(temp_file, OPEN_READ | OPEN_UNLINK);
  678.     if (patternf == NULL) goto err2;
  679.  
  680.     header.ckh_pattern_offset = ftell(compf);
  681.  
  682.     while ((c = getc(patternf)) != EOF)
  683.     putc(c, compf);
  684.  
  685.     fclose(patternf);
  686.  
  687.     rewind(compf);
  688.  
  689.     header.ckh_magic = COMP_KILL_MAGIC;
  690.     header.ckh_db_check = master.db_created;
  691.  
  692.     if (fwrite((char *)&header, sizeof(header), 1, compf) != 1)
  693.     goto err2;
  694.  
  695.     fclose(compf);
  696.     fclose(killf);
  697.     if (dropf != NULL) fclose(dropf);
  698.  
  699.     if (any_errors) {
  700.     putchar(NL);
  701.     any_key(0);
  702.     }
  703.  
  704.     return 1;
  705.  
  706.  bad_entry:
  707.     printf("Incomplete kill file entry:\n%s", line);
  708.     fl;
  709.     any_errors++;
  710.  
  711.  drop_entry:
  712.     if (dropf == NULL) {
  713.     dropf = open_file(relative(nn_directory, KILL_FILE),
  714.               OPEN_UPDATE | DONT_CREATE);
  715.     if (dropf == NULL) goto next_entry;
  716.     }
  717.     fseek(dropf, cur_line_start, 0);
  718.     fwrite("# ", sizeof(char), 2, dropf);
  719.     goto next_entry;
  720.  
  721.  err3:
  722.     fclose(patternf);
  723.     unlink(temp_file);
  724.  err2:
  725.     fclose(compf);
  726.     rm_kill_file();
  727.  err1:
  728.     fclose(killf);
  729.     if (dropf != NULL) fclose(dropf);
  730.  
  731.     msg("cannot compile kill file");
  732.     return 0;
  733. }
  734.  
  735. init_kill()
  736. {
  737.     FILE *killf;
  738.     comp_kill_header header;
  739.     comp_kill_entry  entry;
  740.     register kill_list_entry *kl;
  741.     register kill_group_regexp *tb;
  742.     register group_header *gh;
  743.     time_t kill_age, comp_age;
  744.     register long n;
  745.     int first_try = 1;
  746.     import char delayed_msg[];
  747.  
  748.     Loop_Groups_Header(gh)
  749.     gh->kill_list = NULL;
  750.  
  751.     kill_age = file_exist(relative(nn_directory, KILL_FILE), "frw");
  752.     if (kill_age == 0) return 0;
  753.  
  754.     comp_age = file_exist(relative(nn_directory, COMPILED_KILL), "fr");
  755.  again:
  756.     if (comp_age < kill_age && !compile_kill_file()) return 0;
  757.  
  758.     kill_tab = NULL;
  759.     kill_patterns = NULL;
  760.     group_regexp_table = NULL;
  761.     regexp_table_size = 0;
  762.  
  763.     killf = open_file(relative(nn_directory, COMPILED_KILL), OPEN_READ);
  764.     if (killf == NULL) return 0;
  765.  
  766.     if (fread((char *)&header, sizeof(header), 1, killf) != 1) goto err;
  767.     /* MAGIC check: format changed or using different hardware */
  768.     if (header.ckh_magic != COMP_KILL_MAGIC) goto err;
  769.     /* DB check: if database is rebuilt, group numbers may change */
  770.     if (header.ckh_db_check != master.db_created) goto err;
  771.  
  772.     kill_patterns = newstr(header.ckh_pattern_size);
  773.     kill_tab = newobj(kill_list_entry, header.ckh_entries);
  774.     if (regexp_table_size = header.ckh_regexp_size)
  775.     group_regexp_table = newobj(kill_group_regexp, header.ckh_regexp_size);
  776.  
  777.     fseek(killf, (off_t)(header.ckh_entries * sizeof(entry)), 1);
  778.     if (fread(kill_patterns, sizeof(char), (int)header.ckh_pattern_size, killf)
  779.     !=  header.ckh_pattern_size) goto err;
  780.  
  781.     tb = group_regexp_table;
  782.  
  783.     fseek(killf, (off_t)sizeof(header), 0);
  784.     for (n = header.ckh_entries, kl = kill_tab; --n >= 0; kl++) {
  785.     if (fread((char *)&entry, sizeof(entry), 1, killf) != 1) goto err;
  786.     if (header.ckh_pattern_size <= entry.ck_pattern_index ||
  787.         entry.ck_pattern_index < 0) goto err;
  788.  
  789.     kl->kill_pattern = kill_patterns + entry.ck_pattern_index;
  790.     kl->kill_flag = entry.ck_flag;
  791.  
  792.     if (kl->kill_flag & KILL_ON_REGEXP)
  793.         kl->kill_regexp = regcomp(kl->kill_pattern);
  794.     else
  795.         kl->kill_regexp = NULL;
  796.  
  797.     if (kl->kill_flag & GROUP_REGEXP) {
  798.         if (kl->kill_flag & GROUP_REGEXP_HDR) {
  799.         if (header.ckh_pattern_size <= entry.ck_group ||
  800.             entry.ck_group < 0) goto err;
  801.         tb->group_regexp = regcomp(kill_patterns + entry.ck_group);
  802.         } else
  803.         tb->group_regexp = NULL;
  804.         tb->kill_entry = kl;
  805.         tb++;
  806.     } else
  807.     if (entry.ck_group >= 0) {
  808.         gh = active_groups + entry.ck_group;
  809.         kl->next_kill = (kill_list_entry *)(gh->kill_list);
  810.         gh->kill_list = (char *)kl;
  811.     } else {
  812.         kl->next_kill = global_kill_list;
  813.         global_kill_list = kl;
  814.     }
  815.     }
  816.  
  817.     fclose(killf);
  818.  
  819.     return 1;
  820.  
  821.  err:
  822.     if (group_regexp_table != NULL) freeobj(group_regexp_table);
  823.     if (kill_patterns != NULL) freeobj(kill_patterns);
  824.     if (kill_tab != NULL) freeobj(kill_tab);
  825.  
  826.     fclose(killf);
  827.     rm_kill_file();
  828.     if (first_try) {
  829.     first_try = 0;
  830.     comp_age = 0;
  831.     goto again;
  832.     }
  833.  
  834.     strcpy(delayed_msg, "Error in compiled kill file (ignored)");
  835.  
  836.     Loop_Groups_Header(gh)
  837.     gh->kill_list = NULL;
  838.  
  839.     global_kill_list = NULL;
  840.     group_regexp_table = NULL;
  841.  
  842.     return 0;
  843. }
  844.  
  845.  
  846.  
  847. rm_kill_file()
  848. {
  849.     unlink(relative(nn_directory, COMPILED_KILL));
  850. }
  851.  
  852.  
  853. static free_kill_list(kl)
  854. register kill_list_entry *kl;
  855. {
  856.     register kill_list_entry *nxt;
  857.     while (kl) {
  858.     nxt = kl->next_kill;
  859.     if (kl->kill_regexp != NULL) freeobj(kl->kill_regexp);
  860.     if ((kl->kill_flag & COMP_KILL_ENTRY) == 0) {
  861.         if (kl->kill_pattern != NULL) freeobj(kl->kill_pattern);
  862.         freeobj(kl);
  863.     }
  864.     kl = nxt;
  865.     }
  866. }
  867.  
  868. free_kill_entries()
  869. {
  870.     register group_header *gh;
  871.     register kill_group_regexp *tb;
  872.     register int n;
  873.  
  874.     Loop_Groups_Header(gh)
  875.     if (gh->kill_list) {
  876.         free_kill_list((kill_list_entry *)(gh->kill_list));
  877.         gh->kill_list = NULL;
  878.     }
  879.  
  880.     free_kill_list(global_kill_list);
  881.     global_kill_list = NULL;
  882.  
  883.     if (tb = group_regexp_table) {
  884.     for (n = regexp_table_size; --n >= 0; tb++)
  885.         if (tb->group_regexp != NULL) freeobj(tb->group_regexp);
  886.  
  887.     freeobj(group_regexp_table);
  888.     group_regexp_table = NULL;
  889.     }
  890.  
  891.     if (kill_patterns != NULL) freeobj(kill_patterns);
  892.     if (kill_tab != NULL) freeobj(kill_tab);
  893. }
  894.  
  895.  
  896. static flag_type pk_prev_and;
  897.  
  898. static print_kill(kl)
  899. register kill_list_entry *kl;
  900. {
  901.     register flag_type flag = kl->kill_flag;
  902.  
  903.     if (pg_next() < 0) return -1;
  904.  
  905.     if (pk_prev_and)
  906.     printf("\r    AND ");
  907.     else
  908.     printf("\r%s%s ON ",
  909.            flag & AUTO_KILL ? "AUTO KILL" :
  910.            flag & AUTO_SELECT ? "AUTO SELECT" : "",
  911.  
  912.            (flag & KILL_UNLESS_MATCH) == 0 ? "" :
  913.            flag & AUTO_KILL ? " UNLESS" :
  914.            flag & AUTO_SELECT ? "" : "KEEP");
  915.  
  916.     printf("%s '%.35s'%s\n",
  917.        flag & ON_SUBJECT ? "SUBJECT" :
  918.        flag & ON_SENDER ? "NAME" : "????",
  919.  
  920.        kl->kill_pattern,
  921.  
  922.        flag & KILL_CASE_MATCH ?
  923.        (flag & KILL_ON_REGEXP ? " (re exact)" : " (exact)") :
  924.        (flag & KILL_ON_REGEXP ? " (re fold)" : ""));
  925.  
  926.     pk_prev_and = flag & AND_MATCH;
  927.  
  928.     return 0;
  929. }
  930.  
  931. dump_kill_list()
  932. {
  933.     register kill_list_entry *kl;
  934.  
  935.     pg_init(0, 1);
  936.     pg_next();
  937.  
  938.     kl = (kill_list_entry *)(current_group->kill_list);
  939.     if (current_kill_group != current_group) build_regexp_kill();
  940.  
  941.     if (kl == NULL && regexp_kill_list == NULL) {
  942.     printf("No kill entries for %s", current_group->group_name);
  943.     } else {
  944.     so_printf("\1GROUP %s kill list entries\1", current_group->group_name);
  945.  
  946.     pk_prev_and = 0;
  947.     for ( ; kl; kl = kl->next_kill)
  948.         if (print_kill(kl) < 0) goto out;
  949.  
  950.     pk_prev_and = 0;
  951.     for (kl = regexp_kill_list ; kl; kl = kl->next_kill)
  952.         if (print_kill(kl) < 0) goto out;
  953.  
  954.     if (pg_next() < 0) goto out;
  955.     }
  956.  
  957.     if (pg_next() < 0) goto out;
  958.     so_printf("\1GLOBAL kill list entries:\1");
  959.  
  960.     pk_prev_and = 0;
  961.     for (kl = global_kill_list; kl != NULL; kl = kl->next_kill)
  962.     if (print_kill(kl) < 0) goto out;
  963.  
  964.  out:
  965.     pg_end();
  966. }
  967.