home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / alib / d1xx / d191 / ispell.lha / ISpell / src.zoo / tree.c < prev    next >
C/C++ Source or Header  |  1989-02-22  |  18KB  |  790 lines

  1. /* -*- Mode:Text -*- */
  2.  
  3. /*
  4.  * tree.c - a hash style dictionary for user's personal words
  5.  *
  6.  * Pace Willisson, 1983
  7.  * Hash support added by Geoff Kuenning, 1987
  8.  */
  9.  
  10. #include <stdio.h>
  11. #include <ctype.h>
  12. #ifndef AMIGA
  13. #include <sys/param.h>
  14. #endif
  15. #include "config.h"
  16. #include "ispell.h"
  17.  
  18. char *getenv();
  19. struct dent *lookup();
  20. char *upcase();
  21.  
  22. static int cantexpand = 0;    /* NZ if an expansion fails */
  23. static struct dent *htab = NULL; /* Hash table for our stuff */
  24. static int hsize = 0;        /* Space available in hash table */
  25. static int hcount = 0;        /* Number of items in hash table */
  26.  
  27. /*
  28.  * Hash table sizes.  Prime is probably a good idea, though in truth I
  29.  * whipped the algorithm up on the spot rather than looking it up, so
  30.  * who knows what's really best?  If we overflow the table, we just
  31.  * use a double-and-add-1 algorithm.
  32.  *
  33.  * The strange pattern in the table is because this table is sometimes
  34.  * used with huge dictionaries, and we want to get the table bigger fast.
  35.  * 23003 just happens to be such that the original dict.191 will fill
  36.  * the table to just under 70%.  31469 is similarly selected for dict.191
  37.  * combined with /usr/dict/words.  The other numbers are on 10000-word
  38.  * intervals starting at 30000.  (The table is still valid if MAXPCT
  39.  * is changed, but the dictionary sizes will no longer fall on neat
  40.  * boundaries).
  41.  */
  42. static int goodsizes[] = {
  43.     53, 223, 907,
  44. #if ((BIG_DICT * 100) / MAXPCT) <= 23003
  45.     23003,                /* ~16000 words */
  46. #endif
  47. #if ((BIG_DICT * 100) / MAXPCT) <= 31469
  48.     31469,                /* ~22000 words */
  49. #endif
  50. #if ((BIG_DICT * 100) / MAXPCT) <= 42859
  51.     42859,                /* ~30000 words */
  52. #endif
  53. #if ((BIG_DICT * 100) / MAXPCT) <= 57143
  54.     57143,                /* ~40000 words */
  55. #endif
  56.     71429                /* ~50000 words */
  57. };
  58.  
  59. struct dent *treeinsert();
  60. struct dent *tinsert();
  61. struct dent *treelookup();
  62. char *upcase ();
  63. char *lowcase ();
  64.  
  65. static char personaldict[MAXPATHLEN];
  66. static FILE *dictf;
  67. static newwords = 0;
  68.  
  69. extern char *index ();
  70. extern char *calloc ();
  71. extern char *malloc ();
  72. extern char *realloc ();
  73.  
  74. extern struct dent *hashtbl;
  75. extern int hashsize;
  76.  
  77. treeinit (p)
  78. char *p;
  79. {
  80.     register char *h;
  81.     char *orig;
  82.     char buf[BUFSIZ];
  83.     register struct dent *dp;
  84.  
  85.     /*
  86.     ** if p exists and begins with '/' we don't really need HOME,
  87.     ** but it's not very likely that HOME isn't set anyway.
  88.     */
  89.     orig = p;
  90.     if (p == NULL)
  91.         p = getenv (PDICTVAR);
  92.     if ((h = getenv ("HOME")) == NULL)
  93. #ifndef AMIGA
  94.         return;
  95. #else
  96.         h = LIBDIR;
  97. #endif
  98.  
  99.     if (p == NULL)
  100.         sprintf(personaldict,"%s/%s",h,DEFPDICT);
  101.     else {
  102.         if (*p == '/')
  103.             strcpy(personaldict,p);
  104.         else {
  105.             /*
  106.             ** The user gave us a relative pathname.  How we
  107.             ** interpret it depends on how it was given:
  108.             **
  109.             ** -p switch:  as-is first, then $HOME/name
  110.             ** PDICTVAR:   $HOME/name first, then as-is
  111.             **/
  112.             if (orig == NULL)
  113.                 sprintf (personaldict, "%s/%s", h, p);
  114.             else            /* -p switch */
  115.                 strcpy (personaldict, p);
  116.         }
  117.     }
  118.  
  119.     if ((dictf = fopen (personaldict, "r")) == NULL) {
  120.         /* The file doesn't exist. */
  121.         if (p != NULL) {
  122.             /* If pathname is relative, try another place */
  123.             if (*p != '/') {
  124.                 if (orig == NULL)
  125.                     strcpy (personaldict, p);
  126.                 else            /* -p switch */
  127.                     sprintf (personaldict, "%s/%s", h, p);
  128.                 dictf = fopen (personaldict, "r");
  129.             }
  130.             if (dictf == NULL) {
  131.                 (void) fprintf (stderr, "Couldn't open ");
  132.                 perror (p);
  133.                 if (*p != '/') {
  134.                     /*
  135.                     ** Restore the preferred default, so
  136.                     ** that output will go th the right
  137.                     ** place.
  138.                     */
  139.                     if (orig == NULL)
  140.                         sprintf (personaldict,
  141.                           "%s/%s", h, p);
  142.                     else            /* -p switch */
  143.                         strcpy (personaldict, p);
  144.                 }
  145.             }
  146.         }
  147.         /* If the name wasn't specified explicitly, we don't object */
  148.         return;
  149.     }
  150.  
  151.     while (fgets (buf, sizeof buf, dictf) != NULL) {
  152.         int len = strlen (buf) - 1;
  153.  
  154.         if (buf [ len ] == '\n')
  155.             buf [ len-- ] = '\0';
  156.         if ((h = index (buf, '/')) != NULL)
  157.             *h++ = '\0';
  158.         dp = treeinsert (buf, 1);
  159.         if (h != NULL) {
  160.             while (*h != '\0'  &&  *h != '\n') {
  161. /* The manx C compiler has a nasty bug. It wont allow you to
  162.    have large case statments.  the toupper() has been used here to
  163.    reduce the size of the case statment whithout changing the original
  164.    intent.  -- luis
  165. */
  166.                 if ( islower(*h) )
  167.                      *h = toupper(*h);
  168.                 switch (*h++) {
  169.                 case 'D':
  170.                     dp->d_flag = 1;
  171.                     break;
  172.                 case 'G':
  173.                     dp->g_flag = 1;
  174.                     break;
  175.                 case 'H':
  176.                     dp->h_flag = 1;
  177.                     break;
  178.                 case 'J':
  179.                     dp->j_flag = 1;
  180.                     break;
  181.                 case 'M':
  182.                     dp->m_flag = 1;
  183.                     break;
  184.                 case 'N':
  185.                     dp->n_flag = 1;
  186.                     break;
  187.                 case 'P':
  188.                     dp->p_flag = 1;
  189.                     break;
  190.                 case 'R':
  191.                     dp->r_flag = 1;
  192.                     break;
  193.                 case 'S':
  194.                     dp->s_flag = 1;
  195.                     break;
  196.                 case 'T':
  197.                     dp->t_flag = 1;
  198.                     break;
  199.                 case 'V':
  200.                     dp->v_flag = 1;
  201.                     break;
  202.                 case 'X':
  203.                     dp->x_flag = 1;
  204.                     break;
  205.                 case 'Y':
  206.                     dp->y_flag = 1;
  207.                     break;
  208.                 case 'Z':
  209.                     dp->z_flag = 1;
  210.                     break;
  211.                 default:
  212.                     fprintf (stderr,
  213.                       "Illegal flag in personal dictionary - %c (word %s)\n",
  214.                       h[-1], buf);
  215.                     break;
  216.                 }
  217.                 /* Accept old-format dicts with extra slashes */
  218.                 if (*h == '/')
  219.                     h++;
  220.             }
  221.         }
  222.     }
  223.  
  224.     fclose (dictf);
  225.  
  226.     newwords = 0;
  227.  
  228.     if (!lflag && !aflag && access (personaldict, 2) < 0)
  229.         fprintf (stderr,
  230.             "Warning: Cannot update personal dictionary (%s)\r\n",
  231.             personaldict);
  232. }
  233.  
  234. struct dent *
  235. treeinsert (word, keep)
  236. char *word;
  237. {
  238.     register int i;
  239.     register struct dent *dp;
  240.     struct dent *olddp;
  241.     struct dent *oldhtab;
  242.     int oldhsize;
  243.     char nword[BUFSIZ];
  244.     int len;
  245. #ifdef CAPITALIZE
  246.     register char *cp;
  247.     char *saveword;
  248.     int capspace;
  249. #endif
  250.  
  251.     strcpy (nword, word);
  252.     upcase (nword);
  253.     len = strlen (nword);
  254.     if ((dp = lookup (nword, len, 0)) != NULL)
  255.         dp->keep = keep;
  256.     /*
  257.      * Expand hash table when it is MAXPCT % full.
  258.      */
  259.     else if (!cantexpand  &&  (hcount * 100) / MAXPCT >= hsize) {
  260.         oldhsize = hsize;
  261.         oldhtab = htab;
  262.         for (i = 0;  i < sizeof goodsizes / sizeof (goodsizes[0]);  i++)
  263.             if (goodsizes[i] > hsize)
  264.                 break;
  265.         if (i >= sizeof goodsizes / sizeof goodsizes[0])
  266.             hsize += hsize + 1;
  267.         else
  268.             hsize = goodsizes[i];
  269.         htab = (struct dent *) calloc (hsize, sizeof (struct dent));
  270.         if (htab == NULL) {
  271.             (void) fprintf (stderr,
  272.                 "Ran out of space for personal dictionary\n");
  273.             /*
  274.              * Try to continue anyway, since our overflow
  275.              * algorithm can handle an overfull (100%+) table,
  276.              * and the malloc very likely failed because we
  277.              * already have such a huge table, so small mallocs
  278.              * for overflow entries will still work.
  279.              */
  280.             if (oldhtab == NULL)
  281.                 exit (1);    /* No old table, can't go on */
  282.             (void) fprintf (stderr,
  283.                 "Continuing anyway (with reduced performance).\n");
  284.             cantexpand = 1;        /* Suppress further messages */
  285.             hsize = oldhsize;    /* Put this back how the were */
  286.             htab = oldhtab;        /* ... */
  287.             newwords = 1;        /* And pretend it worked */
  288.             return tinsert (nword, (struct dent *) NULL, keep);
  289.         }
  290.         else {
  291.             /*
  292.              * Re-insert old entries into new table
  293.              */
  294.             for (i = 0;  i < oldhsize;  i++) {
  295.                 dp = &oldhtab[i];
  296.                 if (oldhtab[i].used) {
  297.                     tinsert ((char *) NULL, dp, 0);
  298.                     dp = dp->next;
  299.                     while (dp != NULL) {
  300.                         tinsert ((char *) NULL, dp, 0);
  301.                         olddp = dp;
  302.                         dp = dp->next;
  303.                         free ((char *) olddp);
  304.                     }
  305.                 }
  306.             }
  307.             if (oldhtab != NULL)
  308.                 free ((char *) oldhtab);
  309.             dp = NULL;    /* This will force the insert below */
  310.         }
  311.     }
  312.     newwords |= keep;
  313.     if (dp == NULL)
  314.         dp = tinsert (nword, (struct dent *) NULL, keep);
  315. #ifdef CAPITALIZE
  316.     if (dp == NULL)
  317.         return NULL;
  318.     /*
  319.     ** Figure out the capitalization rules from the
  320.     ** capitalization of the sample entry.  If the sample is
  321.     ** all caps, we don't change the existing flags, since
  322.     ** all-caps gives us no information.  Tinsert initializes
  323.     ** new entries with "allcaps" set, so if the word is truly
  324.     ** required to appear in capitals, the correct result
  325.     ** will be achieved.
  326.     */
  327.     for (cp = word;  *cp;  cp++) {
  328.         if (mylower (*cp))
  329.             break;
  330.     }
  331.     if (*cp) {
  332.         /*
  333.         ** Sample entry has at least some lowercase.  See if
  334.         ** the case is mixed.
  335.         */
  336.         for (cp = word;  *cp;  cp++) {
  337.             if (myupper (*cp))
  338.                 break;
  339.         }
  340.         if (*cp == '\0'  &&  !dp->followcase) {
  341.             /*
  342.             ** Sample entry is all lowercase, and word is not
  343.             ** followcase.  Clear all of the capitalization flags.
  344.             */
  345.             dp->allcaps = 0;
  346.             dp->capitalize = 0;
  347.             if (keep) {
  348.                 dp->k_allcaps = 0;
  349.                 dp->k_capitalize = 0;
  350.                 dp->k_followcase = 0;
  351.             }
  352.         }
  353.         else {
  354.             /*
  355.             ** The sample entry is mixed case (or all-lower and the
  356.             ** entry is already followcase).  If it's simply
  357.             ** capitalized, set the capitalize flag and that's that.
  358.             */
  359.             for (cp = word + 1;  *cp  &&  !myupper (*cp);  )
  360.                 cp++;
  361.             if (*cp == 0  &&  myupper (*word)) {
  362.                 dp->allcaps = 0;
  363.                 dp->capitalize = 1;
  364.                 if (keep) {
  365.                     dp->k_allcaps = 0;
  366.                     dp->k_capitalize = 1;
  367.                 }
  368.             }
  369.             else {
  370.                 /*
  371.                 ** The sample entry is followcase.  Make the
  372.                 ** dictionary entry followcase if necessary.
  373.                 */
  374.                 if (!dp->followcase) {
  375.                     dp->followcase = 1;
  376.                     if (keep)
  377.                         dp->k_followcase = 1;
  378.                     capspace = 2 * len + 4;
  379.                     if (dp->word >= hashstrings
  380.                       &&  dp->word <=
  381.                         hashstrings
  382.                          + hashheader.stringsize) {
  383.                         cp = dp->word;
  384.                         dp->word = malloc (capspace);
  385.                         if (dp->word)
  386.                             strcpy (dp->word, cp);
  387.                     }
  388.                     else
  389.                         dp->word = realloc (dp->word,
  390.                                     capspace);
  391.                     if (dp->word == NULL) {
  392.                         fprintf (stderr,
  393.                           "Ran out of space for personal dictionary\n");
  394.                         exit (1);
  395.                     }
  396.                     cp = dp->word + len + 1;
  397.                     if (dp->capitalize  ||  dp->allcaps)
  398.                         *cp++ = 0;
  399.                     else {
  400.                         *cp++ = 1;
  401.                         strcpy (cp + 1, dp->word);
  402.                         lowcase (cp + 1);
  403.                     }
  404.                     *cp = dp->keep ? '+' : '-';
  405.                 }
  406.                 dp->allcaps = 0;
  407.                 if (keep)
  408.                     dp->k_allcaps = 0;
  409.                 cp = dp->word + len + 1;
  410.                 /* See if the capitalization is already there */
  411.                 for (i = 0, saveword = cp + 1;
  412.                     i < (*cp & 0xFF);
  413.                     i++) {
  414.                     if (strcmp (saveword + 1, word) == 0)
  415.                         break;
  416.                     saveword += len + 2;
  417.                 }
  418.                 if (i != (*cp & 0xFF)) {
  419.                     if (keep)
  420.                         *saveword = '+';
  421.                 }
  422.                 else {
  423.                     /* Add a new capitalization */
  424.                     (*cp)++;
  425.                     capspace = (cp - dp->word + 1)
  426.                             * ((*cp & 0xFF) + 1);
  427.                     if (dp->word >= hashstrings
  428.                       &&  dp->word <=
  429.                         hashstrings + hashheader.stringsize) {
  430.                         saveword = dp->word;
  431.                         dp->word = malloc (capspace);
  432.                         if (dp->word) {
  433.                             cp = dp->word;
  434.                             while (--capspace >= 0)
  435.                                 *cp++ =
  436.                                   *saveword++;
  437.                         }
  438.                     }
  439.                     else
  440.                         dp->word =
  441.                           realloc (dp->word, capspace);
  442.                     if (dp->word == NULL) {
  443.                         fprintf (stderr,
  444.                           "Ran out of space for personal dictionary\n");
  445.                         exit (1);
  446.                     }
  447.                     cp = dp->word + len + 1;
  448.                     cp += ((*cp & 0xFF) - 1)
  449.                           * (cp - dp->word + 1) + 1;
  450.                     *cp++ = keep ? '+' : '-';
  451.                     strcpy (cp, word);
  452.                 }
  453.             }
  454.         }
  455.     }
  456. #endif
  457.     return dp;
  458. }
  459.  
  460. static
  461. struct dent *
  462. tinsert (word, proto, keep)
  463. char *word;            /* One of word/proto must be null */
  464. struct dent *proto;
  465. {
  466.     register int hcode;
  467.     register struct dent *hp; /* Next trial entry in hash table */
  468.     register struct dent *php;    /* Previous value of hp, for chaining */
  469.     register char *cp;
  470.  
  471.     if (word == NULL)
  472.         word = proto->word;
  473.     hcode = hash (word, strlen (word), hsize);
  474.     php = NULL;
  475.     hp = &htab[hcode];
  476.     if (hp->used) {
  477.         while (hp != NULL) {
  478.             if (strcmp (word, hp->word) == 0) {
  479.                 if (keep)
  480.                     hp->keep = 1;
  481.                 return hp;
  482.             }
  483.             php = hp;
  484.             hp = hp->next;
  485.         }
  486.         hp = (struct dent *) calloc (1, sizeof (struct dent));
  487.         if (hp == NULL) {
  488.             (void) fprintf (stderr,
  489.                 "Ran out of space for personal dictionary\n");
  490.             exit (1);
  491.         }
  492.     }
  493.     if (proto != NULL) {
  494.         *hp = *proto;
  495.         if (php != NULL)
  496.             php->next = hp;
  497.         hp->next = NULL;
  498.         return &htab[hcode];
  499.     } else {
  500.         if (php != NULL)
  501.             php->next = hp;
  502.         hp->word = (char *) malloc (strlen (word) + 1);
  503.         if (hp->word == NULL) {
  504.             (void) fprintf (stderr,
  505.                 "Ran out of space for personal dictionary\n");
  506.             exit (1);
  507.         }
  508.         strcpy (hp->word, word);
  509.         hp->used = 1;
  510.         hp->next = NULL;
  511.         hp->d_flag = 0;
  512.         hp->g_flag = 0;
  513.         hp->h_flag = 0;
  514.         hp->j_flag = 0;
  515.         hp->m_flag = 0;
  516.         hp->n_flag = 0;
  517.         hp->p_flag = 0;
  518.         hp->r_flag = 0;
  519.         hp->s_flag = 0;
  520.         hp->t_flag = 0;
  521.         hp->v_flag = 0;
  522.         hp->x_flag = 0;
  523.         hp->y_flag = 0;
  524.         hp->z_flag = 0;
  525. #ifdef CAPITALIZE
  526.         hp->allcaps = 1;        /* Assume word is all-caps */
  527.         hp->k_allcaps = 1;
  528.         hp->capitalize = 0;
  529.         hp->k_capitalize = 0;
  530.         hp->followcase = 0;
  531.         hp->k_followcase = 0;
  532. #endif
  533.         hp->keep = keep;
  534.         hcount++;
  535.         return (hp);
  536.     }
  537. }
  538.  
  539. struct dent *
  540. treelookup (word)
  541. char *word;
  542. {
  543.     register int hcode;
  544.     register struct dent *hp;
  545.     char nword[BUFSIZ];
  546.  
  547.     if (hsize <= 0)
  548.         return NULL;
  549.     strcpy (nword, word);
  550.     hcode = hash (nword, strlen (nword), hsize);
  551.     hp = &htab[hcode];
  552.     while (hp != NULL  &&  hp->used) {
  553.         if (strcmp (nword, hp->word) == 0)
  554.             break;
  555.         hp = hp->next;
  556.     }
  557.     if (hp != NULL  &&  hp->used)
  558.         return hp;
  559.     else
  560.         return NULL;
  561. }
  562.  
  563. #if SORTPERSONAL != 0
  564. /* Comparison routine for sorting the personal dictionary with qsort */
  565. pdictcmp (enta, entb)
  566. struct dent **enta;
  567. struct dent **entb;
  568. {
  569.     /* The parentheses around *enta/*entb below are NECESSARY!
  570.     ** Otherwise the compiler reads it as *(enta->word), or
  571.     ** enta->word[0], which is illegal (but pcc takes it and
  572.     ** produces wrong code).
  573.     **/
  574.     return casecmp ((*enta)->word, (*entb)->word);
  575. }
  576. #endif
  577.  
  578. treeoutput ()
  579. {
  580.     register struct dent *cent;    /* Current entry */
  581.     register struct dent *lent;    /* Linked entry */
  582. #if SORTPERSONAL != 0
  583.     int pdictsize;            /* Number of entries to write */
  584.     struct dent **sortlist;        /* List of entries to be sorted */
  585.     register struct dent **sortptr;    /* Handy pointer into sortlist */
  586. #endif
  587.     register struct dent *ehtab;    /* End of htab, for quick looping */
  588.  
  589.     if (newwords == 0)
  590.         return;
  591.  
  592.     if ((dictf = fopen (personaldict, "w")) == NULL) {
  593.         fprintf (stderr, "Can't create %s\r\n", personaldict);
  594.         return;
  595.     }
  596.  
  597. #if SORTPERSONAL != 0
  598.     /*
  599.     ** If we are going to sort the personal dictionary, we must know
  600.     ** how many items are going to be sorted.
  601.     */
  602.     if (hcount >= SORTPERSONAL)
  603.         sortlist = NULL;
  604.     else {
  605.         pdictsize = 0;
  606.         for (cent = htab, ehtab = htab + hsize;
  607.             cent < ehtab;
  608.             cent++) {
  609.             for (lent = cent;  lent != NULL;  lent = lent->next) {
  610.                 if (lent->used  &&  lent->keep)
  611.                     pdictsize++;
  612.             }
  613.         }
  614.         for (cent = hashtbl, ehtab = hashtbl + hashsize;
  615.             cent < ehtab;
  616.             cent++) {
  617.             if (cent->keep  &&  cent->used)
  618.                 pdictsize++;
  619.         }
  620.         sortlist = (struct dent **)
  621.             malloc (pdictsize * sizeof (struct dent));
  622.     }
  623.     if (sortlist == NULL) {
  624. #endif
  625.         for (cent = htab, ehtab = htab + hsize;
  626.             cent < ehtab;
  627.             cent++) {
  628.             for (lent = cent;  lent != NULL;  lent = lent->next) {
  629.                 if (lent->used  &&  lent->keep)
  630.                     toutent (lent);
  631.             }
  632.         }
  633.         for (cent = hashtbl, ehtab = hashtbl + hashsize;
  634.             cent < ehtab;
  635.             cent++) {
  636.             if (cent->used  &&  cent->keep)
  637.                 toutent (cent);
  638.         }
  639. #if SORTPERSONAL != 0
  640.         return;
  641.     }
  642.     /*
  643.     ** Produce dictionary in sorted order.  We used to do this
  644.     ** destructively, but that turns out to fail because in some modes
  645.     ** the dictionary is written more than once.  So we build an
  646.     ** auxiliary pointer table (in sortlist) and sort that.  This
  647.     ** is faster anyway, though it uses more memory. 
  648.     */
  649.     sortptr = sortlist;
  650.     for (cent = htab, ehtab = htab + hsize;  cent < ehtab;  cent++) {
  651.         for (lent = cent;  lent != NULL;  lent = lent->next) {
  652.             if (lent->used  &&  lent->keep)
  653.                 *sortptr++ = lent;
  654.         }
  655.     }
  656.     for (cent = hashtbl, ehtab = hashtbl + hashsize;
  657.         cent < ehtab;
  658.         cent++) {
  659.         if (cent->used  &&  cent->keep)
  660.             *sortptr++ = cent;
  661.     }
  662.     /* Sort the list */
  663.     qsort ((char *) sortlist, pdictsize, sizeof (sortlist[0]), pdictcmp);
  664.     /* Write it out */
  665.     for (sortptr = sortlist;  --pdictsize >= 0;  )
  666.         toutent (*sortptr++);
  667.     free ((char *) sortlist);
  668. #endif
  669.  
  670.     newwords = 0;
  671.  
  672.     fclose (dictf);
  673. }
  674.  
  675. static int hasslash;
  676.  
  677. static
  678. toutent (cent)
  679. register struct dent *cent;
  680. {
  681. #ifdef CAPITALIZE
  682.     register char *cp;
  683.     int len;
  684.     register int wcount;
  685.     char wbuf[WORDLEN + 1];
  686.  
  687.     strcpy (wbuf, cent->word);
  688.     if (cent->k_followcase) {
  689.         if (cent->k_capitalize) {
  690.             lowcase (wbuf);
  691.             if (mylower (wbuf[0]))
  692.                 wbuf[0] = toupper (wbuf[0]);
  693.             toutword (wbuf, cent);
  694.         }
  695.         len = strlen (wbuf) + 1;
  696.         cp = cent->word + len;
  697.         wcount = *cp++ & 0xFF;
  698.         while (--wcount >= 0) {
  699.             if (*cp++ == '+')
  700.                 toutword (cp, cent);
  701.             cp += len;
  702.         }
  703.     }
  704.     else {
  705.         if (!cent->k_allcaps)
  706.             lowcase (wbuf);
  707.         if (cent->k_capitalize  &&  mylower (wbuf[0]))
  708.             wbuf[0] = toupper (wbuf[0]);
  709.         toutword (wbuf, cent);
  710.     }
  711. #else
  712.     toutword (cent->word, cent);
  713. #endif
  714. }
  715.         
  716. static
  717. toutword (word, cent)
  718. char *word;
  719. register struct dent *cent;
  720. {
  721.     hasslash = 0;
  722.     fprintf (dictf, "%s", word);
  723.     if (cent->d_flag)
  724.         flagout ('D');
  725.     if (cent->g_flag)
  726.         flagout ('G');
  727.     if (cent->h_flag)
  728.         flagout ('H');
  729.     if (cent->j_flag)
  730.         flagout ('J');
  731.     if (cent->m_flag)
  732.         flagout ('M');
  733.     if (cent->n_flag)
  734.         flagout ('N');
  735.     if (cent->p_flag)
  736.         flagout ('P');
  737.     if (cent->r_flag)
  738.         flagout ('R');
  739.     if (cent->s_flag)
  740.         flagout ('S');
  741.     if (cent->t_flag)
  742.         flagout ('T');
  743.     if (cent->v_flag)
  744.         flagout ('V');
  745.     if (cent->x_flag)
  746.         flagout ('X');
  747.     if (cent->y_flag)
  748.         flagout ('Y');
  749.     if (cent->z_flag)
  750.         flagout ('Z');
  751.     fprintf (dictf, "\n");
  752. }
  753.  
  754. static
  755. flagout (flag)
  756. {
  757.     if (!hasslash)
  758.         putc ('/', dictf);
  759.     hasslash = 1;
  760.     putc (flag, dictf);
  761. }
  762.  
  763. char *
  764. upcase (s)
  765. register char *s;
  766. {
  767.     register char *os = s;
  768.  
  769.     while (*s) {
  770.         if (mylower (*s))
  771.             *s = toupper (*s);
  772.         s++;
  773.     }
  774.     return (os);
  775. }
  776.  
  777. char *
  778. lowcase (s)
  779. register char *s;
  780. {
  781.     register char *os = s;
  782.  
  783.     while (*s) {
  784.         if (myupper (*s))
  785.             *s = tolower (*s);
  786.         s++;
  787.     }
  788.     return (os);
  789. }
  790.