amiga-request@ab20.larc.nasa.gov (Amiga Sources/Binaries Moderator) (04/17/91)
Submitted-by: umueller@iiic.ethz.ch Posting-number: Volume 91, Issue 089 Archive-name: shells/cshell-5.10/part02 #!/bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 2 (of 6)." # Contents: changes.doc csh.doc.ad rawcon.c sub.c # Wrapped by tadguy@ab20 on Tue Apr 16 15:34:34 1991 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'changes.doc' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'changes.doc'\" else echo shar: Extracting \"'changes.doc'\" \(11556 characters\) sed "s/^X//" >'changes.doc' <<'END_OF_FILE' XNew features to 5.10: X- totally rewritten parser, therefore: X- local variables, in aliases as well as in batch files X- blocks can be formed, redirected, aborted: {e yo;e ho} X- $(foo) will insert output of program foo at that point, similar with `foo` X- wildcard expansion also done in first arg, e.g. '*' is a legal command line X- command lines like '$mydir/$mycommand ram:' now works X- aliases/foreach/forline/fornum/source can be redirected as a whole X- direct recursion in aliases prevented, 'alias ls "ls -s"' works X- additional speedup; twice as fast as 4.xx, four times as fast as c:Execute X- '.bra' and '.ket' introduced as dummy commands for script compatibility X- 'assign' now yields a list of all assings including late/nonbinding, paths X- 'assign -p' does path assigns (like 'c:Assign ... ADD) X- 'cat' CR terminated non-CR-termintad files again if not redirected X- 'class' can pattern-match now, e.g. 'class sound name="mod.*"' X- 'copy' has a larger buffer and checks for ^C more often X- 'copy -m' moves files (but not directories yet) X- 'dir -p' prints full paths and suppresses titles X- 'dir' can separate direcories at the top or bottom X- 'dir -z formatstring' is a very powerful formatting feature X- 'error' generates a desired return code X- 'forline i STDIN' reads args from stdin X- 'input' removes leading, trailing and multiple blanks X- 'local' and 'unlocal' for generating and deleting local variables X- 'man' can handle multiple .doc files, so you can document your own aliases X- 'mem' uses , instead of ' and is right adjusted X- 'source' can handle multiple line blocks of arbitrary length X- 'qsort' can now sort in reverse order X- 'rback'/'run' now set the variable '$_newproc' to the # of the new process X- $_abbrev can be used to disable abbreviation if internal commands X- $_ioerr contains the secondary return code after an error X- $_path now contains S: and CSH: in order to run scripts X- $_pipe now holds the directory for the temporary pipe files X- $_verbose need to be set to special values, now can trace alias calls X- @console tells whether stdin or stdout are interactive X- @ioerr converts a secondary error number to a string (like 'Why') X- @mounted indicates whether a device has been mounted yet or not X- @sortnum sorts its arguments numerically X- @volume now suppresses ugly system requesters X- shift-arrow-up and shift-arrow-down now behave like under AmigaDOS X- there is now an editing function that duplicates the last word X- csh has a startup option to enable '*' as an alias for '#?' in AmigaDOS X- a directory CSH: is proposed for scripts and the doc X- concatted lines in source files can be as long as you want X- . now stands for current directory, .. for parent X- soft and hard links to directories are ignored on wild card expansion X XBug fixes of 5.10 (in order of severity): bug present since X- AmigaDOS residents should work fine now. V37 kickstart required 5.00 X- 'Wait 5&' as an alias for 'rback Wait 5' now works 5.00 X- 'rm -r ram:' and 'search -r "" hello' finally do their job 4.00 X- 'c:execute' works. check the restrictions chapter 3.00 X- '!!' was broken in the first parser rewrite, fixed now. 5.00 X- 'return' occasionally didn't skip the whole source file 5.00 X- '~' now can correctly be used in path names: ~/*.c 5.00 X- 'Repeat' from menu caused a loop (correct behaviour!) 5.00 X- 'forline' no longer crashes if the from-file does not exist 4.00 X- 'set x 1 2;echo hello$x' finally yields 'hello1 2'. 1.00 X- 'goto' also works if there are '\' in the source file 4.00 X- command line editing on VT100's was broken. fixed. 5.00 X- low mem situations now cause a graceful exit instead of a crash 1.00 X- actions on files with embedded blanks are performed correcty 5.00 X- 'man' no longer opens garbage files (SnoopDos is hard to cheat) 5.00 X- 'dir -o' now works (ls -o always did) 5.00 X- 'dir df0:' with no disk in drive yields correct error message 5.00 X- 'dir' no longer adds line feeds to invalid date stamps 5.00 X- 'set _prompt "% "' is now allowed (not only 'set _prompt "%% "') 5.00 X- 'search -l' no longer does unnecessary line feeds 5.00 X- '@pickopts' stops picking options at the first non-option 5.00 X- system requesters are no longer unintentionally disabled 5.00 X XIncompatibilities in 5.10 X- a stand-alone '.' must be quoted now. check your strhead's and strtail's! X- the quick-cd file and the manual (now named csh.doc) should reside in csh: X- some builtin aliases and preset function keys have been moved to compat.sh X- the functions of shift-arrow-up & down are mapped to esc-arrow-up & down X- the path is now searched before auto CD is attempted (mimicking AmigaDOS) X- the characters { } @ ( ) should be quoted if you just want to echo them X- a redirection error now causes a return code of 20 instead of 1 X- _verbose now holds numeric values, 'set _verbose yes' won't work anymore X- @volume no longer appends a trailing colon to the volume name X- an empty string is now passed to external commands as "" X- future: a leading '/' and '~' might change meaning towards UNIX X XNew features to 5.00 X- read the doc! almost everything's new. X XBug fixes to 5.00: X- recursive wild card expansion does not crash the Amiga 3000 anymore X- recursive wild card expansion does not lose memory anymore X- now works on AUX: X- trying to start a non-object-file now properly prints 'Command Not Found' X- automatic sourcing now also works if you already add .sh to the file name X- files longer than 999999 bytes no longer misalign 'dir' X- exec does not discard the rest of the command line ('exec echo hi;echo ho') X- all memory trashing fixed. Thanks to C= for their great debugging tools! X- source doesn't forget last character if batchfile was not CR terminated X- run & rback also search AmigaDOS path now X- division by zero does not crash rpn anymore X- temporary pipe files are now written to t: instead of ram: X- shift-tab does not cause a lockup anymore X- running the shell via aux: no longer crashes the machine X- 'history partial' now numbers the lines correctly X- strleft, strright and strmid no longer crash on strings > 256 bytes X- source with no arguments now prints correct error message X- 'input' now cuts down lines longer than 256 bytes instead of crashing X- cursor-up no more deletes lines if there's an invalid entry in the history X- if history fails, no empty history entry is generated X- 'echo "---"' and even 'echo ---' work, but 'echo "-a"' still doesn't X- international character sets can be used X- 'copy -u' won't copy a file with identical date stamp but in uppercase X- 'copy -u' will no longer access low memory X- 'echo "echo mem | shell" | shell' now works, not only every second time X- starting from workbench now prevented X- editing lines longer than 256 bytes is now correctly prevented X- word-right cursor movement works correclty with multiple blanks X- 'if'-stack will be adjusted when a batch file is exited X- relabel occasionally crashed in Syquest drives. should be okay now X XKnown bugs in 5.00: X- under 2.0, doing a 'cat' with no args and pressing return will cause a X character to appear at the right border of the window. seems to be a X kickstart bug, as it does not happen under older kickstarts. X- AmigaDOS 2.0 CLI commands cannot be made resident. They read their command X line from stdin. This is definitely a kickstart bug. X- guaranteed to crash in extreme low mem situations X- command line editing doesn't quite work on a (physical) vt100. OK on VT200 X- under kick 2.00, fast repetition of ^W crashed. OK under 2.02 X- 'set x a b;echo hello$x' still outputs only 'a b' X XIncompatibilities in 5.00: X- 'copy -f' now means 'freshen'. Old meaning of -f is now -p (protection) X- '~' at the beginning of a file name must now be quoted X- '@' is the beginning of an argument should be quoted X- 'cat' no longer adds a CR to a non-CR-terminated file X- '0' is now 'false'. Example: 'if 0;e hi;else;e ho;endif --> ho X- variables with comparision operators inside now cause problems in 'if' X- a single '?' will not pattern match, but be passed as a string X X X XNew to 4.02A: X- Fixed bug that caused loss of memory on exit. X- cp now copies protection bits; use -f switch if you want to avoid this. X- Added commands: man (and alias manlist), uniq, head, tail, tee. X- This doc has been reformatted to work with man. X XNew to 4.01A: X- This version features mostly bug fixes and corrections: X * Window title is restored after quitting. X * rxrec now answers to the 'bye' message. X * rpn can now be redirected and piped; however, this causes X some problem (see rpn for info). X * resident list now works with ARP 1.3. To recompile source, you must X modify include file "libraries/arpbase.h". X Change definition of rpn_Usage in struct ResidentProgramNode from LONG X to WORD. X * pri no more assumes 20 CLI maximum. X * you can now split long lines in source files even into more than 2 lines. X- Added much info in this doc about source files (chapter XI) X- Added copyright notice (see under restrictions). X XNew to 4.00A: X- This version is called 4.00A because it is not 100% compatible with X previous versions. We choose to accept this in order to better support X the new ARP.library 1.3. X- External commands are searched in a different order than before; Shell X path is now searched AFTER current directory, AmigaDOS path and C:. X- ARP pattern matching has been implemented (in part for line arg expanding, X fully for search -w). X- Internal changes for various optimizations. X- Search command has been improved in several ways. X- New commands: basename, tackon. X- New options: if -v, resident -d, fornum -v -s, dir -n. X- Fixed bugs with dir (some dirs remained locked), foreach -v, htype X (blanks were treated as binary), info (for devices > 32M). X- rback command now works ok (run, however, doesn't). X- Oh, I forgot: it also has an AREXX port... And you don't even have to get X AREXX to use it. See new commands rxsend, rxrec X XNew to 3.03A: X- New filter commands fltlower, fltupper. X- Added configuration file feature: now if you have a file named S:.login, X it will be sourced for every Shell you start. X- New option dir -c. X- New editing feature: shift-left(right) arrow move cursor to previous(next) X word. X- Bugs fixed: alias command wasn't listed in help; typing a number as a X command was interpreted like 'alias'. X XNew to 3.02A: X- New commands: fornum, forline, strleft, strright, strmid, strlen, exec. X- Improved commands: foreach, pri. X- New system variable _clinumber. X- You can now split long lines in source files (see source for details). X- window -q now lists also position of screens/windows, not only dimension. X- Since strings are handled directly from Shell with new commands, X rpn is now used only for calculations; string commands are gone. X However, now RPN is really usable. X- Changed rawgets() to fix some problems with function keys, multi-line X editing and window resizing; also, fixed bug with ^E. X- cat now warns you if it can't find any file matching your pattern. X- Now uses DOS packets to get ptr to CLI window; this fixes a bug that X caused problems if Shell was run on unactive windows. X- Fixed minor bugs (htype printed some more ASCII bytes, some commands X returned random values, history didn't print CR's). END_OF_FILE if test 11556 -ne `wc -c <'changes.doc'`; then echo shar: \"'changes.doc'\" unpacked with wrong size! fi # end of 'changes.doc' fi if test -f 'csh.doc.ad' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'csh.doc.ad'\" else echo shar: Extracting \"'csh.doc.ad'\" \(11563 characters\) sed "s/^X//" >'csh.doc.ad' <<'END_OF_FILE' X If no _except variable exists, any command which fails causes the X rest of the line to abort as if an ABORTLINE had been executed. If X the _except variable exists, it is of the form: X X "nnn;commands..." X X where nnn is some value representing the minimum return code X required to cause an error. Whenever a command returns a code X which is larger or equal to nnn, the commands in _except are X executed before anything. WHEN _except EXISTS, THE COMMAND LINE X DOES NOT ABORT AUTOMATICALLY. Thus, if you want the current line X being executed to be aborted, the last command in _except should be X an "abortline". X X Exception handling is disabled while in the exception handling X routine (thus you can't get into any infinite loops this way). X X Thus if _except = ";", return codes are completely ignored. X X Example: X X set _except "20;abortline" X X XXII. EXAMPLE SOURCE FILES X------------------------- X XIf from a CLI or the startup-script you say 'SHELL filename', that file is Xsourced first. X X### compat.sh ### X X# this makes sure that your old abbreviations don't call new commands X Xalias as aset Xalias cl close Xalias g goto Xalias h help Xalias he help Xalias m md Xalias q quit Xalias re rename Xalias w window X Xalias kr "rm -r ram:* >NIL: X X### End of compat.sh ### X X XMoreover, if you have a file called S:.login, it will be sourced for every XShell you run. This is useful for aliases and setting that you want in ALL XShells. X X X### Example S:.login ### X XHere is an example .login file: X Xset F5 "cdir WORK:"^M Xset f9 "ed s:login.sh"^M Xset F9 "ed df0:s/startup-sequence"^M X Xalias toram "%q foreach i ( $q ) \"cp -r $i: ram:$i >NIL:;assign $i: ram:$i Xalias ramop "md RAM:op; assign OP: ram:op Xalias noop "assign OP: ; rm -r ram:op Xalias newop "rm -r OP:* Xalias dc "dfc df0: to df1: Xalias go "%q assign WORK: Boot:$q; cd WORK:; source startme.sh Xalias get "%q cp $q RAM: >NIL: Xalias filter "%a%b%c exec $b \\<$a \\>$c X # reads $a, filters it with $b and writes result to $c X Xalias rm "%q \\rm @confirm( Remove $q ) X X#alias rm "%a set f @pickargs( $a );set opts @pickargs( $a );\ X# e -n OK to delete @words( @files( $f ) ) file(s) and @words( @dirs( $f ) )\ X# directories\"? \";input b;if $b = y;\\rm $opts $f;endif X# # for the anxious among us: confirmed rm X Xset _prompt "%c%p> " X # this puts the path highlighted in the prompt X X# this one puts cli number, free mem, date and time in title bar Xset _titlebar "Shell %n Mem %m Date %d Time %t X X# This file will be sourced for every Shell you start X X### End of example .login ### X X**************************************************************************** X XIf you are a CLI user, your startup-sequence may be as simple as: X X C:csh S:startup.sh X XHere's a startup code: X X### Example S:startup.sh ### X Xwind -l # if you are on a PAL machine, or use overscan X # note that commands may be abbreviated (wind=window) X Xassign LC: Stuff:c INCLUDE: Stuff:include LIB: Boot:lib QUAD: RAM: X Xrback C:FaccII; sleep 1 X # after spawning a process, it is always better to allow it X # to load the command, to avoid excessive drive head movement X Xresident -d blink lc1 lc2 >NIL: #defer loading X XC:PopCli 300 C:Newcli #using full pathname loads faster XC:FF -1 Siesta.font >NIL: XC:Patch_1 >NIL: Xstack 8000 # lc1 and lc2 need this X Xsource S:setdate.sh # this is listed next X X### End of example startup.sh ### X X**************************************************************************** X XThe following is an example source file to set date and time; it may be Xused at startup if you don't have an internal clock. X X### setdate.sh ### X Xopen CON:200/100/440/80/SetDate write 1 Xecho >.1 -n "Current date is " Xdate >.1 Xecho >.1 -n "Please enter date: " Xinput <.1 d Xclose 1 Xstrlen len $d Xif $len > 1 ; date $d ; endif Xecho -n "New date: " ; date X X### End of setdate.sh ### X X*************************************************************************** X XNext comes a makefile that needs no Make program: may be executed from XShell directely!!! X X### make.sh ### X Xif -t Shell.syms Shell.h; cc +HShell.syms Shell.h; rm shell.o; endif Xif -t RAM:Shell.syms Shell.syms; cp -d Shell.syms RAM:; endif X Xforeach i ( main comm1 comm2 comm3 execom globals rawconsole run set \ X sub ) "if -t $i.o $i.c; echo Compile $i...;cc +IRAM:shell.syms $i.c; endif" X X# we used line continuation for better visibility. this is not necessary, X# you can type it all in one line. no more limit of 256 bytes per line X Xif -t Shell run.o main.o comm1.o comm2.o comm3.o execom.o \ Xset.o sub.o globals.o rawconsole.o X ln +q -m -o Shell run.o main.o comm1.o comm2.o comm3.o\ X execom.o set.o sub.o globals.o rawconsole.o -la -lc Xendif X X### End of make.sh ### X X XXIII. Default Values X-------------------- X XTo make things easier, some aliases are predefined whenever you start a Xnew Shell. These are: X X CLS X Simply clear the screen. X X CDIR X Use "cdir directory" to clear the screen, set CD to directory, X and list it. X X EXIT X Leave Shell and exit CLI. X X FG X Runs current shell in foreground, this means priority 1. X X KR X Used to delete everything on RAM:. This one is gone, if you still X want it, you'll have to put it in your s:.login X X LP X List to printer one or more files. X X MANLIST X Display a list of possible arguments to man. You can pipe this to X qsort to get a sorted output. X X NICE X Sets this shell to priority -1. X XMoreover, many variables have default values, and many function keys are Xpredefined. You can use set command to determine all of these. X XXIV. Object oriented features X------------------------------ X X CLASSES OF FILES X X You can define a class of files using several 'class' commands. X Here a simple example: X X class picture suff=.pic suff=.iff suff=.ilbm X class anim suff=.anim X X From now on, everything with the suffix .pic, .iff or .ilbm will X be identified as a picture. Please note that there may be no blanks X between the names and the '=', and that blanks inside the names X must be put in quotes. So these are the ways to identify a file: X X suff=.doc True if the suffix of the file is .doc X name=readme True if the file is "readme" X name="mod.*" True if the name starts with 'mod.' X offs=14,DC..C4FD True if the bytes starting at $14 are $DC, X anything, $C4, $FD (all numbers hexadecimal!). X Each pair of dots means one byte ignored. X chars True if 90% of the bytes in the file are 32..127 X or 9..13 X default Always true, used to define the default type X X Note that only the first character is examined, so 's' = 'suff'. X One class can be identified by more than one 'class' statement. X They are looked at in the same sequence they were entered. So to X make sure that an zoo archive misnamed as .lzh is identified X correctly, use the following 'class' statements: X X class zoo offs=14,DCA7C4FD X class lzh offs=2,2D6C68..2D X class zoo suff=.zoo X class lzh suff=.lzh X X Moreover, there is a builtin class 'dir', which means directory. X Now we know many file types. But what to do with them? This is X where we define 'actions'. X X ACTIONS ON CLASSES X X There may be one or more 'class' commands that define what actions X need to be taken in various cases for that specific class: X X class zoo actions view="zoo -list" extr="zoo -extract" X class lzh actions view="lz l" extr="lz e" X X Whenever somebody tries to 'view' a test.zoo, the command X 'zoo -list test.zoo' will be issued, but if he tries to X view test.lzh, then 'lz l test.lzh' will be executed. Note X that any command supplied here goes through the normal csh X parser, so AmigaDOS and csh paths will be searched. Aliases X with arguments are allowed here, too, so whatever the user X typed will be stored in the variable after the '%'. X X How do I tell a file that I want to 'view' it? There comes the X second command used for object oriented features: X X action view test.zoo X X will first identify the type of that file and then apply, if X possible, the 'view' action to it. Of course, this works best X inside an alias: alias v "action view" will define a v-command X that views all types of files known to cshell. Similarly, you X can define alias xtr "action extr" and use this command to X extract files from any type of archive. X There is one action that will be sent to every file that you X try to start but is not executable. This action is 'exec'. X Assume you have defined the class 'picture', then after X X class picture actions view=Mostra exec=Mostra X X you can display a picture using Mostra by just typing its name. X More builtin actions like 'rm' and 'dir' may be implemented, X so don't use command names for action names. X X The batch file class.sh defines a few useful classes. X X X XXV. Keymaps X--------------- X X You define a keymap as a collection of key/function pairs. Both X are given as numbers. There can be several keymaps which activate X each other, but at first we only edit keymap 0, which is active X at the beginning. All keys you define will eventually overwrite X the old definitions in an existing keymap. Everithing marked with X a (*) is not yet implemented. X X KEYCODES X X 1..255 The corresponding ASCII character X 256 Up Arrow X 257 Down Arrow X 258 Right Arrow X 259 Left Arrow X 260 Help X 261..270 F1..F10 (unshifted) X X X Modifiers (add them to the key code) X X 512 SHIFT (only necessary for arrows and fkeys) X 1024 ESC (was pressed & released before this key) X X EDITFUNCTIONS X X - Movement Move cursor... X 0 CursLeft 1 left X 1 CursRight 1 right X 2 WordLeft 1 word left X 3 WordRight 1 word right X 4 BegOfLine to beginning of line X 5 EndOfLine to end of line X X - Deleting Delete... X 10 Backspace char left from cursor X 11 Delete char right from cursor X 12 BkspcWord word left from cursor X 13 DelWord word right from cursor X 14 DeleteToSOL to start of line X 15 DeleteToEOL to end of line X 16 DeleteLine whole line X X - History insert X 20 Back Move one line back in history X 21 Forward Move one line forward in history X 22 Beg Move to first line in history X 23 End Move to last line in history X 24 Complete History retrieve like '!' X 25 Exec Execute history line & bring up next X 26 Tail Insert previous line except first word X 27 Bottom Go below last history command X 28 DupWord Duplicates the last word on this line X X - Completion X 30 Normal Insert first matching file (or cycle) X 31 Partial Insert substring of all matching files X 32 All Insert all matching files X 33 Directory Find dir in quick cd list X 34 LastCD Insert last current directory X X - Special X 40 Insert Toggle Insert/Overwrite X 41 Quit Silently perform 'quit' X 42 Help Silently perform 'help' X 43 Refresh Redraw current line X 44 Execute Execute current line X 45 Leave Edit new line, store this in hist X 46 EOF Terminate shell X 47 NOP Do nothing X 48 Echo^O Echoes a ^O X 49 Beep Echoes a ^G X X - Other X 50 Fkey Execute command associated to last fkey X 51 Menu Execute command associated to last menu X 52 Undo Undoes last edit X 53 Repeat Repeats last function X X X Command types X X 0 +x Editing function x, see above descriptions X 512 +x Setmap x, x=0..7 X 1024+x Insert key x, x=1..255 X 1536+x Macro x x=1..15 (*) X 2048+x String x x=1..15 (*) END_OF_FILE if test 11563 -ne `wc -c <'csh.doc.ad'`; then echo shar: \"'csh.doc.ad'\" unpacked with wrong size! fi # end of 'csh.doc.ad' fi if test -f 'rawcon.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'rawcon.c'\" else echo shar: Extracting \"'rawcon.c'\" \(18348 characters\) sed "s/^X//" >'rawcon.c' <<'END_OF_FILE' X/* X * rawcon.c X * X * Shell 2.07M 17-Jun-87 X * console handling, command line editing support for Shell X * using new console packets from 1.2. X * Written by Steve Drew. (c) 14-Oct-86. X * 16-Dec-86 Slight mods to rawgets() for Disktrashing. X * X * Version 4.01A by Carlo Borreo & Cesare Dieni 17-Feb-90 X * Version 5.00L by Urban Mueller 17-Feb-91 X * X */ X X#include "shell.h" X Xstatic int myget( void ); Xstatic void myunget(int c); Xstatic void setrawcon( long flag, int ievent ); Xstatic int get_seq( long *param ); Xstatic int bkspcword( int i, int max, int cnt ); X X X#if RAW_CONSOLE X Xstatic char *tyahdptr, *lasttya; Xstatic int tabctr, qcdctr, unget; X X#define SETRAW setrawcon(-1L,1); X#define SETCON setrawcon( 0L,1); X Xint w_width; Xextern char *MenuCommand[MAXMENUS][MAXITEMS]; X X#define CTRL -64 X#define SHIFT 512 X#define ESC 1024 X X#define CUP 256 X#define CDN 257 X#define CRT 258 X#define CLT 259 X#define TAB 9 X Xstatic int Curmap; Xstatic USHORT *Keymap[8]; Xstatic USHORT DefKeymap0[]={ X CLT, 0, /* CursLt = Move.Left */ X CRT, 1, /* CursRt = Move.Right */ X SHIFT+CLT, 2, /* SCursLt= Move.WordL */ X SHIFT+CRT, 3, /* SCursRt= Move.WordR */ X ESC+CLT, 4, /* ESC-CLt= Move.SOL */ X ESC+CRT, 5, /* ESC-CRt= Move.EOL */ X CTRL+'A', 4, /* ^A = Move.SOL */ X CTRL+'E', 5, /* ^E = Move.EOL */ X CTRL+'Z', 4, /* ^Z = Move.SOL */ X 8, 10, /* BackSp = Del.BackSp */ X 127, 11, /* Delete = Del.Delete */ X ESC+ 8, 12, /* ESC-BkS= Del.WordL */ X ESC+127, 13, /* ESC-Del= Del.WordR */ X CTRL+'W', 12, /* ^W = Del.WordL */ X CTRL+'B', 14, /* ^B = Del.SOL */ X CTRL+'K', 15, /* ^K = Del.EOL */ X ESC+'x',513, /* ESC-x = Setmap 1 */ X ESC+'d', 16, /* ESC-d = Del.Line */ X CTRL+'X', 16, /* ^X = Del.Line */ X CUP, 20, /* CursUp = Hist.Back */ X CDN, 21, /* CursDn = Hist.Forw */ X ESC+CUP, 22, /* ECursUp= Hist.Beg */ X ESC+CDN, 23, /* ECursDn= Hist.End */ X SHIFT+CUP, 24, /* SCursUp= Hist.Compl */ X ESC+CUP, 24, /* ESC-! = Hist.Compl */ X ESC+ 13, 25, /* ESC-Ret= Hist.Exec */ X CTRL+'T', 26, /* ^T = Hist.Tail */ X SHIFT+CDN, 27, /* SCursDn= Hist.Clr */ X CTRL+'P', 28, /* ^P = Hist.DupWrd*/ X TAB, 30, /* Tab = Comp.Norm */ X SHIFT+TAB, 31, /* STab = Comp.Part */ X ESC+TAB, 32, /* ESC-TAB= Comp.All */ X ESC+'c', 33, /* ESC-c = Comp.CD */ X ESC+'~', 34, /* ESC-~ = Comp.LastCD*/ X ESC+'i', 40, /* ESC-i = Spec.Insert*/ X CTRL+'L', 43, /* ^L = Spec.Refr */ X 10, 44, /* Enter = Spec.Accept*/ X 13, 44, /* ^Enter = Spec.Accept*/ X CTRL+'N', 45, /* ^N = Spec.Next */ X CTRL+'O', 48, /* ^O = Spec.EchoO */ X CTRL+'\\', 46, /* ^\ = Spec.EOF */ X 260, 42, /* Help = Misc.Help */ X 271, 51, /* Menu = Menu */ X CTRL+'U', 52, /* Undo = Spec.Undo */ X CTRL+'R', 53, /* Repeat = Spec.Repeat*/ X 0, 0 X}; X Xstatic USHORT DefKeymap1[]={ X 8, 14, X 127, 15 X}; X Xstatic char *Line, *Prompt; Xstatic int Pl; Xstatic char LastDir[128]; X Xvoid Xinitmap(void) X{ X if( !Keymap[0] ) X Keymap[0]=DefKeymap0, Keymap[1]=DefKeymap1; X} X Xchar * Xrawgets( char line[], char prompt[] ) X{ X static int inslen, lastrecall=-1; X static int lastfn, lastkey; X X int n, pl, max, i, c, key, fn, cnt; X USHORT *p; X char *s, *ps, typeahd[256], undo[256], *src, tmp; X int savn, insert=1, recall, undo_i=0, undo_max=0; X struct HIST *hist; X char **eav=NULL, *ret, fake; X int eac, eactr=0; X long param[10], *par; X X typeahd[0]=0; X tyahdptr=lasttya=typeahd; X X newwidth(); X X if ( o_noraw || !IsInteractive(Input()) ) { X if( IsInteractive(Input())) { X printf("%s",prompt); X fflush(stdout); X } X return(gets(line)); X } X X if (WaitForChar((long)Input(), 100L) || /* don't switch to 1L ...*/ X CHARSWAIT(stdin)) { /* else causes read err's*/ X gets(line); X return(line); X } X X SETRAW; Xbegin: X printf("\015%s\033[6n",prompt); X fake= savn = pl = n = 0; X tyahdptr = typeahd; X X while( (typeahd[n]=getchar()) != 'R') { X if (typeahd[n] == 155) savn = n; X if (typeahd[n] == 27 && getchar()=='[') X typeahd[n] =155, savn=n; X n++; X } X /* typeahd now contains possible type a head chars X followed by <CSI> cursor position report. */ X X typeahd[savn] = '\0'; X if (typeahd[n-2] != ';') pl = (typeahd[n-2] -'0') * 10; X pl += typeahd[n-1] - 49; X ps = line + pl; X line[max = i = pl] = '\0'; X X Line=line; Prompt=prompt; Pl=pl; X X if (s = get_var (LEVEL_SET, "_insert")) insert = atoi(s) ? 1 : 0; X X if( (recall=lastrecall)>=0 ) { X lastrecall=-1; X goto recallh; X } X X while( (c=myget()) != -1) { X int esc=0; X key=-1; X if( c==27 ) { X esc=ESC; X if((c=myget())=='[') X c=155,esc=0; X } X switch(c) { X case 155: X switch(c=myget()) { X case 'A': key=256; break; /* CursUp */ X case 'B': key=257; break; /* CursDn */ X case 'C': key=258; break; /* CursRt */ X case 'D': key=259; break; /* CursLt */ X case 'T': key=256+SHIFT; break; /* SCursUp */ X case 'S': key=257+SHIFT; break; /* SCursDn */ X case ' ': X switch( myget() ) { X case '@': key=258+SHIFT; break; /* SCursRt */ X case 'A': key=259+SHIFT; break; /* SCursLt */ X } X break; X case 'Z': key= 9+SHIFT; break; /* STab */ X case '?': key= 260; myget(); break; /* Help */ X default : X myunget(c); X par=param; X do { X for( *par=0; (c=myget())>='0' && c<='9'; ) X *par=10* *par + c-'0'; X par++; X } while( c==';' ); X if( c=='~' ) { X key=param[0]+261; X if( key>270 ) key+=SHIFT-10; X } X if( c=='|' ) key=271; X } break; X default: key=c; break; X } X key+=esc; X X for( fn=-1, p=Keymap[Curmap]; *p; p+=2 ) X if( *p==key ) X { fn=p[1]; break; } X if( fn==-1 && key>=261 && key<=270 || key>=261+SHIFT && key<=270+SHIFT ) X fn=50; X X if( fn!=52 && !*lasttya) { X memcpy( undo+pl, line+pl, max-pl ); X undo_i=i; undo_max=max; X } X X switch( fn/512 ) { X case 1: X fn&=511; X if( fn<8 && Keymap[fn] ) Curmap=fn; X fn=-2; X break; X case 2: X key=fn&511, fn=-1; X break; X } X X if( fn!=-2 ) X Curmap=0; X X if( fn!=53 && !*lasttya ) X lastfn=fn, lastkey=key; X Xdofn: X switch( fn ) { X case -2: X break; X X case 0: /* cursor left */ X if (i > pl) X i--, printf("\033[D"); X break; X case 1: /* cursor right */ X if (i < max) X i++, printf("\033[C"); X break; X case 2: /* word left */ X for (cnt=0; i>pl && line[i-1] == ' '; cnt++,i--); X for ( ; i>pl && line[i-1] != ' '; cnt++,i--); X if( cnt ) printf("\033[%dD",cnt); X break; X case 3: /* word right */ X for( cnt=0; i<max && line[i] != ' '; i++,cnt++) ; X for( ; i<max && line[i] == ' '; i++,cnt++) ; X if( cnt ) printf("\033[%dC",cnt); X break; X case 4: /* beg of line */ X if (i>pl) printf("\033[%dD",i-pl); X i = pl; X break; X case 5: /* end of line */ X if (i!=max) printf("\033[%dC",max - i); X i = max; X break; X X case 10: /* backspace */ X if (i > pl) { X i--; X printf("\010"); X } else break; X case 11: /* delete */ X if (i < max) { X int j,t,l = 0; X memmove(&line[i],&line[i+1],max-i); X --max; X printf("\033[P"); X j = w_width - i % w_width - 1; /* amount to end */ X t = max/w_width - i/w_width; /* no of lines */ X for(n = 0; n < t; n++) { X l += j; /* # of char moved*/ X if (j) printf("\033[%dC",j);/* goto eol */ X printf("%c\033[P",line[w_width*(i/w_width+n+1)-1]); X j = w_width-1; X } X if (t) X printf("\033[%dD",l+t); /* get back */ X } X break; X case 12: /* bkspc word */ X cnt= bkspcword(i,max,-1); X max-=cnt; i-=cnt; X break; X case 13: X for( cnt=0; i<max && line[i]!=' '; i++,cnt++ ) ; X for( ; i<max && line[i]==' '; i++,cnt++ ) ; X if ( cnt ) printf("\033[%dC",cnt); X cnt=bkspcword(i,max,cnt); X i-=cnt; max-=cnt; X break; X case 14: X cnt=bkspcword(i,max,i-pl); X i-=cnt; max-=cnt; X break; X case 16: /* delete line */ X if (i>pl) printf("\033[%dD",i-pl); X i = pl; X case 15: /* delete to EOL */ X printf("\033[J"); X max = i; X line[i] = '\0'; X break; X X X case 20: /* history up */ X ++recall; X case 21: /* history down */ Xrecallh: X line[pl] = '\0'; X if (recall >= 0 || fn==20) { X if ( fn==21 ) --recall; X n=recall; X if (recall >= 0) { X for(hist = H_head; hist && n--; X hist = hist->next); X if (hist) strcpy(&line[pl],hist->line); X else recall = H_len; X } X } X if (i != pl) X printf("\033[%dD",i-pl); X printf("\033[J%s",ps); X i = max = strlen(ps) + pl; X break; X case 22: /* beg of hist */ X recall = H_len-1; X case 23: /* end of hist */ X line[pl] = '\0'; X if (fn == 23) { X recall = 0; X if (H_head) strcpy(&line[pl], H_head->line); X } else if (H_tail) X strcpy(&line[pl], H_tail->line); X printf("\015\033[J%s%s", prompt, ps); X i = max = strlen(ps) + pl; X break; X case 24: /* complete hist */ X line[max]=0; X if( s=get_history(&line[pl-1],0 )) { X if (i>pl) printf("\033[%dD\033[J",i-pl); X line[i=max=pl]=0; X strncpy(typeahd,s,256); X tyahdptr=typeahd; X } X break; X case 25: /* exec hist */ X lastrecall= recall; X goto done; X case 26: /* tail of prev */ X if( H_head && (s=H_head->line) && (s=index(s,' ')) ) X tyahdptr=s; X break; X case 27: /* botton */ X recall=-1; X goto recallh; X case 28: /* duplicate word */ X for(s=line+i; s>ps && *(s-1)==' '; --s ) ; X tmp=*s; *s=0; X if( !(src=rindex(ps,' '))) src=""; X strcpy(tyahdptr=typeahd,src); X *s=tmp; X break; X X case 30: /* complete */ X case 31: X case 32: X case 33: { X static int lastcompl; X int j, k, n, e, cnt, len, radlen; X char *name, *q, abbrev; X X abbrev= fn==31; Xcomplete: X tyahdptr=""; X if( tabctr!=0 ) { X char *dest=typeahd, *lcd; X X lastcompl=fn; X for( cnt=0; i<max && line[i]!=' '; ++i, ++cnt ) ; X if(cnt) printf("\033[%dC",cnt); X for( e=i, j=i-1, cnt=0; j>=pl && line[j]!=' ' && line[j]!='<' && X line[j]!='>' && line[j]!=';' ; --j ) cnt++; X ++j; X X if( line[j]=='~' && (lcd=get_var(LEVEL_SET,v_lcd))) { X strcpy(dest,lcd); X dest+=strlen(dest); X j++; X } X memcpy(dest,&line[j],e-j); X dest+=e-j; X if( fn!=33 ) X *dest++='*'; X *dest=0; X if( eav ) free_expand( eav ), eav=NULL; X breakreset(); X tabctr=1; X if( fn==33 ) { X strncpy(LastDir,typeahd,128); X if( !quick_cd( name=typeahd+128, LastDir, 0)) X { putchar(7); break; } X } else { X eav =expand(typeahd,&eac); X if( eac==0 ) { putchar(7); break; } X QuickSort(eav, eac); X if( fn==30 ) X name=eav[ eactr=0 ]; X else X name=compile_av(eav,0,eac,' ',1), tabctr=0; X } X inslen=cnt; X } else { X abbrev=0, tabctr=1; X if( lastcompl==33 ) { X quick_cd( name=typeahd+128, LastDir, 1); X } else { X if( !eac ) break; X name=eav[eactr=++eactr % eac]; X } X } X len=bkspcword(i,max,inslen); X i-=len; max-=len; X if( abbrev && eac>1) { X strcpy( typeahd, eav[0] ); X radlen= 9999; X for( k=0; k<eac; k++ ) { X if ( (n=strlen(eav[k])) < radlen ) radlen=n; X for( n=0; n<radlen && eav[0][n]==eav[k][n]; n++ ) ; X if ( n<radlen ) radlen=n; X } X typeahd[radlen]=0; X eactr--; X } else { X if( lastcompl==32 ) { X strncpy( typeahd,name,250 ); X name[250]=0; X } else { X strcpy(typeahd,(q=index( name, ' ' )) ? "\"" : "" ); X strcat(typeahd,name); X if( q ) strcat(typeahd,"\""); X if( lastcompl==33 || isdir(name) ) X appendslash( typeahd ); X else X strcat( typeahd, " " ); X } X } X tyahdptr=typeahd; X inslen=strlen(typeahd); X } X break; X case 34: X strncpy(typeahd,get_var( LEVEL_SET, v_lcd ),230); X appendslash(tyahdptr=typeahd); X break; X X case 40: /* ins/ovr */ X insert ^= 1; X break; X case 41: /* quit */ X strcpy(ps,"quit"); X goto done; X case 42: /* help */ X strcpy(ps,"help"); X goto done; X case 43: /* refresh */ X if ((n = i/w_width)) printf("\033[%dF",n); X printf("\015\033[J%s%s",prompt,ps); X i = max; X break; X case 44: X line[max] = '\0'; Xdone: printf("\033[%dC\n",max - i); X strcpy(line, ps); X ret=line; X if( fake ) goto begin; X goto exit; X case 45: /* leave */ X line[max] = '\0'; X add_history( ps ); X fake=1; X goto done; X case 46: /* EOF */ X ret=NULL; X goto exit; X case 47: X break; X case 48: X printf("\017"); X break; X case 49: X printf("\07"); X break; X X case 50: { X char fkeys[8]; X sprintf(fkeys,"%c%d",param[0]>=10?'F':'f',param[0]%10+1); X if (s = get_var(LEVEL_SET, fkeys)) { X tyahdptr = strcpy(typeahd,s); X a0tospace( tyahdptr ); X } X break; X } X case 51: { X int class=param[0], code=param[2]; X if( class==10 ) { X int num=MENUNUM( code ), item=ITEMNUM( code ); X tyahdptr=""; X if( num>=0 && num<MAXMENUS && item>=0 && item<=MAXITEMS ) X tyahdptr=MenuCommand[num][item]; X } X if( class==11 ) { X strcpy(ps,"quit"); X goto done; X } X } X case 52: { X int t; X X if ((n = i/w_width)) printf("\033[%dF",n); X swapmem( undo+pl, line+pl, MAX( max, undo_max)-pl ); X t=max; max=undo_max; undo_max=t; X t=i; i =undo_i; undo_i =t; X line[max]=0; X printf("\015\033[J%s%s",prompt,ps); X if( i<max ) printf("\033[%dD",max-i); X } X break; X case 53: X fn=lastfn; key=lastkey; X goto dofn; X X default: X key&=255; X if (key == 9) key = 32; X if (key > 31 && (insert?max:i) < 256) { X if (i < max && insert) { X int j,t,l = 0; X memmove(&line[i+1], &line[i], max - i); X printf("\033[@%c",key); X t = max/w_width - i/w_width; X j = w_width - i % w_width - 1; X for(n = 0; n < t; n++) { X l += j; X if (j) printf("\033[%dC",j); X printf("\033[@%c",line[w_width*(i/w_width+n+1)]); X j = w_width-1; X } X if (t) printf("\033[%dD",l + t); X ++max; X } X else { X if(i == pl && max == i) printf("\015%s%s",prompt,ps); X putchar(key); X } X line[i++] = key; X if (max < i) max = i; X line[max] = '\0'; X } X } X } X ret=NULL; Xexit: X newwidth(); X if( eav ) free_expand(eav); X SETCON; X return ret; X} X Xint Xbkspcword( int i, int max, int cnt ) X{ X int o=i; X X if( !cnt ) return 0; X X if( cnt==-1 ) { X cnt=0; X while( i>Pl && Line[i-1]==' ' ) i--, cnt++; X while( i>Pl && Line[i-1]!=' ' ) i--, cnt++; X } else X i-=cnt; X X if( cnt ) printf("\033[%dD",cnt); X memmove( Line+i, Line+o, max-o ); X memset ( Line+max-cnt, ' ', cnt ); X X printf("%s",Line+i); X X if( max-i ) printf("\033[%dD", max-i ); X fflush(stdout); X Line[max-=cnt]=0; X X return cnt; X} X Xvoid Xsetrawcon( long flag, int ievent ) /* -1L=RAW:, 0L=CON: */ X{ X static char menuon, button; X long packargs[8]; X X if( !o_nowindow && ievent && flag==0 && menuon) X printf("\033[10}"), menuon=0; X X packargs[0]=flag; X SendPacket(994L, packargs, (void *)Myprocess->pr_ConsoleTask); X X if( !o_nowindow && ievent && flag==-1 ) { X if( !menuon ) X printf("\033[10{"), menuon=1; X if( !button ) X printf("\033[11{"), button=1; X } X fflush(stdout); X} X X X Xstatic int row, height, cnt, noquick=1; Xstatic char scrollstr[10]; X Xextern BPTR OldCin; X Xstatic int FromTee; X Xvoid Xprepscroll( int fromtee ) X{ X BPTR truecin=0; X long param[8]; X X row=height=0; X FromTee=fromtee; X X if(( noquick=!o_scroll ||o_noraw || o_nofastscr )) X return; X if(( noquick=!IsInteractive(Output()) && !fromtee )) X return; X if( !IsInteractive(Input())) { X truecin=Myprocess->pr_CIS; X X if( noquick=!IsInteractive(OldCin) ) X return; X X Myprocess->pr_CIS = DEVTAB(stdin) = OldCin; X } X X if( !CHARSWAIT(stdin) ) { X SETRAW; X fprintf(fromtee?stderr:stdout,"\033[ q"); X get_seq( param ); X height=param[2]; X while( getchar()!='r') ; X X fprintf(fromtee?stderr:stdout,"\033[6n"); X get_seq( param ); X row=param[0]; X X SETCON; X X cnt= height-row+1; X noquick= height<o_minrows; X } X X sprintf(scrollstr,"\033[%cS\033[%cA", o_scroll+'0', o_scroll+'0'); X X if( truecin ) X Myprocess->pr_CIS = DEVTAB(stdin) = truecin; X} X Xstatic int Xget_seq( long *param ) X{ X int c; X X while( (c=getchar())!=155 ) ; X do { X *param=0; X while( (c=getchar())>='0' && c<='9' ) X *param=10* *param + c-'0'; X param++; X } while( c==';' ); X X return c; X} X X Xvoid Xquickscroll( void ) X{ X if( noquick ) return; X X if( --cnt<=0 ) { X cnt=o_scroll; X fprintf( FromTee ? stderr : stdout, "%s",scrollstr); X } X} X Xint Xdo_keymap( void ) X{ X int i, n, len; X USHORT *tmp, *put, *get, *map; X char *ind; X X n=myatoi(av[1],0,7); X if( atoierr ) return 20; X X map=Keymap[n]; len=0; X if( map ) X for( len=0; map[2*len]; len++ ) ; X X put=tmp=salloc((len+ac)*2*sizeof(USHORT)); X for( i=2; i<ac; i++ ) { X if( !(ind=index(av[i],'='))) { X ierror( av[i],500); X free( tmp ); X return 20; X } X *put++=atoi(av[i]); X *put++=atoi(ind+1); X } X X for( i=0; i<len; i++ ) { X for( get=tmp; get<put; get+=2 ) X if( *get==map[2*i] ) X break; X if( get==put ) { X *put++=map[2*i]; X *put++=map[2*i+1]; X } X } X X if( map && map!=DefKeymap0 && map!=DefKeymap1 ) X free( map ); X Keymap[n]=tmp; X Curmap=0; X X return 0; X} X Xstatic int Xmyget( void ) X{ X int c; X X lasttya=tyahdptr; X if( unget ) X c=unget, unget=0; X else if( tyahdptr && *tyahdptr) X c=*tyahdptr++; X else { X#ifndef AZTEC_C X fflush(stdout); X#endif X if( (c=getchar())!=155 ) X tabctr--, qcdctr--; X } X X return c; X} X Xstatic void Xmyunget(int c) X{ X unget=c; X} X Xint Xnewwidth( void ) X{ X extern struct Window *Win; X X w_width=80; X if( !o_nowindow && Win ) X w_width=(Win->Width-(Win->BorderLeft+Win->BorderRight))/ X Win->RPort->TxWidth; X if( w_width<1 ) w_width=1; /* crash after resizing was reported */ X return w_width; X} X X X X#else X Xprepscroll(){} Xquickscroll(){} X X#endif X END_OF_FILE if test 18348 -ne `wc -c <'rawcon.c'`; then echo shar: \"'rawcon.c'\" unpacked with wrong size! fi # end of 'rawcon.c' fi if test -f 'sub.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'sub.c'\" else echo shar: Extracting \"'sub.c'\" \(21464 characters\) sed "s/^X//" >'sub.c' <<'END_OF_FILE' X X/* X * SUB.C X * X * (c)1986 Matthew Dillon 9 October 1986 X * X * Version 2.07M by Steve Drew 10-Sep-87 X * Version 4.01A by Carlo Borreo & Cesare Dieni 17-Feb-90 X * Version 5.00L by Urban Mueller 17-Feb-91 X * X */ X X#include "shell.h" X#include "proto.h" X Xstatic void del_history( void ); Xstatic int dnext( struct DPTR *dp, char **pname, int *stat); Xstatic char *svfile( char *s1, char *s2, FIB *fib); Xstatic int exall( BPTR lock, char *path ); Xstatic void quicksort( char **av, int n ); X X X#define HM_STR 0 /* various HISTORY retrieval modes */ X#define HM_REL 1 X#define HM_ABS 2 X Xvoid Xseterr( int err ) X{ X static int LastErr; X char buf[32], *val; X int stat=0; X X Lastresult=err; X X if( LastErr!=err ) { X LastErr=err; X sprintf(buf, "%d", err); X set_var(LEVEL_SET, v_lasterr, buf); X X if( val=get_var(LEVEL_SET, v_stat)) X stat = atoi(val); X if (stat < Lastresult) set_var(LEVEL_SET, v_stat, buf); X } X} X X#define ISSPACE(c) ((c)==' ' || (c)==9 || (c)==0xA0) X Xchar * Xnext_word( char *str ) X{ X while (*str && ! ISSPACE(*str)) ++str; X while (*str && ISSPACE(*str)) ++str; X return str; X} X X/* X * FREE(ptr) --frees without actually freeing, so the data is still good X * immediately after the free. X */ X X Xvoid XFree( void *ptr ) X{ X static char *old_ptr; X X if (old_ptr) free (old_ptr); X old_ptr = ptr; X} X X/* X * Add new string to history (H_head, H_tail, H_len, X * S_histlen X */ X Xvoid Xadd_history( char *str ) X{ X struct HIST *hist; X char *get; X X for( get=str; *get; get++ ) X if( (*get&127)<' ') X *get=' '; X X if (H_head != NULL && !strcmp(H_head->line, str)) X return; X while (H_len > S_histlen) X del_history(); X hist = (struct HIST *)salloc (sizeof(struct HIST)); X if (H_head == NULL) { X H_head = H_tail = hist; X hist->next = NULL; X } else { X hist->next = H_head; X H_head->prev = hist; X H_head = hist; X } X hist->prev = NULL; X hist->line = salloc (strlen(str) + 1); X strcpy (hist->line, str); X ++H_len; X} X Xstatic void Xdel_history() X{ X if (H_tail) { X --H_len; X ++H_tail_base; X free (H_tail->line); X if (H_tail->prev) { X H_tail = H_tail->prev; X free (H_tail->next); X H_tail->next = NULL; X } else { X free (H_tail); X H_tail = H_head = NULL; X } X } X} X Xchar * Xget_history( char *ptr, int echo ) X{ X struct HIST *hist; X int len; X int mode = HM_REL; X int num = 1; X char *str=NULL; X char *result = NULL; X X if (ptr[1] >= '0' && ptr[1] <= '9') { X mode = HM_ABS; X num = atoi(&ptr[1]); X goto skip; X } X switch (ptr[1]) { X case '!': X break; X case '-': X num += atoi(&ptr[2]); X break; X default: X mode = HM_STR; X str = ptr + 1; X break; X } Xskip: X switch (mode) { X case HM_STR: X len = strlen(str); X for (hist = H_head; hist; hist = hist->next) { X if (strncmp(hist->line, str, len) == 0 && *hist->line != '!') { X result = hist->line; X break; X } X } X break; X case HM_REL: X for (hist = H_head; hist && num--; hist = hist->next); X if (hist) X result = hist->line; X break; X case HM_ABS: X len = H_tail_base; X for (hist = H_tail; hist && len != num; hist = hist->prev, ++len); X if (hist) X result = hist->line; X break; X } X if( echo ) X fprintf(stderr, result ? "%s\n" : "History failed\n", result); X if( !result ) result=""; X return result; X} X Xvoid Xreplace_head( char *str ) X{ X if (str && strlen(str) && H_head) { X free (H_head->line); X H_head->line = salloc (strlen(str)+1); X strcpy (H_head->line, str); X } X} X X X#if 0 X#define CDLEN 20 Xstatic int cd_len=CDLEN, cd_read, cd_write, cd_current; Xstatic char *cd_hist[CDLEN]; X Xadd_cdhist( char *str ) X{ X if( !str ) X return; X if( cd_hist[cd_write] ) X free(cd_hist[cd_write]); X cd_hist[cd_write++]=str; X cd_write%=cd_len; X cd_current=cd_write; X} X Xchar * Xback_cdhist( void ) X{ X if( cd_current!=cd_write ) cd_current= --cd_current % cd_len; X return cd_hist[cd_current]; X} X Xchar * Xforw_cdhist( void ) X{ X if( cd_current!=cd_read ) cd_current= ++cd_current % cd_len; X return cd_hist[cd_current]; X} X#endif X Xvoid XpError(char *str ) X{ X int ierr = (long)IoErr(); X ierror(str, ierr); X} X Xierror( char *str, int err ) X{ X struct PERROR *per = Perror; X X setioerror(err); X X if (err) { X for (; per->errstr; ++per) { X if (per->errnum == err) { X fprintf (stderr, "%s%s%s\n", X per->errstr, X (str) ? ": " : "", X (str) ? str : ""); X return err; X } X } X fprintf (stderr, "Unknown DOS error %d: %s\n", err, (str) ? str : ""); X } X return err; X} X Xvoid Xsetioerror( int err ) X{ X static int LastIoError=-1; X char buf[20]; X X IoError=err; X if( IoError<0 ) IoError=0; X if( LastIoError!=IoError) { X LastIoError=IoError; X sprintf(buf, "%d", IoError); X set_var(LEVEL_SET, v_ioerr, buf); X } X} X Xchar * Xioerror(int num) X{ X struct PERROR *per = Perror; X X for ( ; per->errstr; ++per) X if (per->errnum == num) X return per->errstr; X return NULL; X} X X/* X * Disk directory routines X * X * dptr = dopen(name, stat) X * struct DPTR *dptr; X * char *name; X * int *stat; X * X * dnext(dptr, name, stat) X * struct DPTR *dptr; X * char **name; X * int *stat; X * X * dclose(dptr) -may be called with NULL without harm X * X * dopen() returns a struct DPTR, or NULL if the given file does not X * exist. stat will be set to 1 if the file is a directory. If the X * name is "", then the current directory is openned. X * X * dnext() returns 1 until there are no more entries. The **name and X * *stat are set. *stat != 0 if the file is a directory. X * X * dclose() closes a directory channel. X * X */ X Xstruct DPTR * Xdopen( char *name, int *stat) X{ X struct DPTR *dp; X X IoError=0; X *stat = 0; X dp = (struct DPTR *)salloc(sizeof(struct DPTR)); X if (*name == '\0') X dp->lock = DupLock(Myprocess->pr_CurrentDir); X else X dp->lock = Lock (name,ACCESS_READ); X if (dp->lock == NULL) { X IoError=IoErr(); X free (dp); X return NULL; X } X dp->fib = (FIB *)SAllocMem((long)sizeof(FIB), MEMF_PUBLIC); X if (!Examine (dp->lock, dp->fib)) { X pError (name); X dclose (dp); X return NULL; X } X if (dp->fib->fib_DirEntryType >= 0) *stat = 1; X return dp; X} X Xstatic int Xdnext( struct DPTR *dp, char **pname, int *stat) X{ X if (dp == NULL) return (0); X X if (ExNext (dp->lock, dp->fib)) { X *stat = 0; X if( dp->fib->fib_DirEntryType >= 0) X *stat= dp->fib->fib_DirEntryType!=ST_USERDIR ? 2 : 1; X *pname = dp->fib->fib_FileName; X return 1; X } X return 0; X} X Xint Xdclose( struct DPTR *dp ) X{ X if (dp == NULL) X return 1; X if (dp->fib) X FreeMem (dp->fib,(long)sizeof(*dp->fib)); X if (dp->lock) X UnLock (dp->lock); X free (dp); X return 1; X} X X Xint Xisdir( char *file ) X{ X struct DPTR *dp; X int stat; X X stat = 0; X if (dp = dopen (file, &stat)) X dclose(dp); X return (stat!=0); X} X X Xvoid Xfree_expand( char **av ) X{ X char **get = av; X X if (av) { X while (*get) X free (*get++-sizeof(struct file_info)); X free (av); X } X} X X/* X * EXPAND(base,pac) X * base - char * (example: "df0:*.c") X * pac - int * will be set to # of arguments. X * X * 22-May-87 SJD. Heavily modified to allow recursive wild carding and X * simple directory/file lookups. Returns a pointer to X * an array of pointers that contains the full file spec X * eg. 'df0:c/sear*' would result in : 'df0:C/Search' X * X * Now no longer necessary to Examine the files a second time X * in do_dir since expand will return the full file info X * appended to the file name. Set by formatfile(). X * eg. fullfilename'\0'rwed NNNNNN NNNN DD-MMM-YY HH:MM:SS X * X * Caller must call free_expand when done with the array. X * X * base bname = ename = X * ------ ------- ------- X * "*" "" "*" X * "!*.info" "" "*.info" (wild_exclude set) X * "su*d/*" "" "*" (tail set) X * "file.*" "" "file.*" X * "df0:c/*" "df0:c" "*" X * "" "" "*" X * "df0:.../*" "df0:" "*" (recur set) X * "df0:sub/.../*" "df0:sub" "*" (recur set) X * X * ---the above base would be provided by execom.c or do_dir(). X * ---the below base would only be called from do_dir(). X * X * "file.c" "file.c" "" if (dp == 0) fail else get file.c X * "df0:" "df0:" "*" X * "file/file" "file/file" "" (dp == 0) so fail X * "df0:.../" "df0:" "*" (recur set) X * X */ X Xchar ** Xexpand( char *base, int *pac ) X{ X char *ptr; X char **eav = (char **)salloc(sizeof(char *) * (2)); X short eleft, eac; X char *name; X char *bname, *ename, *tail; X int stat, recur, scr, bl; X struct DPTR *dp; X X IoError = *pac = recur = eleft = eac = 0; X X base = strcpy(malloc(strlen(base)+1), base); X for (ptr = base; *ptr && *ptr != '?' && *ptr != '*'; ++ptr); X X if (!*ptr) /* no wild cards */ X --ptr; X else X for (; ptr >= base && !(*ptr == '/' || *ptr == ':'); --ptr); X X if (ptr < base) { X bname = strcpy (malloc(1), ""); X } else { X scr = ptr[1]; X ptr[1] = '\0'; X if (!strcmp(ptr-3,".../")) { X recur = 1; X *(ptr-3) = '\0'; X } X bname = strcpy (salloc(strlen(base)+2), base); X ptr[1] = scr; X } X bl = strlen(bname); X ename = ++ptr; X for (; *ptr && *ptr != '/'; ++ptr); X scr = *ptr; X *ptr = '\0'; X if (scr) ++ptr; X tail = ptr; X X if ((dp = dopen (bname, &stat)) == NULL || (stat == 0 && *ename)) { X free (bname); X free (base); X free (eav); X return (NULL); X } X X if (!stat) { /* eg. 'dir file' */ X char *p,*s; X for(s = p = bname; *p; ++p) if (*p == '/' || *p == ':') s = p; X if (s != bname) ++s; X *s ='\0'; X eav[eac++] = svfile(bname,dp->fib->fib_FileName,dp->fib); X goto done; X } X if (!*ename) ename = "*"; /* eg. dir df0: */ X if (*bname && bname[bl-1] != ':' && bname[bl-1] != '/') { /* dir df0:c */ X bname[bl] = '/'; X bname[++bl] = '\0'; X } X while ((dnext (dp, &name, &stat)) && !breakcheck()) { X int match = compare_ok(ename,name,0); X if (match && (recur || !*tail)) { X if (eleft < 2) { X char **scrav = (char **)salloc(sizeof(char *) * (eac + 10)); X memmove (scrav, eav, (eac + 1) << 2); X free (eav); X eav = scrav; X eleft = 10; X } X eav[eac++] = svfile(bname,name,dp->fib); X --eleft; X } X if ((*tail && match) || recur) { X int alt_ac; X char *search, **alt_av, **scrav; X BPTR lock; X X if (stat!=1) /* expect more dirs, but this not a dir */ X continue; X lock = CurrentDir (dp->lock); X search = salloc(strlen(ename)+strlen(name)+strlen(tail)+6); X strcpy (search, name); X strcat (search, "/"); X if (recur) { X strcat(search, ".../"); X strcat(search, ename); X } X strcat (search, tail); X scrav = alt_av = expand (search, &alt_ac); X free(search); X CurrentDir (lock); X if (scrav) { X while (*scrav) { X int l; X if (eleft < 2) { X char **scrav = (char **)salloc(sizeof(char *)*(eac+10)); X memmove ( scrav, eav, (eac + 1) << 2); X free (eav); X eav = scrav; X eleft = 10; X } X X l = strlen(*scrav); X eav[eac] = salloc(bl+l+1+sizeof(struct file_info)); X memcpy( eav[eac], *scrav-sizeof(struct file_info), X sizeof(struct file_info)); X eav[eac]+=sizeof(struct file_info); X strcpy( eav[eac], bname); X strcat( eav[eac], *scrav); X X free (*scrav-sizeof(struct file_info)); X ++scrav; X --eleft, ++eac; X } X free (alt_av); X } X } X } Xdone: X dclose (dp); X *pac = eac; X eav[eac] = NULL; X free (bname); X free (base); X if (eac) X return (eav); X free (eav); X return (NULL); X} X Xchar * Xstrupr( char *s ) X{ X char *old=s; X while (*s) *s=toupper(*s), s++; X return old; X} X Xchar * Xstrlwr( char *s ) X{ X char *old=s; X while (*s) *s=tolower(*s), s++; X return old; X} X X/* X * Compare a wild card name with a normal name X */ X Xint Xcompare_ok( char *wild, char *name, int casedep) X{ X int queryflag; X char buf[260], wildbuf[260], *lowname; X X if (queryflag=(*wild=='&')) wild++; X if (*wild=='!') *wild='~'; X X if (! casedep) { X strupr(wild); X strcpy(buf,name); X strupr(buf); X lowname=buf; X } else X lowname=name; X X PreParse(wild, wildbuf); X if ( ! PatternMatch(wildbuf,lowname)) return 0; X X if (queryflag) { X printf("Select %s%-16s%s [y/n] ? ",o_hilite,name,o_lolite); X gets(buf); X return (toupper(*buf)=='Y'); X } X return 1; X} X Xstatic char * Xsvfile( char *s1, char *s2, FIB *fib) X{ X int len=strlen(s1)+strlen(s2)+1; X char *p = salloc (len+sizeof(struct file_info)); X struct file_info *info; X X info=(struct file_info *)p; X p+=sizeof(struct file_info); X strcpy(p, s1); X strcat(p, s2); X info->flags = fib->fib_Protection; X if( fib->fib_DirEntryType<0 ) { X info->size = fib->fib_Size; X info->blocks= fib->fib_NumBlocks; X } else { X info->size = -1; X info->blocks= 0; X } X if( fib->fib_Comment[0] ) X info->flags|= 1<<30; X info->date=fib->fib_Date; X info->class[0]=1; X return p; X} X X X Xstatic FILE *out; Xstatic int NumDirs; X Xvoid Xexpand_all( char *name, FILE *file ) X{ X BPTR lock; X char path[300]; X FIB *fib; X X out=file; X printf( " %s\n", name ); X NumDirs=0; X X if(fib=AllocMem(sizeof(struct FileInfoBlock),0)) { X if( lock=Lock( name, ACCESS_READ )) { X strcpy( path, name ); X exall( lock, path ); X printf( "\n", NumDirs ); X } X FreeMem(fib,sizeof(struct FileInfoBlock)); X } X} X Xstatic int Xexall( BPTR lock, char *path ) X{ X BPTR old, sublock; X int len; X struct FileInfoBlock *fib; X X old=CurrentDir( lock ); X X if( !(fib=AllocMem(sizeof(struct FileInfoBlock),0)) ) X return 1; X X len=strlen( path ); X Examine( lock, fib ); X while( ExNext( lock, fib ) ) { X if( fib->fib_DirEntryType==ST_USERDIR ) X if( sublock=Lock( fib->fib_FileName, ACCESS_READ )) { X if( !len || path[len-1]==':' ) X sprintf(path+len,"%s", fib->fib_FileName); X else X sprintf(path+len,"/%s", fib->fib_FileName); X fprintf( out, "%s\n", path ); X fprintf( stdout, " Directories: %d\015", ++NumDirs ); X fflush ( stdout ); X if(exall( sublock, path )) X break; X path[len]=0; X } X } X FreeMem( fib, sizeof(struct FileInfoBlock)); X CurrentDir( old ); X return dobreak(); X} X X X X/* Sort routines */ X Xstatic int reverse, factor; X Xint Xcmp( FILEINFO *s1, FILEINFO *s2) X{ X return Strcmp( (char *)(s1+1), (char *)(s2+1) ); X} X Xint Xsizecmp( FILEINFO *s1, FILEINFO *s2) X{ X return s2->size - s1->size; X} X Xint Xdatecmp( FILEINFO *s1, FILEINFO *s2 ) X{ X int r; X struct DateStamp *d1=&s1->date, *d2=&s2->date; X if( !(r= d2->ds_Days - d1->ds_Days)) X if( !(r=d2->ds_Minute - d1->ds_Minute ) ) X r=d2->ds_Tick - d1->ds_Tick; X return r; X} X X Xint Xnumcmp( FILEINFO *s1, FILEINFO *s2 ) X{ X return atoi((char *)(s1+1))-atoi((char *)(s2+1)); X} X Xstatic void Xenterclass( FILEINFO *info ) X{ X char *class, *iclass=info->class, *t; X X if( *iclass==1 ) { X if( class=getclass( (char *)(info+1))) { X strncpy( iclass, class, 11 ); X iclass[11]=0; X if( t=index(iclass,0xA0)) X *t=0; X } else X iclass[0]=0; X } X} X Xint Xclasscmp( FILEINFO *info1, FILEINFO *info2 ) X{ X int r; X X enterclass( info1 ); X enterclass( info2 ); X X r= Strcmp( info1->class, info2->class ); X if( !r ) r=Strcmp((char *)(info1+1),(char *)(info2+1)); X return r; X} X X Xvoid XQuickSort( char *av[], int n) X{ X reverse=factor=0; X DirQuickSort( av, n, cmp, 0, 0 ); X} X Xstatic int (*compare)(FILEINFO *, FILEINFO *); X Xvoid XDirQuickSort( char *av[], int n, int (*func)(FILEINFO *,FILEINFO *), int rev, int fac) X{ X reverse=rev; compare=func; factor=fac; X quicksort( av, n-1 ); X} X Xstatic int Xdocompare(char *s1,char *s2) X{ X FILEINFO *i1=(FILEINFO *)s1-1, *i2=(FILEINFO *)s2-1; X int r=(*compare)( i1,i2 ); X X if( reverse ) r =-r; X if( factor ) r+= factor*((i2->size<0) - (i1->size<0)); X return r; X} X X Xstatic void Xquicksort( char **av, int n ) X{ X char **i, **j, *x, *t; X X X if( n>0 ) { X i=av; j=av+n; x=av[ n>>1 ]; X do { X while( docompare(*i,x)<0 ) i++; X while( docompare(x,*j)<0 ) --j; X if( i<=j ) X { t=*i; *i=*j; *j=t; i++; j--; } X } while( i<=j ); X X if( j-av < av+n-i ) { X quicksort( av, j-av ); X quicksort( i , av+n-i); X } else { X quicksort( i , av+n-i); X quicksort( av, j-av ); X } X } X} X X Xint Xfilesize( char *name ) X{ X BPTR lock; X struct FileInfoBlock *fib; X int len=0; X X if( lock = Lock (name,ACCESS_READ)) { X if( fib=(struct FileInfoBlock *)AllocMem(sizeof(*fib),MEMF_PUBLIC)) { X if (Examine (lock, fib)) X len=fib->fib_Size; X FreeMem( fib, sizeof(*fib)); X } X UnLock(lock); X } X return len; X} X X X#ifndef MIN X#define MIN(x,y) ((x)<(y)?(x):(y)) X#endif X Xchar ** Xand( char **av1, int ac1, char **av2, int ac2, int *ac, int base ) X{ X char **av=(char **)salloc(MIN(ac1,ac2)*sizeof(char *) ), *str; X int i, j, k=0; X X for( i=0; i<ac1; i++ ) X for( j=0, str=base ? BaseName(av1[i]) : av1[i]; j<ac2; j++ ) X if( !Strcmp(str, base ? BaseName(av2[j]) : av2[j])) X av[k++]=av1[i]; X *ac=k; X return av; X} X Xchar ** Xwithout( char **av1, int ac1, char **av2, int ac2, int *ac, int base ) X{ X char **av=(char **)salloc(ac1*sizeof(char *) ), *str; X int i, j, k=0; X X for( i=0; i<ac1; i++ ) { X for( j=0, str=base ? BaseName(av1[i]) : av1[i]; j<ac2; j++ ) X if( !Strcmp(str, base ? BaseName(av2[j]) : av2[j] ) ) X break; X if( j==ac2 ) X av[k++]=av1[i]; X } X *ac=k; X return av; X} X Xchar ** Xor( char **av1, int ac1, char **av2, int ac2, int *ac, int base ) X{ X char **av=(char **)salloc((ac1+ac2)*sizeof(char *) ), *str; X int i, j, k=0; X X for( i=0; i<ac1; i++ ) X av[k++]=av1[i]; X X for( i=0; i<ac2; i++ ) { X for( j=0, str=base ? BaseName(av2[i]) : av2[i]; j<ac1; j++ ) X if( !Strcmp(str, base ? BaseName(av1[j]) : av1[j] ) ) X break; X if( j==ac1 ) X av[k++]=av2[i]; X } X X *ac=k; X return av; X} X Xvoid Xclear_archive_bit( char *name ) X{ X struct DPTR *dp; X int stat; X X if(dp = dopen(name,&stat) ) { X SetProtection( name, dp->fib->fib_Protection&~FIBF_ARCHIVE); X dclose( dp ); X } X} X Xchar * Xitoa( int i ) X{ X static char buf[20]; X char *pos=buf+19; X int count=4, flag=0; X X if( i<0 ) X flag=1, i=-i; X X do { X if( !--count ) X count=3, *--pos=','; X *--pos= i%10+'0'; X } while( i/=10 ); X X if( flag ) X *--pos='-'; X X return pos; X} X Xchar * Xitok( int i ) X{ X static char buf[16], which; X char *exp=" KMG", *ptr= buf+(which=8-which); X X do X i=(i+512)/1024, exp++; X while( i>1024 ); X sprintf( ptr,"%d%c",i,*exp); X X return ptr; X} X Xchar * Xnext_a0( char *str ) X{ X while( *str && *str!=0xA0 && *str!='=' && *str!=',') str++; X return *str ? str+1 : NULL; X} X Xstatic int Xgethex( char *str, int l ) X{ X int i, val=0, n, c; X X if( *str=='.' ) return l==2 ? 256 : 0; X X for( i=0; i<l || !l; i++ ) { X c=*str++; X if ( c>='0' && c<='9' ) n=c-'0'; X else if( c>='a' && c<='f' ) n=c-'a'+10; X else if( c>='A' && c<='F' ) n=c-'A'+10; X else break;; X val=16*val+n; X } X return (l && i!=l) ? -1 : val; X} X Xstrwrdcmp( char *str, char *wrd ) X{ X int ret; X char *ind=index(wrd,0xA0); X X if( ind ) *ind=0; X ret=compare_ok(wrd,str,0); X if( ind ) *ind=0xA0; X return !ret; X} X Xint Xwrdlen( char *str ) X{ X char *old=str; X X while( *str && *str!=0xA0 ) str++; X return str-old; X} X Xchar *classfile; X Xchar * Xgetclass(char *file) X{ X CLASS *cl; X char *class, *str, *arg, *get, *buf; X int offs, byte, len, fail; X BPTR fh; X X if( classfile ) { X char buf[80]; X sprintf(buf,"source %s",classfile); X execute(buf); X classfile=0; X } X X if( isdir(file) ) return "dir"; X X if( !(buf=calloc(1024,1))) return NULL; X if( !(fh=Open(file,MODE_OLDFILE))) return NULL; X len=Read( fh,buf,1023); X Close(fh); X X for( cl=CRoot; cl; cl=cl->next ) { X class=cl->name; X if(!(str=next_a0(cl->name))) continue; X while( str ) { X if(!(arg=next_a0( str ))) goto nextclass; X switch( *str ) { X case 's': X if( (offs=strlen(file)-wrdlen(arg))<0 ) break; X if( !strwrdcmp(file+offs,arg)) goto found; X break; X case 'n': X if( !strwrdcmp(BaseName(file),arg) ) goto found; X break; X case 'd': X goto found; X case 'o': X offs=gethex(arg,0); X if( !(arg=index(arg,','))) goto nextclass; X if( offs>len-10 ) break; X for( get=buf+offs, ++arg; (byte=gethex(arg,2))>=0; arg+=2 ) X if( *get++!=byte && byte!=256 ) X goto nexttry; X goto found; X case 'c': X if( !len ) X goto nexttry; X for( get=buf, fail=0; get<buf+len; get++ ) X if( *get<9 || *get>13 && *get<32 || *get>127 ) X fail++; X if( fail*8>len ) X goto nexttry; X goto found; X case 'a': X goto nextclass; X default: X goto nextclass; X } Xnexttry: str=next_a0(arg); X } Xnextclass: ; X } X X free(buf); X return NULL; X Xfound: X free(buf); X return class; X} X Xchar * Xgetaction( char *class, char *action ) X{ X CLASS *cl; X char *cur, *ind; X int len; X X for( len=0; class[len] && class[len]!=0xA0; len++ ) ; X for( cl=CRoot; cl; cl=cl->next ) { X if( strncmp( cur=cl->name,class,len+1 )) X continue; X do X cur=index( cur,0xA0 ); X while( cur && *++cur!='a'); X X if( cur && (cur=index( ++cur,0xA0 ))) { X do { X if( !(ind=index( ++cur,'=' ))) X return NULL; X len=ind-cur; X if( len==strlen(action) && !strncmp(action,cur,len)) X return ++ind; X } while( cur=index(cur,0xA0) ); X } X } X return NULL; X} X Xint Xdoaction( char *file, char *action, char *args ) X{ X char *class, *com, *c, *copy, *spc=index(file,' '); X X if( !(class=getclass(file))) X return 10; X if( !(com=getaction(class,action))) X return 11; X if( c=index(com,0xA0) ) X *c=0; X copy=salloc( strlen(com)+strlen(file)+strlen(args)+7 ); X sprintf(copy,spc?"%s \"%s\" %s":"%s %s %s", com, file, args); X execute(copy); X free(copy); X if( c ) X *c=0xA0; X return 0; X} X Xvoid * Xsalloc( int len ) X{ X void *ret; X X if( !len ) len++; X X if( !(ret=malloc(len))) { X fprintf(stderr,"Out of memory -- exiting\n"); X main_exit( 20 ); X } X return ret; X} X Xvoid * XSAllocMem( long size, long req ) X{ X void *ret; X X if( !(ret=AllocMem(size,req))) { X fprintf(stderr,"Out of memory -- exiting\n"); X main_exit( 20 ); X } X return ret; X} END_OF_FILE if test 21464 -ne `wc -c <'sub.c'`; then echo shar: \"'sub.c'\" unpacked with wrong size! fi # end of 'sub.c' fi echo shar: End of archive 2 \(of 6\). cp /dev/null ark2isdone MISSING="" for I in 1 2 3 4 5 6 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 6 archives. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0 -- Mail submissions (sources or binaries) to <amiga@uunet.uu.net>. Mail comments to the moderator at <amiga-request@uunet.uu.net>. Post requests for sources, and general discussion to comp.sys.amiga.misc.