home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / isp31b3.zip / ispell / source / tree.c < prev    next >
C/C++ Source or Header  |  1995-01-23  |  21KB  |  735 lines

  1. #ifndef lint
  2. static char Rcs_Id[] =
  3.     "$Id: tree.c,v 1.56 1995/01/08 23:23:49 geoff Exp $";
  4. #endif
  5.  
  6. /*
  7.  * tree.c - a hash style dictionary for user's personal words
  8.  *
  9.  * Pace Willisson, 1983
  10.  * Hash support added by Geoff Kuenning, 1987
  11.  *
  12.  * Copyright 1987, 1988, 1989, 1992, 1993, Geoff Kuenning, Granada Hills, CA
  13.  * All rights reserved.
  14.  *
  15.  * Redistribution and use in source and binary forms, with or without
  16.  * modification, are permitted provided that the following conditions
  17.  * are met:
  18.  *
  19.  * 1. Redistributions of source code must retain the above copyright
  20.  *    notice, this list of conditions and the following disclaimer.
  21.  * 2. Redistributions in binary form must reproduce the above copyright
  22.  *    notice, this list of conditions and the following disclaimer in the
  23.  *    documentation and/or other materials provided with the distribution.
  24.  * 3. All modifications to the source code must be clearly marked as
  25.  *    such.  Binary redistributions based on modified source code
  26.  *    must be clearly marked as modified versions in the documentation
  27.  *    and/or other materials provided with the distribution.
  28.  * 4. All advertising materials mentioning features or use of this software
  29.  *    must display the following acknowledgment:
  30.  *      This product includes software developed by Geoff Kuenning and
  31.  *      other unpaid contributors.
  32.  * 5. The name of Geoff Kuenning may not be used to endorse or promote
  33.  *    products derived from this software without specific prior
  34.  *    written permission.
  35.  *
  36.  * THIS SOFTWARE IS PROVIDED BY GEOFF KUENNING AND CONTRIBUTORS ``AS IS'' AND
  37.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  38.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  39.  * ARE DISCLAIMED.  IN NO EVENT SHALL GEOFF KUENNING OR CONTRIBUTORS BE LIABLE
  40.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  41.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  42.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  43.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  44.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  45.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  46.  * SUCH DAMAGE.
  47.  */
  48.  
  49. /*
  50.  * $Log: tree.c,v $
  51.  * Revision 1.56  1995/01/08  23:23:49  geoff
  52.  * Support PDICTHOME for DOS purposes.
  53.  *
  54.  * Revision 1.55  1994/10/25  05:46:27  geoff
  55.  * Fix a comment that looked to some compilers like it might be nested.
  56.  *
  57.  * Revision 1.54  1994/01/25  07:12:15  geoff
  58.  * Get rid of all old RCS log lines in preparation for the 3.1 release.
  59.  *
  60.  */
  61.  
  62. #include <ctype.h>
  63. #include <errno.h>
  64. #include "config.h"
  65. #include "ispell.h"
  66. #include "proto.h"
  67. #include "msgs.h"
  68.  
  69. void        treeinit P ((char * p, char * LibDict));
  70. static FILE *    trydict P ((char * dictname, char * home,
  71.           char * prefix, char * suffix));
  72. static void    treeload P ((FILE * dictf));
  73. void        treeinsert P ((char * word, int wordlen, int keep));
  74. static struct dent * tinsert P ((struct dent * proto));
  75. struct dent *    treelookup P ((ichar_t * word));
  76. #if SORTPERSONAL != 0
  77. static int    pdictcmp P ((struct dent ** enta, struct dent **entb));
  78. #endif /* SORTPERSONAL != 0 */
  79. void        treeoutput P ((void));
  80. VOID *        mymalloc P ((unsigned int size));
  81. void        myfree P ((VOID * ptr));
  82. #ifdef REGEX_LOOKUP
  83. char *        do_regex_lookup P ((char * expr, int whence));
  84. #endif /* REGEX_LOOKUP */
  85.  
  86. static int        cantexpand = 0;    /* NZ if an expansion fails */
  87. static struct dent *    pershtab;    /* Aux hash table for personal dict */
  88. static int        pershsize = 0;    /* Space available in aux hash table */
  89. static int        hcount = 0;    /* Number of items in hash table */
  90.  
  91. /*
  92.  * Hash table sizes.  Prime is probably a good idea, though in truth I
  93.  * whipped the algorithm up on the spot rather than looking it up, so
  94.  * who knows what's really best?  If we overflow the table, we just
  95.  * use a double-and-add-1 algorithm.
  96.  */
  97. static int goodsizes[] =
  98.     {
  99.     53, 223, 907, 3631
  100.     };
  101.  
  102. static char        personaldict[MAXPATHLEN];
  103. static FILE *        dictf;
  104. static            newwords = 0;
  105.  
  106. void treeinit (p, LibDict)
  107.     char *        p;        /* Value specified in -p switch */
  108.     char *        LibDict;    /* Root of default dict name */
  109.     {
  110.     int            abspath;    /* NZ if p is abs path name */
  111.     char *        h;        /* Home directory name */
  112.     char        seconddict[MAXPATHLEN]; /* Name of secondary dict */
  113.     FILE *        secondf;    /* Access to second dict file */
  114.  
  115.     /*
  116.     ** If -p was not specified, try to get a default name from the
  117.     ** environment.  After this point, if p is null, the the value in
  118.     ** personaldict is the only possible name for the personal dictionary.
  119.     ** If p is non-null, then there is a possibility that we should
  120.     ** prepend HOME to get the correct dictionary name.
  121.     */
  122.     if (p == NULL)
  123.     p = getenv (PDICTVAR);
  124.     /*
  125.     ** if p exists and begins with '/' we don't really need HOME,
  126.     ** but it's not very likely that HOME isn't set anyway (on non-DOS
  127.     ** systems).
  128.     */
  129.     if ((h = getenv (HOME)) == NULL)
  130.     {
  131. #ifdef PDICTHOME
  132.     h = PDICTHOME;
  133. #else /* PDICTHOME */
  134.     return;
  135. #endif /* PDICTHOME */
  136.     }
  137.  
  138.     if (p == NULL)
  139.     {
  140.     /*
  141.      * No -p and no PDICTVAR.  We will use LibDict and DEFPAFF to
  142.      * figure out the name of the personal dictionary and where it
  143.      * is.  The rules are as follows:
  144.      *
  145.      * (1) If there is a local dictionary and a HOME dictionary,
  146.      *     both are loaded, but changes are saved in the local one.
  147.      *     The dictionary to save changes in is named "personaldict".
  148.      * (2) Dictionaries named after the affix file take precedence
  149.      *     over dictionaries with the default suffix (DEFPAFF).
  150.      * (3) Dictionaries named with the new default names
  151.      *     (DEFPDICT/DEFPAFF) take precedence over the old ones
  152.      *     (OLDPDICT/OLDPAFF).
  153.      * (4) Dictionaries aren't combined unless they follow the same
  154.      *     naming scheme.
  155.      * (5) If no dictionary can be found, a new one is created in
  156.      *     the home directory, named after DEFPDICT and the affix
  157.      *     file.
  158.      */
  159.     dictf = trydict (personaldict, (char *) NULL, DEFPDICT, LibDict);
  160.     secondf = trydict (seconddict, h, DEFPDICT, LibDict);
  161.     if (dictf == NULL  &&  secondf == NULL)
  162.         {
  163.         dictf = trydict (personaldict, (char *) NULL, DEFPDICT, DEFPAFF);
  164.         secondf = trydict (seconddict, h, DEFPDICT, DEFPAFF);
  165.         }
  166.     if (dictf == NULL  &&  secondf == NULL)
  167.         {
  168.         dictf = trydict (personaldict, (char *) NULL, OLDPDICT, LibDict);
  169.         secondf = trydict (seconddict, h, OLDPDICT, LibDict);
  170.         }
  171.     if (dictf == NULL  &&  secondf == NULL)
  172.         {
  173.         dictf = trydict (personaldict, (char *) NULL, OLDPDICT, OLDPAFF);
  174.         secondf = trydict (seconddict, h, OLDPDICT, OLDPAFF);
  175.         }
  176.     if (personaldict[0] == '\0')
  177.         {
  178.         if (seconddict[0] != '\0')
  179.         (void) strcpy (personaldict, seconddict);
  180.         else
  181.         (void) sprintf (personaldict, "%s/%s%s", h, DEFPDICT, LibDict);
  182.         }
  183.     if (dictf != NULL)
  184.         {
  185.         treeload (dictf);
  186.         (void) fclose (dictf);
  187.         }
  188.     if (secondf != NULL)
  189.         {
  190.         treeload (secondf);
  191.         (void) fclose (secondf);
  192.         }
  193.     }
  194.     else
  195.     {
  196.     /*
  197.     ** Figure out if p is an absolute path name.  Note that beginning
  198.     ** with "./" and "../" is considered an absolute path, since this
  199.     ** still means we can't prepend HOME.
  200.     */
  201.     abspath = (*p == '/'  ||  strncmp (p, "./", 2) == 0
  202.       ||  strncmp (p, "../", 3) == 0);
  203.     if (abspath)
  204.         {
  205.         (void) strcpy (personaldict, p);
  206.         if ((dictf = fopen (personaldict, "r")) != NULL)
  207.         {
  208.         treeload (dictf);
  209.         (void) fclose (dictf);
  210.         }
  211.         }
  212.     else
  213.         {
  214.         /*
  215.         ** The user gave us a relative pathname.  We will try it
  216.         ** locally, and if that doesn't work, we'll try the home
  217.         ** directory.  If neither exists, it will be created in
  218.         ** the home directory if words are added.
  219.         */
  220.         (void) strcpy (personaldict, p);
  221.         if ((dictf = fopen (personaldict, "r")) != NULL)
  222.         {
  223.         treeload (dictf);
  224.         (void) fclose (dictf);
  225.         }
  226.         else if (!abspath)
  227.         {
  228.         /* Try the home */
  229.         (void) sprintf (personaldict, "%s/%s", h, p);
  230.         if ((dictf = fopen (personaldict, "r")) != NULL)
  231.             {
  232.             treeload (dictf);
  233.             (void) fclose (dictf);
  234.             }
  235.         }
  236.         /*
  237.          * If dictf is null, we couldn't open the dictionary
  238.          * specified in the -p switch.  Complain.
  239.          */
  240.         if (dictf == NULL)
  241.         {
  242.         (void) fprintf (stderr, CANT_OPEN, p);
  243.         perror ("");
  244.         return;
  245.         }
  246.         }
  247.     }
  248.  
  249.     if (!lflag  &&  !aflag
  250.       &&  access (personaldict, 2) < 0  &&  errno != ENOENT)
  251.     {
  252.     (void) fprintf (stderr, TREE_C_CANT_UPDATE, personaldict);
  253.     (void) sleep ((unsigned) 2);
  254.     }
  255.     }
  256.  
  257. /*
  258.  * Try to open a dictionary.  As a side effect, leaves the dictionary
  259.  * name in "filename" if one is found, and leaves a null string there
  260.  * otherwise.
  261.  */
  262. static FILE * trydict (filename, home, prefix, suffix)
  263.     char *        filename;    /* Where to store the file name */
  264.     char *        home;        /* Home directory */
  265.     char *        prefix;        /* Prefix for dictionary */
  266.     char *        suffix;        /* Suffix for dictionary */
  267.     {
  268.     FILE *        dictf;        /* Access to dictionary file */
  269.  
  270.     if (home == NULL)
  271.     (void) sprintf (filename, "%s%s", prefix, suffix);
  272.     else
  273.     (void) sprintf (filename, "%s/%s%s", home, prefix, suffix);
  274.     dictf = fopen (filename, "r");
  275.     if (dictf == NULL)
  276.     filename[0] = '\0';
  277.     return dictf;
  278.     }
  279.  
  280. static void treeload (loadfile)
  281.     register FILE *    loadfile;    /* File to load words from */
  282.     {
  283.     char        buf[BUFSIZ];    /* Buffer for reading pers dict */
  284.  
  285.     while (fgets (buf, sizeof buf, loadfile) != NULL)
  286.     treeinsert (buf, sizeof buf, 1);
  287.     newwords = 0;
  288.     }
  289.  
  290. void treeinsert (word, wordlen, keep)
  291.     char *        word;    /* Word to insert - must be canonical */
  292.     int            wordlen; /* Length of the word buffer */
  293.     int            keep;
  294.     {
  295.     register int    i;
  296.     struct dent        wordent;
  297.     register struct dent * dp;
  298.     struct dent *    olddp;
  299. #ifndef NO_CAPITALIZATION_SUPPORT
  300.     struct dent *    newdp;
  301. #endif
  302.     struct dent *    oldhtab;
  303.     int            oldhsize;
  304.     ichar_t        nword[INPUTWORDLEN + MAXAFFIXLEN];
  305. #ifndef NO_CAPITALIZATION_SUPPORT
  306.     int            isvariant;
  307. #endif
  308.  
  309.     /*
  310.      * Expand hash table when it is MAXPCT % full.
  311.      */
  312.     if (!cantexpand  &&  (hcount * 100) / MAXPCT >= pershsize)
  313.     {
  314.     oldhsize = pershsize;
  315.     oldhtab = pershtab;
  316.     for (i = 0;  i < sizeof goodsizes / sizeof (goodsizes[0]);  i++)
  317.         {
  318.         if (goodsizes[i] > pershsize)
  319.         break;
  320.         }
  321.     if (i >= sizeof goodsizes / sizeof goodsizes[0])
  322.         pershsize += pershsize + 1;
  323.     else
  324.         pershsize = goodsizes[i];
  325.     pershtab =
  326.       (struct dent *) calloc ((unsigned) pershsize, sizeof (struct dent));
  327.     if (pershtab == NULL)
  328.         {
  329.         (void) fprintf (stderr, TREE_C_NO_SPACE);
  330.         /*
  331.          * Try to continue anyway, since our overflow
  332.          * algorithm can handle an overfull (100%+) table,
  333.          * and the malloc very likely failed because we
  334.          * already have such a huge table, so small mallocs
  335.          * for overflow entries will still work.
  336.          */
  337.         if (oldhtab == NULL)
  338.         exit (1);        /* No old table, can't go on */
  339.         (void) fprintf (stderr, TREE_C_TRY_ANYWAY);
  340.         cantexpand = 1;        /* Suppress further messages */
  341.         pershsize = oldhsize;    /* Put things back */
  342.         pershtab = oldhtab;        /* ... */
  343.         newwords = 1;        /* And pretend it worked */
  344.         }
  345.     else
  346.         {
  347.         /*
  348.          * Re-insert old entries into new table
  349.          */
  350.         for (i = 0;  i < oldhsize;  i++)
  351.         {
  352.         dp = &oldhtab[i];
  353.         if (dp->flagfield & USED)
  354.             {
  355. #ifdef NO_CAPITALIZATION_SUPPORT
  356.             (void) tinsert (dp);
  357. #else
  358.             newdp = tinsert (dp);
  359.             isvariant = (dp->flagfield & MOREVARIANTS);
  360. #endif
  361.             dp = dp->next;
  362. #ifdef NO_CAPITALIZATION_SUPPORT
  363.             while (dp != NULL)
  364.             {
  365.             (void) tinsert (dp);
  366.             olddp = dp;
  367.             dp = dp->next;
  368.             free ((char *) olddp);
  369.             }
  370. #else
  371.             while (dp != NULL)
  372.             {
  373.             if (isvariant)
  374.                 {
  375.                 isvariant = dp->flagfield & MOREVARIANTS;
  376.                 olddp = newdp->next;
  377.                 newdp->next = dp;
  378.                 newdp = dp;
  379.                 dp = dp->next;
  380.                 newdp->next = olddp;
  381.                 }
  382.             else
  383.                 {
  384.                 isvariant = dp->flagfield & MOREVARIANTS;
  385.                 newdp = tinsert (dp);
  386.                 olddp = dp;
  387.                 dp = dp->next;
  388.                 free ((char *) olddp);
  389.                 }
  390.             }
  391. #endif
  392.             }
  393.         }
  394.         if (oldhtab != NULL)
  395.         free ((char *) oldhtab);
  396.         }
  397.     }
  398.  
  399.     /*
  400.     ** We're ready to do the insertion.  Start by creating a sample
  401.     ** entry for the word.
  402.     */
  403.     if (makedent (word, wordlen, &wordent) < 0)
  404.     return;            /* Word must be too big or something */
  405.     if (keep)
  406.     wordent.flagfield |= KEEP;
  407.     /*
  408.     ** Now see if word or a variant is already in the table.  We use the
  409.     ** capitalized version so we'll find the header, if any.
  410.     **/
  411.     (void) strtoichar (nword, word, sizeof nword, 1);
  412.     upcase (nword);
  413.     if ((dp = lookup (nword, 1)) != NULL)
  414.     {
  415.     /* It exists.  Combine caps and set the keep flag. */
  416.     if (combinecaps (dp, &wordent) < 0)
  417.         {
  418.         free (wordent.word);
  419.         return;
  420.         }
  421.     }
  422.     else
  423.     {
  424.     /* It's new. Insert the word. */
  425.     dp = tinsert (&wordent);
  426. #ifndef NO_CAPITALIZATION_SUPPORT
  427.     if (captype (dp->flagfield) == FOLLOWCASE)
  428.        (void) addvheader (dp);
  429. #endif
  430.     }
  431.     newwords |= keep;
  432.     }
  433.  
  434. static struct dent * tinsert (proto)
  435.     struct dent *    proto;        /* Prototype entry to copy */
  436.     {
  437.     ichar_t        iword[INPUTWORDLEN + MAXAFFIXLEN];
  438.     register int    hcode;
  439.     register struct dent * hp;        /* Next trial entry in hash table */
  440.     register struct dent * php;        /* Prev. value of hp, for chaining */
  441.  
  442.     if (strtoichar (iword, proto->word, sizeof iword, 1))
  443.     (void) fprintf (stderr, WORD_TOO_LONG (proto->word));
  444. #ifdef NO_CAPITALIZATION_SUPPORT
  445.     upcase (iword);
  446. #endif
  447.     hcode = hash (iword, pershsize);
  448.     php = NULL;
  449.     hp = &pershtab[hcode];
  450.     if (hp->flagfield & USED)
  451.     {
  452.     while (hp != NULL)
  453.         {
  454.         php = hp;
  455.         hp = hp->next;
  456.         }
  457.     hp = (struct dent *) calloc (1, sizeof (struct dent));
  458.     if (hp == NULL)
  459.         {
  460.         (void) fprintf (stderr, TREE_C_NO_SPACE);
  461.         exit (1);
  462.         }
  463.     }
  464.     *hp = *proto;
  465.     if (php != NULL)
  466.     php->next = hp;
  467.     hp->next = NULL;
  468.     return hp;
  469.     }
  470.  
  471. struct dent * treelookup (word)
  472.     register ichar_t *    word;
  473.     {
  474.     register int    hcode;
  475.     register struct dent * hp;
  476.     char        chword[INPUTWORDLEN + MAXAFFIXLEN];
  477.  
  478.     if (pershsize <= 0)
  479.     return NULL;
  480.     (void) ichartostr (chword, word, sizeof chword, 1);
  481.     hcode = hash (word, pershsize);
  482.     hp = &pershtab[hcode];
  483.     while (hp != NULL  &&  (hp->flagfield & USED))
  484.     {
  485.     if (strcmp (chword, hp->word) == 0)
  486.         break;
  487. #ifndef NO_CAPITALIZATION_SUPPORT
  488.     while (hp->flagfield & MOREVARIANTS)
  489.         hp = hp->next;
  490. #endif
  491.     hp = hp->next;
  492.     }
  493.     if (hp != NULL  &&  (hp->flagfield & USED))
  494.     return hp;
  495.     else
  496.     return NULL;
  497.     }
  498.  
  499. #if SORTPERSONAL != 0
  500. /* Comparison routine for sorting the personal dictionary with qsort */
  501. static int pdictcmp (enta, entb)
  502.     struct dent **    enta;
  503.     struct dent **    entb;
  504.     {
  505.  
  506.     /* The parentheses around *enta and *entb below are NECESSARY!
  507.     ** Otherwise the compiler reads it as *(enta->word), or
  508.     ** enta->word[0], which is illegal (but pcc takes it and
  509.     ** produces wrong code).
  510.     **/
  511.     return casecmp ((*enta)->word, (*entb)->word, 1);
  512.     }
  513. #endif
  514.  
  515. void treeoutput ()
  516.     {
  517.     register struct dent *    cent;    /* Current entry */
  518.     register struct dent *    lent;    /* Linked entry */
  519. #if SORTPERSONAL != 0
  520.     int                pdictsize; /* Number of entries to write */
  521.     struct dent **        sortlist; /* List of entries to be sorted */
  522.     register struct dent **    sortptr; /* Handy pointer into sortlist */
  523. #endif
  524.     register struct dent *    ehtab;    /* End of pershtab, for fast looping */
  525.  
  526.     if (newwords == 0)
  527.     return;
  528.  
  529.     if ((dictf = fopen (personaldict, "w")) == NULL)
  530.     {
  531.     (void) fprintf (stderr, CANT_CREATE, personaldict);
  532.     return;
  533.     }
  534.  
  535. #if SORTPERSONAL != 0
  536.     /*
  537.     ** If we are going to sort the personal dictionary, we must know
  538.     ** how many items are going to be sorted.
  539.     */
  540.     pdictsize = 0;
  541.     if (hcount >= SORTPERSONAL)
  542.     sortlist = NULL;
  543.     else
  544.     {
  545.     for (cent = pershtab, ehtab = pershtab + pershsize;
  546.       cent < ehtab;
  547.       cent++)
  548.         {
  549.         for (lent = cent;  lent != NULL;  lent = lent->next)
  550.         {
  551.         if ((lent->flagfield & (USED | KEEP)) == (USED | KEEP))
  552.             pdictsize++;
  553. #ifndef NO_CAPITALIZATION_SUPPORT
  554.         while (lent->flagfield & MOREVARIANTS)
  555.           lent = lent->next;
  556. #endif
  557.         }
  558.         }
  559.     for (cent = hashtbl, ehtab = hashtbl + hashsize;
  560.       cent < ehtab;
  561.       cent++)
  562.         {
  563.         if ((cent->flagfield & (USED | KEEP)) == (USED | KEEP))
  564.         {
  565.         /*
  566.         ** We only want to count variant headers
  567.         ** and standalone entries.  These happen
  568.         ** to share the characteristics in the
  569.         ** test below.  This test will appear
  570.         ** several more times in this routine.
  571.         */
  572. #ifndef NO_CAPITALIZATION_SUPPORT
  573.         if (captype (cent->flagfield) != FOLLOWCASE
  574.           &&  cent->word != NULL)
  575. #endif
  576.             pdictsize++;
  577.         }
  578.         }
  579.     sortlist = (struct dent **) malloc (pdictsize * sizeof (struct dent));
  580.     }
  581.     if (sortlist == NULL)
  582.     {
  583. #endif
  584.     for (cent = pershtab, ehtab = pershtab + pershsize;
  585.       cent < ehtab;
  586.       cent++)
  587.         {
  588.         for (lent = cent;  lent != NULL;  lent = lent->next)
  589.         {
  590.         if ((lent->flagfield & (USED | KEEP)) == (USED | KEEP))
  591.             {
  592.             toutent (dictf, lent, 1);
  593. #ifndef NO_CAPITALIZATION_SUPPORT
  594.             while (lent->flagfield & MOREVARIANTS)
  595.             lent = lent->next;
  596. #endif
  597.             }
  598.         }
  599.         }
  600.     for (cent = hashtbl, ehtab = hashtbl + hashsize;
  601.       cent < ehtab;
  602.       cent++)
  603.         {
  604.         if ((cent->flagfield & (USED | KEEP)) == (USED | KEEP))
  605.         {
  606. #ifndef NO_CAPITALIZATION_SUPPORT
  607.         if (captype (cent->flagfield) != FOLLOWCASE
  608.           &&  cent->word != NULL)
  609. #endif
  610.             toutent (dictf, cent, 1);
  611.         }
  612.         }
  613. #if SORTPERSONAL != 0
  614.     return;
  615.     }
  616.     /*
  617.     ** Produce dictionary in sorted order.  We used to do this
  618.     ** destructively, but that turns out to fail because in some modes
  619.     ** the dictionary is written more than once.  So we build an
  620.     ** auxiliary pointer table (in sortlist) and sort that.  This
  621.     ** is faster anyway, though it uses more memory. 
  622.     */
  623.     sortptr = sortlist;
  624.     for (cent = pershtab, ehtab = pershtab + pershsize;  cent < ehtab;  cent++)
  625.     {
  626.     for (lent = cent;  lent != NULL;  lent = lent->next)
  627.         {
  628.         if ((lent->flagfield & (USED | KEEP)) == (USED | KEEP))
  629.         {
  630.         *sortptr++ = lent;
  631. #ifndef NO_CAPITALIZATION_SUPPORT
  632.         while (lent->flagfield & MOREVARIANTS)
  633.             lent = lent->next;
  634. #endif
  635.         }
  636.         }
  637.     }
  638.     for (cent = hashtbl, ehtab = hashtbl + hashsize;  cent < ehtab;  cent++)
  639.     {
  640.     if ((cent->flagfield & (USED | KEEP)) == (USED | KEEP))
  641.         {
  642. #ifndef NO_CAPITALIZATION_SUPPORT
  643.         if (captype (cent->flagfield) != FOLLOWCASE
  644.           &&  cent->word != NULL)
  645. #endif
  646.         *sortptr++ = cent;
  647.         }
  648.     }
  649.     /* Sort the list */
  650.     qsort ((char *) sortlist, (unsigned) pdictsize,
  651.       sizeof (sortlist[0]),
  652.       (int (*) P ((const void *, const void *))) pdictcmp);
  653.     /* Write it out */
  654.     for (sortptr = sortlist;  --pdictsize >= 0;  )
  655.     toutent (dictf, *sortptr++, 1);
  656.     free ((char *) sortlist);
  657. #endif
  658.  
  659.     newwords = 0;
  660.  
  661.     (void) fclose (dictf);
  662.     }
  663.  
  664. VOID * mymalloc (size)
  665.     unsigned int    size;
  666.     {
  667.  
  668.     return malloc ((unsigned) size);
  669.     }
  670.  
  671. void myfree (ptr)
  672.     VOID * ptr;
  673.     {
  674.     if (hashstrings != NULL  &&  (char *) ptr >= hashstrings
  675.       &&  (char *) ptr <= hashstrings + hashheader.stringsize)
  676.     return;            /* Can't free stuff in hashstrings */
  677.     free (ptr);
  678.     }
  679.  
  680. #ifdef REGEX_LOOKUP
  681.  
  682. /* check the hashed dictionary for words matching the regex. return the */
  683. /* a matching string if found else return NULL */
  684. char * do_regex_lookup (expr, whence)
  685.     char *    expr;    /* regular expression to use in the match   */
  686.     int        whence;    /* 0 = start at the beg with new regx, else */
  687.             /* continue from cur point w/ old regex     */
  688.     {
  689.     static struct dent *    curent;
  690.     static int            curindex;
  691.     static struct dent *    curpersent;
  692.     static int            curpersindex;
  693.     static char *        cmp_expr;
  694.     char            dummy[INPUTWORDLEN + MAXAFFIXLEN];
  695.     ichar_t *            is;
  696.  
  697.     if (whence == 0)
  698.     {
  699.     is = strtosichar (expr, 0);
  700.     upcase (is);
  701.     expr = ichartosstr (is, 1);
  702.         cmp_expr = REGCMP (expr);
  703.         curent = hashtbl;
  704.         curindex = 0;
  705.         curpersent = pershtab;
  706.         curpersindex = 0;
  707.     }
  708.     
  709.     /* search the dictionary until the word is found or the words run out */
  710.     for (  ; curindex < hashsize;  curent++, curindex++)
  711.     {
  712.         if (curent->word != NULL
  713.           &&  REGEX (cmp_expr, curent->word, dummy) != NULL)
  714.         {
  715.         curindex++;
  716.         /* Everybody's gotta write a wierd expression once in a while! */
  717.         return curent++->word;
  718.         }
  719.     }
  720.     /* Try the personal dictionary too */
  721.     for (  ; curpersindex < pershsize;  curpersent++, curpersindex++)
  722.     {
  723.         if ((curpersent->flagfield & USED) != 0
  724.           &&  curpersent->word != NULL
  725.           &&  REGEX (cmp_expr, curpersent->word, dummy) != NULL)
  726.         {
  727.         curpersindex++;
  728.         /* Everybody's gotta write a wierd expression once in a while! */
  729.         return curpersent++->word;
  730.         }
  731.     }
  732.     return NULL;
  733.     }
  734. #endif /* REGEX_LOOKUP */
  735.