home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / isp31b3.zip / ispell / source / correct.c < prev    next >
C/C++ Source or Header  |  1994-11-02  |  44KB  |  1,706 lines

  1. #ifndef lint
  2. static char Rcs_Id[] =
  3.     "$Id: correct.c,v 1.58 1994/11/02 06:56:00 geoff Exp $";
  4. #endif
  5.  
  6. /*
  7.  * correct.c - Routines to manage the higher-level aspects of spell-checking
  8.  *
  9.  * This code originally resided in ispell.c, but was moved here to keep
  10.  * file sizes smaller.
  11.  *
  12.  * Copyright (c), 1983, by Pace Willisson
  13.  *
  14.  * Copyright 1992, 1993, Geoff Kuenning, Granada Hills, CA
  15.  * All rights reserved.
  16.  *
  17.  * Redistribution and use in source and binary forms, with or without
  18.  * modification, are permitted provided that the following conditions
  19.  * are met:
  20.  *
  21.  * 1. Redistributions of source code must retain the above copyright
  22.  *    notice, this list of conditions and the following disclaimer.
  23.  * 2. Redistributions in binary form must reproduce the above copyright
  24.  *    notice, this list of conditions and the following disclaimer in the
  25.  *    documentation and/or other materials provided with the distribution.
  26.  * 3. All modifications to the source code must be clearly marked as
  27.  *    such.  Binary redistributions based on modified source code
  28.  *    must be clearly marked as modified versions in the documentation
  29.  *    and/or other materials provided with the distribution.
  30.  * 4. All advertising materials mentioning features or use of this software
  31.  *    must display the following acknowledgment:
  32.  *      This product includes software developed by Geoff Kuenning and
  33.  *      other unpaid contributors.
  34.  * 5. The name of Geoff Kuenning may not be used to endorse or promote
  35.  *    products derived from this software without specific prior
  36.  *    written permission.
  37.  *
  38.  * THIS SOFTWARE IS PROVIDED BY GEOFF KUENNING AND CONTRIBUTORS ``AS IS'' AND
  39.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  40.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  41.  * ARE DISCLAIMED.  IN NO EVENT SHALL GEOFF KUENNING OR CONTRIBUTORS BE LIABLE
  42.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  43.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  44.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  45.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  46.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  47.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  48.  * SUCH DAMAGE.
  49.  */
  50.  
  51. /*
  52.  * $Log: correct.c,v $
  53.  * Revision 1.58  1994/11/02  06:56:00  geoff
  54.  * Remove the anyword feature, which I've decided is a bad idea.
  55.  *
  56.  * Revision 1.57  1994/10/26  05:12:39  geoff
  57.  * Try boundary characters when inserting or substituting letters, except
  58.  * (naturally) at word boundaries.
  59.  *
  60.  * Revision 1.56  1994/10/25  05:46:30  geoff
  61.  * Fix an assignment inside a conditional that could generate spurious
  62.  * warnings (as well as being bad style).  Add support for the FF_ANYWORD
  63.  * option.
  64.  *
  65.  * Revision 1.55  1994/09/16  04:48:24  geoff
  66.  * Don't pass newlines from the input to various other routines, and
  67.  * don't assume that those routines leave the input unchanged.
  68.  *
  69.  * Revision 1.54  1994/09/01  06:06:41  geoff
  70.  * Change erasechar/killchar to uerasechar/ukillchar to avoid
  71.  * shared-library problems on HP systems.
  72.  *
  73.  * Revision 1.53  1994/08/31  05:58:38  geoff
  74.  * Add code to handle extremely long lines in -a mode without splitting
  75.  * words or reporting incorrect offsets.
  76.  *
  77.  * Revision 1.52  1994/05/25  04:29:24  geoff
  78.  * Fix a bug that caused line widths to be calculated incorrectly when
  79.  * displaying lines containing tabs.  Fix a couple of places where
  80.  * characters were sign-extended incorrectly, which could cause 8-bit
  81.  * characters to be displayed wrong.
  82.  *
  83.  * Revision 1.51  1994/05/17  06:44:05  geoff
  84.  * Add support for controlled compound formation and the COMPOUNDONLY
  85.  * option to affix flags.
  86.  *
  87.  * Revision 1.50  1994/04/27  05:20:14  geoff
  88.  * Allow compound words to be formed from more than two components
  89.  *
  90.  * Revision 1.49  1994/04/27  01:50:31  geoff
  91.  * Add support to correctly capitalize words generated as a result of a
  92.  * missing-space suggestion.
  93.  *
  94.  * Revision 1.48  1994/04/03  23:23:02  geoff
  95.  * Clean up the code in missingspace() to be a bit simpler and more
  96.  * efficient.
  97.  *
  98.  * Revision 1.47  1994/03/15  06:24:23  geoff
  99.  * Fix the +/-/~ commands to be independent.  Allow the + command to
  100.  * receive a suffix which is a deformatter type (currently hardwired to
  101.  * be either tex or nroff/troff).
  102.  *
  103.  * Revision 1.46  1994/02/21  00:20:03  geoff
  104.  * Fix some bugs that could cause bad displays in the interaction between
  105.  * TeX parsing and string characters.  Show_char now will not overrun
  106.  * the inverse-video display area by accident.
  107.  *
  108.  * Revision 1.45  1994/02/14  00:34:51  geoff
  109.  * Fix correct to accept length parameters for ctok and itok, so that it
  110.  * can pass them to the to/from ichar routines.
  111.  *
  112.  * Revision 1.44  1994/01/25  07:11:22  geoff
  113.  * Get rid of all old RCS log lines in preparation for the 3.1 release.
  114.  *
  115.  */
  116.  
  117. #include <ctype.h>
  118. #include "config.h"
  119. #include "ispell.h"
  120. #include "proto.h"
  121. #include "msgs.h"
  122. #include "version.h"
  123.  
  124. void        givehelp P ((int interactive));
  125. void        checkfile P ((void));
  126. void        correct P ((char * ctok, int ctokl, ichar_t * itok, int itokl,
  127.           char ** curchar));
  128. static void    show_line P ((char * line, char * invstart, int invlen));
  129. static int    show_char P ((char ** cp, int linew, int output, int maxw));
  130. static int    line_size P ((char * buf, char * bufend));
  131. static void    inserttoken P ((char * buf, char * start, char * tok,
  132.           char ** curchar));
  133. static int    posscmp P ((char * a, char * b));
  134. int        casecmp P ((char * a, char * b, int canonical));
  135. void        makepossibilities P ((ichar_t * word));
  136. static int    insert P ((ichar_t * word));
  137. #ifndef NO_CAPITALIZATION_SUPPORT
  138. static void    wrongcapital P ((ichar_t * word));
  139. #endif /* NO_CAPITALIZATION_SUPPORT */
  140. static void    wrongletter P ((ichar_t * word));
  141. static void    extraletter P ((ichar_t * word));
  142. static void    missingletter P ((ichar_t * word));
  143. static void    missingspace P ((ichar_t * word));
  144. int        compoundgood P ((ichar_t * word, int pfxopts));
  145. static void    transposedletter P ((ichar_t * word));
  146. static void    tryveryhard P ((ichar_t * word));
  147. static int    ins_cap P ((ichar_t * word, ichar_t * pattern));
  148. static int    save_cap P ((ichar_t * word, ichar_t * pattern,
  149.           ichar_t savearea[MAX_CAPS][INPUTWORDLEN + MAXAFFIXLEN]));
  150. int        ins_root_cap P ((ichar_t * word, ichar_t * pattern,
  151.           int prestrip, int preadd, int sufstrip, int sufadd,
  152.           struct dent * firstdent, struct flagent * pfxent,
  153.           struct flagent * sufent));
  154. static void    save_root_cap P ((ichar_t * word, ichar_t * pattern,
  155.           int prestrip, int preadd, int sufstrip, int sufadd,
  156.           struct dent * firstdent, struct flagent * pfxent,
  157.           struct flagent * sufent,
  158.           ichar_t savearea[MAX_CAPS][INPUTWORDLEN + MAXAFFIXLEN],
  159.           int * nsaved));
  160. static char *    getline P ((char * buf));
  161. void        askmode P ((void));
  162. void        copyout P ((char ** cc, int cnt));
  163. static void    lookharder P ((char * string));
  164. #ifdef REGEX_LOOKUP
  165. static void    regex_dict_lookup P ((char * cmd, char * grepstr));
  166. #endif /* REGEX_LOOKUP */
  167.  
  168. void givehelp (interactive)
  169.     int            interactive;    /* NZ for interactive-mode help */
  170.     {
  171. #ifdef COMMANDFORSPACE
  172.     char ch;
  173. #endif
  174.     register FILE *helpout;    /* File to write help to */
  175.  
  176.     if (interactive)
  177.     {
  178.     erase ();
  179.     helpout = stdout;
  180.     }
  181.     else
  182.     helpout = stderr;
  183.  
  184.     (void) fprintf (helpout, CORR_C_HELP_1);
  185.     (void) fprintf (helpout, CORR_C_HELP_2);
  186.     (void) fprintf (helpout, CORR_C_HELP_3);
  187.     (void) fprintf (helpout, CORR_C_HELP_4);
  188.     (void) fprintf (helpout, CORR_C_HELP_5);
  189.     (void) fprintf (helpout, CORR_C_HELP_6);
  190.     (void) fprintf (helpout, CORR_C_HELP_7);
  191.     (void) fprintf (helpout, CORR_C_HELP_8);
  192.     (void) fprintf (helpout, CORR_C_HELP_9);
  193.  
  194.     (void) fprintf (helpout, CORR_C_HELP_COMMANDS);
  195.  
  196.     (void) fprintf (helpout, CORR_C_HELP_R_CMD);
  197.     (void) fprintf (helpout, CORR_C_HELP_BLANK);
  198.     (void) fprintf (helpout, CORR_C_HELP_A_CMD);
  199.     (void) fprintf (helpout, CORR_C_HELP_I_CMD);
  200.     (void) fprintf (helpout, CORR_C_HELP_U_CMD);
  201.     (void) fprintf (helpout, CORR_C_HELP_0_CMD);
  202.     (void) fprintf (helpout, CORR_C_HELP_L_CMD);
  203.     (void) fprintf (helpout, CORR_C_HELP_X_CMD);
  204.     (void) fprintf (helpout, CORR_C_HELP_Q_CMD);
  205.     (void) fprintf (helpout, CORR_C_HELP_BANG);
  206.     (void) fprintf (helpout, CORR_C_HELP_REDRAW);
  207.     (void) fprintf (helpout, CORR_C_HELP_SUSPEND);
  208.     (void) fprintf (helpout, CORR_C_HELP_HELP);
  209.  
  210.     if (interactive)
  211.     {
  212.     (void) fprintf (helpout, "\r\n\r\n");
  213.     (void) fprintf (helpout, CORR_C_HELP_TYPE_SPACE);
  214.     (void) fflush (helpout);
  215. #ifdef COMMANDFORSPACE
  216.     ch = GETKEYSTROKE ();
  217.     if (ch != ' ' && ch != '\n' && ch != '\r')
  218.         (void) ungetc (ch, stdin);
  219. #else
  220.     while (GETKEYSTROKE () != ' ')
  221.         ;
  222. #endif
  223.     }
  224.     }
  225.  
  226. void checkfile ()
  227.     {
  228.     int        bufno;
  229.     int        bufsize;
  230.     int        ch;
  231.  
  232.     for (bufno = 0;  bufno < contextsize;  bufno++)
  233.     contextbufs[bufno][0] = '\0';
  234.  
  235.     for (  ;  ;  )
  236.     {
  237.     for (bufno = contextsize;  --bufno > 0;  )
  238.         (void) strcpy (contextbufs[bufno],
  239.           contextbufs[bufno - 1]);
  240.     if (quit)    /* quit can't be set in l mode */
  241.         {
  242.         while (fgets (contextbufs[0],
  243.           sizeof contextbufs[0], infile) != NULL)
  244.         (void) fputs (contextbufs[0], outfile);
  245.         break;
  246.         }
  247.     /*
  248.      * Only read in enough characters to fill half this buffer so that any
  249.      * corrections we make are not likely to cause an overflow.
  250.      */
  251.     if (fgets (contextbufs[0], (sizeof contextbufs[0]) / 2, infile)
  252.       == NULL)
  253.         break;
  254.     /*
  255.      * If we didn't read to end-of-line, we may have ended the
  256.      * buffer in the middle of a word.  So keep reading until we
  257.      * see some sort of character that can't possibly be part of a
  258.      * word. (or until the buffer is full, which fortunately isn't
  259.      * all that likely).
  260.      */
  261.     bufsize = strlen (contextbufs[0]);
  262.     if (bufsize == (sizeof contextbufs[0]) / 2 - 1)
  263.         {
  264.         ch = (unsigned char) contextbufs[0][bufsize - 1];
  265.         while (bufsize < sizeof contextbufs[0] - 1
  266.           &&  (iswordch ((ichar_t) ch)  ||  isboundarych ((ichar_t) ch)
  267.           ||  isstringstart (ch)))
  268.         {
  269.         ch = getc (infile);
  270.         if (ch == EOF)
  271.             break;
  272.         contextbufs[0][bufsize++] = (char) ch;
  273.         contextbufs[0][bufsize] = '\0';
  274.         }
  275.         }
  276.     checkline (outfile);
  277.     }
  278.     }
  279.  
  280. void correct (ctok, ctokl, itok, itokl, curchar)
  281.     char *        ctok;
  282.     int            ctokl;
  283.     ichar_t *        itok;
  284.     int            itokl;
  285.     char **        curchar;
  286.     {
  287.     register int    c;
  288.     register int    i;
  289.     int            col_ht;
  290.     int            ncols;
  291.     char *        start_l2;
  292.     char *        begintoken;
  293.  
  294.     begintoken = *curchar - strlen (ctok);
  295.  
  296.     if (icharlen (itok) <= minword)
  297.     return;            /* Accept very short words */
  298.  
  299. checkagain:
  300.     if (good (itok, 0, 0, 0, 0)  ||  compoundgood (itok, 0))
  301.     return;
  302.  
  303.     erase ();
  304.     (void) printf ("    %s", ctok);
  305.     if (currentfile)
  306.     (void) printf (CORR_C_FILE_LABEL, currentfile);
  307.     if (readonly)
  308.     (void) printf (" %s", CORR_C_READONLY);
  309.     (void) printf ("\r\n\r\n");
  310.  
  311.     makepossibilities (itok);
  312.  
  313.     /*
  314.      * Make sure we have enough room on the screen to hold the
  315.      * possibilities.  Reduce the list if necessary.  co / (maxposslen + 8)
  316.      * is the maximum number of columns that will fit.  col_ht is the
  317.      * height of the columns.  The constant 4 allows 2 lines (1 blank) at
  318.      * the top of the screen, plus another blank line between the
  319.      * columns and the context, plus a final blank line at the bottom
  320.      * of the screen for command entry (R, L, etc).
  321.      */
  322.     col_ht = li - contextsize - 4 - minimenusize;
  323.     ncols = co / (maxposslen + 8);
  324.     if (pcount > ncols * col_ht)
  325.     pcount = ncols * col_ht;
  326.  
  327. #ifdef EQUAL_COLUMNS
  328.     /*
  329.      * Equalize the column sizes.  The last column will be short.
  330.      */
  331.     col_ht = (pcount + ncols - 1) / ncols;
  332. #endif
  333.  
  334.     for (i = 0; i < pcount; i++)
  335.     {
  336. #ifdef BOTTOMCONTEXT
  337.     move (2 + (i % col_ht), (maxposslen + 8) * (i / col_ht));
  338. #else /* BOTTOMCONTEXT */
  339.     move (3 + contextsize + (i % col_ht), (maxposslen + 8) * (i / col_ht));
  340. #endif /* BOTTOMCONTEXT */
  341.     if (i >= easypossibilities)
  342.         (void) printf ("??: %s", possibilities[i]);
  343.     else if (easypossibilities >= 10  &&  i < 10)
  344.         (void) printf ("0%d: %s", i, possibilities[i]);
  345.     else
  346.         (void) printf ("%2d: %s", i, possibilities[i]);
  347.     }
  348.  
  349. #ifdef BOTTOMCONTEXT
  350.     move (li - contextsize - 1 - minimenusize, 0);
  351. #else /* BOTTOMCONTEXT */
  352.     move (2, 0);
  353. #endif /* BOTTOMCONTEXT */
  354.     for (i = contextsize;  --i > 0;  )
  355.     show_line (contextbufs[i], contextbufs[i], 0);
  356.  
  357.     start_l2 = contextbufs[0];
  358.     if (line_size (contextbufs[0], *curchar) > co - (sg << 1) - 1)
  359.     {
  360.     start_l2 = begintoken - (co / 2);
  361.     while (start_l2 < begintoken)
  362.         {
  363.         i = line_size (start_l2, *curchar) + 1;
  364.         if (i + (sg << 1) <= co)
  365.         break;
  366.         start_l2 += i - co;
  367.         }
  368.     if (start_l2 > begintoken)
  369.         start_l2 = begintoken;
  370.     if (start_l2 < contextbufs[0])
  371.         start_l2 = contextbufs[0];
  372.     }
  373.     show_line (start_l2, begintoken, (int) strlen (ctok));
  374.  
  375.     if (minimenusize != 0)
  376.     {
  377.     move (li - 2, 0);
  378.     (void) printf (CORR_C_MINI_MENU);
  379.     }
  380.  
  381.     for (  ;  ;  )
  382.     {
  383.     (void) fflush (stdout);
  384.     switch (c = (GETKEYSTROKE () & NOPARITY))
  385.         {
  386.         case 'Z' & 037:
  387.         stop ();
  388.         erase ();
  389.         goto checkagain;
  390.         case ' ':
  391.         erase ();
  392.         (void) fflush (stdout);
  393.         return;
  394.         case 'q': case 'Q':
  395.         if (changes)
  396.             {
  397.             (void) printf (CORR_C_CONFIRM_QUIT);
  398.             (void) fflush (stdout);
  399.             c = (GETKEYSTROKE () & NOPARITY);
  400.             }
  401.         else
  402.             c = 'y';
  403.         if (c == 'y' || c == 'Y')
  404.             {
  405.             erase ();
  406.             (void) fflush (stdout);
  407.             done (0);
  408.             }
  409.         goto checkagain;
  410.         case 'i': case 'I':
  411.         treeinsert (ichartosstr (strtosichar (ctok, 0), 1),
  412.          ICHARTOSSTR_SIZE, 1);
  413.         erase ();
  414.         (void) fflush (stdout);
  415.         changes = 1;
  416.         return;
  417.         case 'u': case 'U':
  418.         itok = strtosichar (ctok, 0);
  419.         lowcase (itok);
  420.         treeinsert (ichartosstr (itok, 1), ICHARTOSSTR_SIZE, 1);
  421.         erase ();
  422.         (void) fflush (stdout);
  423.         changes = 1;
  424.         return;
  425.         case 'a': case 'A':
  426.         treeinsert (ichartosstr (strtosichar (ctok, 0), 1),
  427.           ICHARTOSSTR_SIZE, 0);
  428.         erase ();
  429.         (void) fflush (stdout);
  430.         return;
  431.         case 'L' & 037:
  432.         goto checkagain;
  433.         case '?':
  434.         givehelp (1);
  435.         goto checkagain;
  436.         case '!':
  437.         {
  438.         char    buf[200];
  439.  
  440.         move (li - 1, 0);
  441.         (void) putchar ('!');
  442.         if (getline (buf) == NULL)
  443.             {
  444.             (void) putchar (7);
  445.             erase ();
  446.             (void) fflush (stdout);
  447.             goto checkagain;
  448.             }
  449.         (void) printf ("\r\n");
  450.         (void) fflush (stdout);
  451. #ifdef    USESH
  452.         shescape (buf);
  453. #else
  454.         (void) shellescape (buf);
  455. #endif
  456.         erase ();
  457.         goto checkagain;
  458.         }
  459.         case 'r': case 'R':
  460.         move (li - 1, 0);
  461.         if (readonly)
  462.             {
  463.             (void) putchar (7);
  464.             (void) printf ("%s ", CORR_C_READONLY);
  465.             }
  466.         (void) printf (CORR_C_REPLACE_WITH);
  467.         if (getline (ctok) == NULL)
  468.             {
  469.             (void) putchar (7);
  470.             /* Put it back */
  471.             (void) ichartostr (ctok, itok, ctokl, 0);
  472.             }
  473.         else
  474.             {
  475.             inserttoken (contextbufs[0],
  476.               begintoken, ctok, curchar);
  477.             if (strtoichar (itok, ctok, itokl, 0))
  478.             {
  479.             (void) putchar (7);
  480.             (void) printf (WORD_TOO_LONG (ctok));
  481.             }
  482.             changes = 1;
  483.             }
  484.         erase ();
  485.         if (icharlen (itok) <= minword)
  486.             return;        /* Accept very short replacements */
  487.         goto checkagain;
  488.         case '0': case '1': case '2': case '3': case '4':
  489.         case '5': case '6': case '7': case '8': case '9':
  490.         i = c - '0';
  491.         if (easypossibilities >= 10)
  492.             {
  493.             c = GETKEYSTROKE () & NOPARITY;
  494.             if (c >= '0'  &&  c <= '9')
  495.             i = i * 10 + c - '0';
  496.             else if (c != '\r'  &&  c != '\n')
  497.             {
  498.             (void) putchar (7);
  499.             break;
  500.             }
  501.             }
  502.         if (i < easypossibilities)
  503.             {
  504.             (void) strcpy (ctok, possibilities[i]);
  505.             changes = 1;
  506.             inserttoken (contextbufs[0],
  507.             begintoken, ctok, curchar);
  508.             erase ();
  509.             if (readonly)
  510.             {
  511.             move (li - 1, 0);
  512.             (void) putchar (7);
  513.             (void) printf ("%s", CORR_C_READONLY);
  514.             (void) fflush (stdout);
  515.             (void) sleep ((unsigned) 2);
  516.             }
  517.             return;
  518.             }
  519.         (void) putchar (7);
  520.         break;
  521.         case '\r':    /* This makes typing \n after single digits */
  522.         case '\n':    /* ..less obnoxious */
  523.         break;
  524.         case 'l': case 'L':
  525.         {
  526.         char    buf[100];
  527.         move (li - 1, 0);
  528.         (void) printf (CORR_C_LOOKUP_PROMPT);
  529.         if (getline (buf) == NULL)
  530.             {
  531.             (void) putchar (7);
  532.             erase ();
  533.             goto checkagain;
  534.             }
  535.         (void) printf ("\r\n");
  536.         (void) fflush (stdout);
  537.         lookharder (buf);
  538.         erase ();
  539.         goto checkagain;
  540.         }
  541.         case 'x': case 'X':
  542.         quit = 1;
  543.         erase ();
  544.         (void) fflush (stdout);
  545.         return;
  546.         default:
  547.         (void) putchar (7);
  548.         break;
  549.         }
  550.     }
  551.     }
  552.  
  553. static void show_line (line, invstart, invlen)
  554.     char *        line;
  555.     register char *    invstart;
  556.     register int    invlen;
  557.     {
  558.     register int    width;
  559.  
  560.     width = invlen ? (sg << 1) : 0;
  561.     while (line < invstart  &&  width < co - 1)
  562.     width += show_char (&line, width, 1, invstart - line);
  563.     if (invlen)
  564.     {
  565.     inverse ();
  566.     invstart += invlen;
  567.     while (line < invstart  &&  width < co - 1)
  568.         width += show_char (&line, width, 1, invstart - line);
  569.     normal ();
  570.     }
  571.     while (*line  &&  width < co - 1)
  572.     width += show_char (&line, width, 1, 0);
  573.     (void) printf ("\r\n");
  574.     }
  575.  
  576. static int show_char (cp, linew, output, maxw)
  577.     register char **    cp;
  578.     int            linew;
  579.     int            output;        /* NZ to actually do output */
  580.     int            maxw;        /* NZ to limit width shown */
  581.     {
  582.     register int    ch;
  583.     register int    i;
  584.     int            len;
  585.     ichar_t        ichar;
  586.     register int    width;
  587.  
  588.     ch = (unsigned char) **cp;
  589.     if (l1_isstringch (*cp, len, 0))
  590.     ichar = SET_SIZE + laststringch;
  591.     else
  592.     ichar = chartoichar (ch);
  593.     if (!vflag  &&  iswordch (ichar)  &&  len == 1)
  594.     {
  595.     if (output)
  596.         (void) putchar (ch);
  597.     (*cp)++;
  598.     return 1;
  599.     }
  600.     if (ch == '\t')
  601.     {
  602.     if (output)
  603.         (void) putchar ('\t');
  604.     (*cp)++;
  605.     return 8 - (linew & 0x07);
  606.     }
  607.     /*
  608.      * Character is non-printing, or it's ISO and vflag is set.  Display
  609.      * it in "cat -v" form.  For string characters, display every element
  610.      * separately in that form.
  611.      */
  612.     width = 0;
  613.     if (maxw != 0  &&  len > maxw)
  614.     len = maxw;            /* Don't show too much */
  615.     for (i = 0;  i < len;  i++)
  616.     {
  617.     ch = (unsigned char) *(*cp)++;
  618.     if (ch > '\177')
  619.         {
  620.         if (output)
  621.         {
  622.         (void) putchar ('M');
  623.         (void) putchar ('-');
  624.         }
  625.         width += 2;
  626.         ch &= 0x7f;
  627.         }
  628.     if (ch < ' '  ||  ch == '\177')
  629.         {
  630.         if (output)
  631.         {
  632.         (void) putchar ('^');
  633.         if (ch == '\177')
  634.             (void) putchar ('?');
  635.         else
  636.             (void) putchar (ch + 'A' - '\001');
  637.         }
  638.         width += 2;
  639.         }
  640.     else
  641.         {
  642.         if (output)
  643.         (void) putchar (ch);
  644.         width += 1;
  645.         }
  646.     }
  647.     return width;
  648.     }
  649.  
  650. static int line_size (buf, bufend)
  651.     char *        buf;
  652.     register char *    bufend;
  653.     {
  654.     register int    width;
  655.  
  656.     for (width = 0;  buf < bufend  &&  *buf != '\0';  )
  657.     width += show_char (&buf, width, 0, bufend - buf);
  658.     return width;
  659.     }
  660.  
  661. static void inserttoken (buf, start, tok, curchar)
  662.     char *        buf;
  663.     char *        start; 
  664.     register char *    tok;
  665.     char **        curchar;
  666.     {
  667.     char        copy[BUFSIZ];
  668.     register char *    p;
  669.     register char *    q;
  670.     char *        ew;
  671.  
  672.     (void) strcpy (copy, buf);
  673.  
  674.     for (p = buf, q = copy; p != start; p++, q++)
  675.     *p = *q;
  676.     q += *curchar - start;
  677.     ew = skipoverword (tok);
  678.     while (tok < ew)
  679.     *p++ = *tok++;
  680.     *curchar = p;
  681.     if (*tok)
  682.     {
  683.  
  684.     /*
  685.     ** The token changed to two words.  Split it up and save the
  686.     ** second one for later.
  687.     */
  688.  
  689.     *p++ = *tok;
  690.     *tok++ = '\0';
  691.     while (*tok)
  692.         *p++ = *tok++;
  693.     }
  694.     while ((*p++ = *q++) != '\0')
  695.     ;
  696.     }
  697.  
  698. static int posscmp (a, b)
  699.     char *        a;
  700.     char *        b;
  701.     {
  702.  
  703.     return casecmp (a, b, 0);
  704.     }
  705.  
  706. int casecmp (a, b, canonical)
  707.     char *        a;
  708.     char *        b;
  709.     int            canonical;    /* NZ for canonical string chars */
  710.     {
  711.     register ichar_t *    ap;
  712.     register ichar_t *    bp;
  713.     ichar_t        inta[INPUTWORDLEN + 4 * MAXAFFIXLEN + 4];
  714.     ichar_t        intb[INPUTWORDLEN + 4 * MAXAFFIXLEN + 4];
  715.  
  716.     (void) strtoichar (inta, a, sizeof inta, canonical);
  717.     (void) strtoichar (intb, b, sizeof intb, canonical);
  718.     for (ap = inta, bp = intb;  *ap != 0;  ap++, bp++)
  719.     {
  720.     if (*ap != *bp)
  721.         {
  722.         if (*bp == '\0')
  723.         return hashheader.sortorder[*ap];
  724.         else if (mylower (*ap))
  725.         {
  726.         if (mylower (*bp)  ||  mytoupper (*ap) != *bp)
  727.             return (int) hashheader.sortorder[*ap]
  728.               - (int) hashheader.sortorder[*bp];
  729.         }
  730.         else
  731.         {
  732.         if (myupper (*bp)  ||  mytolower (*ap) != *bp)
  733.             return (int) hashheader.sortorder[*ap]
  734.               - (int) hashheader.sortorder[*bp];
  735.         }
  736.         }
  737.     }
  738.     if (*bp != '\0')
  739.     return -(int) hashheader.sortorder[*bp];
  740.     for (ap = inta, bp = intb;  *ap;  ap++, bp++)
  741.     {
  742.     if (*ap != *bp)
  743.         {
  744.         return (int) hashheader.sortorder[*ap]
  745.           - (int) hashheader.sortorder[*bp];
  746.         }
  747.     }
  748.     return 0;
  749.     }
  750.  
  751. void makepossibilities (word)
  752.     register ichar_t *    word;
  753.     {
  754.     register int    i;
  755.  
  756.     for (i = 0; i < MAXPOSSIBLE; i++)
  757.     possibilities[i][0] = 0;
  758.     pcount = 0;
  759.     maxposslen = 0;
  760.     easypossibilities = 0;
  761.  
  762. #ifndef NO_CAPITALIZATION_SUPPORT
  763.     wrongcapital (word);
  764. #endif
  765.  
  766. /* 
  767.  * according to Pollock and Zamora, CACM April 1984 (V. 27, No. 4),
  768.  * page 363, the correct order for this is:
  769.  * OMISSION = TRANSPOSITION > INSERTION > SUBSTITUTION
  770.  * thus, it was exactly backwards in the old version. -- PWP
  771.  */
  772.  
  773.     if (pcount < MAXPOSSIBLE)
  774.     missingletter (word);        /* omission */
  775.     if (pcount < MAXPOSSIBLE)
  776.     transposedletter (word);    /* transposition */
  777.     if (pcount < MAXPOSSIBLE)
  778.     extraletter (word);        /* insertion */
  779.     if (pcount < MAXPOSSIBLE)
  780.     wrongletter (word);        /* substitution */
  781.  
  782.     if ((compoundflag != COMPOUND_ANYTIME)  &&  pcount < MAXPOSSIBLE)
  783.     missingspace (word);    /* two words */
  784.  
  785.     easypossibilities = pcount;
  786.     if (easypossibilities == 0  ||  tryhardflag)
  787.     tryveryhard (word);
  788.  
  789.     if ((sortit  ||  (pcount > easypossibilities))  &&  pcount)
  790.     {
  791.     if (easypossibilities > 0  &&  sortit)
  792.         qsort ((char *) possibilities,
  793.           (unsigned) easypossibilities,
  794.           sizeof (possibilities[0]),
  795.           (int (*) P ((const void *, const void *))) posscmp);
  796.     if (pcount > easypossibilities)
  797.         qsort ((char *) &possibilities[easypossibilities][0],
  798.           (unsigned) (pcount - easypossibilities),
  799.           sizeof (possibilities[0]),
  800.           (int (*) P ((const void *, const void *))) posscmp);
  801.     }
  802.     }
  803.  
  804. static int insert (word)
  805.     register ichar_t *    word;
  806.     {
  807.     register int    i;
  808.     register char *    realword;
  809.  
  810.     realword = ichartosstr (word, 0);
  811.     for (i = 0; i < pcount; i++)
  812.     {
  813.     if (strcmp (possibilities[i], realword) == 0)
  814.         return (0);
  815.     }
  816.  
  817.     (void) strcpy (possibilities[pcount++], realword);
  818.     i = strlen (realword);
  819.     if (i > maxposslen)
  820.     maxposslen = i;
  821.     if (pcount >= MAXPOSSIBLE)
  822.     return (-1);
  823.     else
  824.     return (0);
  825.     }
  826.  
  827. #ifndef NO_CAPITALIZATION_SUPPORT
  828. static void wrongcapital (word)
  829.     register ichar_t *    word;
  830.     {
  831.     ichar_t        newword[INPUTWORDLEN + MAXAFFIXLEN];
  832.  
  833.     /*
  834.     ** When the third parameter to "good" is nonzero, it ignores
  835.     ** case.  If the word matches this way, "ins_cap" will recapitalize
  836.     ** it correctly.
  837.     */
  838.     if (good (word, 0, 1, 0, 0))
  839.     {
  840.     (void) icharcpy (newword, word);
  841.     upcase (newword);
  842.     (void) ins_cap (newword, word);
  843.     }
  844.     }
  845. #endif
  846.  
  847. static void wrongletter (word)
  848.     register ichar_t *    word;
  849.     {
  850.     register int    i;
  851.     register int    j;
  852.     register int    n;
  853.     ichar_t        savechar;
  854.     ichar_t        newword[INPUTWORDLEN + MAXAFFIXLEN];
  855.  
  856.     n = icharlen (word);
  857.     (void) icharcpy (newword, word);
  858. #ifndef NO_CAPITALIZATION_SUPPORT
  859.     upcase (newword);
  860. #endif
  861.  
  862.     for (i = 0; i < n; i++)
  863.     {
  864.     savechar = newword[i];
  865.     for (j=0; j < Trynum; ++j)
  866.         {
  867.         if (Try[j] == savechar)
  868.         continue;
  869.         else if (isboundarych (Try[j])  &&  (i == 0  ||  i == n - 1))
  870.         continue;
  871.         newword[i] = Try[j];
  872.         if (good (newword, 0, 1, 0, 0))
  873.         {
  874.         if (ins_cap (newword, word) < 0)
  875.             return;
  876.         }
  877.         }
  878.     newword[i] = savechar;
  879.     }
  880.     }
  881.  
  882. static void extraletter (word)
  883.     register ichar_t *    word;
  884.     {
  885.     ichar_t        newword[INPUTWORDLEN + MAXAFFIXLEN];
  886.     register ichar_t *    p;
  887.     register ichar_t *    r;
  888.  
  889.     if (icharlen (word) < 2)
  890.     return;
  891.  
  892.     (void) icharcpy (newword, word + 1);
  893.     for (p = word, r = newword;  *p != 0;  )
  894.     {
  895.     if (good (newword, 0, 1, 0, 0))
  896.         {
  897.         if (ins_cap (newword, word) < 0)
  898.         return;
  899.         }
  900.     *r++ = *p++;
  901.     }
  902.     }
  903.  
  904. static void missingletter (word)
  905.     ichar_t *        word;
  906.     {
  907.     ichar_t        newword[INPUTWORDLEN + MAXAFFIXLEN + 1];
  908.     register ichar_t *    p;
  909.     register ichar_t *    r;
  910.     register int    i;
  911.  
  912.     (void) icharcpy (newword + 1, word);
  913.     for (p = word, r = newword;  *p != 0;  )
  914.     {
  915.     for (i = 0;  i < Trynum;  i++)
  916.         {
  917.         if (isboundarych (Try[i])  &&  r == newword)
  918.         continue;
  919.         *r = Try[i];
  920.         if (good (newword, 0, 1, 0, 0))
  921.         {
  922.         if (ins_cap (newword, word) < 0)
  923.             return;
  924.         }
  925.         }
  926.     *r++ = *p++;
  927.     }
  928.     for (i = 0;  i < Trynum;  i++)
  929.     {
  930.     if (isboundarych (Try[i]))
  931.         continue;
  932.     *r = Try[i];
  933.     if (good (newword, 0, 1, 0, 0))
  934.         {
  935.         if (ins_cap (newword, word) < 0)
  936.         return;
  937.         }
  938.     }
  939.     }
  940.  
  941. static void missingspace (word)
  942.     ichar_t *        word;
  943.     {
  944.     ichar_t        firsthalf[MAX_CAPS][INPUTWORDLEN + MAXAFFIXLEN];
  945.     int            firstno;    /* Index into first */
  946.     ichar_t *        firstp;        /* Ptr into current firsthalf word */
  947.     ichar_t        newword[INPUTWORDLEN + MAXAFFIXLEN + 1];
  948.     int            nfirsthalf;    /* No. words saved in 1st half */
  949.     int            nsecondhalf;    /* No. words saved in 2nd half */
  950.     register ichar_t *    p;
  951.     ichar_t        secondhalf[MAX_CAPS][INPUTWORDLEN + MAXAFFIXLEN];
  952.     int            secondno;    /* Index into second */
  953.  
  954.     /*
  955.     ** We don't do words of length less than 3;  this keeps us from
  956.     ** splitting all two-letter words into two single letters.  We
  957.     ** also don't do maximum-length words, since adding the space
  958.     ** would exceed the size of the "possibilities" array.
  959.     */
  960.     nfirsthalf = icharlen (word);
  961.     if (nfirsthalf < 3  ||  nfirsthalf >= INPUTWORDLEN + MAXAFFIXLEN - 1)
  962.     return;
  963.     (void) icharcpy (newword + 1, word);
  964.     for (p = newword + 1;  p[1] != '\0';  p++)
  965.     {
  966.     p[-1] = *p;
  967.     *p = '\0';
  968.     if (good (newword, 0, 1, 0, 0))
  969.         {
  970.         /*
  971.          * Save_cap must be called before good() is called on the
  972.          * second half, because it uses state left around by
  973.          * good().  This is unfortunate because it wastes a bit of
  974.          * time, but I don't think it's a significant performance
  975.          * problem.
  976.          */
  977.         nfirsthalf = save_cap (newword, word, firsthalf);
  978.         if (good (p + 1, 0, 1, 0, 0))
  979.         {
  980.         nsecondhalf = save_cap (p + 1, p + 1, secondhalf);
  981.         for (firstno = 0;  firstno < nfirsthalf;  firstno++)
  982.             {
  983.             firstp = &firsthalf[firstno][p - newword];
  984.             for (secondno = 0;  secondno < nsecondhalf;  secondno++)
  985.             {
  986.             *firstp = ' ';
  987.             (void) icharcpy (firstp + 1, secondhalf[secondno]);
  988.             if (insert (firsthalf[firstno]) < 0)
  989.                 return;
  990.             *firstp = '-';
  991.             if (insert (firsthalf[firstno]) < 0)
  992.                 return;
  993.             }
  994.             }
  995.         }
  996.         }
  997.     }
  998.     }
  999.  
  1000. int compoundgood (word, pfxopts)
  1001.     ichar_t *        word;
  1002.     int            pfxopts;    /* Options to apply to prefixes */
  1003.     {
  1004.     ichar_t        newword[INPUTWORDLEN + MAXAFFIXLEN];
  1005.     register ichar_t *    p;
  1006.     register ichar_t    savech;
  1007.     long        secondcap;    /* Capitalization of 2nd half */
  1008.  
  1009.     /*
  1010.     ** If compoundflag is COMPOUND_NEVER, compound words are never ok.
  1011.     */
  1012.     if (compoundflag == COMPOUND_NEVER)
  1013.     return 0;
  1014.     /*
  1015.     ** Test for a possible compound word (for languages like German that
  1016.     ** form lots of compounds).
  1017.     **
  1018.     ** This is similar to missingspace, except we quit on the first hit,
  1019.     ** and we won't allow either member of the compound to be a single
  1020.     ** letter.
  1021.     **
  1022.     ** We don't do words of length less than 2 * compoundmin, since
  1023.     ** both halves must at least compoundmin letters.
  1024.     */
  1025.     if (icharlen (word) < 2 * hashheader.compoundmin)
  1026.     return 0;
  1027.     (void) icharcpy (newword, word);
  1028.     p = newword + hashheader.compoundmin;
  1029.     for (  ;  p[hashheader.compoundmin - 1] != 0;  p++)
  1030.     {
  1031.     savech = *p;
  1032.     *p = 0;
  1033.     if (good (newword, 0, 0, pfxopts, FF_COMPOUNDONLY))
  1034.         {
  1035.         *p = savech;
  1036.         if (good (p, 0, 1, FF_COMPOUNDONLY, 0)
  1037.           ||  compoundgood (p, FF_COMPOUNDONLY))
  1038.         {
  1039.         secondcap = whatcap (p);
  1040.         switch (whatcap (newword))
  1041.             {
  1042.             case ANYCASE:
  1043.             case CAPITALIZED:
  1044.             case FOLLOWCASE:    /* Followcase can have l.c. suffix */
  1045.             return secondcap == ANYCASE;
  1046.             case ALLCAPS:
  1047.             return secondcap == ALLCAPS;
  1048.             }
  1049.         }
  1050.         }
  1051.     else
  1052.         *p = savech;
  1053.     }
  1054.     return 0;
  1055.     }
  1056.  
  1057. static void transposedletter (word)
  1058.     register ichar_t *    word;
  1059.     {
  1060.     ichar_t        newword[INPUTWORDLEN + MAXAFFIXLEN];
  1061.     register ichar_t *    p;
  1062.     register ichar_t    temp;
  1063.  
  1064.     (void) icharcpy (newword, word);
  1065.     for (p = newword;  p[1] != 0;  p++)
  1066.     {
  1067.     temp = *p;
  1068.     *p = p[1];
  1069.     p[1] = temp;
  1070.     if (good (newword, 0, 1, 0, 0))
  1071.         {
  1072.         if (ins_cap (newword, word) < 0)
  1073.         return;
  1074.         }
  1075.     temp = *p;
  1076.     *p = p[1];
  1077.     p[1] = temp;
  1078.     }
  1079.     }
  1080.  
  1081. static void tryveryhard (word)
  1082.     ichar_t *        word;
  1083.     {
  1084.     (void) good (word, 1, 0, 0, 0);
  1085.     }
  1086.  
  1087. /* Insert one or more correctly capitalized versions of word */
  1088. static int ins_cap (word, pattern)
  1089.     ichar_t *        word;
  1090.     ichar_t *        pattern;
  1091.     {
  1092.     int            i;        /* Index into savearea */
  1093.     int            nsaved;        /* No. of words saved */
  1094.     ichar_t        savearea[MAX_CAPS][INPUTWORDLEN + MAXAFFIXLEN];
  1095.  
  1096.     nsaved = save_cap (word, pattern, savearea);
  1097.     for (i = 0;  i < nsaved;  i++)
  1098.     {
  1099.     if (insert (savearea[i]) < 0)
  1100.         return -1;
  1101.     }
  1102.     return 0;
  1103.     }
  1104.  
  1105. /* Save one or more correctly capitalized versions of word */
  1106. static int save_cap (word, pattern, savearea)
  1107.     ichar_t *        word;        /* Word to save */
  1108.     ichar_t *        pattern;    /* Prototype capitalization pattern */
  1109.     ichar_t        savearea[MAX_CAPS][INPUTWORDLEN + MAXAFFIXLEN];
  1110.                     /* Room to save words */
  1111.     {
  1112.     int            hitno;        /* Index into hits array */
  1113.     int            nsaved;        /* Number of words saved */
  1114.     int            preadd;        /* No. chars added to front of root */
  1115.     int            prestrip;    /* No. chars stripped from front */
  1116.     int            sufadd;        /* No. chars added to back of root */
  1117.     int            sufstrip;    /* No. chars stripped from back */
  1118.  
  1119.     if (*word == 0)
  1120.     return 0;
  1121.  
  1122.     for (hitno = numhits, nsaved = 0;  --hitno >= 0  &&  nsaved < MAX_CAPS;  )
  1123.     {
  1124.     if (hits[hitno].prefix)
  1125.         {
  1126.         prestrip = hits[hitno].prefix->stripl;
  1127.         preadd = hits[hitno].prefix->affl;
  1128.         }
  1129.     else
  1130.         prestrip = preadd = 0;
  1131.     if (hits[hitno].suffix)
  1132.         {
  1133.         sufstrip = hits[hitno].suffix->stripl;
  1134.         sufadd = hits[hitno].suffix->affl;
  1135.         }
  1136.     else
  1137.         sufadd = sufstrip = 0;
  1138.     save_root_cap (word, pattern, prestrip, preadd,
  1139.         sufstrip, sufadd,
  1140.         hits[hitno].dictent, hits[hitno].prefix, hits[hitno].suffix,
  1141.         savearea, &nsaved);
  1142.     }
  1143.     return nsaved;
  1144.     }
  1145.  
  1146. int ins_root_cap (word, pattern, prestrip, preadd, sufstrip, sufadd,
  1147.   firstdent, pfxent, sufent)
  1148.     register ichar_t *    word;
  1149.     register ichar_t *    pattern;
  1150.     int            prestrip;
  1151.     int            preadd;
  1152.     int            sufstrip;
  1153.     int            sufadd;
  1154.     struct dent *    firstdent;
  1155.     struct flagent *    pfxent;
  1156.     struct flagent *    sufent;
  1157.     {
  1158.     int            i;        /* Index into savearea */
  1159.     ichar_t        savearea[MAX_CAPS][INPUTWORDLEN + MAXAFFIXLEN];
  1160.     int            nsaved;        /* Number of words saved */
  1161.  
  1162.     nsaved = 0;
  1163.     save_root_cap (word, pattern, prestrip, preadd, sufstrip, sufadd,
  1164.       firstdent, pfxent, sufent, savearea, &nsaved);
  1165.     for (i = 0;  i < nsaved;  i++)
  1166.     {
  1167.     if (insert (savearea[i]) < 0)
  1168.         return -1;
  1169.     }
  1170.     return 0;
  1171.     }
  1172.  
  1173. /* ARGSUSED */
  1174. static void save_root_cap (word, pattern, prestrip, preadd, sufstrip, sufadd,
  1175.   firstdent, pfxent, sufent, savearea, nsaved)
  1176.     register ichar_t *    word;        /* Word to be saved */
  1177.     register ichar_t *    pattern;    /* Capitalization pattern */
  1178.     int            prestrip;    /* No. chars stripped from front */
  1179.     int            preadd;        /* No. chars added to front of root */
  1180.     int            sufstrip;    /* No. chars stripped from back */
  1181.     int            sufadd;        /* No. chars added to back of root */
  1182.     struct dent *    firstdent;    /* First dent for root */
  1183.     struct flagent *    pfxent;        /* Pfx-flag entry for word */
  1184.     struct flagent *    sufent;        /* Sfx-flag entry for word */
  1185.     ichar_t        savearea[MAX_CAPS][INPUTWORDLEN + MAXAFFIXLEN];
  1186.                     /* Room to save words */
  1187.     int *        nsaved;        /* Number saved so far (updated) */
  1188.     {
  1189. #ifndef NO_CAPITALIZATION_SUPPORT
  1190.     register struct dent * dent;
  1191. #endif /* NO_CAPITALIZATION_SUPPORT */
  1192.     int            firstisupper;
  1193.     ichar_t        newword[INPUTWORDLEN + 4 * MAXAFFIXLEN + 4];
  1194. #ifndef NO_CAPITALIZATION_SUPPORT
  1195.     register ichar_t *    p;
  1196.     int            len;
  1197.     int            i;
  1198.     int            limit;
  1199. #endif /* NO_CAPITALIZATION_SUPPORT */
  1200.  
  1201.     if (*nsaved >= MAX_CAPS)
  1202.     return;
  1203.     (void) icharcpy (newword, word);
  1204.     firstisupper = myupper (pattern[0]);
  1205. #ifdef NO_CAPITALIZATION_SUPPORT
  1206.     /*
  1207.     ** Apply the old, simple-minded capitalization rules.
  1208.     */
  1209.     if (firstisupper)
  1210.     {
  1211.     if (myupper (pattern[1]))
  1212.         upcase (newword);
  1213.     else
  1214.         {
  1215.         lowcase (newword);
  1216.         newword[0] = mytoupper (newword[0]);
  1217.         }
  1218.     }
  1219.     else
  1220.     lowcase (newword);
  1221.     (void) icharcpy (savearea[*nsaved], newword);
  1222.     (*nsaved)++;
  1223.     return;
  1224. #else /* NO_CAPITALIZATION_SUPPORT */
  1225. #define flagsareok(dent)    \
  1226.     ((pfxent == NULL \
  1227.     ||  TSTMASKBIT (dent->mask, pfxent->flagbit)) \
  1228.       &&  (sufent == NULL \
  1229.     ||  TSTMASKBIT (dent->mask, sufent->flagbit)))
  1230.  
  1231.     dent = firstdent;
  1232.     if ((dent->flagfield & (CAPTYPEMASK | MOREVARIANTS)) == ALLCAPS)
  1233.     {
  1234.     upcase (newword);    /* Uppercase required */
  1235.     (void) icharcpy (savearea[*nsaved], newword);
  1236.     (*nsaved)++;
  1237.     return;
  1238.     }
  1239.     for (p = pattern;  *p;  p++)
  1240.     {
  1241.     if (mylower (*p))
  1242.         break;
  1243.     }
  1244.     if (*p == 0)
  1245.     {
  1246.     upcase (newword);    /* Pattern was all caps */
  1247.     (void) icharcpy (savearea[*nsaved], newword);
  1248.     (*nsaved)++;
  1249.     return;
  1250.     }
  1251.     for (p = pattern + 1;  *p;  p++)
  1252.     {
  1253.     if (myupper (*p))
  1254.         break;
  1255.     }
  1256.     if (*p == 0)
  1257.     {
  1258.     /*
  1259.     ** The pattern was all-lower or capitalized.  If that's
  1260.     ** legal, insert only that version.
  1261.     */
  1262.     if (firstisupper)
  1263.         {
  1264.         if (captype (dent->flagfield) == CAPITALIZED
  1265.           ||  captype (dent->flagfield) == ANYCASE)
  1266.         {
  1267.         lowcase (newword);
  1268.         newword[0] = mytoupper (newword[0]);
  1269.         (void) icharcpy (savearea[*nsaved], newword);
  1270.         (*nsaved)++;
  1271.         return;
  1272.         }
  1273.         }
  1274.     else
  1275.         {
  1276.         if (captype (dent->flagfield) == ANYCASE)
  1277.         {
  1278.         lowcase (newword);
  1279.         (void) icharcpy (savearea[*nsaved], newword);
  1280.         (*nsaved)++;
  1281.         return;
  1282.         }
  1283.         }
  1284.     while (dent->flagfield & MOREVARIANTS)
  1285.         {
  1286.         dent = dent->next;
  1287.         if (captype (dent->flagfield) == FOLLOWCASE
  1288.           ||  !flagsareok (dent))
  1289.         continue;
  1290.         if (firstisupper)
  1291.         {
  1292.         if (captype (dent->flagfield) == CAPITALIZED)
  1293.             {
  1294.             lowcase (newword);
  1295.             newword[0] = mytoupper (newword[0]);
  1296.             (void) icharcpy (savearea[*nsaved], newword);
  1297.             (*nsaved)++;
  1298.             return;
  1299.             }
  1300.         }
  1301.         else
  1302.         {
  1303.         if (captype (dent->flagfield) == ANYCASE)
  1304.             {
  1305.             lowcase (newword);
  1306.             (void) icharcpy (savearea[*nsaved], newword);
  1307.             (*nsaved)++;
  1308.             return;
  1309.             }
  1310.         }
  1311.         }
  1312.     }
  1313.     /*
  1314.     ** Either the sample had complex capitalization, or the simple
  1315.     ** capitalizations (all-lower or capitalized) are illegal.
  1316.     ** Insert all legal capitalizations, including those that are
  1317.     ** all-lower or capitalized.  If the prototype is capitalized,
  1318.     ** capitalized all-lower samples.  Watch out for affixes.
  1319.     */
  1320.     dent = firstdent;
  1321.     p = strtosichar (dent->word, 1);
  1322.     len = icharlen (p);
  1323.     if (dent->flagfield & MOREVARIANTS)
  1324.     dent = dent->next;    /* Skip place-holder entry */
  1325.     for (  ;  ;  )
  1326.     {
  1327.     if (flagsareok (dent))
  1328.         {
  1329.         if (captype (dent->flagfield) != FOLLOWCASE)
  1330.         {
  1331.         lowcase (newword);
  1332.         if (firstisupper  ||  captype (dent->flagfield) == CAPITALIZED)
  1333.             newword[0] = mytoupper (newword[0]);
  1334.         (void) icharcpy (savearea[*nsaved], newword);
  1335.         (*nsaved)++;
  1336.         if (*nsaved >= MAX_CAPS)
  1337.             return;
  1338.         }
  1339.         else
  1340.         {
  1341.         /* Followcase is the tough one. */
  1342.         p = strtosichar (dent->word, 1);
  1343.         (void) bcopy ((char *) (p + prestrip),
  1344.           (char *) (newword + preadd),
  1345.           (len - prestrip - sufstrip) * sizeof (ichar_t));
  1346.         if (myupper (p[prestrip]))
  1347.             {
  1348.             for (i = 0;  i < preadd;  i++)
  1349.             newword[i] = mytoupper (newword[i]);
  1350.             }
  1351.         else
  1352.             {
  1353.             for (i = 0;  i < preadd;  i++)
  1354.             newword[i] = mytolower (newword[i]);
  1355.             }
  1356.         limit = len + preadd + sufadd - prestrip - sufstrip;
  1357.         i = len + preadd - prestrip - sufstrip;
  1358.         p += len - sufstrip - 1;
  1359.         if (myupper (*p))
  1360.             {
  1361.             for (p = newword + i;  i < limit;  i++, p++)
  1362.             *p = mytoupper (*p);
  1363.             }
  1364.         else
  1365.             {
  1366.             for (p = newword + i;  i < limit;  i++, p++)
  1367.               *p = mytolower (*p);
  1368.             }
  1369.         (void) icharcpy (savearea[*nsaved], newword);
  1370.         (*nsaved)++;
  1371.         if (*nsaved >= MAX_CAPS)
  1372.             return;
  1373.         }
  1374.         }
  1375.     if ((dent->flagfield & MOREVARIANTS) == 0)
  1376.         break;        /* End of the line */
  1377.     dent = dent->next;
  1378.     }
  1379.     return;
  1380. #endif /* NO_CAPITALIZATION_SUPPORT */
  1381.     }
  1382.  
  1383. static char * getline (s)
  1384.     register char *    s;
  1385.     {
  1386.     register char *    p;
  1387.     register int    c;
  1388.  
  1389.     p = s;
  1390.  
  1391.     for (  ;  ;  )
  1392.     {
  1393.     (void) fflush (stdout);
  1394.     c = (GETKEYSTROKE () & NOPARITY);
  1395.     if (c == '\\')
  1396.         {
  1397.         (void) putchar ('\\');
  1398.         (void) fflush (stdout);
  1399.         c = (GETKEYSTROKE () & NOPARITY);
  1400.         backup ();
  1401.         (void) putchar (c);
  1402.         *p++ = (char) c;
  1403.         }
  1404.     else if (c == ('G' & 037))
  1405.         return (NULL);
  1406.     else if (c == '\n' || c == '\r')
  1407.         {
  1408.         *p = 0;
  1409.         return (s);
  1410.         }
  1411.     else if (c == uerasechar)
  1412.         {
  1413.         if (p != s)
  1414.         {
  1415.         p--;
  1416.         backup ();
  1417.         (void) putchar (' ');
  1418.         backup ();
  1419.         }
  1420.         }
  1421.     else if (c == ukillchar)
  1422.         {
  1423.         while (p != s)
  1424.         {
  1425.         p--;
  1426.         backup ();
  1427.         (void) putchar (' ');
  1428.         backup ();
  1429.         }
  1430.         }
  1431.     else
  1432.         {
  1433.         *p++ = (char) c;
  1434.         (void) putchar (c);
  1435.         }
  1436.     }
  1437.     }
  1438.  
  1439. void askmode ()
  1440.     {
  1441.     int            bufsize;    /* Length of contextbufs[0] */
  1442.     int            ch;        /* Next character read from input */
  1443.     register char *    cp1;
  1444.     register char *    cp2;
  1445.     ichar_t *        itok;        /* Ichar version of current word */
  1446.     int            hadnl;        /* NZ if \n was at end of line */
  1447.  
  1448.     if (fflag)
  1449.     {
  1450.     if (freopen (askfilename, "w", stdout) == NULL)
  1451.         {
  1452.         (void) fprintf (stderr, CANT_CREATE, askfilename);
  1453.         exit (1);
  1454.         }
  1455.     }
  1456.  
  1457.     (void) printf ("%s\n", Version_ID[0]);
  1458.  
  1459.     contextoffset = 0;
  1460.     while (1)
  1461.     {
  1462.     (void) fflush (stdout);
  1463.     /*
  1464.      * Only read in enough characters to fill half this buffer so that any
  1465.      * corrections we make are not likely to cause an overflow.
  1466.      */
  1467.     if (contextoffset == 0)
  1468.         {
  1469.         if (xgets (contextbufs[0], (sizeof contextbufs[0]) / 2, stdin)
  1470.           == NULL)
  1471.         break;
  1472.         }
  1473.     else
  1474.         {
  1475.         if (fgets (contextbufs[0], (sizeof contextbufs[0]) / 2, stdin)
  1476.           == NULL)
  1477.         break;
  1478.         }
  1479.     /*
  1480.      * If we didn't read to end-of-line, we may have ended the
  1481.      * buffer in the middle of a word.  So keep reading until we
  1482.      * see some sort of character that can't possibly be part of a
  1483.      * word. (or until the buffer is full, which fortunately isn't
  1484.      * all that likely).
  1485.      */
  1486.     bufsize = strlen (contextbufs[0]);
  1487.     if (contextbufs[0][bufsize - 1] == '\n')
  1488.         {
  1489.         hadnl = 1;
  1490.         contextbufs[0][--bufsize] = '\0';
  1491.         }
  1492.     else
  1493.         hadnl = 0;
  1494.     if (bufsize == (sizeof contextbufs[0]) / 2 - 1)
  1495.         {
  1496.         ch = (unsigned char) contextbufs[0][bufsize - 1];
  1497.         while (bufsize < sizeof contextbufs[0] - 1
  1498.           &&  (iswordch ((ichar_t) ch)  ||  isboundarych ((ichar_t) ch)
  1499.           ||  isstringstart (ch)))
  1500.         {
  1501.         ch = getc (stdin);
  1502.         if (ch == EOF)
  1503.             break;
  1504.         contextbufs[0][bufsize++] = (char) ch;
  1505.         contextbufs[0][bufsize] = '\0';
  1506.         }
  1507.         }
  1508.     /*
  1509.     ** *line is like `i', @line is like `a', &line is like 'u'
  1510.     ** `#' is like `Q' (writes personal dictionary)
  1511.     ** `+' sets tflag, `-' clears tflag
  1512.     ** `!' sets terse mode, `%' clears terse
  1513.     ** `~' followed by a filename sets parameters according to file name
  1514.     ** `^' causes rest of line to be checked after stripping 1st char
  1515.     */
  1516.     if (contextoffset != 0)
  1517.         checkline (stdout);
  1518.     else
  1519.         {
  1520.         if (contextbufs[0][0] == '*'  ||  contextbufs[0][0] == '@')
  1521.         treeinsert(ichartosstr (strtosichar (contextbufs[0] + 1, 0), 1),
  1522.           ICHARTOSSTR_SIZE,
  1523.           contextbufs[0][0] == '*');
  1524.         else if (contextbufs[0][0] == '&')
  1525.         {
  1526.         itok = strtosichar (contextbufs[0] + 1, 0);
  1527.         lowcase (itok);
  1528.         treeinsert (ichartosstr (itok, 1), ICHARTOSSTR_SIZE, 1);
  1529.         }
  1530.         else if (contextbufs[0][0] == '#')
  1531.         {
  1532.         treeoutput ();
  1533.         math_mode = 0;
  1534.         LaTeX_Mode = 'P';
  1535.         }
  1536.         else if (contextbufs[0][0] == '!')
  1537.         terse = 1;
  1538.         else if (contextbufs[0][0] == '%')
  1539.         terse = 0;
  1540.         else if (contextbufs[0][0] == '-')
  1541.         {
  1542.         math_mode = 0;
  1543.         LaTeX_Mode = 'P';
  1544.         tflag = 0;
  1545.         }
  1546.         else if (contextbufs[0][0] == '+')
  1547.         {
  1548.         math_mode = 0;
  1549.         LaTeX_Mode = 'P';
  1550.         tflag = strcmp (&contextbufs[0][1], "nroff") != 0
  1551.           &&  strcmp (&contextbufs[0][1], "troff") != 0;
  1552.         }
  1553.         else if (contextbufs[0][0] == '~')
  1554.         {
  1555.         defdupchar = findfiletype (&contextbufs[0][1], 1, (int *) NULL);
  1556.         if (defdupchar < 0)
  1557.             defdupchar = 0;
  1558.         }
  1559.         else
  1560.         {
  1561.         if (contextbufs[0][0] == '^')
  1562.             {
  1563.             /* Strip off leading uparrow */
  1564.             for (cp1 = contextbufs[0], cp2 = contextbufs[0] + 1;
  1565.               (*cp1++ = *cp2++) != '\0';
  1566.               )
  1567.             ;
  1568.             contextoffset++;
  1569.             }
  1570.         checkline (stdout);
  1571.         }
  1572.         }
  1573.     if (hadnl)
  1574.         contextoffset = 0;
  1575.     else
  1576.         contextoffset += bufsize;
  1577. #ifndef USG
  1578.     if (sflag)
  1579.         {
  1580.         stop ();
  1581.         if (fflag)
  1582.         {
  1583.         rewind (stdout);
  1584.         (void) creat (askfilename, 0666);
  1585.         }
  1586.         }
  1587. #endif
  1588.     }
  1589.     }
  1590.  
  1591. /* Copy/ignore "cnt" number of characters pointed to by *cc. */
  1592. void copyout (cc, cnt)
  1593.     register char **    cc;
  1594.     register int    cnt;
  1595.     {
  1596.  
  1597.     while (--cnt >= 0)
  1598.     {
  1599.     if (**cc == '\0')
  1600.         break;
  1601.     if (!aflag && !lflag)
  1602.         (void) putc (**cc, outfile);
  1603.     (*cc)++;
  1604.     }
  1605.     }
  1606.  
  1607. static void lookharder (string)
  1608.     char *        string;
  1609.     {
  1610.     char        cmd[150];
  1611.     char        grepstr[100];
  1612.     register char *    g;
  1613.     register char *    s;
  1614. #ifndef REGEX_LOOKUP
  1615.     register int    wild = 0;
  1616. #ifdef LOOK
  1617.     static int        look = -1;
  1618. #endif /* LOOK */
  1619. #endif /* REGEX_LOOKUP */
  1620.  
  1621.     g = grepstr;
  1622.     for (s = string; *s != '\0'; s++)
  1623.     {
  1624.     if (*s == '*')
  1625.         {
  1626. #ifndef REGEX_LOOKUP
  1627.         wild++;
  1628. #endif /* REGEX_LOOKUP */
  1629.         *g++ = '.';
  1630.         *g++ = '*';
  1631.         }
  1632.     else
  1633.         *g++ = *s;
  1634.     }
  1635.     *g = '\0';
  1636.     if (grepstr[0])
  1637.     {
  1638. #ifdef REGEX_LOOKUP
  1639.     regex_dict_lookup (cmd, grepstr);
  1640. #else /* REGEX_LOOKUP */
  1641. #ifdef LOOK
  1642.     /* now supports automatic use of look - gms */
  1643.     if (!wild && look)
  1644.         {
  1645.         /* no wild and look(1) is possibly available */
  1646.         (void) sprintf (cmd, "%s %s %s", LOOK, grepstr, WORDS);
  1647.         if (shellescape (cmd))
  1648.         return;
  1649.         else
  1650.         look = 0;
  1651.         }
  1652. #endif /* LOOK */
  1653.     /* string has wild card chars or look not avail */
  1654.     if (!wild)
  1655.         (void) strcat (grepstr, ".*");    /* work like look */
  1656.     (void) sprintf (cmd, "%s ^%s$ %s", EGREPCMD, grepstr, WORDS);
  1657.     (void) shellescape (cmd);
  1658. #endif /* REGEX_LOOKUP */
  1659.     }
  1660.     }
  1661.  
  1662. #ifdef REGEX_LOOKUP
  1663. static void regex_dict_lookup (cmd, grepstr)
  1664.     char *        cmd;
  1665.     char *        grepstr;
  1666.     {
  1667.     char *        rval;
  1668.     int            whence = 0;
  1669.     int            quitlookup = 0;
  1670.     int            count = 0;
  1671.     int            ch;
  1672.  
  1673.     (void) sprintf (cmd, "^%s$", grepstr);
  1674.     while (!quitlookup  &&  (rval = do_regex_lookup (cmd, whence)) != NULL)
  1675.     {
  1676.     whence = 1;
  1677.         (void) printf ("%s\r\n", rval);;
  1678.     if ((++count % (li - 1)) == 0)
  1679.         {
  1680.         inverse ();
  1681.         (void) printf (CORR_C_MORE_PROMPT);
  1682.         normal ();
  1683.         (void) fflush (stdout);
  1684.         if ((ch = GETKEYSTROKE ()) == 'q'
  1685.           ||  ch == 'Q'  ||  ch == 'x'  ||  ch == 'X' )
  1686.              quitlookup = 1;
  1687.         /*
  1688.          * The following line should blank out the -- more -- even on
  1689.          * magic-cookie terminals.
  1690.          */
  1691.         (void) printf (CORR_C_BLANK_MORE);
  1692.         (void) fflush (stdout);
  1693.         }
  1694.     }
  1695.     if ( rval == NULL )
  1696.     {
  1697.     inverse ();
  1698.     (void) printf (CORR_C_END_LOOK);
  1699.     normal ();
  1700.     (void) fflush (stdout);
  1701.     (void) GETKEYSTROKE ();    
  1702.     }
  1703.     }
  1704.  
  1705. #endif /* REGEX_LOOKUP */
  1706.