home *** CD-ROM | disk | FTP | other *** search
/ back2roots/padua / padua.7z / padua / uucp / duucp-1.17 / AU-117b4-src.lha / src / dmail / load_mail.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-01-18  |  16.6 KB  |  880 lines

  1. /*#define PROCDEBUG*/
  2. /*
  3.  *  LOAD_MAIL.C
  4.  *
  5.  *  (C) Copyright 1985-1990 by Matthew Dillon,    All Rights Reserved.
  6.  *
  7.  *  file-io routines to scan the mail file and load required information.
  8.  *
  9.  *
  10.  *  Global Routines:    HOLD_LOAD()        hold on loading mail after change
  11.  *            NOHOLD_LOAD()        hold off.. load if changes
  12.  *            LOAD_CHANGES()        reload mail if changed
  13.  *            LOAD_MAIL()        load/reload mail
  14.  *            SAVE_FILE()        save mail items back to spool
  15.  *            CHECK_NEW_MAIL()    check for new mail
  16.  *            WRITE_FILE()        append mail items to a file
  17.  *            GET_EXTRA_OVR()     ret index of Field (create if not)
  18.  *            ADD_EXTRA()        add another field (reloads mail)
  19.  *            DELETE_EXTRA()        delete a field
  20.  *            GET_EXTRA()        ret index of Field, or error
  21.  *            M_SELECT()        select on current message list
  22.  *
  23.  *
  24.  *  Static Routines:    LOAD_HASH()        load hash table from fields list
  25.  *            FREE_ENTRY()        unload EVERYTHING
  26.  *            FREE_TABLE()        unload all Fields table
  27.  *            LOAD_FILE()        raw file loading/counting
  28.  *
  29.  *
  30.  */
  31.  
  32. #include <stdio.h>
  33. #include <string.h>
  34. #include <sys/file.h>
  35. #include <time.h>
  36. #include "dmail.h"
  37.  
  38. #ifdef PROCDEBUG
  39. #define PROC(x)     static const char _proc [] = { x }; static const time_t _tt; time (&_tt); printf ("%s: enter\n", _proc)
  40. #define D(x)        printf ("%s: ", _proc); printf x
  41. #define EXIT(x)     printf ("%s: exit (elapsed %ld) ", _proc, (time (NULL) - _tt)); printf x
  42. #else
  43. #define PROC(x)
  44. #define D(x)
  45. #define EXIT(x)
  46. #endif
  47.  
  48. #ifndef LOCK_EX
  49. #define LOCK_SH     0x01        /* shared file lock */
  50. #define LOCK_EX     0x02        /* exclusive file lock */
  51. #define LOCK_NB     0x04        /* don't block when locking */
  52. #define LOCK_UN     0x08        /* unlock file */
  53. #endif
  54.  
  55. Prototype void hold_load (void);
  56. Prototype void nohold_load (void);
  57. Prototype void load_changes (void);
  58. Prototype int  initial_load_mail (void);
  59. Prototype void do_flock (int, int);
  60. Prototype void free_entry (void);
  61. Prototype int  save_file (int, int, int);
  62. Prototype void check_new_mail (void);
  63. Prototype int  write_file (char *, int, int, int);
  64. Prototype int  get_extra_ovr (char *);
  65. Prototype int  add_extra (char *);
  66. Prototype int  get_extra (char *);
  67. Prototype int  m_select (register char **, int);
  68.  
  69. static int  load_mail (int at, int from0);
  70. static int  load_file (int at);
  71. static void load_hash (void);
  72. static void free_table (int at, int hold);
  73. static int  search_from (FILE *fi);
  74.  
  75. #define NOHOLD    0
  76. #define HOLD    1
  77.  
  78. #define NO_BASE     0
  79. #define NO_FIELDS   1
  80. #define ENTRY_OK    2
  81.  
  82. struct FIND
  83.     Find [MAXTYPE + 1] = {
  84.         "From:"   , 5, 1, 0,
  85.         "To:"     , 3, 1, 0,
  86.         "Subject:", 8, 1, 0,
  87.         "Cc:"     , 3, 1, 0,
  88.         "Date:"   , 5, 1, 0
  89.     };
  90.  
  91. static int
  92.     File_size = 0,
  93.     changed = 0,
  94.     load_hold = 0,
  95.     Hash [256] = { 0 };
  96.  
  97. static char
  98.     *quo_quo = "";
  99.  
  100. void
  101. hold_load (void)
  102. {
  103.     PROC ("hold_load");
  104.  
  105.     load_hold = 1;
  106.  
  107.     EXIT (("\n"));
  108.     return;
  109. }
  110.  
  111. void
  112. nohold_load (void)
  113. {
  114.     PROC ("nohold_load");
  115.  
  116.     load_hold = 0;
  117.     load_changes ();
  118.  
  119.     EXIT (("\n"));
  120.     return;
  121. }
  122.  
  123. void
  124. load_changes (void)
  125. {
  126.     PROC ("load_changes");
  127.  
  128.     if (changed && !load_hold)
  129.         load_mail (Entries, 1);
  130.  
  131.     EXIT (("\n"));
  132.     return;
  133. }
  134.  
  135. int
  136. initial_load_mail (void)
  137. {
  138.     PROC ("initial_load_mail");
  139.  
  140.     if (load_mail (0, 0) < 0) {
  141.         EXIT (("-1, load_mail (0, 0) < 0\n"));
  142.         return -1;
  143.     }
  144.  
  145.     EXIT (("%ld, Entries %ld\n", Entries ? 1 : -1, Entries));
  146.     return Entries ? 1 : -1;
  147. }
  148.  
  149. static int
  150. load_mail (int at, int from0)
  151. {
  152.     FILE
  153.         *fi;
  154.     int
  155.         i,
  156.         count,
  157.         file_size;
  158.  
  159.     PROC ("load_mail");
  160.     D (("at %ld, from0 %ld\n", at, from0));
  161.  
  162.     if (No_load_mail) {
  163.         EXIT (("-1, NoLoadMail\n"));
  164.         return -1;
  165.     }
  166.     push_break ();
  167.  
  168.     load_hash ();
  169.     if (from0)
  170.         free_table (0, HOLD);
  171.     else
  172.         free_table (at, NOHOLD);
  173.  
  174.     fi = fopen (mail_file, "r+");
  175.  
  176.     if (m_fi)
  177.         fclose (m_fi);
  178.     m_fi = fopen (mail_file, "r+");
  179.  
  180.     if (fi == NULL    ||  m_fi == NULL) {
  181.         pop_break ();
  182.         EXIT (("-1, fi 0x%lx m_fi 0x%lx\n", fi, m_fi));
  183.         return -1;
  184.     }
  185.  
  186.     do_flock (fileno (m_fi), LOCK_EX);
  187.     if (at) {
  188.         D (("fseek (0x%lx, %ld, 0)\n", fi, Entry [at].fpos));
  189.         fseek (fi, Entry [at].fpos, 0);
  190.     }
  191. #if 0
  192.     else {
  193.         D (("fseek (0x%lx, 0, 0)\n", fi, 0, 0));
  194.         fseek (fi, 0, 0);
  195.     }
  196. #endif
  197.  
  198.     count = Entries;
  199.     while (search_from (fi))
  200.         ++count;
  201.  
  202.     if (Entries != count) {
  203.         if (!lmessage_overide)
  204.             printf ("%d Other Items loaded\n", count - Entries);
  205.         lmessage_overide = 0;
  206.         Entry = (struct ENTRY *) realloc (Entry, sizeof (*Entry) * (count + 1));
  207.         bzero (&Entry [Entries], sizeof (*Entry) * (count + 1 - Entries));
  208.     }
  209.     Entries = count;
  210.  
  211.     for (i = at; i < Entries; ++i) {
  212.         Entry [i].no  = 0;
  213.         Entry [i].status = 0;
  214.     }
  215.     Entry [i].fpos = File_size = file_size = ftell (fi);
  216.     D (("ftell (0x%lx) == %ld\n", fi, File_size));
  217.     fclose (fi);
  218.  
  219.     load_file ((from0) ? 0 : at);
  220.     if (file_size != File_size) {        /* Last entry incomplete?        */
  221.         free_table (Entries - 1, NOHOLD);
  222.     }
  223.     changed = 0;
  224.     if (SelAll)
  225.         m_select (Nulav, 0);
  226.     flock (fileno (m_fi), LOCK_UN);
  227.     pop_break ();
  228.  
  229.     EXIT (("1\n"));
  230.     return 1;
  231. }
  232.  
  233. void
  234. do_flock (int fd, int stat)
  235. {
  236.     PROC ("do_flock");
  237.  
  238. #ifdef AMIGA
  239.     /* FIXME - use OwnDevUnit */
  240. #else
  241.     if (flock (fd, stat | LOCK_NB) < 0) {
  242.         puts ("File in use, Waiting for lock");
  243.         flock (fd, stat);
  244.         puts ("Have lock");
  245.     }
  246. #endif
  247.     EXIT (("\n"));
  248.     return;
  249. }
  250.  
  251. static int
  252. load_file (int at)
  253. {
  254.     FILE
  255.         *fi;
  256.     char
  257.         *next,
  258.         *ptr;
  259.     int
  260.         i,
  261.         bit,
  262.         maxbit,
  263.         len,
  264.         count,
  265.         havefrom;
  266.     extern int
  267.         _bufsiz;
  268.  
  269.     PROC ("load_file");
  270.     D (("at %ld _bufsiz %ld\n", at, _bufsiz));
  271.  
  272.     maxbit = 0;
  273.     for (i = 0; Find [i].search != NULL; ++i)
  274.         maxbit = (maxbit << 1) | 1;
  275.     fi = fopen (mail_file, "r");
  276.     count = -1;
  277.     havefrom = 0;
  278.     while (havefrom || search_from (fi)) {
  279.         havefrom = 0;
  280.         if (++count >= Entries)
  281.             break;
  282.         len = strlen (Buf) - 1;
  283.         Buf [len] = '\0';
  284.         next = next_word (Buf);
  285.         len -= next - Buf;
  286.         Entry [count].fpos = ftell (fi);
  287.         Entry [count].from = xmalloc (len + 1);
  288.         /*D (("ftell (0x%lx) == %ld\n", fi, Entry [count].fpos));*/
  289.         bcopy (next, Entry [count].from, len + 1);
  290.  
  291.         /* SEARCH FIELD LIST */
  292.  
  293.         bit = 0;
  294.         if (XDebug)
  295.             printf ("No %d  ---------------------\n", count + 1);
  296.         while (fgets (Buf, MAXFIELDSIZE, fi) != NULL) {
  297.             if (Buf [0] == '\n')
  298.                 break;
  299.             if (isfrom (Buf)) {
  300.                 havefrom = 1;
  301.                 break;
  302.             }
  303.             len = strlen (Buf) - 1;
  304.             Buf [len] = '\0';
  305.             if (XDebug)
  306.                 printf ("CHECK: %s  ", Buf);
  307.             next = next_word (Buf);
  308.             len -= next - Buf;
  309.             if (XDebug)
  310.                 printf ("HASH: %d\n", Hash [*(ubyte *) Buf]);
  311.             if (Hash [*(ubyte *) Buf] == 0)
  312.                 continue;
  313.             if (Hash [*(ubyte *) Buf] > 0) {
  314.                 i = Hash [*(ubyte *) Buf] & 0xff;
  315.                 if (strncmp (Find [i].search, Buf, Find [i].len) == 0)
  316.                     goto found;
  317.                 continue;
  318.             }
  319.             for (i = -Hash [*(ubyte *) Buf] & 0xff; Find [i].search; ++i) {
  320.                 if (strncmp (Find [i].search, Buf, Find [i].len) == 0)
  321.                     goto found;
  322.             }
  323.             continue;
  324. found:
  325.             if (XDebug)
  326.                 printf ("Found: %d %s\n", i, Buf);
  327.             if (Find [i].notnew == 0) {
  328.                 Find [i].notnew = 1;
  329.                 ptr = Buf;
  330.                 while (*ptr  &&  *ptr != ':')
  331.                     ++ptr;
  332.                 ++ptr;
  333.                 Find [i].search =
  334.                     realloc (Find [i].search, ptr - Buf + 1);
  335.                 strncpy (Find [i].search, Buf, ptr - Buf);
  336.                 *(Find [i].search + (ptr - Buf)) = '\0';
  337.                 Find [i].len = strlen (Find [i].search);
  338.             }
  339.             compile_field (Buf, fi);
  340.             Entry [count].fields [i] =
  341.                 xmalloc (strlen (next) + 1);
  342.             strcpy (Entry [count].fields [i], next);
  343.             if ((bit |= (1 << i)) == maxbit)
  344.                 break;
  345.         }
  346.         if (bit != maxbit) {
  347.                 for (i = 0; Find [i].search; ++i) {
  348.                     if (((1 << i) & bit) == 0) {
  349.                         Entry [count].fields [i] = quo_quo;
  350.                     }
  351.                 }
  352.         }
  353.     }
  354.  
  355.     File_size = ftell (fi);
  356.     D (("ftell (0x%lx) == %ld\n", fi, File_size));
  357.     fclose (fi);
  358.  
  359.     EXIT (("1\n"));
  360.     return 1;
  361. }
  362.  
  363. static void
  364. load_hash (void)
  365. {
  366.     int
  367.         i,
  368.         v,
  369.         c;
  370.  
  371.     PROC ("load_hash");
  372.  
  373.     bzero (Hash, sizeof (Hash));
  374.     for (i = 0; Find [i].search; ++i) {
  375.         if (XDebug)
  376.             printf ("LOADH %d %s\n", i, Find [i].search);
  377.  
  378.         c = *(ubyte *) Find [i].search;
  379.         v = Hash[c];
  380.  
  381.         if (v == 0) {
  382.             Hash [c] = i | 0x100;
  383.         }
  384.         else {
  385.             if (v < 0)
  386.                 v = -v;
  387.             if (i < (v & 0xFF))
  388.                 v = i | 0x100;
  389.             Hash [c] = -v;
  390.         }
  391.     }
  392.  
  393.     EXIT (("\n"));
  394.     return;
  395. }
  396.  
  397. void
  398. free_entry (void)
  399. {
  400.     /*PROC ("free_entry");*/
  401.  
  402.     free_table (0, NOHOLD);
  403.     Entry = (struct ENTRY *) realloc (Entry, sizeof (*Entry));
  404.     bzero (Entry[0].fields, sizeof (Entry [0].fields));
  405.     File_size = Entries = 0;
  406.     Entry->status = Entry->no = Entry->fpos = Current = 0;
  407.     Listsize = 3;
  408.     if (m_fi) {
  409.         fclose (m_fi);
  410.         m_fi = NULL;
  411.     }
  412.     /*EXIT (("\n"));*/
  413.     return;
  414. }
  415.  
  416. static void
  417. free_table (int at, int hold)
  418. {
  419.     int
  420.         i,
  421.         j;
  422.     /*PROC ("free_table");*/
  423.  
  424.     for (i = at; i < Entries; ++i) {
  425.         xfree (Entry [i].from);
  426.         for (j = 0; Find [j].search != NULL; ++j) {
  427.             if (Entry [i].fields [j] != quo_quo)
  428.                 xfree (Entry [i].fields [j]);
  429.         }
  430.     }
  431.     Entries = (hold == HOLD) ? Entries : at;
  432.     File_size = at ? Entry [Entries].fpos : 0;
  433.  
  434.     /*EXIT (("\n"));*/
  435.     return;
  436. }
  437.  
  438. static int
  439. search_from (FILE *fi)
  440. {
  441.     while (fgets (Buf, MAXFIELDSIZE, fi) != NULL) {
  442.         if (isfrom (Buf)) {
  443.             return 1;
  444.         }
  445.     }
  446.  
  447.     return 0;
  448. }
  449.  
  450. int
  451. save_file (int reload, int mark, int notmark)
  452. {
  453.     FILE
  454.         *fiscr;
  455.     int
  456.         fdscr,
  457.         i,
  458.         count;
  459.     char
  460.         scratch [64];
  461.  
  462.     PROC ("save_file");
  463.     D (("reload %ld, mark %ld, notmark %ld\n", reload, mark, notmark));
  464.  
  465.     for (i = 0; i < Entries; ++i) {
  466.         if ((Entry[i].status & mark) != mark  || (~Entry[i].status & notmark) != notmark)
  467.             break;
  468.     }
  469.     if (i == Entries) {
  470.         m_select (Nulav, M_RESET);
  471.         printf ("No Changes Made\n");
  472.         return Entries;
  473.     }
  474.  
  475.     if (m_fi == NULL) {
  476.         EXIT (("-1, no open m_fi\n"));
  477.         return -1;
  478.     }
  479.  
  480.     count = 0;
  481.     sprintf (scratch, "t:dmail%d", getpid());
  482.     do_flock (fileno (m_fi), LOCK_EX);
  483.     fdscr = open (scratch, O_RDWR | O_CREAT | O_TRUNC, MAILMODE);
  484. #ifndef _DCC
  485. #ifdef AMIGA        /*    fix bug in Lattice C fdopen */
  486.     fiscr = fopen ("nil:", "w");
  487.     fclose (fiscr);
  488. #endif
  489. #endif
  490.     fiscr = fdopen (fdscr, "a+");
  491.     for (i = 0; i < Entries; ++i) {
  492.         if ((Entry[i].status & mark) == mark  &&
  493.                 (~Entry[i].status & notmark) == notmark) {
  494.             ++count;
  495.             fputs ("From ", fiscr);
  496.             fputs (Entry[i].from, fiscr);
  497.             putc ('\n', fiscr);
  498.             D (("fseek (0x%lx, %ld, 0)\n", m_fi, Entry [i].fpos));
  499.             fseek (m_fi, Entry [i].fpos, 0);
  500.             while (fgets (Buf, MAXFIELDSIZE, m_fi) != NULL) {
  501.                 if (isfrom (Buf))
  502.                     break;
  503.                 fputs (Buf, fiscr);
  504.             }
  505.         }
  506.     }
  507.  
  508.     /*
  509.      *  If new mail has come in, append to the scratch file as well.
  510.      *  NOTE: for some machines like the Amiga an already open descriptor
  511.      *      does not know about any new data, thus we cannot simply
  512.      *      use m_fi .
  513.      */
  514.  
  515.     {
  516.         FILE
  517.             *fi;
  518.  
  519.         if (fi = fopen (mail_file, "r")) {
  520.             D (("fseek (0x%lx, %ld, 0)\n", fi, File_size));
  521.             fseek (fi, File_size, 0);
  522.             while (fgets (Buf, MAXFIELDSIZE, fi))
  523.                 fputs (Buf, fiscr);
  524.             fclose (fi);
  525.         }
  526.     }
  527.  
  528.     /* Write scratch file back to mail file, or try to */
  529.  
  530.     fflush (fiscr);
  531.     fflush (m_fi);
  532.  
  533.     D (("lseek (%ld, 0, 0)\n", fdscr));
  534.     lseek (fdscr, 0 ,0);
  535. #ifdef UNIX
  536.     lseek (fileno(m_fi), 0, 0);
  537.     while ((i = read (fdscr, Buf, MAXFIELDSIZE)) > 0)
  538.         write (fileno(m_fi), Buf, i);
  539.     ftruncate (fileno(m_fi), lseek (fileno(m_fi), 0, 1));
  540. #else
  541.     fclose (m_fi);
  542.     if (m_fi = fopen (mail_file, "w")) {
  543.         while ((i = read (fdscr, Buf, MAXFIELDSIZE)) > 0)
  544.             write (fileno (m_fi), Buf, i);
  545.         fclose (m_fi);
  546.         m_fi = fopen (mail_file, "r+");
  547.     }
  548.     if (m_fi == NULL) {
  549.         printf ("Unable to re-open %s !\n", mail_file);
  550.         EXIT (("-1, can't reopen %s\n", mail_file));
  551.         return -1;
  552.     }
  553. #endif
  554.     D (("lseek (fileno (0x%lx), 0, 2)\n", m_fi));
  555.     if (lseek (fileno (m_fi), 0, 2) == 0  &&  !reload) {
  556.         if (Did_cd == 0) {
  557.             fclose(m_fi);
  558.             m_fi = NULL;
  559.             if (unlink (mail_file) == 0)
  560.                 printf ("%s  Removed\n", mail_file);
  561.             else
  562.                 printf ("0 messages left in %s\n", mail_file);
  563.         }
  564.     }
  565.     fclose (fiscr);
  566.  
  567.     if (m_fi)
  568.         fclose (m_fi);    /* Effectively unlocks the descriptor */
  569.     m_fi = NULL;
  570.  
  571.     unlink (scratch);
  572.  
  573.     if (reload) {
  574.         free_entry ();
  575.         load_mail (0, 0);
  576.     }
  577.     m_select (Nulav, M_RESET);
  578.  
  579.     EXIT (("%ld\n", count));
  580.     return (count);
  581. }
  582.  
  583. void
  584. check_new_mail (void)
  585. {
  586.     FILE
  587.         *fi;
  588.  
  589.     PROC ("check_new_mail");
  590.  
  591.     push_break ();
  592.  
  593.     if (m_fi == NULL) {
  594.         m_fi = fopen (mail_file, "r+");
  595.         if (m_fi == NULL) {
  596.             pop_break ();
  597.             EXIT (("no m_fi\n"));
  598.             return;
  599.         }
  600.     }
  601.  
  602.     if (fi = fopen (mail_file, "r")) {
  603.         D (("fseek (0x%lx, 0, 2), ftell (0x%lx) == %ld\n", fi, fi, ftell (fi)));
  604.         if (fseek (fi, 0, 2) < 0 || ftell (fi) != File_size)
  605.             load_mail (Entries, 1);
  606.         fclose (fi);
  607.     }
  608.  
  609.     pop_break ();
  610.  
  611.     EXIT (("\n"));
  612.     return;
  613. }
  614.  
  615. int
  616. write_file (char *file, int modes, int mark, int notmark)
  617. {
  618.     int
  619.         i,
  620.         fd = 1,
  621.         notopen = 1;
  622.     FILE
  623.         *fi = NULL;
  624.  
  625.     PROC ("write_file");
  626.     D (("file %s, modes 0%o, mark %ld, notmark %ld\n",
  627.         file, modes, mark, notmark));
  628.  
  629.     for (i = 0; i < Entries; ++i) {
  630.         if ((Entry[i].status & mark) == mark  &&
  631.                 (~Entry[i].status & notmark) == notmark) {
  632.             if (notopen) {
  633.                 notopen = 0;
  634.                 fd = open (file, O_APPEND | O_WRONLY | modes, MAILMODE);
  635.                 if (fd < 0) {
  636.                     EXIT (("-1, can't open %s\n", file));
  637.                     return -1;
  638.                 }
  639.                 do_flock (fd, LOCK_EX);
  640. #ifndef _DCC
  641. #ifdef AMIGA            /*  fix bug in Lattice C fdopen */
  642.                 fi = fopen ("nil:", "w");
  643.                 fclose (fi);
  644. #endif
  645. #endif
  646.                 fi = fdopen (fd, "a");
  647.             }
  648.             fputs ("From ", fi);
  649.             fputs (Entry [i].from, fi);
  650.             putc ('\n', fi);
  651.             if (m_fi) {
  652.                 D (("fseek (0x%lx, %ld, 0)\n", m_fi, Entry [i].fpos));
  653.                 fseek (m_fi, Entry [i].fpos, 0);
  654.                 while (fgets (Buf, MAXFIELDSIZE, m_fi) != NULL) {
  655.                     if (isfrom (Buf))
  656.                         break;
  657.                     fputs (Buf, fi);
  658.                 }
  659.             }
  660.         }
  661.     }
  662.  
  663.     if (!notopen)
  664.         fclose (fi);
  665.  
  666.     EXIT (("1\n"));
  667.     return 1;
  668. }
  669.  
  670. /*
  671.  * Basic scheme: Each entry has a fields list.    Each entry in the fields list
  672.  * is guarenteed to be a valid malloc'd pointer (except some may be set to
  673.  * quo_quo).
  674.  *
  675.  * The find[] struct array holds the field name and length, the index
  676.  * corresponding to the index into the field[] in an Entry.
  677.  *
  678.  * The header and width arrays hold the list format.
  679.  */
  680.  
  681. int
  682. get_extra_ovr (char *str)
  683. {
  684.     register int
  685.         i;
  686.  
  687.     PROC ("get_extra_ovr");
  688.  
  689.     i = get_extra (str);
  690.     if (i < 0) {
  691.         i = add_extra (str);
  692.         load_changes ();
  693.     }
  694.  
  695.     EXIT (("\n"));
  696.     return i;
  697. }
  698.  
  699. /*
  700.  *  If there's room to add it, append to end.
  701.  *  Else Find oldest field which doesn't exist in the setlist and replace it
  702.  *  with the new one.
  703.  */
  704.  
  705. int
  706. add_extra (char *str)
  707. {
  708.     register int
  709.         i,
  710.         j,
  711.         j_age,
  712.         k;
  713.  
  714.     PROC ("add_extra");
  715.  
  716.     for (i = EXSTART; i < MAXTYPE; ++i) {
  717.         if (Find [i].search == NULL)
  718.             break;
  719.         ++Find [i].age;
  720.     }
  721.     if (i == MAXTYPE) {            /* No room to add onto end */
  722.         j = j_age = -1;
  723.         for (i = EXSTART; i < MAXTYPE; ++i) {
  724.             for (k = 0; k < Listsize; ++k) {
  725.                 if (i == header [k])
  726.                     break;
  727.             }
  728.             if (k == Listsize  &&  Find [i].age > j_age) {
  729.                 j = i;
  730.                 j_age = Find [i].age;
  731.             }
  732.         }
  733.         i = j;
  734.     }
  735.     if (i < 0) {
  736.         EXIT (("-1\n"));
  737.         return -1;
  738.     }
  739.  
  740.     push_break ();
  741.  
  742.     if (Find [i].search)
  743.         xfree (Find [i].search);
  744.     Find [i].len = strlen (str);
  745.     Find [i].search = xmalloc (Find [i].len + 1);
  746.     Find [i].notnew = Find [i].age = 0;
  747.     strcpy (Find [i].search, str);
  748.     changed = 1;
  749.     D (("set changed = 1\n"));
  750.     for (j = 0; j < Entries; ++j) {
  751.         if (Entry [j].fields [i] && Entry [j].fields [i] != quo_quo)
  752.             xfree (Entry [j].fields [i]);
  753.         Entry [j].fields [i] = quo_quo;
  754.     }
  755.  
  756.     pop_break ();
  757.  
  758.     EXIT (("%ld\n", i));
  759.     return i;
  760. }
  761.  
  762. int
  763. get_extra (char *str)
  764. {
  765.     int
  766.         len = strlen (str),
  767.         i;
  768.  
  769.     PROC ("get_extra");
  770.     D (("string '%s' (len %ld)\n", str, len));
  771.  
  772.     for (i = 0; Find [i].search; ++i) {
  773.         if (strncmp (str, Find [i].search, len) == 0) {
  774.             Find [i].age = 0;
  775.  
  776.             EXIT (("%ld (found '%s')\n", i, str));
  777.             return i;
  778.         }
  779.     }
  780.  
  781.     EXIT (("-1 (not found '%s')\n", str));
  782.     return -1;
  783. }
  784.  
  785. int
  786. m_select (register char **sav, int mode)
  787. {
  788.     char
  789.         *ptr,
  790.         *dest,
  791.         l_map [256];
  792.     int
  793.         idx [MAXLIST],
  794.         ix = 0,
  795.         ok,
  796.         not,
  797.         len,
  798.         scr;
  799.     register int
  800.         i,
  801.         j,
  802.         avi;
  803.  
  804.     PROC ("m_select");
  805.  
  806.     for (i = 0;i < 256; ++i)
  807.         l_map[i] = i;
  808.     for (i = 'A'; i <= 'Z'; ++i)
  809.         l_map[i] += 'a' - 'A';
  810.  
  811.     hold_load ();
  812.     i = 0;
  813.     idx [ix++] = get_extra_ovr (sav [i++]);
  814.     for (; sav[i]; ++i) {
  815.         if (strcmp (sav [i], ",") == 0  &&  sav [i + 1])
  816.             idx [ix++] = get_extra_ovr (sav [++i]);
  817.     }
  818.     idx [ix] = -1;
  819.     nohold_load ();
  820.  
  821.     j = 1;
  822.     push_break ();
  823.     for (i = 0; i < Entries; ++i) {
  824.         if (mode == M_CONT  &&    Entry [i].no == 0)
  825.             continue;
  826.         ix = ok = 0;
  827.         avi = 1;
  828.         while ((ptr = sav [avi]) != NULL) {
  829.             if (ptr [0] == ',' && ptr [1] == '\0' && sav [avi + 1]) {
  830.                 ++ix;
  831.                 avi += 2;
  832.                 continue;
  833.             }
  834.             if (not = (*ptr == '!'))
  835.                 ++ptr;
  836.             len = strlen (ptr);
  837.             dest = Entry [i].fields [idx [ix]];
  838.             if (*ptr == '\0') {
  839.                 ok = 1;
  840.                 goto gotit;
  841.             }
  842.             while (*dest) {
  843.                 scr = 0;
  844.                 while (l_map [dest [scr]] == l_map [ptr [scr]] && ptr [scr])
  845.                     ++scr;
  846.                 if (ptr [scr] == '\0') {
  847.                     ok = 1;
  848.                     goto gotit;
  849.                 }
  850.                 ++dest;
  851.             }
  852.             ++avi;
  853.         }
  854. gotit:
  855.         Entry [i].no = (ok ^ not) ? j++ : 0;
  856.     }
  857.  
  858.     pop_break ();
  859.  
  860.     if (Current < 0)
  861.         Current = 0;
  862.     if (Entries) {
  863.         if (Entry [Current].no == 0) {
  864.             Current = indexof (1);
  865.             if (Current < 0) {
  866.                 Current = 0;
  867.  
  868.                 EXIT (("-1\n"));
  869.                 return -1;
  870.             }
  871.         }
  872.     }
  873.     else {
  874.         Current = -1;
  875.     }
  876.  
  877.     EXIT (("1\n"));
  878.     return 1;
  879. }
  880.