home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Programming Win32 Under the API
/
ProgrammingWin32UnderTheApiPatVillani.iso
/
Chapter9
/
cmd32
/
Cmd.c
< prev
next >
Wrap
C/C++ Source or Header
|
2000-07-10
|
16KB
|
717 lines
//
/* */
/* cmd.c */
/* */
/* command.com Top Level Driver */
/* */
/* Copyright (c) 2000 */
/* Pasquale J. Villani */
/* All Rights Reserved */
/* */
/* */
/* CMD32 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. */
/* */
/* CMD32 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 CMD32; see the file COPYING. If not, */
/* write to the Free Software Foundation, 675 Mass Ave, */
/* Cambridge, MA 02139, USA. */
//
// $Logfile$
/* $Log$
* $EndLog$ */
#include <windows.h>
#include <stdarg.h>
#include <stdlib.h>
#include <ctype.h>
#define MAIN
#include "globals.h"
#include "proto.h"
#ifdef VERSION_STRINGS
static BYTE *RcsId = "$Header$";
#endif
#define SIZE 4096
struct table
{
BYTE *str;
int (*func)(INT, BYTE **);
};
static BOOL
bCFlag;
struct table *lookup(struct table *, BYTE *);
VOID err_report(INT);
VOID put_prompt(BYTE *);
VOID Redirect(BYTE *, BYTE *, BYTE *, BOOL *);
VOID RestoreIO(HANDLE, HANDLE);
static BOOL MatchCommand(BYTE *pszPattern, BYTE *pszCmd, BOOL *pbBatch);
BOOL ExecCmd(INT argc, BYTE *argv[]);
BOOL CmdExit(INT argc, BYTE *argv[]);
BOOL cd(INT argc, BYTE *argv[]);
BOOL cmd_date(INT argc, BYTE *argv[]);
BOOL cmd_time(INT argc, BYTE *argv[]);
BOOL copy(INT argc, BYTE *argv[]);
BOOL del(INT argc, BYTE *argv[]);
BOOL dir(INT argc, BYTE *argv[]);
BOOL echo_bat(INT argc, BYTE *argv[]);
BOOL echo_dot_bat(INT argc, BYTE *argv[]);
BOOL mkdir(INT argc, BYTE *argv[]);
BOOL ren(INT argc, BYTE *argv[]);
BOOL rmdir(INT argc, BYTE *argv[]);
BOOL type(INT argc, BYTE *argv[]);
BOOL ver(INT argc, BYTE *argv[]);
BOOL Prompt(INT argc, BYTE *argv[]);
BOOL cmd_path(INT argc, BYTE *argv[]);
BOOL set_bat(INT argc, BYTE *argv[]);
BOOL rem_bat(INT argc, BYTE *argv[]);
INT GetDrive(void);
// External commands
struct table commands[] =
{
{"cd", cd},
{"copy", copy},
{"date", cmd_date},
{"del", del},
{"dir", dir},
{"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},
{"exit", CmdExit},
{"md", mkdir},
{"mkdir", mkdir},
{"path", cmd_path},
{"prompt", Prompt},
{"rem", rem_bat},
{"ren", ren},
{"rmdir", rmdir},
{"rd", rmdir},
{"set", set_bat},
{"time", cmd_time},
{"type", type},
{"ver", ver},
{"", ExecCmd}
};
WORD printf (CONST BYTE *fmt, ...);
static INT argc;
static BYTE *argv[NPARAMS];
static BOOL cflag = FALSE, bootup = FALSE;
INT
main(INT argc, BYTE *argv[])
{
BYTE szPath[SIZE] = "";
DWORD nRead;
BYTE *cmd_tail;
/* First, establish stdin and stdout */
hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
hStdin = GetStdHandle(STD_INPUT_HANDLE);
hStderr = GetStdHandle(STD_ERROR_HANDLE);
if(hStdin == INVALID_HANDLE_VALUE
|| hStdout == INVALID_HANDLE_VALUE
|| hStderr == INVALID_HANDLE_VALUE)
return 0;
/* Initialize our program */
hInput = hStdin;
hOutput = hStdout;
bCFlag = FALSE;
echo_FLAG = FALSE;
batch_FLAG = FALSE;
strcpy(szDfltPrompt, "$p$g ");
strcpy(szDfltPath, "");
hHeap = GetProcessHeap();
if(!hHeap)
{
return 1;
}
cmd_tail = GetCommandLine();
/* Check what PROMPT is set in env to over ride default */
nRead = GetEnvironmentVariable("PROMPT", szPrompt, sizeof(szPrompt));
if(!nRead)
{
strcpy(szPrompt, szDfltPrompt);
}
/* Check what PATH is set in env to over ride default */
pszPath = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, SIZE);
nRead = GetEnvironmentVariable("PATH", szPath, SIZE);
if(nRead)
{
strcpy(pszPath, szPath);
}
else
{
strcpy(pszPath, szDfltPath);
}
/* Main body for code. Either read from Stdin and execute */
/* commands or simply execute just once if -c specified. */
if(!cflag)
{
/* Announce our version */
printf(ANNOUNCE, copyright);
printf("\n\n");
FOREVER
{
default_drive = GetDrive();
put_prompt(szPrompt);
if(!ReadFile(hStdin, szCmdLine, MAX_CMDLINE, &nRead, 0))
continue;
do_command(nRead);
}
}
else
{
BYTE FAR *p;
default_drive = GetDrive();
for(p = cmd_tail; *p != '\r'; p++)
{
if(*p == '/' && (*(p + 1) == 'c' || *(p + 1) == 'C'))
break;
}
p += 2;
strncpy(szCmdLine, p, 0x7f);
for(nRead = 0; *p != '\r'; nRead++, p++)
;
++nRead;
do_command(nRead);
}
return 0;
}
VOID Redirect(BYTE *pszCmdLine,
BYTE *pszInput, BYTE *pszOutput,
BOOL *pbAppendMode)
{
BYTE
szLocalBuffer[MAX_CMDLINE],
*pszLine, *pszDest = pszCmdLine;
SECURITY_ATTRIBUTES saAttr;
// First - create an image, since we'll be copying back into
// the original buffer.
strcpy(szLocalBuffer, pszCmdLine);
// Initialize the destination names for later use.
*pszInput = *pszOutput = '\0';
// Next, start looking for redirect symbols.
pszLine = skipwh(szLocalBuffer);
while(*pszLine != '\0')
{
switch(*pszLine)
{
case '<':
pszLine = scan(++pszLine, pszInput);
break;
case '>':
if(*(pszLine + 1) == '>')
{
++pszLine;
*pbAppendMode = TRUE;
}
else
{
*pbAppendMode = FALSE;
}
pszLine = scan(++pszLine, pszOutput);
break;
default:
*pszDest++ = *pszLine++;
break;
}
}
*pszDest = '\0';
// Set the bInheritHandle flag so file handles are inherited.
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// Now that we have the requested redirection, time
// to perform it on the users behalh.
if(*pszInput)
{
hInput = CreateFile(
pszInput, // pointer to name of the file
GENERIC_READ, // access (read-write) mode
FILE_SHARE_READ, // share mode
&saAttr, // pointer to security attributes
OPEN_EXISTING, // how to open
FILE_ATTRIBUTE_NORMAL, // file attributes
0); // handle to file with attributes to copy
if(hInput == INVALID_HANDLE_VALUE)
{
hInput = hStdin;
}
else
{
SetStdHandle(STD_INPUT_HANDLE, hInput);
}
}
if(*pszOutput)
{
hOutput = CreateFile(
pszOutput, // create output file
GENERIC_WRITE, // open for writing
0, // do not share
NULL, // no security
CREATE_ALWAYS, // overwrite existing
FILE_ATTRIBUTE_NORMAL | // normal file
FILE_FLAG_OVERLAPPED, // asynchronous I/O
NULL); // no attr. template
if(hOutput == INVALID_HANDLE_VALUE)
{
hOutput = hStdout;
}
else
{
SetStdHandle(STD_OUTPUT_HANDLE, hOutput);
}
}
}
VOID do_command(INT nRead)
{
BYTE *pszLine;
struct table *pTable;
INT nIndex = 0;
BOOL IORedirected = FALSE;
BYTE szInput[MAX_CMDLINE], szOutput[MAX_CMDLINE];
BOOL bAppendMode;
// If nothing to do, just return
if(nRead <= 0)
return;
// Initialize local variables
szCmdLine[nRead] = '\0';
bAppendMode = FALSE;
// Parse for command line redirection.
Redirect(szCmdLine, szInput, szOutput, &bAppendMode);
IORedirected = (*szInput != '\0' || *szOutput != '\0');
// Now parse for local command arguments.
for(argc = 0; argc < 16; argc++)
{
argv[argc] = (BYTE *)0;
args[argc][0] = '\0';
}
pszLine = scanspl(szCmdLine, args[0], '/');
if(args[0][0] == '@')
{
at_FLAG = TRUE;
nIndex++;
}
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 insensitive.
while(args[0][nIndex] != '\0')
{
if(at_FLAG)
args[0][nIndex-1] = tolower(args[0][nIndex]);
else
args[0][nIndex] = tolower(args[0][nIndex]);
nIndex++;
}
if(at_FLAG)
args[0][nIndex-1] = '\0';
argv[0] = args[0];
// this kludge is for a win32 wart emulation (see ExecCmd)
tail = skipwh(pszLine);
for(argc = 1; argc < NPARAMS; argc++)
{
pszLine = scan(pszLine, 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] == '\0')
{
BYTE c = argv[0][0];
if(c >= 'a' && c <= 'z')
c = c - 'a' + 'A';
if(c >= 'A' && c <= 'Z')
default_drive = (c - 'A');
}
/* It may be a help command request. */
else if( (argc > 1) &&
(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(hStdin, hStdout);
}
/* do a normal command execution */
else
{
#ifdef DEBUG
printf("Looking up %s\n", argv[0]);
#endif
pTable = lookup(commands, argv[0]);
(*(pTable -> func))(argc, argv);
if(IORedirected)
RestoreIO(hStdin, hStdout);
}
}
}
BOOL Prompt(INT argc, BYTE *argv[])
{
BYTE *p;
BYTE *cmd = "PROMPT";
if(argc == 1)
{
strcpy(szPrompt, dflt_pr_string);
SetEnvironmentVariable(cmd, szPrompt);
}
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 */
strcpy(szPrompt, tail);
/* Now set the environment variable for all children to */
/* see. */
SetEnvironmentVariable(cmd, szPrompt);
}
return TRUE;
}
struct table *lookup(p, token)
struct table *p;
BYTE *token;
{
while(*(p -> str) != '\0')
{
if(strcmp(p -> str, token) == 0)
break;
else
++p;
}
return p;
}
VOID RestoreIO(HANDLE hDupStdin, HANDLE hDupStdout)
{
// After process creation, restore the saved STDIN and STDOUT.
if (!SetStdHandle(STD_INPUT_HANDLE, hDupStdin))
error_message(CANNOT_RESTORE);
if (!SetStdHandle(STD_OUTPUT_HANDLE, hDupStdout))
error_message(CANNOT_RESTORE);
if(hOutput != hStdout)
{
CloseHandle(hOutput);
hOutput = hDupStdout;
}
if(hInput != hStdin)
{
CloseHandle(hInput);
hInput = hDupStdin;
}
}
static BOOL MatchCommand(BYTE *pszPattern, BYTE *pszCmd, BOOL *pbBatch)
{
WORD nIdx;
BYTE szPattern[SIZE];
WIN32_FIND_DATA dmp;
HANDLE hDir;
struct _Ext
{
BYTE *pszExt;
BOOL bBatch;
};
#define NUMEXT 3
static struct _Ext exTable[NUMEXT] =
{
{".bat", TRUE},
{".exe", FALSE},
{".com", FALSE}
};
// First, see if we already have a desired extension. If so, try finding it.
for(nIdx = 0; nIdx < NUMEXT; nIdx++)
{
if((strlen(pszPattern) >4)
&& (!_stricmp(&pszPattern[strlen(pszPattern)-4], exTable[nIdx].pszExt)))
{
if((hDir = FindFirstFile((LPCTSTR)pszPattern, (LPWIN32_FIND_DATA)&dmp))
!= INVALID_HANDLE_VALUE)
{
if(pszCmd)
{
strcpy(pszCmd, pszPattern);
}
CloseHandle(hDir);
return TRUE;
}
else
{
return FALSE;
}
}
}
// OK, we need to check, in order, for the command.
for(nIdx = 0; nIdx < NUMEXT; nIdx++)
{
strcpy(szPattern, pszPattern);
strcat(szPattern, exTable[nIdx].pszExt);
if((hDir = FindFirstFile((LPCTSTR)szPattern, (LPWIN32_FIND_DATA)&dmp))
!= INVALID_HANDLE_VALUE)
{
if(pszCmd)
{
strcpy(pszCmd, szPattern);
}
CloseHandle(hDir);
return TRUE;
}
}
// Didn't find it, return an error.
return FALSE;
}
BOOL ExecCmd(INT argc, BYTE *argv[])
{
PROCESS_INFORMATION piProcInfo;
SECURITY_ATTRIBUTES saAttr;
BOOL bRetVal, bFound = FALSE, bBatch = FALSE;
STARTUPINFO siStartInfo;
WORD nIdx;
BYTE szChildCmdLine[MAX_CMDLINE];
BYTE szPath[SIZE] = "", *pszPath, *pszTerm;
DWORD nRead;
// See if there's a PATH spec out there somewhere. If not, use the default.
nRead = GetEnvironmentVariable("PATH", szPath, SIZE);
if(!nRead)
{
strcpy(szPath, szDfltPath);
}
// Go through and search the path. Loop through the path looking
// for the command. Look in the current directory
// first. If it matches a local file, copy in actual file name.
if(MatchCommand(argv[0], szChildCmdLine, &bBatch))
{
bFound = TRUE;
}
else
{
for(pszPath = szPath, pszTerm = szPath; *pszTerm ; pszPath = pszTerm)
{
// Isolate a path component to search
while(*pszTerm && (*pszTerm != ';'))
{
++pszTerm;
}
if(*pszTerm == ';')
{
*pszTerm = '\0';
++pszTerm;
}
strcpy(szChildCmdLine, pszPath);
strcat(szChildCmdLine, "\\");
strcat(szChildCmdLine, argv[0]);
if(MatchCommand(szChildCmdLine, 0, &bBatch))
{
bFound = TRUE;
break;
}
}
}
// Did we find it? If not, report an error and return
if(!bFound)
{
error_message(FILE_NOT_FOUND);
return FALSE;
}
// Build a command line to execute
for(nIdx = 1; nIdx < argc; ++nIdx)
{
strcat(szChildCmdLine, " ");
strcat(szChildCmdLine, argv[nIdx]);
}
// Set the bInheritHandle flag so file handles are inherited.
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// Set up members of STARTUPINFO structure.
ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO); // Create the child process.
// The following is somewhat inetersting. We don't specify a module
// name because of NT stream operating systems. These require a
// null as the module in order to execute 16-bit programs.
// Yet another Microsoft "design feature."
bRetVal = CreateProcess(NULL,
szChildCmdLine, // command line
&saAttr, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION
// Wait for the process to exit, if we didn't have an error
if(bRetVal)
{
WaitForSingleObject(piProcInfo.hProcess, INFINITE);
}
else
{
error_message(EXEC_ERR);
}
return bRetVal;
}
BOOL CmdExit(INT argc, BYTE FAR *argv[])
{
#ifdef DEBUG
printf("Entered CmdExit\n");
#endif
/* If no values passed, return errorvalue = 0 */
if(argc == 1)
ExitProcess(0);
/* otherwise return what the user asked for */
else
{
INT nRetVal;
static BYTE szNums[] = "0123456789";
BYTE *pszNum;
for(nRetVal = 0, pszNum = argv[1]; isdigit(*pszNum); pszNum++)
{
INT j;
for(j = 0; j < 10; j++)
if(szNums[j] == *pszNum)
break;
nRetVal += j;
}
ExitProcess(nRetVal);
}
return TRUE;
}
INT GetDrive(void)
{
BYTE directory[NAMEMAX];
GetCurrentDirectory(NAMEMAX, directory);
return (directory[0] - 'A');
}
/* Win32 adaptation of printf generic output routine */
void HandleChar(BYTE **pszChar, INT cChar)
{
BYTE szBuffer[2];
DWORD cWritten;
if(pszChar && *pszChar != 0)
{
*(*pszChar)++ = cChar;
**pszChar = '\0';
}
else
{
szBuffer[0] = cChar;
szBuffer[1] = '\0';
WriteFile(hOutput, szBuffer, 1, &cWritten, NULL);
}
}