;/*
; *                      Microsoft Confidential
; *                      Copyright (C) Microsoft Corporation 1991
; *                      All Rights Reserved.
; */

/***************************************************************************/
/* PARSE.C 													 									*/				 
/*																									*/						
		 
/*	Command line parsing functions for SETVER.C.										*/
/*																									*/
/*	Valid command lines are:																*/
/*		List table: 					SETVER [D:\path] 									*/
/*		Add entry:						SETVER [D:\path] name.ext X.XX			 	*/
/*		Delete entry:					SETVER [D:\path] name.ext /DELETE		 	*/
/*		Display help					SETVER /? 											*/
/*		Delete entry quietly:		SETVER [D:\path] name.ext /DELETE /QUIET	*/
/*																									*/
/*	The following error codes are returned: 											*/
/*																									*/
/*		S_INVALID_SWITCH	Invalid switch										 			*/
/*		S_INVALID_FNAME	Invalid file name 								 			*/
/*		S_BAD_VERSION_FMT	Invalid version number format 		 					*/
/*		S_BAD_DRV_SPEC		Invalid drive/path specifier				 				*/
/*		S_TOO_MANY_PARMS	Too many command line parameters	 						*/
/*		S_MISSING_PARM		Missing parameter 								 			*/
/*		S_INVALID_PATH		Path specifier is invalid									*/
/*																									*/
/*	johnhe	05-01-90																			*/
/***************************************************************************/

#include	<stdio.h>
#include	<stdlib.h>
#include	<ctype.h>
#include	<string.h>
#include	<dos.h>
#include	<direct.h>

#include	<setver.h>

/***************************************************************************/
/* Parses the command line to get the optional drive letter, optional 		*/
/* executable file name and optional switch /DELETE. Also handles a single */
/* "/?" switch for displaying command help. The /DELETE switch will accept */
/* any number of chars in the the word DELETE for the switch. 	Also			*/
/* supports a /QUIET switch, similarly handled, but only valid in 			*/
/* combination with the /DELETE switch													*/
/*																									*/
/*	int ParseCmd( int argc, char *argv[], struct TableEntry *Entry )	 		*/
/*																									*/
/*	ARGUMENTS:	argc	- Count of command line arguments 					 		*/
/*					argv	- Array of ptrs to command line argments		 			*/
/*					Entry - Ptr to struct to be filled in 						 		*/
/*	RETURNS:		int 	- Valid function number or parse error code  			*/
/*																									*/
/***************************************************************************/

int ParseCmd( int argc, char *argv[], struct TableEntry *Entry )
{
	register		Funct;
	unsigned		uVersion;
	int			iTmp;
	int			iStrLen;

	strcpy( Entry->Path, argv[0] );			/* Set default setver.exe path	*/

	if ( argc == 1 )								/* Chk for default of 0 parms		*/
		return( DO_LIST );						/* No args so just do a listing	*/

	for ( iTmp = 1; iTmp < argc; iTmp++ )
                strupr( argv[ iTmp ] );                                        /* Convert params to upper case */

														/* Chk for help switch				*/
	if ( MatchSwitch( argv[ 1 ], HELP_SWITCH ) )
		return( argc > 2 ? S_TOO_MANY_PARMS : DO_HELP);

	iTmp = 1;

												/* Chk for optional drive:\path spec	*/
	if ( strchr( argv[1], ':' ) )
	{
                if ( IsValidDrive( (unsigned)argv[1][0] - 0x40 ) && argv[1][1] == ':' )
		{
			if ( (iStrLen = strlen( argv[1] )) > (MAX_PATH_LEN - 1) )
				return( S_INVALID_PATH );
			else
			{
				strcpy( Entry->Path, argv[1] );
#ifdef DBCS
				if ( (*(Entry->Path + iStrLen - 1) != '\\' && argv[1][2] != EOL )
					|| CheckDBCSTailByte(Entry->Path,Entry->Path + iStrLen - 1) )
#else
				if ( *(Entry->Path + iStrLen - 1) != '\\' && argv[1][2] != EOL )
#endif
					strcat( Entry->Path, "\\" );
				strcat( Entry->Path, "SETVER.EXE" );
				iTmp++;
			}
		}
		else
			return( S_BAD_DRV_SPEC );
	}

	if ( iTmp >= argc )
		Funct = DO_LIST;

	else if ( IsValidFileName( argv[ iTmp ] ) )
	{
		strcpy( Entry->szFileName, argv[ iTmp++ ] );

		if ( iTmp >= argc )				/* Version # or /D or /Q must follow	*/
			Funct = S_MISSING_PARM;

				/* note that Quiet switch requires Del switch also be supplied */
		else if ( MatchSwitch( argv[ iTmp ], DEL_SWITCH ) )
		{
			if ( ++iTmp < argc )	 /* more args left */
			{
				if (MatchSwitch(argv[iTmp], QUIET_SWITCH))
					Funct = (++iTmp < argc ? S_TOO_MANY_PARMS : DO_QUIET);
				else
					Funct = S_TOO_MANY_PARMS;
			}
			else
				Funct = DO_DELETE;
		}
		else if ( MatchSwitch( argv[iTmp], QUIET_SWITCH ) )
		{
			if ( ++iTmp < argc )						 /* must find delete switch	*/
				if (MatchSwitch(argv[iTmp], DEL_SWITCH))
					Funct = (++iTmp < argc ? S_TOO_MANY_PARMS : DO_QUIET);
				else
					Funct = S_INVALID_SWITCH;
			else
				Funct = S_INVALID_SWITCH;
		}
		else if ( *argv[iTmp] == '/' )		/* Make sure not a bogus switch	*/
			Funct = S_INVALID_SWITCH;
		else if ( (uVersion = ParseVersion( argv[ iTmp++ ] )) != 0 )
		{
			Entry->MajorVer = (char)(uVersion >> 8);
			Entry->MinorVer = (char)(uVersion & 0xff);
			Funct = (iTmp < argc ? S_TOO_MANY_PARMS : DO_ADD_FILE);
		}
		else
			Funct = S_BAD_VERSION_FMT;
	}
	else
		Funct = S_INVALID_FNAME;

	return( Funct );
}


/***************************************************************************/
/* Parses a DOS major and minor version number from an ascii string in the */
/* form of "00.00" where the major number is on the left of the decminal	*/
/* point and the minor version follows the version number. Valid version	*/
/* numbers are decimal numbers between 2.00 and 9.99.				 				*/
/*										 															*/
/*	unsigned ParseVersion( char *szDosVer )					 						*/
/*										 															*/
/*	ARGUMENTS:	szDosVer - Ptr to an ascii verion number string 	 			*/
/*	RETURNS:		unsigned - Version # in the form (Major << 8) + 	 			*/
/*								  Minor or 0 if not valid version string  			*/
/*																									*/
/***************************************************************************/

unsigned ParseVersion( char *szDosVer )
{
	unsigned		Version = 0;
	size_t		Len;
	char			*szMinor;

		/* First parse the minor version number */
	if ( (szMinor = strchr( szDosVer, '.' )) != NULL )
	{
		*szMinor = EOL;
		szMinor++;
		if ( (Len = strlen( szMinor )) > 2	|| !IsDigitStr( szMinor ) )
			Version = (unsigned) S_ERROR;
		else
		{
			Version = (unsigned)atoi( szMinor );
			while( Len++ < 2 )								/* Convert .x to .x0	*/
				Version *= 10;
		}
	}
		/* Now get the major part of the number */
	szDosVer = SkipLeadingChr( szDosVer, '0' );
	if ( Version == (unsigned)S_ERROR || strlen( szDosVer ) > 2 ||
			 !IsDigitStr( szDosVer ) )
		Version = 0;
	else
		Version |= ((unsigned)atoi( szDosVer ) << 8);

		/* Check for min and max versions */
	if ( Version < MIN_VERSION || Version >= MAX_VERSION )
		Version = 0;

	return( Version );
}

/***************************************************************************/
/* Checks a string to verify that all characters in the string are decmial */
/* numbers 0-9.									 											*/
/*										 															*/
/*	int IsDigitStr( char *szStr )						 									*/
/*										 															*/
/*	ARGUMENTS:	szStr - Ptr to ascii string to be scanned 				 		*/
/*	RETURNS:		int 	- TRUE if all chars are numbers else FALSE	 			*/
/*										 															*/
/***************************************************************************/

int IsDigitStr( char *szStr )
{
	while( *szStr != EOL	)
	{
		if ( !isdigit( *(szStr++) ) )
			return( FALSE );
	}
	return( TRUE );
}

/***************************************************************************/
/* Accepts a pointer to a string and a single character to match. Returns  */
/* a ptr to the first character in the string not matching the specified	*/
/* character.									 												*/
/*										 															*/
/*	char *SkipLeadingChr( char *szStr, char chChar )			 					*/
/*										 															*/
/*	ARGUMENTS:	szStr  - Ptr to an ascii string										*/
/*					chChar - Ascii character to match 								 	*/
/*	RETURNS:		char * - Ptr to first char in the string not			 			*/
/*								matching the specified character 				 		*/
/***************************************************************************/

char *SkipLeadingChr( char *szStr, char chChar )
{
	while( *szStr == chChar )
		szStr++;
	return( szStr );
}


/***************************************************************************/
/* Compares a cmd line switch against a test string. The test switch is an */
/* ascii string which will be used as a pattern to be matched against the  */
/* command string. The command string may be any subset of the test string */
/* which has been prefixed with a switch character.				 				*/
/*										 															*/
/*	int MatchSwitch( char *szCmdParm, char *szTestSwitch )			 			*/
/*										 															*/
/*	ARGUMENTS:	szCmdParm		- Command line parameter to be tested 			*/
/*					szTestSwitch	- Switch to test command line against 			*/
/*	RETURN:		int				- TRUE if there is a match else FALSE 			*/
/*																									*/
/***************************************************************************/

int MatchSwitch( char *szCmdParm, char *szTestSwitch )
{
		/* Must have a leading '/' and at least 1 char */
	if ( *(szCmdParm++) != SWITCH_CHAR || *szCmdParm == EOL )
		return( FALSE );

	while ( *szTestSwitch != EOL && *szTestSwitch == *szCmdParm )
		szTestSwitch++, szCmdParm++;

	return( *szCmdParm == EOL ? TRUE : FALSE );
}


/***************************************************************************/
/* Scans a string to see if the string can be used a valid file name.		*/
/* The scan checks to be sure each character in the name is a valid		 	*/
/* character for a path name. There is also a check to be sure that only	*/
/* there is not more than 1 decimal in the name and that if there is a		*/
/* decimal that the primary name and extension do not exceed the maximum	*/
/* length of 8 chars for primary and 3 for extension. If the name does		*/
/* not include a decimal the max length is 8 characters.			 				*/
/*										 															*/
/*	int IsValidFileName( char *szPath )					 								*/
/*										 															*/
/*	ARGUMENTS:	szFile - String containing a file name. 					 		*/
/*	RETURNS	:	int 	 - TRUE if valid name else FALSE. 					 		*/
/*										 															*/
/***************************************************************************/

int IsValidFileName( char *szFile )
{
	char *szDecimal;

	RemoveTrailing( szFile, '.' );

		/*
		 *	Check to be sure length of filename is greater than 0,
		 *	there are no invalid file characters,
		 *	there is no path associated with the filename,
		 *	the filename is not a reserved DOS filename, and
		 *	there are no wildcard characters used in the filename.
	 	*/
#ifdef DBCS
	if ( strlen( szFile ) > 0 && ValidFileChar( szFile ) &&
			 ((strchr(szFile, '\\') == NULL) || CheckDBCSTailByte(szFile,strchr(szFile, '\\'))) &&
			 !IsReservedName( szFile ) && !IsWildCards( szFile ) )
#else
	if ( strlen( szFile ) > 0 && ValidFileChar( szFile ) &&
			 (strchr(szFile, '\\') == NULL) &&
			 !IsReservedName( szFile ) && !IsWildCards( szFile ) )
#endif
	{
			/* Check for appropriate 8.3 filename */
		if ( (szDecimal = strchr( szFile, '.' )) != NULL )
		{
			if ( strchr( szDecimal + 1, '.' ) == NULL &&	/* Chk for more '.'s */
					 (szDecimal - szFile) <= 8 && 			/* Chk lengths			*/
					 (strchr( szDecimal, EOL ) - szDecimal - 1) <= 3 )
				return ( TRUE );
		}
		else if ( strlen( szFile ) <= 8 )
			return ( TRUE );
	}
	return( FALSE );
}

/***************************************************************************/
/* Checks all of the characters in a string to see if they are vaild path  */
/* name characaters.								 											*/
/*										 															*/
/*	int ValidFileChar( char *szFile )					 								*/
/*										 															*/
/*	ARGUMENTS:	szFile - File name string 												*/
/*	RETURN:		int 	 - TRUE if chars in string are valid else 	 			*/
/*								FALSE																*/
/*										 															*/
/***************************************************************************/

int ValidFileChar( char *szFile )
{
	int IsOk = TRUE;

	while ( IsOk && *szFile != EOL )
	#ifdef DBCS
		if (IsDBCSLeadByte(*szFile))
			szFile += 2;
		else
	#endif
		IsOk = IsValidFileChr( *(szFile++) );
	return( IsOk );
}


/***************************************************************************/
/* Checks a file or path name against a list of reserved DOS filenames and */
/* returns TRUE if the name is a reserved name. The function must first		*/
/* off any extension from the name.						 								*/
/*										 															*/
/*	int IsReservedName( char *szFile )					 								*/
/*										 															*/
/*	ARGUMENTS:	szFile - File name string				 								*/
/*	RETURN:		int 	 - TRUE if name is reserved DOS name				 		*/
/*																									*/
/***************************************************************************/

int IsReservedName( char *szFile )
{
	register Status;
	register i;
	char *szTmp;
	static char *apszRes[] = { "AUX", "CLOCK$", "COM1", "COM2",
										"COM3", "COM4", "CON", "LPT", "LPT1",
										"LPT2", "LPT3", "LST", "NUL", "PRN", NULL };

	if ( (szTmp = strchr( szFile, '.' )) != NULL )
		*szTmp = EOL;
	for ( i = 0, Status = FALSE; Status == FALSE && apszRes[i] != NULL; i++ )
                Status = !strcmpi( szFile, apszRes[i] );
	if ( szTmp != NULL )
		*szTmp = '.';

	return( Status );
}

/***************************************************************************/
/* Checks a file or path name for any wildcards (* and ?).	If wildcard 	*/
/* characters exist, it returns TRUE.  Otherwise, it returns FALSE. 			*/
/*										 															*/
/*	int IsWildCards( char *szFile )									 					*/
/*										 															*/
/*	ARGUMENTS:	szFile - File name string				 								*/
/*	RETURN:		int 	 - TRUE if wildcards exist in name					 		*/
/*																									*/
/***************************************************************************/

int IsWildCards( char *szFile )
{
	if ( ((strchr( szFile, '*' )) != NULL) ||
		  ((strchr( szFile, '?' )) != NULL) )
		return( TRUE );
	return( FALSE );
}


/***************************************************************************/
/* Validates a character as a valid path and file name character. 			*/
/*													 												*/
/*	IsValidFileChr( char Char )						 									*/
/*													 												*/
/*	ARGUMENTS:	Char - Character to be tested 										*/
/*	RETURNS:    int  - TRUE if a valid character else FALSE 		 				*/
/*													 												*/
/***************************************************************************/

int IsValidFileChr( char Char )
{
	int IsOk;

	switch( Char )
	{
		case ' '	:
		case '\t' :
		case 0x0d :
		case '/'	:
		case ':'	:
		case ';'	:
		case '='	:
		case '<'	:
		case '>'	:
		case '|'	:
			IsOk = FALSE;
			break;
		default		:
			IsOk = TRUE;
			break;
	}
	return( IsOk );
}

/***************************************************************************/
/* Removes all trailing characters of the type specified from a string. 	*/
/*																									*/
/*	void RemoveTrailing( char *String, char Char )							 		*/
/*																									*/
/*	ARGUMENTS:	String - pointer to a string				 							*/
/*					Char	 - ascii char to remove from end of string				*/
/*	RETURNS:	void							 													*/
/*										 															*/
/***************************************************************************/

void RemoveTrailing( char *String, char Char )
{
	char *EndOfString;

	EndOfString = strchr(String, EOL );
	while( EndOfString != String && *(EndOfString-1) == Char )
		EndOfString--;
	*EndOfString = EOL;
}

/***************************************************************************/
/* Copyright (c) 1989 - Microsoft Corp.                                    */
/* All rights reserved.                                                    */
/*                                                                         */
/* Returns a pointer to the first character in the filename which may or	*/
/* may not be appended to a path.														*/
/* 																								*/
/* char *ParseFileName( char *szPath ) 												*/
/* 																								*/
/* ARGUMENTS:	szPath	- Ptr to a file path in the form d:\xxxx\xxx.xxx	*/
/* RETURNS: 	char *	- Ptr to file name or character after last			*/
/* 							  backslash or ':' in the string if the path did	*/
/* 							  not contain a file name									*/
/*										 															*/
/***************************************************************************/

char *ParseFileName( char *szPath )
{
	char	*szPtr;

	for ( szPtr = szPath;
			*szPtr != EOL && (IsValidFileChr( *szPtr ) ||	*szPtr == ':');
			szPtr++ )
		#ifdef DBCS
			if (IsDBCSLeadByte(*szPtr))
				szPtr++;
		#else
			;
		#endif

	#ifdef DBCS
		while(( --szPtr >= szPath && *szPtr != '\\' && *szPtr != ':') ||
				(szPtr >= szPath && CheckDBCSTailByte(szPath,szPtr)) )
	#else
		while( --szPtr >= szPath && *szPtr != '\\' && *szPtr != ':' )
	#endif
			;

	return( ++szPtr );
}

#ifdef DBCS
/***************************************************************************/
/* Test if the character is DBCS lead byte. 											*/
/*																									*/
/*	int IsDBCSLeadByte(char c)																*/
/*																									*/
/*	ARGUMENTS:	c - character to test 													*/
/*	RETURNS:	TRUE if leadbyte																*/
/*										 															*/
/***************************************************************************/

int IsDBCSLeadByte(c)
unsigned char c;
{
	static unsigned char far *DBCSLeadByteTable = NULL;
	union REGS inregs,outregs;
	struct SREGS segregs;
	unsigned char far *p;


	if (DBCSLeadByteTable == NULL)
	{
		inregs.x.ax = 0x6300;							/* get DBCS lead byte table */
		intdosx(&inregs, &outregs, &segregs);
		FP_OFF(DBCSLeadByteTable) = outregs.x.si;
		FP_SEG(DBCSLeadByteTable) = segregs.ds;
	}

	p = DBCSLeadByteTable;
	while (p[0] || p[1])
	{
		if (c >= p[0] && c <= p[1])
			return TRUE;
		p += 2;
	}
	return ( FALSE );
}


/***************************************************************************/
/*
/*	Check if the character point is at tail byte
/*
/*	input:	*str = strart pointer of the string
/*		*point = character pointer to check
/*	output:	TRUE if at the tail byte
/*
/***************************************************************************/

int	CheckDBCSTailByte(str,point)
unsigned char *str,*point;
{
	unsigned char *p;

	p = point;
	while (p != str)
	{
		p--;
		if (!IsDBCSLeadByte(*p))
		{
			p++;
			break;
		}
	}
	return ((point - p) & 1 ? TRUE : FALSE);
}
#endif