ftp.nice.ch/pub/next/unix/text/NeXT_French_Dictionary.3.1.08.I.bs.tar.gz#/NeXT_French_Dictionary3.1.08/src/ispell-3.1.08.tar.gz#/ispell-3.1/defmt.c

This is defmt.c in view mode; [Download] [Up]

#ifndef lint
static char Rcs_Id[] =
    "$Id: defmt.c,v 1.31 1994/05/25 04:29:28 geoff Exp $";
#endif

/*
 * 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.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 "config.h"
#include "ispell.h"
#include "proto.h"
#include "msgs.h"

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

#define ISTEXTERM(c)   (((c) == TEXLEFTCURLY) || \
			((c) == TEXRIGHTCURLY) || \
			((c) == TEXLEFTSQUARE) || \
			((c) == TEXRIGHTSQUARE))
#define ISMATHCH(c)    (((c) == TEXBACKSLASH) || \
			((c) == TEXDOLLAR) || \
			((c) == TEXPERCENT))

static 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)  &&  !TeX_comment))
      )
	{
	/* 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 (TeX_comment)
		;			/* Don't check comments */
	    else if (*bufp == TEXPERCENT)
		TeX_comment = 1;
	    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 == 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;
		    }
		}
	    }
	bufp++;
	}
    if (*bufp == '\0')
	TeX_comment = 0;
    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')
	    {
	    TeX_comment = 0;
	    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)));
			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)));
			}
		    }
		}
	    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 (TeX_comment)
	return 0;
    else if (**bufp == TEXDOLLAR)
	{
	if ((*bufp)[1] == TEXDOLLAR)
	    (*bufp)++;
	return 1;
	}
    else if (**bufp == TEXPERCENT)
	{
	TeX_comment = 1;
	return 0;
	}
    /* processing extended TeX command */
    (*bufp)++;
    if (**bufp == TEXRIGHTPAREN  ||  **bufp == 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 == TEXDOLLAR)
	{
	if ((*bufp)[1] == TEXDOLLAR)
	    (*bufp)++;
	return 1;
	}
    while (**bufp == TEXBACKSLASH)
	{
	(*bufp)++; /* check for null char here? */
	if (**bufp == TEXLEFTPAREN  ||  **bufp == TEXLEFTSQUARE)
	    return 1;
	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 == TEXLEFTSQUARE  ||  **bufp == TEXLEFTANGLE)
      &&  (*bufp)[1] == TEXDOT)
	{
	(*bufp)++;
	while (**bufp)
	    {
	    if (*(*bufp)++ == TEXDOT
	      &&  (**bufp == TEXRIGHTSQUARE  ||  **bufp == TEXRIGHTANGLE))
		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 != TEXRIGHTCURLY)
	(*bufp)++;
    }

static void TeX_open_paren (bufp)
    char **	bufp;
    {
    while (**bufp  &&  **bufp != TEXLEFTCURLY)
	(*bufp)++;
    }

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

    /* ADDITIONALLY, MAY WANT TO ADD:
     * input, include, includeonly,
     * documentstyle, pagestyle, pagenumbering
     * WITH TWO {} {}'S TO SKIP:
     * setcounter, addtocounter,
     * setlength, addtolength, settowidth
     */

    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
#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_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
	{
	/* 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 just igore it rather than make a
	** full LaTeX parser.
	*/

	/* Must look at the space after the command. */
	while (**bufp
	  && (l1_isstringch (*bufp, charlen, 0)
	    ||  iswordch (chartoichar (**bufp))))
	    {
	    if (!isstringch (*bufp + charlen, 0)
	      &&  !iswordch (chartoichar ((*bufp)[charlen])))
		break;
	    *bufp += charlen;
	    }
	}
    }

/*
 * TeX_strncmp is like strncmp, except that it returns inequality if
 * the following character of a is not 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;
    }

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.