Sh: A simple Command Shell for QDOS and SMS ------------------------------------------- v1.08 by Adrian.D.Ives 04/04/1998 (Note: Original Shell project by P.J.Taylor 08/11/1991) Introduction ------------ The Shell is a simple command processor for QDOS. There are many who will groan in horror at such a prospect, because it can be said that any concentration on command line interfaces is a throwback to the old days ... and a painful reminder of MS-DOS and CP/M. Well, yes and no. It doesn't matter how good graphical interfaces become (and the Pointer Environment for QDOS/SMS is actually very good indeed considering its small size relative to the "Lumbering Behemoth and Eater of all RAM" that runs on The Unmentionable) there are times when a simple command line interface will be simpler and more appropriate. And, anyway, I like having a good command interpreter, and I like being able to load and execute programs just by typing their name. If you are developing in a language like C, a command interpreter is very handy for quickly issuing instructions to compile, link, and assemble source files. Anyway, enough of the sales pitch, if you don't like command line interfaces, it's a simple job to load up QPAC 2, select all of the files relating to the Shell, and delete them from your hard disk. I found the original shell program on the TF Services BBS, uploaded way back in 1991, and compiled with v1.05 of C68! I had been looking for a Shell ever since I saw one referenced in somebody's Make file, so I downloaded it and started to take a look. Peter Taylor had done a really excellent job of creating all of the complex Job loading and piping code, so it was really a breeze to start adding in the features that I was after. (Sorry, Peter, if you're reading this, I couldn't resist reformatting the source to my own style - but I didn't change the logic of the core job execution stuff much). To support the execution of Things and the link to FileInfo II, I pinched some code that Jonathan Hudson had written for Qascade (another program that is constantly running on my desktop). There is little point in reinventing the wheel if somebody has already been there before you! Instead, I concentrated on adding in some additional internal commands, adding support for environment variables, internal variables, and parameterised batch files. The end result is the program supplied in this archive. There is much that could be done to improve this program, in particular it shows its C68 v1.05 heritage - the latest version of the compiler libraries supplied with C68 could replace several of the key routines that are included in the Shell. In keeping with the spirit of the original author, who included a statement that the files could be freely distributed, I have included all of the source code so that people can develop the Shell to meet their own specific needs. I place no restrictions on the distribution of these files, other than that Peter Taylor, Jonathan Hudson (some of whose code I've used) and myself are given credit for the work that we have done. I will continue to develop the Shell, and all of my utility programs have been modified to support "neater" execution from it. If anyone has any suggestions for future enhancements, I would like to hear from them. So, if you're still awake, to answer the question "What is the Shell"? ... The Shell is an interactive command processor to support the control and execution of Jobs under QDOS and SMSQ. Its primary purpose is to deliver a quick interface to the Operating System job control facilities to help developers using compiled languages such as C. Its secondary purpose is to deliver a simple scripting facility for automating sequences of Jobs. Running the Shell ----------------- The Shell is executed like any other QDOS/SMSQ program, and if you use the Toolkit II EX/EW commands to start it you have the advantage that you can pass channels and/or a command string. EW 'Sh',#1 Will take input from S*BASIC Channel #1 and also send its output there. EW 'Sh',#0,#1 Will take input from S*BASIC Channel #0 and echo completed command lines to S*BASIC Channel #1, together with the output. Similarly ... EW 'Sh',#0,#2,#1 Will take Standard Input from #0, and send Standard Output to #1 and Standard Error to #2 (note the order '#0,#2,#1' this follows TK II and C68 conventions). A parameter string may also be supplied to re-direct standard input and standard output, as usual for all C68 compiled programs, using the '>' and '<' symbols. When Standard Input and Standard Output are both directed to the same console device each command line is prompted for using a '>'. There are two main types of commands that can be given to the Shell - 'External' and 'Internal'. The command itself is always case-independant, but parameters are passed to external programs with no character translation. Forcing execution of a command ------------------------------ If the command line passed to the Shell begins with an @ sign, the remainder of the line is assumed to be a command for the Shell to execute immediately. On completion of executing such a command, the Shell will then exit. EW 'Sh',#1;'@ls' This (pointless) example will load the Shell, whch will then load the external LS command, sending the output to channel #1 (exactly the same as EX 'ls',#1 but it does show the principle). This facility is somewhat limited. In particular any redirection symbols that you include are taken as applying to the Shell, and not to the command. This is because of the way in which the C68 run-time system processes the command line for maximum compatibility with UNIX systems. Please bear this in mind if using the '@' option. Because of this use of the '@' sign you should regard this symbol as a reserved character, and avoid using it in parameters passed to batch files - this could cause the Shell some confusion. Special Symbols --------------- The Shell recognises a number of special symbols, the values of which are substituted BEFORE any command line is processed. All special symbols begin with a dollar sign ($). To use a single $ sign anywhere in a command, use two of them ($$) instead. Parameter Values ---------------- $. The number of parameters that were passed to the batch file. $A .. $Z The values of the first 26 parameters that are supplied to a batch file. e.g: EX 'Sh';'' provided the executed program handles the redirection). The executed job is 'owned' by the Shell. If the job completes with a non-zero return code a line of the form ... Return code: nnn ... is written to the Shell's standard output, where 'nnn' is the return code as a signed decimal number. The equivalent of EX 'make';'sh' is: make sh & In this case no Standard Input/Output channels are passed to the executed Job and the executed Job is 'owned' by Job 0 - i.e. is completely independant of the Shell. As soon as the Job is started a line of the form ... Job nnn:mmm is written to the Shell's Standard Output, where 'nnn' is the Job number and 'mmm' the Job tag. Multiple external commands -------------------------- Several commands can be supplied on a single line: make sh ; make echo is equivalent to the two lines ... EW 'make';'sh' EW 'make';'echo' similarly ... make sh & make echo is equivalent to: EX 'make';'sh' EW 'make';'echo' and ... make sh & make echo & is equivalent to ... EX 'make';'sh' EX 'make';'echo' Piping the output of one job to the input of the next is done in MS-DOS and UNIX fashion with ... make sh | echo sh_res This causes 'make' and 'echo' to be executed with 'make' owned by 'echo' and 'echo' owned by the Shell. The Standard Input of the Shell is passed to 'make', the Standard Output of make is piped to the Standard Input of 'echo' and the Standard Output of the Shell is passed to 'echo'. this is roughly equivalent to TK II ... EX 'make';'sh' TO 'echo';'sh_res' The pipeline may involve more than two jobs: e.g.:- make sh | echo sh_res | echo ser1h Each Standard Output is piped to the Standard Input of the Job on its right and is owned by the job on its right; with the Shell owning the last Job in the chain. The Shell's Standard Input is passed to the leftmost Job and Standard Output to the rightmost Job. To pipe Standard Output AND Standard Error use a command of the form ... make sh |& echo sh_res This does not have a TK II equivalent. '|' and '&' may be combined ... make sh | echo sh_res & Will execute 'make' and 'echo as background Jobs with 'make' owned by 'echo' and 'echo' owned by Job 0. In this instance 'make' is given no Standard Input and 'echo' no Standard Output. A more complex combination of '&' and '|' is ... make sh | echo sh_res & make echo | echo echo_res This is treated the same as the two command lines ... make sh | echo sh_res & make echo | echo echo_res Internal commands ----------------- These are commands that are handled by the Shell itself. They always appear one to a line. If internal commands are combined with each other or with other external commands they are treated as external commands of the same name. Also any internal command prefixed with a '!' is treated as an external command of the same name (without the '!'). EXIT ---- Exits from the Shell. QUIT is accepted as a synonym. Syntax: EXIT {n} QUIT {n} EXIT alone exits the Shell with a zero return code, while EXIT n exits the Shell with a return code of n - a signed decimal integer, WAIT ---- Pause. Syntax: WAIT {n} WAIT alone waits for 5 seconds, while WAIT n waits for n seconds. n must be a positive decimal integer. KILL ---- Remove a Job. Syntax: KILL Num Tag Where Num and Tag are the Number and Tag respectively of the Job that you wish to remove. This is directly equivalent to the Toolkit II command RJOB Num, Tag. IF .. ELSE .. ENDIF ------------------- Conditionally perform instructions according to the result of a test. Syntax: IF Left Op Right .. ELSE .. ENDIF There must always be three parameters to an IF command: Left, Op, and Right. The operator determines the type of comparison that is made, and the result is used to either execute commands in the IF part of the construct, or in the ELSE part. An ELSE clause is optional. Internal variables and environment variables are compared by using the default string substitution mechanism described earlier in this document. At present spaces may NOT be included in either Left or Right. The valid operators are as follows:- == Strings are equal != Strings are not equal in The string Left is present somewhere in Right begins The string Left begins with the string Right ends The string Left ends with the string Right matches The string Left matches the wildcard pattern specified by the string Right = Numbers are equal <> Numbers are not equal > The number Left is greater than the number Right < The number Left is less than the number Right >= Left is greater than or equal to Right <= Left is less than or equal to Right Note that Left and Right MUST represent valid numbers (integer or floating point) in order for the tests relating to numbers to work correctly. Example:- FTEST Flag_txt IF $(%result) = 1 PRINT Flag file found ELSE COPY CON Flag_txt ENDIF Note that, in this example, COPY refers to an external command that performs this function, COPY is not a command that is intrinsic to the Shell. You cannot nest IF tests. The Shell is not a programming language, nor does it seek to duplicate or replace the excellent S*BASIC or REXX languages which are far more suitable for complex Job control activities. ECHO ---- Enable/disable the echoing of commands when running batch files. Syntax: ECHO ON | OFF By default, echo is disabled when running batch files. This prevents each command from being displayed on Standard Output before it is executed, and is usually neater. However, for debugging purposes, it can be useful to show the commands. PRINT ----- Display a string on stdout. Syntax: PRINT AnyText This command allows you to send messages to the Shell's Standard Output. It is of most use for reporting progress in batch files. Because the substitution of special symbols takes place before a command is executed, the values of environment variables may be displayed very easily ... PRINT $(SHELL) VER --- Display the Shell version number. Syntax: VER Displays version number of the Shell on Standard Output. The version number can also be accessed with the %ver internal variable, and as %v in the prompt string. PROMPT ------ Set the prompt characters. Syntax: PROMPT String By default, the Shell displays a prompt of ">" whenever it is expecting input from the console. (Although this default can be changed by setting the environment variable SHELL_PROMPT to a string that conforms to the same format set out below). Any string can be used here, and spaces will be significant. You can also use the special symbols %d and %p - which will be replaced by the current Data Directory and the current Program Directory, respectively. Use %% to get a single percent sign into the prompt. Other symbols are listed in the table below. Special symbols:- %d Current Data Directory %p Current Program Directory %D Current date in the form: Day Month Date Year %T Current time in the form: HH:MM:SS %v Current Shell version number %[Name] Current value of the named environment variable %[%Name] Current value of the named Shell Internal Note that %[Name] and %[%Name] are there because any attempt to use $(Name) and $(%Name) as part of the argument to the PROMPT command would cause these values to be substituted immediately, rather than at the time the prompt is actually printed. Important: Case is significant - %d is not the same as %D. LET --- Set/Remove a local environment variable. Syntax: LET Var=Value Assign Value to Var LET Var= Assign an empty string to Var LET Var Delete Var from the environment This command is used to assign values to the Shell's environment variables. Because of the way in which C68 programs use their own UNIX-like private environment, the LET command does not affect the master environment as seen by S*BASIC (use the Set external command to do this). However, by default the environment will be passed down to any C68 programs that the Shell executes (see the OPT command for more details). The external command LENV can be used to display the contents of the environment. FOR --- Execute a command for every file that matches. Syntax: FOR WildCard {,WildCard} Command For every file whose name matches the wildcard specification supplied, the remainder of the line is executed as if it were an ordinary command being submitted to the Shell. The special symbol $*, if used in the command, will be translated to the name of the current file. You can specify multiple wildcard specifications in a single command by separating each one with a comma. In this case, the command will process each specification in turn. Example:- FOR *_txt FOLD <$* >$*_new For every _txt file in the current directory, call the external FOLD command with that file as stdin, and stdout sent to a file with the same name, but _new appended. There is also another special variable whose value only has meaning when used in a FOR command. The symbol $^ returns the current filename minus the extension (i.e. all characters starting from the rightmost underscore are stripped off). The following example shows how this facility can be used to change file extensions:- FOR *_txt FOLD <$* >$^_new The difference between this example, and the first is that the first method would create a file called Foo_txt_new, whereas the second produces Foo_new. The Shell does not perform any substitution of special symbols ($A etc.) on a FOR command, until the line is actually executed - so the substitution occurs once for each matching file. Note that the string functions listed in the section on Special Symbols are most useful here. For example ... FOR *_h PRINT $(%part 4:$*) ... If, say, you were in a directory WIN2_C_SH_, this would display a list of just the names of each _h file - directory, and no extension. And finally, you can specify multiple sets of files with a single command ... FOR *_c,*_h cp *$ FLP1_ ... copies all C source files to FLP1. DO -- Execute a command whilst iterating a loop variable. Syntax: DO Value | From:To {,Value | From:To ...} Command This very simple looping construct will execute the supplied command for every value in the From and To range. If both From and To are numbers, an integer sequence is processed. If either From or To are characters, the loop is conducted for a single character that takes on each value in the range. There can be no spaces between the From and To - which means that a space cannot be used as part of a character loop. Also, the comma is used to separate multiple DO ranges or values. If From is less than To, the loop counts down instead of up. The current value of the loop counter is accessed by the reserved symbol "$+". Example:- DO 1:10 PRINT $+ Print the numbers from 1 to 10. DO a:f RM file$+_txt If you have the external RM command, this will delete the files filea_txt, fileb_txt .. filef_txt. Multiple ranges or values can also be used:- DO 1,3,5,7 DO a,s,d,f DO 1,6:12,15,20:25 Note that you cannot have any spaces anywhere in the list of DO ranges as this will be interpreted as the start of the command that you wish to DO. OPT --- Set Shell options. Syntax: OPT {Option} This command is provided for easy access to the special environment variable SHELL_OPTS. This variable holds a string of switches which normally take the form +X - where X is an upper or lower case character. Case is significant. Note that you can preset the Shell Options by setting the value of SHELL_OPTS before the Shell is started. OPT followed by any string checks the SHELL_OPTS variable to see if that string is already present. If so, it removes it, otherwise it appends it to the end of the variable. Examples:- Before Command After ------ ------- ----- OPT +d +d +d OPT +d +d OPT +e +d+e Passing no options will display the current option string. This can also be done by PRINTing $(SHELL_OPTS). The following options are recognised:- +d Force automatic update of directories. (Whenever you change a directory in the Shell, it is also changed in QDOS/SMSQ). See the DIR command. +e Force the environment to be passed to an external command. -e Prevent the environment from being passed to an external command. +u Force directory names for CD and MD into upper case +l Force directory names for CD and MD into lower case The +e and -e switches deserve some explanation. C68 programs support a mechanism for passing the environment between parent and child. Prior to v2.01 of C68 the only transfer supported was for the three directory strings. The Shell tries to determine the nature of any program file that it loads as an external command. If it thinks it has been created with a 'new' version of C68, it will pass a long pointer to the environment on the job's stack - otherwise it will pass the three directories. Setting +e forces the environment to be passed regardless of the program file, whereas -e prevents the Shell from ever passing the environment. Note that the Shell uses one method or the other: environment OR directories. Not both and not neither. The -e option, if present, will override +e if that is present as well. +e might be useful if the Shell fails to recognise a 'new' C68 program, while -e is useful to stop a child process from inheriting the Shell's environment. Note that the command "OPT -e" does not remove a +e setting, as this is not how the OPT command operates. To remove +e issue another OPT +e (confused yet? Well I've tried ...) CD -- Change the Data Directory. Syntax: CD Directory Changes the current Data Directory to that specified. If the directory does NOT begin with a valid device name, the new directory is appended to the current one, otherwise it replaces it in its entirety. Thus, if the current Data Directory was WIN1_DOCS_, and you issued the command CD TECH, the new directory would become WIN1_DOCS_TECH_, but issuing the command CD RAM1_Scratch would change it to RAM1_Scratch_. CD also recognises some (primarily) MS-DOS and UNIX style special characters. The backslash symbol (\) means go to the root of the device that is currently the Data Default, while double dot (..) is translated into "move to the parent directory". Thus ... Current Command New ------- ------- --- WIN1_HI_THERE_ CD .. WIN1_HI_ FLP1_BAD_SECTORS_ CD \ FLP1_ RAM1_PROCS_ CD \TXT RAM1_TXT_ WIN1_DOCS_TECH_ CD ..\FICTION WIN1_DOCS_FICTION_ For convenience, you may omit the space between CD and its parameter, IF the parameter begins with \ or .. - this is compatible with the Command Interpreter in MS-DOS. The Shell maintains its own "local" directories which it inherits from S*BASIC when it is initially started. Consequently any changes to the directories within the Shell are not reflected back in S*BASIC. Programs compiled using C68 have a mechanism for passing directories from a parent to a child - although this method changed from version 2.01 onwards to enable a pointer to the environment variables to be passed instead. The Shell supports some additional syntax that will force the S*BASIC directory to match the setting in the Shell, and this is done by prefixing the directory name with an '@' sign. e.g:- CD @TEXT ... will act as documented in the table above, but will also set the S*BASIC Data Directory to the new string. For convenience, you may omit the space between the CD and the '@'. CD@ alone, will copy the Shell's Data Directory setting back to S*BASIC. If you like your directory names to be in upper or lower case, you can force this automatically by setting the Shell Options +u and +l (see the OPT command for details) Another Shell Option that affects this command is +d (also documented under the OPT command). This facility lets you force the automatic synchronisation of the directories, and is very useful if you are using primarily non-C68 external commands. CPD --- Change the Program Directory. Syntax: CPD Directory This command behaves exactly like CD, except that it operates on the Program Directory. CDD --- Change the Destination Directory. Syntax: CDD Directory Although this command behaves in exactly the same way as the CD and CPD commands, it is unlikely that you will ever need to use it in this way. Normally CDD is used to set an output device for spooling and printing operations. However, it can specify a directory for use in copy operations. MD -- Make a new directory. Syntax: MD Directory This command is used to create a new directory on a Level 2 device. As with the CD command, MD also recognises some special symbols that enable you to specify where the directory will be created. If the directory does not begin with a valid device, the directory is created in the current Data Default, otherwise it is created on the device and location that you specify. You can use the \ and .. symbols to force creation of a directory off the root, or at the same level (i.e. below the parent of the current directory). Here are some examples:- MD Hello Create Hello below Data Default MD RAM1_ME Create directory ME on RAM1_ MD \NEW If the Data Default was currently WIN1_WORK_ this command creates a directory WIN1_NEW_ MD ..\Asm If the Data Default was currently WIN1_WORK_BAS_ this command creates a directory WIN1_WORK_Asm_ (but does not change the Data Default) As with CD, you can omit the space between the command and its parameter, if that parameter begins with \ or .. You can force new directory names to upper or lower case by using the Shell Options +u and +l respectively (see the OPT command). PAUSE ----- Pause the execution of a batch file and wait for the user to hit a key. Syntax: PAUSE {Message} This command can be useful to pause at the end of a series of commands to indicate that the operation has been completed. Optionally, a message can be printed. If the Shell has no CONsole or SCReen channels open for Standard Input or Standard Output, it will open a CON channel specifcally for this purpose, and close it afterwards. The ASCII code of the key that the user pressed is stored in the Shell internal variable %lastkey - this enables you to prompt for a user action and perform commands conditionally depending upon the key they hit. FTEST ----- Test for the existence of a file or directory. Syntax FTEST File | Directory Checks whether the specified file or directory actually exists, and sets the value of the internal variable %result accordingly (1= TRUE, 0=FALSE). %result may then be tested by a subsequent IF test. Screen-Related Commands ----------------------- The internal commands in this section are only valid if the Shell's Standard Output is a SCR or CON device. If the Standard Output is not SCR or CON these commands are treated as external commands of the same name. CLS --- Clear the Standard Output window. Syntax: CLS INK --- Set the ink colour of the Standard Output window. Syntax: INK Colour Directly equivalent to the S*BASIC command of the same name. The new ink setting takes effect from the next characters to be displayed. PAPER ----- Set both the paper and strip colours of the Standard Output Window. Syntax: PAPER Colour Directly equivalent to issuing the S*BASIC PAPER and STRIP commands. The new paper setting takes effect from the next characters to be displayed. BORDER ------ Set the border colour and width of the Standard Output window. Syntax: BORDER Width Colour This command can be used to set the width (in pixels) and colour of the Shell's main CONsole window. In fact it drops right through to the CON command, which is described below, to do all the work. You can just change the colour of the border by passing -1 for the width. Or, you can just change the width by leaving off the colour parameter. WINPOS ------ Set the position of the Standard Output window. Syntax: WINPOS XPos Ypos This command positions the top left corner of the Standard Output window at the indicated Xpos and Ypos pixel coordinates, leaving the width and height unchanged. To just change the X position, omit Y. To just change Y, pass -1 for X. This command drops through to CON (documented below) to do the work. WINSIZE ------- Set the size of the Standard Output window. Syntax: WINSIZE Width Height Redefines the width and height of the Standard Output window, the position of the top left corner is left unchanged. To just change the Width, omit the height. To just change the Height, pass -1 for the Width. This command drops through to CON (see below) to do the work. CON --- Set all CONsole parameters in one go. Syntax: CON Width Height Xpos YPos BWidth BColour Paper Ink XShad YShad Use this command to set size, position, colour etc. at the same time. Any trailing parameter may be ommitted, and the defaults will be used. To leave a particular parameter unchanged, pass the value -1. CON on its own will reset all of the window defaults to their startup value. Quite that this might do, if the Shell was passed another job's console window at startup is likely to be the source of some amusement. The command CON -m will enter interactive mode, allowing you to move and size the window using the cursor keys - very useful when you're trying to share screen space between applications. Note that, under the Pointer Environment, this command also resets the window's outline. Some Useful External Programs ----------------------------- To do anything really useful with the Shell you need some external programs. There are some very good programs supplied with the C68 compiler (for instance, ECHO which copies its command line to Standard Output - effectively functioning as a PRINT command for the Shell), and the GNU Text Utilities also work well with it. I also use a collection of very useful programs written by Richard Kettlewell, which come bundled with the EMACS disk from QUBBESoft P/D - these include COPY, DIR and REN. All of the latest versions of my QL Toolbox 98 range of Pointer Environment utilities recognise when they've been passed three channels and a command-line and work just as if they were ordinary commands. And, of course, the programs that make up the very good C68 suite can all be executed directly from the Shell. So that you can use this program straight away, I am recommend downloading Richard's utilities and putting them in the Program Directory, where they will then function just as if they were ordinary commands. I have also written some external utilities specifically to be used from the Shell. These are available as separate archives, complete with source code - so you can fiddle around with them to your heart's content. LENV ---- LENV is a very simple program to display the contents of the local environment (just like the S*BASIC ENV_LIST command, in fact). Again, this file should be placed in your program directory. A companion utility to this is SET, which operates on the master environment. LS -- LS gets its name from the UNIX command to display a list of files. It is very similar to Richard Kettlewell's DIR, except that it displays additional information such as the file type and dataspace (if the file is executable). LS also strips the directory off the filenames before displaying them, making its output a lot easier on the eyes. To find out what parameters LS will accept simply execute it with a -? parameter. Batch Files ----------- A batch file is a simple plain text file that contains a list of commands which the Shell will try and execute. Internal and external commands can be freely mixed, and IF tests allow very basic decision making. When you execute a batch file, either directly by, say, EX 'sh';'