diff --git a/Applications/Makefile b/Applications/Makefile index 1ec3f9a453..d7f6e1a7c0 100644 --- a/Applications/Makefile +++ b/Applications/Makefile @@ -1,7 +1,7 @@ # APPS = util cmd sh games cave cpm v7games games cursesgames \ as09 ld09 netd MWC flashrom ue cpmfs plato \ - emulators cpnet dw assembler CC cpp ar + emulators cpnet dw assembler CC cpp ar wish .PHONY: $(APPS) @@ -79,6 +79,9 @@ cpp: ar: +(cd ar; $(MAKE) -f Makefile.$(USERCPU)) +wish: + +(cd wish; $(MAKE) -f Makefile.$(USERCPU)) + clean: (cd util; $(MAKE) -f Makefile.$(USERCPU) clean) (cd V7/cmd; $(MAKE) -f Makefile.$(USERCPU) clean) @@ -104,3 +107,4 @@ clean: (cd cpp; $(MAKE) -f Makefile.$(USERCPU) clean) (cd dw; $(MAKE) -f Makefile.$(USERCPU) clean) (cd ar; $(MAKE) -f Makefile.$(USERCPU) clean) + (cd wish; $(MAKE) -f Makefile.$(USERCPU) wish) diff --git a/Applications/wish/Ifdefs b/Applications/wish/Ifdefs new file mode 100644 index 0000000000..b4b38b8683 --- /dev/null +++ b/Applications/wish/Ifdefs @@ -0,0 +1,95 @@ + #defines used in Wish 2.0 + ------------------------- + +$Revision: 41.1 $ $Date: 1995/12/29 02:57:38 $ + +Here are the defines that are used in Wish 2.0. Code that uses specific +library calls and system calls are appropriately ifdef'd. + +DEBUG /* debugging code used in development */ +MALLOCDEBUG /* malloc debugging code used in development */ +MINIX1_5 /* Minix 1.5 specific stuff */ +NOTYET /* code that's not being used, & may never be */ +POSIXJOB /* POSIX style job control */ +PROTO /* Ansi-C function prototypes */ +STDARG /* use */ +UCBJOB /* 4.3BSD style job control */ +USES_DIRECT /* struct direct instead of struct dirent */ +USES_GETWD /* getwd() use instead of getcwd() */ +USES_MORESIG /* sgtty moresig code */ +USES_SGTTY /* bcopy() and sgttyb/TCIOGETP use */ +USES_TCGETA /* termio/TCGETA use */ +USES_TERMIOS /* termios/tcgetattr use */ +V7JOB /* 7th Edition ptrace(1) job control */ +VARARGS /* use */ + +A small amount of code is specific to some systems. The following systems +are defined in the m_*.h files. Only the MINIX1_5 define is used in the +source code. If you are porting Wish to another system, define a name for +the system, but try not to use this when #ifdef'ing code. Use the #define's +above, or create a new define, e.g #define USES_INDEX if using index(). + + +AUX2_01 /* A/UX 2.01 */ +COHERENT /* Coherent */ +FREEBSD_1 /* FreeBSD 1.x */ +FREEBSD_2 /* FreeBSD 2.x */ +GENBSD /* Generic BSD 4.x machine */ +GENSYSV /* Generic SysV machine */ +J386BSD0_1 /* 386BSD-0.1 */ +MINIX1_5 /* Minix 1.5 */ +MINIX1_7 /* Minix 1.7 */ +OSX5_1_ATT /* Pyramid under OSx 5.1a, SysV Universe */ +OSX5_1_BSD /* Pyramid OSx 5.1a, BSD Universe */ +SOLARIS_2 /* Solaris 2.x */ +SUNOS3 /* Sun OS 3.x */ +SUNOS4 /* Sun OS 4.x */ +ULTRIX4_2 /* Ultrix 4.2 */ + +Job Control +----------- + + There are four styles of job control available: 7th Edition Unix +job control (using ptrace), Berkeley job control, POSIX job control and +no job control whatsoever. These are enabled by the following defines, only +one of which should be defined. + +#define POSIXJOB /* POSIX job control */ +#define V7JOB /* 7th Edition Unix job control */ +#define UCBJOB /* Berkeley job control */ + +Argument Passing +---------------- + + The prints() [and friends] functions take a variable number of +arguments. Where possible, argument passing macros should be used. There +are three main types: no argument passing macros, varargs.h style argument +passing and stdarg.h stype argument passing macros. These are enabled by the +following defines, only one of which should be defined. + +#define VARARGS /* varargs.h style argument passing */ +#define STDARG /* stdarg.h stype argument passing */ + + +Miscellaneous +------------- + + There are a few other miscellaneous defines which control things +like types of functions used for directory searches, current working directory +information. + +#define USES_GETWD /* Uses getwd(), not getcwd() */ + +#define PLONG int /* The type of arguments to ptrace(). Minix 1.5 */ + /* uses longs! This is only used when V7JOB is on */ + +#define SIGTYPE void /* Signal handlers return this type. Usually void or */ + /* int, but might also be __sighandler_t */ + +#define USES_DIRECT /* Uses struct direct, not struct dirent */ + +#define PROTO /* The compiler supports Ansi-C prototyped function */ + /* declarations */ + +#define DEBUG /* Debugging code in Wish is #ifdef'd with this. You */ + /* should not need (or want) to turn this on. */ diff --git a/Applications/wish/Installation b/Applications/wish/Installation new file mode 100644 index 0000000000..e8f58e1649 --- /dev/null +++ b/Applications/wish/Installation @@ -0,0 +1,54 @@ + Installation of Wish 2.0 + ------------------------ + +$Revision: 41.4 $ $Date: 2003/04/21 13:13:32 $ + +1) Link the most appropriate file from the list of the m_*.h files to machine.h. + The current machine types are: + + m_386bsd.h 386BSD-0.1 + m_attpyr.h OSx 5.1a, SysV Universe + m_aux.h A/UX 2.01 + m_bsdpyr.h OSx 5.1a, BSD Universe + m_coherent.h Coherent + m_freebsd1.h FreeBSD 1.x + m_freebsd2.h FreeBSD 2.x + m_freebsd4.h FreeBSD 4.x + m_genbsd.h Generic 4.x BSD machine + m_gensysv.h Generic SysV machine + m_minix1.5.h Minix 1.5 + m_minix1.7.h Minix 1.7 + m_solaris.h Solaris + m_sunos3.h Sun OS 3.x + m_sunos4.h Sun OS 4.x + m_ultrix.h Ultrix 4.2 + + Not all of these have been tested recently, only those described in the + `Systems' file. If you get any to work, especially with the changes + required, let me know. + + If there is no m_*.h file for your system, pick one which is close, and + alter it to work: + + POSIX: m_sunos4.h or m_386bsd.h + Old SYSV: m_gensysv.h + 4.3BSD: m_genbsd.h + + Put a unique system #define in the file, and try to #ifdef code with this + define only when absolutely necessary. The file `Ifdefs' has information + about what defines are available in Wish. Please send me a copy of the new + machine file when it works. + +2) Copy the file Std.Makefile to Makefile. Make sure Makefile is writeable. + Edit the Makefile to define how to compile the shell. You should define: + + CFLAGS The compiler flags used when compiling each .c file + LDFLAGS The link flags used when linking the program + CC The name of your compiler + DEFINES Special defines needed during the compilation stage + CLIB Any libraries needed during the link stage + FINAL_TOUCHES Any final touches needed after the linking + + Suggestions for these defines are given in the Makefile. + +3) Run `make' to make the shell. diff --git a/Applications/wish/LICENSE b/Applications/wish/LICENSE new file mode 100644 index 0000000000..1726f18925 --- /dev/null +++ b/Applications/wish/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2016, Warren Toomey +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of Wish nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Applications/wish/Makefile.68000 b/Applications/wish/Makefile.68000 new file mode 100644 index 0000000000..a1afdf7fe9 --- /dev/null +++ b/Applications/wish/Makefile.68000 @@ -0,0 +1 @@ +include Makefile.common diff --git a/Applications/wish/Makefile.common b/Applications/wish/Makefile.common new file mode 100644 index 0000000000..367928f220 --- /dev/null +++ b/Applications/wish/Makefile.common @@ -0,0 +1,23 @@ +include $(FUZIX_ROOT)/Target/rules.$(USERCPU) + +SRC = alias.c bind.c builtin.c clebuf.c clex.c comlined.c exec.c file.c \ + hist.c job.c main.c malloc.c meta.c parse.c prints.c signal.c \ + term.c val.c var.c + +.SUFFIXES: .c .o + +OBJ = $(SRC:.c=.o) + +all: wish + +.c.o: + $(CC) $(CFLAGS) $(CCOPTS) -c $< + +wish: $(OBJ) + $(LINKER) $(CRT0) $^ -o $@ $(LINKER_OPT) -ltermcap$(USERCPU) $(LINKER_TAIL) + +clean: + rm -f *.o wish core *~ *.asm *.lst *.sym *.map *.noi *.lk *.ihx *.tmp *.bin size.report + +rmbak: + rm -f *~ core diff --git a/Applications/wish/README b/Applications/wish/README new file mode 100644 index 0000000000..35bdb06516 --- /dev/null +++ b/Applications/wish/README @@ -0,0 +1,29 @@ + Wish 2.0 - Warren's Interactive Shell + ------------------------------------- + +Welcome to Wish, a sort-of-tcsh-like shell based largely on the Clam shell +written by Callum Gibson. The aims of wish are: + + + to be as small as possible, so as to compile on small + systems like Minix + + to have some of the interactive features of tcsh, such + as command-line editing, tab and ^D expansion, history, + aliases etc. + + to be portable across as many systems as possible, + + to be fast and have minimal impact on the system. + +I worked on this shell from 1991 to 1996 and then gave up to finish off +my Ph.D. This release of Wish is an alpha-release, and I doubt I will ever +get back to finishing it off. It is known to run on FreeBSD 4.7, but I have +not tried it on any other system since 1996. + +The manual is in nroff form in `Wish.1'. Instructions on how to compile wish +are in the `Installation'. If you are porting wish to another system, read +the file `Ifdefs', `Systems' and the `m_*.h' files. + +Anybody who would like to take over development of the shell should email me, +as I can supply some good documentation about the development up to now, and +also a good description of the shell's internals. + + Warren Toomey wkt@tuhs.org 1996, then a bit in 2003. + diff --git a/Applications/wish/Wish.1 b/Applications/wish/Wish.1 new file mode 100644 index 0000000000..3d36a2f725 --- /dev/null +++ b/Applications/wish/Wish.1 @@ -0,0 +1,1216 @@ +."$Revision: 41.2 $ $Date: 1995/12/29 03:23:32 $ +.TH WISH 1L +.nr LL 6i +.SH NAME +wish \- command interpreter and programming shell +.SH SYNOPSIS +.B wish +[ options ] [ script-file ] +.SH DESCRIPTION +.PP +.I Wish +is a shell (command interpreter) which reads commands from either a +file or the terminal and executes them. It executes system commands as well +as builtin commands (see below for description). Main features include: a +history mechanism, enabling previous commands to be easily redone; a +comprehensive command line editor for ease of use from terminal; word completion +and option listing on the command line; full input and output redirection and +aliases. These features are described in detail below. +.PP +This manual describes internal version 41 of Wish 2.0. +.PP +Wish is derived from the Clam shell, written by Callum Gibson, and is in some +sense a partial rewrite of Clam with some new features added and some old +features removed. Many thanks to Callum for allowing me to use his source, +and for his suggestions and criticisms during Wish's development. +.".SH OPTIONS +."The shell may be invoked with a number of options: +.".TP 4 +.".B \-c +." \- the following arguments are passed to Wish for execution. +.".TP 4 +.".B \-e +." \- exit shell if any command returns a non-zero exit status. +.".TP4 +.".B \-f +." \- fast start up. Do not read the .wishrc file. +.".TP 4 +.".B \-p +." \- parse the commands but do not execute so that checking can be performed +."on shell scripts. +.".TP 4 +.".B \-v +." \- verbose mode. Commands echo back in same form as saved to history +."prior to execution. No metacharacter expansion is done (*,?,{},[],``) +."before the line is echoed. +.".TP 4 +.".B \-x +." \- similar to verbose mode except that commands are echoed back in expanded +."form. +.".TP 4 +.".B \-V +." \- Enter verbose mode even before .wishrc is read. +.".TP 4 +.".B \-X +." \- \-X is to \-x as \-V is to \-v +.PP +.SH Basic Command Line Parsing +.PP +.PP +The command line, whether read from stdin or file, is broken up into +.I words. +Words are sequences of characters which are separated by +word-delimiters (the most obvious example is a +.I space). +The following characters have effects on the words of the +command line and act as word delimiters: +.PP +< +.I filename. +Standard input redirection. Instead of reading input from the +terminal, input is read from the named file (the following word). e.g. +.PP +% prog < inputfile +.PP +> +.I filename. +Standard output redirection. Instead of outputting to the +terminal screen, output is redirected to the named file. e.g. +.PP +% prog > outputfile +.PP +.PP +n> +.I filename. +Other output redirection. Instead of outputting to the +terminal screen, output from the given file descriptor +is redirected to the named file. e.g. +.PP +% prog 5> outputfile +.PP +2> Standard error redirection. The default is the terminal screen, but instead +standard error output is directed to the named file. e.g. +.PP +% program 2> errfile +.PP +[n]>> +.I filename. +As above but the named file is appended to, not erased first. +.PP +| Pipe output of one program to input of another. A series of programs joined +by pipes is called a +.I pipeline. +A pipe joins the standard output +of the first program to the standard input of the second program. e.g. +.PP +% prog1 | prog2 +.PP +& background. When this symbol appears, it runs the previous command in +the background. A number indicating the process id and Wish job number +(job control only) is returned and the prompt is given immediately for the next +command. +.PP +; semicolon is used to separate pipelines. A semicolon can be typed instead +of a newline so that a new command or pipeline can begin. The effect is +exactly the same as a newline and thus it has the highest priority of these +metacharacters. +.".PP +."&& The double ampersand separates two commands (or command pipelines) and +."specifies that the latter should only be executed if the former returns a zero +."status (i.e. terminates successfully). +.".PP +."|| The double pipe is similar to the double ampersand except that it executes +."the latter if the former returns with +.".I non-zero +."status. +.PP +There are valid combinations of these, too, although it is illegal to +redirect output to two places, for example. +Some other characters also have special meaning and these may cause the words +to be interpreted in different ways. Ordinarily the words become the arguments +of an executing program. +." We don't have backslash as yet, sigh. +."To use any of these characters without invoking their +."special meaning, it is necessary to use the backslash character `\\' which +." causes the shell to ignore them and treat them as part of a word. +Most of +these "metacharacters" will also be ignored inside double quotes `"' and +all (except history substitution commands using !) are ignored inside single +quotes `''. (See the section titled "Quoting") +.PP +.SH Job Control +.PP +.PP +If job control is included, Wish +allows the user to have control over their processes or +.I jobs. +Whenever a process is run it is given a job number. This applies to +all background jobs and the current foreground one if it exists. This number +can then be used to identify a process for use with bg or fg. +The list of all currently active jobs can be displayed with the +.I jobs +command. +The +.I bg +and +.I fg +commands allow jobs to be placed into the background (where +the prompt is returned but the command still runs) or the foreground (where +the user is in an interactive state with the process). The stop signal can be +generated by typing `^Z' while a foreground job is running. This has the effect +of stopping that process immediately, which may be restarted with either the bg +or fg command. +." I don't think we have ^Y yet. +." A foreground process may also be stopped by typing `^Y' which +." delays stopping until the input buffer is emptied. +Note that bg and fg always +refer to the current (most recent) job +by default. For further explanation, see the "Builtin Commands" section below. +.PP +Background jobs are allowed to write to the terminal by default but not +read from it. (The shell places a job in the background by disassociating it +with the control terminal's process group. See +.I setpgrp(2)). +.PP + The +.I stty(1) +"tostop" mode can be used if output from background jobs is to be disallowed. +This mode will cause background jobs to stop if a write to the terminal is +attempted. Reads from the background are always disallowed. +The user is notified of a processes status after return is pressed +and before the next prompt appears. This gives information about completion +or stoppage of background jobs. +.PP +Those people using +.B Minix +should use +.I stty(1) +to redefine their `quit' key from ^\\ to ^Z, as there is a primitive +form of job control available when +.I Wish +is compiled with #define V7JOB. This doesn't always work, but most of +the time you can ^Z, bg and fg. +.PP +Job control under Posix systems is still slightly broken; I haven't +had the time to sit down and grok the full implications of the Posix +rationale for job control. +.PP +.SH Command Line Editing (CLE). +.PP +.PP +This feature allows the use of control keys and meta-character key sequences +to perform editing operations upon the current line. This is done by using +"cbreak" terminal mode (see +.I stty(1) +) which causes an interrupt to be generated after each character is typed and +hence available for input, rather than just at the end of a line when return is +typed. Wish also turns echoing off so +that it can position the characters properly +and effect autowrap, control character representation, etc. These two modes +are reset when a foreground job is executed. Other modes are settable by the +user and not changed by Wish. +.PP +European users will be happy to know that the CLE is now 8-bit transparent. +For users with 7-bit keyboards, the CLE now has a command to produce +8-bit keystrokes. When 8-bit characters cannot be displayed on the screen, +turn the Wish variable `MSB' on; 8-bit characters will then be displayed +in bold. +.PP +The Command Line Editor provides many editing commands. The following +table lists the possible commands. The three columns hold: the default +key binding for the command, the internal key value for the command (usually +the same as the key binding), and a brief description of the action. Most +actions are obvious; the complex ones are described below. +.PP +.in +2 +.nf +KEY VALUE ACTION +=== ===== ====== +^@ \\000 save current position +^A \\001 goto start of line +^B \\002 go backwards one character +^C \\003 interrupt - discard line and start again +^D \\004 delete current char, list options, or exit shell +^E \\005 goto end of line +^F \\006 go forward one character +^G \\017 kill whole line +^H \\010 back-delete one character +^I TAB \\011 complete current word +^J LF \\012 finish line (same as return) +^K \\013 kill from cursor to end of line +^L FF \\014 clear screen and redisplay line +^M CR \\015 finish line (same as linefeed) +^N \\016 step forward in history +^O \\017 toggle insert/overwrite mode +^P \\020 step backward in history +^Q \\021 resume tty output +^R \\022 redisplay the line +^S \\023 stop tty output +^T \\024 transpose the current and previous chars +^U \\025 accepts number and key to perform num times +^V \\026 next character to be read literally +^W \\027 delete word backwards +^X \\030 goto saved position +^Y \\031 yank the previous word into a buffer +^Z suspend process (no effect upon CLE) +^[ ESC prefix for meta-commands +^\\ \\034 beep +^] \\035 turn CLE mode bit on (see bindings below) +^^ \\036 turn CLE mode bit off (see bindings below) +^_ \\037 turn the following character's msb on +SPC../ insert that character +0..9 insert the character or read digit (^U) +:..~ insert that character +^? DEL back-delete character (same as ^H) +ESC-^P \\201 match previous partial command +ESC-b +ESC-B \\202 skip backwards one word +ESC-d +ESC-D \\203 delete word forwards +ESC-f +ESC-F \\204 skip forward one word +ESC-h +ESC-H \\205 get help on word +ESC-p +ESC-P \\206 insert buffer onto the line +ESC-y +ESC-Y \\207 yank the next word into a buffer +ESC-/ \\210 search forward for next typed character +ESC-? \\211 search backwards for next typed character +.in -2 +.fi +.PP +Note that some keys which normally generate signals (e.g. ^Z, ^Y, ^\\) +do not do so whilst a line is being entered/edited. Their signal generating +status is returned whenever a foreground job is in progress. +.PP +.SH Complex CLE Functions +.PP +.PP +Some of the CLE commands are complicated. Their full description is given here. +.PP +^D Delete char, list options, leave shell +.PP +This command has three functions. If you use the command at the beginning of +a line, you will exit the shell. If your cursor is on top of a character, +that character will be deleted. Finally, if your cursor is at the end of the +line, a list of `expansions' will be shown. This command will show files, +programs you can run, user's home directories and variables, depending upon +how you use it. +.PP +For example, to see a list of files in your current directory, you can do +.PP +.nf + % word ^D +.fi +.PP +To see all the files starting with `s', do +.PP +.nf + % word word s^D +.fi +.PP +The word can be a relative or absolute path name; to see the files starting +with `std' in the include directory, and the parent directory, do +.PP +.nf + % word word /usr/include/std^D + % word word ../std^D +.fi +.PP +If the word is the first on the line, aliases, builtins and programs in +your PATH are listed. To see all the commands, and all the commands starting +with `cl' do +.PP +.nf + % ^D + % cl^D +.fi +.PP +If the word begins with a `$', the command will give a list of variable names; +to see all the variable names, and all starting with `H', do +.PP +.nf + % word word $^D + % word word $H^D +.fi +.PP +If the word begins with a `~', the command gives a list of users. Examples: +.PP +.nf + % word word ~^D + % word word ~ro^D +.fi +.PP +Words with `/' signs in them will be expanded internally to their pathname +equivalents. The word `~#/' means the list of aliases and builtins. +For example, to list the files in fred's directory, in $HOME, and the +aliases and builtins, do +.PP +.nf + % word word ~fred/^D + % word word $HOME/^D + % word word ~#/^D +.fi +.PP + Filename Completion +.PP +Filename completion is closely associated with the list options function. If +you use instead of ^D, the word on the command line is completed up +to the first duplicate letter. For example, if you have two files 'bigfile.c' +and `bigfile.o' in the current directory, then +.PP +.nf + % ls bi becomes + % ls bigfile. +.fi +.PP +Words with only one match are fully completed. This function understands the +same words as the list options functions, so you can complete variables, +usernames, programs, and files in directories like +.PP +.nf + % ls $HOME/.cl becomes + % ls $HOME/.wishrc +.fi +.PP +Quoting Commands +.PP +The CLE provides commands to quote characters instead of them performing +functions. The first is ^V. For example, to insert a ^A on the line without +^A doing its usual action, do +.PP +.nf + % word word ^V^A +.fi +.PP +The ^_ command works exactly the same, but also turns the character's +most significant bit on, so 8-bit keystrokes can be entered from a 7-bit +keyboard. +.PP +^U Repeat Keystroke +.PP +^U is followed by a decimal number (which isn't displayed), and one character. +That character is repeated number times. +.PP +ESC-^P Match Partial Command +.PP +This command only works for the first word. When the cursor is at the end of +the first word, and this command is done, the last command that matched +the word is added to the line. For example: +.PP +.nf + % ls -l + % wc file + % echo hello + % lESC-^P becomes + % ls -l +.fi +.PP +Word Commands +.PP +The word commands are affected by the Wish variable wordterm. This can be set +to holds the characters that `end' a word. If this variable isn't defined, +the CLE assumes it holds the following characters: space, tab, > < | / ; = +& and `. +.PP +.SH Bindings +.PP +.PP +Wish now distinguishes between the commands available in the CLE and the +keystrokes that cause the commands to occur. This allows Wish to have +different editor bindings. For example, Wish defaults to an emacs-like +binding, as noted above, but can be given vi-like bindings by sourcing +the file Vibind. +.PP +The command characters given in the above table are used by the Wish +.B bind +builtin command, which can bind a keystroke sequence to another keystroke +sequence. With the default emacs-like style, most of the emacs keys are +identical to the internal CLE commands, and only the ESC commands are +internally bound by Wish. Setting up another editor style involves the use +of the `bind' and `unbind' builtins described in the Builtin Commands +section. +.PP +The CLE also offers a boolean `mode'. +When the mode is on, all current key bindings apply. However, when the mode +is off, key bindings marked as `mode only' are disabled. This allows the +vi-style set of bindings to be used. +.PP +The help command calls the system manual command with the word immediately +preceding the cursor as the argument. +.PP +." The next history command (^N) will attempt to predict the user's next command +." if it reaches the end of the history list (i.e. when nothing has been typed +." on the current line, yet) by looking for the most recent occurrence of +." the previous command (not +." including the previous command itself) and then loading the command +." .I after +." that onto the command line. This may be set up to occur automatically by +." setting the TARDIS variable (see Special Variables section). This enables +." the user to set up a loop of commands. A beep sounds if the shell is unable to +." predict what command is next (if the previous command never occurred before, +." for instance). +." .PP +." .nf +." % comm1 +." % comm2 +." % comm3 +." % comm1 +." % ^N --> % comm2 +." .fi +." .PP +.SH Variables. +.PP +.PP +The shell variable facility relies on the following metacharacters: +.PP +$ denoting a variable. Wish will interpret $ as a reference to a +shell variable. +The word immediately adjacent to the `$' is taken +as the variable name. This word is delimited by any of the metacharacters above, +by quotes, by another `$', or by an array subscript. e.g. +.PP +% echo $fred +.PP +This will echo the contents of the variable fred. +." , while this: +." .PP +." % echo $fred[2] +." .PP +." will echo the contents of the third field of fred. Note that array subscripts +." start from zero. Any access to a nonexistent field will return a null word. +.PP += assignment of variables. Variables are assigned simply by stating the name(s) +of the variable(s), followed by the equal `=' sign, and then the value(s). +." It is possible to assign two or more variables simultaneously. e.g. +." .PP +." % a b = x y +." .PP +." This statement assigns 'x' to $a and 'y' to $b. Any left-over words on the +." right hand side of the equals are assigned to the last variable listed on +." the left hand side. Similarly, any left-over variable names (on LHS) are +." assigned null values. Note, however, that this assignment is not truly +." concurrent (at this stage). (i.e. assignment is done left to right, not as +." if it all happened simultaneously. Therefore, variable swapping is not +." possible with only two variables. "a b=$b $a" would set both "a" and "b" to the +." original value of "b". +." .PP +." += appending to a variable. Variables can also be appended to with the `+=' +." operator. This works in the same way as the `=' but does not destroy the old +." value of the variable. To append to a variable, the variable must have existed +." previously. +.PP +Wish is responsible for two separate sets of variables. The examples given +above refer to +.I internal +shell variables. These variables are immediately +accessible to the user and also act as flags for some Wish functions +(e.g. HASH,ignoreeof,UNIV). The other set is known as +.I environment +variables. +Environment variables are passed to any new process by the execve system call. +When the shell is started all environment variables are loaded into the +shell's internal variable storage space. +When this initialisation occurs, +these variables are automatically marked for +.I export +, meaning that when execution of a command +takes place, the variables marked for export form the environment for +the executed command. +It is also possible to mark any variable for export by +using the +.I export +command described below. +.PP +." While there is no +." .I set +." command, variables can be deleted using the +." .I unset +." command. Also +." .I unsetenv +." can be used to delete environment variables. If you +." don't want all of the environment variables cluttering up your internal +." variable list, there is no harm in deleting them but they will not be +." re-exported. To reload the environment variables, simply use the command: +." .PP +." e.g. % PATH=$PATH +." .PP +." since the environment is searched for the variable if it does not exist in the +." internal variable list. +." .PP +." A new feature found only in Wish is the +." .I edit +." command which allows the user +." to edit a variable. This is particularly handy for long variables such as PATH +." and TERMCAP in which only a small component has to be altered. Editing is done +." using the command line editor features described above. +.PP +Wish also has several inbuilt variables: +.PP +.nf + $$ - The process-id of the process + $? - The exit status of the last process + $# - The number of arguments to the process + $n - (where n is a digit) The n'th argument to the process. + $* - All the arguments to the process (except $0) +.fi +.PP +When used interactively, these refer to the Wish itself. However, in +aliases and Wish scripts, these variables refer to the alias or script. +For example, if `prog' is a Wish script, and you type +.PP +% prog foo bar +.PP +then from within "prog", $0 will be "prog", $1 will be "foo" and $2 will be +"bar". Similarly, an alias such as +.PP +% alias l '/bin/ls -lg $*' +.PP +will do '/bin/ls -lg' on all of its arguments. +.PP +.SH History and History Substitution. +.PP +.PP +The history mechanism enables every command typed to be stored and accessed +at a later time. To get a list of previous commands use the +.I history +command. Wish does not enter the +.I history +command into the history, nor does +it store single letter lines since these can be more easily retyped than +by using history substitution (described below). The number of commands +remembered by Wish is set by the internal "history" variable. +.PP +It is possible to recall a previous line(s) from the history by using the +metacharacter `!', followed by the absolute history number, a relative history +number, a string which matches the most recent command starting with the +same string, or another `!' which refers to the previous command. e.g. +.PP +% !12 fred +.PP +This will substitute the history event numbered 12 onto the line at that +position and the argument "fred" will remain as an argument to that command. +.PP +% !-3 +.PP +This will substitute the event from three commands ago. It is still possible +to have an argument or a different substitution on the same line. +.PP +% !fo +.PP +This will substitute the most recent command starting with "fo". +.PP +You can also append characters onto a previous history command rather than +adding a new word. If we issued a command "ls -l" and then used "!la" the +shell would search for a command starting with "la" instead of finding "ls -l" +and appending the "a". So, to achieve this, simply quote the characters that +are to be appended (actually you only need quote the first). Hence, +.br +% !l"a" +.br +will give the desired substitution of "ls -la". Spaces may be included in +the string by using backslash or quotes. +.br +% !"v d" + +." .PP +." Another form of substitution exists which operates only on the previous +." line as an error correction mechanism. Although this is mostly superseded by +." the history pullback (^P) and command line editor operations, it was included +." for completeness. If a line starts with a `^' and has the format: +." .PP +." % ^string1^string2 +." .PP +." then string1 is replaced by string2 in the previous command line. +." .PP +." .B Command Substitution. +." .PP +." The +." .I output +." of a command can be substituted into the command line by using +." .I backquotes +." to surround the command. e.g. +." .PP +." % d=`date` +." .PP +." This command causes the system program +." .I date(1) +." to be executed, and the resulting output is loaded (word by word) into the +." line to be parsed. +." .I Words +." are sequences of non-blank characters. A +." .I blank +." is a space, tab or newline. The "blank" characters are all interpreted as +." spaces when loaded into the line, however, the output of a program in +." backquotes can be preserved as a single argument by surrounding the backquotes +." with double quotes (""). Single quotes would cause the backquotes to remain +." uninterpreted, however. +.PP +.SH Filename Substitution. +.PP +.PP +Rather than type a lot of filenames, sometimes it is possible to specify +a group of files using the "globbing" technique. This means that using the +following metacharacters, you can specify files in any directory using a +sort of shorthand notation. The shell then performs what is called "pattern +matching" to produce the file names wanted. +.PP +* represents any number of characters including zero. +.br +? represents any one character. +.br +[] encloses a list of characters that can match. Two characters may also +have a hyphen separating them meaning "up to". So [A-Za-z] includes the +letters 'A' to 'Z' and 'a' to 'z'. +.PP +e.g. ab* matches all files in current directory starting with "ab". +.br +/usr/bin/lo*.? matches all files in /usr/bin starting with "lo" then +ending with a "." and any other character. +.br +[abc]*.c matches all the C-files in the current directory starting with +an "a", "b", or "c". +.PP +It is also possible to specify +.I subdirectories +using pattern matching e.g. +*/*.c matches all C-files in any subdirectory. +.PP +A further abbreviation method is available for referring to the home +directories of users. This is done by typing a `~' followed by a +.I username +or if no username is given then the home directory of the user running +the shell is used (i.e. ~ refers to your directory). +.br +e.g. "~cgibson" will be replaced by "/u1/hons/cgibson" +.br +"~mike/work" will be replaced by "/u1/staff/mike/work". +.PP +Wish now fully copes with systems that uses yypasswd. +." .PP +." It is also possible to abbreviate by using the {} metacharacters. Actually, +." only the `{' is recognised as a metacharacter. This can be achieved by +." placing a set of strings separated by commas `,' inside the curly braces. +." Note that the shell blindly expands without checking for existence of files +." (no matching done) and thus this command can refer to words other than +." filenames. +." .PP +." e.g. % ls {a,b,c}*.c +." .PP +." This will once again give the listing of C-files in the current directory +." but the difference is that before pattern matching takes place, the line +." is expanded to: "ls a*.c b*.c c*.c" and hence is less efficient since the +." shell must match 3 separate patterns. The following example shows another +." use of the curly brace expansion. +." .PP +." % echo {0,1}{0,1}{0,1}{0,1}{0,1}{0,1}{0,1} +." .PP +." This will echo the representation of the binary numbers from 0 to 127 +." because left to right order is preserved. +." .PP +." So, it is possible to combine any number of these metacharacters *,?,[],{},~ +." to save +." typing, but note that the more complicated the pattern, the longer the shell +." will take to expand it, particularly if many subdirectory levels are involved. +." Also, if many different sets of braces are given in the one word then the +." expansion becomes large and may not even fit on a line. For example, the above +." example using {0,1} has two options in each set of braces and seven sets of +." braces, resulting in two to the power of eight (128) different words. +.PP +.SH Quoting. +.PP +.PP +There are three types of quotes (not including backquotes) which may be used to +stop the above mentioned metacharacters from being interpreted. +." The most +." powerful is the +." .I backslash +." `\\' which only quotes the next character. +." This will quote absolutely anything except +." .I null +." (^@) which may not be quoted since it represents the end of a line +." (null-terminated strings. See +." .I string(3) +." ) and can't be typed, anyway. The next set of quotes, called +Single quotes '' will stop all interpretation except history +substitution (!). +." It is necessary to use the backslash here if the actual +." exclamation mark character is intended. +The last set of quotes is double quotes. +They allow history and variable substitutions but disallow pattern matching. +Note that quotes may be nested but only the outer set have any effect. +." The exception is the use of the +." .I backslash +." when quoting the `!' and when quoting +." the `$' and ``' in double quotes. +.PP +.SH Aliases - User defined Command Sequences +.PP +.PP +It is possible for the user to define their own set of aliases. Unlike +.I Csh(1) +where the first word of any command is checked to see if it's an +alias, and then the alias is substituted onto the line, Wish allows full +shell scripts to be defined which are then executed by the shell after +parsing the line. These aliases are stored internally and executed in +a similar way as a Wish script. Thus it is possible to pass arguments as +$1 $2 etc. or $* for all arguments. In fact, the aliases should behave exactly +as a script stored in the user's directory but without taking up directory +space. Remember that the more aliases that exist, the more slowly commands +will run. +." This problem is greatly reduced in Wish compared with +." .I Csh +." if +." .I hashing +." is on, because the builtins and aliases are hashed from the path along +." with all the other commands and so access to them is still quick via this +." method. The only reason for not using hashing would be if the hash table got +." too full (too many files on your PATH) or process space was needed on the +." system. +." Also using the `~#' metanotation, you can specify explicitly to use a builtin +." or alias if they occur lower down in the path then a system command of the same +." name. e.g. +." .PP +." % ~#/list +." .PP +." This will execute the builtin command +." .I list +." rather than the system command +." .I list(1). +Because of the way that these aliases are implemented, it is easy to +call builtins, system commands, and even other aliases, from within an alias. +You should be careful not to call an alias from within itself since this will +cause an error. Aliases can be created using the builtin command +.I alias +, see below in Builtin Commands section. +.PP +.SH Builtin Commands +.PP +.PP +This section describes the commands that are builtin to the shell. +Builtins are only executed in a subshell if they are at the start or in the +middle of a pipeline or run in the background. +." They exist in a virtual directory +." ~# and can be referred to in this way. See the Aliases section above for more +." information. +.PP +This section is divided up into several subsections, because some +builtins can be left out if they cannot be used or are not needed. +To find out which builtins you have, do +.PP +.nf + % ~#/^D +.fi +.PP +This gives you a list of aliases and builtins. +.PP +.B 1. Standard Builtins +.PP +.TP 4 +." alias [ [ -e -l -s] [ ] ] +alias [ [ -e -s] [ ] ] +When invoked with no argument this command lists the aliases +currently defined. +." If the -e flag is given then an editor (default is .I vi(1)) +." is invoked upon a temporary file and this allows the user to create their +." Wish script which is loaded into the shell as an alias upon completion. This +." is needed if +." multi-lined aliases are to be defined since Wish's command line editor will not +." allow newlines to be displayed and inserted. +The -l flag instructs Wish to load +up the alias from the named file (the alias is given the same name). In the same +way -s causes Wish to save the alias to file. If none +of these flags appear, then the first argument is taken as the alias name, and +the remaining arguments as the alias definition. If there are no other +arguments, that alias's definition is printed. +.TP 4 +bind [[-m] keysequence [newkeysequence]] +This command allows the user to bind a series of keystrokes to a new series +of keystrokes. Note that most ASCII control characters (\\000 to \\037), and +some other control characters (\\201 to \\211) cause the CLE to perform a +function, so most bindings are to one or more of these characters. +With no arguments, +bind +shows the current set of key bindings. With a keysequence argument, +the binding of +the given sequence is shown. With a keysequence and a value, the sequence is +bound to the action. For example: +.PP + % bind '^[[A' ^P +.PP +binds the up-arrow key to the `step backward in history' action. +Bind +also understands C-like octal sequences, so that +.PP + % bind '^[[A' \001hello\005 +.PP +will cause the up-arrow to insert the word `hello' at the beginning of the +line. +.PP +If a keysequence is bound using the `-m' option, then the binding is marked +`mode-only', and is only invoked when the CLE is in MODEON mode. Otherwise +it is ignored. +.PP +Note that the CLE uses the first match found when parsing keyboard input. +For example, if the current bindings are: +.PP + % bind + ^[[A is bound to ^P + ^[[B is bound to ^N + ^[[Aa is bound to ^A + ^[[B is bound to ^E +.PP +then the keyboard input `^[[Aa' will do the command ^P (i.e. go back in +history), and then +add an `a' to the command line; similarly, the input '^[[B' will do the +command ^E (i.e. go to the end of the line). +.PP +Key bindings can be recursive. A large error will occur if you do +.PP + % bind a aa +.PP +as the next time you type `a', it will become an infinite number of a's, +up to the size of the CLE line buffer. +.TP 4 +cd [ ] or chdir [ ] +This command changes the current working directory of the +current shell process. Thus, if cd occurs inside a child process, when that +process dies, the parent will not have changed directory. Cd takes a single +argument which is the name of the directory to change to. The directory name +may be a full path name starting from the root directory or a path name +relative to the current directory. The `~' notation can also be used to refer +to the home directory of users. +.TP 4 +echo [-n] +Echo writes the given arguments out on a single line on standard output. A +newline is appended unless the -n option is given. +." .TP 4 +." edit +." Edit takes one argument which is a variable name that is +." to be edited. The editor used is the command line editor. After completion of +." the editing sequence, press return and the variable is saved. Note variables +." must exist prior to editing them. (See also Command Line Editing section). +." .TP 4 +." exec [ ] +." Replace the current shell with a different program which is +." supplied as arguments. All arguments are passed to +." .I execve(2). +." .TP 4 +." exit [ ] +." Causes the termination of the shell (unless it is the +." login shell). If a numerical argument is given then the shell exits +." with that argument. Default status is zero. +.TP 4 +export [ [ - ] ] +mark a variable for export, or if `-' flag given, unmark +export ability for that variable. When a variable is marked for export, it +means that the internal copy of the variable is transferred to the environment +whenever a program is executed, making the new copy available to the new +process. +.TP 4 +history [ m[-n ] ] +Lists the previously executed commands remembered by the shell. An optional +range can be specified which will list the commands numbered from +.I m +to +.I n. +A single number argument will display the m most recent commands. +.TP 4 +list [ env ] +List the internal variables defined in Wish. With the "env" option, the +environment variables are listed (like +.I printenv(1)). +." .TP 4 +." load +." Load the arguments supplied onto the next command line. Can be used to +." load up alias definitions for example if they need to be slightly altered, +." or to check a history substitution (or other substitution) without automatically +." executing the line. Allows a second carriage return for confirmation. +." .TP 4 +." lock +." Lock the keyboard. User is prompted for a password which must be retyped +." to confirm it. The shell enters "lock" mode and this mode can only be exited by +." once again typing the correct password. +." .TP 4 +." logout +." The command used to logout from the shell and hangup from +." the system. This command only has effect from the lowest shell level (i.e. +." the login shell). +." .TP 4 +." repeat +." This statement causes the command to be executed the number of times specified. +.TP 4 +source [ ] +Cause the Wish script to be executed in the current shell, as if the contents +of the file had been typed from the keyboard. This is useful if the environment +variables are to be set for the shell. (e.g. if you have a script that changes +your TERMCAP or PRINTER variable etc.) This script could also be loaded as an +alias with "alias -l". +.TP 4 +tilde [-l], tilde [[shorthand] [dirname]] +Wish keeps an internal list of shorthand names for directories. Tilde adds +the given shorthand/dirname pair to the list. From then on, you can use +~shorthand to mean the dirname. If just shorthand is given, the expansion +for that shorthand is given. With no arguments, all the internal pairs are +given. The -l argument will also show the username/directory pairs from +the /etc/passwd file. +.TP 4 +unalias ... +Delete the named alias if it exists. If not an error message is returned. +.TP 4 +unbind keysequence +Unbinds the given key sequence, if that sequence has been bound. +.TP 4 +untilde +Removes the shorthand name/directory pair from the internal list of +directory shorthands. +unset ... +Delete the internal variable named. If an environment variable of the same +name exists, it will be deleted. +.TP 4 +unsetenv ... +Delete the environment variable named. This command should be used with +caution since some system programs use the environment variables. The +shell with still have an non-exported copy of the variable. +." .TP 4 +." which [ -o ] [ -O ] +." In its basic form, tells you the full pathname of the file you execute by +." typing . With -o, aliases are ignored and with -O aliases and builtins +." are ignored. +.PP +.B 2. Job Control Builtins +.PP +.TP 4 +bg [ % ] [ ] +This command has the effect of starting the current job (which will be +stopped) in the background if given no arguments. If given arguments it starts +the specified jobs in the background. The arguments are simply the job numbers +which have been assigned by the shell. These can be obtained using the +.I jobs +command. +.TP 4 +fg [ % ] [ ] +Cause the current job to execute in the foreground +whether stopped or executing in the background. If an argument is supplied +it is taken as the job number (as described in +.I bg +and Job Control section). +.TP 4 +jobs +Give a list of current jobs (processes) recognised by +Wish. These jobs are the either running in the background or are stopped. +The shell cannot keep track of processes that result from a +.I fork(2). +In the att universe, job control is severely restricted. (See Job Control +section). +." .TP 4 +." kill [ - ] % [ or ] ... +." Kill the specified job number or process id (pid) with the optionally +." specified +." .I signal. +." Signals are the same as those defined in +." .I sigvec(2) +." (ucb) or +." .I signal(2) +." (att) but with the prefix "SIG" removed. If the signal is unspecified, then +." the SIGTERM signal is sent. If the SIGTERM and SIGHUP signals are sent the +." the shell will send the +." SIGCONT signal as well, which is needed if a stopped job is to receive the +." signal. +." .TP 4 +." stop [ % ... ] +." Stop the current (or named) job that is running in the background. Jobs may be +." restarted using either +." .I bg +." or +." .I fg. +." .TP 4 +." suspend +." Cause this shell to suspend or stop. Control is returned to an outer level +." but the shell will remain in a stopped state (receives a stop signal, like +." typing ^Z). +." .TP 4 +." wait +." Cause the shell to wait for all background processes. The wait can be +." interrupted (^C) at which stage the shell prints a list of all jobs +." currently outstanding. +." .PP +." .B 3. Hashing Builtins +." .PP +." .TP 4 +." hashstat [ x ] +." Display an analysis of the internal hashing table. +." With no arguments, hashstat returns the actual hits and misses whenever the +." shell tries to execute a program and also a hit ratio. With the single argument +." "x", Wish returns the hits and misses encountered when creating the hash +." table, an expected hit ratio, and how full the table is. +." .TP 4 +." rehash +." This causes the files on the user's path to be entered into a hash table. +." If hashing was previously turned off, then it is turned on with this command. +." You will need to do this if a new executable file appears in a directory on +." your path. +." .PP +." .B 4. Non-Minix Builtins +." .PP +." .TP 4 +." help [ ] +." Help gives some additional information about the previous error that occurred, +." or if supplied with an internal error number, explains that particular error +." in more detail. +." .PP +." .B 5. Shell Script Builtins +." .PP +." Currently case, for, if, repeat, shift, and while exist as stub builtins, i.e +." they do nothing. It is planned to give Wish a scripting language as close +." as possible to sh(1). The only written shell script builtin is read. +." .PP +." .TP 4 +." read [ ... ] +." Read from the standard input until an end of line is reached. The characters +." that were read are assigned to the named. If more than one variable +." is to be read, the first word of the input is assigned to the first variable; +." the second to the second respectively; the final variable is assigned any left +." text. If there are more variables than words read, remaining variables are +." assigned null strings. Note this is logically equivalent to variable assignment. +." (see Variables.) +.PP +.SH Special Variables. +.PP +.PP +This section contains a description of the variables which are used by the +shell to indicate certain conditions or which actually affect the running +of the shell in some way. The user may set these variables to suit their own +personal tastes. +.TP 4 +KEEPSTTY +This variable affects the state of the terminal under Wish. If not defined, +you can change the terminal state using stty, and Wish will use the new +state, with the exception of cbreak/cooked modes. with KEEPSTTY, Wish will +always reset the terminal to the initial state. This is useful if you have +a program with sometimes crashes, leaving the terminal in nl state or even +changing the speed. Defining this variable will set the terminal back to a +sane state when you return to Wish. +.TP 4 +MSB +This variable affects the way characters with their msb on are printed. +Wish defaults to printing characters as they are. However, if MSB is set to +any value, characters above 127 are AND-ed back to 0-127, and are printed +in bold. +." HASH +." If HASH exists, hashing for the path will be turned on. If unset then +." hashing is turned off. HASH needs no value. +." .TP 4 +." TARDIS +." Cause the shell to automatically try to predict what command the user wants +." typed every time the prompt is returned. This is the same as typing ^N every +." time the prompt appears. +." .TP 4 +." UNIV +." For dual universe implementations of Unix, such as Pyramid OSx. +." Can be set to either "att" or "ucb" +." and is probably extremely machine dependent. Contact your system administrator +." regarding this. +.TP 4 +beep +This variable can be set to any string of characters +which will be output whenever a bell would normally sound. By default, +this value is ASCII 7 (^G). If set to null, the shell will give no warning +bell. (For terminals with inverse video, "^[[?5h^[[?5l" will produce a +flash). +.TP 4 +cwd +Holds the value of the current working directory. This variable is automatically +updated by the shell whenever a cd or chdir occurs. It generally should not be +changed by the user. +.TP 4 +." history +." The number of commands the shell is to remember for use with the +." .I history +." command. If set to zero, then of course no commands are remembered. The larger +." history's value, the more space is occupied by the shell process since it must +." store all the previous commands. History is set to 25 by default. +." .TP 4 +." ignoreeof +." If set, ignoreeof causes the shell to ignore the ^D key as a means of exiting +." the shell. Ignoreeof needs no value. +." .TP 4 +." nohistdup +." If set, successive identical lines are not saved in the history. +." .TP 4 +prompt +.in -4 +.br +." prompt2 +." .in +4 +." .br +Prompt contains the format description for the user prompt. By default +this is a `%' sign. Prompt2 contains the format description for the second +prompt, given when more information is needed, like in a while, case, or if +statement being executed in interactive mode (i.e. from keyboard). +The format description is simply a string of characters which are displayed, +except that some special attributes may be entered using the `%' metacharacter. +.br +.in +4 +%% a percent `%' sign. +.br +%d current working directory. +.br +%! +.br +%h current history number. +.br +%S start standout mode (see +.I termcap(4)). +.br +%s stop standout mode. +.br +%@ +.br +%t time in 24-hour format with seconds. +.br +.in -4 +If any other character follows the `%' then the `%' and then that character is +printed. Other characters are just printed. e.g. "%d[%h] " could produce +"/u1/staff/mike/work[58]" as the prompt. +This prompt format is similar to that used by +.I tcsh(1). +." .TP 4 +." umask +." Contains an octal number representing the umask for files when created by +." the shell. The default file creation mode can be found by OR-ing this value +." with octal 0777. See +." .I chmod(1) +." and +." .I umask(2). +." .TP 4 +." wid +." Sets the terminal width. Tells the command line editor when to effect a line +." wrap so that the cursor stays in the right place. +.TP 4 +wordterm +This variable holds all the characters that delimit words, and is used in +the word-oriented CLE functions. If undefined, the CLE will use the +characters SPACE, TAB, > < | / ; = & and `. +.SH AUTHORS +Callum Gibson- Honours project 1988. +Warren Toomey. +.SH FILES +.nf +~/.wishrc Read at beginning of execution of Wish. +." ~/.history Saved commands from history list. +." ~/.login Read only by login shell, after .wishrc. +." ~/.logout Read at logout time. +." /etc/passwd source of directories for ~name. +." /usr/local/man help manual called from CLE, defined at +." compile time. +." /usr/local/lib/wish.hlp Text for help builtin command. +.fi +.SH LIMITATIONS +Maximum line length is 2048 (but redefinable). Maximum number of arguments +is 512. Not all of the above features exist, yet, and some new ones may be added +in future versions. +.SH SEE ALSO +chmod(1), csh(1), sh(1), stty(1), tcsh(1), exec(2), fork(2), pipe(2), umask(2), +wait(2), string(3), signal(3), termcap(4), tty(4), environ(7). diff --git a/Applications/wish/Wishrc b/Applications/wish/Wishrc new file mode 100644 index 0000000000..ddd53e63eb --- /dev/null +++ b/Applications/wish/Wishrc @@ -0,0 +1,15 @@ +# Here are some basic aliases and stuff for Wish 2.0 +# +# setenv PATH $HOME/.bin:/usr/local/bin:/usr/etc:/usr/ucb:/bin:/usr/bin +set dir ' ' +set prompt '%d: ' +# +alias , 'make $*' +alias boring 'echo I know' +alias h history +alias j jobs +alias logo exit +#alias ls '/bin/ls -lg $*' +#alias l '/bin/ls -C $*' +alias mn 'nroff -man $* | less' +alias zless 'zcat $* | less' diff --git a/Applications/wish/alias.c b/Applications/wish/alias.c new file mode 100644 index 0000000000..e1ad54c31a --- /dev/null +++ b/Applications/wish/alias.c @@ -0,0 +1,73 @@ +/* This file contains functions relevant to aliases, including alias + * creation in two ways, listing, interpreting. + * + * $Revision: 41.2 $ $Date: 1996/06/14 06:24:54 $ + */ + +#include "header.h" + +#ifndef NO_ALIAS +struct vallist alist= /* The list of aliases */ + { NULL, NULL}; + +static struct val *aline; /* Pointer to one alias */ + +/* Checkalias returns a pointer to the alias named by aname. If + * no alias is found, it returns NULL. It also points aline to the + * alias definition. + */ +struct val *checkalias(aname) + char *aname; +{ + aline= searchval(&alist, aname, TRUE, FALSE); + return(aline); +} + +/* Getaliasline returns the alias one line at a time. + * It depends on having aline initialised correctly. + */ +bool getaliasline(line, nosave) + uchar *line; + int *nosave; + { + + *nosave=0; + if (aline==NULL) return(FALSE); /* No alias to return */ + strcpy(line,aline->val); + aline=NULL; + return(TRUE); + } + +int alias(argc,argv) + int argc; + char *argv[]; +{ + struct val *v; + +#ifdef DEBUG + fprints(2,"In alias with argc %d argv[1] %s\n",argc,argv[1]); +#endif + + if (argc==1) + for (v=alist.head; v; v=v->next) prints("%s\t'%s'\n",v->name, v->val); + if (argc==2) + { + v= searchval(&alist,argv[1],TRUE,FALSE); + prints("%s\t'%s'\n",v->name, v->val); + } + if (argc==3) + setval(argv[1], argv[2], &alist); + return(0); +} + +int unalias(argc,argv) + int argc; + char *argv[]; +{ + int i; + + for (i=1;argv[i];i++) + searchval(&alist,argv[i],FALSE,FALSE); + return(0); +} +#endif diff --git a/Applications/wish/bind.c b/Applications/wish/bind.c new file mode 100644 index 0000000000..e992672454 --- /dev/null +++ b/Applications/wish/bind.c @@ -0,0 +1,317 @@ +/* The key binding routines used by the command line editor + * + * $Revision: 41.3 $ $Date: 1996/06/14 06:24:54 $ + */ + +#include "header.h" + +/* Wish allows keystrokes to be bound to other keystrokes. The following + * structure is used to hold these bindings. Note well the mode bit; a + * binding with mode!=0 is only used when the CLE has it's mode bit on. + */ + +#ifndef NO_BIND +struct keybind +{ + uchar *key; /* The key sequence we have bound */ + int len; /* The length of the key sequence */ + uchar *cmd; /* The string it is mapped to */ + uchar mode; /* Mode bit. Binding only used when mode on */ + struct keybind *next; +}; + + +static int Keylength = 0; /* The maximum key length */ +static struct keybind *Bindhead = NULL; /* List of bindings */ + +uchar bindbuf[512]; /* Buffer used to expand bindings */ +uchar *bindptr; /* Pointer into bindbuf */ +uchar CLEmode; /* This holds the CLE mode */ + +/* The default key bindings + */ + +static char *defbind[15][3] = { + { NULL, "\033\020", "\201" }, + { NULL, "\033B", "\202" }, + { NULL, "\033b", "\202" }, + { NULL, "\033D", "\203" }, + { NULL, "\033d", "\203" }, + { NULL, "\033F", "\204" }, + { NULL, "\033f", "\204" }, + { NULL, "\033H", "\205" }, + { NULL, "\033h", "\205" }, + { NULL, "\033P", "\206" }, + { NULL, "\033p", "\206" }, + { NULL, "\033Y", "\207" }, + { NULL, "\033y", "\207" }, + { NULL, "\033/", "\210" }, + { NULL, "\033?", "\211" } +}; + +/* Bind is a builtin. With no arguments, it lists the current key bindings. + * With 1 arg, it shows the binding (if any) for argv[1]. With 2 args + * the string argv[1] will be replaced by argv[2]. + */ + +int +Bind(argc, argv) + int argc; + uchar *argv[]; +{ + int s, showall; + uchar *key, *cmd, *ctemp; + struct keybind *temp, *Bindtail; + + if (argc > 4 || argc < 3) + { + fprints(2, "usage: bind [[-m] key [value]]\n"); + return (1); + } + showall = 0; + switch (argc) + { + case 4: + key = argv[2]; + cmd = argv[3]; /* Bind a string to another */ + goto bindit; /* Yuk, a goto */ + case 3: + key = argv[1]; + cmd = argv[2]; /* Bind a string to another */ + bindit: + s = strlen((char *)key); /* Get the key's length */ + if (s == 0) + break; + + temp = (struct keybind *) malloc(sizeof(struct keybind)); + if (!temp) + { + perror("malloc"); + return (1); + } + temp->key = (uchar *) malloc(s + 1); + if (!(temp->key)) + { + perror("malloc"); + return (1); + } + + strcpy((char *)temp->key, (char *)key); /* Copy the key */ + temp->len = s; + temp->cmd = (uchar *) malloc(strlen((char *)cmd) + 1); + if (!(temp->cmd)) + { + perror("malloc"); + return (1); + } + /* Copy the value */ + ctemp = temp->cmd; + while (*cmd != EOS) + { + if (*cmd != '\\') + *(ctemp++) = *(cmd++); + else + { + showall = 0; + cmd++; /* or an octal value */ + while (isdigit(*cmd)) + showall = (showall << 3) + (*(cmd++) - 48); + *(ctemp++) = showall & 0xff; + } + } + *ctemp = EOS; + temp->next = NULL; + if (argc == 4) + temp->mode = 1; + else + temp->mode = 0; + if (s > Keylength) + Keylength = s; + + if (!Bindhead) /* Add to linked list */ + Bindhead = temp; /* Currently this allows duplicates :-( */ + else + { + for (Bindtail = Bindhead; Bindtail->next; Bindtail = Bindtail->next); + Bindtail->next = temp; + } + break; + case 1: + showall = 1; /* Print one or more bindings */ + case 2: + for (temp = Bindhead; temp; temp = temp->next) + if (showall || !strcmp((char *)temp->key, (char *)key)) + { + if (temp->mode) + prints(" * "); + else + prints(" "); + mprint(temp->key, 1); + for (s = Keylength - temp->len; s; s--) + write(1, " ", 1); + prints(" bound to "); + mprint(temp->cmd, 0); + } + } + return (0); +} + +/* Unbind is a builtin which removes argv[1] from the list of key bindings */ + +int +unbind(argc, argv) + int argc; + uchar *argv[]; +{ + int s; + uchar *key; + struct keybind *temp, *t2; + + if (argc != 2) + { + fprints(2, "usage: unbind string, or unbind all\n"); + return (1); + } + key = argv[1]; + if (!strcmp((char *)key,"all")) + { + for (temp = Bindhead, t2 = Bindhead; temp; t2 = temp, temp = temp->next) + { + free(temp->key); + free(temp->cmd); + free(temp); + } + Bindhead = NULL; + return(0); + } + s = strlen((char *)key); /* Get the key's length */ + if (s == 0) + return (1); + + Keylength = 0; + for (temp = Bindhead, t2 = Bindhead; temp; t2 = temp, temp = temp->next) + if (s == temp->len && !strcmp((char *)temp->key, (char *)key)) + { + if (temp == Bindhead) + Bindhead = temp->next; + else + t2->next = temp->next; + free(temp->key); + free(temp->cmd); + free(temp); + } + else if (temp->len > Keylength) + Keylength = temp->len; + return (0); +} + +/* Initialise the default key bindings */ +void initbind() + { + int i; + + for (i = 0; i < 15; i++) + Bind(3, (uchar **) defbind[i]); /* Set default bindings */ + CLEmode = 0; /* and start in mode 1 */ + } + + +/* Exbind: get one or more characters from the user, expanding bindings + * along the way. This routine is recursive & hairy! When called from + * getcomcmd(), inbuf is NULL, indicating we want user keystrokes. + * These are read in, and if there are no partial bind matches, are + * returned to getcomcmd(). If any partial matches, they are buffered + * in bindbuf until either no partials or 1 exact match. Once a match is + * found, we call ourselves with inbuf pointing to the replacement string. + * Thus bindings can recurse, up to the size of bindbuf. Note also that + * after we recurse once, we check to see if there are any leftover chars + * in inbuf, and recurse on them as well. + */ + +#ifdef PROTO +static void expbind(uchar *inbuf) +#else +static void +expbind(inbuf) /* Expand bindings from user's input */ + uchar *inbuf; +#endif +{ + uchar a, *startptr, *exactptr; + int c, currlen, partial, exact; + struct keybind *temp; + + if (inbuf == NULL) + bindptr = bindbuf; + startptr = bindptr; + currlen = 0; + + while (1) /* Look for a keystroke binding */ + { + partial = exact = 0; + if ((inbuf == NULL) || ((a = *(inbuf++)) == EOS)) + c = read(0, (char *)&a, 1); + if (c != -1) /* Decide which uchar to put in the buffer */ + { + *(bindptr++) = a; + *bindptr = EOS; + currlen++; + if ((int) (bindptr - bindbuf) > 510) + return; + } + + for (temp = Bindhead; temp != NULL; temp = temp->next) + { /* Count the # of partial & exact matches */ + /* We exclude mode bindings when CLEmode==0 */ + if (CLEmode == 0 && temp->mode == 1) + continue; + if (currlen > temp->len) + continue; + if (!strcmp((char *)startptr, (char *)temp->key)) + { + exact++; + exactptr = temp->cmd; + } + if (!strncmp((char *)startptr, (char *)temp->key, currlen)) + partial++; + } + if (partial == 0) + break; /* No binding at all */ + if (partial == 1 && exact == 1) /* An exact match, call ourselves */ + { + bindptr = startptr; /* with the matched word */ + expbind(exactptr); + break; + } + } + + if (inbuf != NULL && *inbuf != EOS) /* If any part of our word left over */ + expbind(inbuf); /* check it as well */ +} + +/* Getcomcmd converts a user's keystokes into the commands used by the CLE. + * If there are no chars handy in bindbuf, we call expbind() to get some. + * Then we scan thru the chars and deliver chars or commands to the CLE. + * Hopefully because we use commands>255, the CLE will work with 8-bit + * extended ASCII. + */ + +int +getcomcmd() /* Get either a character or a command from the */ +{ /* user's input */ + int c; + + /* If no chars, get chars from stdin and */ + /* expand bindings */ + while ((c = *(bindptr++)) == 0) + { + expbind(NULL); + bindptr = bindbuf; + } + + /* Default to usual keys */ +#ifdef DEBUG + fprints(2, "Returning %x\n", c); +#endif + return (c); +} +#endif /* NO_BIND */ diff --git a/Applications/wish/builtin.c b/Applications/wish/builtin.c new file mode 100644 index 0000000000..4362ab9ad8 --- /dev/null +++ b/Applications/wish/builtin.c @@ -0,0 +1,275 @@ +/* This file does builtins + * + * $Revision: 41.2 $ $Date: 1996/06/14 06:24:54 $ + */ + +#include "header.h" + +#ifdef PROTO +static int Echo(int argc, char *argv[]), Cd(int argc, char *argv[]); +static int Tilde(int argc, char *argv[]), Untilde(int argc, char *argv[]); +static int Exit(int argc, char *argv[]), Exec(int argc, char *argv[]); +static int Umask(int argc, char *argv[]); +#else +static int Echo(), Cd(); +static int Tilde(), Untilde(); +static int Exit(), Exec(); +static int Umask(); +#endif + +struct builptr buillist[] = { + {"cd", Cd}, + {"echo", Echo}, + {"exit", Exit}, + {"exec", Exec}, + {"umask", Umask}, + {"source", source}, +#ifndef NO_BIND + {"bind", Bind}, + {"unbind", unbind}, +#endif +#ifndef NO_HISTORY + {"history", history}, +#endif +#ifndef NO_VAR + {"export", export}, + {"unexport", unset}, + {"set", set}, + {"unset", unset}, + {"setenv", set}, + {"shift", shift}, +#endif +#ifndef NO_ALIAS + {"alias", alias}, + {"unalias", unalias}, +#endif +#ifndef NO_TILDE + {"tilde", Tilde}, + {"untilde", Untilde}, +#endif +#ifndef NO_JOB +#if defined(UCBJOB) || defined(POSIXJOB) || defined(V7JOB) + {"bg", bg}, + {"fg", fg}, +#endif + {"jobs", joblist}, + {"kill", Kill}, +#endif + {NULL, NULL}}; + + +static int +Exit(argc, argv) + int argc; + char *argv[]; +{ + int how; + + if (argc == 2) + how = atoi(argv[1]); + else + how = 0; + leave_shell(how); + return (1); +} + +static int +Exec(argc, argv) + int argc; + char *argv[]; +{ + argv++; + execvp(argv[0], argv); + fprints(2, "Can't exec %s\n", argv[0]); + return (1); +} + +static int +Echo(argc, argv) + int argc; + char *argv[]; +{ + int doreturn = 1; + int firstarg = 1; + + if (argc > 1 && !strcmp(argv[1], "-n")) + { + doreturn = 0; + firstarg = 2; + } + + for (; firstarg < argc; firstarg++) + { + (void) write(1, argv[firstarg], strlen(argv[firstarg])); + (void) write(1, " ", 1); + } + if (doreturn) + (void) write(1, "\n", 1); + return (0); +} + +/* Here is the global definition for the current directory variable */ +char currdir[128]; + +static int +Cd(argc, argv) + int argc; + char *argv[]; +{ + extern struct vallist vlist; + char *path; + + if (argc > 1) + path = argv[1]; + else if ((path = EVget("HOME")) == NULL) + path = "."; + if (chdir(path) == -1) + { + fprints(2, "%s: bad directory\n", path); + return (1); + } +#ifdef USES_GETWD + if (getwd(currdir)) +#else + if (getcwd(currdir, MAXPL)) +#endif + { + setval("cwd", currdir, &vlist); + return (0); + } + else + { + (void) write(2, "Can't get cwd properly\n", 23); + return (1); + } +} + + +#ifndef NO_TILDE +/* Tilde is a builtin which associates a dir name + * with a shorthand for that dir. + * If called with -l, print out all the passwd file too. + * + * Name holds the shorthand name (starting with a ~). + * Var holds the actual name of the directory. + */ + +struct vallist tlist; /* The tilde list */ + +static int +Tilde(argc, argv) + int argc; + char *argv[]; +{ + struct val *t; + struct passwd *entry; + bool printall = FALSE; + + switch (argc) + { + case 3: + setval(argv[1], argv[2], &tlist); + return (0); + break; + case 2: + if (strcmp(argv[1], "-l")) + { + if ((t = searchval(&tlist, argv[1], TRUE, FALSE)) != NULL) + prints("%s %s\n", t->name, t->val); + break; + } + else + printall = TRUE; + case 1: + for (t = tlist.head; t; t = t->next) + prints("%s %s\n", t->name, t->val); + if (printall) + { + while ((entry = getpwent()) != NULL) + prints("%s %s\n", entry->pw_name, entry->pw_dir); + endpwent(); + } + break; + default: + prints("Usage: tilde [shorthand [dir]] or tilde -l\n"); + return (1); + } + return (0); +} + + +/* Untilde removes a shorthand from the tilde list */ + +static int +Untilde(argc, argv) + int argc; + char *argv[]; +{ + int i; + + if (argc < 2) + { + prints("Usage: %s [short] [short] ...\n", argv[0]); + return (1); + } + for (i = 1; i < argc; i++) + if (!searchval(&tlist, argv[i], FALSE, FALSE)) + prints("No such shorthand: %s\n", argv[i]); + return (0); +} +#endif + + +static int Umask(argc,argv) + int argc; + char *argv[]; + { + int umaskval; + char *cmd; + + switch(argc) + { + case 1: umaskval=umask(0); umask(umaskval); + prints("0%o\n",umaskval); + return(0); + case 2: if (*argv[1]=='0') /* Convert octal */ + { + cmd= argv[1]; umaskval=0; + while (isdigit(*cmd)) + umaskval = (umaskval << 3) + (*(cmd++) - 48); + } + else umaskval=atoi(argv[1]); + umask(umaskval); + return(0); + default: + fprints(2,"Usage: umask [value]\n"); return(1); + } + } + +/* Do builtin. This returns either the positive exit status of the builtin, + * or -1 indicating there was no builtin. We also return 0 for the `pid' + * of the builtin. Note that `fg' is an exception - it returns the new + * fg pid (i.e exit status for fg of 0) OR 0 (i.e exit status for fg of 1). + * So we have to do some mangling with the fg return value only. + */ +int +builtin(argc, argv, rtnpid) + int argc; + char *argv[]; + int *rtnpid; +{ + struct builptr *bptr; + + *rtnpid= 0; /* Usually */ + for (bptr = buillist; bptr->name != NULL; bptr++) + { + if (!strcmp(argv[0], bptr->name)) + if (strcmp(argv[0],"fg")) /* Not fg */ + return ((*(bptr->fptr)) (argc, argv)); + else + { *rtnpid= (*(bptr->fptr)) (argc, argv); + return((*rtnpid)?0:1); + } + } + return (-1); +} diff --git a/Applications/wish/clebuf.c b/Applications/wish/clebuf.c new file mode 100644 index 0000000000..7e0cb157b9 --- /dev/null +++ b/Applications/wish/clebuf.c @@ -0,0 +1,79 @@ +/* Command Line Editor Buffer functions + * + * $Revision: 41.2 $ $Date: 1996/06/14 06:24:54 $ + */ + +#include "header.h" + +/* This version of CLE buffers all characters destined for the terminal + * into a buffer which is flushed just before getcomcmd is called. This + * should hopefully reduce the number of syscalls used in the CLE. The + * following variables hold the buffer & associated pointers. + */ + +#define OUTSIZE 128 /* The size of the buffer */ + +#ifndef NO_COMLINED +extern int curs[2]; +#endif +extern int wid; +extern bool Msb; + +static char outbuf[OUTSIZE]; /* The buffer we use */ +static char *outptr = outbuf; /* Ptr into outbuf for next char */ +static int outcnt = 0; /* Number of chars in outbuf */ + +/* Flushbuf simply flushes the buffer to stdout */ + +void +flushbuf() +{ + if (outcnt == 0) return; + write(1, outbuf, outcnt); + outcnt = 0; + outptr = outbuf; +} + +/* Addbuf takes a string and adds it to the outbuf. If outbuf is + * full, we flush the buffer. + */ + +void +addbuf(str) + char *str; +{ + while (1) + { + for (; *str && outcnt < (OUTSIZE - 1); outcnt++) + *(outptr++) = *(str++); + if (*str == EOS) return; + flushbuf(); + } +} + +/* Mputc prints out a character and updates the cursor position. + * It also handles control chars by preceding them with a caret. + */ + +void +mputc(b) + int b; +{ + extern char *so, *se; + uchar c = (uchar) b; + char d = c; + + if (Msb && c > 0x7f) + { addbuf(so); d &= 0x7f; } + *(outptr++) = d; /* We mimic addbuf for 1 char */ + if (++outcnt == (OUTSIZE - 1)) flushbuf(); + if (Msb && c > 0x7f) addbuf(se); +#ifndef NO_COMLINED + curs[0]++; + if (curs[0] >= wid) + { addbuf("\n"); /* goto start of next line */ + curs[0] = curs[0] % wid; /* hopefully gives zero */ + curs[1]++; + } +#endif +} diff --git a/Applications/wish/clex.c b/Applications/wish/clex.c new file mode 100644 index 0000000000..db218e16ba --- /dev/null +++ b/Applications/wish/clex.c @@ -0,0 +1,476 @@ +/* Here is the declaration of the candidate array. This is used in meta.c + * and probably in parse.c, as well as several million other places. + */ + +#include "header.h" + +struct candidate carray[MAXCAN]; +extern int ncand; +static int maxlen; + +/* Compare is the routine used by qsort to reorder the elements + * in the carray. + */ +int +compare(a, b) +#ifdef BSD43 + CONST struct candidate *a, *b; +#else + CONST void *a, *b; +#endif +{ + struct candidate *c, *d; + + c = (struct candidate *) a; + d = (struct candidate *) b; + return (strcmp(c->name, d->name)); +} + +/* Addcarray adds a new node to the carray. If prev is non-null, then + * prev->next points to the new node. If malc is TRUE, space is malloc'd + * and the word copied. + */ +void +addcarray(word, prev, mode, malc) + char *word; + struct candidate *prev; + int mode; + bool malc; /* Ha ha - a play on clam :-) */ +{ + int j; + struct candidate *here; + + if (ncand == MAXCAN) return; + here = &carray[ncand]; + if (malc && word) + { j = strlen(word); + here->name = (char *) Malloc((unsigned) (j + 2), "addcarray"); + strcpy(here->name, word); + } + else here->name = word; + if (prev >= carray) prev->next = here; + here->next = NULL; + here->mode = mode; + ncand++; +} + + +#ifndef NO_CLEX +/* Print out the maxlen partial match on the word, placing the result at + * pos in the given line. + */ +#ifdef PROTO +static void extend(char *line, int *pos, char *word) +#else +static void +extend(line, pos, word) + char *line; + int *pos; + char *word; +#endif +{ + extern char *wbeep; + extern int beeplength; + int i, j, nostop = 1; + char *newword, *t; + + if (*word == '~' || *word == '$') word++; + t = strrchr(word, '/'); /* Go to the last '/' */ + if (t != NULL) word = ++t; + + switch (ncand) + { + case 0: + Beep; + return; + case 1: + newword = carray[0].name; + default: + if ((newword = (char *) malloc((unsigned) maxlen + 2)) == NULL) + return; + for (i = 0; i < maxlen + 1; i++) /* Clear the new word */ + newword[i] = EOS; + strcpy(newword, word); /* Set up as much as we have */ + + for (i = strlen(word); nostop && i < maxlen; i++) + for (j = 0; j < ncand; j++) + { if (strlen(carray[j].name) <= i) /* Candidate too short, stop */ + { newword[i] = EOS; nostop = 0; break; } + if (newword[i] == 0) /* Copy 1 letter over */ + newword[i] = carray[j].name[i]; + if (newword[i] != carray[j].name[i]) /* Doesn't match the copy */ + { newword[i] = EOS; /* the scrub letter and stop */ + nostop = 0; + break; + } + } + } + for (i = strlen(word); i < strlen(newword); i++) + insert((uchar *) line, (*pos)++, (uchar) newword[i]); + + if (ncand == 1) + { if ((carray[0].mode & S_IFMT) == S_IFDIR) + insert((uchar *) line, (*pos)++, '/'); + else + insert((uchar *) line, (*pos)++, ' '); + } + else + { Beep; free(newword); } +} + + +/* Print out the candidates found in columns. + */ +#ifdef PROTO +static void colprint(void) +#else +static void +colprint() +#endif +{ + extern int wid; + int i, j, collength, numperline, index; + char format[6]; + + maxlen += 2; + numperline = wid / maxlen; + collength = ncand / numperline; + if (ncand % numperline) collength++; + sprints(format, "%%%ds", maxlen); + for (i = 0; i < collength; i++) + { write(1, "\n", 1); + for (j = 0; j < numperline; j++) + { index = i + j * collength; + if (index >= ncand) break; + if ((carray[index].mode & S_IFMT) == S_IFDIR) + strcat(carray[index].name, "/"); +#ifdef S_IFLNK + else if ((carray[index].mode & S_IFMT) == S_IFLNK) + strcat(carray[index].name, "@"); +#endif + else if (carray[index].mode & 0111) + strcat(carray[index].name, "*"); + prints(format, carray[index].name); + } + } + write(1, "\n", 1); +} + + + + +/* Find the name of a file, given a partial word to match against. The + * word may be an absolute path name, or a relative one. Any matches + * against the word are added to the carray. + */ +#ifdef PROTO +static void findfile(char *word) +#else +static void +findfile(word) + char *word; +#endif +{ + extern char currdir[]; + int i, j; + char partdir[MAXWL]; + char *cddir; + char *match; + DIR *dirp; + struct stat statbuf; +#ifdef USES_DIRECT + struct direct *entry; +#else + struct dirent *entry; +#endif + + for (i = 0; i < MAXWL; i++) + partdir[i] = EOS; + + if (word != NULL && *word != EOS) + { + /* Make full pathname */ + if (*word != '/') + { strcpy(partdir, currdir); + strcat(partdir, "/"); + } + strcat(partdir, word); + + /* Find the directory name */ + if ((match = strrchr(partdir, '/')) == NULL) + { prints("Looney! No / in word %s\n", partdir); return; } + + *match = EOS; + match++; /* Match holds the partial file name */ + + i = strlen(match); /* Get the length of the name */ + } + else + { strcpy(partdir, currdir); i = 0; } + + cddir= partdir; + if (*partdir == EOS) /* Can occur when only / is 1st char */ + cddir="/"; + + if ((dirp = opendir(cddir)) == NULL) + { prints("Could not open the directory %s\n", cddir); return; } + + if (chdir(cddir) == 0) + while ((entry = readdir(dirp)) != NULL && ncand < MAXCAN) + { + /* Ignore dot and dot-dot */ + if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) + continue; + + /* If we find a partial match add to list */ + if (i == 0 || !strncmp(entry->d_name, match, i)) + { j = strlen(entry->d_name); + if (j > maxlen) maxlen = j; + /* and get the mode as well */ + statbuf.st_mode = 0; + stat(entry->d_name, &statbuf); + addcarray(entry->d_name, NULL, statbuf.st_mode, TRUE); + } + } + closedir(dirp); + chdir(currdir); +} + + +/* Find the name of a variable, or if word has a / in it, get the + * variable's value and treat it as a path. + * Any matches against the word are added to the carray. + */ +#ifdef PROTO +static void finddollar(char *word) +#else +static void +finddollar(word) + char *word; +#endif +{ + char dir[MAXWL]; + extern struct vallist vlist; + struct val *v; + int i, j, mode; + char *a; + + word++; /* Skip over dollar sign */ + i = strlen(word); + if ((a = strchr(word, '/')) != NULL) /* We have to find a file */ + { *(a++) = EOS; /* Get the var name */ + for (v = vlist.head; v; v = v->next) + if (!strncmp(v->name, word, i)) + { sprints(dir, "%s/%s", v->val, a); /* Form the full path name */ + findfile(dir); /* and match against it */ + } + *(--a) = '/'; /* Extend needs to see the / */ + return; + } + + /* Look through the var names */ + for (v = vlist.head; v; v = v->next) + if (!strncmp(v->name, word, i)) + { j = strlen(v->name); + if (*(v->val) == '/') mode = S_IFDIR; /* Guess if it's a dir */ + else mode = 0; + addcarray(v->name, NULL, mode, TRUE); + if (j > maxlen) maxlen = j; + } +} + +/* Find the name of an alias or builtin, given the partial word. + * Any matches against the word are added to the carray. + */ + +#ifdef PROTO +static void findbuilt(char *word) +#else +static void +findbuilt(word) + char *word; +#endif +{ + extern struct builptr buillist[]; +#ifndef NO_ALIAS + extern struct vallist alist; +#endif + int h, i, j; + struct val *v; + + i = strlen(word); + +#ifndef NO_ALIAS + for (v = alist.head; v; v = v->next) + if (!strncmp(v->name, word, i)) + { j = strlen(v->name); + addcarray(v->name, NULL, 0700, TRUE); + if (j > maxlen) maxlen = j; + } +#endif + + for (h = 0; buillist[h].name; h++) /* Then the builtins */ + if (i == 0 || !strncmp(buillist[h].name, word, i)) + { j = strlen(buillist[h].name); + addcarray(buillist[h].name, NULL, 0700, TRUE); + if (j > maxlen) maxlen = j; + } +} + +/* Find the name of a file, or a user, given the partial word. If there + * are no slashes, just go for a user, else get the home dir & call + * findfile. Any matches against the word are added to the carray. + * + * This now uses the tilde list. + */ +#ifdef PROTO +static void findpasswd(char *word) +#else +static void +findpasswd(word) + char *word; +#endif +{ + extern struct vallist tlist; + struct val *t; + int i, j; + char dir[MAXWL]; + + struct passwd *entry; + + if (!strncmp(word, "~#/", 3)) /* If looking for builtins, */ + { findbuilt(&word[3]); + return; + } /* use findbuilt() */ + + if (strchr(word, '/') != NULL)/* We have to find a file */ + { tilde(word, dir); + if (*dir != 0) findfile(dir); + return; + } + + word++; /* Skip over tilde */ + i = strlen(word); + + /* Try our tilde list first */ + for (t = tlist.head; t; t = t->next) + if (!strncmp(t->name, word, i)) + { j = strlen(t->name); + addcarray(t->name, NULL, S_IFDIR, TRUE); + if (j > maxlen) maxlen = j; + } + + while ((entry = getpwent()) != NULL && ncand < MAXCAN) + { + if (i == 0 || !strncmp(entry->pw_name, word, i)) + { j = strlen(entry->pw_name); + addcarray(entry->pw_name, NULL, S_IFDIR, TRUE); + if (j > maxlen) maxlen = j; + } + } + endpwent(); +} + +/* Find the name of a file by looking through $PATH. + * Any matches against the word are added to the carray. + */ +#ifdef PROTO +static void findbin(char *word) +#else +static void +findbin(word) + char *word; +#endif +{ + int i; + char word2[MAXWL]; + char *Path, *thispath, *temp; + + temp = EVget("PATH"); /* Get the PATH value */ + if (temp == NULL) return; + Path = thispath = (char *) malloc((unsigned) strlen(temp) + 4); + if (thispath == NULL) return; + strcpy(thispath, temp); + while (thispath != NULL) + { for (i = 0; i < MAXWL; i++) word2[i] = EOS; + temp = thispath; + while (*temp != EOS && *temp != ' ' && *temp != ':') temp++; + if (*temp == EOS) temp = NULL; + else *(temp++) = EOS; + + strcpy(word2, thispath); + strcat(word2, "/"); + if (word != NULL || *word != EOS) strcat(word2, word); + + findfile(word2); + + while (temp != NULL && *temp != EOS && (*temp == ' ' || *temp == ':')) + temp++; + if (temp == NULL || *temp == EOS) thispath = NULL; + else thispath = temp; + } + free(Path); +} + + +/* Complete subsumes the work of two routines in old Clam, depending on how: + * + * case 0: Print out a columnated list of files that match the word + * at position pos. The line is unchanged. + * case 1: Try to complete as much as possible the word at pos, by + * using the find routines above. Add the completion to the line. + */ +void +complete(line, pos, how) + char *line; + int *pos; + bool how; +{ + extern char *wbeep, yankbuf[]; + extern int beeplength; + extern char *wordterm; + char *b, *yankword = yankbuf; + int startpos; + + ncand = maxlen = 0; + + if (line[(*pos) - 1] == ' ') + { + strcpy(yankbuf, ""); + startpos = *pos - 1; + } /* nothing there to get */ + else + { b = wordterm; /* first get the thing we've got so far */ + wordterm = " "; + startpos = yankprev((uchar *) line, *pos); + wordterm = b; + } + + if (*yankword == '\'' || *yankword == '"' || *yankword == '`') + yankword++; + if (startpos == 0 && *yankword != '.' && *yankword != '/' && *yankword != '~' + && *yankword != '$') + findbin(yankword); + else + switch (*yankword) + { case '~': + findpasswd(yankword); break; + case '$': + finddollar(yankword); break; + default: + findfile(yankword); + } + + if (how == TRUE) extend(line, pos, yankword); + else + { if (ncand == 0) Beep; + else + { qsort((void *) carray, ncand, sizeof(struct candidate), compare); + colprint(); + prprompt(); + show((uchar *) line, TRUE); + } + } + while ((--ncand) >= 0) free(carray[ncand].name); +} +#endif /* NO_CLEX */ diff --git a/Applications/wish/comlined.c b/Applications/wish/comlined.c new file mode 100644 index 0000000000..f6ed3a221d --- /dev/null +++ b/Applications/wish/comlined.c @@ -0,0 +1,775 @@ +/* List of commands defined for the command line editor. + * We only use the ctrl chars, so that people can use + * the editor with 8-bit ascii characters. + * + * $Revision: 41.3 $ $Date: 2003/04/21 13:08:43 $ + */ + +#include "header.h" + +#define MARK 0 /* Save position, make a mark */ +#define START 1 /* Go to start of line */ +#define BAKCH 2 /* Go back one character */ +#define INT 3 /* Interrupt this task */ +#define DELCH 4 /* Delete the current character */ +#define END 5 /* Goto the end of the line */ +#define FORCH 6 /* Go forward one char */ +#define BEEP 7 /* Simply ring the bell */ +#define BKSP 8 /* Backspace over previous character */ +#define COMPLETE 9 /* Complete the current word */ +#define FINISH 10 /* Finish and execute the line */ +#define KILLEOL 11 /* Kill from cursor to end of line */ +#define CLREDISP 12 /* Clear screen & redisplay line */ +#define NL 13 /* Same as FINISH */ +#define NEXTHIST 14 /* Step forward in history */ +#define OVERWRITE 15 /* Toggle insert/overwrite mode */ +#define BACKHIST 16 /* Step backward in history */ +#define XON 17 /* Resume tty output */ +#define REDISP 18 /* Redisplay the line */ +#define XOFF 19 /* Stop tty output */ +#define TRANSPCH 20 /* Transpose current & previous characters */ +#define KILLALL 21 /* Kill the whole line */ +#define QUOTE 22 /* Quote next character literally */ +#define DELWD 23 /* Delete word backwards */ +#define GOMARK 24 /* Goto a mark */ +#define YANKLAST 25 /* Yank the previous word into a buffer */ +#define SUSP 26 /* Suspend process */ + +/* A Gap here for 27 This can be filled as needed */ +/* A Gap here for 28 This can be filled as needed */ + +#define MODEON 29 /* Include bindings with mode bit on */ +#define MODEOFF 30 /* Exclude bindings with mode bit on */ +#define QUOTEUP 31 /* Turn the following char's msb on */ + +#define DEL 127 /* Same as BKSP */ + +#define MATCHPART 129 /* Match a previous partial command */ +#define BAKWD 130 /* Go backwards one word */ +#define DELWDF 131 /* Delete word forwards */ +#define FORWD 132 /* Go forwards one word */ +#define GETHELP 133 /* Get help on a word */ +#define PUT 134 /* Insert buffer on the line */ +#define YANKNEXT 135 /* Yank the next word into the buffer */ +#define SEARCHF 136 /* Search forward for next typed character */ +#define SEARCHB 137 /* Search backwards for next typed character */ + +#define isctrl(x) (((x+1)&0x7f)<33) + +/* Strip takes the line, and removes leading spaces. If the first non-space + * character is a hash, it returns 1. This should also remove trailing + * comments; I might just move the whole thing into meta_1. + */ +#ifdef PROTO +static int strip(uchar *line) +#else +static int +strip(line) + uchar *line; +#endif +{ + int i, nosave = 0; + + for (i = 0; line[i] == ' '; i++); + if (line[i] == '#') + { nosave = 1; i++; } + if (i) strcpy((char *) line, (char *) &line[i]); + return (nosave); +} + +bool Msb; /* Is var Msb defined? */ +#ifndef NO_COMLINED +uchar yankbuf[512]; /* Buffer used when yanking words */ +uchar *wordterm; /* Characters that terminate words */ +extern int wid; /* The width of the screen (minus 1) */ + +/* A quick blurb on the curs[] structure. + * Curs[0] holds the column pos'n of the cursor, curs[1] holds the # of + * lines the cursor is below the prompt line. e.g (0,0) is the first char + * of the prompt, (25,3) is in column 25, 3 lines below the prompt line. + */ +int curs[2]; + + +/* Go moves the cursor to the position (vert,hor), and updates the cursor */ +#ifdef PROTO +static void go(int hor, int vert) +#else +static void +go(hor, vert) + int hor, vert; +#endif +{ + extern char *bs, *nd, *up; + int hdiff, vdiff; + + vdiff = vert - curs[1]; /* vertical difference between */ + /* current and future positions */ + + if (vdiff <= 0) /* if negative go up */ + for (; vdiff; vdiff++) addbuf(up); + else + { /* else go down */ + for (; vdiff; vdiff--) addbuf("\n"); + curs[0] = 0; + } + + hdiff = hor - curs[0]; /* horizontal difference between */ + /* current and future positions */ + curs[0] = hor; + curs[1] = vert; /* a new current pos, hopefully */ + /* assigned here because hor changed */ + /* below and curs needed above */ + + if (hdiff < 0) /* if negative go back */ + { + if (-hdiff <= hor) /* if shorter distance just use ^H */ + for (; hdiff; hdiff++) addbuf(bs); + else + { /* else cr and go forward */ + addbuf("\r"); + for (; hor; hor--) addbuf(nd); + } + } + else + for (; hdiff; hdiff--) /* have to go forward */ + addbuf(nd); +} + +/* Backward: Move the cursor backwards one character */ +#ifdef PROTO +static void backward(void) +#else +static void +backward() +#endif +{ + extern char *bs; + + if (curs[0] == 0) go(wid - 1, curs[1] - 1); + else { addbuf(bs); curs[0]--; } +} + +/* Forward: Move the cursor forwards one character */ +#ifdef PROTO +static void forward(void) +#else +static void +forward() +#endif +{ + extern char *nd; + + curs[0]++; + if (curs[0] >= wid) + { + addbuf("\n"); /* goto start of next line */ + curs[0] = curs[0] % wid; /* hopefully gives zero */ + curs[1]++; + } + else addbuf(nd); +} + +/* Show is a routine that replaces four routines in the old version of Clam. + * The flag variable holds the id of which `routine' to emulate: + * + * case 0: Insert Insert the letter at position pos in the line, + * updating the cursor, and redrawing the line. + * case 1: Overwrite Overwrite the letter at pos in the line, + * updating the cursor, and redrawing the line. + * Note that to toggle ins/ovw, we can have a + * variable ovwflag, and do ovwflag= 1-ovwflag. + * case 2: Show Just redisplay the line. + * case 3: Goend Goto the end of the line. + */ +int +Show(line, pos, let, flag) + uchar *line; + int let, pos, flag; +{ + extern int lenprompt; + int i, horig=0, vorig=0; + uchar letter = (uchar) let, c; + + switch (flag) + { /* Case 0: insert character */ + case 0: + for (i = pos; line[i]; i++); /* goto end of line */ + for (; i != pos; i--) /* copy characters forward */ + line[i] = line[i - 1]; + case 1: + line[pos] = letter; /* Case 1: overwrite char */ + letter &= 0x7f; + if (isctrl(letter)) horig = curs[0] + 2; + else horig = curs[0] + 1; /* Calculate the new cursor */ + vorig = curs[1]; /* position */ + if (horig > wid - 1) + { horig = horig % wid; + vorig++; + } + break; + case 2: + pos = 0; /* Case 2: show the line */ + horig = curs[0]; + vorig = curs[1]; /* save original values */ + curs[0] = lenprompt; + curs[1] = 0; + } /* Case 3: goto end of line */ + for (c = line[pos]; c; c = line[++pos]) /* write out rest of line */ + { + if (isctrl(c)) /* if it's a control char */ + { mputc('^'); /* print out the ^ and */ + mputc(c | 64); /* the equivalent char for it */ + } + else mputc(c); /* else just show it */ + } + if (flag != 3) + go(horig, vorig); /* h/vorig unused for goend */ + return (pos); /* goend only uses this value */ +} + + +/* I'm not exactly sure what copyback() does yet - Warren */ +#ifdef PROTO +static void copyback(uchar *line, int pos, int count) +#else +static void +copyback(line, pos, count) + uchar *line; + int pos, count; +#endif +{ + uchar c; + int i, horig, vorig, wipe; + +#ifdef DEBUG + fprints(2, "pos %d count %d\n", pos, count); +#endif + if ((i = pos + count) < MAXLL) + { + wipe = 0; + for (horig = pos; horig < i; horig++) + if (isctrl(line[horig])) + wipe += 2; /* calculate amount to blank */ + else + wipe++; + for (; line[i] != EOS; ++i) /* copy line back count chars */ + line[i - count] = line[i]; + for (horig = i - count; i > horig && i < MAXLL + 1; i--) + line[i - 1] = EOS; /* end with nulls */ + } + else + { + write(2, "count passed to copyback is too big\n", 35); + return; + } + horig = curs[0]; + vorig = curs[1]; + for (c = line[pos]; c; c = line[++pos]) + if (isctrl(c)) + { + mputc('^'); + mputc(c | 64); + } + else + mputc(c); +#ifdef DEBUG + fprints(2, "wipe %d\n", wipe); +#endif + for (i = 0; i < wipe; i++) + mputc(' '); /* blank old chars */ + go(horig, vorig); +} + +#define delnextword(line,pos) nextword(line,&pos,0) +#define forword(line,pos) nextword(line,pos,1) +#define yanknext(line,pos) nextword(line,&pos,2) + +#define delprevword(line,pos) prevword(line,pos,0) +#define backword(line,pos) prevword(line,pos,1) + +/* The following two routines each replace three separate ones from old Clam */ + +/* Nextword works on the word after/at the cursor poition, depending on flag: + * + * case 0: Delnextword The word after the cursor is removed + * from the line, and the display is updated. + * case 1: Forword The cursor is moved to the start of the + * next word. + * case 2: Yanknext The word after the cursor is put into yankbuf. + * + * Although pos is passed as a pointer, only forword() updates the value. + */ +#ifdef PROTO +static void nextword(uchar *line, int *p, int flag) +#else +static void +nextword(line, p, flag) + uchar *line; + int *p, flag; +#endif +{ + int inword = 0, l = 1, pos = *p, charcount = 0; + uchar c; + + while (l) + { + if ((c = line[pos]) == EOS) + { l = 0; break; } + if (strchr((char *) wordterm, c) != NULL) /* Found end of a word */ + { charcount++; + pos++; + if (inword) l = 0; + } + else + { inword = 1; + charcount++; + pos++; + } + } + +#ifdef DEBUG + fprints(2, "Deleting %d chars\n", charcount); +#endif + switch (flag) + { + case 0: + copyback(line, *p, charcount); + break; + case 1: + for (; l < charcount; l++) forward(); /* move forward */ + *p = pos; + case 2: + for (pos = *p; l < charcount; l++, pos++) + yankbuf[l] = line[pos]; + yankbuf[l] = EOS; + } +} + +/* Prevword works on the word before the cursor poition, depending on flag: + * + * case 0: Delprevword The word before the cursor is removed + * from the line, and the display is updated. + * case 1: Backword The cursor is moved to the start of the + * previous word. + * case 2: Yankprev The word before the cursor is put into yankbuf. + * + * Although pos is passed as a pointer, only delprevword and + * backword update the value. + */ +int +prevword(line, p, flag) + uchar *line; + int *p, flag; +{ + int inword = 0, l = 1, q, pos = *p, charcount = 0; + + while (l) + { + if (pos == 0) + { l = 0; break; } + if (strchr((char *) wordterm, line[pos - 1]) != NULL) + { /* Found end of a word */ + if (inword) l = 0; + else + { charcount++; pos--; } + } + else + { + inword = 1; + charcount++; + pos--; + } + } +#ifdef DEBUG + fprints(2, "Deleting %d chars\n", charcount); +#endif + + switch (flag) + { + case 0: + for (; l < charcount; l++) backward(); /* move back */ + copyback(line, pos, charcount); /* and copy the line on top */ + *p = pos; + break; + case 1: + for (; l < charcount; l++) backward(); /* move back */ + *p = pos; + break; + case 2: + q = pos; + for (; l < charcount; l++, q++) yankbuf[l] = line[q]; + yankbuf[l] = EOS; + } + return (pos); +} + + +/* Clrline: The line from the position pos is cleared */ +#ifdef PROTO +static void clrline(uchar *line, int pos) +#else +static void +clrline(line, pos) + uchar *line; + int pos; +#endif +{ + extern char *cd; + int i, horig=0, vorig=0; + + if (*cd != EOS) /* If there's a special char for clr */ + { /* then use it */ + addbuf(cd); + for (i = pos; line[i]; i++) line[i] = EOS; +#ifdef DEBUG + fprints(2, "cleared ok\n"); +#endif + } + else horig = curs[0]; /* else we gotta use spaces. */ + vorig = curs[1]; /* Orginal values for curs set. */ + for (i = pos; line[i]; i++) /* Wipe out all those chars */ + { /* with spaces (so slow), */ + mputc(' '); + if (isctrl(line[i])) mputc(' '); + line[i] = EOS; + } + go(horig, vorig); /* restore original curs position */ +} + +/* Transpose transposes the characters at pos and pos-1 */ +#ifdef PROTO +static void transpose(uchar *line, int pos) +#else +static void +transpose(line, pos) + uchar *line; + int pos; +#endif +{ + uchar temp; + + if (isctrl(line[pos - 1])) backward(); + backward(); + temp = line[pos]; + line[pos] = line[pos - 1]; + line[pos - 1] = temp; + if (isctrl(line[pos - 1])) + { mputc('^'); + mputc(line[pos - 1] | 64); + } + else mputc(line[pos - 1]); + if (isctrl(line[pos])) + { mputc('^'); + mputc(line[pos] | 64); + } + else mputc(line[pos]); + if (isctrl(line[pos])) backward(); + backward(); +} + + +/* Getuline gets a line from the user, returning a flag if the line + * should be saved. + */ +bool +getuline(line, nosave) + uchar *line; + int *nosave; +{ + extern char *wbeep, *cl, *cd; + extern uchar bindbuf[], *bindptr, CLEmode; + extern int errno, lenprompt, curr_hist; + uchar a; + int c, times = 1, i, pos = 0, hist = curr_hist, + hsave = lenprompt, vsave = 0, possave = 0; + int beeplength = strlen(wbeep); + + memset(line, 0, MAXLL); + wordterm = (uchar *) EVget("Wordterm"); /* Determine the word + terminators */ + if (wordterm == (uchar *) NULL || *wordterm == EOS) + wordterm = (uchar *) " \t><|/;=&`"; + if (EVget("Msb")) Msb = TRUE; + else Msb = FALSE; + bindptr = bindbuf; /* Set up the pointer to the bind buffer */ + *bindptr = EOS; + + prprompt(); /* Print out our prompt */ + setcbreak(); /* and set the terminal into cbreak mode */ + curs[0] = lenprompt; /* lenprompt global set by prprompt or when + prompt set */ + curs[1] = 0; /* start on line 0 */ + while (1) + { + flushbuf(); /* Ensure user can see the current line */ + c = getcomcmd(); /* Get a character or a command */ + for (; times > 0; times--) + switch (c) + { + case EOF: + fprints(2, "%d\n", errno); + perror("comlined"); + exit(1); + case MARK: + hsave = curs[0]; /* save position (make mark) */ + vsave = curs[1]; + possave = pos; + break; + case START: + go(lenprompt, 0); /* goto start of the line */ + pos = 0; + break; + case BAKCH: + if (pos > 0) /* if not at home, go back */ + { if (isctrl(line[pos - 1])) backward(); + backward(); + pos--; + } + else Beep; /* else ring bell */ + break; + case INT: + pos = goend(line, pos); + addbuf("\n"); + flushbuf(); + setcooked(); + return (FALSE); + case DELCH: + if (line[0] == EOS) /* Leave shell if possible */ + { flushbuf(); + if (EVget("ignoreeof")) + fprints(2, "Use `exit' to exit wish\n"); + else leave_shell(0); /* eof */ + setcooked(); + return (FALSE); /* return if no exit */ + } + else if (line[pos] != EOS) + copyback(line, pos, 1); /* delete char */ + else + complete((char *) line, &pos, FALSE); + break; + case END: + pos = goend(line, pos); /* goto end of the line */ + break; + case FORCH: + if (line[pos] != EOS) /* if not at end, go forward */ + { if (isctrl(line[pos])) forward(); + forward(); + pos++; + } + else Beep; /* else ring bell */ + break; + case KILLALL: + go(lenprompt, 0); /* goto start */ + pos = 0; + clrline(line, 0); /* and kill from pos=0 */ + hist = curr_hist; /* reset hist */ + for (pos = 0; pos < MAXLL; pos++) + line[pos] = EOS; + pos = possave = 0; + break; + case DEL: + case BKSP: + if (pos > 0) /* if not at home, delete */ + { if (isctrl(line[pos - 1])) backward(); + backward(); + copyback(line, --pos, 1); /* move line back on to */ + } /* prev char */ + else Beep; /* else ring bell */ + break; + case COMPLETE: + if (line[0] != EOS) /* try to complete word */ + complete((char *) line, &pos, TRUE); + else Beep; + break; + case NL: + case FINISH: + pos = goend(line, pos); + addbuf("\n"); + flushbuf(); + setcooked(); + line[pos++] = EOS; + *nosave = strip(line);/* process it now */ + if (line[0] != EOS) return (TRUE); + else return (FALSE); +#ifndef NO_HISTORY + case NEXTHIST: + if (hist < curr_hist) /* put next hist in line buf */ + { loadhist((char *) line, ++hist); + goto redisp; /* Yuk, a goto */ + } + else Beep; + break; + case BACKHIST: + if (hist == 1) Beep; + else + { if (hist == curr_hist) /* in line buf */ + (void) savehist((char *) line, 0); + loadhist((char *) line, --hist); + } +#endif + case REDISP: + redisp: + go(lenprompt, 0); + addbuf(cd); /* Clear to end of screen */ + show(line, TRUE); /* Show the line typed so far. */ + pos = goend(line, 0); /* goto end of the line */ + break; + case CLREDISP: + addbuf(cl); /* Clear the screen */ + flushbuf(); + prprompt(); /* Reprint the prompt and */ + show(line, TRUE); /* the line typed so far. */ + break; + case KILLEOL: + clrline(line, pos); /* kill line from cursor on */ + break; + case XON: + continue; /* can't use this */ + case XOFF: + continue; /* can't use this */ + case TRANSPCH: + if (pos > 0 && line[pos] != EOS) /* if not home or at end */ + transpose(line, pos); /* swap current and prev char */ + else Beep; /* else ring bell */ + break; + case QUOTE: + if (pos >= MAXLL) + { Beep; + continue; + } + mputc('"'); + backward(); /* literal char */ + flushbuf(); + read(0, (char *) &a, 1); + c = a; + if (c) /* don't allow EOS (null) */ + insert(line, pos++, c); + break; + case DELWD: + if (pos) delprevword(line, &pos); + else Beep; + break; + case GOMARK: + pos = possave; + go(hsave, vsave); + break; + case YANKLAST: + if (pos != 0) /* if not at home */ + yankprev(line, pos);/* yank previous word */ + else Beep; /* else ring bell */ + break; + case SUSP: + continue; + case BAKWD: + if (pos > 0) + backword(line, &pos); + else Beep; + break; + case DELWDF: + if (line[pos] != EOS) + delnextword(line, pos); + else Beep; + break; + case FORWD: + if (line[pos] != EOS) + forword(line, &pos); + else Beep; + break; + case PUT: + if (pos > MAXLL - strlen((char *) yankbuf)) + { Beep; + break; + } + for (i = 0; yankbuf[i]; i++) /* insert yank buffer */ + insert(line, pos++, yankbuf[i]); + break; + case YANKNEXT: + if (line[pos] != EOS) /* if not at end */ + yanknext(line, pos);/* yank next word */ + else Beep; /* else ring bell */ + break; + case SEARCHF: + if (line[pos]) /* search forward */ + { read(0, (char *) &a, 1); + c = a; /* char to search for */ + for (i = pos + 1; line[i] && c != line[i]; i++); + if (line[i]) + while (pos < i) + { pos++; + if (isctrl(line[pos - 1])) forward(); + forward(); + } + else Beep; /* not found */ + } + else Beep; /* at end of line */ + break; + case SEARCHB: + if (pos > 0) /* search backwards */ + { read(0, (char *) &a, 1); + c = a; /* char to search for */ + for (i = pos - 1; i >= 0 && c != line[i]; i--); + if (i >= 0) + while (pos > i) + { pos--; + if (isctrl(line[pos])) backward(); + backward(); + } + else Beep; /* not found */ + } + else Beep; /* at end of line */ + break; + case MODEON: + CLEmode = 1; /* Turn the CLE mode on */ + break; + case MODEOFF: + CLEmode = 0; /* Turn the CLE mode off */ + break; + case BEEP: + Beep; /* Simply beep */ + break; + case QUOTEUP: + if (pos >= MAXLL) /* Turn on next char's msb */ + { Beep; + continue; + } + mputc('"'); + backward(); /* literal char */ + flushbuf(); + read(0, (char *) &a, 1); + c = a; + if (c) /* don't allow EOS (null) */ + { c |= 0x80; + insert(line, pos++, c); + } + break; + default: + if (pos >= MAXLL - 1) + { Beep; + break; + } + insert(line, pos++, c); + break; + } + times = 1; + } +} +#else /* NO_COMLINED */ +bool +getuline(line, nosave) + uchar *line; + int *nosave; +{ + int i; + + prprompt(); /* Print out our prompt */ + for (i=0; inewfd[i]) + { + close(i); /* Close the one we are using */ + /* and dup back the old one */ + if (dup2(d->newfd[i], i) == -1) + { + fprints(2, "Can't undup fd %d\n", i); + } + close(d->newfd[i]); + } + } + /* Now pull the dup object off the stack */ + if (!nd) + duptop = d->prev; + free(d); +} + +/* + * Redirect redirects the file descriptors for fds 0 to 2 for the + * current process; for each fd it also takes a bitmap indicating whether + * to append to the output file, or to open from an input file. + * If an error occurs, we return -1, else 0. We only push a dupstack + * if no error occurs, so we must not call undirect afterwards. + */ +#ifdef PROTO +static int redirect(struct rdrct newfd[3]) +#else +static int +redirect(newfd) + struct rdrct newfd[3]; +#endif +{ + int i, j, appnd; + int mode, flags; + struct dupstack *d; + +#ifdef DEBUG + fprints(2, "Redirect...\n"); +#endif + + + d = (struct dupstack *) Malloc(sizeof(struct dupstack), "redirect"); + + /* Clear all the fields */ + d->newfd[0] = d->newfd[1] = d->newfd[2] = 0; + + for (i = 0; i < 3; i++) /* Do each fd in turn */ + { + /* Not this one */ + if (newfd[i].fd < 3 && newfd[i].file == NULL) + continue; + appnd = newfd[i].how & H_APPEND; + + + j = dup(i); /* Copy that file desc */ + if (j == -1) + { + fprints(2, "Couldn't dup fd %d\n", i); + undirect(d); + return (-1); + } + else + d->newfd[i] = j; + + if (newfd[i].fd > 2) /* Fd is already open */ + { + close(i); + if (dup2(newfd[i].fd, i) == -1) + { + fprints(2, "Could not dup2(%d,%d)\n", newfd[i].fd, i); + undirect(d); + return (-1); + } +#ifdef DEBUG + fprints(2, "Dup'd %d to %d\n", newfd[i].fd, i); +#endif + close(newfd[i].fd); + } + /* Open this file */ + else + { + close(i); + if (newfd[i].how & H_FROMFILE) /* for reading */ + { + flags = O_RDONLY; + if (open(newfd[i].file, flags) == -1) + { + fprints(2, "Can't open %s\n", newfd[i].file); + undirect(d); + return (-1); + } + } + else + /* for writing */ + { + flags = O_WRONLY | O_CREAT; + if (!appnd) + flags |= O_TRUNC; + else + flags |= O_APPEND; + mode = 0666; + if (open(newfd[i].file, flags, mode) == -1) + { + fprints(2, "Can't open %s\n", newfd[i].file); + undirect(d); + return (-1); + } +#ifdef DEBUG + fprints(2, "Opened %s as %d\n", newfd[i].file, i); +#endif + } + } + } + /* Now put the old fd's on the stack */ + d->prev = duptop; + duptop = d; + return (0); +} + +#ifndef NO_ALIAS +#define MAXAL 16 +static char *curr_alias[MAXAL]; /* The list of aliases we have done */ +static int adepth = 0; /* Our current depth */ + +/* Runalias checks to see if there is an alias, and then runs it. + * It either returns the exit status of the alias, or -1 if there + * was no alias. The only bit in how we are interested in is + * H_BCKGND. This gets passed onto doline to indicate how to waitfor() + * the child. + */ +#ifdef PROTO +static int runalias(int argc, char *argv[], int how) +#else +static int +runalias(argc, argv, how) + int argc; + char *argv[]; + int how; +#endif +{ + extern int Argc, saveh, Exitstatus; + extern char **Argv; +#ifdef PROTO + extern bool(*getaline) (uchar *line , int *nosave ); + bool(*oldgetline) (uchar *line , int *nosave ); +#else + extern bool(*getaline) (); + bool(*oldgetline) (); +#endif + + int i, oldsaveh, oldargc; + char **oldargv; + + /* Check for already used alias */ + for (i = 0; i < adepth; i++) + if (!strcmp(curr_alias[i], argv[0])) + return (-1); + + if (checkalias(argv[0]) != NULL) /* Find an alias */ + { + oldargc = Argc; + oldargv = Argv; + Argc = argc; + Argv = argv; + oldsaveh = saveh; + oldgetline = getaline; + if (adepth == MAXAL) + { + fprints(2, "Aliases nested too deep\n"); + /* exit(1); */ + return (-1); + } + curr_alias[adepth++] = argv[0]; +#ifdef DEBUG + fprints(11, "About to send the alias through doline()\n"); +#endif + saveh = FALSE; + how= (how & H_BCKGND) | TRUE; + getaline = getaliasline; + doline(how); + adepth--; + Argc = oldargc; + Argv = oldargv; + getaline = oldgetline; + saveh = oldsaveh; + return (Exitstatus); + } + else + return (-1); +} +#endif /* NO_ALIAS */ + + +static int bgpgrp; /* Background process group id */ + +/* Invoke simple command. This takes the args to pass to the executed + * process/builtin, the list of new file descriptors, and a bitmap + * indicating how to run the process. We only use the background bits + * at the moment. + */ + +int +invoke(argc, argv, newfd, how, anydups) + int argc, how; + char *argv[]; +struct rdrct newfd[]; +int anydups; + +{ + extern int saveh, Exitstatus; + int pid, i; + +#ifdef DEBUG + if (argv[0]) + fprints(2, "Invoking %s\n", argv[0]); +#endif + /* Firstly redirect the input/output */ + if (anydups) + { + i = redirect(newfd); + if (i == -1) + return (0); + } + + if (!(how & H_BCKGND)) /* If a foreground process */ + { + bgpgrp=0; /* Reset for next bg process */ + /* Try builtins first */ + if (argc == 0 || ((i = builtin(argc, argv, &pid)) != -1)) + { + Exitstatus = i; + if (anydups) + undirect(NULL); + return (pid); + } + +#ifndef NO_ALIAS + if ((i = runalias(argc, argv, how)) >= 0) + { Exitstatus= i; + if (anydups) + undirect(NULL); + return (0); + } +#endif + } + + /* Else fork/exec the process */ + switch (pid = fork()) + { + case -1: + fprints(2, "Can't create new process\n"); + if (anydups) + undirect(NULL); + return (0); + /* Restore signals to normal if fg */ + case 0: + saveh = FALSE; + dflsig(); +#ifdef V7JOB + ptrace(0,0,(PLONG)0,(PLONG)0); /* Enable tracing on the child */ +#endif + if (how & H_BCKGND) + { /* Move process to new proc-grp if bg */ +#if defined(UCBJOB) || defined(POSIXJOB) +#ifdef POSIXJOB + /* First get a pgrp-id if none */ + if (bgpgrp==0) bgpgrp=getpid(); + i = setpgid(0, bgpgrp); + if (i == -1) + fprints(2, "I was -1\n"); +#else + i = setpgrp(0, getpid()); + if (i == -1) + fprints(2, "I was -1\n"); +#endif /* POSIXJOB */ +#endif /* UCBJOB || POSIXJOB */ + } +#ifndef NO_VAR + if (!EVupdate()) + fatal("Can't update environment"); +#endif + /* The fork only wants fds 0,1,2 */ +#ifdef DEBUG + fprints(2, "The child is closing 3 to 20\n"); +#endif + for (i = 3; i < 20; i++) + close(i); + + /* Try builtins first */ + if (argc == 0 || ((i = builtin(argc, argv, &pid)) != -1)) + { + exit(i); + } + +#ifndef NO_ALIAS + if ((i = runalias(argc, argv, how)) >= 0) + { + if (anydups) + undirect(NULL); + exit(i); + } +#endif + /* Finally exec() the beast */ + + execvp(argv[0], argv); + /* Failed, exit the child */ + fprints(2, "Can't execute %s\n", argv[0]); + exit(1); + + default: +#ifdef DEBUG + fprints(2, "Just forked %s, pid %d\n", argv[0], pid); +#endif + /* and return the new pid */ +#ifndef NO_JOB + addjob(pid, argv[0], how & H_BCKGND); +#endif +#ifdef V7JOB +/* We can't just leave a backgrounded process as is, because it will + * stop on the exec(), and because we won't waitfor() it, it will stay + * like that. So we wake it up here. + */ + if (how & H_BCKGND) + ptrace(7,pid,(PLONG)1,(PLONG)0); +#endif + if (anydups) + undirect(NULL); + return (pid); + } +} diff --git a/Applications/wish/file.c b/Applications/wish/file.c new file mode 100644 index 0000000000..8b6aa9db5f --- /dev/null +++ b/Applications/wish/file.c @@ -0,0 +1,129 @@ +/* File does the work for opening & reading lines from files. We keep a + * stack of open fds and buffers for each file. This is to minimise the + * overhead or read()ing, but avoiding the use of stdio. + * + * $Revision: 41.1 $ $Date: 1995/12/29 02:10:46 $ + */ + +#include "header.h" + +static int currfile = 0; +static int filedesc[10], bufbytes[10]; +static char *bufptr[10], *filebuf[10]; + + +/* Fileopen opens the named file read-only, and sets the number of bytes + * buffered in yankbuf to zero. NOTE if filename==NULL, we assume that + * fd has already been open. + */ +bool +fileopen(filename, fd) + char *filename; + int fd; +{ + if (currfile == 9) return (FALSE); /* No space on stack left */ + currfile++; + if (filename) + { if ((filedesc[currfile] = open(filename, O_RDONLY, 0)) == -1) + { currfile--; fprints(2, "Can't open %s\n", filename); return (FALSE); } + } + else + filedesc[currfile] = fd; + + filebuf[currfile] = (char *) malloc(MAXLL); + if (filebuf[currfile] == NULL) + { close(filedesc[currfile]); currfile--; + fprints(2, "Can't malloc file buffer\n"); return (FALSE); + } + bufptr[currfile] = filebuf[currfile]; + bufbytes[currfile] = 0; + return (TRUE); +} + +void +fileclose() +{ + free(filebuf[currfile]); + close(filedesc[currfile]); + currfile--; +} + +/* Getfileline obtains a line from the opened file. + */ +bool +getfileline(line, nosave) + uchar *line; + int *nosave; +{ + int in_line = 0; + + *nosave = 0; + while (1) /* Until we have a line, copy stuff */ + { /* We've already got some to copy */ + if (!in_line) + { + for (; bufbytes[currfile] && (*bufptr[currfile] == ' ' || + *bufptr[currfile] == '\t'); bufbytes[currfile]--, bufptr[currfile]++); + in_line = 0; + } + if (bufbytes[currfile] == 0) + { in_line = 1; goto doread; } /* Yuk, a goto */ + + if (*bufptr[currfile] == '#') *nosave = 1; + for (; bufbytes[currfile] && *bufptr[currfile] != '\n'; bufbytes[currfile]--) + *line++ = *bufptr[currfile]++; + + if (bufbytes[currfile]) /* We hit a \n, so return */ + { *line = EOS; bufptr[currfile]++; + bufbytes[currfile]--; return (TRUE); + } + +doread: + bufptr[currfile] = filebuf[currfile]; + /* Get more characters */ + bufbytes[currfile] = read(filedesc[currfile], filebuf[currfile], MAXLL); + if (bufbytes[currfile] < 1) return (FALSE); /* End of file */ + } +} + +int +source(argc, argv) + int argc; + char *argv[]; +{ + extern int saveh; +#ifdef PROTO + extern bool(*getaline) (uchar *line , int *nosave ); + bool(*oldgetline) (uchar *line , int *nosave ); +#else + extern bool(*getaline) (); + bool(*oldgetline) (); +#endif + + int oldsaveh; + + if (argc != 2) + { prints("Usage: source file\n"); return (1); } + +#ifdef DEBUG + prints("About to fileopen %s\n", argv[1]); +#endif + + if (fileopen(argv[1], 0) == FALSE) + { prints("Unable to source file %s\n", argv[1]); return (1); } + else + { +#ifdef DEBUG + prints("About to send the file through doline()\n"); +#endif + oldsaveh = saveh; + saveh = FALSE; + oldgetline = getaline; + getaline = getfileline; + doline(FALSE); + getaline = oldgetline; + saveh = oldsaveh; + } + fileclose(); + return (0); +} diff --git a/Applications/wish/fuzix-wish.pkg b/Applications/wish/fuzix-wish.pkg new file mode 100644 index 0000000000..f5561bcd8e --- /dev/null +++ b/Applications/wish/fuzix-wish.pkg @@ -0,0 +1,7 @@ +package wish +if-file wish + +f 0755 /usr/bin/wish wish +f 0655 /usr/man/man1/wish.1 Wish.1 +f 0644 /root/.wishrc Wishrc + diff --git a/Applications/wish/global.c b/Applications/wish/global.c new file mode 100644 index 0000000000..2b42c0960f --- /dev/null +++ b/Applications/wish/global.c @@ -0,0 +1,3 @@ +#include "header.h" + +char currdir[128]; diff --git a/Applications/wish/header.h b/Applications/wish/header.h new file mode 100644 index 0000000000..2739cc7938 --- /dev/null +++ b/Applications/wish/header.h @@ -0,0 +1,206 @@ +/* Definitions of all the various names and macros that we need to + * compile Wish. + * + * $Revision: 41.3 $ $Date: 2003/04/21 13:08:43 $ + */ + +#include "machine.h" + +/* These defines turn off some of the capabilities + * of the shell. They are mainly used for finding + * the location of bugs. + */ +#undef NO_ALIAS /* No alias functions */ +#undef NO_JOB /* No job functions */ +#undef NO_HISTORY /* No history stuff */ +#undef NO_TILDE /* No tilde stuff */ +#undef NO_VAR /* No variables */ +#undef NO_COMLINED /* No command line editor */ +#ifdef NO_COMLINED +# define NO_CLEX /* No command line extensions */ +# define NO_BIND /* No key bindings */ +#endif + +#if defined(__STDC__) && __STDC__ && !defined(MINIX1_5) +# define PROTO +# define CONST const +# ifndef __GNUC__ +# include +# endif +#else +# define CONST +#endif + +#ifndef NULL /* Generic null pointer. May need */ +# define NULL 0 /* changing on some machines to */ +#endif /* (char *)0 or (void *)0 */ + +#ifndef SIGTYPE /* Signal handlers return this */ +# define SIGTYPE void +#endif + +#undef EOF /* Minix defines this in stdio.h */ +#define EOF -1 + +#ifndef MAXSIG +# define MAXSIG 27 /* Maximum signals known to Wish */ +#endif + +#define UNDEF -1 /* Used when setting cbreak etc. */ +#define EOS '\0' /* End of string terminator */ +#define BADFD -2 +#define MAXARG 300 /* Max # args passed to processes */ +#define MAXWORD 200 /* Only used in parse, try to lose */ +#define MAXFNAME 200 /* Only used in parse, try to lose */ +#define MAXPL 512 /* Path length */ +#define MAXLL 2048 /* Used in comlined & hist */ +#define MAXWL 512 /* Used in clex & meta */ +#define MAXCAN 1000 /* maximum number of candidates */ + + +/* The following used to be enums, + * but they get used an awful lot + * with ints. + */ +#undef TRUE +#undef FALSE +#define TRUE 1 +#define FALSE 0 +#if (!defined(FREEBSD_4) && !defined(FREEBSD_5)) +typedef int bool; /* Must be int as we pass bools as f'n args */ +#endif +typedef unsigned char uchar; /* CLE uses unsigned chars throughout */ + +/* We use the following when debugging malloc/free */ +#ifdef MALLOCDEBUG +# define free myFree +# define malloc myMalloc +#endif + +#define fatal(mess) { fprints(2,"%s\n",mess); exit(1); } + + +/* + * These structures are used to store the definitions for aliases, history, + * the tilde lists, variables. + */ + +struct val { + char *name; /* The name of the var/history/tilde/alias */ + char *val; /* The value */ + int hnum; /* History only: the history number */ + bool exported; /* Variable only: is this exported? */ + struct val *next; +}; + +struct vallist { + struct val *head; /* Singly-linked list with head/tail */ + struct val *tail; +}; + +/* Clex and Meta + * + * The following structure is used by both clex.c and meta.c. Meta + * uses it to hold candidates files that matched a ^D or expression. + * Here the mode holds the mode of each file as obtained by stat(). + * Clex uses the struct to build a linked list of words that will + * eventually be parsed by the parser. Here, the mode is a complex bitfield. + * For normal words,the mode is used as a bitfield to indicate: + * - if the name has been malloc'd (mode&TRUE) + * - if the name has an invisible space at the end (mode&C_SPACE) + * - if the name is in single quotes (mode&C_QUOTE) + * - if the name is in backquotes (mode&C_BACKQUOTE) + * - if the name is in curly brackets (mode&C_CURLY) + * - if the name starts with a $ sign (mode&C_DOLLAR) + * Often there will be nodes in the list with mode==0 & name==NULL, these + * indicate words removed during meta and should be skipped by the parser. + */ + +#define C_SPACE 002 /* Word has a space on the end */ +#define C_DOLLAR 004 /* Word starts with a $ sign */ +#define C_QUOTE 010 /* Word in single quotes */ +/* #define C_DBLQUOTE 020 * Word in double quotes */ +#define C_CURLY 020 /* Word in curly braces {} */ +#define C_BACKQUOTE 040 /* Word in backquotes */ +/* #define C_QUOTEBITS 060 * Dbl and back quote bits */ + +/* Some words have name==NULL & mode!=0; these are special words for the + * parser. They are separate, non-malloc'd, non-quoted words, and the bits + * used above can be reused. The bits must be in C_WORDMASK + */ +#define C_WORDMASK 01700 /* Bits must fall in here */ + +#define C_SEMI 0100 /* The word is a semicolon */ +#define C_DOUBLE C_SEMI /* Add this to `double' the symbol */ +#define C_PIPE 0200 /* The word is a pipe */ +#define C_DBLPIPE 0300 /* The word is a double pipe */ +#define C_AMP 0400 /* The word is an ampersand */ +#define C_DBLAMP 0500 /* The word is a double ampersand */ +#define C_LT 0600 /* The word is a less-than. */ +#define C_LTLT 0700 /* The word is two less-thans. */ +#define C_FD 03 /* File descriptor bits */ +#define C_GT 01000 /* The word is a greater-than. Bits */ + /* in the mask C_FD hold the fd (1-2) */ +#define C_GTGT 01100 /* The word is two greater-thans.Bits */ + /* in the mask C_FD hold the fd (0-9) */ +struct candidate + { char *name; /* The file's name */ + struct candidate *next; /* Next field in linked list */ + int mode; /* File's mode, or malloc'd bool */ + }; + +/* Execution + * + * The how parameter to execute() indicates how the process should be + * executed. + */ + +#define H_BCKGND 002 /* Process running in background */ +#define H_FROMFILE 004 /* Process has input from file */ +#define H_APPEND 010 /* Process is appending output */ + +/* Redirection + * + * The rdrct structure holds the new file descriptors for the process, + * or their file names (if any). Redirect() gets 3 of these, and + * for (i=0 to 2) { if name not null, open(name); else if fd not 0, + * dup2(fd,i); } + * The how field can have the H_ bits described above. + */ + +struct rdrct { + int fd; /* The input file descriptor */ + int how; /* How to open up the file */ + char *file; /* Input file's name */ + }; + +/* Command Line Editing + * + * Several old routines have been subsumed by one routine, Show(). + * Unfortunately, these are used in comlined, clex and hist, so the + * defines that map the old onto the new have to be out here. + */ + +#define insert(a,b,c) (void)Show(a,b,c,0) +#define show(a,c) (void)Show(a,0,0,2) +#define goend(a,b) Show(a,b,0,3) +#define yankprev(line,pos) prevword(line,&pos,2) +#define Beep write(1,wbeep,beeplength) + + +/* Builtins + * + * The following structure holds the builtins. This is only used by + * builtin.c and clex.c. Pity we can't leave it in builtin.c + */ + +struct builptr { + char *name; +#ifdef PROTO + int (*fptr)(int argc, char *argv[]); +#else + int (*fptr)(); +#endif + }; + +#include "proto.h" diff --git a/Applications/wish/hist.c b/Applications/wish/hist.c new file mode 100644 index 0000000000..56efd069f5 --- /dev/null +++ b/Applications/wish/hist.c @@ -0,0 +1,151 @@ +/* The command history is stored as a singly linked list, each node holding + * a command, its history number, and a pointer to the next node. + * + * $Revision: 41.2 $ $Date: 1996/06/14 06:24:54 $ + */ + +#include "header.h" + +#ifndef NO_HISTORY +static struct vallist hlist = /* The history list */ + { NULL, NULL}; +int curr_hist = 1; /* The current history number */ +static int maxhist = 25; /* Maximum # of saved lines */ +static int curr_saved = 0; /* How many we have saved currently */ +static bool nohistdup = TRUE; /* Do we omit duplicate lines? */ + + +/* Savehist saves the given line into the history with the given history + * number, and ensuring that there are no more than max histories. + * It returns 1 if saved, or 0 if a duplicate or other errors. + */ +int +savehist(line, andadd) + char *line; + bool andadd; +{ + struct val *v; + char *mh; + + mh = EVget("history"); /* Set the current history */ + if (mh) maxhist = atoi(mh); + + /* Adjust the number saved if necessary */ + while (curr_saved > maxhist) { pullval(&hlist); curr_saved--; } + + /* If we don't care about duplicates, or there */ + /* isn't a duplicate, save the history */ + if (!nohistdup || ((v = searchval(&hlist, line, TRUE, FALSE)) == NULL)) + { + v = (struct val *) Malloc(sizeof(struct val), "savehist malloc"); + v->name = Malloc(strlen(line) + 1, "savehist malloc"); + strcpy(v->name, line); v->hnum = curr_hist++; appendval(&hlist, v); + if (++curr_saved > maxhist) { pullval(&hlist); curr_saved--; } + } + return (1); +} + +/* Loadhist finds the command line with the given histnum, and loads it + * at the given pos into the command line. + */ +void +loadhist(line, histnum) + char *line; + int histnum; +{ + struct val *ptr; + + /* Find either the last hist or the histnum one */ + for (ptr = hlist.head; ptr && ptr->hnum != histnum; ptr = ptr->next); + if (ptr) strcpy(line, ptr->name); /* copy into the command line */ + /* else we can't find that number so load */ + /* nothing. Maybe it should beep here */ +} + +/* The history builtin prints out the current history list. + * None of the arguments are used. + */ +int +history(argc, argv) + int argc; + char *argv[]; +{ + struct val *ptr; + + for (ptr = hlist.head; ptr && ptr->hnum < curr_hist; ptr = ptr->next) + if (ptr->hnum >= curr_hist - maxhist) + { prints("%d ", ptr->hnum); + mprint((uchar *) ptr->name, 0); /* this routine intercepts the ^ chars */ + } + return (0); +} + +/* Getnumhist returns the command line with the given history number. + * If none exists, NULL is returned. + */ +#ifdef PROTO +static char * getnumhist(int histnum) +#else +static char * +getnumhist(histnum) + int histnum; +#endif +{ + struct val *ptr; + + for (ptr = hlist.head; ptr; ptr = ptr->next) + if (ptr->hnum == histnum) return (ptr->name); + return (NULL); +} + +/* Gethist takes a event string, and returns a match from + * the history. The event can be one of the following: + * Value Return value + * an integer The command line with the given histnum + * "!" The last command line + * -integer The line integer histories ago + * word The last line that strncmps the word + * + * If no event is found, NULL is returned. + */ +char * +gethist(event) + char *event; +{ + struct val *ptr, *this; + int histnum, len; + + if ((*event >= '0' && *event <= '9') || (*event == '-') || (*event == '!')) + { + switch (*event) + { + case '-': + histnum = curr_hist - atoi(event + 1); + break; + case '!': + histnum = curr_hist - 1; + break; + default: + histnum = atoi(event); + } +#ifdef DEBUG + prints("Histnum is %d\n", histnum); +#endif + return (getnumhist(histnum)); + } + else + { + /* + If the history matches the requested event then that history line is + copied into the event pointer. Note that the first histnum characters of + event will not change and thus further searches for more recently + matching histories will still be valid. + */ + len= strlen(event); + for (ptr=NULL, this = hlist.head; this; this = this->next) + if (!strncmp(this->name, event, len)) ptr=this; + + if (ptr) return (ptr->name); else return(NULL); + } +} +#endif /* NO_HISTORY */ diff --git a/Applications/wish/job.c b/Applications/wish/job.c new file mode 100644 index 0000000000..b97007a5d2 --- /dev/null +++ b/Applications/wish/job.c @@ -0,0 +1,530 @@ +/* Job control. If UCBJOB is defined, we use BSD4.x job control. If POSIXJOB + * is defined, we use POSIX job control. IF V7JOB is defined, we provide + * job control using ptrace(). If none are defined, we simulate job control + * as best we can. + * + * $Revision: 41.1 $ $Date: 1995/12/29 02:10:46 $ + */ + +#include "header.h" + +/* We define the wait return status macros for SysV. We also define + * WIFCORE, which evaluates as true if WIFSIGNALED is TRUE and + * a core has been dumped. This only works under SysV and BSD. + * Also defined are WRUNFG and WRUNBG, which are true is the + * process is running in the fore or background. + */ +#define RUNFG -1 /* Status if running in fg */ +#define RUNBG -2 /* Status if running in bg */ + +#define WAIT_T int +#define STATUS status +#ifdef UCBJOB +# undef WAIT_T +# undef STATUS +# define WAIT_T union wait +# define STATUS status.w_status +#endif + + +/* The job structure hold the information needed to manipulate the + * jobs, using job numbers instead of pids. Note that the linked + * list used by Wish is ordered by jobnumber. + */ +struct job +{ + int jobnumber; /* The job number */ + int pid; /* The pid of the job */ + char *name; /* Job's argv[0]; */ + char *dir; /* The job's working directory */ + WAIT_T status; /* Job's status */ + bool changed; /* Changed since last CLE? */ + struct job *next; /* Pointer to next job */ +}; + +static struct job *jtop = NULL, /* The head of the linked list */ + *currentjob = NULL; /* Pointer to current job structure */ + +int Exitstatus = 0; /* The exit status of the last child */ +static bool donepipe = TRUE; /* Addjob makes a new currentjob if this is */ + /* TRUE, then sets it FALSE. Joblist sets */ + /* it TRUE again. */ + +#ifdef PROTO +# define P(s) s +#else +# define P(s) () +#endif + +#if defined(UCBJOB) || defined(POSIXJOB) || defined(V7JOB) +static struct job *findjob P((int pid )); +static void rmjob P((struct job *ptr )); +#endif +#undef P + +#ifdef POSIXJOB +# include "posixjob.c" +#endif + +#ifdef UCBJOB +# include "ucbjob.c" +#endif + +#ifdef V7JOB +# include "v7job.c" +#endif + +#if defined(UCBJOB) || defined(POSIXJOB) || defined(V7JOB) +/* Given a process id, return a pointer to it's job structure */ +static struct job * +findjob(pid) + int pid; +{ + struct job *ptr; + + for (ptr = jtop; ptr; ptr = ptr->next) + if (ptr->pid == pid) + return (ptr); + return (NULL); +} +#endif + + +/* Given a job number, return it's process id */ +#ifdef PROTO +static int pidfromjob(int jobno) +#else +static int +pidfromjob(jobno) + int jobno; +#endif +{ + struct job *ptr; + + for (ptr = jtop; ptr; ptr = ptr->next) + if (ptr->jobnumber == jobno) + return (ptr->pid); + return (-1); +} + + +/* Add the pid and it's argv[0] to the job list. Return the allocated + * job number. + */ +int +addjob(pid, name, isbg) + int pid, isbg; + char *name; +{ + extern char currdir[]; + int jobno, last = 0; + struct job *ptr, *old, *new; + +#ifndef V7JOB + if (currentjob && donepipe == FALSE) /* We already have a current job */ + { +#ifdef DEBUG + fprints(2, "I already got one, you sons of a donkey\n"); +#endif + return (0); + } +#endif + /* Build the structure */ + ptr = old = (struct job *) malloc((unsigned) sizeof(struct job)); + if (ptr == NULL) + { + perror("addjob"); + return (0); + } + ptr->pid = pid; + ptr->name = (char *) malloc((unsigned) (strlen(name) + 1)); + if (ptr->name) + (void) strcpy(ptr->name, name); + else + { + perror("addjob"); + return (0); + } + /* We need some way of indicating the child */ + /* is still running. I am using RUNxx for now. */ + if (isbg) + ptr->STATUS = RUNBG; + else + ptr->STATUS = RUNFG; + + ptr->dir = (char *) malloc((unsigned) (strlen(currdir) + 1)); + if (ptr->dir) + (void) strcpy(ptr->dir, currdir); + else + { + perror("addjob"); + return (0); + } + ptr->changed = FALSE; + + /* Find the end of list */ + if (jtop == NULL) + { + jobno = ptr->jobnumber = 1; + jtop = ptr; ptr->next = NULL; + } + else + { /* Find a gap to put the node in */ + for (last = 0, old = new = jtop; + new != NULL && new->jobnumber - last == 1; + old = new, last = new->jobnumber, new = new->next); + last++; + if (new == NULL) + { + old->next = ptr; + ptr->next = NULL; + } + else + { + ptr->next = new; + old->next = ptr; + } + jobno = ptr->jobnumber = last; + } + + currentjob = ptr; /* a new current job */ + donepipe = FALSE; +#ifdef DEBUG + fprints(2, "added jobno %d pid %d\n", jobno, pid); +#endif + return (jobno); +} + +#if defined(UCBJOB) || defined(POSIXJOB) || defined(V7JOB) +/* Remove the job from the job list */ +static void +rmjob(ptr) + struct job *ptr; +{ + struct job *prev; + + if (ptr==NULL) return; +#ifdef DEBUG + fprints(2, "removing pid %d from list\n", ptr->pid); +#endif + if (ptr==jtop) jtop=jtop->next; + else + { + for (prev=jtop;prev && prev->next!=ptr; prev=prev->next) + ; + if (prev==NULL) return; + prev->next= ptr->next; + } + free(ptr->name); + if (ptr == currentjob) + { + currentjob = NULL; + Exitstatus = (WIFEXITED(ptr->status)) ? WEXITSTATUS(ptr->status) : 1; + } + free(ptr); +} +#endif + +/* Print out the list of current jobs and their status. + * Note although this is a builtin, it is called from + * main with an argc value of 0, to show new jobs only. + */ +int +joblist(argc, argv) + int argc; + char *argv[]; +{ +#ifndef FUZIX + struct job *ptr, *next; + bool exitzero; + + if (argc > 1) + { + prints("Usage: jobs\n"); + return (1); + } + +#if defined(UCBJOB) || defined(POSIXJOB) + waitfor(0); /* Collect any jobs doline() might have missed */ +#endif + + for (ptr = jtop; ptr; ptr = ptr->next) + { +/* Printing out conditions are tricky. We print if we're being called as + * a builtin (when argc!=0); otherwise only if the status has changed, + * and it wasn't the current job or exited with an exit status of 0. + * Also don't print if it's RUNFG or RUNBG. + */ + exitzero = (WIFEXITED(ptr->status) && (WEXITSTATUS(ptr->status) == 0)); + if (argc || (ptr->changed && !WRUNFG(ptr->status) && !WRUNBG(ptr->status) + && ptr != currentjob && !exitzero)) + { + if (currentjob && currentjob->pid == ptr->pid) + prints(" + [%d] %d", ptr->jobnumber, ptr->pid); + else + prints(" [%d] %d", ptr->jobnumber, ptr->pid); + prints(" %s ", ptr->name); + /* Still running */ + if (WRUNFG(ptr->status) || WRUNBG(ptr->status)) + { + prints("\n"); + continue; + } + if (WIFEXITED(ptr->status)) + { + prints(" Exited %d\n", WEXITSTATUS(ptr->status)); + continue; + } + if (WIFSTOPPED(ptr->status)) + { + prints(" Stopped %s\n", siglist[WSTOPSIG(ptr->status)]); + continue; + } + if (WIFSIGNALED(ptr->status)) + { + prints(" %s", siglist[WTERMSIG(ptr->status)]); + if (WIFCORE(ptr->status)) + prints(" (core dumped)\n"); + else + prints("\n"); + continue; + } + } + } + for (ptr = jtop; ptr; ptr = next) + { + next= ptr->next; + if (WRUNFG(ptr->status) || WRUNBG(ptr->status)|| WIFSTOPPED(ptr->status)) + ptr->changed = FALSE; + else + rmjob(ptr); + } + donepipe = TRUE; /* It must be, if doline() is calling us */ +#endif + return (0); +} + +int +Kill(argc, argv) + int argc; + char *argv[]; + +{ + int pid, sig = 0; + char *jobname, *sigwd; +#ifdef V7JOB + struct job *job; +#endif + + switch (argc) + { + case 2: + sig = 9; + jobname = argv[1]; + goto killit; /* Yuk, a goto */ + case 3: + jobname = argv[2]; + sigwd = argv[1]; + if (*sigwd == '-') + sigwd++; + sig = atoi(sigwd); +#ifdef FUZIX + goto killit; /* Yuk, a goto */ +#else + if (sig == 0) + for (i = 1; signame[i]; i++) + if (!strcmp(sigwd, signame[i])) + { + sig = i; + break; + } +#endif + killit: + if (sig == 0) + { + fprints(2,"Bad signal argument\n"); + return (1); + } + if (*jobname == '%') + pid = pidfromjob(atoi(++jobname)); + else + pid = atoi(jobname); + if (pid < 1) + { + fprints(2, "No such job number: %s\n", jobname); + return (1); + } +#ifdef V7JOB +/* A stopped job can't be killed until the parent (i.e us) restarts it. + * So if the job is stopped, we restart it so we can kill it :-) + */ + job= findjob(pid); + if (job && WIFSTOPPED(job->status)) + { +# ifdef DEBUG + fprints(2,"About to restart ptrace the job\n"); +# endif + ptrace(7,pid,(PLONG)1,(PLONG)0); /* Start it up again */ + } +#endif + kill(pid, sig); + return (0); + default: +#ifdef FUZIX + prints("Usage: kill [-]number job|pid\n"); +#else + prints("Usage: kill [-]signame|number job|pid\n"); + prints(" Signal names are:\n"); + for (i = 1; signame[i]; i++) + { + if (i % 6 == 1) + prints(" "); + prints("%s ", signame[i]); + if (i % 6 == 0) + prints("\n"); + } + prints("\n"); +#endif + return (1); + } +} + +#ifdef FUZIX +void +waitfor(pid) + int pid; +{ + int status; + int wpid; + do { + wpid = wait(&status); + } while (wpid != pid); +} +#endif + + +#if defined(UCBJOB) || defined(POSIXJOB) || defined(V7JOB) +/* Builtins */ +int +bg(argc, argv) + int argc; + char *argv[]; + +{ + int pid=0; + char *c=NULL; + + if (argc > 2) + { + prints("usage: bg [pid]\n"); + return (1); + } + if (argc == 1) + { +#ifdef DEBUG + fprints(2,"Trying to use current job pointer\n"); +#endif + if (currentjob) + { + pid = currentjob->pid; + } + } + else + { + c = argv[1]; + if (*c == '%') + pid = pidfromjob(atoi(++c)); + else + pid = atoi(c); + } + if (pid < 1) + { + fprints(2, "No such job number: %s\n", c); + return (1); + } + bgstuff(pid); + + return (0); +} + + +/* Fg is a special builtin. Instead of returning an exitstatus, it returns + * the pid of the fg'd process. Builtin() knows about this. + */ +int +fg(argc, argv) + int argc; + char *argv[]; + +{ + extern char currdir[]; + extern struct vallist vlist; + int pid; + struct job *ptr; + char *c; + + if (argc > 2) + { + prints("usage: fg [pid]\n"); + return (0); + } + if (argc == 1) + { +#ifdef DEBUG + fprints(2,"Trying to use current job pointer\n"); +#endif + if (currentjob) + { + pid = currentjob->pid; + } + } + else + { + c = argv[1]; + if (*c == '%') + pid = pidfromjob(atoi(++c)); + else + pid = atoi(c); + } + if (pid < 1) + { + fprints(2, "No such job number: %s\n", c); + return (0); + } + + if (fgstuff(pid)) return(0); + + ptr = findjob(pid); + if (ptr) + { + if (strcmp(ptr->dir, currdir)) /* If directory has changed */ + { + fprints(2, "[%d] %d %s (wd now: %s)\n", ptr->jobnumber, ptr->pid, + ptr->name, ptr->dir); + + chdir(ptr->dir); /* Reset our current directory */ + strcpy(currdir,ptr->dir); + setval("cwd", currdir, &vlist); + } + else + fprints(2, "[%d] %d %s\n", ptr->jobnumber, ptr->pid, ptr->name); + } + else + { + fprints(2, "Couldn't find job ptr in fg\n"); + return (0); + } + /* and finally wake them up */ + +#ifndef V7JOB +# ifdef DEBUG + fprints(2,"About to SIGCONT %d\n", pid); +# endif + kill(pid, SIGCONT); +#endif + + ptr->STATUS = RUNFG; + currentjob = ptr; + return (pid); +} +#endif /* defined(UCBJOB) || defined(POSIXJOB) || defined(V7JOB) */ diff --git a/Applications/wish/m_fuzix.h b/Applications/wish/m_fuzix.h new file mode 100644 index 0000000000..17edaa1e6c --- /dev/null +++ b/Applications/wish/m_fuzix.h @@ -0,0 +1,26 @@ +/* FUZIX specific includes and defines + * + */ +#define FUZIX0_4 /* FUZIX 0.4 */ +#define FUZIX + +#define USES_TERMIOS +#define PROTO +#define STDARG + +#include +#include +#include /* Only for perror */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/Applications/wish/machine.h b/Applications/wish/machine.h new file mode 100644 index 0000000000..17edaa1e6c --- /dev/null +++ b/Applications/wish/machine.h @@ -0,0 +1,26 @@ +/* FUZIX specific includes and defines + * + */ +#define FUZIX0_4 /* FUZIX 0.4 */ +#define FUZIX + +#define USES_TERMIOS +#define PROTO +#define STDARG + +#include +#include +#include /* Only for perror */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/Applications/wish/main.c b/Applications/wish/main.c new file mode 100644 index 0000000000..f461c4da91 --- /dev/null +++ b/Applications/wish/main.c @@ -0,0 +1,333 @@ +/* The main loop of the shell + */ + +#include "header.h" +char x,y,z; + +int Argc; /* The number of arguments for Wish */ +char **Argv; /* The argument list for Wish */ +static char *prompt; /* A pointer to the prompt string */ +int lenprompt; /* and the length of the prompt */ +int saveh; /* Should we save history? */ +static bool Loginshell=FALSE; /* Indicates if we're a login shell */ +static struct candidate *next_word; + +#ifdef PROTO +bool(*getaline) (uchar *line,int *nosave) = getuline; +#else +bool(*getaline) () = getuline; /* Our input routine defaults to getuline() */ +#endif + + +/* Lengthint is used by prprompt to determine the number + * of chars in the string version of an integer; this is + * needed so the prompt length is calculated properly. + */ +#ifdef PROTO +static int lengthint(int num) +#else +static int lengthint(num) + int num; +#endif +{ + int i; + + for (i = 0; num; num = num / 10, i++); + return (i); +} + +/* Print out the current time in a set length string */ +#ifdef PROTO +static void printime(void) +#else +static void +printime() +#endif +{ + long clock; + struct tm *t; + + time(&clock); + t = localtime(&clock); + prints("%2d:%02d:%02d", t->tm_hour, t->tm_min, t->tm_sec); +} + +/* Prprompt prints out the prompt. It parses all the special options + * that the prompt can take, and determines the effective length of + * the prompt as it appears on the screen. If there is no $prompt, + * it defaults to '% '. + */ +void +prprompt() +{ + extern int curr_hist; + extern char *so, *se; + char c; + int i, len; + + lenprompt = 0; + prompt = EVget("prompt"); + if (prompt == NULL) + prompt = "% "; + for (i = 0; prompt[i]; i++) + if (prompt[i] == '%') + switch (prompt[++i]) + { + case EOS: + case '%': + write(1, "%", 1); + lenprompt++; + break; + case '!': +#ifndef NO_HISTORY + case 'h': + lenprompt += lengthint(curr_hist); + prints("%d", curr_hist); + break; +#endif + case 'd': + len = strlen(EVget("cwd")); + lenprompt += len; + write(1, EVget("cwd"), len); + break; + case 'S': + prints("%s", so); + break; + case 's': + prints("%s", se); /* temp stop standout */ + break; + case '@': + case 't': + lenprompt += 8; + printime(); + break; + default: + write(1, "%", 1); + lenprompt += 2; + if (prompt[i] < 32 || prompt[i] == 127) + { + write(1, "^", 1); + c = prompt[i] + 64; + write(1, &c, 1); + lenprompt++; + } + else + write(1, &prompt[i], 1); + } + else + { + if (prompt[i] < 32 || prompt[i] == 127) + { + write(1, "^", 1); + c = prompt[i] + 64; + write(1, &c, 1); + lenprompt++; + } + else + write(1, &prompt[i], 1); + lenprompt++; + } +} + +/* Set the terminal back to normal + * before leaving the shell + */ +void +leave_shell(how) + int how; +{ + char *argv[2]; + + setcooked(); + if (Loginshell) /* If we're a login shell */ + { + argv[0] = "source"; + argv[1] = ".klogout"; /* Source .klogout */ + source(2, argv); + } + write(1, "\n", 1); + exit(how); +} + +/* Setup does most of the initialisation of the shell. Firstly the default + * variables & the environ variables are set up. Then the termcap strings + * are found, and then any other misc. things are done. + */ +#ifdef PROTO +static void setup(void) +#else +static void +setup() +#endif +{ + extern char currdir[]; + extern struct vallist vlist; + char *argv[2]; + char *home; + + /* Initialise the environment */ + if (!EVinit()) + fatal("Can't initialise environment"); +#ifdef USES_GETWD + if (getwd(currdir)) /* Get the current directory */ +#else + if (getcwd(currdir, MAXPL)) +#endif + setval("cwd", currdir, &vlist); + else + write(2, "Can't get cwd properly\n", 23); + setval("prompt", "% ", &vlist); /* Set the prompt to % for now */ + setval("Version", "2.0.41", &vlist); + catchsig(); /* Catch signals */ + getstty(); /* Set up the stty for Wish */ + terminal(); /* Get the termcap strings */ + +#ifndef NO_BIND + initbind(); /* Set the default key bindings */ +#endif + + argv[0] = "source"; + home= EVget("HOME"); + if (home) + { argv[1]= Malloc(strlen(home) + strlen(".wishrc")+3, "setup malloc"); + sprints(argv[1],"%s/.wishrc", home); + } + else argv[1] = ".wishrc"; /* Source .wishrc */ + source(2, argv); + free(argv[1]); + if (*Argv[0]=='-') /* If we're a login shell */ + { + Loginshell= TRUE; /* Make sure we know */ + if (home) + { argv[1]= Malloc(strlen(home) + strlen(".wishlogin")+3, "setup malloc"); + sprints(argv[1],"%s/.wishlogin", home); + } + else argv[1] = ".wishlogin"; /* Source .wishlogin */ + source(2, argv); + free(argv[1]); + } + saveh = TRUE; +} + + +/* Doline is the loop which gets a line from the user, runs it through + * the metachar expansion, and finally calls command() to execute it. + * It returns when there are no more lines available - so it really + * should be called dolines()! + * + * If isalias is FALSE, we are not expanding aliases. Otherwise we are, + * so don't clear the carray, our args are in there. Also, how will + * then hold H_BCKGND if we are a subshell. + */ + +void +doline(isalias) + int isalias; +{ + extern struct candidate carray[], *wordlist; /* Wordlist is list or words */ + extern int Exitstatus, ncand; + int i; + +/* Nextword holds the word for the next pipeline, + * termword holds the token that terminated the pipeline. + */ + struct candidate *termword; + char *linebuf; + int pid, q, oldncand; + int term; + +#ifdef DEBUG + i = (int) sbrk(0); + prints("In doline, Brk value is %x\n", i); + prints("Getaline f'n ptr is %x\n", getaline); +#endif + linebuf = (char *) malloc(MAXLL); + if (linebuf == NULL) + fatal("Couldn't malloc linebuf\n"); + + while ((*getaline) (linebuf, &q) == TRUE) /* Get a line from user */ + { + if (q || *linebuf == EOS) + continue; + +#ifdef DEBUG + fprints(2, "In doline, line is %s\n", linebuf); +#endif + if (isalias == FALSE) + ncand = 0; /* Initialise the carray */ + next_word = &carray[ncand]; /* list for meta.c */ + oldncand = ncand; + + if (meta_1(linebuf, FALSE) == FALSE) + continue; /* Expand ! */ +#ifndef NO_HISTORY + if (saveh) + savehist(expline(carray), 1); /* Save the line */ +#endif + +/* At this point, we need to extract pipelines + * from the parsed line, so that we expand + * metachars properly. This means we need yet + * another loop. Oh well. + */ + for (wordlist = next_word; wordlist != NULL; wordlist = next_word) + { + /* Find pipeline end */ + while (next_word && (i = next_word->mode & C_WORDMASK) != C_SEMI && i != C_DBLAMP + && i != C_DBLPIPE) + next_word = next_word->next; + /* If we found one */ + if (next_word) + { + termword = next_word; /* Save it */ + next_word = next_word->next; /* Nextword is next pipeline */ + termword->next = NULL; /* Terminate our pipeline */ + } + meta_2(); /* Expand metacharacters */ + term = command(&pid, FALSE, NULL, FALSE); /* Actually run it here */ + + if (isalias & H_BCKGND) pid= -pid; /* Wait ALWAYS if subshell */ + if (term != C_AMP && pid) + waitfor(pid); /* Wait for it to finish */ + if (term == C_DBLAMP && Exitstatus != 0) + break; + if (term == C_DBLPIPE && Exitstatus == 0) + break; +#ifndef NO_JOB + if (saveh) + joblist(0, NULL); /* print the list of jobs */ +#endif + + } + + for (i = oldncand; i < ncand; i++) /* Free the command words */ + if (carray[i].mode == TRUE) + { + carray[i].mode = FALSE; + free(carray[i].name); + } + ncand = oldncand; + + } + free(linebuf); +} + +/* Main. This is the bit that starts the whole ball rolling. + */ +int +main(argc, argv) + int argc; + char *argv[]; + +{ + + Argc = argc; /* Set up arg variables */ + Argv = argv; +#ifdef MALLOCDEBUG + initmall(); /* Initialise the malloc arrays */ +#endif + setup(); /* Set up the vars & the termcap stuff */ + + while (1) + doline(FALSE); /* We only exit when we shutdown() */ +} diff --git a/Applications/wish/malloc.c b/Applications/wish/malloc.c new file mode 100644 index 0000000000..e42969b1be --- /dev/null +++ b/Applications/wish/malloc.c @@ -0,0 +1,79 @@ +/* This is a version of malloc which will always + * return a valid pointer, or exit(1) with a + * message. + * + * $Revision: 41.1 $ $Date: 1995/12/29 02:10:46 $ + */ + +#include "header.h" + +#ifdef MALLOCDEBUG +static void *StartSbrk; +static void *mallptr[1024]; +static int mallwho[1024]; +static int mallsize[1024]; +#endif + +char *Malloc(size, mesg) + unsigned int size; + char *mesg; + { + char *a; + + if ((a=(char *)malloc(size))==NULL) + { fprints(2,"Malloc error: %s\n",mesg); exit(1); } + return(a); + } + + +/* The following routines are non-portable, and are used solely for + * debugging malloc/free calls. + */ + +#ifdef MALLOCDEBUG +# undef free +# undef malloc + +void initmall() + { + int i; + + for (i=0;i<1024;i++) mallptr[i]=NULL; + StartSbrk= sbrk(0); + } + +void *myMalloc(size) + unsigned int size; + { + void *a; + int i, *b; + + b= (int *)&size; b--; + a= (void *)malloc(size); + if (a) + for (i=0;i<1024;i++) + if (mallptr[i]==NULL) + { mallptr[i]=a; mallwho[i]= *b; mallsize[i]=size; break; } + fprints(2,"0x%x malloc'd 0x%x, size %d\n",*b,a,size); + return(a); + } + +void myFree(ptr) + void *ptr; + { + int *b; + int i; + + b= (int *)&ptr; b--; + fprints(2,"0x%x ",*b); + if (ptr<= StartSbrk) + { fprints(2,"Trying to free a bad pointer, 0x%x\n",ptr); exit(1); } + else + { + for (i=0;i<1024;i++) if (mallptr[i]==ptr) { mallptr[i]=NULL; break; } + if (i==1024) { fprints(2,"Unmalloc'd ptr 0x%x\n",ptr); exit(1); } + fprints(2,"%d Freeing 0x%x\n",i,ptr); + free(ptr); + } + } +#endif /* MALLOCDEBUG */ diff --git a/Applications/wish/meta.c b/Applications/wish/meta.c new file mode 100644 index 0000000000..dcd1fc3e88 --- /dev/null +++ b/Applications/wish/meta.c @@ -0,0 +1,1073 @@ +/* Metacharacter expansion. This file holds the routines that do the first + * parsing of the command line, and the expansion of the metacharacters + * ' " ` \ $ ~ * ? and [ + * + * $Revision: 41.2 $ $Date: 1996/06/14 06:24:54 $ + */ + +#include "header.h" +#define MAXMATCH 512 +#define OK 0 +#define NX_ERR -1 +#define BR_ERR -2 + + +extern struct candidate carray[]; /* The matched files */ +extern struct candidate *next_word; +int ncand; +struct candidate *wordlist; /* The list of words for the parser */ +static int Mode; /* Used by star */ +static char *qline; /* ``'d line from backquot */ + +#ifdef PROTO +static int matchdir(char *directory, char *pattern); +#else +static int matchdir(); +#endif +#ifdef DEBUG +static void listcarray(); +#endif + +/* Match takes a string, and a pattern, which can contain *, ? , \ and [], + * and returns 0 if the strings matched the pattern, otherwise a negative + * number. If the pattern ends in a '/', matchdir is called with the + * arg accumdir/string to find further matches. Match also adds each match + * into the matches array given above. + */ +#ifdef PROTO +static int match(char *string, char *pattern, char *accumdir) +#else +static int +match(string, pattern, accumdir) + char *string, *pattern, *accumdir; +#endif +{ + char c, *findex, *pindex, *fmatch, *pmatch, rempat[MAXWL], where[MAXPL]; + int i, mismatch, found, star; + + findex = string; /* Initialise our vars */ + pindex = pattern; + if ((*findex == '.') && (*pindex != '.')) /* Don't match . unless asked */ + return (NX_ERR); + mismatch = star = 0; + while (*findex && !mismatch) /* While no mismatch */ + { + switch (*pindex) /* Match the pattern's char */ + { + case '*': + pindex++; /* Skip the '*' */ + pmatch = pindex; /* We must match from here on */ + fmatch = findex; + star = 1; /* Found a star */ + break; + case '?': + pindex++; /* just increment, letter automatically + matches. */ + findex++; + break; + case '[': + found = 0; /* For all chars in [] */ + for (; (*pindex) && (*pindex != ']'); pindex++) + { + switch (*pindex) + { + case '\\': + if (*(pindex + 1) == *findex) /* Try match on next char */ + found = 1; + break; + case '-': + if ((*(pindex + 1) == ']') || (*(pindex - 1) == '[')) + if (*pindex == *findex) + { + found = 1; /* Try a match on '-' */ + break; + } /* or try all chars in the range */ + for (c = *(pindex - 1) + 1; c <= *(pindex + 1) && !found && c != ']'; c++) + if (c == *findex) + found = 1; + break; + default: /* Default: just match that char */ + if (*pindex == *findex) + found = 1; + break; + } + } +/* + * We could exit the loop as soon as found is set to 1 but we have to + * keep going to the end of the list so we can recommence matching + * after the ']'. This also allows us to test for the unmatched + * bracket. + */ + if (*pindex == EOS) + { + fprints(2, "Unmatched bracket '['\n"); + return (BR_ERR); + } + if (found) + { + pindex++; /* move past the ']' */ + findex++; /* and on to the next letter */ + break; + } + if (!star) + mismatch = 1; /* failure for this letter of file */ + else + { + pindex = pmatch; + findex = ++fmatch; + } + break; + case '\\': + pindex++; /* go on to next symbol */ + + default: + if (*findex != *pindex) /* No match and not a star */ + if (!star) /* gives an error */ + mismatch = 1; + else + { + pindex = pmatch; /* Move back to the char we must */ + findex = ++fmatch; /* match, & move along string */ + } + else + { + pindex++; /* Yes, a match, move up */ + findex++; + } + break; + } + } + if (mismatch) + return (NX_ERR); /* it didn't match, go on to next one */ + for (; *pindex == '*'; pindex++); /* get rid of trailing stars in pattern */ + if (*findex == *pindex) + { + /* add it to the candidate list */ + where[0] = EOS; + if (*accumdir) + { + strcpy(where, accumdir); + strcat(where, "/"); + } + strcat(where, string); + if (ncand == MAXCAN) + return (OK); + carray[ncand].name = (char *) malloc((unsigned) (strlen(where) + 4)); + if (carray[ncand].name == NULL) + return (OK); + strcpy(carray[ncand].name, where); + carray[ncand++].mode = Mode | TRUE | C_SPACE; /* Was malloc'd */ + return (OK); + } + else if (*pindex == '/') /* test if file is a directory */ + { + for (i = 0; *pindex; i++, pindex++) + rempat[i] = *(pindex + 1);/* even copies null across */ + strcpy(where, accumdir); + if (where[0] != EOS) + { + strcat(where, "/"); + strcat(where, string); + } + else + strcpy(where, string); + return (matchdir(where, rempat)); /* recursively call this pair again */ + } + else /* it just doesn't match */ + return (NX_ERR); +} + +/* Matchdir takes a directory name and a pattern, and returns 0 if any + * files in the directory match the pattern. If none, it returns a negative + * number. Note that this may be recursive, as it calls match(), which calls + * matchdir(). + */ +#ifdef PROTO +static int matchdir(char *directory, char *pattern) +#else +static int +matchdir(directory, pattern) + char *directory, *pattern; +#endif +{ + DIR *dirp; + +#ifdef USES_DIRECT + struct direct *entry; +#else + struct dirent *entry; +#endif + int foundany = 0; + + if (*directory != EOS) + { + if ((dirp = opendir(directory)) == NULL) + return (NX_ERR); + } + else + { + if ((dirp = opendir(".")) == NULL) + return (NX_ERR); + } + while ((entry = readdir(dirp)) != NULL) + if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..") && + !match(entry->d_name, pattern, directory)) + foundany++; + closedir(dirp); + if (foundany) + return (OK); + else + return (NX_ERR); +} + +#ifdef PROTO +static void finddir(char *word, char *dir) +#else +static +void +finddir(word, dir) + char *word, *dir; +#endif +{ + char c; + int i = 0, j, l = 1; + +/* This function finds the directory to start the matching from. */ + while (l) + switch (c = word[i]) + { + case '*': + case '?': + case '[': + for (; dir[i] != '/' && i; i--); /* go back to end of previous + component */ + if (i) + dir[i++] = EOS; + else if (dir[0] == '/') + dir[++i] = EOS; + else + dir[0] = EOS; + l = 0; + break; + default: + dir[i++] = c; + } + for (l = i, j = 0; word[l]; l++, j++) + word[j] = word[l]; + word[j] = EOS; +} + + +#ifndef NO_TILDE +/* Tilde takes the word beginning with a ~, and returns the word with the + * first part replaced by the directory name. If ~/, we use $HOME + * e.g ~fred -> /u1/staff/fred + * ~jim/Dir/a*.c -> /usr/tmp/jim/Dir/a*.c + * ~/Makefile -> /u1/staff/warren/Makefile + * If it cannot expand, it returns dir as a zero-terminated string + * + * It also uses the tilde list now as well. + */ +void +tilde(word, dir) + char *word, *dir; +{ + extern struct vallist tlist; + + struct passwd *entry; + struct val *t; + char *a; + + word++; + *dir = EOS; + if (*word == '/') /* use $HOME if ~/ */ + { + a = word; + entry = getpwuid(getuid()); + strcpy(dir, entry->pw_dir); + } + else + { + a = strchr(word, '/'); /* Get the word to parse */ + if (a != NULL) + *a = EOS; + for (t = tlist.head; t; t = t->next) /* Try the tilde list next */ + { + if (!strcmp(word, t->name)) + { + strcpy(dir, t->val); + break; + } + } + if (*dir == EOS) /* Finally, consult the passwd file */ + { + entry = getpwnam(word); /* Get the directory's name */ + if (entry) + strcpy(dir, entry->pw_dir); + } + } + endpwent(); + if (*dir == EOS) + return; + /* Form the real partial name */ + if (a != NULL) + { + *a = '/'; + strcat(dir, a); + } +} +#endif + +/* Dollar expands a variable. It takes a pointer to a candidiate, + * and replaces it with the variable's value. + */ +#ifdef PROTO +static void dollar(struct candidate *cand) +#else +static void +dollar(cand) + struct candidate *cand; +#endif +{ + extern int Exitstatus, Argc; + extern char **Argv; + struct candidate *next; + char *end, *value; + char c = EOS; + char *a, *doll; + unsigned int length, base, mall = 0; /* Mall is 1 if we've malloc'd */ + + + doll = cand->name; + /* If a special variable */ + if (strlen(doll) == 1 || doll[1] == '=') + { + switch (*doll) + { + case '$': /* Replace with pid */ + value = (char *) malloc(10); + length = getpid(); + if (value) + { + sprints(value, "%d", length); + mall = 1; + } + break; + case '#': /* Replace with # of args */ + value = (char *) malloc(10); + if (value) + { + sprints(value, "%d", Argc - 1); + mall = 1; + } + break; + case '?': /* Replace with exist status */ + value = (char *) malloc(10); + if (value) + { + sprints(value, "%d", Exitstatus); + mall = 1; + } + break; + case '0': + case '1': + case '2': + case '3': + case '4': /* Replace with Argv[n] */ + case '5': + case '6': + case '7': + case '8': + case '9': + length = *doll - '0'; + value = (length < Argc ? Argv[length] : ""); + break; + case '*': /* Replace with all args */ + if (Argc > 1) + { + base = ncand; + ncand += (Argc - 1); + carray[ncand - 1].next = cand->next; + carray[ncand - 1].name = Argv[Argc - 1]; + carray[ncand - 1].mode = C_SPACE; + cand->next = &carray[base]; + for (length = 1; base < ncand - 1; base++, length++) + { + carray[base].name = Argv[length]; + carray[base].mode = C_SPACE; + carray[base].next = &carray[base + 1]; + } + } + if (cand->mode & TRUE) + { + free(cand->name); + cand->mode = FALSE; + } + cand->name = NULL; + return; + default: + fprints(2, "Unknown variable $%c\n", *doll); + return; + } + if (doll[1] == '=') /* An assignment */ + { + end = doll + 1; + c = '='; + } + } + else + { /* A normal variable */ + /* Find the last char of the var */ + for (end = doll; end != EOS && isalnum(*end); end++); + c = *end; + *end = EOS; /* Terminate the name */ + + value = EVget(doll); /* Get the value */ + if (!value) + { + fprints(2, "Unknown variable $%s\n", doll); + return; + } + else if (c == '[') /* or a field of it */ + { + length = atoi(++end); /* Get the field number */ + /* Skip the number */ + while (*end != ']' && *end != EOS) + end++; + if (*end == ']') + end++; + c = *end; + *end = EOS; + for (a = value; length; length--) + { /* Find a gap */ + if (!(a = strpbrk(a, " \t"))) + { + value = NULL; + break; + } + while (*a == ' ' || *a == '\t') + a++; /* Skip the gap */ + if (*a == EOS) + { + value = NULL; + break; + } + } + if (value) /* If a valid field */ + { + length = strcspn(a, " \t"); /* Get the length of the field */ + value = (char *) malloc(length + 2); + if (value) + { + strncpy(value, a, length); /* Make a copy - can't change var */ + value[length] = EOS; + mall = TRUE; + } + } + } + } + + cand->mode &= ~C_DOLLAR; + if (c) /* If there's more to the value */ + { + *end = c; /* Insert the rest in the list */ + next = cand->next; + addcarray(end, cand, cand->mode | TRUE, TRUE); + carray[ncand - 1].next = next; + cand->mode &= ~C_SPACE; + } + if (cand->mode & TRUE) + free(cand->name); /* we can replace totally */ + cand->name = value; + if (mall) + cand->mode |= TRUE; + else + cand->mode &= ~TRUE; +#ifdef DEBUG + fprints(2, " In dollar "); + listcarray(); +#endif + return; +} + + +/* Expline takes a linked list of words, and converts them into a normal + * string. This is used to save history. No double quotes now. + */ +char * +expline(list) + struct candidate *list; +{ + struct candidate *q; + char *out, *a; + int i, mode = 0; + bool lastspace = FALSE; + + /* Find the amount to malloc */ + for (i = 0, q = list; q != NULL; q = q->next) + if (q->name) + i += strlen(q->name) + 6; /* Just in case */ + + out = (char *) malloc((unsigned) i + 4); + if (out == NULL) + return (NULL); + + for (a = out, q = list; q; q = q->next) + { + mode = q->mode; + if (q->name) /* It's a word */ + { + if (lastspace) + *(a++) = ' '; + if (mode & C_QUOTE) + *(a++) = '\''; + if (mode & C_CURLY) + *(a++) = '{'; + if (mode & C_BACKQUOTE) + *(a++) = '`'; + if (mode & C_DOLLAR) + *(a++) = '$'; /* Add $ to vars */ + strcpy(a, q->name); + a += strlen(q->name); /* Add the word!! */ + if (mode & C_QUOTE) + *(a++) = '\''; + if (mode & C_CURLY) + *(a++) = '}'; + if (mode & C_BACKQUOTE) + *(a++) = '`'; + lastspace = mode & C_SPACE; + } + else + { + if (lastspace) + *(a++) = ' '; + switch (mode & C_WORDMASK) + { + case C_SEMI: + strcpy(a, "; "); + a += 2; + break; + case C_PIPE: + strcpy(a, "| "); + a += 2; + break; + case C_DBLPIPE: + strcpy(a, "|| "); + a += 3; + break; + case C_AMP: + strcpy(a, "& "); + a += 2; + break; + case C_DBLAMP: + strcpy(a, "&& "); + a += 3; + break; + case C_LT: + strcpy(a, "< "); + a += 2; + break; + case C_LTLT: + strcpy(a, "<< "); + a += 3; + break; + case C_GT: + if ((mode & C_FD) == 2) + { + strcpy(a, ">& "); + a += 3; + } + else + { + strcpy(a, "> "); + a += 2; + } + break; + case C_GTGT: + strcpy(a, ">> "); + a += 3; + break; + } + lastspace = FALSE; + } + } + return (out); +} + +#ifdef DEBUG +/* Wordlist is used for debugging to print out the carray linked list */ +static void +listcarray() +{ + struct candidate *curr; + + fprints(2, "Here's the wordlist:\n"); + for (curr = wordlist; curr != NULL; curr = curr->next) + { + fprints(2, "--> %x ", curr); + if (curr->name) + fprints(2, "%s", curr->name); + if (curr->mode & C_WORDMASK) + switch (curr->mode & C_WORDMASK) + { + case C_SEMI: + fprints(2, "; C_SEMI"); + break; + case C_PIPE: + fprints(2, "| C_PIPE"); + break; + case C_DBLPIPE: + fprints(2, "|| C_DBLPIPE"); + break; + case C_AMP: + fprints(2, "& C_AMP"); + break; + case C_DBLAMP: + fprints(2, "&& C_DBLAMP"); + break; + case C_LT: + fprints(2, "< C_LT"); + break; + case C_LTLT: + fprints(2, "<< C_LTLT"); + break; + case C_GT: + fprints(2, "%d> C_GT", curr->mode & C_FD); + break; + case C_GTGT: + fprints(2, "%d>> C_GTGT", curr->mode & C_FD); + break; + default: + fprints(2, "%o C_UNKNOWN", curr->mode); + } + else + { + if (curr->mode & C_SPACE) + fprints(2, " C_SPACE"); + if (curr->mode & C_DOLLAR) + fprints(2, " C_DOLLAR"); + if (curr->mode & C_QUOTE) + fprints(2, " C_QUOTE"); + if (curr->mode & C_CURLY) + fprints(2, " C_DBLQUOTE"); + if (curr->mode & C_BACKQUOTE) + fprints(2, " C_BACKQUOTE"); + if (curr->mode & TRUE) + fprints(2, " (malloc'd)"); + } + fprints(2, " %x\n", curr->next); + } +} + +#endif + +/* Addword is used by Meta_1 to add a word into the carray list. + * This is where history is expanded. + */ +#ifdef PROTO +static void addword(char *string, int mode) +#else +static void +addword(string, mode) + char *string; + int mode; +#endif +{ + char *b; + + if (string != NULL) /* It's a word, check it out */ + { + if (*string == EOS) + return; + +#ifndef NO_HISTORY + /* Find any history and retrieve it */ + if ((mode & (C_QUOTE | C_BACKQUOTE | C_DOLLAR | C_CURLY)) == 0) + if ( *string == '!' && (b = gethist(++string)) != NULL) + { + meta_1(b, FALSE); /* Expand it too */ + return; + } +#endif + /* mode &= ~FALSE; I have NO idea what this does! */ + } + addcarray(string, &carray[ncand - 1], mode, mode & TRUE); +} + + +/* Star expands * ? and [] in the carray */ +#ifdef PROTO +static void star(struct candidate *curr) +#else +static void +star(curr) + struct candidate *curr; +#endif +{ + char dir[MAXWL]; + struct candidate *a; + int base; + + Mode = curr->mode & ~(TRUE | C_SPACE); + base = ncand; /* Save start of expansion */ + dir[0] = EOS; + finddir(curr->name, dir); /* expand them */ + a = curr->next; + if (!matchdir(dir, curr->name)) + { + qsort((void *) &carray[base], ncand - base, sizeof(struct candidate), compare); + /* Now insert into list */ + carray[ncand - 1].next = a; + curr->next = &carray[base]; + for (; base < ncand - 1; base++) + carray[base].next = &carray[base + 1]; + } + if (curr->mode & TRUE) + { + free(curr->name); + curr->mode = FALSE; + } + curr->name = NULL; +#ifdef DEBUG + fprints(2, " In star "); + listcarray(); +#endif +} + +/* Getbackqline is called when backquot forks to run a `command`. + */ +#ifdef PROTO +static bool getbackqline( uchar *line, int *nosave) +#else +static bool +getbackqline(line, nosave) + uchar *line; + int *nosave; +#endif +{ + *nosave = 0; + if (qline) + { + strcpy(line, qline); + qline = NULL; + return (TRUE); + } + else + return (FALSE); +} + +/* Backquot expands backquotes */ +#ifdef PROTO +static void backquot(struct candidate *curr) +#else +static void +backquot(curr) + struct candidate *curr; +#endif +{ +#ifdef PROTO + extern bool(*getaline) (uchar *line , int *nosave ); +#else + extern bool(*getaline) (); +#endif + extern int saveh; + struct candidate *first; + int term, base; + int pfd[2]; + char *line; + + term = pipe(pfd); + if (term == -1) + { + fprints(2, "Cannot open pipe in backquot\n"); + return; + } + switch (fork()) + { + case -1: + fatal("Bad fork in meta_4\n"); + + case 0: + /* Redirect our output */ + close(pfd[0]); + close(1); + dup(pfd[1]); + close(pfd[1]); + qline = curr->name; + getaline = getbackqline; /* Pass line to doline */ + saveh = FALSE; + doline(FALSE); + exit(0); /* and die */ + default: + close(pfd[1]); + base = ncand; /* Get start of expansion */ + first = curr; + curr = curr->next; /* Move curr up now */ + line = (char *) malloc(MAXLL); + if (line == NULL) + { + fprints(2, "Can't malloc line in backquot\n"); + return; + } + fileopen(NULL, pfd[0]); + while (getfileline(line, &term)) + meta_1(line, C_SPACE | TRUE); /* Add words to the carray */ + fileclose(); + free(line); + /* Delete first node */ + if (first->mode & TRUE) + free(first->name); + first->name = NULL; + first->mode = 0; + /* Insert words in list */ + if (ncand > base) + { + carray[base - 1].next = NULL; /* Undo addcarray */ + first->next = &carray[base]; + carray[ncand - 1].next = curr; + } + } +#ifdef DEBUG + fprints(2, " In backquot "); + listcarray(); +#endif +} + +/* Meta_1 takes the user's input line (old), and builds a linked list of words + * in the carray. It also expands history. Meta_1 returns TRUE if + * successful; if any errors occur, it returns FALSE. + * + * If mustmalc is TRUE, meta_1 assumes the word must be strcpy'd. + * As of version 38, support for double quotes is gone, and backquotes are + * treated in the same way as single quotes. + */ +bool +meta_1(old, mustmalc) + char *old; + bool mustmalc; +{ + char bq[3]; /* Used to find backslashes in quotes */ + char *a, *old2; + char c; + int mode; + int lastdollar = 0; /* Was last word a variable? */ + + bq[0]= '\\'; bq[2]= EOS; + while (1) /* Parse each word */ + { + a = strpbrk(old, " \t\n\\'`{;|<>&$"); + /* why `&& *old' below - Not all strpbrk()s give NULL when *old=EOS */ + if (a != NULL && *old) /* We found a word */ + { + c = *a; + *a = EOS; + mode = (c == ' ' || c == '\t') ? C_SPACE : 0; + /* Add the word to the list */ + if (a > old) + addword(old, mode | lastdollar | mustmalc); + + mode = 0; + switch (c) /* Now deal with the `token' */ + { + case '{': + mode = C_CURLY; + c = '}'; + goto quotes; /* Yuk, a goto */ + case '`': + mode = C_BACKQUOTE; + goto quotes; /* Yuk, a goto */ + case '\'': + mode = C_QUOTE; + quotes: + old2= old = ++a; bq[1]= c; + quotes2: + a = strpbrk(old2,bq); /* Find either slosh or matching quote */ + if (a == NULL) + { + fprints(2, "No matching %c\n", c); + return (FALSE); + } + if (*a == '\\') + { + if (a>old) /* there is something before the slosh */ + { + *a= EOS; + addword(old, mode | lastdollar | mustmalc); + } + old= ++a; old2= old+1; + goto quotes2; /* Yuk, another goto */ + } + *(a++) = EOS; /* Terminate the word */ + if (*a == ' ' || *a == '\t') /* Check for trailing spaces */ + { + a++; + mode |= C_SPACE; + } + addword(old, mode | lastdollar | mustmalc); + break; + case '\\': + old = (char *) malloc(2); + if (old) + { + old[0] = *(a + 1); + old[1] = EOS; + addword(old, mode | lastdollar | TRUE); + } + a += 2; + break; + case '|': + mode = C_PIPE; + goto doubles; /* Yuk, a goto */ + case '&': + mode = C_AMP; + goto doubles; /* Yuk, a goto */ + case '<': + mode = C_LT; + doubles: + a++; + if (*a == c) + { + a++; + mode += C_DOUBLE; + } + addword(NULL, mode); + break; + case '>': + mode = 1 | C_GT; /* Default to stdout */ + switch (*(a + 1)) + { + case '>': + mode = 1 | C_GTGT; + a++; + break; + case '&': + mode = 2 | C_GT; + a++; + } + addword(NULL, mode); + a++; + break; + case ';': + addword(NULL, C_SEMI); + case '$': + if (lastdollar && (a==old)) /* We found a $$ */ + { + a++; mode= C_DOLLAR | TRUE ; + mode|= ((*a==' ')||(*a=='\t')) ? C_SPACE : FALSE; + addword("$",mode); a--; + c=' '; /* Don't recognise 2nd $ below */ + } + case ' ': + case '\t': + a++; + case '\n': + break; + default: + fprints(2, "Unknown token %c in meta_1", c); + return (FALSE); + } + lastdollar = (c == '$') ? C_DOLLAR : 0; /* Mark variables */ + + /* Ok, skip intermediate spaces */ + for (old = a; *old && (*old == ' ' || *old == '\t'); old++); + } + else + { + addword(old, lastdollar | mustmalc); + break; + } + } +#ifdef DEBUG + fprints(2, " In meta_1 "); + wordlist = next_word; + listcarray(); +#endif + return (TRUE); +} + + +/* Joinup concatenates two carray elements together. The following pointers + * must be non-null: curr, curr->next, curr->name, curr->next->name. + */ +#ifdef PROTO +static void joinup(struct candidate *curr) +#else +static void +joinup(curr) + struct candidate *curr; +#endif +{ + char *a; + + a = (char *) malloc((unsigned) (strlen(curr->name) + strlen(curr->next->name) + 1)); + if (!a) + return; + strcpy(a, curr->name); + strcat(a, curr->next->name); + if (curr->mode & TRUE) + free(curr->name); + curr->mode = curr->next->mode | TRUE; + curr->next = curr->next->next; + curr->name = a; +} + +/* Meta_2 expands $ and ~ in the carray */ +void +meta_2() +{ + struct candidate *curr; + char *a, tildir[MAXWL]; + + for (curr = wordlist; curr != NULL; curr = curr->next) + { + a = curr->name; + if (a == NULL || curr->mode & C_QUOTE) + continue; + +#ifndef NO_TILDE + if (*a == '~') /* If a ~ */ + { + tilde(a, tildir); /* expand it too */ +#ifdef DEBUG + fprints(2, "tildir is %s\n", tildir); +#endif + if (curr->mode & TRUE) + free(curr->name); + curr->name = (char *) malloc((unsigned) strlen(tildir) + 4); + if (curr->name != NULL) + { + strcpy(curr->name, tildir); + curr->mode = TRUE | C_SPACE; + } + } +#endif + + if (curr->mode & C_DOLLAR) /* If it has a $ */ + dollar(curr); /* expand it */ + + } + for (curr = wordlist; curr != NULL; curr = curr->next) + { + while (curr->name && curr->next && curr->next->name && !(curr->mode & C_SPACE)) + joinup(curr); /* No space, we'd better join em */ + +#ifdef NOTYET + if (curr->mode & C_CURLY) /* If it is in {} */ + curly(curr); /* expand it */ +#endif + + if (curr->mode & C_BACKQUOTE) /* Ha! Found a backquote word */ + backquot(curr); + + if (!(curr->mode & C_QUOTE) && curr->name && strpbrk(curr->name, "*?[")) + star(curr); + + while (curr->name && curr->next && curr->next->name + && !(curr->mode & ~TRUE) && !(curr->next->mode & ~(TRUE|C_SPACE))) + joinup(curr); /* No space, we'd better join em */ + } +#ifdef DEBUG + fprints(2, " In meta_2 "); + listcarray(); +#endif +} diff --git a/Applications/wish/parse.c b/Applications/wish/parse.c new file mode 100644 index 0000000000..5afa3b14c5 --- /dev/null +++ b/Applications/wish/parse.c @@ -0,0 +1,186 @@ +/* Parsing the input file + * + * $Revision: 41.1 $ $Date: 1995/12/29 02:10:46 $ + */ + +#include "header.h" + +/* The parser uses the symbols parsed by meta.c, but it needs a few more + * definitions for proper parsing. These are below. + */ + +#define C_WORD C_SPACE /* This indicates the returned word is a */ + /* usual word */ +#define C_EOF FALSE /* Returned when no more tokens left */ + +extern struct candidate *wordlist; /* The list of words to parse */ +static struct candidate *pcurr; /* A ptr to the word we are parsing */ + +/* Gettoken returns the symbol which stands for the current word, or 0. + * If 0, word points to a string that holds a normal word. This is guaranteed + * not to be clobbered until we get back out to main(). + */ +#ifdef PROTO +static int gettoken(char **word, int *fd) +#else +static int +gettoken(word, fd) + char **word; + int *fd; +#endif +{ + int mode; + + /* Skip any null words */ + while (1) + { + /* Return EOF on end of list */ + if (pcurr == NULL) + return (C_EOF); + mode = pcurr->mode; + + if (mode & C_WORDMASK) /* We've found a token, return */ + { + *word = NULL; + *fd = mode & C_FD; + mode &= C_WORDMASK; /* stripping any fd */ + pcurr = pcurr->next; + return (mode); + } + + if (pcurr->name && *(pcurr->name) != EOS) + { + *word = pcurr->name; /* just return it */ + pcurr = pcurr->next; + return (C_WORD); + } + pcurr = pcurr->next; /* Not a valid word or symbol, skip */ + } +} + +/* Command parses the input and calls invoke() to redirect and execute + * each simple command in the parsebuf. It returns the pid to wait on + * in waitpid, or 0 if no pid to wait on, as well as the token that ended + * the pasebuf. This routine is recursive, and when passed makepipe=TRUE, + * makes a pipe, and returns the fd of the writing end in pipefdp. + */ +int +command(waitpid, makepipe, pipefdp, anydups) /* Do simple command */ + int *waitpid, *pipefdp; + int makepipe, anydups; +{ + int token, term; + int argc, pid, pfd[2]; + char *argv[MAXARG + 1]; + char *word; + int i, fd, how = 0; + struct rdrct newfd[3]; + + argc = 0; + argv[0] = NULL; + for (i = 0; i < 3; i++) + { + newfd[i].fd = 0; + newfd[i].how= 0; + newfd[i].file = NULL; + } + if (makepipe == FALSE) + pcurr = wordlist; /* Start parsing the wordlist */ + + while (1) + { + token = gettoken(&word, &fd); +#ifdef DEBUG + prints("Token is %o", token); + if (word != NULL) + prints(" word is %s", word); + write(1, "\n", 1); +#endif + switch (token) + { + case C_WORD: + if (argc == MAXARG) + { + fprints(2, "Too many args\n"); + break; + } + argv[argc++] = word; + continue; + case C_LT: + if (makepipe) + { + fprints(2, "Extra <\n"); + break; + } + if (gettoken(&(newfd[fd].file), &i) != C_WORD) + { + fprints(2, "Illegal <\n"); + break; + } + newfd[fd].how = H_FROMFILE; + anydups= TRUE; + continue; + case C_GT: + case C_GTGT: + if (newfd[fd].fd != 0 || newfd[fd].file != NULL) + { + fprints(2, "Extra > or >>\n"); + break; + } + if (gettoken(&(newfd[fd].file), &i) != C_WORD) + { + fprints(2, "Illegal > or >>\n"); + break; + } + if (token == C_GTGT) + newfd[fd].how = H_APPEND; + anydups= TRUE; + continue; + case C_AMP: /* If a pipe, call ourselves to get */ + case C_EOF: /* the write file descriptor */ + case C_SEMI: + case C_DBLAMP: + case C_DBLPIPE: + case C_PIPE: + argv[argc] = NULL; + if (token == C_PIPE) + { + anydups= TRUE; + if (newfd[1].fd != 0 || newfd[1].file != NULL) + { + fprints(2, "> or >> conflicts with |\n"); + break; + } + term = command(waitpid, TRUE, &(newfd[1].fd),anydups); + } /* and set up the terminal token */ + else + term = token; + /* If called by us, make the needed pipe */ + if (makepipe) + { + if (pipe(pfd) == -1) + { + perror("pipe"); + break; + } + /* and return the write file descriptor */ +#ifdef DEBUG + fprints(2, "Opened pipe fds %d and %d\n", pfd[0], pfd[1]); +#endif + *pipefdp = pfd[1]; + newfd[0].fd = pfd[0]; + } + if (term == C_AMP) + how = H_BCKGND; + pid = invoke(argc, argv, newfd, how, anydups); + /* End of command line, return pid to wait */ + if (token != C_PIPE) + *waitpid = pid; + if (argc == 0 && token != C_EOF) + fprints(2, "Missing command\n"); + return (term); + default: + prints("Unknown token %o in command()\n", token); + } + } +} diff --git a/Applications/wish/posixjob.c b/Applications/wish/posixjob.c new file mode 100644 index 0000000000..98d5e97e9f --- /dev/null +++ b/Applications/wish/posixjob.c @@ -0,0 +1,148 @@ +/* POSIX Job Control functions + * + * $Revision: 41.2 $ $Date: 1996/06/14 06:24:54 $ + * + */ + +#define WIFCORE(x) (FALSE) /* We can't tell */ +#define WRUNFG(x) ((x) == RUNFG) +#define WRUNBG(x) ((x) == RUNBG) + +/* The following tables are all VERY OS-dependent, especially on POSIX + * systems. If you find the shell giving you weird signal messages, you + * should change the names of the signals below. + */ +static char *siglist[] = { + "", "Hangup", "Interrupt", "Quit", "Illegal Instruction", + "Trace/BPT Trap", "IOT Trap", "EMT Trap", "Floating Point Exception", + "Killed", "Bus Error", "Segmentation Violation", "Bad System Call", + "Broken Pipe", "Alarm", "Terminated" ,"Urgent Socket Condition", + "Stopped (signal)", "Stopped", "Continue", "Child Status Change", + "(tty input)", "(tty output)", "I/O", "Cpu Time Limit", + "File Size Limit", "Virtual Time Alarm", "Profile Alarm", + "Window Change", "Resource Lost", "User Signal 1", "User Signal 2" +}; + +char *signame[] = { + "", "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "EMT", "FPE", "KILL", + "BUS", "SEGV", "SYS", "PIPE", "ALRM", "TERM" ,"URG", "STOP", "TSTP", + "CONT", "CHLD", "TTIN", "TTOU", "IO", "XCPU", "XFSZ", "VTALRM", "PROF", + "WINCH", "LIST", "USR1", "USR2", NULL +}; + +/* Waitfor is called to wait for a requested job to stop/die. It receives the + * new status of any children. If it's the requested job, we return. Pid holds + * the process-id of the job we are after; if it is 0, we return when no + * processes are left waiting to report a change in state. + * + * If pid is negative, we are a subshell and _must_ return only when the pid + * dies. We set the subshell flag and make pid positive again. Then, if we + * find out that the job was stopped, we wake it up. This works because we + * are put to sleep at the same time as the child, and the original shell + * fg()s _us_. + */ +void +waitfor(pid) + int pid; +{ + struct job *thisjob; + int wpid; + bool subshell=FALSE; + int status; + + int waitflags; + waitflags = (pid == 0) ? WNOHANG | WUNTRACED : WUNTRACED; + +#ifdef DEBUG + fprints(2,"In waitfor\n"); +#endif + + if (pid<0) { pid= -pid; subshell= TRUE; } + while (1) + { + wpid = waitpid(-1, &status, waitflags); + +#ifdef DEBUG + fprints(2,"waitxx() returned\n"); +#endif + if (wpid == -1 || wpid == 0) + break; + + if (subshell && WIFSTOPPED(status)) /* We can't return yet */ + { + kill(wpid, SIGCONT); /* Wake the child up first - waah! */ + continue; + } +#ifndef NO_JOB + thisjob = findjob(wpid); + if (thisjob == NULL) + continue; + thisjob->status = status; + thisjob->changed = TRUE; +#endif + if (pid == wpid) + return; + } +} + +/* Stopjob is only called when we received a SIGTSTP. If we are wait3()ing + * a pid, we send a SIGSTOP to that pid. This should then cause wait3() + * to exit, and thus waitfor() will return. + */ +SIGTYPE +stopjob(a) + int a; +{ + signal(SIGTSTP, stopjob); +#ifdef DEBUG + fprints(2,"In stopjobs\n"); +#endif + if (currentjob) + { +#ifdef DEBUG + fprints(2,"About to stop pid %d\n", currentjob->pid); +#endif + kill(currentjob->pid, SIGSTOP); + } +} + + +static void +bgstuff(pid) + int pid; +{ + struct job *ptr; + + setpgid(pid, pid); +#ifdef DEBUG + fprints(2,"About to SIGCONT %d\n", pid); +#endif + kill(pid, SIGCONT); +#ifndef NO_JOB + ptr = findjob(pid); + if (ptr) { ptr->STATUS = RUNBG; currentjob= ptr; } +#endif +} + + +static int +fgstuff(pid) + int pid; +{ + +/* Under UCBJOB and POSIXJOB, we don't try & bring the pid into our + * pgrp, that doesn't work. Instead, we move the terminal + * over to that pgrp, and move ourselves to that pgrp as well. + */ + if (tcsetpgrp(0, pid) == -1) /* Move the terminal to that pgrp */ + { + perror("fg tcsetpgrp"); + return (1); + } + if (setpgid(0, pid) == -1) /* Set shell's process group to the pid's */ + { + perror("fg setpgid"); + return (1); + } + return(0); +} diff --git a/Applications/wish/prints.c b/Applications/wish/prints.c new file mode 100644 index 0000000000..b4030cf4d2 --- /dev/null +++ b/Applications/wish/prints.c @@ -0,0 +1,353 @@ +/* This file contains functions that replace printf, fprintf and sprintf, + * which are smaller and do not use stdio buffering. They can only handle + * %c %s %o %d and %x as format types, although %2x and %02x can be done as + * well. Note that fprintf takes as its first argument a file descriptor, + * not a FILE pointer. + * + * These routines were derived from the prints routine in Minix 1.5. + * + * $Revision: 41.2 $ $Date: 2003/04/21 13:09:53 $ + */ +#define NO_PRINTS_DEFN +#include "header.h" + +/* If your compiler/system supports one of the following, + * you should use it, preferably stdarg.h. + */ +#ifdef STDARG +#include +#endif +#ifdef VARARGS +#ifdef va_start +# undef va_start +#endif +#include +#endif + +#if !defined(STDARG) && !defined(VARARGS) + +/* Otherwise we must make do with these */ +#define va_list char ** +#define va_arg(x,t) (t)*(x++) +#endif + +#define MAXDIG 11 /* 32 bits in radix 8 */ +#define TRUNC_SIZE 1024 + +static char Buf[TRUNC_SIZE], *Bufp; +static char Intbuf[MAXDIG]; +static int Out; +static char *Dest; + +#ifdef PROTO +static void __itoa(char *p, unsigned int num, int radix) +#else +static void +_itoa(p, num, radix) + char *p; + unsigned int num; + int radix; +#endif +{ + int i; + char *q; + + q = p + MAXDIG; + do + { + i = (int) (num % radix); + i += '0'; + if (i > '9') + i += 'A' - '0' - 10; + *--q = i; + } while ((num = (num / radix))); + i = p + MAXDIG - q; + do + *p++ = *q++; + while (--i); + *p = 0; +} + +#ifdef PROTO +static void _put(char c) +#else +static void +_put(c) + char c; +#endif +{ + if (Bufp < &Buf[TRUNC_SIZE]) + *Bufp++ = c; +} + +#ifdef PROTO +static void printvoid(char *str, va_list ap) +#else +static void +printvoid(str, ap) + char *str; + va_list ap; +#endif +{ + int w; + int k, x, radix; + char *p, *p1, c, fillchar; + + Bufp = Buf; + while (*str != '\0') + { + if (*str != '%') + { + _put(*str++); + continue; + } + w = 0; + fillchar = ' '; + str++; + while (*str >= '0' && *str <= '9') + { + if (*str == '0' && !w) + fillchar = '0'; + w = 10 * w + (*str - '0'); + str++; + } + + switch (*str) + { + case 'c': + k = va_arg(ap, int); + _put(k); + str++; + continue; + case 's': + p = va_arg(ap, char *); + p1 = p; + while ((c = *p++)) + _put(c); + for (x = strlen(p1); w > x; w--) + _put(fillchar); + str++; + continue; + case 'x': + radix = 16; + goto printnum; + case 'd': + radix = 10; + goto printnum; + case 'o': + radix = 8; + printnum: + x = va_arg(ap, int); + str++; + __itoa(Intbuf, x, radix); + p = Intbuf; + for (x = strlen(p); w > x; w--) + _put(fillchar); + while ((c = *p++)) + _put(c); + continue; + default: + _put(*str++); + continue; + } + + } + /* write everything in one blow. */ + if (Out == -1) + { + *Bufp++ = 0; +#ifdef USES_BCOPY + bcopy(Buf, Dest, (int) (Bufp - Buf)); +#else + memcpy(Dest, Buf, (int) (Bufp - Buf)); +#endif + } + else + write(Out, Buf, (int) (Bufp - Buf)); +} + +#ifdef VARARGS +/* For systems that have varargs.h, use the following three routines + */ +/* VARARGS */ +void +fprints(va_alist) +va_dcl +{ + va_list argptr; + char *str; + + va_start(argptr); + Out = va_arg(argptr, int); + str = va_arg(argptr, char *); + printvoid(str, argptr); + va_end(argptr); +} +/* VARARGS */ +void +sprints(va_alist) +va_dcl +{ + va_list argptr; + char *str; + + va_start(argptr); + Dest = va_arg(argptr, char *); + Out = -1; + str = va_arg(argptr, char *); + printvoid(str, argptr); + va_end(argptr); +} +/* VARARGS */ +void +prints(va_alist) +va_dcl +{ + va_list argptr; + char *str; + + va_start(argptr); + Out = 1; + str = va_arg(argptr, char *); + printvoid(str, argptr); + va_end(argptr); +} + +#endif + +#ifdef STDARG +/* For systems that have stdarg.h, use the following three routines + */ +/* VARARGS */ +#ifdef PROTO +void fprints(int fd, char *str, ...) +#else +void fprints(fd, str) + int fd; + char *str; +#endif +{ + va_list argptr; + + va_start(argptr, str); + Out = fd; + printvoid(str, argptr); + va_end(argptr); +} +/* VARARGS */ +#ifdef PROTO +void sprints(char *out, char *str, ...) +#else +void sprints(out, str) + char *out; + char *str; +#endif +{ + va_list argptr; + + va_start(argptr, str); + Dest = out; + Out = -1; + printvoid(str, argptr); + va_end(argptr); +} +/* VARARGS */ +#ifdef PROTO +void prints(char *str, ...) +#else +void prints(str) + char *str; +#endif +{ + va_list argptr; + + va_start(argptr, str); + Out = 1; + printvoid(str, argptr); + va_end(argptr); +} + +#endif + +#if !defined(STDARG) && !defined(VARARGS) +/* For systems that have neither, we use these. + */ +/* VARARGS */ +void +fprints(fd, str, argptr) + int fd; + char *str; + char *argptr; +{ + Out = fd; + printvoid(str, &argptr); +} +/* VARARGS */ +void +sprints(out, str, argptr) + char *out; + char *str; + char *argptr; +{ + Dest = out; + Out = -1; + printvoid(str, &argptr); +} +/* VARARGS */ +void +prints(str, argptr) + char *str; + char *argptr; +{ + Out = 1; + printvoid(str, &argptr); +} + +#endif + +/* Mprint is a small utility routine that prints out a line with control + * characters converted to Ascii, e.g ^A etc. If nocr is true, no \n is + * appended. Mprint now uses the MSB variable to determine how to print + * out msb-on chars. If MSB, use standout mode. + */ +void +mprint(line, nocr) + uchar *line; + int nocr; +{ + extern char *so, *se; + char c, low, buf[MAXLL]; + bool msb; + int i, j, k; + + if (EVget("Msb")) + msb = TRUE; + else + msb = FALSE; + for (j = 0, i = 0; line[i]; i++) + { + c = line[i]; + low = c & 0x7f; /* Turn the msb off */ + + if (low < 32 || low == 127) /* If a ctrl-char, insert ^ */ + { + buf[j++] = '^'; + c |= 64; + low |= 64; /* and make it a capital */ + } + if (msb && low != c) /* Bold msb-on char if wanted */ + { + for (k = 0; so[k]; j++, k++) + buf[j] = so[k]; + buf[j++] = low; + for (k = 0; se[k]; j++, k++) + buf[j] = se[k]; + } + else + buf[j++] = c; + } + buf[j] = EOS; + write(1, buf, j); + if (!nocr) + write(1, "\n", 1); +} diff --git a/Applications/wish/proto.h b/Applications/wish/proto.h new file mode 100644 index 0000000000..069c18d9da --- /dev/null +++ b/Applications/wish/proto.h @@ -0,0 +1,227 @@ +/* Prototypes for Wish. This file contains the K&R and Ansi prototypes for + * all the non-static routines in the shell, plus prototypes for all the + * external routines that the shell uses. Expect to comment some of these + * out depending upon what is already defined in your system's header files. + * + * $Revision: 41.1 $ $Date: 1995/12/29 02:10:46 $ + */ + +#ifdef PROTO +# define P(s) s +#else +# define P(s) () +#endif + + +/* Trying to use the intuitive prototypes below AND + * allow the use of stdargs or varargs is basically + * impossible. + * + */ +#ifndef NO_PRINTS_DEFN +void fprints P((int fd, CONST char *fmt, ...)); +void sprints P((char *out, CONST char *fmt, ...)); +void prints P((CONST char *fmt, ...)); +#endif + +void mprint P((uchar *line , int nocr )); + +/* alias.c */ +struct val *checkalias P((char *aname )); +bool getaliasline P((uchar *line , int *nosave )); +int alias P((int argc , char *argv [])); +int unalias P((int argc , char *argv [])); + +/* bind.c */ +int Bind P((int argc , uchar *argv [])); +int unbind P((int argc , uchar *argv [])); +void initbind P((void )); +int getcomcmd P((void )); + +/* builtin.c */ +int builtin P((int argc , char *argv [], int *rtnpid )); + +/* clebuf.c */ +void flushbuf P((void )); +void addbuf P((char *str )); +void mputc P((int b )); + +/* clex.c */ +int compare P((CONST void *a , CONST void *b )); +void addcarray P((char *word , struct candidate *prev , int mode , bool malc )); +void complete P((char *line , int *pos , bool how )); + +/* comlined.c */ +int Show P((uchar *line , int pos , int let , int flag )); +int prevword P((uchar *line , int *p , int flag )); +bool getuline P((uchar *line , int *nosave )); + +/* exec.c */ +int invoke P((int argc , char *argv [], struct rdrct newfd [], int how , int anydups )); + +/* file.c */ +bool fileopen P((char *filename , int fd )); +void fileclose P((void )); +bool getfileline P((uchar *line , int *nosave )); +int source P((int argc , char *argv [])); + +/* hist.c */ +int savehist P((char *line , bool andadd )); +void loadhist P((char *line , int histnum )); +int history P((int argc , char *argv [])); +char *gethist P((char *event )); + +/* job.c */ +int addjob P((int pid , char *name , int isbg )); +int joblist P((int argc , char *argv [])); +int Kill P((int argc , char *argv [])); +int bg P((int argc , char *argv [])); +int fg P((int argc , char *argv [])); + +/* posixjob.c */ +void waitfor P((int pid )); +SIGTYPE stopjob P((int a)); + +/* main.c */ +void prprompt P((void )); +void leave_shell P((int how )); +void doline P((int isalias )); +int main P((int argc , char *argv [])); + +/* malloc.c */ +char *Malloc P((unsigned int size , char *mesg )); +void initmall P((void )); +void *myMalloc P((unsigned int size )); +void myFree P((void *ptr )); + +/* meta.c */ +void tilde P((char *word , char *dir )); +char *expline P((struct candidate *list )); +bool meta_1 P((char *old , bool mustmalc )); +void meta_2 P((void )); + +/* parse.c */ +int command P((int *waitpid , int makepipe , int *pipefdp , int anydups )); + +/* signal.c */ +void catchsig P((void )); +void dflsig P((void )); + +/* term.c */ +void getstty P((void )); +void setcbreak P((void )); +void setcooked P((void )); +void getstty P((void )); +void setcbreak P((void )); +void setcooked P((void )); +void getstty P((void )); +void setcbreak P((void )); +void setcooked P((void )); +void terminal P((void )); + +/* val.c */ +void appendval P((struct vallist *l , struct val *v )); +struct val *searchval P((struct vallist *l , char *name , int mode , bool sub )); +void saveval P((struct vallist *l , struct val *v )); +struct val *pullval P((struct vallist *l )); +void setval P((char *name , char *val , struct vallist *l )); + +/* var.c */ +char *EVget P((char *name )); +bool EVinit P((void )); +bool EVupdate P((void )); +int export P((int argc , char *argv [])); +int shift P((int argc , char *argv [])); +int unset P((int argc , char *argv [])); +int set P((int argc , char *argv [])); + + + +/* The following are defined so that gcc -Wall won't complain about + * missing prototypes - you may need to alter or comment them out + * for your own machine. Some of these were borrowed from Minix. + */ + + +#ifdef UNUSED +/* Ansi C routines */ + +int atoi P((CONST char *nptr)); +void free P((void *ptr)); +void *malloc P((size_t size)); +void *memcpy P((void *s1, CONST void *s2, size_t n)); +void *memset P((void *s, int c, size_t n)); +void qsort P((void *base, size_t nmemb, size_t size, + int (*compare)(CONST void *a, CONST void *b))); +void *realloc P((void *ptr, size_t size)); +char *strcat P((char *s1, CONST char *s2)); +char *strchr P((CONST char *s, int c)); +int strcmp P((CONST char *s1, CONST char *s2)); +char *strcpy P((char *s1, CONST char *s2)); +size_t strlen P((CONST char *s)); +int strncmp P((CONST char *s1, CONST char *s2, size_t n)); +char *strncpy P((char *s1, CONST char *s2, size_t n)); +char *strpbrk P((CONST char *s1, CONST char *s2)); +char *strrchr P((CONST char *s, int c)); + + +/* POSIX routines */ + +int chdir P((CONST char *path)); +int closedir P((DIR *dirp)); +int close P((int fd)); +int dup P((int fd)); +int dup2 P((int fd, int fd2)); +int execvp P((CONST char *file, char *argv[])); +void exit P((int status)); +pid_t fork P((void)); +char *getcwd P((char *buf, int size)); +pid_t getpid P((void)); +uid_t getuid P((void)); +struct passwd *getpwent P((void)); +struct passwd *getpwnam P((CONST char *name)); +struct passwd *getpwuid P((int uid)); +void endpwent P((void)); +int kill P((pid_t pid, int sig)); +struct tm *localtime P((CONST time_t *timer)); +off_t lseek P((int fd, off_t offset, int whence)); +int open P((CONST char *path, int oflag, mode_t modes)); +void perror P((CONST char *s)); +int pipe P((int fildes[2])); +int read P((int fd, char *buf, unsigned int n)); +int setpgid P((int pid, int pgid)); +int getpgrp P((int pid)); +SIGTYPE (*signal)(); +int stat P((CONST char *filename, struct stat *s)); +int tcgetattr P((int fd, struct termios *tptr)); +int tcsetattr P((int fd, int opt_actions, struct termios *tptr)); +int tcsetpgrp P((int fd, int pgrp)); +time_t time P((time_t *timeptr)); +pid_t wait P((int *stat_loc)); +pid_t waitpid P((pid_t pid, int *stat_loc, int options)); +int write P((int fd, CONST char *buf, unsigned int n)); +DIR *opendir P((CONST char *dirname)); +int closedir P((DIR *dirp)); +#ifdef USES_DIRECT +struct direct *readdir P((DIR *dirp)); +#else +struct dirent *readdir P((DIR *dirp)); +#endif + + +/* Other routines */ + +char *getcwd P((char *buf)); +int ioctl P((int fd, int request, ... )); +int setpgrp P((int pid, int pgid)); +int tgetent P((char *bp, CONST char *name)); +int tgetflag P((CONST char *id)); +int tgetnum P((CONST char *id)); +char *tgetstr P((CONST char *id, char **area)); +int wait3 P((union wait *status, int options, struct rusage *rusage)); +void bzero P((char *b1, int length)); +void bcopy P((CONST char *b1, char *b2, int length)); + +#endif /* UNUSED */ + +#undef P diff --git a/Applications/wish/signal.c b/Applications/wish/signal.c new file mode 100644 index 0000000000..83ea5d8504 --- /dev/null +++ b/Applications/wish/signal.c @@ -0,0 +1,66 @@ +/* Most of the functions that handle signals are kept in this file. + * Exceptions are some signal() calls done in job.c + * + * $Revision: 41.1 $ $Date: 1995/12/29 02:10:46 $ + */ + +#include "header.h" + +/* Graceful is set to catch most of the unused signals in Wish. All it + * does is print out an error message and exit. + */ +#ifdef PROTO +static SIGTYPE graceful(int sig) +#else +static SIGTYPE +graceful(sig) + int sig; +#endif +{ + extern char *signame[]; + + setcooked(); + fprints(2, "Received SIG%s signal, # %d\n", signame[sig],sig); + exit(1); +} + + +/* Catchsig is called once at Wish startup, and it sets graceful to + * catch most of the signals unused by Wish. + */ +void +catchsig() +{ +#ifdef DEBUG + int i; + + for (i = 1; i <= MAXSIG; i++) + { + if (i != SIGKILL && i != SIGCONT) /* SIGKILL cannot be caught or ignored */ + signal(i, graceful); + } +#endif + + signal(SIGINT, SIG_IGN); /* Sometimes taken out for debugging */ + signal(SIGQUIT, SIG_IGN); + +#if defined(UCBJOB) || defined(POSIXJOB) + /* Also catch these for job control */ + signal(SIGTSTP, stopjob); + signal(SIGCHLD, SIG_IGN); +#endif +} + +/* Dflsig sets all the signals to their default handlers, so that exec'd + * programs will have a standard signal environment. + */ +void +dflsig() +{ + int i; + +#ifdef DEBUG + fprints(2,"%d signals defined\n",MAXSIG); +#endif + for (i = 1; i <= MAXSIG; i++) signal(i, SIG_DFL); +} diff --git a/Applications/wish/term.c b/Applications/wish/term.c new file mode 100644 index 0000000000..ad05f3eeef --- /dev/null +++ b/Applications/wish/term.c @@ -0,0 +1,314 @@ +/* This file contains routines for setting the terminal characteristics, + * and for getting the termcap strings. + * + * $Revision: 41.3 $ $Date: 2003/04/21 13:08:43 $ + */ + +#include "header.h" + +int beeplength; /* Strlen of beep */ +int wid; /* The width of the screen (minus 1) */ + + /* The termcap strings Wish uses */ +char *bs, *nd, *cl, *cd, *up, *so, *se, *wbeep; + +static char *termcapbuf; /* Buffer to get termcap strings */ + +/* Wish keeps the ``normal'' terminal characteristics (i.e the ones Wish's + * children see) in a structure, and restores the characteristics each + * time it forks. The structure depends upon the OS we are being compiled + * under. + */ + +#ifdef USES_TCGETA +static struct termio tbuf, tbuf2; + +#endif + +#ifdef USES_SGTTY +static struct sgttyb tbuf, tbuf2; +static struct tchars sbuf, sbuf2; + +# ifdef USES_MORESIG +static struct ltchars moresigc, moresigc2; +# endif +#endif + +#ifdef USES_TERMIOS +static struct termios tbuf, tbuf2; + +#endif + + +#ifdef DEBUG +/* Printctrl prints out a terminal sequence that Wish got + * from termcap. Only used when debugging the ensure Wish + * is actually getting the strings, and the right ones. + */ +static void +printctrl(name, str) + char *name, *str; +{ + int i; + + prints("%s: ", name); + for (i = 0; str[i]; i++) + if (str[i] > 31) + prints("%c",str[i]); + else + { + prints("%c",'^'); + if (str[i] > 26) + prints("%c",str[i] + 64); + else + prints("%c",str[i] + 96); + } + prints('\n'); +} + +#endif + + +/* There are 3 versions of getstty, setcooked and setcbreak, + * defined by USES_TCGETA, USES_TERMIOS and USES_SGTTY + */ + +#ifdef USES_TCGETA +/* Getstty: Get the current terminal structures for Wish */ +void +getstty() +{ + if (ioctl(0, TCGETA, &tbuf)) + perror("ioctl in getstty"); + memcpy(&tbuf2, &tbuf, sizeof(tbuf)); +} + +/* Setcbreak: Set terminal to cbreak mode */ +void +setcbreak() +{ + bool keepstty; + + if (EVget("Keepstty")) + keepstty = TRUE; + else + keepstty = FALSE; + + if (!keepstty) + getstty(); + tbuf2.c_lflag &= ~(ICANON | ECHO | ISIG); /* Turn off canonical input and echo */ + tbuf2.c_cc[4] = 1; /* read 1 char before returning like CBREAK */ + if (ioctl(0, TCSETA, &tbuf2)) + perror("ioctl in setcbreak"); +} + +/* Setcooked: Set terminal to cooked mode */ +void +setcooked() +{ + if (ioctl(0, TCSETA, &tbuf)) + perror("ioctl in setcooked"); +} + +#endif /* USES_TCGETA */ + + +#ifdef USES_TERMIOS +/* Getstty: Get the current terminal structures for Wish */ +void +getstty() +{ + if (tcgetattr(0, &tbuf)) + perror("tcgetattr in getstty"); + memcpy(&tbuf2, &tbuf, sizeof(tbuf)); +} + +/* Setcbreak: Set terminal to cbreak mode */ +void +setcbreak() +{ + int i; + bool keepstty; + + if (EVget("Keepstty")) + keepstty = TRUE; + else + keepstty = FALSE; + + if (!keepstty) + getstty(); + /* Turn off canonical input and echo */ + tbuf2.c_lflag = tbuf2.c_lflag & (~ICANON) & (~ECHO); + for (i=0; i< NCCS; i++) tbuf2.c_cc[i]=0; + tbuf2.c_cc[VMIN] = 1; /* read 1 char before returning like CBREAK */ + if (tcsetattr(0, TCSANOW, &tbuf2)) + perror("cbreak tcsetattr"); +} + +/* Setcooked: Set terminal to cooked mode */ +void +setcooked() +{ + if (tcsetattr(0, TCSANOW, &tbuf)) + perror("cooked tcsetattr"); +} + +#endif /* USES_TERMIOS */ + + +#ifdef USES_SGTTY +/* Getstty: Get the current terminal structures for Wish */ +void +getstty() +{ + if (ioctl(0, TIOCGETP, &tbuf))/* get the sgttyb struct */ + perror("ioctl2 in getstty"); + if (ioctl(0, TIOCGETC, &sbuf))/* get the tchars struct */ + perror("ioctl3 in getstty"); +#ifdef USES_MORESIG + bcopy(&sbuf, &sbuf2, sizeof(sbuf)); /* and copy them so we can change */ + bcopy(&tbuf, &tbuf2, sizeof(tbuf)); + if (ioctl(0, TIOCGLTC, &moresigc)) /* get the ltchars struct */ + perror("ioctl4 in getstty"); + bcopy(&moresigc, &moresigc2, sizeof(moresigc)); +#else + memcpy(&sbuf2, &sbuf, sizeof(sbuf)); /* and copy them so we can change */ + memcpy(&tbuf2, &tbuf, sizeof(tbuf)); +#endif +} + +/* Setcbreak: Set terminal to cbreak mode */ +void +setcbreak() +{ + bool keepstty; + + if (EVget("Keepstty")) + keepstty = TRUE; + else + keepstty = FALSE; + + if (!keepstty) + getstty(); + /* setup terminal with ioctl calls */ + + tbuf2.sg_flags |= CBREAK; /* cbreak mode to get each char */ + tbuf2.sg_flags &= (~ECHO); /* do not echo chars to screen */ + if (ioctl(0, TIOCSETP, &tbuf2)) /* put it back, modified */ + perror("ioctl1 su"); + sbuf2.t_intrc = (UNDEF); /* no interrupt or quitting */ + /* sbuf2.t_quitc=(UNDEF); Allow quit while debugging */ + sbuf2.t_eofc = (UNDEF); /* or eof signalling */ + if (ioctl(0, TIOCSETC, &sbuf2)) /* put it back, modified */ + perror("ioctl2 scb"); +#ifdef USES_MORESIG + moresigc2.t_suspc = (UNDEF); /* no stopping */ + moresigc2.t_dsuspc = (UNDEF); /* or delayed stopping */ + moresigc2.t_rprntc = (UNDEF); /* or reprinting */ + moresigc2.t_flushc = (UNDEF); /* or flushing */ + moresigc2.t_werasc = (UNDEF); /* or word erasing */ + moresigc2.t_lnextc = (UNDEF); /* or literal quoting */ + if (ioctl(0, TIOCSLTC, &moresigc2)) /* put it back, modified */ + perror("ioctl3"); +#endif +} + + +/* Setcooked: Set terminal to cooked mode */ +void +setcooked() +{ + if (ioctl(0, TIOCSETP, &tbuf)) + perror("ioctl1 sd"); + if (ioctl(0, TIOCSETC, &sbuf)) + perror("ioctl2 sd"); +#ifdef USES_MORESIG + if (ioctl(0, TIOCSLTC, &moresigc)) /* set ltchars struct to be default */ + perror("ioctl3 in setcooked"); +#endif +} + +#endif /* USES_SGTTY */ + + +/* gettstring: Given the name of a termcap string, gets the string + * and places it into loc. Returns 1 if ok, 0 if no string. + * If no string, loc[0] is set to EOS. + */ +#ifdef PROTO +static bool gettstring(char *name, char **loc) +#else +static bool +gettstring(name, loc) + char *name, **loc; +#endif +{ + char bp[50], *area = bp; + + if (tgetstr(name, &area) != NULL) + { + area = bp; + while (isdigit(*area)) + area++; /* Skip time delay chars */ + *loc = (char *) malloc((unsigned) strlen(area) + 2); + if (!(*loc)) + return (FALSE); + strcpy(*loc, area); + return (TRUE); + } + else + *loc = ""; + return (FALSE); +} + + +/* Terminal is called at the beginning of Wish to get the termcap + * strings needed by the Command Line Editor. If they are not got, + * or the wrong ones are found, the CLE will act strangely. Wish + * should at this stage default to a dumber CLE. + */ +void +terminal() +{ + extern int beeplength; + char *t, term[20]; + +/* set up cursor control sequences from termcap */ + + t=EVget("TERM"); + if (!t) fatal("No termcap entry available"); + strncpy(term, t, 10); + termcapbuf = (char *) malloc(2048); + if (!termcapbuf) + return; + tgetent(termcapbuf, term); + if (tgetflag("bs") == 1) + bs = "\b"; + else + gettstring("bc", &bs); + if ((wid = tgetnum("co")) == -1) + wid = 80; + wid--; /* this is to eliminate unwanted auto newlines */ + gettstring("cl", &cl); + gettstring("cd", &cd); + gettstring("nd", &nd); + gettstring("up", &up); + gettstring("so", &so); + gettstring("se", &se); + gettstring("bl", &wbeep); + if (*wbeep == EOS) + + wbeep = "\007"; + beeplength = strlen(wbeep); +#ifdef DEBUG + printctrl("bs", bs); + printctrl("cl", cl); + printctrl("cd", cd); + printctrl("nd", nd); + printctrl("up", up); + printctrl("so", so); + printctrl("se", se); + printctrl("beep", wbeep); +#endif + free(termcapbuf); +} diff --git a/Applications/wish/ucbjob.c b/Applications/wish/ucbjob.c new file mode 100644 index 0000000000..81f419a446 --- /dev/null +++ b/Applications/wish/ucbjob.c @@ -0,0 +1,160 @@ +/* 4.3BSD Job Control functions + * + * $Revision: 41.1 $ $Date: 1995/12/29 02:10:46 $ + * + */ + +#define WIFCORE(x) ((x).w_coredump) +#ifndef WEXITSTATUS +# define WEXITSTATUS(x) ((x).w_retcode) +#endif +#ifndef WTERMSIG +# define WTERMSIG(x) ((x).w_termsig) +#endif +#ifndef WSTOPSIG +# define WSTOPSIG(x) ((x).w_stopsig) +#endif +#define WRUNFG(x) ((x).w_status == RUNFG) +#define WRUNBG(x) ((x).w_status == RUNBG) + +/* The following tables are all VERY OS-dependent, especially on POSIX + * systems. If you find the shell giving you weird signal messages, you + * should change the names of the signals below. + */ + +static char *siglist[] = { + "", "Hangup", "Interrupt", "Quit", "Illegal Instruction", + "Trace/BPT Trap", "IOT Trap", "EMT Trap", "Floating Point Exception", + "Killed", "Bus Error", "Segmentation Violation", "Bad System Call", + "Broken Pipe", "Alarm", "Terminated" ,"Urgent Socket Condition", + "Stopped (signal)", "Stopped", "Continue", "Child Status Change", + "(tty input)", "(tty output)", "I/O", "Cpu Time Limit", + "File Size Limit", "Virtual Time Alarm", "Profile Alarm", + "Window Change", "Resource Lost", "User Signal 1", "User Signal 2" +}; + +char *signame[] = { + "", "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "EMT", "FPE", "KILL", + "BUS", "SEGV", "SYS", "PIPE", "ALRM", "TERM" ,"URG", "STOP", "TSTP", + "CONT", "CHLD", "TTIN", "TTOU", "IO", "XCPU", "XFSZ", "VTALRM", "PROF", + "WINCH", "LIST", "USR1", "USR2", NULL +}; + +/* Waitfor is called to wait for a requested job to stop/die. It receives the + * new status of any children. If it's the requested job, we return. Pid holds + * the process-id of the job we are after; if it is 0, we return when no + * processes are left waiting to report a change in state. + * + * If pid is negative, we are a subshell and _must_ return only when the pid + * dies. We set the subshell flag and make pid positive again. Then, if we + * find out that the job was stopped, we wake it up. This works because we + * are put to sleep at the same time as the child, and the original shell + * fg()s _us_. + */ +void +waitfor(pid) + int pid; +{ + struct job *thisjob; + int wpid; + bool subshell=FALSE; + + union wait status; + + int waitflags; + waitflags = (pid == 0) ? WNOHANG | WUNTRACED : WUNTRACED; + +#ifdef DEBUG + fprints(2,"In waitfor\n"); +#endif + + if (pid<0) { pid= -pid; subshell= TRUE; } + while (1) + { + wpid = wait3(&status, waitflags, NULL); + +#ifdef DEBUG + fprints(2,"waitxx() returned\n"); +#endif + if (wpid == -1 || wpid == 0) + break; + + if (subshell && WIFSTOPPED(status)) /* We can't return yet */ + { + kill(wpid, SIGCONT); /* Wake the child up first - waah! */ + continue; + } + thisjob = findjob(wpid); + if (thisjob == NULL) + continue; + thisjob->status = status; + thisjob->changed = TRUE; + if (pid == wpid) + return; + } +} + +/* Stopjob is only called when we received a SIGTSTP. If we are wait3()ing + * a pid, we send a SIGSTOP to that pid. This should then cause wait3() + * to exit, and thus waitfor() will return. + */ +SIGTYPE +stopjob() +{ + signal(SIGTSTP, stopjob); +#ifdef DEBUG + fprints(2,"In stopjobs\n"); +#endif + if (currentjob) + { +#ifdef DEBUG + fprints(2,"About to stop pid %d\n", currentjob->pid); +#endif + kill(currentjob->pid, SIGSTOP); + } +} + + +static void +bgstuff(pid) + int pid; +{ + struct job *ptr; + + setpgrp(pid, pid); +# ifdef DEBUG + fprints(2,"About to SIGCONT %d\n", pid); +# endif + kill(pid, SIGCONT); + ptr = findjob(pid); + if (ptr) + { ptr->STATUS = RUNBG; currentjob= ptr; } +} + + +static int +fgstuff(pid) + int pid; +{ + int pgrp; + +/* Under UCBJOB and POSIXJOB, we don't try & bring the pid into our + * pgrp, that doesn't work. Instead, we move the terminal + * over to that pgrp, and move ourselves to that pgrp as well. + */ + pgrp = getpgrp(pid); /* Determine if job's pgrp != ours */ + if (pgrp == pid) + { + if (ioctl(0, TIOCSPGRP, &pgrp) == -1) /* Move the terminal to that pgrp */ + { + perror("fg setpgrp ioctl"); + return (1); + } + if (setpgrp(getpid(), pgrp) == -1) /* Set shell's process group to the pid's */ + { + perror("fg setpgrp"); + return (1); + } + } + return(0); +} diff --git a/Applications/wish/v7job.c b/Applications/wish/v7job.c new file mode 100644 index 0000000000..635f49e18a --- /dev/null +++ b/Applications/wish/v7job.c @@ -0,0 +1,162 @@ +/* 7th Edition Job Control functions + * + * $Revision: 41.1 $ $Date: 1995/12/29 02:10:46 $ + * + */ +#ifndef WIFSTOPPED +#define WIFSTOPPED(x) (((x) & 0xFF) == 0x7F) /* As per my Version7 manual */ +#define WIFSIGNALED(x) ((x) & 0xFF) +#define WIFEXITED(x) (((x) & 0xFF) == 0) +#define WEXITSTATUS(x) ((x) >> 8) +#define WTERMSIG(x) ((x) & 0xFF) +#define WSTOPSIG(x) ((x) >> 8) +#endif +#define WIFCORE(x) ((x) & 0x80) +#define WRUNFG(x) ((x) == RUNFG) +#define WRUNBG(x) ((x) == RUNBG) + +/* The following tables are all VERY OS-dependent, especially on POSIX + * systems. If you find the shell giving you weird signal messages, you + * should change the names of the signals below. + */ +static char *siglist[] = { + "", "Hangup", "Interrupt", "Quit", "Illegal Instruction", + "Trace/BPT Trap", "IOT Trap", "EMT Trap", "Floating Point Exception", + "Killed", "Bus Error", "Segmentation Violation", "Bad System Call", + "Broken Pipe", "Alarm", "Terminated", "User Signal 1", "User Signal 2", + "Child Death", "Power Failure" +}; + +char *signame[] = { + "", "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "EMT", "FPE", "KILL", + "BUS", "SEGV", "SYS", "PIPE", "ALRM", "TERM", "USR1", "USR2", "CLD", + "PWR", NULL +}; + + +/* Waitfor is called to wait for a requested job to stop/die. It receives the + * new status of any children. If it's the requested job, we return. Pid holds + * the process-id of the job we are after; if it is 0, we return when no + * processes are left waiting to report a change in state. + * + * If pid is negative, we are a subshell and _must_ return only when the pid + * dies. We set the subshell flag and make pid positive again. Then, if we + * find out that the job was stopped, we wake it up. This works because we + * are put to sleep at the same time as the child, and the original shell + * fg()s _us_. + */ +void +waitfor(pid) + int pid; +{ + struct job *thisjob; + int wpid; + bool subshell=FALSE; + int stopsig; + + int status; + +#ifdef DEBUG + fprints(2,"In waitfor\n"); +#endif + + if (pid<0) { pid= -pid; subshell= TRUE; } + while (1) + { + if (pid == 0) + return; + wpid = wait(&status); + +#ifdef DEBUG + fprints(2,"waitxx() returned with %x\n",status); +#endif + if (wpid == -1 || wpid == 0) + break; + + thisjob = findjob(wpid); + if (thisjob == NULL) + continue; + +/* Version 7 job control is quite interesting. Since we don't have a ^Z + * key, we use the ^\ key instead. Luckily this is caught by the parent + * and stops the process. When any signal is caught by the child, it + * stops. We must arrange to stop it on SIGQUIT (^\), and restart it on + * any other signal. + */ + if (WIFSTOPPED(status)) + { + stopsig= WSTOPSIG(status); +#ifdef DEBUG + fprints(2,"Stopped status %x, stopsig %d (%s)\n", + status,stopsig,signame[stopsig]); +#endif + if (stopsig!=SIGQUIT) + { + if (stopsig== SIGTRAP) + { +#ifdef DEBUG + fprints(2,"About to ptrace(7,%d,1,0)\n",wpid); +#endif + ptrace(7,wpid,(PLONG)1,(PLONG)0); /* Keep it going after exec */ + } + else + { +#ifdef DEBUG + fprints(2,"About to ptrace(7,%d,1,stopsig)\n",wpid); +#endif + ptrace(7,wpid,(PLONG)1,(PLONG)stopsig); /* Deliver the signal */ + } + continue; + } + } /* SIGQUIT falls out to below where job is marked stopped */ + + thisjob->status = status; + thisjob->changed = TRUE; + if (pid == wpid) + return; + } +} + +#ifdef PROTO +static void bgstuff(int pid) +#else +static void +bgstuff(pid) + int pid; +#endif +{ + struct job *ptr; + +#ifdef DEBUG + fprints(2,"About to ptrace(7,%d,1,0)\n", pid); +#endif + ptrace(7,pid,(PLONG)1,(PLONG)0); /* Start it up again */ + if (ptr) + { ptr->STATUS = RUNBG; currentjob= ptr; } +} + +#ifdef PROTO +static int fgstuff(int pid) +#else +static int +fgstuff(pid) + int pid; +#endif +{ + struct job *ptr; + + ptr=findjob(pid); + +/* We need to wake up stopped jobs before we can wait on them */ + if ((ptr) && WIFSTOPPED(ptr->status)) + { +#ifdef DEBUG + fprints(2,"About to ptrace(7,%d,1,0)\n", pid); +#endif + ptrace(7,pid,(PLONG)1,(PLONG)0); /* Start it up again */ + } + + ptr->STATUS = RUNFG; + currentjob = ptr; + return (0); +} diff --git a/Applications/wish/val.c b/Applications/wish/val.c new file mode 100644 index 0000000000..ca55f1baad --- /dev/null +++ b/Applications/wish/val.c @@ -0,0 +1,134 @@ +/* Functions dealing with lists of values + * + * $Revision: 41.1 $ $Date: 1995/12/29 02:10:46 $ + */ + +#include "header.h" + +void +appendval(l, v) + struct vallist *l; + struct val *v; +{ + v->next = NULL; + if (l->head == NULL) l->head = l->tail = v; + else + { l->tail->next = v; l->tail = v; } +} + + + +/* Searchval searches through a val list for a given val. + * When a position is found, searchval operates according + * to the mode value: if true, return the val, else delete + * the val. If sub is true, substring matches are used. + */ + +struct val * +searchval(l, name, mode, sub) + struct vallist *l; + char *name; + int mode; + bool sub; +{ + struct val *last, *this; + int len; + + switch (sub) + { + case TRUE: + len = strlen(name); + for (last = this = l->head; this; last = this, this = this->next) + if (!strncmp(this->name, name, len)) + break; + break; + case FALSE: + for (last = this = l->head; this; last = this, this = this->next) + if (!strcmp(this->name, name)) + break; + break; + default: + fatal("Bad sub value in searchval"); + } + if (this == NULL) return (NULL); + + switch (mode) + { + case FALSE: + if (l->head == l->tail) + { l->head = l->tail = NULL; return (this); } + + if (this == l->head) l->head = this->next; + else last->next = this->next; + + if (this == l->tail) l->tail = last; + this->next = NULL; + case TRUE: + return (this); + default: + fatal("Bad mode value in searchval"); + } +} + +/* Save the value into the list in the correct order + */ +void +saveval(l, v) + struct vallist *l; + struct val *v; +{ + struct val *last, *this; + + if (l->head == NULL) + { l->head = l->tail = v; v->next = NULL; return; } + + for (last = this = l->head; this; last = this, this = this->next) + { if (!strcmp(this->name, v->name)) + { free(this->val); free(v->name); /* Overwrite the old val */ + this->val = v->val; this->exported = v->exported; + free(v); return; + } + if (strcmp(this->name, v->name) > 0) break; + } + + if (this == NULL) + { l->tail->next = v; v->next = NULL; + l->tail = v; return; + } + + if (this == last) + { v->next = l->head; l->head = v; return; } + + v->next = this; last->next = v; +} + +struct val * +pullval(l) + struct vallist *l; +{ + struct val *temp; + + if (l->head == NULL) return (NULL); + + temp = l->head; + if (l->head == l->tail) + l->head = l->tail = NULL; + else + l->head = l->head->next; + temp->next = NULL; + return (temp); +} + +void +setval(name, val, l) + char *name, *val; + struct vallist *l; +{ + struct val *v; + + v = (struct val *) Malloc(sizeof(struct val), "setval val malloc"); + v->name = Malloc(strlen(name) + 1, "setval name malloc"); + v->val = Malloc(strlen(val) + 1, "setval val malloc"); + strcpy(v->name, name); strcpy(v->val, val); + v->exported = FALSE; saveval(l, v); +} diff --git a/Applications/wish/var.c b/Applications/wish/var.c new file mode 100644 index 0000000000..142619d94a --- /dev/null +++ b/Applications/wish/var.c @@ -0,0 +1,201 @@ +/* Variables + * + * $Revision: 41.2 $ $Date: 1996/06/14 06:24:54 $ + */ + +#include "header.h" + +struct vallist vlist= /* The linked list of vars */ + { NULL, NULL}; + +char * +EVget(name) /* Get value of variable */ + char *name; +{ + struct val *v; + + if ((v = searchval(&vlist, name, TRUE, FALSE)) == NULL || v->name == NULL) + return (NULL); + return (v->val); +} + + +bool +EVinit() /* Initialise symtable from environment */ +{ + extern char **environ; + int i; + char c, *name, *val; + struct val *v; + + for (i = 0; environ[i] != NULL; i++) + { + name = environ[i]; + val = strchr(name, '='); + c = *val; + *(val++) = '\0'; + v= (struct val *)Malloc(sizeof(struct val), "EVinit val malloc"); + v->name= Malloc(strlen(name)+1, "EVinit name malloc"); + v->val= Malloc(strlen(val)+1, "EVinit val malloc"); + strcpy(v->name, name); strcpy(v->val, val); + v->exported=TRUE; + saveval(&vlist, v); + *(--val) = c; + } + return (TRUE); +} + +#ifndef NO_VAR +bool +EVupdate() /* Build envp from symbol table */ +{ + extern char **environ; + int i, envi, nvlen; + struct val *v; + static bool updated = FALSE; + + for (i = 0, v = vlist.head; v != NULL; v = v->next) + i += v->exported; + + if (!updated) + if ((environ = (char **) malloc((i + 1) * sizeof(char *))) == NULL) + return (FALSE); + envi = 0; + for (v = vlist.head; v != NULL; v = v->next) + { + if (v->name == NULL || !v->exported) + continue; + nvlen = strlen(v->name) + strlen(v->val) + 2; + if (!updated) + { + if ((environ[envi] = (char *) malloc(nvlen)) == NULL) + return (FALSE); + } + else if ((environ[envi] = (char *) realloc(environ[envi], nvlen)) == NULL) + return (FALSE); + sprints(environ[envi], "%s=%s", v->name, v->val); + envi++; + } + environ[envi] = NULL; + updated = TRUE; + return (TRUE); +} + +int +export(argc, argv) /* Export command */ + int argc; + char *argv[]; +{ + int i; + struct val *v; + + if (argc < 2) + { + prints("Usage: export var [var] ...\n"); + return (1); + } + for (i = 1; i < argc; i++) + { + v = searchval(&vlist, argv[i], TRUE, FALSE); /* Setenv */ + if (v == NULL) + prints("No such variable: %s\n", argv[i]); + else + v->exported = TRUE; + } + return (0); +} + +int +shift(argc, argv) + int argc; + char *argv[]; +{ + extern int Argc; + extern char **Argv; + int i = 1; + + if (argc > 2) + { + prints("Usage: shift [val]\n"); + return (1); + } + if (argc == 2) + i = atoi(argv[1]); + if (i > Argc) + { + prints("Not enough vars to shift\n"); + return (1); + } + Argv += i; + Argc -= i; + return (0); +} + +int +unset(argc, argv) + int argc; + char *argv[]; +{ + int i, j; + struct val *v; + + j = strcmp(argv[0], "unexport"); + if (argc < 2) + { + prints("Usage: %s [var] [var] ...\n", argv[0]); + return (1); + } + + for (i = 1; i < argc; i++) + switch (j) + { + case 0: + v = searchval(&vlist, argv[i], TRUE, FALSE); /* Unsetenv */ + if (v == NULL) + prints("No such variable: %s\n", argv[i]); + else + v->exported = FALSE; + break; + default: + if (!searchval(&vlist, argv[i], FALSE, FALSE)) + prints("No such variable: %s\n", argv[i]); + } + return (0); +} + +int +set(argc, argv) + int argc; + char *argv[]; +{ + struct val *v; + + if ((argc !=1) && (argc!=3)) + { prints("Usage: %s variable value, or %s\n", argv[0], argv[0]); return (1); } + + if (argc==3) + { setval(argv[1], argv[2], &vlist); + if (!strcmp(argv[0], "setenv")) + { + v = searchval(&vlist, argv[1], TRUE, FALSE); /* Setenv */ + if (v != NULL) v->exported = TRUE; + } + } + else + { + if (!strcmp(argv[0], "setenv")) + { + for (v = vlist.head; v != NULL; v = v->next) + if (v->name != NULL && v->exported) + prints("%s=%s\n", v->name, v->val); + } + else + { + for (v = vlist.head; v != NULL; v = v->next) + if (v->name != NULL) + prints("%s=%s\n", v->name, v->val); + } + } + return (0); +} +#endif /* NO_VAR */ diff --git a/Kernel/platform/platform-cpc6128/Makefile b/Kernel/platform/platform-cpc6128/Makefile index 802b937c8d..b4a4cf1813 100644 --- a/Kernel/platform/platform-cpc6128/Makefile +++ b/Kernel/platform/platform-cpc6128/Makefile @@ -1,11 +1,11 @@ -CSRCS = devtty.c devices.c main.c +CSRCS = devtty.c devices.c main.c devrd.c rd_cpcsme.c CDSRCS = discard.c -DSRCS = ../../dev/tinyide.c ../../dev/tinydisk.c ../../dev/devfdc765.c ../../dev/tinysd.c ../../dev/ch375.c -DDSRCS = ../../dev/tinyide_discard.c ../../dev/tinydisk_discard.c ../../dev/tinysd_discard.c +DSRCS = ../../dev/tinyide.c ../../dev/tinydisk.c ../../dev/devfdc765.c ../../dev/ch375.c +DDSRCS = ../../dev/tinyide_discard.c ../../dev/tinydisk_discard.c DZSRCS = ../../dev/cpc/cpcide.c ../../dev/cpc/cpckeyboard.c ../../dev/cpc/devinput.c ../../dev/cpc/albireo.c DDZSRCS = -ASRCS = crt0.s cpc6128.s cpcvideo.s fdc765.s -ASRCS += tricks.s commonmem.s +ASRCS = crt0.s cpc6128.s cpcvideo.s fdc765.s +ASRCS += tricks.s commonmem.s NSRCS = COBJS = $(CSRCS:.c=.rel) diff --git a/Kernel/platform/platform-cpc6128/README b/Kernel/platform/platform-cpc6128/README index fd31fdd0f5..6692dfd724 100644 --- a/Kernel/platform/platform-cpc6128/README +++ b/Kernel/platform/platform-cpc6128/README @@ -1,7 +1,7 @@ # Amstrad CPC6128 -## Mmeory Model +## Memory Model The CPC6128 supported memory maps: @@ -23,7 +23,13 @@ In the CPC6128: | Kernel | 0 / 1 / 2 | Common 7 (map C1) | Video | 0 / 3 / 2 | Common 7 (map C3) +The use of standard memory expansions as swap space has been implemented. With this memory map, the ability to have multiple tasks simultaneously in RAM has not been implemented. This is because the expanded memory can only be mapped in 16k blocks starting at address 0x4000, which removes the common area from the map (this mapping is used to implement the RAM disk for swap while executing code in the non-common code area). Alternatively, it can be mapped in 64k blocks, replacing the entire memory space without any common area to rely on. See this references: +https://www.cpcwiki.eu/index.php/Gate_Array#Register_MMR_.28RAM_memory_mapping.29 +https://www.cpcwiki.eu/index.php/Standard_Memory_Expansions +This latter 64k block mapping approach, which involves switching between 64k blocks, is being considered for another port inspired by the memory usage in the Cromemco port. + +Two standard memory expansion sizes are supported: 512k and 1024k. To build the port with either of these options, simply define the macros EXTENDED_RAM_512 or EXTENDED_RAM_1024 depending on the desired size in the file config.h. If neither is defined, the build will default to supporting a swap partition on any of the supported disk types. ## STATUS @@ -54,7 +60,7 @@ Support more hardware: M4 Board (storage, network and RTC), Ulifac/Usifac, netwo Fix lots of bugs. -Switch to a thunked memory model based on C2 Map to use the standard and extended RAM expansions up to 4MiB, the Cromemco port could be a model to this solution. As ther is no real common as we are switching the whole 64k space, the common data area has to be updated in all the used banks, but this can give aprox. 60K for kernel and user and hold a lot of processes in memory with a big RAM expansion. If this proves to be too hard, a RAM disk for swapping can be a way to use the RAM expansions. +Switch to a thunked memory model based on C2 Map to use the standard and extended RAM expansions up to 4MiB, the Cromemco port could be a model to this solution. As there is no real common as we are switching the whole 64k space, the common data area has to be updated in all the used banks, but this can give aprox. 60K for kernel and user and hold a lot of processes in memory with a big RAM expansion. Look for speed optimization opportunities. diff --git a/Kernel/platform/platform-cpc6128/config.h b/Kernel/platform/platform-cpc6128/config.h index 85a8d69511..c98d8d1416 100644 --- a/Kernel/platform/platform-cpc6128/config.h +++ b/Kernel/platform/platform-cpc6128/config.h @@ -27,8 +27,8 @@ #define UDATA_BLKS 1 #define UDATA_SIZE 0x200 #define CONFIG_DYNAMIC_BUFPOOL -#define CONFIG_DYNAMIC_SWAP -#define MAXTICKS 20 /* Has to be high because we are swap only */ + +#define MAXTICKS 60 /* Has to be high because we are swap only */ #undef CONFIG_KMOD @@ -70,8 +70,24 @@ #define SWAPBASE 0x0000 #define SWAPTOP 0xC000UL #define SWAP_SIZE 0x61 /* 48K + udata */ -#define MAX_SWAPS 16 -#define SWAPDEV (swap_dev) /* Device for swapping (dynamic). */ +#define EXTENDED_RAM_1024 +#ifdef EXTENDED_RAM_1024 + #define MAX_SWAPS 19 /*See platform devrd.c*/ + #define PTABSIZE 19 + #define TOTAL_SWAP_BLOCKS (1088-128) * 2 +#endif +#ifdef EXTENDED_RAM_512 + #define MAX_SWAPS 8 /*See platform devrd.c*/ + #define PTABSIZE 8 + #define TOTAL_SWAP_BLOCKS (576-128) * 2 +#endif +#if defined EXTENDED_RAM_512 || defined EXTENDED_RAM_1024 + #define SWAPDEV 0x800 /* Device for swapping - RAM disk on standard memory expansion. */ +#else + #define CONFIG_DYNAMIC_SWAP + #define SWAPDEV (swap_dev) /* Device for swapping (dynamic). */ + #define MAX_SWAPS 16 +#endif /* We swap by hitting the user map */ #define swap_map(x) ((uint8_t *)(x)) @@ -83,15 +99,12 @@ #define CONFIG_TINYIDE_SDCCPIO #define CONFIG_TINYIDE_8BIT #define IDE_IS_8BIT(x) 1 -/* SD support */ -/* #define TD_SD_NUM 2 */ -/* #define CONFIG_TD_SD */ -/* Emulator for this platform needs bug workarounds */ -/* #undef CONFIG_TD_SD_EMUBUG */ -/*#undef SD_SPI_CALLTYPE __z88dk_fastcall */ #define BOOTDEVICENAMES "hd#,fd" #define CONFIG_SMALL -/* #define DEBUG */ \ No newline at end of file + + + + diff --git a/Kernel/platform/platform-cpc6128/devices.c b/Kernel/platform/platform-cpc6128/devices.c index f29a9c0ddb..2679324c4a 100644 --- a/Kernel/platform/platform-cpc6128/devices.c +++ b/Kernel/platform/platform-cpc6128/devices.c @@ -6,10 +6,11 @@ #include #include #include -#include +/*#include */ #include #include -#include +/*#include */ +#include struct devsw dev_tab[] = /* The device driver switch table */ { @@ -24,23 +25,25 @@ struct devsw dev_tab[] = /* The device driver switch table */ /* 4: /dev/mem etc System devices (one offs) */ { no_open, no_close, sys_read, sys_write, sys_ioctl }, /* 5: Pack to 7 with nxio if adding private devices and start at 8 */ - #ifdef CONFIG_NET - { nxio_open, no_close, no_rdwr, no_rdwr, no_ioctl }, - { nxio_open, no_close, no_rdwr, no_rdwr, no_ioctl }, - { nxio_open, no_close, no_rdwr, no_rdwr, no_ioctl }, - { nxio_open, no_close, no_rdwr, no_rdwr, no_ioctl }, -#endif + #if defined EXTENDED_RAM_512 || defined EXTENDED_RAM_1024 + /* 5: unused */ + { no_open, no_close, no_rdwr, no_rdwr, no_ioctl }, + /* 6: unused */ + { no_open, no_close, no_rdwr, no_rdwr, no_ioctl }, + /* 7: unused */ + { no_open, no_close, no_rdwr, no_rdwr, no_ioctl }, + /* 8: Standard memory expansions RAM swap */ + { rd_open, no_close, rd_read, rd_write, no_ioctl }, + #endif }; - - bool validdev(uint16_t dev) { /* This is a bit uglier than needed but the right hand side is a constant this way */ if(dev > ((sizeof(dev_tab)/sizeof(struct devsw)) << 8) - 1) - return false; + return false; else return true; } diff --git a/Kernel/platform/platform-cpc6128/devinput.c b/Kernel/platform/platform-cpc6128/devinput.c deleted file mode 100644 index 34e287b76d..0000000000 --- a/Kernel/platform/platform-cpc6128/devinput.c +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Joysticks off the AY sound chip - */ -#include -#include -#include -#include - -static char buf[32]; - -static struct s_queue kqueue = { - buf, buf, buf, sizeof(buf), 0, sizeof(buf) / 2 -}; - -/* Queue a character to the input device */ -void queue_input(uint8_t c) -{ - insq(&kqueue, c); - wakeup(&kqueue); -} - - -int plt_input_read(uint8_t *slot) -{ - uint8_t r, k; - if (remq(&kqueue, &r)) { - remq(&kqueue, &k); - *slot++ = KEYPRESS_CODE | r; - *slot = k; - return 2; - } - return 0; -} - -void plt_input_wait(void) -{ - psleep(&kqueue); /* We wake this on timers so it works for sticks */ -} - -int plt_input_write(uint8_t flag) -{ - flag; - udata.u_error = EINVAL; - return -1; -} - -void poll_input(void) -{ - wakeup(&kqueue); -} - \ No newline at end of file diff --git a/Kernel/platform/platform-cpc6128/devinput.h b/Kernel/platform/platform-cpc6128/devinput.h deleted file mode 100644 index 981f702450..0000000000 --- a/Kernel/platform/platform-cpc6128/devinput.h +++ /dev/null @@ -1,2 +0,0 @@ -extern void queue_input(uint8_t); -extern void poll_input(void); diff --git a/Kernel/platform/platform-cpc6128/devrd.c b/Kernel/platform/platform-cpc6128/devrd.c new file mode 100644 index 0000000000..e8486f9d86 --- /dev/null +++ b/Kernel/platform/platform-cpc6128/devrd.c @@ -0,0 +1,83 @@ +/* + * CPC standard RAM bank memory expansions ramdisc driver, based on platform zxdiv48. + */ + +#include +#include +#include + +#if defined EXTENDED_RAM_512 || defined EXTENDED_RAM_1024 +#include + + +static int rd_transfer(uint8_t is_read, uint8_t rawflag) +{ + nblock = udata.u_nblock; + block = udata.u_block; + rd_dptr = udata.u_dptr; + rd_wr = is_read; + uint16_t swap_bank_long; + uint8_t ct = 0; + + #ifdef DEBUG + kprintf("u_dptr %p Block %u u_nblock %u rd_wr %u\n",udata.u_dptr, udata.u_block, udata.u_nblock, rd_wr); + #endif + + /* It's a disk but only for swapping (and rd_io isn't general purpose) */ + if (((block + nblock) > (TOTAL_SWAP_BLOCKS - 1)) || (rawflag == 1)) { + udata.u_error = EIO; + kprintf("dev_rd_EIO"); + return -1; + } + + /* udata could change under us so keep variables privately */ + while (ct < nblock) { + swap_bank_long = (block >> 5); + swap_bank_long = swap_bank_long + 196 + (((swap_bank_long + 8) / 4) * 4); /*Convert bank number to Register MMR value + *See https://www.cpcwiki.eu/index.php/Gate_Array#Register_MMR_.28RAM_memory_mapping.29*/ + if (swap_bank_long > 255){ + rd_swap_bank = swap_bank_long - 64; + rd_swap_mem_port_h = 0x7e; + } + else{ + rd_swap_bank = swap_bank_long; + rd_swap_mem_port_h = 0x7f; + } + rd_proc_bank = ((uint16_t)rd_dptr / 0x4000) + 0xc4; + rd_swap_bank_addr = ((block & 31) << BLKSHIFT) + 0x4000; + rd_proc_bank_addr = ((uint16_t)rd_dptr & 0x3fff) + 0x4000; + + #ifdef DEBUG + if (nblock == 1) + kprintf("swap_bank %p swap_addr %p proc_bank %p proc_addr %p count %u\n", rd_swap_bank, rd_swap_bank_addr, rd_proc_bank, rd_proc_bank_addr, ct); + #endif + rd_io(); + block++; + rd_dptr += BLKSIZE; + ct++; + } + return ct << BLKSHIFT; /*Total bytes transferred*/ +} + +int rd_open(uint8_t minor, uint16_t flag) +{ + flag; + if(minor != 0) { + udata.u_error = ENODEV; + return -1; + } + return 0; +} + +int rd_read(uint8_t minor, uint8_t rawflag, uint8_t flag) +{ + flag;minor; + return rd_transfer(true, rawflag); +} + +int rd_write(uint8_t minor, uint8_t rawflag, uint8_t flag) +{ + flag;minor; + return rd_transfer(false, rawflag); +} +#endif diff --git a/Kernel/platform/platform-cpc6128/devrd.h b/Kernel/platform/platform-cpc6128/devrd.h new file mode 100644 index 0000000000..e7c4451555 --- /dev/null +++ b/Kernel/platform/platform-cpc6128/devrd.h @@ -0,0 +1,20 @@ +/* + * + * CPC standard RAM bank memory expansions ramdisc driver + */ + +int rd_open(uint8_t minor, uint16_t flags); +int rd_read(uint8_t minor, uint8_t rawflag, uint8_t flag); +int rd_write(uint8_t minor, uint8_t rawflag, uint8_t flag); + +extern uint8_t rd_wr; +extern uint8_t rd_swap_bank; +extern uint8_t rd_swap_mem_port_h; +extern uint8_t rd_proc_bank; +extern uint8_t *rd_dptr; +extern uint16_t rd_swap_bank_addr; +extern uint16_t rd_proc_bank_addr; +extern uint16_t nblock; +extern blkno_t block; + +void rd_io(void); diff --git a/Kernel/platform/platform-cpc6128/discard.c b/Kernel/platform/platform-cpc6128/discard.c index 207e33eff2..cd9a4c8605 100644 --- a/Kernel/platform/platform-cpc6128/discard.c +++ b/Kernel/platform/platform-cpc6128/discard.c @@ -13,6 +13,11 @@ uint8_t plt_param(char *p) /* Nothing to do for the map of init */ void map_init(void) { +#if defined EXTENDED_RAM_512 || defined EXTENDED_RAM_1024 + uint_fast8_t i; + for (i = 0; i < MAX_SWAPS; i++) + swapmap_init(i); +#endif } void plt_copyright(void) diff --git a/Kernel/platform/platform-cpc6128/fuzix.lnk b/Kernel/platform/platform-cpc6128/fuzix.lnk index 2ac77d8022..f9858617a7 100644 --- a/Kernel/platform/platform-cpc6128/fuzix.lnk +++ b/Kernel/platform/platform-cpc6128/fuzix.lnk @@ -6,6 +6,7 @@ platform/platform-cpc6128/crt0.rel platform/platform-cpc6128/commonmem.rel platform/platform-cpc6128/cpc6128.rel +platform/platform-cpc6128/rd_cpcsme.rel platform/platform-cpc6128/cpcvideo.rel platform/platform-cpc6128/main.rel platform/platform-cpc6128/discard.rel @@ -54,4 +55,5 @@ platform/platform-cpc6128/devfdc765.rel platform/platform-cpc6128/fdc765.rel platform/platform-cpc6128/ch375.rel platform/platform-cpc6128/albireo.rel +platform/platform-cpc6128/devrd.rel -e diff --git a/Kernel/platform/platform-cpc6128/rd_cpcsme.c b/Kernel/platform/platform-cpc6128/rd_cpcsme.c new file mode 100644 index 0000000000..b4f6481086 --- /dev/null +++ b/Kernel/platform/platform-cpc6128/rd_cpcsme.c @@ -0,0 +1,117 @@ +/* +* RAM disc helpers for CPC Standard Memory Expansions +*/ + +#include + +#undef di +#undef ei + +#if defined EXTENDED_RAM_512 || defined EXTENDED_RAM_1024 +static void CODESEG(void) __naked { __asm .area _CODE __endasm; } /* .area _CODE */ +void rd_io(void) __naked +{ + __asm + + .globl _rd_io + + .globl _block + .globl _nblock + .globl _rd_wr + .globl _rd_swap_mem_port_h + .globl _rd_swap_bank + .globl _rd_proc_bank + .globl _rd_swap_bank_addr + .globl _rd_proc_bank_addr + .globl _rd_dptr + .globl _int_disabled + .globl _vtborder + .globl map_kernel_restore + + + + di + ld a,(_rd_wr) + or a + jp z, is_wr + ld bc,#0x7f10 + out (c),c + ld c,#0x53 ;Bright cyan + out (c),c + ld a,(_rd_swap_mem_port_h) + ld b,a + ld c,#0xff + ld a,(_rd_swap_bank) + out (c),a + ld hl,(_rd_swap_bank_addr) + ld de, #swapbuffer + ld bc,#512 + ldir + ld bc,#0x7fff + ld a,(_rd_proc_bank) + out (c),a + ld hl,#swapbuffer + ld de,(_rd_proc_bank_addr) + ld bc,#512 + ldir +end_io: + ld bc,#0x7fc1 ; map kernel + out (c),c + call map_kernel_restore ; do it the right way + ld bc,#0x7f10 + out (c),c + ld a,(_vtborder) + out (c),a + ld a,(_int_disabled) + or a + ret nz + ei + ret +is_wr: + ld bc,#0x7f10 + out (c),c + ld c,#0x4c ;Bright red + out (c),c + ld bc,#0x7fff + ld a,(_rd_proc_bank) + out (c),a + ld hl,(_rd_proc_bank_addr) + ld de,#swapbuffer + ld bc,#512 + ldir + ld a,(_rd_swap_mem_port_h) + ld b,a + ld c,#0xff + ld a,(_rd_swap_bank) + out (c),a + ld hl,#swapbuffer + ld de,(_rd_swap_bank_addr) + ld bc,#512 + ldir + jp end_io + +_rd_wr: + .db 0 +_rd_swap_bank: + .db 0 +_rd_proc_bank: + .db 0 +_rd_swap_mem_port_h: + .db 0x7f +_rd_swap_bank_addr: + .dw 0 +_rd_proc_bank_addr: + .dw 0 +_rd_dptr: + .dw 0 +_block: + .dw 0 +_nblock: + .dw 0 +swapbuffer: + .ds 512 + +__endasm; +} +#endif +