shell.com Mcommand processor for ms.dos This is yet another command.com replacement. It implements unix-like shell commands (ls, mv, fgrep, rm, chdir, chmod) etc. 1. USAGE shell [-[vs]] {args . . .} -v causes the shell to echo all commands to the standard error stream. -s causes the shell to execute a file called 'shell.rc' in the current directory before giving control to the user. Other features include: 2. Command line expansion of ambiguous file names Ambiguous file names are expanded to file lists on the command line. This can be defeated by quoting. 3. History substitution - ala C-shell. History substitution is a powerful means to save retyping of long command lines.It allows you to do things like re-execute the last command, re-execute the last command but redirect output to a file, or execute a new command with arguments from previous command lines. The last 20 commands are saved, and can be reviewed by typing the 'history' command. Previous commands can be referred to by their number, or relative to the current command's number. Parameters from previous commands can be seperated out and used individually. History substitutions specifications come in two parts - the command number specifier and the argument specifier, seperated by a colon. The argument specifier is optional; if it is omitted, the entire command line is specified. ::= !! | !n | !-n !! = last command !n = nth command !-n = command n commands before current command number !# = the current command line ::= :[^$*] | :n | :n* | n = number of argument (0 being the command name) ^ = first argument (i.e. argv[1]) $ = last argument * = ^-$, or nothing if only one word on command line n* = arguments n through $ ::= This is not as complicatated as it may appear. Here is an example session. 0% ls *.c *.c foo.c bar.c 1% more foo.c /* edit the last argument of the last command */ 2% edit !!:$ /* go off and edit */ /* reference last argument of last command */ 3% fgrep foo !!:$ bar.c FOO.C : foo BAR.C : foo /* edit the second thru the last args of command 3 */ 4% edit !3:2* (go off and edit) /* repeat last command */ %5 !! (go off and edit) /* remove the 1st argument of the command 2 before the current one */ %6 rm !-6:^ History substitution here is a compatible subset of the [U|XE]NIX C shell history substitution facility. Cshell allows even weirder combinations. 4. Multiple commands on one command line Command lines are split at semicolons. example %0 ls -l *.c ; make shell.exe ; exit 5. Conditional command execution If two commands are seperated by '&&', then the second will be executed only if the first returns 0 as an exit code. If two commands are seperated by '||', then the second will be executed only the first command returns non-zero as an exit code. Example make shell.exe && chmod +w /bin/shell.exe && mv shell.exe /bin If the make operation fails, then the chmod and the mv will not be executed. make shell.exe || echo You blew it bub! If the make operation fails, then the echo operation will be executed. 6. Character escapes and argument quoting \; suppresses the command parser from seeing the semicolon as a command seperator. Quotes are handles thusly: 1. String surrounded by single quotes are stripped of the single quotes, and passed without wild-card expansion to the invoked program. 2. Strings surrounded by double quotes are passed complete with quotes to the calling program. This was done for a version of grep that I have that accepts regular expressions with embedded blanks within double quotes. 7. Shell Variables Shell variables are synonymous for our purposes with environment strings, i.e. they are defined with the 'set' command. Variables are referenced on the command line by prefacing a variable name by a dollar sign. Two dollar signs in a row signify a dollar sign character. EXAMPLE %0 set home = c:/ %1 echo $home C:/ %2 ls $home C:/*.* command.com %3 echo $path C:/bin And so on. 8. Startup Batch Files Any file names specified on the command line will be used as input before passing control to the console. This allows you to set up your desired environment strings, or whatever else you want to take place. If the specified shell script has an 'exit' command in it, the current shell will be terminated. I have been asked about adding more advanced batch file commands - i.e. goto, for, while, etc. I will not do this for two reasons 1) There is already a batch facility for PC-DOS which functions adequately (if not elegantly) and 2) There is a big leap in complexity when you go from sequential execution of commands to changing the flow of execution. You have to jump around in a file, looking for labels (if you just implement gotos), or even worse, you have to keep track of nesting levels for loop constructs. I would have to evaluate expressions for conditionals, etc etc etc. What I implemented here is a nice interactive system, and it would take a complete redesign to make it a full batch language. As it stands now, you can perform tests using the '||' and '&&' operators, which should suffice to keep your shell scripts from running totally amok. 9. Many builtin commands. Output of the 'commands' command a: alias b: c: cat cd chdir chmod cls commands copy cp d: del dir dump e: echo era erase error exit f: fgrep g: h: hd history i: j: ls md mkdir more mv no history popd pushd pwd rd rm rmdir set tee touch unalias version y There are many that are simply aliases, e.g. 'copy' and 'cp' invoke the same program. 10. commands description syntax terms used in syntax explanations : fname ::= PC-DOS ambiguous or unambiguous file or directory name. uname ::= unambiguous PC-DOS file or directory name string ::= any string of printable characters of arbitrary(<512) length. filelist ::= filename [filename .. filename] noargs ::= no arguments at all space ::= any white space characters {arg} ::= term is optional envstring ::= = | = 11. command syntax drive a: | b: | c: | d: | e: | f: | g: | h: | i: | j: changes default drive. If you don't have such a drive, nothing happens. alias alias name = cmdstring { name = cmdstring . . name = cmdstring } assigns cmdstring to name. name can now be used just as if it were a built-in or external command. cmdstring may contain history expressions or variable substitutions. cat cat {} copies specified files to standard output. If none are given, copies standard input to standard output cp cp | copy copies specified files to destination file or device. If more than one file is in the file list, must be a directory. cd cd | chdir makes the current default directory. chmod chmod {-|+[rwh]*} change file permissions for specified files +r, -r turn on or off read permission - i.e. hide the file. +w, -w turn on or off write permission. +h, -h turn on or off hidden attribute - converse of r +a, -a turn on or off archive attribute Note that '-r' or '+rwh' are both valid syntax for switches. Also new permission switches are permissable between file names with the following warning: I don't reset the masks between file names - if you have a second batch of attribute changes on the command line, the effect is additive. If you're not careful, you could make a mess of a files attributes. If you don't specify any attribute switches, file attributes will be set to 0, which means read,write,not hidden,not system, not modified since last backup. cls cls | clear clears the screen and homes the cursor. commands commands prints a table of available built-in commands. del del synonym for rm. dir dir synonym for ls. dump dump filespec [block [page]] | [segment:[offset]] [count] Where a block is 64K bytes and a page is 256 bytes Segment:offset are standard 8086 notation in hexadecimal Count is the number of bytes to dump in decimal This came from some anonymous public domain source, ported by me echo echo echos argument list to screen. era era synonym for rm. error error prints returned value of last command to the screen. exit exit terminates execution of the shell. fgrep fgrep looks for unambiguous pattern in . echos lines matching to the screen. hist hist | history prints history list to standard output. ls ls | dir {-[alqctrR]} Lists files that match -a all files, including system files are listed. '.' and '..' are suppressed, but you know they're there if you need them, don't you? -l prints out file times, permissions, etc -q suppresses header line from display - useful when you want to pipe stuff into another program. -c print as one column. -t sort by time, most recent last -R recurse through all encountered subdirectories. -r reverses sort order. md md | mkdir make a directory. Prints an error if it can't be done more more {-[0-9]*} {} List file to screen with pauses -n specify tab width when expanding tabs, where n is an integer. more acts like 'cat' when redirected - you can concatenate files in this manner. If no files are specifed, standard input is 'mored.' mv mv moves specified file or files to target specifed by . If there is more than one file in list, must be a directory popd popd returns to directory at top of directory stack. pushd pushd save current working directory on directory stack, and changes current working directory to . pwd pwd prints current working directory to standard output. rd rd | rmdir remove specified directory if possible. rm rm {-q} blows away all files in . If -q is specified, will ask if they should be removed. set set { { .. }} sets a string in the environment. If you specify 'name=' with no string after, it will remove it from the environment. If you don't specify a string, set prints out current environment. tee tee Copies standard input to standard output, depositing a copy in touch touch Makes the modification time of specified files the current date and time. unalias unalias aliasname remove alias name from the alias list. y y copies standard input to standard output, and then copies the specified files to standard output. Sort of the opposite of tee, in other words. 8. Helpful hints Use forward slashes in all path names. (See note below on switch characters) If you use DOS 3.0 or higher, this includes paths to transient programs. put single quotes around arguments with semicolons in them, so they don't turn into command delimiters. The set command affects only the local shell's environment. You can 'exit' to command.com and the original environment is intact. The local environment is 4K large - which is useful. 12. Implementation notes DOS doesn't acknowledge a 'change default drive' command until you issue a 'get current directory' call. Why? The only way I figured this out is by disassembling command.com. This was developed with AZTEC C by MANX. In it are a few hacked up pieces of AZTECS library source, which I hereby acknowledge. If MANX has a problem with me distributing them, they can call me direct - I figure I'm doing them a favor by disseminating this program as an example of the power and quality of their compiler and development tools. If you have the AZTEC compiler and MANX's version of make, you can recreate the shell from source, by using arc to unpack everything into a directory, editing the macros BINDIR and CLIB and then making shell.com. I wouldn't try it with any other compiler, because I make a lot of calls to AZTEC specific routines. You can write your own commands and add them by editing cmds.c, and putting the name of your subroutine and its associated command string into the builtin array. You can safely modify any of my builtins, as long as you don't assume that all of your static variables are going to stay initialized to startup values. The main module 'main.s' is source for a state machine compiler I wrote which is not in the public domain yet. I include main.c in the source distribution, so the compiler isn't necessary. I leave figuring out how main.s maps to main.c as an 'exercise for the reader.' Any of the other code (main.c, fexecvp.c fexecv.c) modify at your own peril. I break them every time I do it, and I wrote them!!! PC|MS-DOS has a limit of 20 file handles. If you add a command that opens files, make sure you catch the ctrl-break signal and close them. Look at CAT.C or Y.C for examples. 13. BUGS External DOS commands have trouble parsing the command line when invoked from shell. The command line gets garbled. I spent a lot of time trying to figure this problem out to no avail. They apparently get their command line arguments some way that is a mystery to me. The only solution is either to either run command.com, or 'exit' to the original command prompt. ***FIXED in current version. N.B. that PC-DOS command options must be specified with a dash rather than a forward slash. Programs compiled by AZTEC C that don't set up their own signal handlers seem to be 'unbreakable' - you can't ctrl-break out of them, as though SIGINT is set to SIG_IGN before entry. You might not want to invoke such a program if it lasts hours and you want to be able to break out of it. ***FIXED in current version. Thanks to AZTEC Tech Support. Due to the way that environment strings are expanded on the command line, semicolons inside shell variables look like command seperators. 14. HISTORY V 1.0 - Initial release Functional, but somewhat buggy. Lacked full history substitution. V 1.1 Added history substitution. Fixed some bugs. This has been floating around for a while. V 1.2 Fixed bugs. Added 'free space' display to ls -l. Minimized weird behavior of cp and mv. Did you know that PC-DOS doesn't think the root directory is a directory if you ask it? Caused much pain. V 1.3 chmod wasn't returning explicite value, which screwed up using it in '||' and '&&' pipelines. Acquired PC-Lint from Gimpel software and linted the whole shooting match. No real bugs found, but everything now agrees with everything, as far as is humanly possible. Changed the 'ls' command so that it wasn't such a memory hog when recursing through directories. mv and cp were modified to print error messages and return non-zero when they had trouble. Before they didn't complain about non-existent files or directorys int some cases - they just didn't do anything. The forward slash is now consistently a forward slash throughout. I did this by calling the infamous DOS 37H and changing Dos's switch character to '-.' This means that all paths are specified with forward slashes, including program invocations. As an aside, [PC|MS]-DOS doesn't really care which direction the slash runs when you specify a path. The ambiguity lies in the various external DOS programs and COMMAND.COM, because forward slashes are used to specify command line switches. If you change the switch character to something besides forward slash, '-' for example, then the ambiguity disappears. So now you can do cute things like 0% /bin/command -c dir -w from the shell. This is what happens when people write an operating system by trying to emulate [U|XE]NIX and CP/M80 and satisfy a bunch of pinstripes in Boca Raton at the same time. I can just see the high muckety-mucks from IBM sitting around debating switch characters - "I don't like dashes - make them forward slashes." V 1.4 Added shell variables, and ability to run scripts on startup. Made 'set' command a little more flexible in syntax. Changed handling of blanks inside quoted strings. I noticed that when I fixed the switch character, the DOS external commands seem to be able to digest command lines better. You can even use the DOS print spooler now. You should watch out about invoking 'terminate and stay resident' commands under the shell, and then leaving the shell. It causes no immediate problems, but since DOS allocates program memory from bottom to top, you will end up with a 64K hole where shell.com used to be, that is gone forever until the next reboot. V 1.5 Added options to ls. Cleaned up directory handling (chdir,rmdir,mkdir). Echo history expansions in interactive shells. V 1.6 ADDED ALIASES!!! Fixed some subtle bugs in mv/cp common code. Made the command line arguments shell variables. NOTE - aliases are implemented in a fairly simple-mided fashion, so I'm not guaranteeing they won't blow up if subjected to wanton abuse. 15. DONATIONS REQUESTED The shell seems to have generated an incredible amount of interest, all around the country - someone apparently put this code out over the usenet and arpanet, in addition to its original FIDOnet roots. I would like to keep working on it, but for it to progress further, it can no longer be a 'lunch hour hack.' I am therefore requesting that those who feel that this is a useful piece of software send me a donation to further my efforts towards purchasing a machine for home use. Anyone sending more that 25$ will recieve: 1. a PC-DOS disk with most recent release full source and executable image. 2. Source and executable for various other public domain *nix programs (tail, nroff, cut, paste, diff, grep) If you use this every day, or if you are giving everyone in your place of employment a copy, value it accordingly. This does not constitute a licensing agreement of any sort, nor is any warranty implied. Once you have a copy of this program or its source, it's yours to hack to pieces, use, revere, revile, or whatever. I like the anarchic aspects of the various public domain networks. I just would like to be able to see some reward for the work I've done. QUESTIONS COMMENTS GOTO KENT WILLIAMS NORAND INC. 550 2nd ST. S.E. Cedar Rapids Iowa 52401 (319) 338-6053 (HOME VOICE)