Пример #1
0
/*
 * tokenize - turn a command line into tokens
 */
static void
tokenize(
	const char *line,
	char **tokens,
	int *ntok
	)
{
	register const char *cp;
	register char *sp;
	static char tspace[MAXLINE];

	sp = tspace;
	cp = line;
	for (*ntok = 0; *ntok < MAXTOKENS; (*ntok)++) {
		tokens[*ntok] = sp;
		while (ISSPACE(*cp))
		    cp++;
		if (ISEOL(*cp))
		    break;
		do {
			*sp++ = *cp++;
		} while (!ISSPACE(*cp) && !ISEOL(*cp));

		*sp++ = '\0';
	}
}
Пример #2
0
/*
 *  WriteExpanded - writes up to dwChars or EOL, expanding %SystemRoot%
 *                  returns number of CHARs processed in buffer
 *                          (not number of bytes actually written)
 */
DWORD WriteExpanded(HANDLE hFile,  CHAR *pch, DWORD dwChars)
{
  DWORD dw;
  DWORD dwSave = dwChars;
  CHAR  *pSave = pch;


  while (dwChars && !ISEOL(*pch)) {
        if (*pch == '%' &&
            !_strnicmp(pch, achSYSROOT, sizeof(achSYSROOT)-sizeof(CHAR)) )
           {
            dw = pch - pSave;
            if (dw)  {
                WriteFileAssert(hFile, pSave, dw);
                }

            WriteFileAssert(hFile, achSysRoot, dwLenSysRoot);

            pch     += sizeof(achSYSROOT)-sizeof(CHAR);
            pSave    = pch;
            dwChars -= sizeof(achSYSROOT)-sizeof(CHAR);
            }
        else {
            pch++;
            dwChars -= sizeof(CHAR);
            }
        }

  dw = pch - pSave;
  if (dw) {
      WriteFileAssert(hFile, pSave, dw);
      }

  return (dwSave - dwChars);
}
Пример #3
0
/* return string to end of line from buffer*/
static void
strEol(char *retbuf, char *buf)
{
	while (ISBLANK(*buf))
		++buf;
	while (!ISEOL(*buf))
		*retbuf++ = *buf++;
	*retbuf = 0;
}
Пример #4
0
static void handle_macro(char *s)
{
  char *name;

  if (name = parse_identifier(&s)) {
    s = skip(s);
    if (ISEOL(s))
      s = NULL;  /* no named arguments */
    new_macro(name,endm_dirlist,s);
    myfree(name);
  }
  else
    syntax_error(10);  /* identifier expected */
}
Пример #5
0
PCHAR IsConfigCommand(PCHAR pConfigCommand, int CmdLen, PCHAR pLine)
{
      PCHAR pch;

      if (!_strnicmp(pLine, pConfigCommand, CmdLen)) {
           pch = pLine + CmdLen;
           while (!isgraph(*pch) && !ISEOL(*pch))      // skip to "="
                  pch++;

           if (*pch++ == '=') {
               return pch;
               }
           }

       return NULL;
}
Пример #6
0
char *skip_operand(char *s)
{
  int par_cnt=0;
  while(1){
    if(*s=='(') par_cnt++;
    if(*s==')'){
      if(par_cnt>0)
        par_cnt--;
      else
        syntax_error(3);
    }
    if(ISEOL(s)||(*s==','&&par_cnt==0))
      break;
    s++;
  }
  if(par_cnt!=0)
    syntax_error(4);
  return s;
}
Пример #7
0
static char *skip_macroparam(char *s)
{
  int par_cnt=0;
  char c;

  while(1){
    c = *s;
    if(START_PARENTH(c)) par_cnt++;
    if(END_PARENTH(c)){
      if(par_cnt>0)
        par_cnt--;
      else
        return s;
    }
    if(ISEOL(s)||((c==','||isspace((unsigned char)c))&&par_cnt==0))
      break;
    s++;
  }
  return s;
}
Пример #8
0
char *skip_operand(char *s)
{
  int par_cnt=0;
  char c;

  while(1){
    c = *s;
    if(START_PARENTH(c)) par_cnt++;
    else if(END_PARENTH(c)){
      if(par_cnt>0)
        par_cnt--;
      else
        syntax_error(3);
    }else if(c=='\''||c=='\"')
      s=skip_string(s,c,NULL)-1;
    else if(ISEOL(s)||(c==','&&par_cnt==0))
      break;
    s++;
  }
  if(par_cnt!=0)
    syntax_error(4);
  return s;
}
Пример #9
0
/*
 *  Preprocesses the specfied config file (config.sys\autoexec.bat)
 *  into a temporary file.
 *
 *  - expands %SystemRoot%
 *  - adds SHELL line for config.sys
 *
 *  entry: BOOLEAN bConfig : TRUE  - config.sys
 *                           FALSE - autoexec.bat
 */
void ExpandConfigFiles(BOOLEAN bConfig)
{
   DWORD  dw, dwRawFileSize;

   HANDLE hRawFile;
   HANDLE hTmpFile;
   CHAR **ppTmpFile;
   CHAR *pRawBuffer;
   CHAR *pLine;
   CHAR *pTmp;
   CHAR *pEnvParam= NULL;
   CHAR *pPartyShell=NULL;
   CHAR achRawFile[MAX_PATH+12];
   CHAR *lpszzEnv, *lpszName;
   CHAR cchEnv;

   dw = GetWindowsDirectory(achRawFile, sizeof(achRawFile));
   dwLenSysRoot = GetShortPathNameA(achRawFile, achSysRoot, sizeof(achSysRoot));
   if (dwLenSysRoot >= sizeof(achSysRoot)) {
	dwLenSysRoot = 0;
	achSysRoot[0] = '\0';
	}
   GetPIFConfigFiles(bConfig, achRawFile);
   ppTmpFile = bConfig ? &pchTmpConfigFile : &pchTmpAutoexecFile;

   hRawFile = CreateFile(achRawFile,
                         GENERIC_READ,
                         FILE_SHARE_READ,
                         NULL,
                         OPEN_EXISTING,
                         FILE_ATTRIBUTE_NORMAL,
                         NULL );

   if (hRawFile == (HANDLE)0xFFFFFFFF
       || !dwLenSysRoot
       || dwLenSysRoot >= sizeof(achSysRoot)
       || !(dwRawFileSize = GetFileSize(hRawFile, NULL))
       || dwRawFileSize == 0xFFFFFFFF   )
      {
       RcErrorDialogBox(ED_BADSYSFILE, achRawFile, NULL);
       TerminateVDM();  // skip cleanup since I insist that we exit!
       }

   pRawBuffer = malloc(dwRawFileSize+1);
   // allocate buffer to save environment settings in autoexec.nt
   // I know this is bad to allocate this amount of memory at this
   // moment as we dont know if there are anything we want to keep
   // at all. This allocation simply provides the following error
   // handling easily.
   if(!bConfig) {
	lpszzEnv = lpszzcmdEnv16 = (PCHAR)malloc(dwRawFileSize);
	cchEnv = 0;
   }
   if (!pRawBuffer || (!bConfig && lpszzcmdEnv16 == NULL)) {
       RcErrorDialogBox(ED_INITMEMERR, achRawFile, NULL);
       TerminateVDM();  // skip cleanup since I insist that we exit!
       }

   if (!cmdCreateTempFile(&hTmpFile,ppTmpFile)
       || !ReadFile(hRawFile, pRawBuffer, dwRawFileSize, &dw, NULL)
       || dw != dwRawFileSize )
      {
       GetTempPath(MAX_PATH, achRawFile);
       achRawFile[63] = '\0';
       RcErrorDialogBox(ED_INITTMPFILE, achRawFile, NULL);
       TerminateVDM();  // skip cleanup since I insist that we exit!
       }
    // CHANGE HERE WHEN YOU CHANGE cmdCreateTempFile !!!!!!!!!!
    // we depend on the buffer size allocated for the file name
    dw = GetShortPathNameA(*ppTmpFile, *ppTmpFile, MAX_PATH +13);
    if (dw == 0 || dw > 63)
      {
       GetTempPath(MAX_PATH, achRawFile);
       achRawFile[63] = '\0';
       RcErrorDialogBox(ED_INITTMPFILE, achRawFile, NULL);
       TerminateVDM();  // skip cleanup since I insist that we exit!
       }


      // null terminate the buffer so we can use CRT string functions
    *(pRawBuffer+dwRawFileSize) = '\0';

      // ensure no trailing backslash in System Directory
    if (*(achSysRoot+dwLenSysRoot-1) == '\\') {
        *(achSysRoot + --dwLenSysRoot) = '\0';
        }

    pLine = pRawBuffer;
    while (dwRawFileSize) {
               // skip leading white space
       while (dwRawFileSize && !isgraph(*pLine)) {
            pLine++;
            dwRawFileSize -= sizeof(CHAR);
            }
       if (!dwRawFileSize)  // anything left to do ?
           break;


       if (bConfig)  {
           //
           // filter out country= setting we will create our own based
           // on current country ID and codepage.
           //
           pTmp = IsConfigCommand(achCOUNTRY, sizeof(achCOUNTRY) - sizeof(CHAR), pLine);
           if (pTmp) {
               while (dwRawFileSize && !ISEOL(*pLine)) {
                      pLine++;
                      dwRawFileSize -= sizeof(CHAR);
                      }
               continue;
               }

           // filter out shell= command, saving /E:nn parameter
           pTmp = IsConfigCommand(achSHELL, sizeof(achSHELL) - sizeof(CHAR),pLine);
           if (pTmp) {
                       // skip leading white space
               while (!isgraph(*pTmp) && !ISEOL(*pTmp)) {
                      dwRawFileSize -= sizeof(CHAR);
                      pTmp++;
		      }

                  /*  if for a third party shell (not SCS command.com)
                   *     append the whole thing thru /c parameter
                   *  else
                   *     append user specifed /e: parameter
                   */
               if (!_strnicmp(achSYSROOT,pTmp,sizeof(achSYSROOT)-sizeof(CHAR)))
                  {
                   dw = sizeof(achSYSROOT) - sizeof(CHAR);
                   }
               else if (!_strnicmp(achSysRoot,pTmp, strlen(achSysRoot)))
                  {
                   dw = strlen(achSysRoot);
                   }
               else  {
                   dw = 0;
		   }

	       if (!dw ||
                   _strnicmp(achCOMMAND,pTmp+dw,sizeof(achCOMMAND)-sizeof(CHAR)) )
                  {
                   pPartyShell = pTmp;
                   }
	       else {
                   do {
                      while (*pTmp != '/' && !ISEOL(*pTmp))  // save "/e:"
			     pTmp++;

		      if(ISEOL(*pTmp))
			  break;

                      if (!_strnicmp(pTmp,achENV,sizeof(achENV)-sizeof(CHAR)))
			  pEnvParam = pTmp;

		      pTmp++;

		      } while(1);	 // was: while (!ISEOL(*pTmp));
					 // we have break form this loop now,
					 // and don't need in additional macro..

		   }

                       // skip the "shell=" line
               while (dwRawFileSize && !ISEOL(*pLine)) {
                      pLine++;
                      dwRawFileSize -= sizeof(CHAR);
                      }
               continue;

               }  // END, really is "shell=" line!
           }


       /** Filter out PROMPT, SET and PATH from autoexec.nt
	   for environment merging. The output we prepare here is
	   a multiple strings buffer which has the format as :
	   "EnvName_1 NULL EnvValue_1 NULL[EnvName_n NULL EnvValue_n NULL] NULL
	   We don't take them out from the file because command.com needs
	   them.
	**/
       if (!bConfig)
	    if (!_strnicmp(pLine, achPROMPT, sizeof(achPROMPT) - 1)){
		// prompt command found.
		// the syntax of prompt can be eithe
		// prompt xxyyzz	or
		// prompt=xxyyzz
		//
		strcpy(lpszzEnv, achPROMPT);	// get the name
		lpszzEnv += sizeof(achPROMPT);
		cchEnv += sizeof(achPROMPT);
		pTmp = pLine + sizeof(achPROMPT) - 1;
		// skip possible white chars
		while (!isgraph(*pTmp) && !ISEOL(*pTmp))
		pTmp++;
		if (*pTmp == '=') {
		    pTmp++;
		    while(!isgraph(*pTmp) && !ISEOL(*pTmp))
			pTmp++;
		}
		while(!ISEOL(*pTmp)){
		    *lpszzEnv++ = *pTmp++;
		    cchEnv++;
		}
		// null terminate this
		// it may be "prompt NULL NULL" for delete
		// or "prompt NULL something NULL"
		*lpszzEnv++ = '\0';
		cchEnv++;
	    }
	    else if (!_strnicmp(pLine, achPATH, sizeof(achPATH) - 1)) {
		    // PATH was found, it has the same syntax as
		    // PROMPT
		    strcpy(lpszzEnv, achPATH);
		    lpszzEnv += sizeof(achPATH);
		    cchEnv += sizeof(achPATH);
		    pTmp = pLine + sizeof(achPATH) - 1;
		    while (!isgraph(*pTmp) && !ISEOL(*pTmp))
			pTmp++;
		    if (*pTmp == '=') {
			pTmp++;
			while(!isgraph(*pTmp) && !ISEOL(*pTmp))
			    pTmp++;
		    }
		    while(!ISEOL(*pTmp)) {
			*lpszzEnv++ = *pTmp++;
			cchEnv++;
		    }
		    *lpszzEnv++ = '\0';
		    cchEnv++;
		 }
		 else if(!_strnicmp(pLine, achSET, sizeof(achSET) -1 )) {
			// SET was found, first search for name
			pTmp = pLine + sizeof(achSET) - 1;
			while(!isgraph(*pTmp) && !ISEOL(*pTmp))
			    *pTmp ++;
			// get the name
			lpszName = pTmp;
			// looking for the '='
			// note that the name can have white characters
			while (!ISEOL(*lpszName) && *lpszName != '=')
			    lpszName++;
			if (!ISEOL(*lpszName)) {
			    // copy the name
			    while (pTmp < lpszName) {
				*lpszzEnv++ = *pTmp++;
				cchEnv++;
			    }
			    *lpszzEnv++ = '\0';
			    cchEnv++;
			    // discard the '='
			    pTmp++;
			    // grab the value(may be nothing
			    while (!ISEOL(*pTmp)) {
				*lpszzEnv++ = *pTmp++;
				cchEnv++;
			    }
			    *lpszzEnv++ = '\0';
			    cchEnv++;
			}
		      }


       dw = WriteExpanded(hTmpFile, pLine, dwRawFileSize);
       pLine += dw;
       dwRawFileSize -=dw;

       WriteFileAssert(hTmpFile,achEOL,sizeof(achEOL) - sizeof(CHAR));

       }  // END, while (dwRawFileSize)



    if (bConfig)  {
        UINT OemCP;
        UINT CtryId;
        CHAR szCtryId[64]; // expect "nnn" only

         /*  Ensure that the country settings are in sync with NT This is
          *  especially important for DosKrnl file UPCASE tables. The
          *  doskrnl default is "CTRY_UNITED_STATES, 437". But we add the
          *  country= line to config.sys, even if is US,437, so that the DOS
          *  will know where the default country.sys is.
          */
        if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCOUNTRY,
                          szCtryId, sizeof(szCtryId) - 1) )
          {
           CtryId = strtoul(szCtryId,NULL,10);
           }
        else {
           CtryId = CTRY_UNITED_STATES;
           }

        OemCP = GetOEMCP();

        sprintf(achRawFile,
                "%s=%3.3u,%3.3u,%s\\system32\\%s.sys%s",
                achCOUNTRY, CtryId, OemCP, achSysRoot, achCOUNTRY, achEOL);
        WriteFileAssert(hTmpFile,achRawFile,strlen(achRawFile));



         /*  We cannot allow the user to set an incorrect shell= command
          *  so we will contruct the correct shell= command appending
          *  either (in order of precedence):
          *    1.    /c ThirdPartyShell
          *    2.    /e:NNNN
          *    3.    nothing
          *
          *  If there is a third party shell then we must turn the console
          *  on now since we no longer have control once system32\command.com
          *  spawns the third party shell.
          */

           // write shell=....
        sprintf(achRawFile,
                "%s=%s%s /p %s\\system32",
                achSHELL,achSysRoot, achCOMMAND, achSysRoot);
        WriteFileAssert(hTmpFile,achRawFile,strlen(achRawFile));

           // write extra string (/c ... or /e:nnn)
        if (pPartyShell && isgraph(*pPartyShell)) {
            pTmp = pPartyShell;
            while (!ISEOL(*pTmp))
                   pTmp++;
            }
        else if (pEnvParam && isgraph(*pEnvParam))  {
            pTmp = pEnvParam;
            while (isgraph(*pTmp))
                  pTmp++;
            }
        else {
            pTmp = NULL;
            }

        if (pTmp) {
            *pTmp = '\0';
            if (pPartyShell)  {
                cmdInitConsole();
                strcpy(achRawFile, " /c ");
                strcat(achRawFile, pPartyShell);
                }
            else if (pEnvParam) {
                strcpy(achRawFile, " ");
                strcat(achRawFile, pEnvParam);
                }

            WriteExpanded(hTmpFile, achRawFile, strlen(achRawFile));
            }

        WriteFileAssert(hTmpFile,achEOL,sizeof(achEOL) - sizeof(CHAR));
        }

    SetEndOfFile(hTmpFile);
    CloseHandle(hTmpFile);
    CloseHandle(hRawFile);
    free(pRawBuffer);
    if (!bConfig) {
	// shrink(or free) the memory
	if (cchEnv && lpszzcmdEnv16) {
	    // doubld null terminate it
	    lpszzcmdEnv16[cchEnv++] = '\0';
	    // shrink the memory. If it fails, simple keep
	    // it as is
	    lpszzEnv = realloc(lpszzcmdEnv16, cchEnv);
	    if (lpszzEnv != NULL)
		lpszzcmdEnv16 = lpszzEnv;
	}
	else {
	    free(lpszzcmdEnv16);
	    lpszzcmdEnv16 = NULL;
	}
    }

}
Пример #10
0
void parse(void)
{
  char *s,*line,*inst;
  char *ext[MAX_QUALIFIERS?MAX_QUALIFIERS:1];
  char *op[MAX_OPERANDS];
  int ext_len[MAX_QUALIFIERS?MAX_QUALIFIERS:1];
  int op_len[MAX_OPERANDS];
  int ext_cnt,op_cnt,inst_len;
  instruction *ip;

  while (line = read_next_line()) {

    if (!cond_state()) {
      /* skip source until ELSE or ENDIF */
      int idx = -1;

      s = skip(line);
      if (labname = parse_labeldef(&s,1)) {
        if (*s == ':')
          s++;  /* skip double-colon */
        myfree(labname);
        s = skip(s);
      }
      else {
        if (inst = skip_identifier(s)) {
          inst = skip(inst);
          idx = check_directive(&inst);
        }
      }
      if (idx < 0)
        idx = check_directive(&s);
      if (idx >= 0) {
        if (directives[idx].func == handle_if)
          cond_skipif();
        else if (directives[idx].func == handle_else)
          cond_else();
        else if (directives[idx].func == handle_endif)
          cond_endif();
      }
      continue;
    }

    s = skip(line);
again:
    if (*s=='\0' || *line=='*' || *s==commentchar)
      continue;

    if (labname = parse_labeldef(&s,1)) {
      /* we have found a valid global or local label */
      symbol *sym = new_labsym(0,labname);

      if (*s == ':') {
        /* double colon automatically declares label as exported */
        sym->flags |= EXPORT;
        s++;
      }
      add_atom(0,new_label_atom(sym));
      myfree(labname);
      s = skip(s);
    }
    else {
      /* there can still be a sym. in the 1st fld and an assignm. directive */
      inst = s;
      labname = parse_symbol(&s);
      if (labname == NULL) {
        syntax_error(10);  /* identifier expected */
        continue;
      }
      s = skip(s);

      /* Now we have labname pointing to the first field in the line
         and s pointing to the second. Find out where the directive is. */
      if (!ISEOL(s)) {
#ifdef PARSE_CPU_LABEL
        if (PARSE_CPU_LABEL(labname,&s)) {
          myfree(labname);
          continue;
        }
#endif
        if (handle_directive(s)) {
          myfree(labname);
          continue;
        }
      }

      /* directive or mnemonic must be in the first field */
      myfree(labname);
      s = inst;
    }

    if (!strnicmp(s,".iif",4) || !(strnicmp(s,"iif",3))) {
      /* immediate conditional assembly: parse line after ',' when true */
      s = skip(*s=='.'?s+4:s+3);
      if (do_cond(&s)) {
        s = skip(s);
        if (*s == ',') {
          s = skip(s+1);
          goto again;
        }
        else
          syntax_error(0);  /* malformed immediate-if */
      }
      continue;
    }

    /* check for directives */
    s = parse_cpu_special(s);
    if (ISEOL(s))
      continue;

    if (handle_directive(s))
      continue;

    /* read mnemonic name */
    inst = s;
    ext_cnt = 0;
    if (!ISIDSTART(*s)) {
      syntax_error(10);  /* identifier expected */
      continue;
    }
#if MAX_QUALIFIERS==0
    while (*s && !isspace((unsigned char)*s))
      s++;
    inst_len = s - inst;
#else
    s = parse_instruction(s,&inst_len,ext,ext_len,&ext_cnt);
#endif
    if (!isspace((unsigned char)*s) && *s!='\0')
      syntax_error(2);  /* no space before operands */
    s = skip(s);

    if (execute_macro(inst,inst_len,ext,ext_len,ext_cnt,s))
      continue;

    /* read operands, terminated by comma or blank (unless in parentheses) */
    op_cnt = 0;
    while (!ISEOL(s) && op_cnt<MAX_OPERANDS) {
      op[op_cnt] = s;
      s = skip_operand(s);
      op_len[op_cnt] = oplen(s,op[op_cnt]);
#if !ALLOW_EMPTY_OPS
      if (op_len[op_cnt] <= 0)
        syntax_error(5);  /* missing operand */
      else
#endif
        op_cnt++;
      s = skip(s);
      if (*s != ',')
        break;
      else
        s = skip(s+1);
    }
    eol(s);

    ip = new_inst(inst,inst_len,op_cnt,op,op_len);

#if MAX_QUALIFIERS>0
    if (ip) {
      int i;

      for (i=0; i<ext_cnt; i++)
        ip->qualifiers[i] = cnvstr(ext[i],ext_len[i]);
      for(; i<MAX_QUALIFIERS; i++)
        ip->qualifiers[i] = NULL;
    }
#endif

    if (ip)
      add_atom(0,new_inst_atom(ip));
  }

  cond_check();  /* check for open conditional blocks */
}
Пример #11
0
static void handle_print(char *s)
{
  while (!ISEOL(s)) {
    if (*s == '\"') {
      size_t len;
      char *txt;

      skip_string(s,'\"',&len);
      if (len > 0) {
        txt = mymalloc(len+1);
        s = read_string(txt,s,'\"',8);
        txt[len] = '\0';
        add_atom(0,new_text_atom(txt));
      }
    }
    else {
      int type = PEXP_HEX;
      int size = 16;

      while (*s == '/') {
        /* format character */
        char f;

        s = skip(s+1);
        f = tolower((unsigned char)*s);
        if (s = skip_identifier(s)) {
          switch (f) {
            case 'x':
              type = PEXP_HEX;
              break;
            case 'd':
              type = PEXP_SDEC;
              break;
            case 'u':
              type = PEXP_UDEC;
              break;
            case 'w':
              size = 16;
              break;
            case 'l':
              size = 32;
              break;
            default:
              syntax_error(7,f);  /* unknown print format flag */
              break;
          }
        }
        else {
          syntax_error(9);  /* print format corrupted */
          break;
        }
        s = skip(s);
      }
      add_atom(0,new_expr_atom(parse_expr(&s),type,size));
    }
    s = skip(s);
    if (*s != ',')
      break;
    s = skip(s+1);
  }
  add_atom(0,new_text_atom(NULL));  /* new line */
  eol(s);
}
Пример #12
0
void eol(char *s)
{
  s = skip(s);
  if (!ISEOL(s))
    syntax_error(6);
}
Пример #13
0
/*
 * gettokens - read a line and return tokens
 */
static int
gettokens(
	FILE *fp,
	char *line,
	char **tokenlist,
	int *ntokens
	)
{
	register char *cp;
	register int eol;
	register int ntok;
	register int quoted = 0;

	/*
	 * Find start of first token
	 */
	again:
	while ((cp = fgets(line, MAXLINE, fp)) != NULL) {
		cp = line;
		while (ISSPACE(*cp))
			cp++;
		if (!ISEOL(*cp))
			break;
	}
	if (cp == NULL) {
		*ntokens = 0;
		return CONFIG_UNKNOWN;	/* hack.  Is recognized as EOF */
	}

	/*
	 * Now separate out the tokens
	 */
	eol = 0;
	ntok = 0;
	while (!eol) {
		tokenlist[ntok++] = cp;
		while (!ISEOL(*cp) && (!ISSPACE(*cp) || quoted))
			quoted ^= (*cp++ == '"');

		if (ISEOL(*cp)) {
			*cp = '\0';
			eol = 1;
		} else {		/* must be space */
			*cp++ = '\0';
			while (ISSPACE(*cp))
				cp++;
			if (ISEOL(*cp))
				eol = 1;
		}
		if (ntok == MAXTOKENS)
			eol = 1;
	}

	/*
	 * Return the match
	 */
	*ntokens = ntok;
	ntok = matchkey(tokenlist[0], keywords);
	if (ntok == CONFIG_UNKNOWN)
		goto again;
	return ntok;
}