home *** CD-ROM | disk | FTP | other *** search
/ Amiga Elysian Archive / AmigaElysianArchive.iso / wp_dtp / ispell.lha / tree.c < prev    next >
C/C++ Source or Header  |  1990-12-29  |  18KB  |  802 lines

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