/*
 * defmt.c - Handle formatter constructs, mostly by scanning over them.
 *
 * This code originally resided in ispell.c, but was moved here to keep
 * file sizes smaller.
 *
 * Copyright (c), 1983, by Pace Willisson
 *
 * Copyright 1992, 1993, Geoff Kuenning, Granada Hills, CA
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All modifications to the source code must be clearly marked as
 *    such.  Binary redistributions based on modified source code
 *    must be clearly marked as modified versions in the documentation
 *    and/or other materials provided with the distribution.
 * 4. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgment:
 *      This product includes software developed by Geoff Kuenning and
 *      other unpaid contributors.
 * 5. The name of Geoff Kuenning may not be used to endorse or promote
 *    products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY GEOFF KUENNING AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL GEOFF KUENNING OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * The TeX code is originally by Greg Schaffer, with many improvements from
 * Ken Stevens.  The nroff code is primarily from Pace Willisson, although
 * other people have improved it.
 */

/*
 * $Log: defmt.c,v $
 * Revision 1.41  1995/08/05  23:19:47  geoff
 * Get rid of an obsolete comment.  Add recognition of documentclass and
 * usepackage for Latex2e support.
 *
 * Revision 1.40  1995/03/06  02:42:43  geoff
 * Change TeX backslash processing so that it only assumes alpha
 * characters and the commonly-used "@" character are part of macro
 * names, and so that any other backslashed character (specifically
 * dollar signs) is skipped.
 *
 * Revision 1.39  1995/01/08  23:23:54  geoff
 * Fix typos in a couple of comments.
 *
 * Revision 1.38  1995/01/03  19:24:14  geoff
 * Add code to handle the LaTeX \verb command.
 *
 * Revision 1.37  1994/12/27  23:08:54  geoff
 * Fix a bug in TeX backslash processing that caused ispell to become
 * confused when it encountered an optional argument to a
 * double-backslash command.  Be a little smarter about scanning for
 * curly-brace matches, so that we avoid missing a math-mode transition
 * during the scan.
 *
 * Revision 1.36  1994/10/25  05:46:34  geoff
 * Recognize a few more Latex commands: pagestyle, pagenumbering,
 * setcounter, addtocounter, setlength, addtolength, settowidth.
 *
 * Revision 1.35  1994/10/18  04:03:19  geoff
 * Add code to skip hex numbers if they're preceded by '0x'.
 *
 * Revision 1.34  1994/10/04  03:51:24  geoff
 * Modify the parsing so that TeX commands are ignored even within
 * comments, but do not affect the overall parsing state.  (This is
 * slightly imperfect, in that some types of modality are ignored when
 * comments are entered.  But it should solve nearly all the problems
 * with commented-out TeX commands.)  This also fixes a couple of minor
 * bugs with TeX deformatting.
 *
 * Revision 1.33  1994/10/03  17:06:07  geoff
 * Remember to use contextoffset when reporting complete misses
 *
 * Revision 1.32  1994/08/31  05:58:41  geoff
 * Report the offset-within-line correctly in -a mode even if the line is
 * longer than BUFSIZ characters.
 *
 * Revision 1.31  1994/05/25  04:29:28  geoff
 * If two boundary characters appear in a row, consider it the end of the
 * word.
 *
 * Revision 1.30  1994/05/17  06:44:08  geoff
 * Add the new argument to all calls to good and compoundgood.
 *
 * Revision 1.29  1994/03/16  06:30:41  geoff
 * Don't lose track of math mode when an array environment is embedded.
 *
 * Revision 1.28  1994/03/15  05:31:57  geoff
 * Add TeX_strncmp, which allows us to handle AMS-TeX constructs like
 * \endroster without getting confused.
 *
 * Revision 1.27  1994/02/14  00:34:53  geoff
 * Pass length arguments to correct().
 *
 * Revision 1.26  1994/01/25  07:11:25  geoff
 * Get rid of all old RCS log lines in preparation for the 3.1 release.
 *
 */

#include <ctype.h>
#include <string.h>
#include "ispell.h"
#include "msgs.h"

Local char *	skiptoword (char * bufp);
Global char *	skipoverword (char * bufp);
Global void	checkline (FILE * ofile);
Local int	TeX_math_end (char ** bufp);
Local int	TeX_math_begin (char ** bufp);
Local int	TeX_LR_begin (char ** bufp);
Local int	TeX_LR_check (int begin_p, char ** bufp);
Local void	TeX_skip_args (char ** bufp);
Local int	TeX_math_check (int cont_char, char ** bufp);
Local void	TeX_skip_parens (char ** bufp);
Local void	TeX_open_paren (char ** bufp);
Local void	TeX_skip_check (char ** bufp);
Local int	TeX_strncmp (char * a, char * b, int n);

#define ISTEXTERM(c)   (((c) == hashheader.TeXleftcurly) || \
			((c) == hashheader.TeXrightcurly) || \
			((c) == hashheader.TeXleftsquare) || \
			((c) == hashheader.TeXrightsquare))
#define ISMATHCH(c)    (((c) == hashheader.TeXbackslash) || \
			((c) == hashheader.TeXdollar) || \
			((c) == hashheader.TeXpercent))

Local Logical	    TeX_comment = False;

/*
 * The following variables are used to save the parsing state when
 * processing comments.  This allows comments to be parsed without
 * affecting the overall nesting.
 */

Local int save_math_mode;
Local char save_LaTeX_Mode;

Local char * skiptoword (bufp)		/* Skip to beginning of a word */
    char *	bufp;
    {

    while (*bufp
      &&  ((!isstringch (bufp, 0)  &&  !iswordch (chartoichar (*bufp)))
	||  isboundarych (chartoichar (*bufp))
	||  (tflag  &&  (math_mode & 1)))
      )
	{
	/* check paren necessity... */
	if (tflag) /* TeX or LaTeX stuff */
	    {
	    /* Odd numbers mean we are in "math mode" */
	    /* Even numbers mean we are in LR or */
	    /* paragraph mode */
            if(*bufp == hashheader.TeXpercent && *(bufp - 1) != hashheader.TeXbackslash) /*WM*/
		{
		if (!TeX_comment)
		    {
		    save_math_mode = math_mode;
		    save_LaTeX_Mode = LaTeX_Mode;
		    math_mode = 0;
		    LaTeX_Mode = 'P';
		    TeX_comment = True;
		    }
		}
	    else if (math_mode & 1)
		{
		if ((LaTeX_Mode == 'e'  &&  TeX_math_check('e', &bufp))
		  || (LaTeX_Mode == 'm'  &&  TeX_LR_check(1, &bufp)))
		    math_mode--;    /* end math mode */
		else
		    {
		    while (*bufp  && !ISMATHCH(*bufp))
			bufp++;
		    if (*bufp == 0)
			break;
		    if (TeX_math_end(&bufp))
			math_mode--;
		    }
		if (math_mode < 0)
		    {
		    (void) fprintf (stderr,
		     DEFMT_C_TEX_MATH_ERROR);
		    math_mode = 0;
		    }
		}
	    else
		{
		if (math_mode > 1
		  &&  *bufp == hashheader.TeXrightcurly
		  &&  (math_mode < (math_mode & 127) * 128))
		    math_mode--;    /* re-enter math */
		else if (LaTeX_Mode == 'm'
		    || (math_mode && (math_mode >= (math_mode & 127) * 128)
		  &&  (TeX_strncmp(bufp, "\\end", 4)
		    == 0)))
		    {
		    if (TeX_LR_check(0, &bufp))
			math_mode--;
		    }
		else if (LaTeX_Mode == 'b'  &&  TeX_math_check('b', &bufp))
		    {
		    /* continued begin */
		    math_mode++;
		    }
		else if (LaTeX_Mode == 'r')
		    {
		    /* continued "reference" */
		    TeX_skip_parens(&bufp);
		    LaTeX_Mode = 'P';
		    }
		else if (TeX_math_begin(&bufp))
		    /* checks references and */
		    /* skips \ commands */
		    math_mode++;
		}
	    if (*bufp == 0)
		break;
	    }
	else			/* formatting escape sequences */
	    {
	    if (*bufp == NRBACKSLASH)
		{
		switch ( bufp[1] )
		    {
		    case 'f':
			if(bufp[2] == NRLEFTPAREN)
			    {
			    /* font change: \f(XY */
			    bufp += 5;
			    }
			else
			    {
			    /* ) */
			    /* font change: \fX */
			    bufp += 3;
			    }
			continue;
		    case 's':
			/* size change */
			bufp += 2;
			if (*bufp == '+'  ||  *bufp == '-')
			    bufp++;
			/* This looks wierd 'cause we
			** assume *bufp is now a digit.
			*/
			bufp++;
			if (isdigit (*bufp))
			    bufp++;
			continue;
		    default:
			if (bufp[1] == NRLEFTPAREN)
			    {
			    /* extended char set */
			    /* escape:  \(XX */
			    /* ) */
			    bufp += 4;
			    continue;
			    }
			else if (bufp[1] == NRSTAR)
			    {
			    if (bufp[2] == NRLEFTPAREN)
				bufp += 5;
			    else
				bufp += 3;
			    continue;
			    }
			break;
		    }
		}
	    }
	/*
	 * Skip hex numbers, but not if we're in non-terse askmode.
	 * (In that case, we'd lose sync if we skipped hex.)
	 */
	if (*bufp == '0'
		      &&  (bufp[1] == 'x'  ||  bufp[1] == 'X')
					      &&  (terse  ||  !aflag))
	    {
	    bufp += 2;
	    while (isxdigit (*bufp))
		bufp++;
	    }
	else
	    bufp++;
	}
    if (*bufp == '\0')
	{
	if (TeX_comment)
	    {
	    math_mode = save_math_mode;
	    LaTeX_Mode = save_LaTeX_Mode;
	    TeX_comment = False;
	    }
	}
    return bufp;
    }

char * skipoverword (bufp)	/* Return pointer to end of a word */
    register char *	bufp;	/* Start of word -- MUST BE A REAL START */
    {
    register char *	lastboundary;
    register int	scharlen; /* Length of a string character */

    lastboundary = NULL;
    for (  ;  ;  )
	{
	if (*bufp == '\0')
	    {
	    if (TeX_comment)
		{
		math_mode = save_math_mode;
		LaTeX_Mode = save_LaTeX_Mode;
		TeX_comment = False;
		}
	    break;
	    }
	else if (l_isstringch(bufp, scharlen, 0))
	    {
	    bufp += scharlen;
	    lastboundary = NULL;
	    }
	/*
	** Note that we get here if a character satisfies
	** isstringstart() but isn't in the string table;  this
	** allows string characters to start with word characters.
	*/
	else if (iswordch (chartoichar (*bufp)))
	    {
	    bufp++;
	    lastboundary = NULL;
	    }
	else if (isboundarych (chartoichar (*bufp)))
	    {
	    if (lastboundary == NULL)
		lastboundary = bufp;
	    else if (lastboundary == bufp - 1)
		break;			/* Double boundary -- end of word */
	    bufp++;
	    }
	else
	    break;			/* End of the word */
	}
    /*
    ** If the word ended in one or more boundary characters, 
    ** the address of the first of these is in lastboundary, and it
    ** is the end of the word.  Otherwise, bufp is the end.
    */
    return (lastboundary != NULL) ? lastboundary : bufp;
    }

void checkline (ofile)
    FILE *		ofile;
    {
    register char *	p;
    register char *	endp;
    int			hadlf;
    register int	len;
    register int	i;
    int			ilen;

    currentchar = contextbufs[0];
    len = strlen (contextbufs[0]) - 1;
    hadlf = contextbufs[0][len] == '\n';
    if (hadlf)
	contextbufs[0][len] = 0;

    if (!tflag)
	{
	/* skip over .if */
	if (*currentchar == NRDOT
			  &&  (strncmp (currentchar + 1, "if t", 4) == 0
			    ||  strncmp (currentchar + 1, "if n", 4) == 0))
	    {
	    copyout (&currentchar,5);
	    while (*currentchar &&  myspace (chartoichar (*currentchar)))
		copyout (&currentchar, 1);
	    }

	/* skip over .ds XX or .nr XX */
	if (*currentchar == NRDOT
	  &&  (strncmp (currentchar + 1, "ds ", 3) == 0 
	    ||  strncmp (currentchar + 1, "de ", 3) == 0
	    ||  strncmp (currentchar + 1, "nr ", 3) == 0))
	    {
	    copyout (&currentchar, 4);
	    while (*currentchar
	      &&  myspace (chartoichar (*currentchar)))
		copyout(&currentchar, 1);
	    while (*currentchar
	      &&  !myspace (chartoichar (*currentchar)))
		copyout(&currentchar, 1);
	    if (*currentchar == 0)
		{
		if (!lflag  &&  (aflag  ||  hadlf))
		    (void) putc ('\n', ofile);
		return;
		}
	    }
	}


    /* if this is a formatter command, skip over it */
    if (!tflag && *currentchar == NRDOT)
	{
	while (*currentchar  &&  !myspace (chartoichar (*currentchar)))
	    {
	    if (!aflag && !lflag)
		(void) putc (*currentchar, ofile);
	    currentchar++;
	    }
	if (*currentchar == 0)
	    {
	    if (!lflag  &&  (aflag  ||  hadlf))
		(void) putc ('\n', ofile);
	    return;
	    }
	}

    for (  ;  ;  )
	{
	p = skiptoword (currentchar);
	if (p != currentchar)
	    copyout (&currentchar, p - currentchar);

	if (*currentchar == 0)
	    break;

	p = ctoken;
	endp = skipoverword (currentchar);
	while (currentchar < endp  &&  p < ctoken + sizeof ctoken - 1)
	    *p++ = *currentchar++;
	*p = 0;
	if (strtoichar (itoken, ctoken, INPUTWORDLEN * sizeof (ichar_t), 0))
	    (void) fprintf (stderr, WORD_TOO_LONG (ctoken));
	ilen = icharlen (itoken);

	if (lflag)
	    {
	    if (ilen > minword &&  !good (itoken, 0, 0, 0, 0)
				  &&  !cflag  &&  !compoundgood (itoken, 0))
		(void) fprintf (ofile, "%s\n", ctoken);
	    }
	else
	    {
	    if (aflag)
		{
		if (ilen <= minword)
		    {
		    /* matched because of minword */
		    if (!terse)
			(void) fprintf (ofile, "*\n");
		    continue;
		    }
		if (good (itoken, 0, 0, 0, 0))
		    {
		    if (hits[0].prefix == NULL
		      &&  hits[0].suffix == NULL)
			{
			/* perfect match */
			if (!terse)
			    (void) fprintf (ofile, "*\n");
			}
		    else if (!terse)
			{
			/* matched because of root */
			(void) fprintf (ofile, "+ %s\n",
			  hits[0].dictent->word);
			}
		    }
		else if (compoundgood (itoken, 0))
		    {
		    /* compound-word match */
		    if (!terse)
			(void) fprintf (ofile, "-\n");
		    }
		else
		    {
		    makepossibilities (itoken);
		    if (pcount)
			{
			/*
			** print &  or ?, ctoken, then
			** character offset, possibility
			** count, and the possibilities.
			*/
			(void) fprintf (ofile, "%c %s %d %d",
			  easypossibilities ? '&' : '?',
			  ctoken,
			  easypossibilities,
			  (int) ((currentchar - contextbufs[0])
			    - strlen (ctoken)) + contextoffset);
			for (i = 0;  i < MAXPOSSIBLE;  i++)
			    {
			    if (possibilities[i][0] == 0)
				break;
			    (void) fprintf (ofile, "%c %s",
			      i ? ',' : ':', possibilities[i]);
			    }
			(void) fprintf (ofile, "\n");
			}
		    else
			{
			/*
			** No possibilities found for word TOKEN
			*/
			(void) fprintf (ofile, "# %s %d\n",
			  ctoken,
			  (int) ((currentchar - contextbufs[0])
			    - strlen (ctoken)) + contextoffset);
			}
		    }
		}
	    else
		{
		if (!quit)
		   correct (ctoken, sizeof ctoken, itoken, sizeof itoken,
		     &currentchar);
		}
	    }
	if (!aflag  &&  !lflag)
	   (void) fprintf (ofile, "%s", ctoken);
	}

    if (!lflag  &&  (aflag  ||  hadlf))
       (void) putc ('\n', ofile);
   }

/* must check for \begin{mbox} or whatever makes new text region. */
static int TeX_math_end (bufp)
    char **	bufp;
    {

    if(**bufp == hashheader.TeXdollar && *(*bufp - 1) != hashheader.TeXbackslash) /*WM*/
	{
	if ((*bufp)[1] == hashheader.TeXdollar)
	    (*bufp)++;
	return 1;
	}
     else if(**bufp == hashheader.TeXpercent && *(*bufp - 1) != hashheader.TeXbackslash) /*WM*/
	{
	if (!TeX_comment)
	    {
	    save_math_mode = math_mode;
	    save_LaTeX_Mode = LaTeX_Mode;
	    math_mode = 0;
	    LaTeX_Mode = 'P';
	    TeX_comment = True;
	    }
	return 0;
	}
    /* processing extended TeX command */
    (*bufp)++;
    if (**bufp == hashheader.TeXrightparen  ||  **bufp == hashheader.TeXrightsquare)
	return 1;
    if (TeX_LR_begin (bufp))	/* check for switch back to LR mode */
	return 1;
    if (TeX_strncmp (*bufp, "end", 3) == 0)
	/* find environment that is ending */
	return TeX_math_check ('e', bufp);
    else
	return 0;
    }

static int TeX_math_begin (bufp)
    char **	bufp;
    {

    if(**bufp == hashheader.TeXdollar && *(*bufp - 1) != hashheader.TeXbackslash) /*WM*/
	{
	if ((*bufp)[1] == hashheader.TeXdollar)
	    (*bufp)++;
	return 1;
	}
    while (**bufp == hashheader.TeXbackslash)
	{
	(*bufp)++; /* check for null char here? */
	if (**bufp == hashheader.TeXleftparen || **bufp == hashheader.TeXleftsquare)
	    return 1;
	else if (!isalpha(**bufp)  &&  
				strchr(hashheader.texchars, **bufp) == NULL)
	/*else if (!isalpha(**bufp)  &&  **bufp != '@') WM*/
	    {
	    (*bufp)++;
	    continue;
	    }
	else if (TeX_strncmp (*bufp, "begin", 5) == 0)
	    {
	    if (TeX_math_check ('b', bufp))
		return 1;
	    else
		(*bufp)--;
	    }
	else
	    {
	    TeX_skip_check (bufp);
	    return 0;
	    }
	}
      /*
       * Ignore references for the tib (1) bibliography system, that
       * is, text between a ``[.'' or ``<.'' and ``.]'' or ``.>''.
       * We don't care whether they match, tib doesn't care either.
       *
       * A limitation is that the entire tib reference must be on one
       * line, or we break down and check the remainder anyway.
       */ 
    if ((**bufp == hashheader.TeXleftsquare  ||  **bufp == hashheader.TeXlefttangle)
					      &&  (*bufp)[1] == hashheader.TeXdot)
	{
	(*bufp)++;
	while (**bufp)
	    {
	    if (*(*bufp)++ == hashheader.TeXdot
	      &&  (**bufp == hashheader.TeXrightsquare  ||  **bufp == hashheader.TeXrighttangle))
		return TeX_math_begin (bufp);
	    }
	return 0;
	}
    else
	return 0;
    }

static int TeX_LR_begin (bufp)
    char **	bufp;
    {

    if ((TeX_strncmp (*bufp, "mbox", 4) == 0)
      ||  (TeX_strncmp (*bufp, "makebox", 7) == 0)
      ||  (TeX_strncmp (*bufp, "fbox", 4) == 0)
      || (TeX_strncmp (*bufp, "framebox", 8) == 0))
	math_mode += 2;
    else if ((TeX_strncmp(*bufp, "parbox", 6) == 0)
      || (TeX_strncmp(*bufp, "raisebox", 8) == 0))
	{
	math_mode += 2;
	TeX_open_paren (bufp);
	if (**bufp)
	    (*bufp)++;
	else
	    LaTeX_Mode = 'r'; /* same as reference -- skip {} */
	}
    else if (TeX_strncmp(*bufp, "begin", 5) == 0)
	return TeX_LR_check (1, bufp);	/* minipage */
    else
	return 0;

    /* skip tex command name and optional or width arguments. */
    TeX_open_paren (bufp);
    return 1;
    }

static int TeX_LR_check (begin_p, bufp)
    int		begin_p;
    char **	bufp;
    {

    TeX_open_paren (bufp);
    if (**bufp == 0)	/* { */
	{
	LaTeX_Mode = 'm';
	return 0;	/* remain in math mode until '}' encountered. */
	}
    else
	LaTeX_Mode = 'P';
    if (strncmp (++(*bufp), "minipage", 8) == 0)
	{
	TeX_skip_parens (bufp);
	if (**bufp)
	    (*bufp)++;
	if (begin_p)
	    {
	    TeX_skip_parens (bufp); /* now skip opt. args if on this line. */
	    math_mode += 2;
	    /* indicate minipage mode. */
	    math_mode += ((math_mode & 127) - 1) * 128;
	    }
	else
	    {
	    math_mode -= (math_mode & 127) * 128;
	    if (math_mode < 0)
		{
		(void) fprintf (stderr, DEFMT_C_LR_MATH_ERROR);
		math_mode = 1;
		}
	    }
	return 1;
	}
    (*bufp)--;
    return 0;
    }

/* Skips the begin{ARG}, and optionally up to two {PARAM}{PARAM}'s to
 *  the begin if they are required.  However, Only skips if on this line.
 */
static void TeX_skip_args (bufp)
    char **	bufp;
    {
    register int skip_cnt = 0; /* Max of 2. */

    if (strncmp(*bufp, "tabular", 7) == 0
      ||  strncmp(*bufp, "minipage", 8) == 0)
	skip_cnt++;
    if (strncmp(*bufp, "tabular*", 8) == 0)
	skip_cnt++;
    TeX_skip_parens (bufp);	/* Skip to the end of the \begin{} parens */
    if (**bufp)
	(*bufp)++;
    else
	return;
    if (skip_cnt--)
	TeX_skip_parens (bufp);	/* skip 1st {PARAM}. */
    else
	return;
    if (**bufp)
	(*bufp)++;
    else
	return;
    if (skip_cnt)
	TeX_skip_parens (bufp);	/* skip to end of 2nd {PARAM}. */
    }

static int TeX_math_check (cont_char, bufp)
    int		cont_char;
    char **	bufp;
    {

    TeX_open_paren (bufp);
    /* Check for end of line, continue later. */
    if (**bufp == 0)
	{
	LaTeX_Mode = (char) cont_char;
	return 0;
	}
    else
	LaTeX_Mode = 'P';

    if (strncmp (++(*bufp), "equation", 8) == 0
      ||  strncmp (*bufp, "eqnarray", 8) == 0
      ||  strncmp (*bufp, "displaymath", 11) == 0
      ||  strncmp (*bufp, "picture", 7) == 0
#ifdef IGNOREBIB
      ||  strncmp (*bufp, "thebibliography", 15) == 0
#endif
      ||  strncmp (*bufp, "math", 4) == 0)
	{
	(*bufp)--;
	TeX_skip_parens (bufp);
	return 1;
	}
    if (cont_char == 'b')
	TeX_skip_args (bufp);
    else
	TeX_skip_parens (bufp);
    return 0;
    }

static void TeX_skip_parens (bufp)
    char **	bufp;
    {

    while (**bufp  &&  **bufp != hashheader.TeXrightcurly  && 
	    	!(**bufp == hashheader.TeXdollar && 
				   *(*bufp - 1) != hashheader.TeXbackslash))
	(*bufp)++;
    }

static void TeX_open_paren (bufp)
    char **	bufp;
    {
    while (**bufp  &&  **bufp != hashheader.TeXleftcurly  && 
	    			!(**bufp == hashheader.TeXdollar &&
				       *(*bufp - 1) != hashheader.TeXbackslash))
	(*bufp)++;
    }

static void TeX_skip_check (bufp)
    char **	bufp;
    {
    int		skip_ch;

    if (TeX_strncmp(*bufp, "end", 3) == 0
      ||  TeX_strncmp(*bufp, "vspace", 6) == 0
      ||  TeX_strncmp(*bufp, "hspace", 6) == 0
      ||  TeX_strncmp(*bufp, "cite", 4) == 0
      ||  TeX_strncmp(*bufp, "ref", 3) == 0
      ||  TeX_strncmp(*bufp, "parbox", 6) == 0
      ||  TeX_strncmp(*bufp, "label", 5) == 0
      ||  TeX_strncmp(*bufp, "input", 5) == 0
      ||  TeX_strncmp(*bufp, "nocite", 6) == 0
      ||  TeX_strncmp(*bufp, "include", 7) == 0
      ||  TeX_strncmp(*bufp, "includeonly", 11) == 0
      ||  TeX_strncmp(*bufp, "documentstyle", 13) == 0
      ||  TeX_strncmp(*bufp, "documentclass", 13) == 0
      ||  TeX_strncmp(*bufp, "usepackage", 10) == 0
      ||  TeX_strncmp(*bufp, "pagestyle", 9) == 0
      ||  TeX_strncmp(*bufp, "pagenumbering", 13) == 0
#ifndef IGNOREBIB
      ||  TeX_strncmp(*bufp, "bibliography", 12) == 0
      ||  TeX_strncmp(*bufp, "bibitem", 7) == 0
#endif
      ||  TeX_strncmp(*bufp, "hyphenation", 11) == 0
      ||  TeX_strncmp(*bufp, "pageref", 7) == 0)
	{
	TeX_skip_parens (bufp);
	if (**bufp == 0)
	    LaTeX_Mode = 'r';
	}
    else if (TeX_strncmp(*bufp, "rule", 4) == 0		/* skip two args. */
      ||  TeX_strncmp(*bufp, "setcounter", 10) == 0
      ||  TeX_strncmp(*bufp, "addtocounter", 12) == 0
      ||  TeX_strncmp(*bufp, "setlength", 9) == 0
      ||  TeX_strncmp(*bufp, "addtolength", 11) == 0
      ||  TeX_strncmp(*bufp, "settowidth", 10) == 0)
	{
	TeX_skip_parens (bufp);
	if (**bufp == 0)	/* Only skips one {} if not on same line. */
	    LaTeX_Mode = 'r';
	else			/* Skip second arg. */
	    {
	    (*bufp)++;
	    TeX_skip_parens (bufp);
	    if (**bufp == 0)
		LaTeX_Mode = 'r';
	    }
	}
    else if (TeX_strncmp (*bufp, "verb", 4) == 0)
	{
	skip_ch = (*bufp)[4];
	*bufp += 5;
	while (**bufp != skip_ch  &&  **bufp != '\0')
	    (*bufp)++;
	}
    else
	{
	/* Optional tex arguments sometimes should and
	** sometimes shouldn't be checked
	** (eg \section [C Programming] {foo} vs
	**     \rule [3em] {0.015in} {5em})
	** SO -- we'll always spell-check it rather than make a
	** full LaTeX parser.
	*/

	/* Must look at the space after the command. */
	while (isalpha(**bufp)  ||  **bufp == '@')
	    (*bufp)++;
	/*
	** Our caller expects to skip over a single character.  So we
	** need to back up by one.  Ugh.
	*/
	(*bufp)--;
	}
    }

/*
 * TeX_strncmp is like strncmp, except that it returns inequality if
 * the following character of a is alphabetic.  We do not use
 * iswordch here because TeX itself won't normally accept
 * nonalphabetics (except maybe on ISO Latin-1 installations?  I'll
 * have to look into that).  As a special hack, because LaTeX uses the
 * @ sign so much, we'll also accept that character.
 *
 * Properly speaking, the @ sign should be settable in the hash file
 * header, but I doubt that it varies, and I don't want to change the
 * syntax of affix files right now.
 */
static int TeX_strncmp (a, b, n)
    char *	a;		/* Strings to compare */
    char *	b;		/* ... */
    int		n;		/* Number of characters to compare */
    {
    int		cmpresult;	/* Result of calling strncmp */

    cmpresult = strncmp (a, b, n);
    if (cmpresult == 0)
	{
	if (isascii (a[n])  &&  isalpha (a[n]))
	    return 1;		/* Force inequality if alpha follows */
	}
    return cmpresult;
    }
