Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!utgpu!water!watmath!clyde!cbosgd!ihnp4!inuxc!pur-ee!j.cc.purdue.edu!h.cc.purdue.edu!s.cc.purdue.edu!doc From: doc@s.cc.purdue.edu.UUCP Newsgroups: comp.sources.amiga Subject: Shell 2.06M sources (part 1 of 2) Message-ID: <251@s.cc.purdue.edu> Date: Mon, 8-Jun-87 13:36:12 EDT Article-I.D.: s.251 Posted: Mon Jun 8 13:36:12 1987 Date-Received: Thu, 11-Jun-87 04:17:39 EDT Reply-To: doc@s.cc.purdue.edu (Craig Norborg) Distribution: world Organization: Purdue University Computing Center Lines: 2086 Approved: doc@j.cc.purdue.edu Here is part 1 of 2 of Steve Drew's version of Matt Dillon's shell for Aztec C. This shell archive contains all the documentation as well as some of the sources. Binaries available over in comp.binaries.amiga. -Craig Norborg comp.sources.amiga moderator # This is a shell archive. # Remove everything above and includeing the cut line. # Then run the rest of the file through sh. #----cut here-----cut here-----cut here-----cut here----# #!/bin/sh # Xshar: Extended Shell Archiver. # This is part 1 out of 2. # Run the following text with /bin/sh to create: # globals.c # main.c # makefile # readme.206m # set.c # shell.doc # sort.c # sub.c # This archive created: Mon Jun 8 12:26:55 1987 # By: Craig Norborg (Purdue University Computing Center) cat << \SHAR_EOF > globals.c /* * GLOBALS.C * * (c)1986 Matthew Dillon 9 October 1986 * * version 2.06M (Manx Version and Additions) by Steve Drew 28-May-87 * * Most global variables. * */ #include "shell.h" struct HIST *H_head, *H_tail; /* HISTORY lists */ struct PERROR Perror[] = { /* error code->string */ 103, "insufficient free storage", 105, "task table full", 120, "argument line invalid or too long", 121, "file is not an object module", 122, "invalid resident library during load", 201, "no default directory", 202, "object in use", 203, "object already exists", 204, "directory not found", 205, "object not found", 206, "bad stream name", 207, "object too large", 209, "action not known", 210, "invalid stream component name", 211, "invalid object lock", 212, "object not of required type", 213, "disk not validated", 214, "disk write protected", 215, "rename across devices", 216, "directory not empty", 217, "too many levels", 218, "device not mounted", 219, "seek error", 220, "comment too long", 221, "disk full", 222, "file delete protected", 223, "file write protected", 224, "file read protected", 225, "not a DOS disk", 226, "no disk", 232, "no more entries in directory", /* custom error messages */ 500, "bad arguments", 501, "label not found", 502, "must be within source file", 503, "Syntax Error", 504, "redirection error", 505, "pipe error", 506, "too many arguments", 507, "destination not a directory", 508, "cannot mv a filesystem", 0, NULL }; char *av[MAXAV]; /* Internal argument list */ long Src_base[MAXSRC]; /* file pointers for source files */ long Src_pos[MAXSRC]; /* seek position storage for same */ char If_base[MAXIF]; /* If/Else stack for conditionals */ int H_len, H_tail_base; /* History associated stuff */ int H_stack; /* AddHistory disable stack */ int E_stack; /* Exception disable stack */ int Src_stack, If_stack; /* Stack Indexes */ int ac; /* Internal argc */ int debug; /* Debug mode */ int disable; /* Disable com. execution (conditionals) */ int Verbose; /* Verbose mode for source files */ int Lastresult; /* Last return code */ int Exec_abortline; /* flag to abort rest of line */ int Exec_ignoreresult; /* flag to ignore result */ int Quit; /* Quit flag */ long Cout, Cin; /* Current input and output file handles */ long Cout_append; /* append flag for Cout */ long Uniq; /* unique value */ char *Cin_name, *Cout_name; /* redirection input/output name or NULL */ char *Pipe1, *Pipe2; /* the two pipe temp. files */ struct Process *Myprocess; int S_histlen = 20; /* Max # history entries */ SHAR_EOF cat << \SHAR_EOF > main.c /* * MAIN.C * * Matthew Dillon, 24 Feb 1986 * (c)1986 Matthew Dillon 9 October 1986 * * version 2.06M (Manx Version and Additions) by Steve Drew 28-May-87 * */ #include "shell.h" int aux; /* for use with aux: driver */ char Inline[256]; #ifndef AZTEC_C char stdout_buff[STDBUF]; #endif main(argc, argv) register char *argv[]; { #ifdef AZTEC_C char *rawgets(); #endif char *prompt; register int i; extern int Enable_Abort; init_vars(); init(); seterr(); do_pwd(NULL); /* set $_cwd */ Enable_Abort = 0; for (i = 1; i < argc; ++i) { if (argv[i][0] == '-' && argv[i][1] == 'c') { Inline[0] = ' '; Inline[1] = '\000'; while (++i < argc) { strcat(Inline,argv[i]); strcat(Inline," "); } exec_command(Inline); main_exit(0); } if (argv[i][0] == '-' && argv[i][1] == 'a') { aux = 1; continue; } strcpy (Inline, "source "); strcat (Inline, argv[i]); av[1] = argv[i]; do_source (Inline); } for (;;) { if ((prompt = get_var (LEVEL_SET, V_PROMPT)) == NULL) prompt = "$ "; if (breakcheck()) { while (WaitForChar(Input(), 100L) || stdin->_bp < stdin->_bend) gets(Inline); } #ifdef AZTEC_C if (Quit || !rawgets(Inline, prompt)) #else printf("%s",prompt); if (Quit || !gets(Inline)) #endif main_exit(0); breakreset(); if (*Inline) exec_command(Inline); } } init_vars() { if (IsInteractive(Input())) set_var (LEVEL_SET, V_PROMPT, "$ "); else set_var (LEVEL_SET, V_PROMPT, ""); set_var (LEVEL_SET, V_HIST, "20"); set_var (LEVEL_SET, V_LASTERR, "0"); set_var (LEVEL_SET, V_PATH, "ram:,ram:c/,c:,df1:c/,df0:c/"); set_var (LEVEL_SET, "_insert", "1"); } init() { static char pipe1[32], pipe2[32]; stdin->_flags |= 0x80; /* make sure we're set as a tty */ stdout->_flags |= 0x80; /* incase of redirection in .login */ #ifdef AZTEC_C Close(_devtab[2].fd); _devtab[2].mode |= O_STDIO; _devtab[2].fd = _devtab[1].fd; /* set stderr to Output() otherwise */ /* don't work with aux driver */ #else stdout->_buff = stdout_buff; stdout->_buflen = STDBUF; #endif Myprocess = (struct Process *)FindTask(0L); Uniq = (long)Myprocess; Pipe1 = pipe1; Pipe2 = pipe2; sprintf (pipe1, "ram:pipe1_%ld", Uniq); sprintf (pipe2, "ram:pipe2_%ld", Uniq); } main_exit(n) { exit (n); } breakcheck() { return (int)(SetSignal(0L,0L) & SIGBREAKF_CTRL_C); } breakreset() { SetSignal(0L, SIGBREAKF_CTRL_C); } /* this routine causes manx to use this Chk_Abort() rather than it's own */ /* otherwise it resets our ^C when doing any I/O (even when Enable_Abort */ /* is zero). Since we want to check for our own ^C's */ Chk_Abort() { return(0); } _wb_parse() { } SHAR_EOF cat << \SHAR_EOF > makefile ###################################################################### # # Makefile to build Shell 2.06M # by Steve Drew 30-May-87 # ###################################################################### OBJS = run.o main.o comm1.o comm2.o execom.o set.o sub.o \ globals.o rawconsole.o sort.o INCL = shell.h Shell : $(OBJS) ln +q -m -o Shell $(OBJS) -lc rawconsole.o : rawconsole.c $(INCL) cc +IShell.syms rawconsole.c run.o : run.c $(INCL) cc +HShell.syms run.c main.o : main.c $(INCL) cc +IShell.syms main.c comm1.o : comm1.c $(INCL) cc +IShell.syms comm1.c comm2.o : comm2.c $(INCL) cc +IShell.syms comm2.c set.o : set.c $(INCL) cc +IShell.syms set.c sub.o : sub.c $(INCL) cc +IShell.syms sub.c globals.o : globals.c $(INCL) cc +IShell.syms globals.c execom.o : execom.c $(INCL) cc +IShell.syms execom.c SHAR_EOF cat << \SHAR_EOF > readme.206m VERSION RELEASES: (Manx Versions) ---------------- 2.06M 30-May-87 Steve Drew :Compiled with manx 3.4a, bugs fixes, :many new features. 2.05M 10-Jan-87 Steve Drew :Few bugs fixed, Matt's new 2.04 :features implemented. 2.04M 09-Dec-86 Steve Drew :Few bugs fixed, Commandline Editing :& Function key support added. :Changed my version to (M)anx. 2.02A 20-oct-86 Steve Drew :Implemented Matt's new features also :now 16 bit compilable. :(Same functionality as Matt's 2.03) 2.01A 27-sep-86 Steve Drew :Major conversion from Lattice > MANX :and added more commands/features. Please send all bug reports/comments to: Steve Drew at (Digital Equipment Corp) ENET: CGFSV1::DREW ARPA: drew%cfgsv1.dec.com@decwrl.dec.com USENET: {decvax|decwrl}!cgfsv1.dec.com!drew or 52 Castledale Cres N.E. Calgary, Alberta Canada Version 2.06M notes: (new features) ----------------------------------- - Minor changes for 3.4a of manx. Slightly smaller executable. (That of was before the new features). - Bug fixes: o New fexec code from 3.4a fixes many problems like some lattice programs causing task helds, when invoked under shell. o Limit of 1 line of type a head fixed. o Added check in mkdir to see if file already exists. o If levels too deep protection added.(prevents potential crash) - Better Pathing: For all external commands including any 'run' from shell we first search $_path variable then search amigados path. - Added -a startup switch, turns of command line editing code for use with AUX or conman handler. - Added setfiledate on copy, but maybe disabled via -d switch. - Also -u switch on copy: if dest file is same name and not older then don't copy. - copy command will now create the destination directory if it does not exist when specified as 'copy [-r] dir dir'. If you specify copy file file file dir, then 'dir' must already exist. - Added recursive wild card support via '.../' specifier eg. dir df0:.../*.c will go down all levels of df0: matching on any .c file. or echo .../* will expand to all files down all trees from our current level. - Exclude pattern matching support, in a limited way , only one exclude pattern per cmd line and for files not dirs. eg. dir !*.info will exclude all *.info files in current dir. dir df0:.../!*.info full directory tree of df0: but exclude any ugly .info files. dir !*.o !*.c (will result in ALL files matching since what doesnt match the !*.o will match the !*.c) - Directories always sorted. Sorted directories before were slow since files were examined twice once by expand() and then by do_dir. Now everything is done by expand() and do_dir() only formats the printed output. -e switch removed since expand does that now. eg. dir df0:.../ does a full (All) sorted directory - Passing an argument that contains a space to an external command is now implement the same way as internal commands, also any expansion of a file name that contains a space will be enclosed in quotes before being pass to an external command. the following will now work correctly: $ rm "foo bar" this worked before $ DELETE "foo bar" needed "\"foo bar"\" before. $ "my prog" would not work before $ myprog * would not work with spaced files before Version 2.05M notes: (new features) ----------------------------------- - Shell search path now used on 'run' command as well. - New -e, exclude files option on dir command. see shell.doc. - Command line editing new key: ^K - delete to end of line. - New variable _insert set to 0 makes default of no insert mode for commandline editing default is 1. (insert mode on). - New 'copy' command from Matt's 2.04 'cp' logs files and directorys as they are created and ^C detection. See doc for -r option. - Few bugs fixed. NEW FEATURES IN 2.04: (from Matt implemented in 2.05M) - RM command now has '-r' option. - \command forces the command parser NOT to look at aliases. Thus, you can alias something like 'cd' and have the alias contain a 'cd' which references the internal cd: alias cd "stuff...;\\cd $x" - _histnum variable .. current history # - expanded filenames are sorted. eg. Dir * will output sorted directory. Version 2.04M notes: (new features) ----------------------------------- - This version runs UNDER WORKBENCH 1.2 ONLY. - COMMAND LINE EDITING - Using Function keys. - New variable _width shows the number of collums in the window. and will change automatically if the user resizes the window. - option -c when starting will invoke the rest of command line and exit. (Thanks Dave.) Usefull to do stuff in the background. e.g. run shell -c copy c:foo ram:;echo done. - pwd gets run as part of cd so as to set _cwd to full path name. Version 2.02A notes: -------------------- - For new features of 2.03 see Matt's instruction.txt appended below. - All Matt's new feature for 2.03 converted to manx. And uses c.lib. - Redirection appears to work fine. Even on bcpl programs. Let me know if you find otherwise. - new varible _path for setting search path for external cmds. Shell does not use the 1.2 PATH feature but you may specify a search path by setting this symbol. e.g. $ set _path "ram:,c:,df0:c/,df1:c/" - Auto requesters are turned off during searching for cmds except when an explicit path is specified eg. df0:c/sort. - Command list is sorted so that help displays readable output. - A few bugs fixed - Changed all i/o routines that used MY.LIB written by Matt to use standard i/o or Amiga routines, since Manx is so efficeint with standard i/o routines compiling all those library functions did'nt gain anything as it does with Lattice. - Dir command rewritten to allow options: -s short mutil(4) collum display of files -d directorys only -f files only - Wildcarding now matches upper or lower case. - Command will no longer abort if one of the arguments which has wild card does not expand. - run program >redir will work properly as long as you run command is called 'run'. With the lattice version the command got parsed like run >redir program, so all you got in you redir file was [CLI n]. - On startup you current directory is determined and set. - Added %full and volume name to devinfo. - ps command added NEW FEATURES IN 2.03. Thanks to Steve Drew who suggested a '_path' variable. The other difference with this version is that BCPL output redirection works properly. Additionaly, alias loops are detected (this is what is known as a hack). NEW FEATURES IN 2.02. I would like to thank Dave Wecker and Steve Drew for their numerous comments on the previous version of my shell. -Return code and error handling (A) retrieve return code (B) Manual or Automatic error handling. -Control C-F now passed to external commands. -can execute shell scripts as if they were normal commands (w/ arguments) (see down below) -BCPL programs which change the CLI state (CD/PATH...) now work with the shell. However, the CLI PATH is not used currently. -MV command embellished... can now specify multiple files with a directory as the destination. -CD re-written, new variable $_cwd. startup directory figured out. -Comment character '#' -EXIT as an alternate to QUIT Additional Commands: abortline forever Additional SET variables (see documentation below) _cwd current directory (see CD below) _maxerr worst return value to date _lasterr return value from last command (internal or external) _except contains error level AND exception code. "nnn;command" (see ABORTLINE, and start of section E) _passed contains passed command line to source files _path contains search path (example: "c:,df1:c/,df0:c/" SHAR_EOF cat << \SHAR_EOF > set.c /* * SET.C * * (c)1986 Matthew Dillon 9 October 1986 * * version 2.06M (Manx Version and Additions) by Steve Drew 28-May-87 * */ #include "shell.h" #define MAXLEVELS (3 + MAXSRC) struct MASTER { struct MASTER *next; struct MASTER *last; char *name; char *text; }; static struct MASTER *Mbase[MAXLEVELS]; char * set_var(level, name, str) register char *name, *str; { register struct MASTER *base = Mbase[level]; register struct MASTER *last; register int len; for (len = 0; isalphanum(name[len]); ++len); while (base != NULL) { if (strlen(base->name) == len && strncmp (name, base->name, len) == 0) { Free (base->text); goto gotit; } last = base; base = base->next; } if (base == Mbase[level]) { base = Mbase[level] = (struct MASTER *)malloc (sizeof(struct MASTER)); base->last = NULL; } else { base = (struct MASTER *)malloc (sizeof(struct MASTER)); base->last = last; last->next = base; } base->name = malloc (len + 1); bmov (name, base->name, len); base->name[len] = 0; base->next = NULL; gotit: base->text = malloc (strlen(str) + 1); strcpy (base->text, str); return (base->text); } char * get_var (level, name) register char *name; { register struct MASTER *base = Mbase[level]; register unsigned char *scr; register int len; for (scr = (unsigned char *)name; *scr && *scr != 0x80 && *scr != ' ' && *scr != ';' && *scr != '|'; ++scr); len = scr - name; while (base != NULL) { if (strlen(base->name) == len && strncmp (name, base->name, len) == 0) return (base->text); base = base->next; } return (NULL); } unset_level(level) { register struct MASTER *base = Mbase[level]; while (base) { Free (base->name); Free (base->text); Free (base); base = base->next; } Mbase[level] = NULL; } unset_var(level, name) char *name; { register struct MASTER *base = Mbase[level]; register struct MASTER *last = NULL; register int len; for (len = 0; isalphanum(name[len]); ++len); while (base) { if (strlen(base->name) == len && strncmp (name, base->name, len) == 0) { if (base != Mbase[level]) last->next = base->next; else Mbase[level] = base->next; if (base->next != NULL) base->next->last = last; if (base == Mbase[level]) Mbase[level] = base->next; Free (base->name); Free (base->text); Free (base); return (1); } last = base; base = base->next; } return (-1); } do_unset_var(str, level) char *str; { register int i; for (i = 1; i < ac; ++i) unset_var (level, av[i]); return (0); } do_set_var(command, level) char *command; { register struct MASTER *base = Mbase[level]; register char *str; if (ac == 1) { while (base) { if (CHECKBREAK()) return(0); printf ("%-10s ", base->name); puts (base->text); base = base->next; } return (0); } if (ac == 2) { str = get_var (level, av[1]); if (str) { printf ("%-10s ", av[1]); puts(str); } else if (level == LEVEL_SET) { /* only create var if set command */ set_var (level, av[1], ""); } } if (ac > 2) set_var (level, av[1], next_word (next_word (command))); if (*av[1] == '_') { S_histlen = (str = get_var(LEVEL_SET, V_HIST)) ? atoi(str) : 0; debug = (str = get_var(LEVEL_SET, V_DEBUG)) ? atoi(str) : 0; Verbose = (get_var(LEVEL_SET, V_VERBOSE)) ? 1 : 0; if (S_histlen < 2) S_histlen = 2; } return (0); } SHAR_EOF cat << \SHAR_EOF > shell.doc INSTRUCTIONS FOR SHELL V2.06M 20-Jan-87 ----------------------------- SHELL V2.04. (C)Copyright 1986, Matthew Dillon, All Rights Reserved. You may distribute this program for non-profit only. Shell V2.06M by Steve Drew. -------------------------- -------------------------------------------------------------------------- Note: These Instructions are my specific 2.06M Instructions and Matt's 2.04 merged together. A preceding | indicates that funtionality has been changed/enhanced, a preceding * indicates that this is functionality or a command that has been added in my manx version. for version releases see readme file. --------------------------------------------------------------------------- (A) Compiling (B) Overview (C) Quicky tech notes on implimentation. (D) Command pre-processor (E) Command Line Editing (F) Function Keys (G) Command-list (H) special SET variables (I) example .login file. (A) COMPILING: | makefile supplied. | | Your manx should be patched for 1.2 (c.lib) otherwise fexec wont work | and you'll just get "command not found" for all external commands. (B) OVERVIEW: OVERVIEW of the major features: -simple history -redirection -piping -command search path -aliases -variables & variable handling (embedded variables) -file name expansion via '?' and '*' -conditionals -source files (w/ gotos and labels) -many built in commands to speed things up PROBLEMS -Append '>>' does NOT work with BCPL programs. It does work with all internal and non-bcpl programs. -This version runs UNDER WORKBENCH 1.2 ONLY. (C) QUICK TECH NOTES: PIPES have been implimented using temporary RAM: files. Thus, you should be careful when specifying a 'ram:*' expansion as it might include the temp. files. These files are deleted on completion of the pipe segment. The file names used are completely unique, even with multiple shell running simultaniously. My favorite new feature is the fact that you can now redirect to and from, and pipe internal commands. 'echo charlie >ram:x', for instance. Another favorite: echo "echo mem | shell" | shell To accomplish these new features, I completely re-wrote the command parser in execom.c The BCPL 'RUN' command should not be redirected.. .strange things happen. NO BCPL program should be output-append redirected (>>). (D) Command pre-processor preprocessing is done on the command line before it is passed on to an internal or external routine: ^c where c is a character is converted to that control character. Thus, say '^l' for control-l. $name where name is a variable name. Variable names can consist of 0-9, a-z, A-Z, and underscore (_). The contents of the specified variable is used. If the variable doesn't exist, the specifier is used. That is, if the variable 'i' contains 'charlie', then '$i' -> 'charlie'. If the variable 'i' doesn't exist, then '$i'->'$i' . ; delimits commands. echo charlie ; echo ben. ' ' (a space). Spaces delimit arguments. "string" a quoted string. For instance, if you want to echo five spaces and an 'a': echo a -> a echo " a" -> a \c overide the meaning of special characters. '\^a' is a circumflex and an a rather than control-a. To get a backslash, you must say '\\'. also used to overide alias searching for commands. >file specify output redirection. All output from the command is placed in the specified file. >>file specify append redirection (Does not work with BCPL programs). nil: df0: df1: * copy will abort after current file on Control-C. | copy by default sets the date of the destination file to that of | the source file. To overide this feature use the -d switch. | Another useful option is the -u (update) mode were copy will not | copy any files which exists already in the destination directory | if the destination file is newer or equal to the source file. | This is usefull when developing code say in ram: eg. 'copy *.c ram:' | when done you can copy -u ram: df1: and only those modules you have | modified will be copied back. | copy command will now create the destination directory if it does | not exist when specified as 'copy [-r] dir dir'. If you specify | copy file file file dir, then 'dir' must already exist. * RUN file * * Finds the specified file via $_path variable, or 2nd via AmigaDos * PATH setting, then calls AmigaDos Run command with the explicit * path/command. This command is really only implemented to allow * consistancy in first checking the shell pathing before amigados * pathing. Since shell allows for device names in _path it does'nt * care when you swap disks; if you had "df0:c/" in _path then whatever * disk you have in df0: will be checked. AmigaDos PATH always resolves * to a Lock that points only to a particular disk label:directory. MKDIR name name name... create the following directories. HISTORY [partial_string] Displays the enumerated history list. The size of the list is controlled by the _history variable. If you specify a partial- string, only those entries matching that string are displayed. MEM Display current memory statistics for CHIP and FAST. CAT [file file....] Type the specified files onto the screen. If no file is specified, STDIN in used. CAT is meant to output text files only. | DIR [-sdf] [path path ... ] | - default output show's date, protection, block size, byte size. * - Dir command rewritten to allow options: * -s short mutil(4) collum display of files * (directory files are in italics). * -d directorys only * -f files only * * eg. dir -s *.info (short directory of all .info files.) | the directory command will also not expand files that are | directories if as result of a wildcard expansion. eg: | 'dir df0:*' and 'dir df0:' will give same results | expect previously if df0:* was specified all subdirectories | of df0: were expanded also. | (to list one level of subdirectories: 'dir df0:*/*') | Directories always sorted. | Wild card expansions: | ? match any single character | * match any string | .../ recursive search down ALL sub directories | | dir df0:.../ does a full (All) sorted directory | dir .../*.c start at current dir and go down looking | for all .c files. | | | Exclude pattern matching specifier: ! | dir !*.info will exclude all *.info files in current dir. | dir df0:.../!*.info full directory tree of df0: but exclude | any ugly .info files. | dir !*.o !*.c (will result in ALL files matching since what | doesnt match the !*.o will match the !*.c) DEVINFO [device: device:... ] Display Device statistics for the current device (CD base), or specified devices. | Gives block used/free, % used, errs and volume name. FOREACH varname ( strings ) command 'strings' is broken up into arguments. Each argument is placed in the variable 'varname' in turn and 'command' executed. To execute multiple commands, place them in quotes: % foreach i ( a b c d ) "echo -n $i;echo \" ha\"" a ha b ha c ha d ha Foreach is especially useful when interpreting passed arguments in an alias or source file. NOTE: a GOTO inside will have indeterminate results. FOREVER command FOREVER "command;command;command..." The specified commands are executed over and over again forever. -Execution stops if you hit ^C -If the commands return with an error code. NOTE: a GOTO inside will have indeterminate results. RETURN [value] return from a source file. The rest of the source file is discarded. If given, the value becomes the return value for the SOURCE command. If you are on the top level, this value is returned as the exit code for the shell. IF argument conditional argument ; IF argument If a single argument is something to another argument. Conditional clauses allowed: <, >, =, and combinations (wire or). Thus <> is not-equal, >= larger or equal, etc... If the left argument is numeric, both arguments are treated as numeric. usually the argument is either a constant or a variable ($varname). The second form if IF is conditional on the existance of the argument. If the argument is a "" string, then false , else TRUE. ELSE ; else clause. ENDIF ; the end of an if statement. LABEL name create a program label right here. GOTO label goto the specified label name. You can only use this command from a source file. DEC var INC var decrement or increment the numerical equivalent of the variable and place the ascii-string result back into that variable. INPUT varname input from STDIN (or a redirection, or a pipe) to a variable. The next input line is placed in the variable. VER display my name and the version number. SLEEP timeout Sleep for 'timeout' seconds. * PS * Gives the following info: * * Proc Command Name CLI Type Pri. Address Directory * 1 SHELL Initial CLI 0 97b0 Stuff:shell * 2 sys:c/clockmem Background -10 2101a8 Workdisk: * 3 c:emacs Background 0 212f58 Stuff:shell * 4 sys:c/VT100 Background 0 227328 Workdisk: * * Address is the addres of the task, directory is the process * currently set directory. (H) SPECIAL SET VARIABLES | _prompt | This variable is set to the command you wish executed that will | create your prompt. Under manx version set this to the string | for your prompt not a command to create the prompt. (Restriction | due to commandline editing support. _history This variable is set to a numerical value, and specifies how far back your history should extend. _debug Debug mode... use it if you dare. must be set to some value _verbose Verbose mode (for source files). display commands as they are executed. _maxerr The worst (highest) return value to date. To use this, you usually set it to '0', then do some set of commands, then check it. _lasterr Return code of last command executed. This includes internal commands as well as external comands, so to use this variables you must check it IMMEDIATELY after the command in question. _cwd Holds a string representing the current directory we are in from root. The SHELL can get confused as to its current directory if some external program changes the directory. Use PWD to rebuild the _cwd variable in these cases. _passed This variable contains the passed arguments when you SOURCE a file or execute a .sh file. For instance: test a b c d -------- file test.sh ---------- echo $_passed foreach i ( $_passed ) "echo YO $i" -------------------------------- _path This variable contains the search path when the shell is looking for external commands. The format is: DIR,DIR,DIR Each DIR must have a trailing ':' or '/'. The current directory is always searched first. The entire path will be searched first for the , then for .sh (automatic shell script sourcing). * The default _path is set to "c:,df1:c/,df0:c/,ram:,ram:c/" * When using 'run' command Shell will now use it's own search * path first to find the command to run. If it fails to find * the command (but the Run command was found) it executes the * command line anyway to let amigaDos search path take over. * _insert * Set's the default for insert/overtype mode for command line * editing. ^A toggles between, but after the default is * set back as indicated by this variable. By default _insert is 1 * indicating insert mode on setting to zero will make overtype * the default. * _width * Indicates the console window width, 77 if unset. * Will change automatically if the user resizes the window. (I) EXAMPLE .login file. from a CLI or the startup-script say 'SHELL filename'. That file is sourced first. thus, 'SHELL .login' will set up your favorite aliases: ------------------------------------------------------------------------ .LOGIN ------------------------------------------------------------------------ # -Steve's .login file- # echo -n "Enter Date [DD-MMM-YY HH:MM] ";input new; DATE $new # ------My search path ------- # set _path ram:c/,ram:,c:,df0:c/,df1:c/,sys:system/,sys:utilities # -------Function keys-------- # set f1 dir df0: set f2 dir df1: set F1 dir -s df0: set F2 dir -s df1: set f3 info set f4 ps # ---------Quickies---------- # # -query delete- # #another favorite eg qd *.c alias qd "%q foreach i ( $q ) \"echo -n Delete [n] ;input a;if $a = y;del $i;endif\"" alias delete rm alias rename mv alias makedir mkdir alias type cat alias print "%q copy $q prt:" alias info "devinfo df0: df1: ram:" alias tosys "assign c: SYS:c" alias toram "assign c: RAM:c;" alias tomanx "assign c: MANX:c; manxinit" alias d "dir -s" alias clr "echo -n ^l" alias wb "loadwb" alias pref "sys:preferences" alias cal "run sys:utilities/calculator" # ------Applications---------- # alias em "run emacs" alias vt "run sys:c/VT100" # --------Finish Up----------- # ver ;echo -n "Shell loaded on ";date ------------------------------------------------------------------------ MANXINIT.SH ------------------------------------------------------------------------ SET INCLUDE=AC:include CCTEMP=ram: makedir ram:lib;Set CLIB=RAM:lib/;copy AC:lib/$libfile ram:lib" alias cleanup "del >NIL: ram:lib/* ram:lib" #run make in background at lower priority: alias make "%q run ChangeTaskPri -5 +^J^J MAKE $q" SHAR_EOF cat << \SHAR_EOF > sort.c /* * sort.c * * * version 2.06M (Manx Version and Additions) by Steve Drew 28-May-87 * * sort has been modified via addition of slashcmp which provides correct * sorting of file names in tree/alphabetical order. This was needed * due to the recursive wild carding support I added in expand(). /Steve */ QuickSort(av, n) char *av[]; int n; { int b; if (n > 0) { b = QSplit(av, n); QuickSort(av, b); QuickSort(av+b+1, n - b - 1); } } /* * QSplit called as a second routine so I don't waste stack on QuickSort's * recursivness. */ QSplit(av, n) char *av[]; int n; { int i, b; char *element, *scr; element = av[0]; for (b = 0, i = 1; i < n; ++i) { if (slashcmp(av[i],element) < 0) { ++b; scr = av[i]; av[i] = av[b]; av[b] = scr; } } scr = av[0]; av[0] = av[b]; av[b] = scr; return (b); } slashcmp(a,b) char *a,*b; { char *ap,*bp; int b_slash=0,a_slash=0,c; char *index(); if (ap = index(a,'/')) ++a_slash; if (bp = index(b,'/')) ++b_slash; if (a_slash && b_slash) { /* both contain slashes */ *bp = *ap = '\0'; c = strcmp(a,b); /* just compare before slash */ *bp = *ap = '/'; if (c == 0) c = slashcmp(ap+1,bp+1); /* recursive */ return(c); } if (!a_slash && !b_slash) /* neither have slashes so just cmp */ return(strcmp(a,b)); return(a_slash - b_slash); } SHAR_EOF cat << \SHAR_EOF > sub.c /* * SUB.C * * (c)1986 Matthew Dillon 9 October 1986 * * version 2.06M (Manx Version and Additions) by Steve Drew 28-May-87 * */ #include "shell.h" #define HM_STR 0 /* various HISTORY retrieval modes */ #define HM_REL 1 #define HM_ABS 2 /* extern struct FileLock *Clock; */ seterr() { char buf[32]; int stat; sprintf(buf, "%d", Lastresult); set_var(LEVEL_SET, V_LASTERR, buf); stat = atoi(get_var(LEVEL_SET, V_STAT)); if (stat < Lastresult) stat = Lastresult; sprintf(buf, "%d", stat); set_var(LEVEL_SET, V_STAT, buf); } char * next_word(str) register char *str; { while (*str && *str != ' ' && *str != 9) ++str; while (*str && (*str == ' ' || *str == 9)) ++str; return (str); } char * compile_av(av, start, end) char **av; { char *cstr; int i, len; len = 0; for (i = start; i < end; ++i) len += strlen(av[i]) + 1; cstr = malloc(len + 1); *cstr = '\0'; for (i = start; i < end; ++i) { strcat (cstr, av[i]); strcat (cstr, " "); } return (cstr); } /* * FREE(ptr) --frees without actually freeing, so the data is still good * immediately after the free. */ Free(ptr) char *ptr; { static char *old_ptr; if (old_ptr) free (old_ptr); old_ptr = ptr; } /* * Add new string to history (H_head, H_tail, H_len, * S_histlen */ add_history(str) char *str; { register struct HIST *hist; if (H_head != NULL && strcmp(H_head->line, str) == 0) return(0); while (H_len > S_histlen) del_history(); hist = (struct HIST *)malloc (sizeof(struct HIST)); if (H_head == NULL) { H_head = H_tail = hist; hist->next = NULL; } else { hist->next = H_head; H_head->prev = hist; H_head = hist; } hist->prev = NULL; hist->line = malloc (strlen(str) + 1); strcpy (hist->line, str); ++H_len; } del_history() { if (H_tail) { --H_len; ++H_tail_base; free (H_tail->line); if (H_tail->prev) { H_tail = H_tail->prev; free (H_tail->next); H_tail->next = NULL; } else { free (H_tail); H_tail = H_head = NULL; } } } char * get_history(ptr) char *ptr; { register struct HIST *hist; register int len; int mode = HM_REL; int num = 1; char *str; char *result = NULL; if (ptr[1] >= '0' && ptr[1] <= '9') { mode = HM_ABS; num = atoi(&ptr[1]); goto skip; } switch (ptr[1]) { case '!': break; case '-': num += atoi(&ptr[2]); break; default: mode = HM_STR; str = ptr + 1; break; } skip: switch (mode) { case HM_STR: len = strlen(str); for (hist = H_head; hist; hist = hist->next) { if (strncmp(hist->line, str, len) == 0 && *hist->line != '!') { result = hist->line; break; } } break; case HM_REL: for (hist = H_head; hist && num--; hist = hist->next); if (hist) result = hist->line; break; case HM_ABS: len = H_tail_base; for (hist = H_tail; hist && len != num; hist = hist->prev, ++len); if (hist) result = hist->line; break; } if (result) { fprintf(stderr,"%s\n",result); return(result); } printf("History failed\n"); return (""); } replace_head(str) char *str; { if (str == NULL) str = ""; if (H_head) { free (H_head->line); H_head->line = malloc (strlen(str)+1); strcpy (H_head->line, str); } } pError(str) char *str; { int ierr = (long)IoErr(); ierror(str, ierr); } ierror(str, err) register char *str; { register struct PERROR *per = Perror; if (err) { for (; per->errstr; ++per) { if (per->errnum == err) { fprintf (stderr, "%s%s%s\n", per->errstr, (str) ? ": " : "", (str) ? str : ""); return ((short)err); } } fprintf (stderr, "Unknown DOS error %ld %s\n", err, (str) ? str : ""); } return ((short)err); } /* * Disk directory routines * * dptr = dopen(name, stat) * struct DPTR *dptr; * char *name; * int *stat; * * dnext(dptr, name, stat) * struct DPTR *dptr; * char **name; * int *stat; * * dclose(dptr) -may be called with NULL without harm * * dopen() returns a struct DPTR, or NULL if the given file does not * exist. stat will be set to 1 if the file is a directory. If the * name is "", then the current directory is openned. * * dnext() returns 1 until there are no more entries. The **name and * *stat are set. *stat = 1 if the file is a directory. * * dclose() closes a directory channel. * */ struct DPTR * dopen(name, stat) char *name; int *stat; { struct DPTR *dp; *stat = 0; dp = (struct DPTR *)malloc(sizeof(struct DPTR)); if (*name == '\0') dp->lock = (struct FileLock *)DupLock ((struct FileLock *)Myprocess->pr_CurrentDir); else dp->lock = (struct FileLock *)Lock (name, ACCESS_READ); if (dp->lock == NULL) { free (dp); return (NULL); } dp->fib = (struct FileInfoBlock *) AllocMem((long)sizeof(struct FileInfoBlock), MEMF_PUBLIC); if (!Examine (dp->lock, dp->fib)) { pError (name); dclose (dp); return (NULL); } if (dp->fib->fib_DirEntryType >= 0) *stat = 1; return (dp); } dnext(dp, pname, stat) struct DPTR *dp; char **pname; int *stat; { if (dp == NULL) return (0); if (ExNext (dp->lock, dp->fib)) { *stat = (dp->fib->fib_DirEntryType < 0) ? 0 : 1; *pname = dp->fib->fib_FileName; return (1); } return (0); } dclose(dp) struct DPTR *dp; { if (dp == NULL) return (1); if (dp->fib) FreeMem (dp->fib,(long)sizeof(*dp->fib)); if (dp->lock) UnLock (dp->lock); free (dp); return (1); } isdir(file) char *file; { register struct DPTR *dp; int stat; stat = 0; if (dp = dopen (file, &stat)) dclose(dp); return (stat == 1); } free_expand(av) register char **av; { char **base = av; if (av) { while (*av) { free (*av); ++av; } free (base); } } /* * EXPAND(base,pac) * base - char * (example: "df0:*.c") * pac - int * will be set to # of arguments. * * 22-May-87 SJD. Heavily modified to allow recursive wild carding and * simple directory/file lookups. Returns a pointer to * an array of pointers that contains the full file spec * eg. 'df0:c/sear*' would result in : 'df0:C/Search' * * Now no longer necessary to Examine the files a second time * in do_dir since expand will return the full file info * appended to the file name. Set by formatfile(). * eg. fullfilename'\0'rwed NNNNNN NNNN DD-MMM-YY HH:MM:SS * * Caller must call free_expand when done with the array. * * base bname = ename = * ------ ------- ------- * "*" "" "*" * "!*.info" "" "*.info" (wild_exclude set) * "su*d/*" "" "*" (tail set) * "file.*" "" "file.*" * "df0:c/*" "df0:c" "*" * "" "" "*" * "df0:.../*" "df0:" "*" (recur set) * "df0:sub/.../*" "df0:sub" "*" (recur set) * * ---the above base would be provided by execom.c or do_dir(). * ---the below base would only be called from do_dir(). * * "file.c" "file.c" "" if (dp == 0) fail else get file.c * "df0:" "df0:" "*" * "file/file" "file/file" "" (dp == 0) so fail * "df0:.../" "df0:" "*" (recur set) * */ char ** expand(base, pac) char *base; int *pac; { register char *ptr; char **eav = (char **)malloc(sizeof(char *) * (2)); short eleft, eac; char *name; char *svfile(); char *bname, *ename, *tail; int stat, recur, scr,wild_exclude,bl; register struct DPTR *dp; *pac = recur = eleft = eac = wild_exclude = 0; base = strcpy(malloc(strlen(base)+1), base); for (ptr = base; *ptr && *ptr != '?' && *ptr != '*'; ++ptr); if (!*ptr) /* no wild cards */ --ptr; else for (; ptr >= base && !(*ptr == '/' || *ptr == ':'); --ptr); if (ptr < base) { bname = strcpy (malloc(1), ""); } else { scr = ptr[1]; ptr[1] = '\0'; if (!strcmp(ptr-3,".../")) { recur = 1; *(ptr-3) = '\0'; } bname = strcpy (malloc(strlen(base)+2), base); ptr[1] = scr; } bl = strlen(bname); ename = ++ptr; for (; *ptr && *ptr != '/'; ++ptr); scr = *ptr; *ptr = '\0'; if (scr) ++ptr; tail = ptr; if (*ename == '!') wild_exclude = 1; if ((dp = dopen (bname, &stat)) == NULL || (stat == 0 && *ename)) { free (bname); free (base); free (eav); return (NULL); } if (!stat) { /* eg. 'dir file' */ char *p,*s; for(s = p = bname; *p; ++p) if (*p == '/' || *p == ':') s = p; if (s != bname) ++s; *s ='\0'; eav[eac++] = svfile(bname,dp->fib->fib_FileName,dp->fib); goto done; } if (!*ename) ename = "*"; /* eg. dir df0: */ if (*bname && bname[bl-1] != ':' && bname[bl-1] != '/') { /* dir df0:c */ bname[bl] = '/'; bname[++bl] = '\0'; } while ((dnext (dp, &name, &stat)) && !breakcheck()) { int match = compare_ok(ename+wild_exclude,name); if (wild_exclude) match ^= 1; if (match && !(!recur && *tail)) { if (eleft < 2) { char **scrav = (char **)malloc(sizeof(char *) * (eac + 10)); movmem (eav, scrav, (eac + 1) << 2); free (eav); eav = scrav; eleft = 10; } eav[eac++] = svfile(bname,name,dp->fib); --eleft; } if ((*tail && match) || recur) { int alt_ac; char *search, **alt_av, **scrav; struct FileLock *lock; if (!stat) /* expect more dirs, but this not a dir */ continue; lock = (struct FileLock *)CurrentDir (dp->lock); search = malloc(strlen(ename)+strlen(name)+strlen(tail)+5); strcpy (search, name); strcat (search, "/"); if (recur) { strcat(search, ".../"); strcat(search, ename); } strcat (search, tail); scrav = alt_av = expand (search, &alt_ac); /* free(search); */ CurrentDir (lock); if (scrav) { while (*scrav) { int l; if (eleft < 2) { char **scrav = (char **)malloc(sizeof(char *) * (eac + 10)); movmem (eav, scrav, (eac + 1) << 2); free (eav); eav = scrav; eleft = 10; } l = strlen(*scrav); scrav[0][l] = ' '; eav[eac] = malloc(bl+l+40); strcpy(eav[eac], bname); strcat(eav[eac], *scrav); eav[eac][l+bl] = '\0'; free (*scrav); ++scrav; --eleft, ++eac; } free (alt_av); } } } done: dclose (dp); *pac = eac; eav[eac] = NULL; free (bname); free (base); if (eac) { return (eav); } free (eav); return (NULL); } /* * Compare a wild card name with a normal name */ #define MAXB 8 compare_ok(wild, name) char *wild, *name; { register char *w = wild; register char *n = name; char *back[MAXB][2]; register char s1, s2; int bi = 0; while (*n || *w) { switch (*w) { case '*': if (bi == MAXB) { printf(stderr,"Too many levels of '*'\n"); return (0); } back[bi][0] = w; back[bi][1] = n; ++bi; ++w; continue; goback: --bi; while (bi >= 0 && *back[bi][1] == '\0') --bi; if (bi < 0) return (0); w = back[bi][0] + 1; n = ++back[bi][1]; ++bi; continue; case '?': if (!*n) { if (bi) goto goback; return (0); } break; default: s1 = (*n >= 'A' && *n <= 'Z') ? *n - 'A' + 'a' : *n; s2 = (*w >= 'A' && *w <= 'Z') ? *w - 'A' + 'a' : *w; if (s1 != s2) { if (bi) goto goback; return (0); } break; } if (*n) ++n; if (*w) ++w; } return (1); } char * svfile(s1,s2,fib) char *s1,*s2; struct FileInfoBlock *fib; { char *p; p = malloc (strlen(s1)+strlen(s2)+40); strcpy(p, s1); strcat(p, s2); formatfile(p,fib); return(p); } /* will have either of these formats: * * fullfilename'\0'rwed DD-MMM-YY HH:MM:SS\n'\0' * fullfilename'\0'rwed NNNNNN NNNN DD-MMM-YY HH:MM:SS\n'\0' * 0123456789012345678901234567890123456 7 8 * */ formatfile(str,fib) char *str; register struct FileInfoBlock *fib; { char *dates(); while(*str++); *str++ = (fib->fib_Protection & FIBF_READ) ? '-' : 'r'; *str++ = (fib->fib_Protection & FIBF_WRITE) ? '-' : 'w'; *str++ = (fib->fib_Protection & FIBF_EXECUTE) ? '-' : 'e'; *str++ = (fib->fib_Protection & FIBF_DELETE) ? '-' : 'd'; if (fib->fib_DirEntryType < 0) sprintf(str," %6ld %4ld ", (long)fib->fib_Size, (long)fib->fib_NumBlocks); else strcpy(str," "); strcat(str,dates(&fib->fib_Date)); } SHAR_EOF