Metropoli BBS
VIEWER: cmd.c MODE: TEXT (ASCII)
/****************************************************************/
/*								*/
/*			       cmd.c				*/
/*								*/
/*		     command.com Top Level Driver 		*/
/*								*/
/*			  August 9, 1991			*/
/*								*/
/*		        Copyright (c) 1995			*/
/*			Pasquale J. Villani			*/
/*			All Rights Reserved			*/
/*								*/
/* This file is part of DOS-C.					*/
/*								*/
/* DOS-C is free software; you can redistribute it and/or	*/
/* modify it under the terms of the GNU General Public License	*/
/* as published by the Free Software Foundation; either version	*/
/* 2, or (at your option) any later version.			*/
/*								*/
/* DOS-C is distributed in the hope that it will be useful, but	*/
/* WITHOUT ANY WARRANTY; without even the implied warranty of	*/
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See	*/
/* the GNU General Public License for more details.		*/
/*								*/
/* You should have received a copy of the GNU General Public	*/
/* License along with DOS-C; see the file COPYING.  If not,	*/
/* write to the Free Software Foundation, 675 Mass Ave,		*/
/* Cambridge, MA 02139, USA.					*/
/****************************************************************/


/* $Logfile:   C:/dos-c/src/command/cmd.c_v  $ */

/*
 * $Log:   C:/dos-c/src/command/cmd.c_v  $ 
 * 
 *    Rev 1.1   01 Sep 1995 18:04:34   patv
 * First GPL release.
 * 
 *    Rev 1.0   02 Jul 1995 10:01:48   patv
 * Initial revision.
 */


#include <ctype.h>
#include "../../hdr/portab.h"
#define MAIN
#include "globals.h"
#include "proto.h"

static BYTE *RcsId = "$Header:   C:/dos-c/src/command/cmd.c_v   1.1   01 Sep 1995 18:04:34   patv  $";

#ifdef PROTO
struct table
{
	BYTE	*entry;
	BOOL	(*func)(COUNT, BYTE **);
};
#else
struct table
{
	BYTE	*entry;
	BOOL	(*func)();
};
#endif

#ifdef PROTO
struct table *lookup(struct table *, BYTE *);
VOID err_report(COUNT);
VOID put_prompt(BYTE *);
VOID Redirect(BYTE *, BYTE *, BYTE *, BOOL *);
VOID RestoreIO(COUNT, COUNT);
#else
struct table *lookup();
VOID err_report();
VOID put_prompt();
VOID Redirect();
VOID RestoreIO();
#endif

BOOL	ExecCmd();
BOOL	prompt();
BOOL	cmd_path();
BOOL	cmd_exit();
BOOL	type();
BOOL	cd();
BOOL	copy();
BOOL	del();
BOOL	ren();
BOOL	mkdir();
BOOL	rmdir();
BOOL	cmd_time();
BOOL	cmd_date();
BOOL	verify();
BOOL	ver();
BOOL	cmd_break();
BOOL 	batch();
BOOL	label_bat();
BOOL	pause_bat();
BOOL	call_bat();
BOOL	echo_bat();
BOOL	echo_dot_bat();
BOOL	for_bat();
BOOL	if_bat();
BOOL	rem_bat();
BOOL	shift_bat();
BOOL	goto_bat();
BOOL	set_bat();

/* External cmmands							*/

struct table  commands[] =
{
	{"break",	cmd_break},
	{"copy",	copy},
	{"cd",		cd},
	{"chdir",	cd},
	{"date",	cmd_date},
	{"del",		del},
	{"dir",		dir},
	{"erase",	del},
	{"exit",	cmd_exit},
	{"mkdir",	mkdir},
	{"md",		mkdir},
	{"path",	cmd_path},
	{"prompt",	prompt},
	{"ren",		ren},
	{"rmdir",	rmdir},
	{"rd",		rmdir},
	{"time",	cmd_time},
	{"type",	type},
	{"verify",	verify},
	{"ver",		ver},
	{"if",		if_bat},
	{"label",	label_bat},
	{"pause",	pause_bat},
	{"call",	call_bat},
	{"echo",	echo_bat},
	{"echo.",	echo_dot_bat},
	{"echo+",	echo_dot_bat},
	{"echo\"",	echo_dot_bat},
	{"echo/",	echo_dot_bat},
	{"echo[",	echo_dot_bat},
	{"echo]",	echo_dot_bat},
	{"echo:",	echo_dot_bat},
	{"for",		for_bat},
	{"rem",		rem_bat},
	{"shift",	shift_bat},
	{"goto",	goto_bat},
	{"set",		set_bat},
	{"",		ExecCmd}
};

static COUNT argc;
static BYTE *argv[NPARAMS];

static BOOL pflag, bootup = FALSE;

VOID main()
{
	COUNT nread;
	BOOL bool_FLAG = FALSE;
	BOOL cflag;
	BYTE FAR *cmd_tail;
	BYTE *p_ptr;
	extern UWORD _psp;
	psp FAR *p;
	COUNT driveno = -1;
	BYTE pattern[MAX_CMDLINE] = "";
	BYTE path[MAX_CMDLINE] = "", esize[MAX_CMDLINE] = "";

	/* Initialize the interpreter					*/
	p = MK_FP(_psp, 0);
	switchchar = '/';
	batch_FLAG = FALSE;
	argv[0] = args[0];
	argv[1] = (BYTE *)0;
	args[0][0] = '\0';
	*tail = '\0';
	env = (BYTE FAR *)MK_FP(p -> ps_environ, 0);
	cmd_tail = MK_FP(_psp, 0x81);
	fstrncpy((BYTE FAR *)tail, cmd_tail, 0x7f);

	pflag = cflag = FALSE;
	dosopt("$d$p*[e:pc]+", (BYTE FAR *)tail,
		&driveno, path, pattern, esize, &pflag, &cflag);

	/* Get the passed-in Environment size and make certain we	*/
	/* allocate enough space					*/
	EnvSize = EnvSizeUp();
	if(EnvSize < ENV_DEFAULT)
		EnvSize = ENV_DEFAULT;
	if(*esize != '\0')
	{
		COUNT size = atoi(esize);

		bool_FLAG = EnvAlloc(size);
		EnvSize = size;
	}
	else
		bool_FLAG = EnvAlloc(EnvSize);

	if(!bool_FLAG)
		error_message(OUT_ENV_SPACE);

	/* Check what PROMPT is set in env to over ride default 	*/
	p_ptr = EnvLookup("PROMPT");
	if(p_ptr != (BYTE *)0)
		scopy(p_ptr, prompt_string);
	else
		scopy(dflt_pr_string, prompt_string);

	/* Check what PATH is set in env to over ride default 		*/
	p_ptr = EnvLookup("PATH");
	if(p_ptr != (BYTE *)0)
		scopy(p_ptr, path);
	else
		scopy(dflt_path_string, path);
	if(!cflag)
	{
		if(pflag)
		{
			/* Special MS-DOS compatability initialization,	*/
			/* all command shells terminate onto		*/
			/* themselves, but we always terminate at the	*/
			/* root shell. If anyone complains, we'll	*/
			/* change it.					*/
#ifndef DEBUG
			p -> ps_parent = _psp;
#endif

			/* Try to exec autoexec.bat			*/
			bootup = TRUE;
			*tail = '\0';
			if(!batch(".\\autoexec.bat", tail))
			{
				*tail = '\0';
				cmd_date(1, argv);
				cmd_time(1, argv);
				bootup = FALSE;
			}
		}
		else
		{
			/* Announce our version				*/
			printf("\nDOS/NT Command Shell\n%s", copyright);
#ifdef SHWR
			printf("**** Shareware version ****\nPlease register your copy.\n");
#else
			printf("\n\n");
#endif
		}

		FOREVER
		{
			default_drive = DosGetDrive();
			put_prompt(prompt_string);
			if((nread = DosRead(STDIN, (BYTE FAR *)cmd_line, MAX_CMDLINE)) < 0)
				continue;
			do_command(nread);
		}
	}
	else
	{
		BYTE FAR *p;

		default_drive = DosGetDrive();
		for(p = cmd_tail; *p != '\r'; p++)
		{
			if(*p == '/' && (*(p + 1) == 'c' || *(p + 1) == 'C'))
				break;
		}
		p += 2;
		fstrncpy((BYTE FAR *)cmd_line, p, 0x7f);
		for(nread = 0; *p != '\r'; nread++, p++)
			;
		++nread;
		do_command(nread);
	}
}


VOID Redirect(cmd_line, Input, Output, AppendMode)
BYTE *cmd_line, *Input, *Output;
BOOL *AppendMode;
{
	BYTE LocalBuffer[MAX_CMDLINE], *lp, *dp = cmd_line;

	/* First - create an image, since we'll be copying back into	*/
	/* the original buffer.						*/
	strcpy(LocalBuffer, cmd_line);

	/* Next, start looking for redirect symbols.			*/
	lp = skipwh(LocalBuffer);
	while(*lp != '\0')
	{
		switch(*lp)
		{
		case '<':
			lp = scan(++lp, Input);
			break;

		case '>':
			if(*(lp + 1) == '>')
			{
				++lp;
				*AppendMode = TRUE;
			}
			lp = scan(++lp, Output);
			break;

		default:
			*dp++ = *lp++;
			break;
		}
	}
	*dp = '\0';
}


VOID do_command(nread)
COUNT nread;
{
	REG struct table *p;
	REG BYTE *lp;
	COUNT index = 0;
	BYTE Input[MAX_CMDLINE], Output[MAX_CMDLINE];
	BOOL AppendMode;
	COUNT OldStdin = -1, OldStdout = -1, ErrorCode;
	BOOL IORedirected = FALSE;

	if(nread <= 0)
		return;
	cmd_line[nread] = '\0';

	/* Pre-scan the command line and look for any re-directs	*/
	*Input = *Output = '\0';
	AppendMode = FALSE;
	Redirect(cmd_line, Input, Output, &AppendMode);
	IORedirected = (*Input != '\0' || *Output != '\0');
	if(*Input != '\0')
	{
		COUNT Handle;

		if(!DosDupHandle(STDIN, (COUNT FAR *)&OldStdin, (COUNT FAR *)&ErrorCode))
		{
			RestoreIO(OldStdin, -1);
			error_message(INTERNAL_ERR);
			return;
		}

		Handle = DosOpen((BYTE FAR *)Input, O_RDWR);
		if((Handle < 0) || (!DosForceDupHandle(Handle, STDIN, (COUNT FAR *)&ErrorCode)))
		{
			RestoreIO(OldStdin, -1);
			error_message(INTERNAL_ERR);
			return;
		}
		DosClose(Handle);
	}

	if(*Output != '\0')
	{
		COUNT Handle;

		if(!DosDupHandle(STDOUT, (COUNT FAR *)&OldStdout, (COUNT FAR *)&ErrorCode))
		{
			RestoreIO(-1, OldStdout);
			error_message(INTERNAL_ERR);
			return;
		}

		if(AppendMode)
		{
			if((Handle = DosOpen((BYTE FAR *)Output, O_RDWR)) < 0)
			{
				RestoreIO(-1, OldStdout);
				error_message(INTERNAL_ERR);
				return;
			}
			DosSeek(Handle, 2, 0l);
		}
		else
			Handle = DosCreat((BYTE FAR *)Output, D_NORMAL | D_ARCHIVE);

		if((Handle < 0) || (!DosForceDupHandle(Handle, STDOUT, (COUNT FAR *)&ErrorCode)))
		{
			RestoreIO(-1, OldStdout);
			error_message(INTERNAL_ERR);
			return;
		}
		DosClose(Handle);
	}

	for(argc = 0; argc < 16; argc++)
	{
		argv[argc] = (BYTE *)0;
		args[argc][0] = '\0';
	}
	lp = scanspl(cmd_line, args[0], '/');

	if(args[0][0] == '@')
	{
		at_FLAG = TRUE;
		index++;
	}
	else
		at_FLAG = FALSE;

	/* If preceeded by a @, swallow it, it was taken care of	*/
	/* elsewhere.  Also, change case so that our command verb is	*/
	/* case sensitive.						*/
	while(args[0][index] != '\0')
	{

		if(at_FLAG)
			args[0][index-1] = tolower(args[0][index]);
		else
			args[0][index] = tolower(args[0][index]);
		index++;
	}
	if(at_FLAG)
		args[0][index-1] = '\0';

	argv[0] = args[0];
	/* this kludge is for an MS-DOS wart emulation */
	tail = skipwh(lp);

	for(argc = 1; argc < NPARAMS; argc++)
	{
		lp = scan(lp, args[argc]);
		if(*args[argc] == '\0')
			break;
		else
			argv[argc] = args[argc];
	}

	if(*argv[0] != '\0')
	{
		/* Look for just a drive change command, and execute	*/
		/* it if found.						*/
		if(argv[0][1] == ':' && argv[0][2] == NULL)
		{
			BYTE c = argv[0][0];

			if(c >= 'a' && c <= 'z')
				c = c - 'a' + 'A';
			if(c >= 'A' && c <= 'Z')
				DosSetDrive(c - 'A');
		}

		/* It may be a help command request.			*/
		else if((argv[1][0] == switchchar) &&
			 (argv[1][1] == '?'))
		{
			strcpy(tail, " ");
			strcat(tail, argv[0]);
			strcat(tail, "\r\n");
			argc = 2;
			argv[1] = argv[0];
			argv[0] = "help";
			argv[2] = 0;
			ExecCmd(argc, argv);
			if(IORedirected)
				RestoreIO(OldStdin, OldStdout);
		}
		/* do a normal command execution			*/
		else
		{
			p = lookup(commands, argv[0]);
			(*(p -> func))(argc, argv);
			if(IORedirected)
				RestoreIO(OldStdin, OldStdout);
		}
	}
}


BOOL prompt(argc, argv)
WORD argc;
BYTE *argv[];
{
	BYTE *p;
	BYTE *cmd = "PROMPT";

	if(argc == 1)
	{
		p = EnvLookup(cmd);
		if(p != (BYTE *)0)
			scopy(p, prompt_string);
		else
		{
			scopy(dflt_pr_string, prompt_string);
			EnvClearVar(cmd);
		}
	}
	else
	{
		/* Trim trailing newline				*/
		for(p = tail; (*p != '\r') && (*p != '\n'); p++)
			;
		*p = '\0';

		/* should be scopy(argv[1], &pr_string[1]); but to	*/
		/* emulate an MS-DOS wart, is				*/
		scopy(tail, prompt_string);

		/* Now set the environment variable for all children to	*/
		/* see.							*/
		EnvSetVar(cmd, prompt_string);
	}
	return TRUE;
}


struct table *lookup(p, token)
struct table *p;
BYTE *token;
{
	while(*(p -> entry) != '\0')
	{
		if(strcmp(p -> entry, token) == 0)
			break;
		else
			++p;
	}
	return p;
}


VOID RestoreIO(DupStdin, DupStdout)
{
	COUNT ErrorCode;

	if(DupStdin >= 0)
	{
		if(!DosForceDupHandle(DupStdin, STDIN, (COUNT FAR *)&ErrorCode))
			error_message(INTERNAL_ERR);
		DosClose(DupStdin);
	}

	if(DupStdout >= 0)
	{
		if(!DosForceDupHandle(DupStdout, STDOUT, (COUNT FAR *)&ErrorCode))
			error_message(INTERNAL_ERR);
		DosClose(DupStdout);
	}
}


BOOL ExecCmd(argc, argv)
COUNT argc;
BYTE *argv[];
{
	exec_blk exb;
	COUNT err;
	BYTE tmppath[64];
	COUNT idx;
	BOOL ext = FALSE;
	BYTE *extp;
	COUNT len;
	BYTE *lp;
	CommandTail CmdTail;
	fcb fcb1, fcb2;
	static BYTE *extns[2] =
	{
		".com",
		".exe"
	};
	static BYTE *batfile = ".bat";
	BYTE PathString[MAX_CMDLINE];
	BYTE Path[MAX_CMDLINE], *pPath;

	/* Build the path string and create the full string that	*/
	/* includes ".\" so that the current directory is searched	*/
	/* first.  Note that Path is initialized outside the loop.	*/
	strcpy(Path, ".\\");
	strcpy(PathString, EnvLookup("PATH"));
	pPath = PathString;

	do
	{
		/* Build a path to the command.				*/
		if(*pPath == ';')
			++pPath;
		strcpy(tmppath, Path);
		if(*tmppath != '\0' && !((tmppath[strlen(tmppath) - 1] != '\\') == 0))
			strcat(tmppath, "\\");
		strcat(tmppath, argv[0]);

		/* batch processing					*/
		/* search for an extension in the specification		*/
		for(idx = len = strlen(argv[0]) ; idx > 0 && idx > (len - FEXT_SIZE - 2); --idx)
		{
			if(argv[0][idx] == '.')
			{
				ext = TRUE;
				extp = &argv[0][idx];
				break;
			}
		}

		/* If no extension was found, the entire path was	*/
		/* specified and we do not append an extension.		*/
		if(!ext)
		{
			strcat(tmppath, batfile);
			extp = batfile;
		}

		/* if it ends with a '.bat' (either user supplied or	*/
		/* previously added), try to run as a batch.		*/
		if((strcmp(extp, batfile) == 0) && batch(tmppath, tail))
		{
			if(pflag && bootup)
				bootup = FALSE;
			return TRUE;
		}

		/* tail comes in as a string with trailing newline. 	*/
		/* Convert it to a return only and put it into CmdTail	*/
		/* format						*/
		CmdTail.ctCount = (argc > 1) ? strlen(tail) : 1;
		strcpy(CmdTail.ctBuffer, " ");
		strcpy(&CmdTail.ctBuffer[1], (argc > 1) ? tail : "");
		CmdTail.ctBuffer[CmdTail.ctCount] = '\0';
		if(CmdTail.ctCount < LINESIZE)
			CmdTail.ctBuffer[CmdTail.ctCount] = '\0';
		rtn_errlvl = 0;
		exb.exec.env_seg = FP_SEG(env);
		exb.exec.cmd_line = (CommandTail FAR *)&CmdTail;

#if 0
		if(argc > 1)
		{
			DosParseFilename((BYTE FAR *)argv[1], (fcb FAR *)&fcb1, 0);
			exb.exec.fcb_1 = (fcb FAR *)&fcb1;
		}
		else
			exb.exec.fcb_1 = (fcb FAR *)0;
		if(argc > 2)
		{
			exb.exec.fcb_2 = (fcb FAR *)&fcb2;
			DosParseFilename((BYTE FAR *)argv[2], (fcb FAR *)&fcb2, 0);
		}
		else
			exb.exec.fcb_2 = (fcb FAR *)0;
#else
		exb.exec.fcb_1 = (fcb FAR *)0;
		exb.exec.fcb_2 = (fcb FAR *)0;
#endif

		for(idx = 0; idx < 2; idx++)
		{
			strcpy(tmppath, Path);
			if(*tmppath != '\0' && !((tmppath[strlen(tmppath) - 1] != '\\') == 0))
				strcat(tmppath, "\\");
			strcat(tmppath, argv[0]);
			if(!ext)
			{
				strcat(tmppath, extns[idx]);
				extp = extns[idx];
			}
			if(!(strcmp(extp, extns[idx]) == 0))
				continue;
			if((rtn_errlvl = err = DosExec((BYTE FAR *)tmppath, (exec_blk FAR *)&exb)) != SUCCESS)
			{
				switch(err)
				{
				case DE_FILENOTFND:
					continue;

				case DE_INVLDFUNC:
					rtn_errlvl = INV_FUNCTION_PARAM;
					goto errmsg;

				case DE_PATHNOTFND:
					rtn_errlvl = PATH_NOT_FOUND;
					goto errmsg;

				case DE_TOOMANY:
					rtn_errlvl = TOO_FILES_OPEN;
					goto errmsg;

				case DE_ACCESS:
					rtn_errlvl = ACCESS_DENIED;
					goto errmsg;

				case DE_NOMEM:
					rtn_errlvl = INSUFF_MEM;
					goto errmsg;

				default:
					rtn_errlvl = EXEC_ERR;
				errmsg:
					error_message(rtn_errlvl);
					return FALSE;
				}
			}
			else
			{
				rtn_errlvl = DosRtnValue() & 0xff;
				return TRUE;
			}
		}
		if(err < 0 || idx == 2)
		{
			if(!(err == DE_FILENOTFND || idx == 2))
			{
				error_message(EXEC_FAIL);
				return FALSE;
			}
			continue;
		}
	}
	while(*Path = '\0', pPath = scanspl(pPath, Path, ';'), *Path != '\0');
	error_message(BAD_CMD_FILE_NAME);
	return FALSE;
}


BOOL cmd_exit(argc, argv)
COUNT argc;
BYTE FAR *argv[];
{
#ifndef DEBUG
	/* Don't exit from a permanent shell				*/
	if(pflag)
		return TRUE;
#endif

	/* If no values passed, return errorvalue = 0			*/
	if(argc == 1)
		DosExit(0);

	/* otherwise return what the user asked for			*/
	else
	{
		COUNT ret_val;
		static BYTE nums[] = "0123456789";
		BYTE FAR *p;

		for(ret_val = 0, p = argv[1]; isdigit(*p); p++)
		{
			COUNT j;

			for(j = 0; j < 10; j++)
				if(nums[j] == *p)
					break;
			ret_val += j;
		}
		DosExit(ret_val);
	}
        return TRUE;
}


VOID sto(c)
COUNT c;
{
	BYTE ch[1];

	*ch = c;
	DosWrite(STDOUT, (BYTE FAR *)ch, 1);
}

[ RETURN TO DIRECTORY ]