/* m2core.f -- translated by f2c (version 19980913).
   You must link the resulting object file with the libraries:
	-lf2c -lm   (in that order)
*/

#include "f2c.h"

/* Table of constant values */

static integer c__10 = 10;
static integer c__1 = 1;
static integer c__0 = 0;
static integer c__2 = 2;

/* $Procedure      M2CORE ( META/2 core syntax checking routines. ) */
/* Subroutine */ int m2core_0_(int n__, char *temp, integer *tbeg, char *
	keywds, char *string, integer *sbeg, logical *reason, integer *cutoff,
	 integer *m2code, integer *score, char *cause, integer *send, ftnlen 
	temp_len, ftnlen keywds_len, ftnlen string_len, ftnlen cause_len)
{
    /* Initialized data */

    static logical pass1 = TRUE_;

    /* System generated locals */
    integer i__1, i__2, i__3;
    char ch__1[2];

    /* Builtin functions */
    integer i_len(char *, ftnlen);
    /* Subroutine */ int s_copy(char *, char *, ftnlen, ftnlen);
    integer s_cmp(char *, char *, ftnlen, ftnlen), s_rnge(char *, integer, 
	    char *, integer);

    /* Local variables */
    static integer pbeg, pend, best[16], slen, nkey;
    static char mssg[420], root[32];
    extern /* Subroutine */ int m2cal_(char *, char *, integer *, ftnlen, 
	    ftnlen);
    static integer i__;
    extern integer cardc_(char *, ftnlen), cardi_(integer *);
    extern /* Subroutine */ int lcase_(char *, char *, ftnlen, ftnlen), 
	    chkin_(char *, ftnlen);
    static integer tcode;
    static logical endok;
    static integer timeb;
    static logical endit;
    extern /* Subroutine */ int repmc_(char *, char *, char *, char *, ftnlen,
	     ftnlen, ftnlen, ftnlen), copyc_(char *, char *, ftnlen, ftnlen);
    static integer lower;
    static logical error;
    static integer count, upper;
    static char known[32*16];
    extern /* Subroutine */ int m2begr_(char *, integer *, integer *, integer 
	    *, integer *, ftnlen), m2mark_(char *, integer *, integer *, char 
	    *, ftnlen, ftnlen);
    extern logical m2wmch_(char *, integer *, integer *, char *, ftnlen, 
	    ftnlen);
    extern /* Subroutine */ int m2clss_(char *, integer *, char *, ftnlen, 
	    ftnlen);
    static integer db, de;
    extern /* Subroutine */ int m2trim_(char *, char *, ftnlen, ftnlen);
    static integer kb;
    extern logical m2keyw_(char *, ftnlen);
    static integer ke, sb, tb, tc, se, te, endchk;
    static logical cmatch;
    extern /* Subroutine */ int scardc_(integer *, char *, ftnlen);
    extern integer esrchc_(char *, integer *, char *, ftnlen, ftnlen);
    static integer tbegin, sbegin;
    static char artcle[2];
    static logical calwrd;
    static integer bscore;
    extern /* Subroutine */ int fndnwd_(char *, integer *, integer *, integer 
	    *, ftnlen);
    static char phrase[120];
    static logical keytbe, useend;
    static integer begout, lastsb, suffsb, orignl, lastse, mspell, dcount, 
	    suffse;
    static char lowerc[64];
    static integer oversb;
    static char upperc[64], countc[64];
    extern integer qlstnb_(char *, ftnlen);
    static logical usekey;
    static integer mcount, overse;
    static logical keywrd;
    static integer scores[16];
    extern /* Subroutine */ int ssizei_(integer *, integer *);
    extern integer qrtrim_(char *, ftnlen);
    static logical uselst;
    extern /* Subroutine */ int ssizec_(integer *, char *, ftnlen), setmsg_(
	    char *, ftnlen), sigerr_(char *, ftnlen), chkout_(char *, ftnlen),
	     suffix_(char *, integer *, char *, ftnlen, ftnlen), bestwd_(char 
	    *, char *, integer *, integer *, integer *, char *, ftnlen, 
	    ftnlen, ftnlen), inttxt_(integer *, char *, ftnlen);
    extern /* Character */ VOID ana_(char *, ftnlen, char *, char *, ftnlen, 
	    ftnlen);
    extern integer pos_(char *, char *, integer *, ftnlen, ftnlen);

/* $ Abstract */

/*     This routine is the header routine for use by M2MTCH and its */
/*     associated entry point M2RCVR.  As it takes no action, it should */
/*     not be called directly. */

/* $ Required_Reading */

/*      The META/2 book. */

/* $ Keywords */

/*     PARSING */
/*     UTILITY */
/*     WORD */

/* $ Declarations */
/* $ Brief_I/O */

/*     See the entry point headers for description of each of the */
/*     input/output arguements. */

/* $ Detailed_Input */

/*     See individual entry points. */

/* $ Detailed_Output */

/*     See individual entry points. */

/* $ Error_Handling */

/*     See individual entry points. */

/* $ Input_Files */

/*     None. */

/* $ Output_Files */

/*     None. */

/* $ Particulars */

/*     This routine serves as the header routine for entry point M2MTCH */
/*     and its associated entry M2RCVR.  M2MTCH is the essential syntax */
/*     checking portion of the META/2 syntax comparison routine. */

/* $ Examples */

/*     To compare two templates call M2MTCH */

/*     To find the position of a mispelled keyword in the input string */
/*     and the possible spelling corrections call M2RCVR */

/* $ Restrictions */

/*     None. */

/* $ Literature_References */

/*     None. */

/* $ Author_and_Institution */

/*     W.L. Taber     (JPL) */
/*     I.M. Underwood (JPL) */

/* $ Version */

/* -     META/2 Configured Version 2.0.0, 9-MAY-1994 (WLT) */

/*         This is the configured version of the Command Loop */
/*         software as of May 9, 1994 */

/* -     META/2 Configured Version 1.0.0, 3-MAY-1994 (WLT) */

/*         This is the configured version of META/2 */
/*         software as of May 3, 1994 */

/*     Version B1.0.0, 7-APR-1988 (WLT) (IMU) */

/* -& */

/*     SPICELIB functions */

/*     Local variables */

/*     KNOWN, PBEG, and PEND are storage for the recovery entry point. */

/*     Should a spelling error be detected, the best matching words will */
/*     be stored in KNOWN and the index of the beginning and ending */
/*     of the problem word in STRING will be stored in PBEG and PEND */
/*     respectively. */

/*     Initial values */

    /* Parameter adjustments */
    if (cause) {
	}

    /* Function Body */
    switch(n__) {
	case 1: goto L_m2mtch;
	case 2: goto L_m2rcvr;
	}

    return 0;
/* $Procedure  M2MTCH ( Match a string with a simple META/2 template ) */

L_m2mtch:
/* $ Abstract */

/*     This entry points compares simple templates with strings and */
/*     produces scores reflecting the extent of agreement between */
/*     the template and string.  If requested diagnostics are produced. */

/* $ Required_Reading */

/*     The META/2 book. */

/* $ Keywords */

/*     PARSING */
/*     UTILITY */
/*     WORD */

/* $ Declarations */

/*     INTEGER               LBCELL */
/*     PARAMETER           ( LBCELL = -5 ) */

/*     CHARACTER*(*)         TEMP */
/*     INTEGER               TBEG */
/*     CHARACTER*(*)         KEYWDS ( LBCELL: * ) */
/*     CHARACTER*(*)         STRING */
/*     INTEGER               SBEG */
/*     LOGICAL               REASON */
/*     INTEGER               CUTOFF */
/*     INTEGER               M2CODE */
/*     INTEGER               SCORE */
/*     CHARACTER*(*)         CAUSE  ( 2 ) */

/* $ Brief_I/O */

/*     VARIABLE  I/O  DESCRIPTION */
/*     --------  ---  -------------------------------------------------- */
/*     TEMP       I   A simple language specification sentence . */
/*     TBEG       I   Position in the template to start match attempt. */
/*     KEYWDS     I   A cell of possible terminators of a META-KEY string */
/*     STRING     I   A potential language sentence */
/*     SBEG      I/O  Marker of the current start of the string */
/*     REASON     I   Set to .TRUE. to request production of diagnostics */
/*     CUTOFF     I   Spelling error threshold */
/*     M2CODE     O   Indicates type of mismatch between TEMP and  STRING */
/*     SCORE      O   Number giving a measure of how closeness of match. */
/*     CAUSE      O   If requested, a diagnostic of mismatch. */

/* $ Detailed_Input */

/*     TEMP       A simple language specification sentence.  Such a */
/*                statement consists of only keyword and META/2 class */
/*                specifiers.  No groups or @then directives are allowed. */

/*     TBEG       Position in the template to start match attempt. */

/*     KEYWDS     A cell of possible terminators of a META-KEY string. */
/*                This is primarily usefull for higher level routines */
/*                that pick simple templates out of more complex META/2 */
/*                specification sentences. */

/*     STRING     A is a collection of words that might make up a valid */
/*                sentence in a META/2 language.  A substring of STRING */
/*                beginning with SBEG will be matched against TEMP to */
/*                see if we have a valid phrase in a META/2 language. */

/*     SBEG       Marker of the current start of the string */

/*     REASON     Set to .TRUE. to request production of diagnostics */

/*     CUTOFF     is a parameter used to determine how close words */
/*                of STRING must match up with keywords in TEMP */
/*                in order to be diagnosed as spelling errors. */
/*                Ranges from 0 to 100 are acceptable.  A "good" range */
/*                of values is from from 65 to 75. */

/* $ Detailed_Output */

/*     SBEG       if the match is successful, SBEG will be set to the */
/*                first word of the input string that follows the */
/*                matched substring. ( Note that words in KEYWDS do */
/*                not qualify as part of the template, but merely */
/*                serve to delimit the ends of variable length */
/*                templates. Thus if one of these words was actually */
/*                used to delimit the end of TEMP, SBEG will point to */
/*                the beginning of that word in STRING.) */

/*     M2CODE     Indicates type of mismatch between TEMP and  STRING */

/*     M2CODE = 0     Indicates that the template supplied matched the */
/*                    input string as far as it went. */

/*     M2CODE = 10    Indicates that the keyword that was supposed to */
/*                    terminate a variable length template was probably */
/*                    mispelled. */

/*     M2CODE = 11    We were expecting a specific keyword and failed */
/*                    in our match attempt.  It is likely that the */
/*                    keyword was simply misspelled. */

/*     M2CODE = 101   Indicates that a variable length template had too */
/*                    few entries before the keyword was encountered. */

/*     M2CODE = 102   Indicates that a variable length template had too */
/*                    many entries before the keyword was encountered. */

/*     M2CODE = 103   Indicates that the correct number of entries */
/*                    for a variable length template were encountered */
/*                    but the input string terminated without finding */
/*                    the correct keyword. */

/*     M2CODE = 104   Indicates that the string should have terminated */
/*                    but instead contained extra characters. */

/*     M2CODE = 105   Indicates that correct number if items were */
/*                    present in the variable length template but that */
/*                    it did not end with an expected keyword. */
/*                    Moreover, it is not thought that the problem is */
/*                    likely to be a simple spelling error. */

/*     M2CODE = 106   The number of items found in a variable length */
/*                    template was too small and we did not get */
/*                    an expected keyword.  A possible explanation */
/*                    is a mistyping one or more of the letters in */
/*                    one of the META class words. */

/*     M2CODE = 107   The number of items found in a variable length */
/*                    template was too large and we did not get */
/*                    an expected keyword.  A possible explanation */
/*                    is a a forgotten keyword. */

/*     M2CODE = 108   We ran out of string while in a fixed length */
/*                    template. */

/*     M2CODE = 109   We failed to match a META class word while within */
/*                    a fixed length template. */

/*     M2CODE = 110   We were expecting to see a specific keyword and */
/*                    got something else.  This is not thought to be */
/*                    the result of a spelling error. */

/*     M2CODE = 111   We were expecting to see a META class word and */
/*                    failed in our matching attempt. */

/*     M2CODE values from 1001 to 1014 indicate problems that can occur */
/*     when attempting to match a substring with the @calendar specifier. */

/*     M2CODE = 1001  Too many tokens in a @calendar string. */

/*     M2CODE = 1002  Time indicated is JD but no numeric portion */
/*                    supplied. */

/*     M2CODE = 1003  The date portion of the Julian date didn't make */
/*                    it through the number parsing. */

/*     M2CODE = 1004  No date was supplied */

/*     M2CODE = 1005  A year was not supplied in a calendar date */

/*     M2CODE = 1006  Ambiguous date specification. */

/*     M2CODE = 1007  Ambiguous month specification */

/*     M2CODE = 1008  Invalid day specification in a calendar date. */

/*     M2CODE = 1009  Year appears as the second item without a */
/*                    spelled month. */

/*     M2CODE = 1010  Month is not spelled and is not an integer */
/*                    between 1 and 12. */

/*     M2CODE = 1011  Month not spelled and day is not between 1 and */
/*                    366. */

/*     M2CODE = 1012  Hour portion of time is not an integer from 0 */
/*                    to 23. */

/*     M2CODE = 1013  Minutes portio of time is not an integer from */
/*                    0 to 59. */

/*     M2CODE = 1014  Seconds must be a positive number less than 61 */

/*     SCORE      Number giving a measure of how closeness of match, 100 */
/*                points are awarded for matched keywords, 15 points */
/*                for matched classes, 100 points for matched calendar */
/*                strings.  Fractions of 100 awarded for words that */
/*                look like they might be misspelled keyword.  The */
/*                score is used primarily in thos cases when a substring */
/*                does not match any of a collection of templates */
/*                exactly.  In this case the one that has the highest */
/*                score is regarded as being what the user probably */
/*                meant. */

/*     CAUSE      If requested, a diagnostic of mismatch. */

/* $ Exceptions */

/*     The following errors are detected by this routine. */

/*           'SPICE(KEYWORDNOTFOUND)' */

/*     Additional errors may be detected by SPICELIB routines called */
/*     by this routine. */

/* $ Input_Files */

/*     None. */

/* $ Output_Files */

/*     None. */

/* $ Particulars */

/*     This routine is the central utility used in META/2 when attempting */
/*     to match potential sentences with language templates.  It compares */
/*     simple templates with substrings of a command and produces a score */
/*     indicating the degree of match.  Moreover, if requested, */
/*     diagnostics are available that indicate why a string did not */
/*     match a given template. */

/* $ Examples */

/*      None. */

/* $ Restrictions */

/*      It is assumed that all templates are simple META/2 templates. */

/* $ Literature_References */

/*     None. */

/* $ Author_and_Institution */

/*     W.L. Taber     (JPL) */
/*     I.M. Underwood (JPL) */

/* $ Version */

/* -     META/2 Configured Version 2.0.0, 9-MAY-1994 (WLT) */

/*         This is the configured version of the Command Loop */
/*         software as of May 9, 1994 */

/* -     META/2 Configured Version 1.0.0, 3-MAY-1994 (WLT) */

/*         This is the configured version of META/2 */
/*         software as of May 3, 1994 */

/*     Version B1.0.0, 7-APR-1988 (WLT) (IMU) */

/* -& */

/*     Take care of the SPICE error handling first. */

    if (pass1) {
	pass1 = FALSE_;
	ssizei_(&c__10, best);
	ssizei_(&c__10, scores);
	ssizec_(&c__10, known, (ftnlen)32);
	scardc_(&c__1, known, (ftnlen)32);
    }
    slen = i_len(string, string_len);
    s_copy(cause, " ", cause_len, (ftnlen)1);
    *score = 0;
    tbegin = *tbeg;
    sbegin = *sbeg;
    error = FALSE_;
    *m2code = 0;
    mspell = 0;
    kb = 0;
    ke = 0;
    nkey = cardc_(keywds, keywds_len);

/*     Locate the next word of the template. */

    fndnwd_(temp, &tbegin, &tb, &te, temp_len);
    while(tb != 0 && ! error) {

/*        Zero out the keyword pointers. */

	kb = 0;
	ke = 0;
	endok = FALSE_;

/*        Examine the current template word.  Is there a range template */
/*        attatched? */

	orignl = tb;
	m2begr_(temp, &tb, &te, &lower, &upper, temp_len);

/*        Locate the boundaries of the root of this template word. */

	m2trim_(temp + (tb - 1), root, te - (tb - 1), (ftnlen)32);
	tc = qrtrim_(root, (ftnlen)32) - 1 + tb;

/*        If TB changed from its original value there is a range template */
/*        attached to the word TEMP(TB:TE).  The associated values are in */
/*        LOWER and UPPER. */

	keytbe = m2keyw_(temp + (tb - 1), te - (tb - 1));
	calwrd = s_cmp(temp + (tb - 1), "@calendar", tc - (tb - 1), (ftnlen)9)
		 == 0 && ! keytbe;
	if (orignl < tb || calwrd) {

/*           Yes.   There is a range template attatched.  Is it of */
/*           variable length? */

	    if (calwrd) {
		lower = 1;
		upper = 40;
		timeb = sbegin;
	    }
	    if (lower != upper) {

/*              Yes.  The template has a variable length. Determine */
/*              what delimiters might signal the end of a matching */
/*              substring of word from string. */

/*              Possibilities are:  The end of the string     (USEEND) */
/*                                  One of the listed KEYWDS  (USELST) */
/*                                  A keyword listed in TEMP. (USEKEY) */

/*              Right now we don't know which of the three cases to use. */

		useend = FALSE_;
		uselst = FALSE_;
		usekey = FALSE_;
		endok = FALSE_;
		endchk = te + 1;

/*              If the end of the current template word, was not */
/*              at the end of the template, then there might be */
/*              a keyword next.  Look for the next word to find out. */

		fndnwd_(temp, &endchk, &kb, &ke, temp_len);
		if (ke > 0) {

/*                 There is a word in the template that follows */
/*                 our current template word.  See if it is a keyword. */

		    if (m2keyw_(temp + (kb - 1), ke - (kb - 1))) {

/*                    If it is a keyword, it will be used as the */
/*                    delimiter for a sequence of words in STRING. */
/*                    ( Note we only want to work with the root of this */
/*                      template word. ) */

			usekey = TRUE_;
			m2trim_(temp + (kb - 1), root, ke - (kb - 1), (ftnlen)
				32);
			ke = qrtrim_(root, (ftnlen)32) + kb - 1;
		    } else {

/*                    Its not a keyword.  Bad, Bad. The user was not */
/*                    using META/2 properly. */

			chkin_("M2MTCH", (ftnlen)6);
			setmsg_("M2MTCH: Any META-KEY that is preceded by a "
				"variable length range template in a specific"
				"ation statement must be followed by a keywor"
				"d. ", (ftnlen)134);
			sigerr_("SPICE(KEYWORDNOTFOUND)", (ftnlen)22);
			chkout_("M2MTCH", (ftnlen)6);
			return 0;
		    }
		} else if (ke <= 0) {

/*                 We got to this point because there was nothing */
/*                 to look at beyond where we were in TEMP.  So we */
/*                 either use one of the listed keywords or the end */
/*                 of the string will be our delimiter. */

		    if (nkey > 0) {
			uselst = TRUE_;
			endok = esrchc_("@end", &nkey, keywds + keywds_len * 
				6, (ftnlen)4, keywds_len) != 0;
		    } else {
			useend = TRUE_;
		    }
		}

/*              Until we have detected one of the keywords */
/*                 or we have not matched the current class */
/*                 or we run out of words in the sentence */

/*                   Grab the next word of the sentence */
/*                   Check it for keyword . */
/*                   Check it for class . */

		endit = FALSE_;
		keywrd = FALSE_;
		mcount = 0;
		suffsb = 0;
		oversb = 0;
		overse = 0;
		lastsb = sbegin;
		lastse = pos_(string, " ", &sbegin, string_len, (ftnlen)1) - 
			1;
		while(! endit) {

/*                 Fetch the next word of the sentence. */

		    fndnwd_(string, &sbegin, &sb, &se, string_len);

/*                 If there WAS a next word SE will not be zero. */

		    if (se == 0) {
			keywrd = useend || endok;
			endit = TRUE_;

/*                    BEGOUT will point past the matched portion of the */
/*                    string.  If no errors occur, it will be used to */
/*                    set SBEG on output. */

			begout = slen + 1;
		    } else {

/*                    is this a delimiting word for a variable length */
/*                    list? */

			if (uselst) {
			    keywrd = esrchc_(string + (sb - 1), &nkey, keywds 
				    + keywds_len * 6, se - (sb - 1), 
				    keywds_len) != 0;
			    endit = keywrd;
			    if (keywrd) {

/*                          Mark the position just before the beginning */
/*                          of this word in STRING  so that SBEG will */
/*                          point to the first word past the end of */
/*                          the matched portion of STRING. */

				begout = sb - 1;
			    }
			} else if (usekey) {
			    keywrd = m2wmch_(string, &sb, &se, temp + (kb - 1)
				    , string_len, ke - (kb - 1)) && m2keyw_(
				    temp + (kb - 1), ke - (kb - 1));
			    endit = keywrd;

/*                       Mark the position of the "next" character */
/*                       in the string beyond the end of the current */
/*                       STRING word. */

			    begout = se + 1;
			}

/*                    If we didn't bump into a keyword this must */
/*                    be (or should be) another of the words specified */
/*                    by the META-KEY TEMP(TB:TE) */

			if (! keywrd) {
			    cmatch = m2wmch_(string, &sb, &se, temp + (tb - 1)
				    , string_len, te - (tb - 1));
			    if (cmatch) {
				++mcount;

/*                          Mark the position of the first character */
/*                          beyond the end of the current STRING */
/*                          word. */

				begout = se + 1;

/*                          If MCOUNT has gotten too big, record the */
/*                          begin and end of the "bad" portion of the */
/*                          substring. */

				if (mcount == lower + 1) {

/*                             Mark the location of the beginning */
/*                             and end of this word in case we need to */
/*                             backtrack to here. */

				    suffsb = sb;
				    suffse = se;
				} else if (mcount <= upper) {

/*                             Mark the end of this word in case */
/*                             we need it later. */

				    suffse = se;
				} else if (mcount == upper + 1) {
				    oversb = sb;
				    overse = se;
				} else if (mcount > upper) {
				    overse = se;
				}
			    } else {
				endit = TRUE_;
			    }
			}
			lastsb = sb;
			lastse = se;
		    }

/*                 Set the pointer to the input string to the first */
/*                 character past the end of the current word. */

		    sbegin = se + 1;
		}

/*              We're now at the end of the loop matching words of STRING */
/*              with the class of object that had a variable length */
/*              template. */

/*              The question now is: 'Did we get out of the loop in */
/*              a healthy or unhealthy way?' */

/*              Did we have the required range of items in the class? */
/*              Did we hit  the keyword? */

/*              If both questions were answered YES, */

		if (keywrd && mcount >= lower && mcount <= upper) {

/*                 Increment the score by METASC times the number of */
/*                 words found in the variable length template plus */
/*                 KEYSC for getting the keyword right. */

		    if (! calwrd) {
			*score = *score + mcount * 15 + 100;
			if (usekey) {

/*                       set the end of the last template word used to */
/*                       be the end of the keyword that we just hit. */

			    te = ke;
			}
		    } else {
			s_copy(mssg, " ", (ftnlen)420, (ftnlen)1);
			m2cal_(string + (timeb - 1), mssg, &tcode, suffse - (
				timeb - 1), (ftnlen)420);
			if (tcode == 0) {
			    *score += 200;
			} else {
			    *score += 100;
			    error = TRUE_;
			    if (*m2code == 0) {
				*m2code = tcode + 1000;
			    }
			    if (*reason) {
				s_copy(cause, "I was not able to parse the c"
					"alendar string \"", cause_len, (
					ftnlen)45);
				suffix_(string + (timeb - 1), &c__0, cause, 
					suffse - (timeb - 1), cause_len);
				suffix_("\". ", &c__0, cause, (ftnlen)3, 
					cause_len);
				suffix_(mssg, &c__1, cause, (ftnlen)420, 
					cause_len);
				m2mark_(string, &timeb, &suffse, cause, 
					string_len, cause_len);
				s_copy(cause + cause_len, cause, cause_len, 
					cause_len);
			    }
			}
		    }

/*              If less than the required range but a keyword was found */
/*              the error was: " not enough values loaded. " */

		} else if (keywrd && mcount < lower) {
		    inttxt_(&lower, lowerc, (ftnlen)64);
		    inttxt_(&mcount, countc, (ftnlen)64);
		    lcase_(lowerc, lowerc, (ftnlen)64, (ftnlen)64);
		    lcase_(countc, countc, (ftnlen)64, (ftnlen)64);
		    error = TRUE_;
		    if (*m2code == 0) {
			*m2code = 101;
		    }

/*                 We grant METASC points for every word of the current */
/*                 class that was found, but we subtract METASC points */
/*                 for each item we were short.  That is: */

/*                 MCOUNT + ( LOWER - MCOUNT ) = 2*MCOUNT - LOWER */

/* Computing MAX */
		    i__1 = 0, i__2 = (mcount << 1) - lower;
		    *score += max(i__1,i__2) * 15;

/*                 Add on KEYSC points for getting the correct keyword. */

		    *score += 100;
		    if (*reason) {
			s_copy(cause, "I was expecting to see at least # # a"
				"t this point in the command string. I counte"
				"d #. ", cause_len, (ftnlen)86);
			m2clss_(temp + (tb - 1), &lower, phrase, tc - (tb - 1)
				, (ftnlen)120);
			repmc_(cause, "#", lowerc, cause, cause_len, (ftnlen)
				1, (ftnlen)64, cause_len);
			repmc_(cause, "#", phrase, cause, cause_len, (ftnlen)
				1, (ftnlen)120, cause_len);
			repmc_(cause, "#", countc, cause, cause_len, (ftnlen)
				1, (ftnlen)64, cause_len);

/*                    OK. now we want to tack on the string and keep */
/*                    track of where the current word STRING(SB:SE) */
/*                    will get put. */

			m2mark_(string, &lastsb, &lastse, cause, string_len, 
				cause_len);
			s_copy(cause + cause_len, cause, cause_len, cause_len)
				;
		    }

/*              If more than the required range but a keyword was found */
/*              the error was too many values loaded. */

		} else if (keywrd && mcount > upper) {
		    inttxt_(&upper, upperc, (ftnlen)64);
		    inttxt_(&mcount, countc, (ftnlen)64);
		    lcase_(upperc, upperc, (ftnlen)64, (ftnlen)64);
		    lcase_(countc, countc, (ftnlen)64, (ftnlen)64);
		    error = TRUE_;
		    if (*m2code == 0) {
			*m2code = 102;
		    }

/*                 We grant METASC points for every word of the current */
/*                 class that was found prior to the cutoff limit. */
/*                 But we subtract METASC points for each extra item. */
/*                 That is: */

/*                 UPPER + ( MCOUNT - UPPER ) = 2*UPPER - MCOUNT */

/* Computing MAX */
		    i__1 = 0, i__2 = (upper << 1) - mcount;
		    *score += max(i__1,i__2) * 15;

/*                 Add on KEYSC points for getting the correct keyword. */

		    *score += 100;
		    if (*reason) {
			s_copy(cause, "I was expecting to see at most # #. I"
				" counted #. I've marked the location of the "
				"problem for you. ", cause_len, (ftnlen)98);
			m2clss_(temp + (tb - 1), &upper, phrase, tc - (tb - 1)
				, (ftnlen)120);
			repmc_(cause, "#", upperc, cause, cause_len, (ftnlen)
				1, (ftnlen)64, cause_len);
			repmc_(cause, "#", phrase, cause, cause_len, (ftnlen)
				1, (ftnlen)120, cause_len);
			repmc_(cause, "#", countc, cause, cause_len, (ftnlen)
				1, (ftnlen)64, cause_len);
			m2mark_(string, &oversb, &overse, cause, string_len, 
				cause_len);
			s_copy(cause + cause_len, cause, cause_len, cause_len)
				;
		    }

/*              If required range but no keyword, error could be */
/*              misspelled keyword ( we estimate this ) or keyword */
/*              was missing. */

		} else if (mcount >= lower && mcount <= upper) {

/*                 Add METASC points to the score for each of the */
/*                 words encountered. */

		    if (se == 0) {

/*                    We are going to try to see if we had a spelling */
/*                    error that caused us to run out of string */

			fndnwd_(string, &suffsb, &db, &de, string_len);
			orignl = suffsb;
			count = lower + 1;
			bscore = 0;
			dcount = 0;
			while(count <= mcount) {
			    if (usekey) {
				s_copy(known + 192, temp + (kb - 1), (ftnlen)
					32, ke - (kb - 1));

/*                          Compare the last word encountered in the */
/*                          string with the KEYWORD we were expecting. */

				bestwd_(string + (db - 1), known, cutoff, 
					best, scores, mssg, de - (db - 1), (
					ftnlen)32, (ftnlen)420);
			    } else if (uselst) {

/*                          Compare the last word that we hit with one */
/*                          of the keywords from the list of possible */
/*                          closing keywords. */

				bestwd_(string + (db - 1), keywds, cutoff, 
					best, scores, mssg, de - (db - 1), 
					keywds_len, (ftnlen)420);
			    }
			    if (cardi_(scores) > 0 && scores[6] >= *cutoff) {

/*                          We are going to treat this as a spelling */
/*                          error. */

				if (*m2code == 0) {
				    *m2code = 13;
				}

/*                          Save the beginning and ending of the */
/*                          problem word for use in the recovery */
/*                          entry point. */

				if (scores[6] > bscore) {
				    bscore = scores[6];
				    pbeg = db;
				    pend = de;

/*                             Everything up to this is now regarded */
/*                             as simply matching the META-KEY.  Store */
/*                             this number of META-KEYs for use by */
/*                             diagnostics generation. */

				    dcount = count - 1;
				}
			    }
			    suffsb = de + 1;

/*                       Look at the next word until we have gone */
/*                       past UPPER even if we already have a */
/*                       candidate for misspelling, there might be */
/*                       a better one. */

			    fndnwd_(string, &suffsb, &db, &de, string_len);
			    ++count;
			}

/*                    Save the misspelling information associated */
/*                    with the best match (if there was one). */

			if (bscore > 0) {
			    if (usekey) {
				s_copy(known + 192, temp + (kb - 1), (ftnlen)
					32, ke - (kb - 1));

/*                          Compare the last word encountered in the */
/*                          string with the KEYWORD we were expecting. */

				bestwd_(string + (pbeg - 1), known, cutoff, 
					best, scores, mssg, pend - (pbeg - 1),
					 (ftnlen)32, (ftnlen)420);
			    } else if (uselst) {

/*                          Compare the last word that we hit with one */
/*                          of the keywords from the list of possible */
/*                          closing keywords. */

				bestwd_(string + (pbeg - 1), keywds, cutoff, 
					best, scores, mssg, pend - (pbeg - 1),
					 keywds_len, (ftnlen)420);

/*                          Save the best matches for use in the */
/*                          recovery entry point. */

				i__1 = cardi_(best);
				for (i__ = 1; i__ <= i__1; ++i__) {
				    s_copy(known + (((i__2 = i__ + 5) < 16 && 
					    0 <= i__2 ? i__2 : s_rnge("known",
					     i__2, "m2core_", (ftnlen)1152)) 
					    << 5), keywds + (best[(i__3 = i__ 
					    + 5) < 16 && 0 <= i__3 ? i__3 : 
					    s_rnge("best", i__3, "m2core_", (
					    ftnlen)1152)] + 5) * keywds_len, (
					    ftnlen)32, keywds_len);
				}
				i__1 = cardi_(best);
				scardc_(&i__1, known, (ftnlen)32);
			    }

/*                       This is not regarded as an error worth */
/*                       stopping for unless our */
/*                       misspelling total has runs over 100. */

			    mspell += 100 - bscore;
			    if (mspell < 100) {
				*score = *score + dcount * 15 + bscore;

/*                          Back the value of SBEGIN back up to the */
/*                          point of failure, so that we can continue */
/*                          processing as if nothing had gone wrong. */

				sbegin = pend + 1;
			    } else {
				*score += dcount * 15;
				error = TRUE_;
			    }
			} else {

/*                       Restore the initial value of SUFFSB */

			    suffsb = orignl;
			    *score += mcount * 15;
			    if (*m2code == 0) {
				*m2code = 103;
			    }

/*                       This occurs if we ran out of stuff in STRING */
/*                       and we were looking to find a keyword instead. */

			    error = TRUE_;
			}
			if (usekey && *reason) {
			    s_copy(cause, "I was looking for the keyword \"", 
				    cause_len, (ftnlen)31);
			    suffix_(temp + (kb - 1), &c__1, cause, ke - (kb - 
				    1), cause_len);
			    suffix_("\" when I reached the", &c__1, cause, (
				    ftnlen)20, cause_len);
			    suffix_("end of the input ", &c__1, cause, (
				    ftnlen)17, cause_len);
			    suffix_("command. ", &c__1, cause, (ftnlen)9, 
				    cause_len);
			} else if (uselst && *reason) {
			    s_copy(cause, "I was looking for one of the keyw"
				    "ords that follow when I reached the end "
				    "of the input command.  Keywords: {", 
				    cause_len, (ftnlen)107);
			    i__1 = nkey;
			    for (i__ = 1; i__ <= i__1; ++i__) {
				suffix_(keywds + (i__ + 5) * keywds_len, &
					c__2, cause, keywds_len, cause_len);
				suffix_(",", &c__0, cause, (ftnlen)1, 
					cause_len);
			    }
			    i__1 = qlstnb_(cause, cause_len) - 1;
			    s_copy(cause + i__1, " }.", cause_len - i__1, (
				    ftnlen)3);
			}
			if (*reason && bscore != 0) {
			    s_copy(cause + cause_len, cause, cause_len, 
				    cause_len);
			    s_copy(cause, " ", cause_len, (ftnlen)1);
			    suffix_(mssg, &c__1, cause, (ftnlen)420, 
				    cause_len);
			    m2mark_(string, &pbeg, &pend, cause, string_len, 
				    cause_len);
			    suffix_(mssg, &c__1, cause + cause_len, (ftnlen)
				    420, cause_len);
			    m2mark_(string, &pbeg, &pend, cause + cause_len, 
				    string_len, cause_len);
			} else if (*reason) {
			    m2mark_(string, &lastsb, &lastse, cause, 
				    string_len, cause_len);
			    s_copy(cause + cause_len, cause, cause_len, 
				    cause_len);
			}

/*                 Recall that we are examining the case when the number */
/*                 of word matches is within the expected range, but */
/*                 no keyword was present.  We have already looked at */
/*                 what to do if we ran out of string prematurely. */

		    } else if (se > 0) {
			*score += mcount * 15;

/*                    We ran into something unexepected.  Possibly */
/*                    a misspelled keyword.  See if any of the */
/*                    expected keywords are close to what we got. */

			if (useend) {
			    error = TRUE_;
			    if (*m2code == 0) {
				*m2code = 104;
			    }
			    if (*reason) {
				s_copy(cause, "The input command contains ex"
					"tra characters that are not part of "
					"a valid command.  ", cause_len, (
					ftnlen)83);
				m2mark_(string, &sb, &se, cause, string_len, 
					cause_len);
				s_copy(cause + cause_len, cause, cause_len, 
					cause_len);
			    }
			} else if (usekey || uselst) {
			    if (usekey) {
				s_copy(known + 192, temp + (kb - 1), (ftnlen)
					32, ke - (kb - 1));

/*                          Compare the last word encountered in the */
/*                          string with the KEYWORD we were expecting. */

				bestwd_(string + (sb - 1), known, cutoff, 
					best, scores, mssg, se - (sb - 1), (
					ftnlen)32, (ftnlen)420);
			    } else if (uselst) {

/*                          Compare the last word that we hit with one */
/*                          of the keywords from the list of possible */
/*                          closing keywords. */

				bestwd_(string + (sb - 1), keywds, cutoff, 
					best, scores, mssg, se - (sb - 1), 
					keywds_len, (ftnlen)420);

/*                          Save the best matches for use in the recovery */
/*                          entry point. */

				i__1 = cardi_(best);
				for (i__ = 1; i__ <= i__1; ++i__) {
				    s_copy(known + (((i__2 = i__ + 5) < 16 && 
					    0 <= i__2 ? i__2 : s_rnge("known",
					     i__2, "m2core_", (ftnlen)1312)) 
					    << 5), keywds + (best[(i__3 = i__ 
					    + 5) < 16 && 0 <= i__3 ? i__3 : 
					    s_rnge("best", i__3, "m2core_", (
					    ftnlen)1312)] + 5) * keywds_len, (
					    ftnlen)32, keywds_len);
				}
				i__1 = cardi_(best);
				scardc_(&i__1, known, (ftnlen)32);
			    }

/*                       We are still checking out the case in which we */
/*                       had a correct range of words for a variable */
/*                       length template, but ran into */
/*                       something that was not a terminating keyword */
/*                       that we were expecting.  Possibly we hit a */
/*                       mispelled keyword. */

/*                       Well? Was there anything to the rumor of a */
/*                       spelling error? */

			    if (cardi_(scores) > 0 && scores[6] >= *cutoff) {
				if (*m2code == 0) {
				    *m2code = 10;

/*                             Save the beginning and ending of the */
/*                             problem word for use in the recovery */
/*                             entry point. */

				    pbeg = sb;
				    pend = se;
				}

/*                          This is probably a spelling error. */
/*                          Point out the error. */

				mspell += 100 - scores[6];
				if (mspell < 100) {
				    *score += scores[6];
				} else {
				    error = TRUE_;
				}
				if (*reason) {

/*                             Construct an error message indicating */
/*                             the spelling diagnostic. */

				    error = TRUE_;
				}
			    } else if (cardi_(scores) == 0 || scores[6] < *
				    cutoff) {

/*                          This is not a misspelling. */
/*                          Set the error flag */

				error = TRUE_;
				if (*m2code == 0) {
				    *m2code = 105;
				}
				s_copy(mssg, " ", (ftnlen)420, (ftnlen)1);
			    }
			    if (*reason && usekey) {
				s_copy(cause, "I was looking for the ", 
					cause_len, (ftnlen)22);
				suffix_("keyword \"", &c__1, cause, (ftnlen)9,
					 cause_len);
				suffix_(temp + (kb - 1), &c__0, cause, ke - (
					kb - 1), cause_len);
				suffix_("\" when I ", &c__0, cause, (ftnlen)9,
					 cause_len);
				suffix_("encountered ", &c__1, cause, (ftnlen)
					12, cause_len);
				suffix_("the word \"", &c__1, cause, (ftnlen)
					10, cause_len);
				suffix_(string + (sb - 1), &c__0, cause, se - 
					(sb - 1), cause_len);
				suffix_("\".   ", &c__0, cause, (ftnlen)5, 
					cause_len);
				suffix_(mssg, &c__1, cause, (ftnlen)420, 
					cause_len);
				s_copy(cause + cause_len, cause, cause_len, 
					cause_len);
				s_copy(cause, mssg, cause_len, (ftnlen)420);
				m2mark_(string, &sb, &se, cause, string_len, 
					cause_len);
				m2mark_(string, &sb, &se, cause + cause_len, 
					string_len, cause_len);
			    } else if (*reason && uselst) {
				s_copy(cause, "I was looking for one of the "
					"keywords in the list: { ", cause_len, 
					(ftnlen)53);
				i__1 = nkey;
				for (i__ = 1; i__ <= i__1; ++i__) {
				    suffix_(keywds + (i__ + 5) * keywds_len, &
					    c__1, cause, keywds_len, 
					    cause_len);
				    if (i__ != nkey) {
					suffix_(",", &c__0, cause, (ftnlen)1, 
						cause_len);
				    }
				}
				suffix_("}  when I ", &c__1, cause, (ftnlen)
					10, cause_len);
				suffix_("encountered ", &c__1, cause, (ftnlen)
					12, cause_len);
				suffix_("the word \"", &c__1, cause, (ftnlen)
					10, cause_len);
				suffix_(string + (sb - 1), &c__0, cause, se - 
					(sb - 1), cause_len);
				suffix_("\".   ", &c__0, cause, (ftnlen)5, 
					cause_len);
				suffix_(mssg, &c__1, cause, (ftnlen)420, 
					cause_len);
				m2mark_(string, &sb, &se, cause, string_len, 
					cause_len);
				s_copy(cause + cause_len, cause, cause_len, 
					cause_len);
			    }
			}
		    }

/*              If out of range and no keyword then we don't have */
/*              a good guess as to what went wrong. */

		} else if (! keywrd && (mcount < lower || mcount > upper)) {
		    if (mcount < lower) {
			if (*m2code == 0) {
			    *m2code = 106;
			}
/* Computing MAX */
			i__1 = 0, i__2 = (mcount << 1) - lower;
			*score += max(i__1,i__2) * 15;
			error = TRUE_;
		    } else if (mcount > upper) {
			if (usekey || uselst) {

/*                       We are going to try to see if we had a spelling */
/*                       error that caused us to get too many words. */

			    fndnwd_(string, &suffsb, &db, &de, string_len);
			    count = lower + 1;
			    bscore = 0;
			    dcount = 0;
			    while(count <= upper + 1) {
				if (usekey) {
				    s_copy(known + 192, temp + (kb - 1), (
					    ftnlen)32, ke - (kb - 1));

/*                             Compare the last word encountered in the */
/*                             string with the KEYWORD we were expecting. */

				    bestwd_(string + (db - 1), known, cutoff, 
					    best, scores, mssg, de - (db - 1),
					     (ftnlen)32, (ftnlen)420);
				} else if (uselst) {

/*                             Compare the last word that we hit with one */
/*                             of the keywords from the list of possible */
/*                             closing keywords. */

				    bestwd_(string + (db - 1), keywds, cutoff,
					     best, scores, mssg, de - (db - 1)
					    , keywds_len, (ftnlen)420);
				}
				if (cardi_(scores) > 0 && scores[6] >= *
					cutoff) {

/*                             We are going to treat this as a spelling */
/*                             error. */

				    if (*m2code == 0) {
					*m2code = 12;
				    }

/*                             Save the beginning and ending of the */
/*                             problem word for use in the recovery */
/*                             entry point. */

				    if (scores[6] > bscore) {
					bscore = scores[6];
					pbeg = db;
					pend = de;

/*                                Everything up to this is now regarded */
/*                                as simply matching the META-KEY.  Store */
/*                                this number of META-KEYs for use by */
/*                                diagnostics generation. */

					dcount = count - 1;
				    }
				}
				suffsb = de + 1;

/*                          Look at the next word until we have gone */
/*                          past UPPER even if we already have a */
/*                          candidate for misspelling, there might be */
/*                          a better one. */

				fndnwd_(string, &suffsb, &db, &de, string_len)
					;
				++count;
			    }

/*                       Save the misspelling information associated */
/*                       with the best match (if there was one). */

			    if (bscore > 0) {
				if (usekey) {
				    s_copy(known + 192, temp + (kb - 1), (
					    ftnlen)32, ke - (kb - 1));

/*                             Compare the last word encountered in the */
/*                             string with the KEYWORD we were expecting. */

				    bestwd_(string + (pbeg - 1), known, 
					    cutoff, best, scores, mssg, pend 
					    - (pbeg - 1), (ftnlen)32, (ftnlen)
					    420);
				} else if (uselst) {

/*                             Compare the last word that we hit with one */
/*                             of the keywords from the list of possible */
/*                             closing keywords. */

				    bestwd_(string + (pbeg - 1), keywds, 
					    cutoff, best, scores, mssg, pend 
					    - (pbeg - 1), keywds_len, (ftnlen)
					    420);

/*                             Save the best matches for use in the */
/*                             recovery entry point. */

				    i__1 = cardi_(best);
				    for (i__ = 1; i__ <= i__1; ++i__) {
					s_copy(known + (((i__2 = i__ + 5) < 
						16 && 0 <= i__2 ? i__2 : 
						s_rnge("known", i__2, "m2cor"
						"e_", (ftnlen)1575)) << 5), 
						keywds + (best[(i__3 = i__ + 
						5) < 16 && 0 <= i__3 ? i__3 : 
						s_rnge("best", i__3, "m2core_"
						, (ftnlen)1575)] + 5) * 
						keywds_len, (ftnlen)32, 
						keywds_len);
				    }
				    i__1 = cardi_(best);
				    scardc_(&i__1, known, (ftnlen)32);
				}

/*                          This is not regarded as an error worth */
/*                          stopping for unless our */
/*                          misspelling total has runs over 100. */

				mspell += 100 - bscore;
				if (mspell < 100) {
				    *score = *score + dcount * 15 + bscore;

/*                             Back the value of SBEGIN back up to the */
/*                             point of failure, so that we can continue */
/*                             processing as if nothing had gone wrong. */

				    sbegin = pend + 1;
				} else {
				    *score += dcount * 15;
				    error = TRUE_;
				}
			    }
			}

/*                    We might not have had a good candidate for a */
/*                    misspelling, if not we don't have a good clue */
/*                    as to what went wrong. */

			if (*m2code == 0) {
			    *m2code = 107;
/* Computing MAX */
			    i__1 = 0, i__2 = (upper << 1) - mcount;
			    *score += max(i__1,i__2) * 15;
			    error = TRUE_;
			}
		    }

/*                 If there is to be a diagnostic generated, set up */
/*                 the beginning of it so that everyone else can */
/*                 share in the same work. */

		    if (*reason) {
			error = TRUE_;
			s_copy(cause, "I was expecting to see between # and "
				"# # ", cause_len, (ftnlen)41);
			m2clss_(temp + (tb - 1), &upper, phrase, tc - (tb - 1)
				, (ftnlen)120);
			repmc_(cause, "#", lowerc, cause, cause_len, (ftnlen)
				1, (ftnlen)64, cause_len);
			repmc_(cause, "#", upperc, cause, cause_len, (ftnlen)
				1, (ftnlen)64, cause_len);
			repmc_(cause, "#", phrase, cause, cause_len, (ftnlen)
				1, (ftnlen)120, cause_len);
			if (usekey) {
			    suffix_("followed by ", &c__1, cause, (ftnlen)12, 
				    cause_len);
			    suffix_("the keyword, ", &c__1, cause, (ftnlen)13,
				     cause_len);
			    suffix_(temp + (kb - 1), &c__1, cause, ke - (kb - 
				    1), cause_len);
			    suffix_(".", &c__0, cause, (ftnlen)1, cause_len);
			} else if (uselst) {
			    suffix_("followed by ", &c__1, cause, (ftnlen)12, 
				    cause_len);
			    suffix_("one of the ", &c__1, cause, (ftnlen)11, 
				    cause_len);
			    suffix_("keywords from the", &c__1, cause, (
				    ftnlen)17, cause_len);
			    suffix_("list {", &c__1, cause, (ftnlen)6, 
				    cause_len);
			    i__1 = nkey;
			    for (i__ = 1; i__ <= i__1; ++i__) {
				suffix_(keywds + (i__ + 5) * keywds_len, &
					c__1, cause, keywds_len, cause_len);
				if (i__ != nkey) {
				    suffix_(",", &c__1, cause, (ftnlen)1, 
					    cause_len);
				}
			    }
			    suffix_("}.", &c__1, cause, (ftnlen)2, cause_len);
			} else if (useend) {
			    suffix_("filling out the   ", &c__1, cause, (
				    ftnlen)18, cause_len);
			    suffix_("end of the string.", &c__1, cause, (
				    ftnlen)18, cause_len);
			}

/*                    Use the information stored in M2CODE to determine */
/*                    how many words we encountered before we figured */
/*                    out we had an error. */

			if (*m2code >= 100) {
			    inttxt_(&mcount, countc, (ftnlen)64);
			    lcase_(countc, countc, (ftnlen)64, (ftnlen)64);
			} else {
			    inttxt_(&dcount, countc, (ftnlen)64);
			    lcase_(countc, countc, (ftnlen)64, (ftnlen)64);
			}
			suffix_("I had counted ", &c__1, cause, (ftnlen)14, 
				cause_len);
			suffix_(countc, &c__1, cause, (ftnlen)64, cause_len);
			if (mcount == 1) {
			    suffix_("such word", &c__1, cause, (ftnlen)9, 
				    cause_len);
			} else {
			    suffix_("such words", &c__1, cause, (ftnlen)10, 
				    cause_len);
			}
			suffix_("when I encountered", &c__1, cause, (ftnlen)
				18, cause_len);
		    }

/*                 We are still in the case of a variable length template */
/*                 for which we did not hit a keyword and did not have */
/*                 the expected range of items for the current META-KEY. */

/*                 OK. Now tailor the end of the message to reflect */
/*                 what went wrong in particular. */

		    if (*reason && *m2code < 100) {
			suffix_("the word \"", &c__1, cause, (ftnlen)10, 
				cause_len);
			suffix_(string + (pbeg - 1), &c__0, cause, pend - (
				pbeg - 1), cause_len);
			suffix_("\" .", &c__0, cause, (ftnlen)3, cause_len);
			suffix_(mssg, &c__1, cause, (ftnlen)420, cause_len);
			m2mark_(string, &pbeg, &pend, cause, string_len, 
				cause_len);
			s_copy(cause + cause_len, cause, cause_len, cause_len)
				;
		    } else if (*reason && se == 0) {
			suffix_("the end of the input", &c__1, cause, (ftnlen)
				20, cause_len);
			suffix_("string.    ", &c__1, cause, (ftnlen)11, 
				cause_len);
			i__1 = qlstnb_(string, string_len) + 1;
			i__2 = qlstnb_(string, string_len) + 1;
			m2mark_(string, &i__1, &i__2, cause, string_len, 
				cause_len);
			s_copy(cause + cause_len, cause, cause_len, cause_len)
				;

/*                  check for a misspell. */

		    } else if (*reason && se != 0) {
			suffix_("the word \"", &c__1, cause, (ftnlen)10, 
				cause_len);
			suffix_(string + (sb - 1), &c__0, cause, se - (sb - 1)
				, cause_len);
			suffix_("\" .", &c__0, cause, (ftnlen)3, cause_len);

/*                    If misspell likely mention that too. */

			if (usekey) {
			    s_copy(known + 192, temp + (kb - 1), (ftnlen)32, 
				    ke - (kb - 1));
			    bestwd_(string + (sb - 1), known, cutoff, best, 
				    scores, mssg, se - (sb - 1), (ftnlen)32, (
				    ftnlen)420);
			} else if (uselst) {
			    bestwd_(string + (sb - 1), keywds, cutoff, best, 
				    scores, mssg, se - (sb - 1), keywds_len, (
				    ftnlen)420);
			}
			if (cardi_(scores) > 0 && scores[6] > *cutoff) {
			    suffix_(mssg, &c__1, cause, (ftnlen)420, 
				    cause_len);
			}
			m2mark_(string, &sb, &se, cause, string_len, 
				cause_len);
			s_copy(cause + cause_len, cause, cause_len, cause_len)
				;
		    }
		}
	    } else {

/*              This "ELSE" is the "NO" response to the question:  "Ok. */
/*              we have a range template. Is it of variable length?" */

		endit = lower == 0;
		mcount = 0;
		while(! endit) {
		    fndnwd_(string, &sbegin, &sb, &se, string_len);
		    if (se == 0) {
			endit = TRUE_;
			error = TRUE_;
			if (*m2code == 0) {
			    *m2code = 108;
			}
			if (*reason) {
			    s_copy(cause, "I was expecting to see # # when I"
				    " ran out of words in the command string. "
				    , cause_len, (ftnlen)74);
			    m2clss_(temp + (tb - 1), &c__1, phrase, tc - (tb 
				    - 1), (ftnlen)120);
			    ana_(ch__1, (ftnlen)2, phrase, "L", (ftnlen)120, (
				    ftnlen)1);
			    s_copy(artcle, ch__1, (ftnlen)2, (ftnlen)2);
			    repmc_(cause, "#", artcle, cause, cause_len, (
				    ftnlen)1, (ftnlen)2, cause_len);
			    repmc_(cause, "#", phrase, cause, cause_len, (
				    ftnlen)1, (ftnlen)120, cause_len);
			    i__1 = qlstnb_(string, string_len) + 1;
			    i__2 = qlstnb_(string, string_len) + 1;
			    m2mark_(string, &i__1, &i__2, cause, string_len, 
				    cause_len);
			    s_copy(cause + cause_len, cause, cause_len, 
				    cause_len);
			}
		    } else if (m2wmch_(string, &sb, &se, temp + (tb - 1), 
			    string_len, te - (tb - 1))) {
			++mcount;
			*score += 15;
			sbegin = se + 1;

/*                    Mark the position of the first character beyond the */
/*                    current STRING word. */

			begout = sbegin;
			endit = mcount >= lower;
		    } else {
			if (*m2code == 0) {
			    *m2code = 109;
			}
			error = TRUE_;
			endit = TRUE_;
			if (*reason) {
			    s_copy(cause, "I was expecting to see # # when I"
				    " encounterd the word \"#\" in the comman"
				    "d. ", cause_len, (ftnlen)74);
			    m2clss_(temp + (tb - 1), &c__1, phrase, tc - (tb 
				    - 1), (ftnlen)120);
			    ana_(ch__1, (ftnlen)2, phrase, "L", (ftnlen)120, (
				    ftnlen)1);
			    s_copy(artcle, ch__1, (ftnlen)2, (ftnlen)2);
			    repmc_(cause, "#", artcle, cause, cause_len, (
				    ftnlen)1, (ftnlen)2, cause_len);
			    repmc_(cause, "#", phrase, cause, cause_len, (
				    ftnlen)1, (ftnlen)120, cause_len);
			    repmc_(cause, "#", string + (sb - 1), cause, 
				    cause_len, (ftnlen)1, se - (sb - 1), 
				    cause_len);
			    m2mark_(string, &sb, &se, cause, string_len, 
				    cause_len);
			    s_copy(cause + cause_len, cause, cause_len, 
				    cause_len);
			}
		    }
		}
	    }
	} else {
	    fndnwd_(string, &sbegin, &sb, &se, string_len);

/*           This "ELSE" is the "NO" response to the question: "Is a */
/*           range template present?" that was asked a very long, long */
/*           time ago. */

	    cmatch = m2wmch_(string, &sb, &se, temp + (tb - 1), string_len, 
		    te - (tb - 1));

/*           Set the string pointer to the first character following */
/*           the current string word. */

	    sbegin = se + 1;

/*           Record SBEGIN in case we have run out of teplate and */
/*           haven't produced any errors. */

	    begout = sbegin;
	    if (cmatch) {
		keywrd = m2keyw_(temp + (tb - 1), te - (tb - 1));
		if (keywrd) {
		    *score += 100;
		} else {
		    *score += 15;
		}
	    } else if (! cmatch) {
		keywrd = m2keyw_(temp + (tb - 1), te - (tb - 1));

/*              See if we were supposed to get a keyword and if */
/*              so see if this is just some simple spelling error. */

		if (keywrd) {
		    s_copy(known + 192, temp + (tb - 1), (ftnlen)32, tc - (tb 
			    - 1));
		    scardc_(&c__1, known, (ftnlen)32);
		    if (se > 0) {
			bestwd_(string + (sb - 1), known, cutoff, best, 
				scores, mssg, se - (sb - 1), (ftnlen)32, (
				ftnlen)420);
		    }
		    if (cardi_(scores) > 0 && scores[6] >= *cutoff) {
			if (*m2code == 0) {
			    *m2code = 11;

/*                       Save the beginning and ending of the */
/*                       problem word for use in the recovery */
/*                       entry point. */

			    pbeg = sb;
			    pend = se;
			}

/*                    We regard this to be a spelling error  of the */
/*                    keyword. This will be a signal to stop looking at */
/*                    this keyword if we are asking for diagnostics. */

			if (mspell > 100) {
			    error = TRUE_;
			} else {
			    *score += scores[6];
			    mspell += 100 - scores[6];
			}
			if (*reason) {
			    error = TRUE_;
			    s_copy(cause, "I was expecting to see the keywor"
				    "d \"", cause_len, (ftnlen)36);
			    suffix_(temp + (tb - 1), &c__0, cause, tc - (tb - 
				    1), cause_len);
			    suffix_("\" when I encountered", &c__0, cause, (
				    ftnlen)20, cause_len);
			    suffix_("the word \"", &c__1, cause, (ftnlen)10, 
				    cause_len);
			    suffix_(string + (sb - 1), &c__0, cause, se - (sb 
				    - 1), cause_len);
			    suffix_("\" in the input ", &c__0, cause, (ftnlen)
				    15, cause_len);
			    suffix_("string.     ", &c__1, cause, (ftnlen)12, 
				    cause_len);
			    suffix_(mssg, &c__1, cause, (ftnlen)420, 
				    cause_len);
			    s_copy(cause + cause_len, cause, cause_len, 
				    cause_len);
			    s_copy(cause, mssg, cause_len, (ftnlen)420);
			    m2mark_(string, &sb, &se, cause, string_len, 
				    cause_len);
			    m2mark_(string, &sb, &se, cause + cause_len, 
				    string_len, cause_len);
			}
		    } else if (cardi_(scores) == 0 || scores[6] < *cutoff) {
			error = TRUE_;
			if (*m2code == 0) {
			    *m2code = 110;
			    if (se > 0) {
				bestwd_(string + (sb - 1), known, &c__1, best,
					 scores, mssg, se - (sb - 1), (ftnlen)
					32, (ftnlen)420);
			    }
			    if (sb != 0 && cardi_(scores) > 0) {
				*score += scores[6];
			    }
			}
			if (*reason) {
			    s_copy(cause, "I was expecting to see the keywor"
				    "d \"", cause_len, (ftnlen)36);
			    suffix_(temp + (tb - 1), &c__0, cause, tc - (tb - 
				    1), cause_len);
			    suffix_("\" when I ", &c__0, cause, (ftnlen)9, 
				    cause_len);
			    if (sb == 0) {
				suffix_("ran out of ", &c__1, cause, (ftnlen)
					11, cause_len);
				suffix_("characters in the", &c__1, cause, (
					ftnlen)17, cause_len);
				suffix_("input string. ", &c__1, cause, (
					ftnlen)14, cause_len);
				sb = qlstnb_(string, string_len) + 1;
				se = sb;
			    } else {
				suffix_("encountered", &c__1, cause, (ftnlen)
					11, cause_len);
				suffix_("the word \"", &c__1, cause, (ftnlen)
					10, cause_len);
				suffix_(string + (sb - 1), &c__0, cause, se - 
					(sb - 1), cause_len);
				suffix_("\" in the input ", &c__0, cause, (
					ftnlen)15, cause_len);
				suffix_("string.     ", &c__1, cause, (ftnlen)
					12, cause_len);
			    }
			    m2mark_(string, &sb, &se, cause, string_len, 
				    cause_len);
			    s_copy(cause + cause_len, cause, cause_len, 
				    cause_len);
			}
		    }
		} else if (! m2keyw_(temp + (tb - 1), te - (tb - 1))) {
		    error = TRUE_;
		    if (*m2code == 0) {
			*m2code = 111;
		    }
		    if (*reason) {
			s_copy(cause, "I was expecting to see # # when I ", 
				cause_len, (ftnlen)34);
			m2clss_(temp + (tb - 1), &c__1, phrase, tc - (tb - 1),
				 (ftnlen)120);
			ana_(ch__1, (ftnlen)2, phrase, "L", (ftnlen)120, (
				ftnlen)1);
			s_copy(artcle, ch__1, (ftnlen)2, (ftnlen)2);
			repmc_(cause, "#", artcle, cause, cause_len, (ftnlen)
				1, (ftnlen)2, cause_len);
			repmc_(cause, "#", phrase, cause, cause_len, (ftnlen)
				1, (ftnlen)120, cause_len);
			if (sb == 0) {
			    suffix_("ran out of characters", &c__1, cause, (
				    ftnlen)21, cause_len);
			    suffix_("in the input string. ", &c__1, cause, (
				    ftnlen)21, cause_len);
			    sb = qlstnb_(string, string_len) + 1;
			    se = sb;
			} else {
			    suffix_("encountered the word \"", &c__1, cause, (
				    ftnlen)22, cause_len);
			    suffix_(string + (sb - 1), &c__0, cause, se - (sb 
				    - 1), cause_len);
			    suffix_("\" in the input string.", &c__0, cause, (
				    ftnlen)22, cause_len);
			}
			m2mark_(string, &sb, &se, cause, string_len, 
				cause_len);
			s_copy(cause + cause_len, cause, cause_len, cause_len)
				;
		    }
		}
	    }
	}
	tbegin = max(ke,te) + 1;

/*        Locate the next word of the template and continue unless */
/*        we get a second error detected. */

	fndnwd_(temp, &tbegin, &tb, &te, temp_len);
    }

/*     If we got out of the template without an error, set SBEG to */
/*     BEGOUT---the first character after the matched portion of the */
/*     STRING and before the first word of whatever is left. */

    if (*m2code == 0) {
	*sbeg = begout;
    }
    return 0;

/* $Prodedure M2RCVR ( Recover from a spelling error ) */

L_m2rcvr:

/* $ Abstract */

/*     Fetch the indices of the beginning and end of a "misspelled" */
/*     keyword along with the list of corrections. */

/* $ Required_Reading */

/*     The META/2 book. */

/* $ Keywords */

/*     UTILITY */

/* $ Declarations */

/*     INTEGER               SBEG */
/*     INTEGER               SEND */
/*     CHARACTER*(*)         KEYWDS ( LBCELL: * ) */

/* $ Brief_I/O */

/*     VARIABLE  I/O  DESCRIPTION */
/*     --------  ---  -------------------------------------------------- */
/*     SBEG       O   Beginning of "misspelled" word in STRING */
/*     SEND       O   Ending of "misspelled" word in STRING */
/*     KEYWDS     O   Cell of possible correct spellings of keyword. */

/* $ Detailed_Input */

/*     None. */

/* $ Detailed_Output */

/*     SBEG       Beginning of "misspelled" word in STRING */

/*     SEND       Ending of "misspelled" word in STRING */

/*     KEYWDS     Cell of possible correct spellings of keyword. */

/* $ Error_Handling */

/*     No errors are detected by this entry point. */

/* $ Input_Files */

/*     None. */

/* $ Output_Files */

/*     None. */

/* $ Particulars */

/* $ Examples */

/* $ Restrictions */

/*     One must call M2MTCH before calling this routine if correct */
/*     results are desired. */

/* $ Literature_References */

/*     None. */

/* $ Author_and_Institution */

/*     W.L. Taber     (JPL) */
/*     I.M. Underwood (JPL) */

/* $ Version */

/* -     META/2 Configured Version 2.0.0, 9-MAY-1994 (WLT) */

/*         This is the configured version of the Command Loop */
/*         software as of May 9, 1994 */

/* -     META/2 Configured Version 1.0.0, 3-MAY-1994 (WLT) */

/*         This is the configured version of META/2 */
/*         software as of May 3, 1994 */

/*     Version B1.0.0, 7-APR-1988 (WLT) (IMU) */

/* -& */

    *sbeg = pbeg;
    *send = pend;
    copyc_(known, keywds, (ftnlen)32, keywds_len);
    return 0;
} /* m2core_ */

/* Subroutine */ int m2core_(char *temp, integer *tbeg, char *keywds, char *
	string, integer *sbeg, logical *reason, integer *cutoff, integer *
	m2code, integer *score, char *cause, integer *send, ftnlen temp_len, 
	ftnlen keywds_len, ftnlen string_len, ftnlen cause_len)
{
    return m2core_0_(0, temp, tbeg, keywds, string, sbeg, reason, cutoff, 
	    m2code, score, cause, send, temp_len, keywds_len, string_len, 
	    cause_len);
    }

/* Subroutine */ int m2mtch_(char *temp, integer *tbeg, char *keywds, char *
	string, integer *sbeg, logical *reason, integer *cutoff, integer *
	m2code, integer *score, char *cause, ftnlen temp_len, ftnlen 
	keywds_len, ftnlen string_len, ftnlen cause_len)
{
    return m2core_0_(1, temp, tbeg, keywds, string, sbeg, reason, cutoff, 
	    m2code, score, cause, (integer *)0, temp_len, keywds_len, 
	    string_len, cause_len);
    }

/* Subroutine */ int m2rcvr_(integer *sbeg, integer *send, char *keywds, 
	ftnlen keywds_len)
{
    return m2core_0_(2, (char *)0, (integer *)0, keywds, (char *)0, sbeg, (
	    logical *)0, (integer *)0, (integer *)0, (integer *)0, (char *)0, 
	    send, (ftnint)0, keywds_len, (ftnint)0, (ftnint)0);
    }