/*
SHELL.C Simple extendable command interpreter
for MS-DOS version 2.0 and later
Copyright 1988 Ray Duncan
Compile: C>CL SHELL.C
Usage: C>SHELL
*/
#include <stdio.h>
#include <process.h>
#include <stdlib.h>
#include <signal.h>
/* macro to return number of
elements in a structure */
#define dim(x) (sizeof(x) / sizeof(x[0]))
unsigned intrinsic(char *); /* function prototypes */
void extrinsic(char *);
void get_cmd(char *);
void get_comspec(char *);
void break_handler(void);
void cls_cmd(void);
void dos_cmd(void);
void exit_cmd(void);
struct cmd_table { /* intrinsic commands table */
char *cmd_name;
int (*cmd_fxn)();
} commands[] =
{ "CLS", cls_cmd,
"DOS", dos_cmd,
"EXIT", exit_cmd, };
static char com_spec[64]; /* COMMAND.COM filespec */
main(int argc, char *argv[])
{
char inp_buf[80]; /* keyboard input buffer */
get_comspec(com_spec); /* get COMMAND.COM filespec */
/* register new handler
for Ctrl-C interrupts */
if(signal(SIGINT, break_handler) == (int(*)()) -1)
{
fputs("Can't capture Control-C Interrupt", stderr);
exit(1);
}
while(1) /* main interpreter loop */
{
get_cmd(inp_buf); /* get a command */
if (! intrinsic(inp_buf) ) /* if it's intrinsic,
run its subroutine */
extrinsic(inp_buf); /* else pass to COMMAND.COM */
}
}
/*
Try and match user's command with intrinsic command
table. If a match is found, run the associated routine
and return true, else return false.
*/
unsigned intrinsic(char *input_string)
{
int i, j; /* some scratch variables */
/* scan off leading blanks */
while(*input_string == '\x20') input_string++ ;
/* search command table */
for(i=0; i < dim(commands); i++)
{
j = strcmp(commands[i].cmd_name, input_string);
if(j == 0) /* if match, run routine */
{
(*commands[i].cmd_fxn)();
return(1); /* and return true */
}
}
return(0); /* no match, return false */
}
/*
Process an extrinsic command by passing it
to an EXEC'd copy of COMMAND.COM.
*/
void extrinsic(char *input_string)
{
int status;
status = system(input_string); /* call EXEC function */
if(status) /* if failed, display
error message */
fputs("\nEXEC of COMMAND.COM failed\n", stderr);
}
/*
Issue prompt, get user's command from standard input,
fold it to upper case.
*/
void get_cmd(char *buffer)
{
printf("\nsh: "); /* display prompt */
gets(buffer); /* get keyboard entry */
strupr(buffer); /* fold to upper case */
}
/*
Get the full path and file specification for COMMAND.COM
from the "COMSPEC=" variable in the environment.
*/
void get_comspec(char *buffer)
{
strcpy(buffer, getenv("COMSPEC"));
if(buffer[0] == NULL)
{
fputs("\nNo COMSPEC in environment\n", stderr);
exit(1);
}
}
/*
This Ctrl-C handler keeps SHELL from losing control.
It just re-issues the prompt and returns.
*/
void break_handler(void)
{
signal(SIGINT, break_handler); /* reset handler */
printf("\nsh: "); /* display prompt */
}
/*
These are the subroutines for the intrinsic commands.
*/
void cls_cmd(void) /* CLS command */
{
printf("\033[2J"); /* ANSI escape sequence */
} /* to clear screen */
void dos_cmd(void) /* DOS command */
{
int status;
/* run COMMAND.COM */
status = spawnlp(P_WAIT, com_spec, com_spec, NULL);
if (status)
fputs("\nEXEC of COMMAND.COM failed\n",stderr);
}
void exit_cmd(void) /* EXIT command */
{
exit(0); /* terminate SHELL */
}