/* * COMMAND.C - command-line interface. * * Version: 0.50 * * Comments: * * 06/17/94 (Tim Norman) --------------------------------------------------- * started. * * 08/08/95 (Matt Rains) --------------------------------------------------- * i have cleaned up the source code. changes now bring this source into * guidelines for recommended programming practice. * * i have added the the standard FreeDOS GNU licence test to the * initialize() function. * * i have started to replease puts() with printf(). this will help * standardize output. please follow my lead. * * i have added some constants to help making changes easier. * * 12/15/95 (Tim Norman) --------------------------------------------------- * major rewrite of the code to make it more efficient and add * redirection support (finally!) * * 1/6/96 (Tim Norman) ----------------------------------------------------- * finished adding redirection support!!! Changed to use our own exec * code (MUCH thanks to Svante Frey!!) * * 1/29/96 (Tim Norman) ---------------------------------------------------- * added support for CHDIR, RMDIR, MKDIR, and ERASE, as per the suggestion * of Steffan Kaiser * * changed the "file not found" error message to "bad command or filename" * thanks to Dustin Norman for noticing that confusing message! * * changed the format to call internal commands (again!) so that if they * want to split their commands, they can do it themselves (none of the * internal functions so far need that much power, anyway) * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "command.h" /* define some error messages */ #define SYNTAXERR "ERROR: syntax error" #define NOENVERR "ERROR: no environment" #define INVALIDDRIVE "ERROR: invalid drive" #define INVALIDFUNCTION "ERROR: invalid function" #define BADCOMMAND "ERROR: bad command or filename" #define FILENOTFOUND "ERROR: file not found" #define ACCESSDENIED "ERROR: access denied" #define NOTENOUGHMEMORY "ERROR: not enough memory" #define BADENVIROMENT "ERROR: bad enviroment" #define BADFORMAT "ERROR: bad format" #define ERROR_E2BIG "ERROR: Argument list too long" #define ERROR_EINVAL "ERROR: Invalid argument" /* define the internal commands */ #define CD "cd" #define CHDIR "chdir" #define DEL "del" #define DIR "dir" #define DOSKEY "doskey" #define ERASE "erase" #define EXIT "exit" #define LH "lh" #define LOADFIX "loadfix" #define LOADHIGH "loadhigh" #define MD "md" #define MKDIR "mkdir" #define PATH "path" #define PROMPT "prompt" #define RD "rd" #define REM "rem" #define REN "ren" #define RMDIR "rmdir" #define SET "set" #define VER "ver" char exitflag = 0; /* indicates EXIT was typed */ char canexit = 1; /* indicates if this shell is exitable */ /* * fatal error handler. * * */ void fatal_error(char *s) { printf("fatal_error() : %s\n", s); exit(100); } /* * is character a delimeter when used on first word? * * */ char is_delim(char c) { return(c == '/' || c == '=' || c == 0 || isspace (c)); } /* * execute this as an external program * * first - first word on command line * rest - rest of command line * */ void execute (char *first, char *rest) { char *paths[129], fullname[128]; int args, r; /* check the command line for drive changes and commands run without */ /* spaces, like CD\DOS, etc */ /* check for a drive change */ if(first[0] && first[1] == ':' && first[2] == 0) { if(isalpha(first[0])) { setdisk(toupper(first[0]) - 'A'); } if(getdisk () != toupper(first[0]) - 'A') { printf("%s\n", INVALIDDRIVE); } return; } /* this scary piece of code simply checks for CD or CHDIR without a */ /* space after it. E.g. CD\DOS */ else if ((memicmp (first, CD, sizeof (CD)-1) == 0 && (first[sizeof (CD)-1] == '\\' || first[sizeof (CD)-1] == '.')) || (memicmp (first, CHDIR, sizeof (CHDIR)-1) == 0 && (first[sizeof (CHDIR)-1] == '\\' || first[sizeof (CHDIR)-1] == '.'))) { cd(first, rest); return; } /* checks for MD or MKDIR w/o a space */ else if ((memicmp (first, MD, sizeof(MD)-1) == 0 && (first[sizeof (MD)-1] == '\\' || first[sizeof (MD)-1] == '.')) || (memicmp (first, MKDIR, sizeof (MKDIR)-1) == 0 && (first[sizeof (MKDIR)-1] == '\\' || first[sizeof (MKDIR)-1] == '.'))) { md(first, rest); return; } /* checks for RD or RMDIR w/o a space */ else if ((memicmp (first, RD, sizeof (RD)-1) == 0 && (first[sizeof (RD)-1] == '\\' || first[sizeof (RD)-1] == '.')) || (memicmp (first, RMDIR, sizeof (RMDIR)-1) == 0 && (first[sizeof (RMDIR)-1] == '\\' || first[sizeof (RMDIR)-1] == '.'))) { rd(first, rest); return; } /* get the PATH environment variable and parse it */ get_paths(paths); /* search the PATH environment variable for the binary */ if(!find_which(paths, first, fullname)) { fprintf(stderr, "%s\n", BADCOMMAND); return; } /* check if this is a .BAT file */ if(!stricmp(strrchr(fullname,'.') + 1, "bat")) { batch(fullname, rest); } /* else exec the program */ else if((r = exec(fullname, rest, EnvSeg)) != 0) { switch(r) { case 1 : { printf("%s\n", INVALIDFUNCTION); break; } case 2 : { printf("%s\n", FILENOTFOUND); break; } case 5 : { printf("%s\n", ACCESSDENIED); break; } case 8 : { printf("%s\n", NOTENOUGHMEMORY); break; } case 10 : { printf("%s\n", BADENVIROMENT); break; } case 11 : { printf("%s\n", BADFORMAT); break; } default : { printf("ERROR: unknown error %d.\n", errno); break; } } } } /* a list of all the internal commands, associating their command names */ /* to the functions to process them */ static struct CMD { char *name; int (*func)(char *, char *); } cmds[] = { { DIR, dir }, { CD, cd }, { CHDIR, cd }, { RD, rd }, { RMDIR, rd }, { MD, md }, { MKDIR, md }, { DEL, del }, { ERASE, del }, { REN, ren }, { REM, rem }, { DOSKEY, doskey }, { EXIT, internal_exit }, { VER, ver }, { SET, set }, { PROMPT, prompt }, { PATH, path }, { LH, loadhigh }, { LOADHIGH, loadhigh }, { LOADFIX, loadfix }, { NULL, NULL } }; /* * look through the internal commands and determine whether or not this * command is one of them. If it is, call the command. If not, call * execute to run it as an external program. * * line - the command line of the program to run * */ void command (char *line) { char com[128]; /* the first word in the command */ int count, /* counter */ start; /* index to start of first word on command line */ int executed = 0; /* whether the command was executed */ char *rest; /* pointer to the rest of the command line */ /* skip over the initial whitespace on the command line */ start = 0; while (isspace (line[start])) start++; /* count to the end of the first word */ for (count = start; !is_delim (line[count]); count++) ; /* store the rest of the command line in rest */ rest = &line[count]; while (isspace (*rest)) rest++; /* copy the first word into com */ memcpy (com, &line[start], count - start); com[count - start] = 0; /* check for empty command line */ if (!com[0]) return; /* look through the command table and call the appropriate function */ for (count = 0; cmds[count].name; count++) if (strcmpi (com, cmds[count].name) == 0) { cmds[count].func (com, rest); executed = 1; break; } /* if none of those work, try calling it as an external program */ if (!executed) execute (com, rest); } /* * process the command line and execute the appropriate functions * full input/output redirection and piping are supported * */ void parsecommandline (char *s) { char in[128] = "", out[128] = "", *pipes[128]; int num, count; int oldinfd, oldoutfd, prevfd = -1, infd, outfd; char tempdir[128], fname[2][128] = {"", ""}, *t; int curfname = 0; /* find the temp directory to store temporary files */ t = getenv ("TEMP"); if (t) strcpy (tempdir, t); else strcpy (tempdir, "."); if (tempdir[strlen (tempdir) - 1] != '\\') strcat (tempdir, "\\"); /* get the redirections from the command line */ get_redirection (s, in, out, pipes, &num); /* inefficient, but oh well for now */ while (isspace (in[0])) memmove (in, &in[1], strlen (in)); while (isspace (out[0])) memmove (out, &out[1], strlen (out)); if (in[0]) { infd = open (in, O_TEXT | O_RDONLY, S_IREAD); if (infd == EOF) { printf ("Can't redirect from file %s\n", in); return; } } else infd = -1; if (out[0]) { outfd = open (out, O_CREAT | O_TEXT, S_IWRITE); if (outfd == EOF) { printf ("Can't redirect to file %s\n", out); close (infd); return; } } else outfd = -1; for (count = 0; count < num; count++) { if (count == 0) /* make backups of stdin and stdout */ { oldinfd = dup (0); oldoutfd = dup (1); } if (count == 0) /* first pipe gets input redirection */ { if (infd != -1) { close (0); dup2 (infd, 0); close (infd); } } else /* input from last pipe's output */ { close (prevfd); prevfd = open (fname[1 - curfname], O_TEXT | O_RDONLY, S_IREAD); if (prevfd == EOF) { close (0); dup2 (oldinfd, 0); close (oldinfd); close (1); dup2 (oldoutfd, 1); close (oldoutfd); /* this might leave some temporary files around... oh well */ fprintf (stderr, "Error! Cannot pipe! Cannot open temporary file!\n"); close (outfd); return; } close (0); dup2 (prevfd, 0); close (prevfd); if (fname[curfname][0]) { unlink (fname[curfname]); } } if (count == num - 1) /* last pipe gets output redirection */ { if (outfd != -1) { close (1); dup2 (outfd, 1); close (outfd); } else { close (1); dup2 (oldoutfd, 1); close (oldoutfd); } } else { strcpy (fname[curfname], tempdir); prevfd = creattemp (fname[curfname], 0); if (prevfd == EOF) { close (0); dup2 (oldinfd, 0); close (oldinfd); close (1); dup2 (oldoutfd, 1); close (oldoutfd); /* might leave some temp files around */ fprintf (stderr, "Error! Cannot pipe! Cannot create temporary file!\n"); close (infd); close (outfd); return; } close (1); dup2 (prevfd, 1); /* closing prevfd here causes things to not work for some reason */ curfname = 1 - curfname; /* switch to other fname for next time */ } /* process this command */ command (pipes[count]); } if (prevfd != -1) close (prevfd); if (fname[1 - curfname][0]) { unlink (fname[1 - curfname]); } if (in[0] || num > 1) { close (0); dup2 (oldinfd, 0); close (oldinfd); } else close (oldinfd); if (out[0]) { close (1); dup2 (oldoutfd, 1); close (oldoutfd); } else close (oldoutfd); } /* * do the prompt/input/process loop * * */ int process_input(void) { char commandline[1024]; do { printprompt(); readcommand(commandline, 128); parsecommandline(commandline); } while(!canexit || !exitflag); return 0; } /* * control-break handler. * * */ int c_brk(void) { return 1; /* continue execution */ } /* * set up global initializations and process parameters * * argc - number of parameters to command.com * argv - command-line parameters * */ #pragma argsused void initialize(int argc, char *argv[]) { ctrlbrk(c_brk); ver("ver", ""); /* set up environment space and such */ if(!EnvSeg) /* fix this to later make its own environment */ { printf("%s\n", NOENVERR); exit(1); } /* figure out if we're a permanent shell... and make it do this */ /* OwnerPSP = _psp; */ return; } /* * main function * * */ int main(int argc, char *argv[]) { /* check switches on command-line */ initialize(argc, argv); return(process_input()); /* call prompt routine */ }