diff options
| author | svn2git <svn2git@FreeBSD.org> | 1993-11-01 08:00:00 +0000 |
|---|---|---|
| committer | svn2git <svn2git@FreeBSD.org> | 1993-11-01 08:00:00 +0000 |
| commit | 8503f4f13f77abf7adc8f7e329c6f9c1d52b6a20 (patch) | |
| tree | c5b2ce776438e0a52b492a2ab6ab41360b8ba1f6 /bin/sh | |
Diffstat (limited to 'bin/sh')
66 files changed, 16399 insertions, 0 deletions
diff --git a/bin/sh/Makefile b/bin/sh/Makefile new file mode 100644 index 000000000000..dc22825ed9a8 --- /dev/null +++ b/bin/sh/Makefile @@ -0,0 +1,41 @@ +# Makefile,v 1.7 1993/08/09 04:58:18 mycroft Exp + +PROG= sh +SRCS= builtins.c cd.c dirent.c echo.c error.c eval.c exec.c expand.c \ + input.c jobs.c mail.c main.c memalloc.c miscbltin.c \ + mystring.c nodes.c options.c parser.c redir.c show.c \ + syntax.c trap.c output.c var.c +OBJS+= init.o +CFLAGS+=-DSHELL -I. -I${.CURDIR} +.PATH: ${.CURDIR}/bltin +CLEANFILES+=\ + builtins.c builtins.h init.c mkinit mknodes mksyntax \ + nodes.c nodes.h syntax.c syntax.h token.def + +.depend parser.o: token.def +token.def: mktokens + sh ${.CURDIR}/mktokens + +builtins.h builtins.c: ${.CURDIR}/mkbuiltins ${.CURDIR}/builtins + cd ${.CURDIR}; sh mkbuiltins + +init.c: mkinit ${SRCS} + ./mkinit '${CC} -c ${CFLAGS} init.c' ${.ALLSRC} + touch ${.TARGET} + +mkinit: ${.CURDIR}/mkinit.c + ${CC} ${CFLAGS} ${LDFLAGS} ${.CURDIR}/mkinit.c -o $@ ${LDADD} + +nodes.c nodes.h: mknodes ${.CURDIR}/nodetypes ${.CURDIR}/nodes.c.pat + ./mknodes ${.CURDIR}/nodetypes ${.CURDIR}/nodes.c.pat + +mknodes: ${.CURDIR}/mknodes.c + ${CC} ${CFLAGS} ${LDFLAGS} ${.CURDIR}/mknodes.c -o $@ ${LDADD} + +syntax.c syntax.h: mksyntax + ./mksyntax + +mksyntax: ${.CURDIR}/mksyntax.c ${.CURDIR}/parser.h + ${CC} ${CFLAGS} ${LDFLAGS} ${.CURDIR}/mksyntax.c -o $@ ${LDADD} + +.include <bsd.prog.mk> diff --git a/bin/sh/TOUR b/bin/sh/TOUR new file mode 100644 index 000000000000..7cc0f226a379 --- /dev/null +++ b/bin/sh/TOUR @@ -0,0 +1,350 @@ +# @(#)TOUR 5.1 (Berkeley) 3/7/91 +# +# /b/source/CVS/src/bin/sh/TOUR,v 1.3 1993/03/23 00:27:32 cgd Exp + + A Tour through Ash + + Copyright 1989 by Kenneth Almquist. + + +DIRECTORIES: The subdirectory bltin contains commands which can +be compiled stand-alone. The rest of the source is in the main +ash directory. + +SOURCE CODE GENERATORS: Files whose names begin with "mk" are +programs that generate source code. A complete list of these +programs is: + + program intput files generates + ------- ------------ --------- + mkbuiltins builtins builtins.h builtins.c + mkinit *.c init.c + mknodes nodetypes nodes.h nodes.c + mksignames - signames.h signames.c + mksyntax - syntax.h syntax.c + mktokens - token.def + bltin/mkexpr unary_op binary_op operators.h operators.c + +There are undoubtedly too many of these. Mkinit searches all the +C source files for entries looking like: + + INIT { + x = 1; /* executed during initialization */ + } + + RESET { + x = 2; /* executed when the shell does a longjmp + back to the main command loop */ + } + + SHELLPROC { + x = 3; /* executed when the shell runs a shell procedure */ + } + +It pulls this code out into routines which are when particular +events occur. The intent is to improve modularity by isolating +the information about which modules need to be explicitly +initialized/reset within the modules themselves. + +Mkinit recognizes several constructs for placing declarations in +the init.c file. + INCLUDE "file.h" +includes a file. The storage class MKINIT makes a declaration +available in the init.c file, for example: + MKINIT int funcnest; /* depth of function calls */ +MKINIT alone on a line introduces a structure or union declara- +tion: + MKINIT + struct redirtab { + short renamed[10]; + }; +Preprocessor #define statements are copied to init.c without any +special action to request this. + +INDENTATION: The ash source is indented in multiples of six +spaces. The only study that I have heard of on the subject con- +cluded that the optimal amount to indent is in the range of four +to six spaces. I use six spaces since it is not too big a jump +from the widely used eight spaces. If you really hate six space +indentation, use the adjind (source included) program to change +it to something else. + +EXCEPTIONS: Code for dealing with exceptions appears in +exceptions.c. The C language doesn't include exception handling, +so I implement it using setjmp and longjmp. The global variable +exception contains the type of exception. EXERROR is raised by +calling error. EXINT is an interrupt. EXSHELLPROC is an excep- +tion which is raised when a shell procedure is invoked. The pur- +pose of EXSHELLPROC is to perform the cleanup actions associated +with other exceptions. After these cleanup actions, the shell +can interpret a shell procedure itself without exec'ing a new +copy of the shell. + +INTERRUPTS: In an interactive shell, an interrupt will cause an +EXINT exception to return to the main command loop. (Exception: +EXINT is not raised if the user traps interrupts using the trap +command.) The INTOFF and INTON macros (defined in exception.h) +provide uninterruptable critical sections. Between the execution +of INTOFF and the execution of INTON, interrupt signals will be +held for later delivery. INTOFF and INTON can be nested. + +MEMALLOC.C: Memalloc.c defines versions of malloc and realloc +which call error when there is no memory left. It also defines a +stack oriented memory allocation scheme. Allocating off a stack +is probably more efficient than allocation using malloc, but the +big advantage is that when an exception occurs all we have to do +to free up the memory in use at the time of the exception is to +restore the stack pointer. The stack is implemented using a +linked list of blocks. + +STPUTC: If the stack were contiguous, it would be easy to store +strings on the stack without knowing in advance how long the +string was going to be: + p = stackptr; + *p++ = c; /* repeated as many times as needed */ + stackptr = p; +The folloing three macros (defined in memalloc.h) perform these +operations, but grow the stack if you run off the end: + STARTSTACKSTR(p); + STPUTC(c, p); /* repeated as many times as needed */ + grabstackstr(p); + +We now start a top-down look at the code: + +MAIN.C: The main routine performs some initialization, executes +the user's profile if necessary, and calls cmdloop. Cmdloop is +repeatedly parses and executes commands. + +OPTIONS.C: This file contains the option processing code. It is +called from main to parse the shell arguments when the shell is +invoked, and it also contains the set builtin. The -i and -j op- +tions (the latter turns on job control) require changes in signal +handling. The routines setjobctl (in jobs.c) and setinteractive +(in trap.c) are called to handle changes to these options. + +PARSING: The parser code is all in parser.c. A recursive des- +cent parser is used. Syntax tables (generated by mksyntax) are +used to classify characters during lexical analysis. There are +three tables: one for normal use, one for use when inside single +quotes, and one for use when inside double quotes. The tables +are machine dependent because they are indexed by character vari- +ables and the range of a char varies from machine to machine. + +PARSE OUTPUT: The output of the parser consists of a tree of +nodes. The various types of nodes are defined in the file node- +types. + +Nodes of type NARG are used to represent both words and the con- +tents of here documents. An early version of ash kept the con- +tents of here documents in temporary files, but keeping here do- +cuments in memory typically results in significantly better per- +formance. It would have been nice to make it an option to use +temporary files for here documents, for the benefit of small +machines, but the code to keep track of when to delete the tem- +porary files was complex and I never fixed all the bugs in it. +(AT&T has been maintaining the Bourne shell for more than ten +years, and to the best of my knowledge they still haven't gotten +it to handle temporary files correctly in obscure cases.) + +The text field of a NARG structure points to the text of the +word. The text consists of ordinary characters and a number of +special codes defined in parser.h. The special codes are: + + CTLVAR Variable substitution + CTLENDVAR End of variable substitution + CTLBACKQ Command substitution + CTLBACKQ|CTLQUOTE Command substitution inside double quotes + CTLESC Escape next character + +A variable substitution contains the following elements: + + CTLVAR type name '=' [ alternative-text CTLENDVAR ] + +The type field is a single character specifying the type of sub- +stitution. The possible types are: + + VSNORMAL $var + VSMINUS ${var-text} + VSMINUS|VSNUL ${var:-text} + VSPLUS ${var+text} + VSPLUS|VSNUL ${var:+text} + VSQUESTION ${var?text} + VSQUESTION|VSNUL ${var:?text} + VSASSIGN ${var=text} + VSASSIGN|VSNUL ${var=text} + +In addition, the type field will have the VSQUOTE flag set if the +variable is enclosed in double quotes. The name of the variable +comes next, terminated by an equals sign. If the type is not +VSNORMAL, then the text field in the substitution follows, ter- +minated by a CTLENDVAR byte. + +Commands in back quotes are parsed and stored in a linked list. +The locations of these commands in the string are indicated by +CTLBACKQ and CTLBACKQ+CTLQUOTE characters, depending upon whether +the back quotes were enclosed in double quotes. + +The character CTLESC escapes the next character, so that in case +any of the CTL characters mentioned above appear in the input, +they can be passed through transparently. CTLESC is also used to +escape '*', '?', '[', and '!' characters which were quoted by the +user and thus should not be used for file name generation. + +CTLESC characters have proved to be particularly tricky to get +right. In the case of here documents which are not subject to +variable and command substitution, the parser doesn't insert any +CTLESC characters to begin with (so the contents of the text +field can be written without any processing). Other here docu- +ments, and words which are not subject to splitting and file name +generation, have the CTLESC characters removed during the vari- +able and command substitution phase. Words which are subject +splitting and file name generation have the CTLESC characters re- +moved as part of the file name phase. + +EXECUTION: Command execution is handled by the following files: + eval.c The top level routines. + redir.c Code to handle redirection of input and output. + jobs.c Code to handle forking, waiting, and job control. + exec.c Code to to path searches and the actual exec sys call. + expand.c Code to evaluate arguments. + var.c Maintains the variable symbol table. Called from expand.c. + +EVAL.C: Evaltree recursively executes a parse tree. The exit +status is returned in the global variable exitstatus. The alter- +native entry evalbackcmd is called to evaluate commands in back +quotes. It saves the result in memory if the command is a buil- +tin; otherwise it forks off a child to execute the command and +connects the standard output of the child to a pipe. + +JOBS.C: To create a process, you call makejob to return a job +structure, and then call forkshell (passing the job structure as +an argument) to create the process. Waitforjob waits for a job +to complete. These routines take care of process groups if job +control is defined. + +REDIR.C: Ash allows file descriptors to be redirected and then +restored without forking off a child process. This is accom- +plished by duplicating the original file descriptors. The redir- +tab structure records where the file descriptors have be dupli- +cated to. + +EXEC.C: The routine find_command locates a command, and enters +the command in the hash table if it is not already there. The +third argument specifies whether it is to print an error message +if the command is not found. (When a pipeline is set up, +find_command is called for all the commands in the pipeline be- +fore any forking is done, so to get the commands into the hash +table of the parent process. But to make command hashing as +transparent as possible, we silently ignore errors at that point +and only print error messages if the command cannot be found +later.) + +The routine shellexec is the interface to the exec system call. + +EXPAND.C: Arguments are processed in three passes. The first +(performed by the routine argstr) performs variable and command +substitution. The second (ifsbreakup) performs word splitting +and the third (expandmeta) performs file name generation. If the +"/u" directory is simulated, then when "/u/username" is replaced +by the user's home directory, the flag "didudir" is set. This +tells the cd command that it should print out the directory name, +just as it would if the "/u" directory were implemented using +symbolic links. + +VAR.C: Variables are stored in a hash table. Probably we should +switch to extensible hashing. The variable name is stored in the +same string as the value (using the format "name=value") so that +no string copying is needed to create the environment of a com- +mand. Variables which the shell references internally are preal- +located so that the shell can reference the values of these vari- +ables without doing a lookup. + +When a program is run, the code in eval.c sticks any environment +variables which precede the command (as in "PATH=xxx command") in +the variable table as the simplest way to strip duplicates, and +then calls "environment" to get the value of the environment. +There are two consequences of this. First, if an assignment to +PATH precedes the command, the value of PATH before the assign- +ment must be remembered and passed to shellexec. Second, if the +program turns out to be a shell procedure, the strings from the +environment variables which preceded the command must be pulled +out of the table and replaced with strings obtained from malloc, +since the former will automatically be freed when the stack (see +the entry on memalloc.c) is emptied. + +BUILTIN COMMANDS: The procedures for handling these are scat- +tered throughout the code, depending on which location appears +most appropriate. They can be recognized because their names al- +ways end in "cmd". The mapping from names to procedures is +specified in the file builtins, which is processed by the mkbuil- +tins command. + +A builtin command is invoked with argc and argv set up like a +normal program. A builtin command is allowed to overwrite its +arguments. Builtin routines can call nextopt to do option pars- +ing. This is kind of like getopt, but you don't pass argc and +argv to it. Builtin routines can also call error. This routine +normally terminates the shell (or returns to the main command +loop if the shell is interactive), but when called from a builtin +command it causes the builtin command to terminate with an exit +status of 2. + +The directory bltins contains commands which can be compiled in- +dependently but can also be built into the shell for efficiency +reasons. The makefile in this directory compiles these programs +in the normal fashion (so that they can be run regardless of +whether the invoker is ash), but also creates a library named +bltinlib.a which can be linked with ash. The header file bltin.h +takes care of most of the differences between the ash and the +stand-alone environment. The user should call the main routine +"main", and #define main to be the name of the routine to use +when the program is linked into ash. This #define should appear +before bltin.h is included; bltin.h will #undef main if the pro- +gram is to be compiled stand-alone. + +CD.C: This file defines the cd and pwd builtins. The pwd com- +mand runs /bin/pwd the first time it is invoked (unless the user +has already done a cd to an absolute pathname), but then +remembers the current directory and updates it when the cd com- +mand is run, so subsequent pwd commands run very fast. The main +complication in the cd command is in the docd command, which +resolves symbolic links into actual names and informs the user +where the user ended up if he crossed a symbolic link. + +SIGNALS: Trap.c implements the trap command. The routine set- +signal figures out what action should be taken when a signal is +received and invokes the signal system call to set the signal ac- +tion appropriately. When a signal that a user has set a trap for +is caught, the routine "onsig" sets a flag. The routine dotrap +is called at appropriate points to actually handle the signal. +When an interrupt is caught and no trap has been set for that +signal, the routine "onint" in error.c is called. + +OUTPUT: Ash uses it's own output routines. There are three out- +put structures allocated. "Output" represents the standard out- +put, "errout" the standard error, and "memout" contains output +which is to be stored in memory. This last is used when a buil- +tin command appears in backquotes, to allow its output to be col- +lected without doing any I/O through the UNIX operating system. +The variables out1 and out2 normally point to output and errout, +respectively, but they are set to point to memout when appropri- +ate inside backquotes. + +INPUT: The basic input routine is pgetc, which reads from the +current input file. There is a stack of input files; the current +input file is the top file on this stack. The code allows the +input to come from a string rather than a file. (This is for the +-c option and the "." and eval builtin commands.) The global +variable plinno is saved and restored when files are pushed and +popped from the stack. The parser routines store the number of +the current line in this variable. + +DEBUGGING: If DEBUG is defined in shell.h, then the shell will +write debugging information to the file $HOME/trace. Most of +this is done using the TRACE macro, which takes a set of printf +arguments inside two sets of parenthesis. Example: +"TRACE(("n=%d0, n))". The double parenthesis are necessary be- +cause the preprocessor can't handle functions with a variable +number of arguments. Defining DEBUG also causes the shell to +generate a core dump if it is sent a quit signal. The tracing +code is in show.c. diff --git a/bin/sh/b.c b/bin/sh/b.c new file mode 100644 index 000000000000..9b7f568ecdff --- /dev/null +++ b/bin/sh/b.c @@ -0,0 +1,106 @@ +/* + * This file was generated by the mkbuiltins program. + */ + +#ifndef lint +static char rcsid[] = "b.c,v 1.2 1993/08/02 17:15:45 mycroft Exp"; +#endif /* not lint */ + +#include "shell.h" +#include "builtins.h" + +int bltincmd(); +int bgcmd(); +int breakcmd(); +int cdcmd(); +int dotcmd(); +int echocmd(); +int evalcmd(); +int execcmd(); +int exitcmd(); +int exportcmd(); +int fgcmd(); +int getoptscmd(); +int hashcmd(); +int jobidcmd(); +int jobscmd(); +int lccmd(); +int localcmd(); +int pwdcmd(); +int readcmd(); +int returncmd(); +int setcmd(); +int setvarcmd(); +int shiftcmd(); +int trapcmd(); +int truecmd(); +int umaskcmd(); +int unsetcmd(); +int waitcmd(); + +int (*const builtinfunc[])() = { + bltincmd, + bgcmd, + breakcmd, + cdcmd, + dotcmd, + echocmd, + evalcmd, + execcmd, + exitcmd, + exportcmd, + fgcmd, + getoptscmd, + hashcmd, + jobidcmd, + jobscmd, + lccmd, + localcmd, + pwdcmd, + readcmd, + returncmd, + setcmd, + setvarcmd, + shiftcmd, + trapcmd, + truecmd, + umaskcmd, + unsetcmd, + waitcmd, +}; + +const struct builtincmd builtincmd[] = { + "command", 0, + "bg", 1, + "break", 2, + "continue", 2, + "cd", 3, + "chdir", 3, + ".", 4, + "echo", 5, + "eval", 6, + "exec", 7, + "exit", 8, + "export", 9, + "readonly", 9, + "fg", 10, + "getopts", 11, + "hash", 12, + "jobid", 13, + "jobs", 14, + "lc", 15, + "local", 16, + "pwd", 17, + "read", 18, + "return", 19, + "set", 20, + "setvar", 21, + "shift", 22, + "trap", 23, + ":", 24, + "true", 24, + "umask", 25, + "unset", 26, + "wait", 27, + NULL, 0 +}; diff --git a/bin/sh/bltin/bltin.h b/bin/sh/bltin/bltin.h new file mode 100644 index 000000000000..8049e4ba8d12 --- /dev/null +++ b/bin/sh/bltin/bltin.h @@ -0,0 +1,75 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * from: @(#)bltin.h 5.1 (Berkeley) 3/7/91 + * bltin.h,v 1.4 1993/08/01 18:58:44 mycroft Exp + */ + +/* + * This file is included by programs which are optionally built into the + * shell. If SHELL is defined, we try to map the standard UNIX library + * routines to ash routines using defines. + */ + +#include "../shell.h" +#include "../mystring.h" +#ifdef SHELL +#include "../output.h" +#define stdout out1 +#define stderr out2 +#define printf out1fmt +#define putc(c, file) outc(c, file) +#define putchar(c) out1c(c) +#define fprintf outfmt +#define fputs outstr +#define fflush flushout +#define INITARGS(argv) +#else +#undef NULL +#include <stdio.h> +#undef main +#define INITARGS(argv) if ((commandname = argv[0]) == NULL) {fputs("Argc is zero\n", stderr); exit(2);} else +#endif + +#ifdef __STDC__ +pointer stalloc(int); +void error(char *, ...); +#else +pointer stalloc(); +void error(); +#endif + + +extern char *commandname; diff --git a/bin/sh/bltin/echo.1 b/bin/sh/bltin/echo.1 new file mode 100644 index 000000000000..59369829b48e --- /dev/null +++ b/bin/sh/bltin/echo.1 @@ -0,0 +1,121 @@ +.\" Copyright (c) 1991 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Kenneth Almquist. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. +.\" +.\" from: @(#)echo.1 5.1 (Berkeley) 3/7/91 +.\" echo.1,v 1.4 1993/08/01 07:58:19 mycroft Exp +.\" +.TH ECHO 1"March 7, 1991" +.UC 7 +.SH NAME +echo \- produce message in a shell script +.SH SYNOPSIS +.B echo +[ +.B -n +| +.B -e +] +.I args... +.SH COPYRIGHT +Copyright 1989 by Kenneth Almquist. +.SH DESCRIPTION +.I Echo +prints its arguments on the standard output, separated by spaces. +Unless the +.B -n +option is present, a newline is output following the arguments. +The +.B -e +option causes +.I echo +to treat the escape sequences specially, as described in the following +paragraph. The +.B -e +option is the default, and is provided solely for compatibility with +other systems. +Only one of the options +.B -n +and +.B -e +may be given. +.PP +If any of the following sequences of characters is encountered during +output, the sequence is not output. Instead, the specified action is +performed: +.nr i 0.6i +.de i +.sp +.ti -\\niu +\\$1 \c +.if \w'\\$1'-\\ni .br +.. +.in 1.1i +.ta 0.6i +.i \eb +A backspace character is output. +.i \ec +Subsequent output is suppressed. This is normally used at the end of the +last argument to suppress the trailing newline that +.I echo +would otherwise output. +.i \ef +Output a form feed. +.i \en +Output a newline character. +.i \er +Output a carriage return. +.i \et +Output a (horizontal) tab character. +.i \ev +Output a vertical tab. +.i \e0\fIdigits\fR +Output the character whose value is given by zero to three digits. +If there are zero digits, a nul character is output. +.i \e\e +Output a backslash. +.in -1.1i +.SH HINTS +Remember that backslash is special to the shell and needs to be escaped. +To output a message to standard error, say +.sp +.ti +1i +echo message >&2 +.SH BUGS +The octal character escape mechanism (\e0\fIdigits\fR) differs from the +C language mechanism. +.PP +There is no way to force +.I echo +to treat its arguments literally, rather than interpreting them as +options and escape sequences. diff --git a/bin/sh/bltin/echo.c b/bin/sh/bltin/echo.c new file mode 100644 index 000000000000..cd2e072ac29d --- /dev/null +++ b/bin/sh/bltin/echo.c @@ -0,0 +1,107 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)echo.c 5.2 (Berkeley) 3/13/91";*/ +static char rcsid[] = "echo.c,v 1.4 1993/08/01 18:58:43 mycroft Exp"; +#endif /* not lint */ + +/* + * Echo command. + */ + +#define main echocmd + +#include "bltin.h" + +/* #define eflag 1 */ + +main(argc, argv) char **argv; { + register char **ap; + register char *p; + register char c; + int count; + int nflag = 0; +#ifndef eflag + int eflag = 0; +#endif + + ap = argv; + if (argc) + ap++; + if ((p = *ap) != NULL) { + if (equal(p, "-n")) { + nflag++; + ap++; + } else if (equal(p, "-e")) { +#ifndef eflag + eflag++; +#endif + ap++; + } + } + while ((p = *ap++) != NULL) { + while ((c = *p++) != '\0') { + if (c == '\\' && eflag) { + switch (*p++) { + case 'b': c = '\b'; break; + case 'c': return 0; /* exit */ + case 'f': c = '\f'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case 'v': c = '\v'; break; + case '\\': break; /* c = '\\' */ + case '0': + c = 0; + count = 3; + while (--count >= 0 && (unsigned)(*p - '0') < 8) + c = (c << 3) + (*p++ - '0'); + break; + default: + p--; + break; + } + } + putchar(c); + } + if (*ap) + putchar(' '); + } + if (! nflag) + putchar('\n'); + return 0; +} diff --git a/bin/sh/builtins b/bin/sh/builtins new file mode 100644 index 000000000000..b605bd890533 --- /dev/null +++ b/bin/sh/builtins @@ -0,0 +1,86 @@ +#!/bin/sh - +# +# Copyright (c) 1991 The Regents of the University of California. +# All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. 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. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. +# +# @(#)builtins 5.1 (Berkeley) 3/7/91 +# +# /b/source/CVS/src/bin/sh/builtins,v 1.4 1993/07/07 01:11:56 jtc Exp + +# +# This file lists all the builtin commands. The first column is the name +# of a C routine. The -j flag, if present, specifies that this command +# is to be excluded from systems without job control. The rest of the line +# specifies the command name or names used to run the command. The entry +# for nullcmd, which is run when the user does not specify a command, must +# come first. +# +# Copyright (C) 1989 by Kenneth Almquist. All rights reserved. +# This file is part of ash, which is distributed under the terms specified +# by the Ash General Public License. See the file named LICENSE. + +bltincmd command +#alloccmd alloc +bgcmd -j bg +breakcmd break continue +#catfcmd catf +cdcmd cd chdir +dotcmd . +echocmd echo +evalcmd eval +execcmd exec +exitcmd exit +exportcmd export readonly +#exprcmd expr test [ +falsecmd false +fgcmd -j fg +getoptscmd getopts +hashcmd hash +jobidcmd jobid +jobscmd jobs +lccmd lc +#linecmd line +localcmd local +#nlechocmd nlecho +pwdcmd pwd +readcmd read +returncmd return +setcmd set +setvarcmd setvar +shiftcmd shift +trapcmd trap +truecmd : true +umaskcmd umask +unsetcmd unset +waitcmd wait diff --git a/bin/sh/cd.c b/bin/sh/cd.c new file mode 100644 index 000000000000..e458c1f0ae20 --- /dev/null +++ b/bin/sh/cd.c @@ -0,0 +1,374 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)cd.c 5.2 (Berkeley) 3/13/91";*/ +static char rcsid[] = "cd.c,v 1.5 1993/08/01 18:58:22 mycroft Exp"; +#endif /* not lint */ + +/* + * The cd and pwd commands. + */ + +#include "shell.h" +#include "var.h" +#include "nodes.h" /* for jobs.h */ +#include "jobs.h" +#include "options.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> + + +#ifdef __STDC__ +STATIC int docd(char *, int); +STATIC void updatepwd(char *); +STATIC void getpwd(void); +STATIC char *getcomponent(void); +#else +STATIC int docd(); +STATIC void updatepwd(); +STATIC void getpwd(); +STATIC char *getcomponent(); +#endif + + +char *curdir; /* current working directory */ +STATIC char *cdcomppath; + +#if UDIR +extern int didudir; /* set if /u/logname expanded */ +#endif + + +int +cdcmd(argc, argv) char **argv; { + char *dest; + char *path; + char *p; + struct stat statb; + char *padvance(); + + nextopt(nullstr); + if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME", 1)) == NULL) + error("HOME not set"); + if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL) + path = nullstr; + while ((p = padvance(&path, dest)) != NULL) { + if (stat(p, &statb) >= 0 + && (statb.st_mode & S_IFMT) == S_IFDIR + && docd(p, strcmp(p, dest)) >= 0) + return 0; + } + error("can't cd to %s", dest); +} + + +/* + * Actually do the chdir. If the name refers to symbolic links, we + * compute the actual directory name before doing the cd. In an + * interactive shell, print the directory name if "print" is nonzero + * or if the name refers to a symbolic link. We also print the name + * if "/u/logname" was expanded in it, since this is similar to a + * symbolic link. (The check for this breaks if the user gives the + * cd command some additional, unused arguments.) + */ + +#if SYMLINKS == 0 +STATIC int +docd(dest, print) + char *dest; + { +#if UDIR + if (didudir) + print = 1; +#endif + INTOFF; + if (chdir(dest) < 0) { + INTON; + return -1; + } + updatepwd(dest); + INTON; +#ifdef not + if (print && iflag) + out1fmt("%s\n", stackblock()); +#endif + return 0; +} + +#else + + + +STATIC int +docd(dest, print) + char *dest; + { + register char *p; + register char *q; + char *symlink; + char *component; + struct stat statb; + int first; + int i; + + TRACE(("docd(\"%s\", %d) called\n", dest, print)); +#if UDIR + if (didudir) + print = 1; +#endif + +top: + cdcomppath = dest; + STARTSTACKSTR(p); + if (*dest == '/') { + STPUTC('/', p); + cdcomppath++; + } + first = 1; + while ((q = getcomponent()) != NULL) { + if (q[0] == '\0' || q[0] == '.' && q[1] == '\0') + continue; + if (! first) + STPUTC('/', p); + first = 0; + component = q; + while (*q) + STPUTC(*q++, p); + if (equal(component, "..")) + continue; + STACKSTRNUL(p); + if (lstat(stackblock(), &statb) < 0) + error("lstat %s failed", stackblock()); + if ((statb.st_mode & S_IFMT) != S_IFLNK) + continue; + + /* Hit a symbolic link. We have to start all over again. */ + print = 1; + STPUTC('\0', p); + symlink = grabstackstr(p); + i = (int)statb.st_size + 2; /* 2 for '/' and '\0' */ + if (cdcomppath != NULL) + i += strlen(cdcomppath); + p = stalloc(i); + if (readlink(symlink, p, (int)statb.st_size) < 0) { + error("readlink %s failed", stackblock()); + } + if (cdcomppath != NULL) { + p[(int)statb.st_size] = '/'; + scopy(cdcomppath, p + (int)statb.st_size + 1); + } else { + p[(int)statb.st_size] = '\0'; + } + if (p[0] != '/') { /* relative path name */ + char *r; + q = r = symlink; + while (*q) { + if (*q++ == '/') + r = q; + } + *r = '\0'; + dest = stalloc(strlen(symlink) + strlen(p) + 1); + scopy(symlink, dest); + strcat(dest, p); + } else { + dest = p; + } + goto top; + } + STPUTC('\0', p); + p = grabstackstr(p); + INTOFF; + if (chdir(p) < 0) { + INTON; + return -1; + } + updatepwd(p); + INTON; +#ifdef not + if (print && iflag) + out1fmt("%s\n", p); +#endif + return 0; +} +#endif /* SYMLINKS */ + + + +/* + * Get the next component of the path name pointed to by cdcomppath. + * This routine overwrites the string pointed to by cdcomppath. + */ + +STATIC char * +getcomponent() { + register char *p; + char *start; + + if ((p = cdcomppath) == NULL) + return NULL; + start = cdcomppath; + while (*p != '/' && *p != '\0') + p++; + if (*p == '\0') { + cdcomppath = NULL; + } else { + *p++ = '\0'; + cdcomppath = p; + } + return start; +} + + + +/* + * Update curdir (the name of the current directory) in response to a + * cd command. We also call hashcd to let the routines in exec.c know + * that the current directory has changed. + */ + +void hashcd(); + +STATIC void +updatepwd(dir) + char *dir; + { + char *new; + char *p; + + hashcd(); /* update command hash table */ + cdcomppath = stalloc(strlen(dir) + 1); + scopy(dir, cdcomppath); + STARTSTACKSTR(new); + if (*dir != '/') { + if (curdir == NULL) + return; + p = curdir; + while (*p) + STPUTC(*p++, new); + if (p[-1] == '/') + STUNPUTC(new); + } + while ((p = getcomponent()) != NULL) { + if (equal(p, "..")) { + while (new > stackblock() && (STUNPUTC(new), *new) != '/'); + } else if (*p != '\0' && ! equal(p, ".")) { + STPUTC('/', new); + while (*p) + STPUTC(*p++, new); + } + } + if (new == stackblock()) + STPUTC('/', new); + STACKSTRNUL(new); + if (curdir) + ckfree(curdir); + curdir = savestr(stackblock()); +} + + + +int +pwdcmd(argc, argv) char **argv; { + getpwd(); + out1str(curdir); + out1c('\n'); + return 0; +} + + + +/* + * Run /bin/pwd to find out what the current directory is. We suppress + * interrupts throughout most of this, but the user can still break out + * of it by killing the pwd program. If we already know the current + * directory, this routine returns immediately. + */ + +#define MAXPWD 256 + +STATIC void +getpwd() { + char buf[MAXPWD]; + char *p; + int i; + int status; + struct job *jp; + int pip[2]; + + if (curdir) + return; + INTOFF; + if (pipe(pip) < 0) + error("Pipe call failed"); + jp = makejob((union node *)NULL, 1); + if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) { + close(pip[0]); + if (pip[1] != 1) { + close(1); + copyfd(pip[1], 1); + close(pip[1]); + } + execl("/bin/pwd", "pwd", (char *)0); + /* error("Cannot exec /bin/pwd");*/ + out2str("Cannot exec /bin/pwd\n"); /* 22 Aug 92*/ + flushall(); + _exit(1); + } + close(pip[1]); + pip[1] = -1; + p = buf; + while ((i = read(pip[0], p, buf + MAXPWD - p)) > 0 + || i == -1 && errno == EINTR) { + if (i > 0) + p += i; + } + close(pip[0]); + pip[0] = -1; + status = waitforjob(jp); + if (status != 0) + error((char *)0); + if (i < 0 || p == buf || p[-1] != '\n') + error("pwd command failed"); + p[-1] = '\0'; + curdir = savestr(buf); + INTON; +} diff --git a/bin/sh/dirent.c b/bin/sh/dirent.c new file mode 100644 index 000000000000..6c3242d1b7e6 --- /dev/null +++ b/bin/sh/dirent.c @@ -0,0 +1,195 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)dirent.c 5.1 (Berkeley) 3/7/91";*/ +static char rcsid[] = "dirent.c,v 1.4 1993/08/01 18:58:21 mycroft Exp"; +#endif /* not lint */ + +#include "shell.h" /* definitions for pointer, NULL, DIRENT, and BSD */ + +#if ! DIRENT + +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <dirent.h> + +#ifndef S_ISDIR /* macro to test for directory file */ +#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif + +#ifdef BSD + +#ifdef __STDC__ +int stat(char *, struct stat *); +#else +int stat(); +#endif + + +/* + * The BSD opendir routine doesn't check that what is being opened is a + * directory, so we have to include the check in a wrapper routine. + */ + +#undef opendir + +DIR * +myopendir(dirname) + char *dirname; /* name of directory */ + { + struct stat statb; + + if (stat(dirname, &statb) != 0 || ! S_ISDIR(statb.st_mode)) { + errno = ENOTDIR; + return NULL; /* not a directory */ + } + return opendir(dirname); +} + +#else /* not BSD */ + +/* + * Dirent routines for old style file systems. + */ + +#ifdef __STDC__ +pointer malloc(unsigned); +void free(pointer); +int open(char *, int, ...); +int close(int); +int fstat(int, struct stat *); +#else +pointer malloc(); +void free(); +int open(); +int close(); +int fstat(); +#endif + + +DIR * +opendir(dirname) + char *dirname; /* name of directory */ + { + register DIR *dirp; /* -> malloc'ed storage */ + register int fd; /* file descriptor for read */ + struct stat statb; /* result of fstat() */ + +#ifdef O_NDELAY + fd = open(dirname, O_RDONLY|O_NDELAY); +#else + fd = open(dirname, O_RDONLY); +#endif + if (fd < 0) + return NULL; /* errno set by open() */ + + if (fstat(fd, &statb) != 0 || !S_ISDIR(statb.st_mode)) { + (void)close(fd); + errno = ENOTDIR; + return NULL; /* not a directory */ + } + + if ((dirp = (DIR *)malloc(sizeof(DIR))) == NULL) { + (void)close(fd); + errno = ENOMEM; + return NULL; /* not enough memory */ + } + + dirp->dd_fd = fd; + dirp->dd_nleft = 0; /* refill needed */ + + return dirp; +} + + + +int +closedir(dirp) + register DIR *dirp; /* stream from opendir() */ + { + register int fd; + + if (dirp == NULL) { + errno = EFAULT; + return -1; /* invalid pointer */ + } + + fd = dirp->dd_fd; + free((pointer)dirp); + return close(fd); +} + + + +struct dirent * +readdir(dirp) + register DIR *dirp; /* stream from opendir() */ + { + register struct direct *dp; + register char *p, *q; + register int i; + + do { + if ((dirp->dd_nleft -= sizeof (struct direct)) < 0) { + if ((i = read(dirp->dd_fd, + (char *)dirp->dd_buf, + DIRBUFENT*sizeof(struct direct))) <= 0) { + if (i == 0) + errno = 0; /* unnecessary */ + return NULL; /* EOF or error */ + } + dirp->dd_loc = dirp->dd_buf; + dirp->dd_nleft = i - sizeof (struct direct); + } + dp = dirp->dd_loc++; + } while (dp->d_ino == 0); + dirp->dd_entry.d_ino = dp->d_ino; + + /* now copy the name, nul terminating it */ + p = dp->d_name; + q = dirp->dd_entry.d_name; + i = DIRSIZ; + while (--i >= 0 && *p != '\0') + *q++ = *p++; + *q = '\0'; + return &dirp->dd_entry; +} + +#endif /* BSD */ +#endif /* DIRENT */ diff --git a/bin/sh/errmsg.c b/bin/sh/errmsg.c new file mode 100644 index 000000000000..fb9dc8405743 --- /dev/null +++ b/bin/sh/errmsg.c @@ -0,0 +1,128 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)errmsg.c 5.1 (Berkeley) 3/7/91";*/ +static char rcsid[] = "errmsg.c,v 1.4 1993/08/01 18:58:20 mycroft Exp"; +#endif /* not lint */ + +#include "shell.h" +#include "output.h" +#include "errmsg.h" +#include <errno.h> + + +#define ALL (E_OPEN|E_CREAT|E_EXEC) + + +struct errname { + short errcode; /* error number */ + short action; /* operation which encountered the error */ + char *msg; /* text describing the error */ +}; + + +STATIC const struct errname errormsg[] = { + EINTR, ALL, "interrupted", + EACCES, ALL, "permission denied", + EIO, ALL, "I/O error", + ENOENT, E_OPEN, "no such file", + ENOENT, E_CREAT, "directory nonexistent", + ENOENT, E_EXEC, "not found", + ENOTDIR, E_OPEN, "no such file", + ENOTDIR, E_CREAT, "directory nonexistent", + ENOTDIR, E_EXEC, "not found", + EISDIR, ALL, "is a directory", +/* EMFILE, ALL, "too many open files", */ + ENFILE, ALL, "file table overflow", + ENOSPC, ALL, "file system full", +#ifdef EDQUOT + EDQUOT, ALL, "disk quota exceeded", +#endif +#ifdef ENOSR + ENOSR, ALL, "no streams resources", +#endif + ENXIO, ALL, "no such device or address", + EROFS, ALL, "read-only file system", + ETXTBSY, ALL, "text busy", +#ifdef SYSV + EAGAIN, E_EXEC, "not enough memory", +#endif + ENOMEM, ALL, "not enough memory", +#ifdef ENOLINK + ENOLINK, ALL, "remote access failed" +#endif +#ifdef EMULTIHOP + EMULTIHOP, ALL, "remote access failed", +#endif +#ifdef ECOMM + ECOMM, ALL, "remote access failed", +#endif +#ifdef ESTALE + ESTALE, ALL, "remote access failed", +#endif +#ifdef ETIMEDOUT + ETIMEDOUT, ALL, "remote access failed", +#endif +#ifdef ELOOP + ELOOP, ALL, "symbolic link loop", +#endif + E2BIG, E_EXEC, "argument list too long", +#ifdef ELIBACC + ELIBACC, E_EXEC, "shared library missing", +#endif + 0, 0, NULL +}; + + +/* + * Return a string describing an error. The returned string may be a + * pointer to a static buffer that will be overwritten on the next call. + * Action describes the operation that got the error. + */ + +char * +errmsg(e, action) { + struct errname const *ep; + static char buf[12]; + + for (ep = errormsg ; ep->errcode ; ep++) { + if (ep->errcode == e && (ep->action & action) != 0) + return ep->msg; + } + fmtstr(buf, sizeof buf, "error %d", e); + return buf; +} diff --git a/bin/sh/errmsg.h b/bin/sh/errmsg.h new file mode 100644 index 000000000000..b5a43b980dbf --- /dev/null +++ b/bin/sh/errmsg.h @@ -0,0 +1,48 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * from: @(#)errmsg.h 5.1 (Berkeley) 3/7/91 + * errmsg.h,v 1.4 1993/08/01 18:58:33 mycroft Exp + */ + +#define E_OPEN 01 +#define E_CREAT 02 +#define E_EXEC 04 + +#ifdef __STDC__ +char *errmsg(int, int); +#else +char *errmsg(); +#endif diff --git a/bin/sh/error.c b/bin/sh/error.c new file mode 100644 index 000000000000..430de28465a8 --- /dev/null +++ b/bin/sh/error.c @@ -0,0 +1,250 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)error.c 5.1 (Berkeley) 3/7/91";*/ +static char rcsid[] = "error.c,v 1.4 1993/08/01 18:58:19 mycroft Exp"; +#endif /* not lint */ + +/* + * Errors and exceptions. + */ + +#include "shell.h" +#include "main.h" +#include "options.h" +#include "output.h" +#include "error.h" +#include <signal.h> +#ifdef __STDC__ +#include "stdarg.h" +#else +#include <varargs.h> +#endif +#include <errno.h> + + +/* + * Code to handle exceptions in C. + */ + +struct jmploc *handler; +int exception; +volatile int suppressint; +volatile int intpending; +char *commandname; + + +/* + * Called to raise an exception. Since C doesn't include exceptions, we + * just do a longjmp to the exception handler. The type of exception is + * stored in the global variable "exception". + */ + +void +exraise(e) { + if (handler == NULL) + abort(); + exception = e; + longjmp(handler->loc, 1); +} + + +/* + * Called from trap.c when a SIGINT is received. (If the user specifies + * that SIGINT is to be trapped or ignored using the trap builtin, then + * this routine is not called.) Suppressint is nonzero when interrupts + * are held using the INTOFF macro. The call to _exit is necessary because + * there is a short period after a fork before the signal handlers are + * set to the appropriate value for the child. (The test for iflag is + * just defensive programming.) + */ + +void +onint() { + if (suppressint) { + intpending++; + return; + } + intpending = 0; +#ifdef BSD + sigsetmask(0); +#endif + if (rootshell && iflag) + exraise(EXINT); + else + _exit(128 + SIGINT); +} + + + +void +error2(a, b) + char *a, *b; + { + error("%s: %s", a, b); +} + + +/* + * Error is called to raise the error exception. If the first argument + * is not NULL then error prints an error message using printf style + * formatting. It then raises the error exception. + */ + +#ifdef __STDC__ +void +error(char *msg, ...) { +#else +void +error(va_alist) + va_dcl + { + char *msg; +#endif + va_list ap; + + CLEAR_PENDING_INT; + INTOFF; +#ifdef __STDC__ + va_start(ap, msg); +#else + va_start(ap); + msg = va_arg(ap, char *); +#endif +#ifdef DEBUG + if (msg) + TRACE(("error(\"%s\") pid=%d\n", msg, getpid())); + else + TRACE(("error(NULL) pid=%d\n", getpid())); +#endif + if (msg) { + if (commandname) + outfmt(&errout, "%s: ", commandname); + doformat(&errout, msg, ap); + out2c('\n'); + } + va_end(ap); + flushall(); + exraise(EXERROR); +} + + + +/* + * Table of error messages. + */ + +struct errname { + short errcode; /* error number */ + short action; /* operation which encountered the error */ + char *msg; /* text describing the error */ +}; + + +#define ALL (E_OPEN|E_CREAT|E_EXEC) + +STATIC const struct errname errormsg[] = { + EINTR, ALL, "interrupted", + EACCES, ALL, "permission denied", + EIO, ALL, "I/O error", + ENOENT, E_OPEN, "no such file", + ENOENT, E_CREAT, "directory nonexistent", + ENOENT, E_EXEC, "not found", + ENOTDIR, E_OPEN, "no such file", + ENOTDIR, E_CREAT, "directory nonexistent", + ENOTDIR, E_EXEC, "not found", + EISDIR, ALL, "is a directory", +/* EMFILE, ALL, "too many open files", */ + ENFILE, ALL, "file table overflow", + ENOSPC, ALL, "file system full", +#ifdef EDQUOT + EDQUOT, ALL, "disk quota exceeded", +#endif +#ifdef ENOSR + ENOSR, ALL, "no streams resources", +#endif + ENXIO, ALL, "no such device or address", + EROFS, ALL, "read-only file system", + ETXTBSY, ALL, "text busy", +#ifdef SYSV + EAGAIN, E_EXEC, "not enough memory", +#endif + ENOMEM, ALL, "not enough memory", +#ifdef ENOLINK + ENOLINK, ALL, "remote access failed", +#endif +#ifdef EMULTIHOP + EMULTIHOP, ALL, "remote access failed", +#endif +#ifdef ECOMM + ECOMM, ALL, "remote access failed", +#endif +#ifdef ESTALE + ESTALE, ALL, "remote access failed", +#endif +#ifdef ETIMEDOUT + ETIMEDOUT, ALL, "remote access failed", +#endif +#ifdef ELOOP + ELOOP, ALL, "symbolic link loop", +#endif + E2BIG, E_EXEC, "argument list too long", +#ifdef ELIBACC + ELIBACC, E_EXEC, "shared library missing", +#endif + 0, 0, NULL +}; + + +/* + * Return a string describing an error. The returned string may be a + * pointer to a static buffer that will be overwritten on the next call. + * Action describes the operation that got the error. + */ + +char * +errmsg(e, action) { + struct errname const *ep; + static char buf[12]; + + for (ep = errormsg ; ep->errcode ; ep++) { + if (ep->errcode == e && (ep->action & action) != 0) + return ep->msg; + } + fmtstr(buf, sizeof buf, "error %d", e); + return buf; +} diff --git a/bin/sh/error.h b/bin/sh/error.h new file mode 100644 index 000000000000..d3cd46124bbc --- /dev/null +++ b/bin/sh/error.h @@ -0,0 +1,114 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * from: @(#)error.h 5.1 (Berkeley) 3/7/91 + * error.h,v 1.4 1993/08/01 18:58:32 mycroft Exp + */ + +/* + * Types of operations (passed to the errmsg routine). + */ + +#define E_OPEN 01 /* opening a file */ +#define E_CREAT 02 /* creating a file */ +#define E_EXEC 04 /* executing a program */ + + +/* + * We enclose jmp_buf in a structure so that we can declare pointers to + * jump locations. The global variable handler contains the location to + * jump to when an exception occurs, and the global variable exception + * contains a code identifying the exeception. To implement nested + * exception handlers, the user should save the value of handler on entry + * to an inner scope, set handler to point to a jmploc structure for the + * inner scope, and restore handler on exit from the scope. + */ + +#include <setjmp.h> + +struct jmploc { + jmp_buf loc; +}; + +extern struct jmploc *handler; +extern int exception; + +/* exceptions */ +#define EXINT 0 /* SIGINT received */ +#define EXERROR 1 /* a generic error */ +#define EXSHELLPROC 2 /* execute a shell procedure */ + + +/* + * These macros allow the user to suspend the handling of interrupt signals + * over a period of time. This is similar to SIGHOLD to or sigblock, but + * much more efficient and portable. (But hacking the kernel is so much + * more fun than worrying about efficiency and portability. :-)) + */ + +extern volatile int suppressint; +extern volatile int intpending; +extern char *commandname; /* name of command--printed on error */ + +#define INTOFF suppressint++ +#define INTON if (--suppressint == 0 && intpending) onint(); else +#define FORCEINTON {suppressint = 0; if (intpending) onint();} +#define CLEAR_PENDING_INT intpending = 0 +#define int_pending() intpending + +#ifdef __STDC__ +void exraise(int); +void onint(void); +void error2(char *, char *); +void error(char *, ...); +char *errmsg(int, int); +#else +void exraise(); +void onint(); +void error2(); +void error(); +char *errmsg(); +#endif + + +/* + * BSD setjmp saves the signal mask, which violates ANSI C and takes time, + * so we use _setjmp instead. + */ + +#ifdef BSD +#define setjmp(jmploc) _setjmp(jmploc) +#define longjmp(jmploc, val) _longjmp(jmploc, val) +#endif diff --git a/bin/sh/eval.c b/bin/sh/eval.c new file mode 100644 index 000000000000..718876e95b50 --- /dev/null +++ b/bin/sh/eval.c @@ -0,0 +1,924 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)eval.c 5.3 (Berkeley) 4/12/91";*/ +static char rcsid[] = "eval.c,v 1.5 1993/08/01 18:58:18 mycroft Exp"; +#endif /* not lint */ + +/* + * Evaluate a command. + */ + +#include "shell.h" +#include "nodes.h" +#include "syntax.h" +#include "expand.h" +#include "parser.h" +#include "jobs.h" +#include "eval.h" +#include "builtins.h" +#include "options.h" +#include "exec.h" +#include "redir.h" +#include "input.h" +#include "output.h" +#include "trap.h" +#include "var.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" +#include <signal.h> + + +/* flags in argument to evaltree */ +#define EV_EXIT 01 /* exit after evaluating tree */ +#define EV_TESTED 02 /* exit status is checked; ignore -e flag */ +#define EV_BACKCMD 04 /* command executing within back quotes */ + + +/* reasons for skipping commands (see comment on breakcmd routine) */ +#define SKIPBREAK 1 +#define SKIPCONT 2 +#define SKIPFUNC 3 + +MKINIT int evalskip; /* set if we are skipping commands */ +STATIC int skipcount; /* number of levels to skip */ +MKINIT int loopnest; /* current loop nesting level */ +int funcnest; /* depth of function calls */ + + +char *commandname; +struct strlist *cmdenviron; +int exitstatus; /* exit status of last command */ + + +#ifdef __STDC__ +STATIC void evalloop(union node *); +STATIC void evalfor(union node *); +STATIC void evalcase(union node *, int); +STATIC void evalsubshell(union node *, int); +STATIC void expredir(union node *); +STATIC void evalpipe(union node *); +STATIC void evalcommand(union node *, int, struct backcmd *); +STATIC void prehash(union node *); +#else +STATIC void evalloop(); +STATIC void evalfor(); +STATIC void evalcase(); +STATIC void evalsubshell(); +STATIC void expredir(); +STATIC void evalpipe(); +STATIC void evalcommand(); +STATIC void prehash(); +#endif + + + +/* + * Called to reset things after an exception. + */ + +#ifdef mkinit +INCLUDE "eval.h" + +RESET { + evalskip = 0; + loopnest = 0; + funcnest = 0; +} + +SHELLPROC { + exitstatus = 0; +} +#endif + + + +/* + * The eval commmand. + */ + +evalcmd(argc, argv) + char **argv; +{ + char *p; + char *concat; + char **ap; + + if (argc > 1) { + p = argv[1]; + if (argc > 2) { + STARTSTACKSTR(concat); + ap = argv + 2; + for (;;) { + while (*p) + STPUTC(*p++, concat); + if ((p = *ap++) == NULL) + break; + STPUTC(' ', concat); + } + STPUTC('\0', concat); + p = grabstackstr(concat); + } + evalstring(p); + } + return exitstatus; +} + + +/* + * Execute a command or commands contained in a string. + */ + +void +evalstring(s) + char *s; + { + union node *n; + struct stackmark smark; + + setstackmark(&smark); + setinputstring(s, 1); + while ((n = parsecmd(0)) != NEOF) { + evaltree(n, 0); + popstackmark(&smark); + } + popfile(); + popstackmark(&smark); +} + + + +/* + * Evaluate a parse tree. The value is left in the global variable + * exitstatus. + */ + +void +evaltree(n, flags) + union node *n; + { + if (n == NULL) { + TRACE(("evaltree(NULL) called\n")); + return; + } + TRACE(("evaltree(0x%x: %d) called\n", (int)n, n->type)); + switch (n->type) { + case NSEMI: + evaltree(n->nbinary.ch1, 0); + if (evalskip) + goto out; + evaltree(n->nbinary.ch2, flags); + break; + case NAND: + evaltree(n->nbinary.ch1, EV_TESTED); + if (evalskip || exitstatus != 0) + goto out; + evaltree(n->nbinary.ch2, flags); + break; + case NOR: + evaltree(n->nbinary.ch1, EV_TESTED); + if (evalskip || exitstatus == 0) + goto out; + evaltree(n->nbinary.ch2, flags); + break; + case NREDIR: + expredir(n->nredir.redirect); + redirect(n->nredir.redirect, REDIR_PUSH); + evaltree(n->nredir.n, flags); + popredir(); + break; + case NSUBSHELL: + evalsubshell(n, flags); + break; + case NBACKGND: + evalsubshell(n, flags); + break; + case NIF: { + int status = 0; + + evaltree(n->nif.test, EV_TESTED); + if (evalskip) + goto out; + if (exitstatus == 0) { + evaltree(n->nif.ifpart, flags); + status = exitstatus; + } else if (n->nif.elsepart) { + evaltree(n->nif.elsepart, flags); + status = exitstatus; + } + exitstatus = status; + break; + } + case NWHILE: + case NUNTIL: + evalloop(n); + break; + case NFOR: + evalfor(n); + break; + case NCASE: + evalcase(n, flags); + break; + case NDEFUN: + defun(n->narg.text, n->narg.next); + exitstatus = 0; + break; + case NPIPE: + evalpipe(n); + break; + case NCMD: + evalcommand(n, flags, (struct backcmd *)NULL); + break; + default: + out1fmt("Node type = %d\n", n->type); + flushout(&output); + break; + } +out: + if (pendingsigs) + dotrap(); + if ((flags & EV_EXIT) || (eflag && exitstatus && !(flags & EV_TESTED))) + exitshell(exitstatus); +} + + +STATIC void +evalloop(n) + union node *n; + { + int status; + + loopnest++; + status = 0; + for (;;) { + evaltree(n->nbinary.ch1, EV_TESTED); + if (evalskip) { +skipping: if (evalskip == SKIPCONT && --skipcount <= 0) { + evalskip = 0; + continue; + } + if (evalskip == SKIPBREAK && --skipcount <= 0) + evalskip = 0; + break; + } + if (n->type == NWHILE) { + if (exitstatus != 0) + break; + } else { + if (exitstatus == 0) + break; + } + evaltree(n->nbinary.ch2, 0); + status = exitstatus; + if (evalskip) + goto skipping; + } + loopnest--; + exitstatus = status; +} + + + +STATIC void +evalfor(n) + union node *n; + { + struct arglist arglist; + union node *argp; + struct strlist *sp; + struct stackmark smark; + + setstackmark(&smark); + arglist.lastp = &arglist.list; + for (argp = n->nfor.args ; argp ; argp = argp->narg.next) { + expandarg(argp, &arglist, 1); + if (evalskip) + goto out; + } + *arglist.lastp = NULL; + + exitstatus = 0; + loopnest++; + for (sp = arglist.list ; sp ; sp = sp->next) { + setvar(n->nfor.var, sp->text, 0); + evaltree(n->nfor.body, 0); + if (evalskip) { + if (evalskip == SKIPCONT && --skipcount <= 0) { + evalskip = 0; + continue; + } + if (evalskip == SKIPBREAK && --skipcount <= 0) + evalskip = 0; + break; + } + } + loopnest--; +out: + popstackmark(&smark); +} + + + +STATIC void +evalcase(n, flags) + union node *n; + { + union node *cp; + union node *patp; + struct arglist arglist; + struct stackmark smark; + + setstackmark(&smark); + arglist.lastp = &arglist.list; + expandarg(n->ncase.expr, &arglist, 0); + for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) { + for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) { + if (casematch(patp, arglist.list->text)) { + if (evalskip == 0) { + evaltree(cp->nclist.body, flags); + } + goto out; + } + } + } +out: + popstackmark(&smark); +} + + + +/* + * Kick off a subshell to evaluate a tree. + */ + +STATIC void +evalsubshell(n, flags) + union node *n; + { + struct job *jp; + int backgnd = (n->type == NBACKGND); + + expredir(n->nredir.redirect); + jp = makejob(n, 1); + if (forkshell(jp, n, backgnd) == 0) { + if (backgnd) + flags &=~ EV_TESTED; + redirect(n->nredir.redirect, 0); + evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */ + } + if (! backgnd) { + INTOFF; + exitstatus = waitforjob(jp); + INTON; + } +} + + + +/* + * Compute the names of the files in a redirection list. + */ + +STATIC void +expredir(n) + union node *n; + { + register union node *redir; + + for (redir = n ; redir ; redir = redir->nfile.next) { + if (redir->type == NFROM + || redir->type == NTO + || redir->type == NAPPEND) { + struct arglist fn; + fn.lastp = &fn.list; + expandarg(redir->nfile.fname, &fn, 0); + redir->nfile.expfname = fn.list->text; + } + } +} + + + +/* + * Evaluate a pipeline. All the processes in the pipeline are children + * of the process creating the pipeline. (This differs from some versions + * of the shell, which make the last process in a pipeline the parent + * of all the rest.) + */ + +STATIC void +evalpipe(n) + union node *n; + { + struct job *jp; + struct nodelist *lp; + int pipelen; + int prevfd; + int pip[2]; + + TRACE(("evalpipe(0x%x) called\n", (int)n)); + pipelen = 0; + for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) + pipelen++; + INTOFF; + jp = makejob(n, pipelen); + prevfd = -1; + for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { + prehash(lp->n); + pip[1] = -1; + if (lp->next) { + if (pipe(pip) < 0) { + close(prevfd); + error("Pipe call failed"); + } + } + if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) { + INTON; + if (prevfd > 0) { + close(0); + copyfd(prevfd, 0); + close(prevfd); + } + if (pip[1] >= 0) { + close(pip[0]); + if (pip[1] != 1) { + close(1); + copyfd(pip[1], 1); + close(pip[1]); + } + } + evaltree(lp->n, EV_EXIT); + } + if (prevfd >= 0) + close(prevfd); + prevfd = pip[0]; + close(pip[1]); + } + INTON; + if (n->npipe.backgnd == 0) { + INTOFF; + exitstatus = waitforjob(jp); + TRACE(("evalpipe: job done exit status %d\n", exitstatus)); + INTON; + } +} + + + +/* + * Execute a command inside back quotes. If it's a builtin command, we + * want to save its output in a block obtained from malloc. Otherwise + * we fork off a subprocess and get the output of the command via a pipe. + * Should be called with interrupts off. + */ + +void +evalbackcmd(n, result) + union node *n; + struct backcmd *result; + { + int pip[2]; + struct job *jp; + struct stackmark smark; /* unnecessary */ + + setstackmark(&smark); + result->fd = -1; + result->buf = NULL; + result->nleft = 0; + result->jp = NULL; + if (n->type == NCMD) { + evalcommand(n, EV_BACKCMD, result); + } else { + if (pipe(pip) < 0) + error("Pipe call failed"); + jp = makejob(n, 1); + if (forkshell(jp, n, FORK_NOJOB) == 0) { + FORCEINTON; + close(pip[0]); + if (pip[1] != 1) { + close(1); + copyfd(pip[1], 1); + close(pip[1]); + } + evaltree(n, EV_EXIT); + } + close(pip[1]); + result->fd = pip[0]; + result->jp = jp; + } + popstackmark(&smark); + TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n", + result->fd, result->buf, result->nleft, result->jp)); +} + + + +/* + * Execute a simple command. + */ + +STATIC void +evalcommand(cmd, flags, backcmd) + union node *cmd; + struct backcmd *backcmd; + { + struct stackmark smark; + union node *argp; + struct arglist arglist; + struct arglist varlist; + char **argv; + int argc; + char **envp; + int varflag; + struct strlist *sp; + register char *p; + int mode; + int pip[2]; + struct cmdentry cmdentry; + struct job *jp; + struct jmploc jmploc; + struct jmploc *volatile savehandler; + char *volatile savecmdname; + volatile struct shparam saveparam; + struct localvar *volatile savelocalvars; + volatile int e; + char *lastarg; + + /* First expand the arguments. */ + TRACE(("evalcommand(0x%x, %d) called\n", (int)cmd, flags)); + setstackmark(&smark); + arglist.lastp = &arglist.list; + varlist.lastp = &varlist.list; + varflag = 1; + for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { + p = argp->narg.text; + if (varflag && is_name(*p)) { + do { + p++; + } while (is_in_name(*p)); + if (*p == '=') { + expandarg(argp, &varlist, 0); + continue; + } + } + expandarg(argp, &arglist, 1); + varflag = 0; + } + *arglist.lastp = NULL; + *varlist.lastp = NULL; + expredir(cmd->ncmd.redirect); + argc = 0; + for (sp = arglist.list ; sp ; sp = sp->next) + argc++; + argv = stalloc(sizeof (char *) * (argc + 1)); + for (sp = arglist.list ; sp ; sp = sp->next) + *argv++ = sp->text; + *argv = NULL; + lastarg = NULL; + if (iflag && funcnest == 0 && argc > 0) + lastarg = argv[-1]; + argv -= argc; + + /* Print the command if xflag is set. */ + if (xflag) { + outc('+', &errout); + for (sp = varlist.list ; sp ; sp = sp->next) { + outc(' ', &errout); + out2str(sp->text); + } + for (sp = arglist.list ; sp ; sp = sp->next) { + outc(' ', &errout); + out2str(sp->text); + } + outc('\n', &errout); + flushout(&errout); + } + + /* Now locate the command. */ + if (argc == 0) { + cmdentry.cmdtype = CMDBUILTIN; + cmdentry.u.index = BLTINCMD; + } else { + find_command(argv[0], &cmdentry, 1); + if (cmdentry.cmdtype == CMDUNKNOWN) { /* command not found */ + exitstatus = 2; + flushout(&errout); + return; + } + /* implement the bltin builtin here */ + if (cmdentry.cmdtype == CMDBUILTIN && cmdentry.u.index == BLTINCMD) { + for (;;) { + argv++; + if (--argc == 0) + break; + if ((cmdentry.u.index = find_builtin(*argv)) < 0) { + outfmt(&errout, "%s: not found\n", *argv); + exitstatus = 2; + flushout(&errout); + return; + } + if (cmdentry.u.index != BLTINCMD) + break; + } + } + } + + /* Fork off a child process if necessary. */ + if (cmd->ncmd.backgnd + || cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0 + || (flags & EV_BACKCMD) != 0 + && (cmdentry.cmdtype != CMDBUILTIN + || cmdentry.u.index == DOTCMD + || cmdentry.u.index == EVALCMD)) { + jp = makejob(cmd, 1); + mode = cmd->ncmd.backgnd; + if (flags & EV_BACKCMD) { + mode = FORK_NOJOB; + if (pipe(pip) < 0) + error("Pipe call failed"); + } + if (forkshell(jp, cmd, mode) != 0) + goto parent; /* at end of routine */ + if (flags & EV_BACKCMD) { + FORCEINTON; + close(pip[0]); + if (pip[1] != 1) { + close(1); + copyfd(pip[1], 1); + close(pip[1]); + } + } + flags |= EV_EXIT; + } + + /* This is the child process if a fork occurred. */ + /* Execute the command. */ + if (cmdentry.cmdtype == CMDFUNCTION) { + trputs("Shell function: "); trargs(argv); + redirect(cmd->ncmd.redirect, REDIR_PUSH); + saveparam = shellparam; + shellparam.malloc = 0; + shellparam.nparam = argc - 1; + shellparam.p = argv + 1; + shellparam.optnext = NULL; + INTOFF; + savelocalvars = localvars; + localvars = NULL; + INTON; + if (setjmp(jmploc.loc)) { + if (exception == EXSHELLPROC) + freeparam((struct shparam *)&saveparam); + else { + freeparam(&shellparam); + shellparam = saveparam; + } + poplocalvars(); + localvars = savelocalvars; + handler = savehandler; + longjmp(handler->loc, 1); + } + savehandler = handler; + handler = &jmploc; + for (sp = varlist.list ; sp ; sp = sp->next) + mklocal(sp->text); + funcnest++; + evaltree(cmdentry.u.func, 0); + funcnest--; + INTOFF; + poplocalvars(); + localvars = savelocalvars; + freeparam(&shellparam); + shellparam = saveparam; + handler = savehandler; + popredir(); + INTON; + if (evalskip == SKIPFUNC) { + evalskip = 0; + skipcount = 0; + } + if (flags & EV_EXIT) + exitshell(exitstatus); + } else if (cmdentry.cmdtype == CMDBUILTIN) { + trputs("builtin command: "); trargs(argv); + mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH; + if (flags == EV_BACKCMD) { + memout.nleft = 0; + memout.nextc = memout.buf; + memout.bufsize = 64; + mode |= REDIR_BACKQ; + } + redirect(cmd->ncmd.redirect, mode); + savecmdname = commandname; + cmdenviron = varlist.list; + e = -1; + if (setjmp(jmploc.loc)) { + e = exception; + exitstatus = (e == EXINT)? SIGINT+128 : 2; + goto cmddone; + } + savehandler = handler; + handler = &jmploc; + commandname = argv[0]; + argptr = argv + 1; + optptr = NULL; /* initialize nextopt */ + exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv); + flushall(); +cmddone: + out1 = &output; + out2 = &errout; + freestdout(); + if (e != EXSHELLPROC) { + commandname = savecmdname; + if (flags & EV_EXIT) { + exitshell(exitstatus); + } + } + handler = savehandler; + if (e != -1) { + if (e != EXERROR || cmdentry.u.index == BLTINCMD + || cmdentry.u.index == DOTCMD + || cmdentry.u.index == EVALCMD + || cmdentry.u.index == EXECCMD) + exraise(e); + FORCEINTON; + } + if (cmdentry.u.index != EXECCMD) + popredir(); + if (flags == EV_BACKCMD) { + backcmd->buf = memout.buf; + backcmd->nleft = memout.nextc - memout.buf; + memout.buf = NULL; + } + } else { + trputs("normal command: "); trargs(argv); + clearredir(); + redirect(cmd->ncmd.redirect, 0); + if (varlist.list) { + p = stalloc(strlen(pathval()) + 1); + scopy(pathval(), p); + } else { + p = pathval(); + } + for (sp = varlist.list ; sp ; sp = sp->next) + setvareq(sp->text, VEXPORT|VSTACK); + envp = environment(); + shellexec(argv, envp, p, cmdentry.u.index); + /*NOTREACHED*/ + } + goto out; + +parent: /* parent process gets here (if we forked) */ + if (mode == 0) { /* argument to fork */ + INTOFF; + exitstatus = waitforjob(jp); + INTON; + } else if (mode == 2) { + backcmd->fd = pip[0]; + close(pip[1]); + backcmd->jp = jp; + } + +out: + if (lastarg) + setvar("_", lastarg, 0); + popstackmark(&smark); +} + + + +/* + * Search for a command. This is called before we fork so that the + * location of the command will be available in the parent as well as + * the child. The check for "goodname" is an overly conservative + * check that the name will not be subject to expansion. + */ + +STATIC void +prehash(n) + union node *n; + { + struct cmdentry entry; + + if (n->type == NCMD && goodname(n->ncmd.args->narg.text)) + find_command(n->ncmd.args->narg.text, &entry, 0); +} + + + +/* + * Builtin commands. Builtin commands whose functions are closely + * tied to evaluation are implemented here. + */ + +/* + * No command given, or a bltin command with no arguments. Set the + * specified variables. + */ + +bltincmd(argc, argv) char **argv; { + listsetvar(cmdenviron); + return exitstatus; +} + + +/* + * Handle break and continue commands. Break, continue, and return are + * all handled by setting the evalskip flag. The evaluation routines + * above all check this flag, and if it is set they start skipping + * commands rather than executing them. The variable skipcount is + * the number of loops to break/continue, or the number of function + * levels to return. (The latter is always 1.) It should probably + * be an error to break out of more loops than exist, but it isn't + * in the standard shell so we don't make it one here. + */ + +breakcmd(argc, argv) char **argv; { + int n; + + n = 1; + if (argc > 1) + n = number(argv[1]); + if (n > loopnest) + n = loopnest; + if (n > 0) { + evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK; + skipcount = n; + } + return 0; +} + + +/* + * The return command. + */ + +returncmd(argc, argv) char **argv; { + int ret; + + ret = exitstatus; + if (argc > 1) + ret = number(argv[1]); + if (funcnest) { + evalskip = SKIPFUNC; + skipcount = 1; + } + return ret; +} + +falsecmd(argc, argv) char **argv; { + return 1; +} + +truecmd(argc, argv) char **argv; { + return 0; +} + + +execcmd(argc, argv) char **argv; { + if (argc > 1) { + iflag = 0; /* exit on error */ + setinteractive(0); +#if JOBS + jflag = 0; + setjobctl(0); +#endif + shellexec(argv + 1, environment(), pathval(), 0); + + } + return 0; +} diff --git a/bin/sh/eval.h b/bin/sh/eval.h new file mode 100644 index 000000000000..abc7bea5809a --- /dev/null +++ b/bin/sh/eval.h @@ -0,0 +1,66 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * from: @(#)eval.h 5.2 (Berkeley) 4/12/91 + * eval.h,v 1.4 1993/08/01 18:58:31 mycroft Exp + */ + +extern char *commandname; /* currently executing command */ +extern int exitstatus; /* exit status of last command */ +extern struct strlist *cmdenviron; /* environment for builtin command */ + + +struct backcmd { /* result of evalbackcmd */ + int fd; /* file descriptor to read from */ + char *buf; /* buffer */ + int nleft; /* number of chars in buffer */ + struct job *jp; /* job structure for command */ +}; + + +#ifdef __STDC__ +void evalstring(char *); +union node; /* BLETCH for ansi C */ +void evaltree(union node *, int); +void evalbackcmd(union node *, struct backcmd *); +#else +void evalstring(); +void evaltree(); +void evalbackcmd(); +#endif + +/* in_function returns nonzero if we are currently evaluating a function */ +#define in_function() funcnest +extern int funcnest; diff --git a/bin/sh/exec.c b/bin/sh/exec.c new file mode 100644 index 000000000000..6f427c1fa07b --- /dev/null +++ b/bin/sh/exec.c @@ -0,0 +1,881 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)exec.c 5.2 (Berkeley) 3/13/91";*/ +static char rcsid[] = "exec.c,v 1.5 1993/08/01 18:58:17 mycroft Exp"; +#endif /* not lint */ + +/* + * When commands are first encountered, they are entered in a hash table. + * This ensures that a full path search will not have to be done for them + * on each invocation. + * + * We should investigate converting to a linear search, even though that + * would make the command name "hash" a misnomer. + */ + +#include "shell.h" +#include "main.h" +#include "nodes.h" +#include "parser.h" +#include "redir.h" +#include "eval.h" +#include "exec.h" +#include "builtins.h" +#include "var.h" +#include "options.h" +#include "input.h" +#include "output.h" +#include "syntax.h" +#include "memalloc.h" +#include "error.h" +#include "init.h" +#include "mystring.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#ifdef _POSIX_VERSION +#define _POSIX_SOURCE /* try to find NGROUPS_MAX */ +#include <limits.h> +#endif +#include <errno.h> +#ifdef BSD +#undef BSD /* temporary, already defined in <sys/param.h> */ +#include <sys/param.h> +#include <unistd.h> +#endif + + +#define CMDTABLESIZE 31 /* should be prime */ +#define ARB 1 /* actual size determined at run time */ + + + +struct tblentry { + struct tblentry *next; /* next entry in hash chain */ + union param param; /* definition of builtin function */ + short cmdtype; /* index identifying command */ + char rehash; /* if set, cd done since entry created */ + char cmdname[ARB]; /* name of command */ +}; + + +STATIC struct tblentry *cmdtable[CMDTABLESIZE]; +STATIC int builtinloc = -1; /* index in path of %builtin, or -1 */ + + +#ifdef __STDC__ +STATIC void tryexec(char *, char **, char **); +STATIC void execinterp(char **, char **); +STATIC void printentry(struct tblentry *); +STATIC int in_group(int gid); +STATIC void clearcmdentry(int); +STATIC struct tblentry *cmdlookup(char *, int); +STATIC void delete_cmd_entry(void); +#else +STATIC void tryexec(); +STATIC void execinterp(); +STATIC void printentry(); +STATIC int in_group(); +STATIC void clearcmdentry(); +STATIC struct tblentry *cmdlookup(); +STATIC void delete_cmd_entry(); +#endif + + + +/* + * Exec a program. Never returns. If you change this routine, you may + * have to change the find_command routine as well. + */ + +void +shellexec(argv, envp, path, index) + char **argv, **envp; + char *path; + { + char *cmdname; + int e; + + if (strchr(argv[0], '/') != NULL) { + tryexec(argv[0], argv, envp); + e = errno; + } else { + e = ENOENT; + while ((cmdname = padvance(&path, argv[0])) != NULL) { + if (--index < 0 && pathopt == NULL) { + tryexec(cmdname, argv, envp); + if (errno != ENOENT && errno != ENOTDIR) + e = errno; + } + stunalloc(cmdname); + } + } + error2(argv[0], errmsg(e, E_EXEC)); +} + + +STATIC void +tryexec(cmd, argv, envp) + char *cmd; + char **argv; + char **envp; + { + int e; + char *p; + +#ifdef SYSV + do { + execve(cmd, argv, envp); + } while (errno == EINTR); +#else + execve(cmd, argv, envp); +#endif + e = errno; + if (e == ENOEXEC) { + initshellproc(); + setinputfile(cmd, 0); + commandname = arg0 = savestr(argv[0]); +#ifndef BSD + pgetc(); pungetc(); /* fill up input buffer */ + p = parsenextc; + if (parsenleft > 2 && p[0] == '#' && p[1] == '!') { + argv[0] = cmd; + execinterp(argv, envp); + } +#endif + setparam(argv + 1); + exraise(EXSHELLPROC); + /*NOTREACHED*/ + } + errno = e; +} + + +#ifndef BSD +/* + * Execute an interpreter introduced by "#!", for systems where this + * feature has not been built into the kernel. If the interpreter is + * the shell, return (effectively ignoring the "#!"). If the execution + * of the interpreter fails, exit. + * + * This code peeks inside the input buffer in order to avoid actually + * reading any input. It would benefit from a rewrite. + */ + +#define NEWARGS 5 + +STATIC void +execinterp(argv, envp) + char **argv, **envp; + { + int n; + char *inp; + char *outp; + char c; + char *p; + char **ap; + char *newargs[NEWARGS]; + int i; + char **ap2; + char **new; + + n = parsenleft - 2; + inp = parsenextc + 2; + ap = newargs; + for (;;) { + while (--n >= 0 && (*inp == ' ' || *inp == '\t')) + inp++; + if (n < 0) + goto bad; + if ((c = *inp++) == '\n') + break; + if (ap == &newargs[NEWARGS]) +bad: error("Bad #! line"); + STARTSTACKSTR(outp); + do { + STPUTC(c, outp); + } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n'); + STPUTC('\0', outp); + n++, inp--; + *ap++ = grabstackstr(outp); + } + if (ap == newargs + 1) { /* if no args, maybe no exec is needed */ + p = newargs[0]; + for (;;) { + if (equal(p, "sh") || equal(p, "ash")) { + return; + } + while (*p != '/') { + if (*p == '\0') + goto break2; + p++; + } + p++; + } +break2:; + } + i = (char *)ap - (char *)newargs; /* size in bytes */ + if (i == 0) + error("Bad #! line"); + for (ap2 = argv ; *ap2++ != NULL ; ); + new = ckmalloc(i + ((char *)ap2 - (char *)argv)); + ap = newargs, ap2 = new; + while ((i -= sizeof (char **)) >= 0) + *ap2++ = *ap++; + ap = argv; + while (*ap2++ = *ap++); + shellexec(new, envp, pathval(), 0); +} +#endif + + + +/* + * Do a path search. The variable path (passed by reference) should be + * set to the start of the path before the first call; padvance will update + * this value as it proceeds. Successive calls to padvance will return + * the possible path expansions in sequence. If an option (indicated by + * a percent sign) appears in the path entry then the global variable + * pathopt will be set to point to it; otherwise pathopt will be set to + * NULL. + */ + +char *pathopt; + +char * +padvance(path, name) + char **path; + char *name; + { + register char *p, *q; + char *start; + int len; + + if (*path == NULL) + return NULL; + start = *path; + for (p = start ; *p && *p != ':' && *p != '%' ; p++); + len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ + while (stackblocksize() < len) + growstackblock(); + q = stackblock(); + if (p != start) { + bcopy(start, q, p - start); + q += p - start; + *q++ = '/'; + } + strcpy(q, name); + pathopt = NULL; + if (*p == '%') { + pathopt = ++p; + while (*p && *p != ':') p++; + } + if (*p == ':') + *path = p + 1; + else + *path = NULL; + return stalloc(len); +} + + + +/*** Command hashing code ***/ + + +hashcmd(argc, argv) char **argv; { + struct tblentry **pp; + struct tblentry *cmdp; + int c; + int verbose; + struct cmdentry entry; + char *name; + + if (argc <= 1) { + for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { + for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { + printentry(cmdp); + } + } + return 0; + } + verbose = 0; + while ((c = nextopt("rv")) != '\0') { + if (c == 'r') { + clearcmdentry(0); + } else if (c == 'v') { + verbose++; + } + } + while ((name = *argptr) != NULL) { + if ((cmdp = cmdlookup(name, 0)) != NULL + && (cmdp->cmdtype == CMDNORMAL + || cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)) + delete_cmd_entry(); + find_command(name, &entry, 1); + if (verbose) { + if (entry.cmdtype != CMDUNKNOWN) { /* if no error msg */ + cmdp = cmdlookup(name, 0); + printentry(cmdp); + } + flushall(); + } + argptr++; + } + return 0; +} + + +STATIC void +printentry(cmdp) + struct tblentry *cmdp; + { + int index; + char *path; + char *name; + + if (cmdp->cmdtype == CMDNORMAL) { + index = cmdp->param.index; + path = pathval(); + do { + name = padvance(&path, cmdp->cmdname); + stunalloc(name); + } while (--index >= 0); + out1str(name); + } else if (cmdp->cmdtype == CMDBUILTIN) { + out1fmt("builtin %s", cmdp->cmdname); + } else if (cmdp->cmdtype == CMDFUNCTION) { + out1fmt("function %s", cmdp->cmdname); +#ifdef DEBUG + } else { + error("internal error: cmdtype %d", cmdp->cmdtype); +#endif + } + if (cmdp->rehash) + out1c('*'); + out1c('\n'); +} + + + +/* + * Resolve a command name. If you change this routine, you may have to + * change the shellexec routine as well. + */ + +void +find_command(name, entry, printerr) + char *name; + struct cmdentry *entry; + { + struct tblentry *cmdp; + int index; + int prev; + char *path; + char *fullname; + struct stat statb; + int e; + int i; + + /* If name contains a slash, don't use the hash table */ + if (strchr(name, '/') != NULL) { + entry->cmdtype = CMDNORMAL; + entry->u.index = 0; + return; + } + + /* If name is in the table, and not invalidated by cd, we're done */ + if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) + goto success; + + /* If %builtin not in path, check for builtin next */ + if (builtinloc < 0 && (i = find_builtin(name)) >= 0) { + INTOFF; + cmdp = cmdlookup(name, 1); + cmdp->cmdtype = CMDBUILTIN; + cmdp->param.index = i; + INTON; + goto success; + } + + /* We have to search path. */ + prev = -1; /* where to start */ + if (cmdp) { /* doing a rehash */ + if (cmdp->cmdtype == CMDBUILTIN) + prev = builtinloc; + else + prev = cmdp->param.index; + } + + path = pathval(); + e = ENOENT; + index = -1; +loop: + while ((fullname = padvance(&path, name)) != NULL) { + stunalloc(fullname); + index++; + if (pathopt) { + if (prefix("builtin", pathopt)) { + if ((i = find_builtin(name)) < 0) + goto loop; + INTOFF; + cmdp = cmdlookup(name, 1); + cmdp->cmdtype = CMDBUILTIN; + cmdp->param.index = i; + INTON; + goto success; + } else if (prefix("func", pathopt)) { + /* handled below */ + } else { + goto loop; /* ignore unimplemented options */ + } + } + /* if rehash, don't redo absolute path names */ + if (fullname[0] == '/' && index <= prev) { + if (index < prev) + goto loop; + TRACE(("searchexec \"%s\": no change\n", name)); + goto success; + } + while (stat(fullname, &statb) < 0) { +#ifdef SYSV + if (errno == EINTR) + continue; +#endif + if (errno != ENOENT && errno != ENOTDIR) + e = errno; + goto loop; + } + e = EACCES; /* if we fail, this will be the error */ + if ((statb.st_mode & S_IFMT) != S_IFREG) + goto loop; + if (pathopt) { /* this is a %func directory */ + stalloc(strlen(fullname) + 1); + readcmdfile(fullname); + if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION) + error("%s not defined in %s", name, fullname); + stunalloc(fullname); + goto success; + } + /* XXX this is almost as bogus as using access() */ + if (geteuid() == 0) { + if ((statb.st_mode & 0111) == 0) + goto loop; + } else if (statb.st_uid == geteuid()) { + if ((statb.st_mode & 0100) == 0) + goto loop; + } else if (in_group(statb.st_gid)) { + if ((statb.st_mode & 010) == 0) + goto loop; + } else { + if ((statb.st_mode & 01) == 0) { +#ifdef BSD + if ((statb.st_mode & 010) == 0) + goto loop; + /* Are you in this group too? */ + { + int group_list[NGROUPS]; + int ngroups, i; + + ngroups = getgroups(NGROUPS, group_list); + for (i = 0; i < ngroups; i++) + if (statb.st_gid == group_list[i]) + goto Found; + } +#endif + goto loop; + } + } +#ifdef BSD + Found: +#endif + TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname)); + INTOFF; + cmdp = cmdlookup(name, 1); + cmdp->cmdtype = CMDNORMAL; + cmdp->param.index = index; + INTON; + goto success; + } + + /* We failed. If there was an entry for this command, delete it */ + if (cmdp) + delete_cmd_entry(); + if (printerr) + outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC)); + entry->cmdtype = CMDUNKNOWN; + return; + +success: + cmdp->rehash = 0; + entry->cmdtype = cmdp->cmdtype; + entry->u = cmdp->param; +} + + + +STATIC int +in_group(gid) + int gid; + { +#ifdef _POSIX_VERSION +#ifdef __STDC__ + /* + * This bogus declararation is to force an error when + * someone fixes getgroups(). + */ + extern int getgroups(int ngroups, int *group_list); +#endif +#if NGROUPS_MAX != 0 + int group_list[NGROUPS_MAX]; +#else +#undef NGROUPS_MAX + size_t NGROUPS_MAX = sysconf(_SC_NGROUPS_MAX); + int *group_list = ckmalloc(NGROUPS_MAX); +#endif + int i; + int ngroups; + + ngroups = getgroups(NGROUPS_MAX, group_list); + for (i = 0; i < ngroups; i++) + if (gid == group_list[i]) { +#ifndef NGROUPS_MAX + ckfree(group_list); +#endif + return 1; + } +#ifndef NGROUPS_MAX + ckfree(group_list); +#endif + return 0; +#else /* ndef _POSIX_VERSION */ + return gid == getegid(); +#endif /* _POSIX_VERSION */ +} + + + +/* + * Search the table of builtin commands. + */ + +int +find_builtin(name) + char *name; + { + const register struct builtincmd *bp; + + for (bp = builtincmd ; bp->name ; bp++) { + if (*bp->name == *name && equal(bp->name, name)) + return bp->code; + } + return -1; +} + + + +/* + * Called when a cd is done. Marks all commands so the next time they + * are executed they will be rehashed. + */ + +void +hashcd() { + struct tblentry **pp; + struct tblentry *cmdp; + + for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { + for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { + if (cmdp->cmdtype == CMDNORMAL + || cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0) + cmdp->rehash = 1; + } + } +} + + + +/* + * Called before PATH is changed. The argument is the new value of PATH; + * pathval() still returns the old value at this point. Called with + * interrupts off. + */ + +void +changepath(newval) + char *newval; + { + char *old, *new; + int index; + int firstchange; + int bltin; + + old = pathval(); + new = newval; + firstchange = 9999; /* assume no change */ + index = 0; + bltin = -1; + for (;;) { + if (*old != *new) { + firstchange = index; + if (*old == '\0' && *new == ':' + || *old == ':' && *new == '\0') + firstchange++; + old = new; /* ignore subsequent differences */ + } + if (*new == '\0') + break; + if (*new == '%' && bltin < 0 && prefix("builtin", new + 1)) + bltin = index; + if (*new == ':') { + index++; + } + new++, old++; + } + if (builtinloc < 0 && bltin >= 0) + builtinloc = bltin; /* zap builtins */ + if (builtinloc >= 0 && bltin < 0) + firstchange = 0; + clearcmdentry(firstchange); + builtinloc = bltin; +} + + +/* + * Clear out command entries. The argument specifies the first entry in + * PATH which has changed. + */ + +STATIC void +clearcmdentry(firstchange) { + struct tblentry **tblp; + struct tblentry **pp; + struct tblentry *cmdp; + + INTOFF; + for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { + pp = tblp; + while ((cmdp = *pp) != NULL) { + if (cmdp->cmdtype == CMDNORMAL && cmdp->param.index >= firstchange + || cmdp->cmdtype == CMDBUILTIN && builtinloc >= firstchange) { + *pp = cmdp->next; + ckfree(cmdp); + } else { + pp = &cmdp->next; + } + } + } + INTON; +} + + +/* + * Delete all functions. + */ + +#ifdef mkinit +MKINIT void deletefuncs(); + +SHELLPROC { + deletefuncs(); +} +#endif + +void +deletefuncs() { + struct tblentry **tblp; + struct tblentry **pp; + struct tblentry *cmdp; + + INTOFF; + for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { + pp = tblp; + while ((cmdp = *pp) != NULL) { + if (cmdp->cmdtype == CMDFUNCTION) { + *pp = cmdp->next; + freefunc(cmdp->param.func); + ckfree(cmdp); + } else { + pp = &cmdp->next; + } + } + } + INTON; +} + + + +/* + * Locate a command in the command hash table. If "add" is nonzero, + * add the command to the table if it is not already present. The + * variable "lastcmdentry" is set to point to the address of the link + * pointing to the entry, so that delete_cmd_entry can delete the + * entry. + */ + +struct tblentry **lastcmdentry; + + +STATIC struct tblentry * +cmdlookup(name, add) + char *name; + { + int hashval; + register char *p; + struct tblentry *cmdp; + struct tblentry **pp; + + p = name; + hashval = *p << 4; + while (*p) + hashval += *p++; + hashval &= 0x7FFF; + pp = &cmdtable[hashval % CMDTABLESIZE]; + for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { + if (equal(cmdp->cmdname, name)) + break; + pp = &cmdp->next; + } + if (add && cmdp == NULL) { + INTOFF; + cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB + + strlen(name) + 1); + cmdp->next = NULL; + cmdp->cmdtype = CMDUNKNOWN; + cmdp->rehash = 0; + strcpy(cmdp->cmdname, name); + INTON; + } + lastcmdentry = pp; + return cmdp; +} + + +/* + * Delete the command entry returned on the last lookup. + */ + +STATIC void +delete_cmd_entry() { + struct tblentry *cmdp; + + INTOFF; + cmdp = *lastcmdentry; + *lastcmdentry = cmdp->next; + ckfree(cmdp); + INTON; +} + + + +#ifdef notdef +void +getcmdentry(name, entry) + char *name; + struct cmdentry *entry; + { + struct tblentry *cmdp = cmdlookup(name, 0); + + if (cmdp) { + entry->u = cmdp->param; + entry->cmdtype = cmdp->cmdtype; + } else { + entry->cmdtype = CMDUNKNOWN; + entry->u.index = 0; + } +} +#endif + + +/* + * Add a new command entry, replacing any existing command entry for + * the same name. + */ + +void +addcmdentry(name, entry) + char *name; + struct cmdentry *entry; + { + struct tblentry *cmdp; + + INTOFF; + cmdp = cmdlookup(name, 1); + if (cmdp->cmdtype == CMDFUNCTION) { + freefunc(cmdp->param.func); + } + cmdp->cmdtype = entry->cmdtype; + cmdp->param = entry->u; + INTON; +} + + +/* + * Define a shell function. + */ + +void +defun(name, func) + char *name; + union node *func; + { + struct cmdentry entry; + + INTOFF; + entry.cmdtype = CMDFUNCTION; + entry.u.func = copyfunc(func); + addcmdentry(name, &entry); + INTON; +} + + +/* + * Delete a function if it exists. + */ + +void +unsetfunc(name) + char *name; + { + struct tblentry *cmdp; + + if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) { + freefunc(cmdp->param.func); + delete_cmd_entry(); + } +} diff --git a/bin/sh/exec.h b/bin/sh/exec.h new file mode 100644 index 000000000000..c9a570317ba0 --- /dev/null +++ b/bin/sh/exec.h @@ -0,0 +1,76 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * from: @(#)exec.h 5.1 (Berkeley) 3/7/91 + * exec.h,v 1.4 1993/08/01 18:58:30 mycroft Exp + */ + +/* values of cmdtype */ +#define CMDUNKNOWN -1 /* no entry in table for command */ +#define CMDNORMAL 0 /* command is an executable program */ +#define CMDBUILTIN 1 /* command is a shell builtin */ +#define CMDFUNCTION 2 /* command is a shell function */ + + +struct cmdentry { + int cmdtype; + union param { + int index; + union node *func; + } u; +}; + + +extern char *pathopt; /* set by padvance */ + +#ifdef __STDC__ +void shellexec(char **, char **, char *, int); +char *padvance(char **, char *); +void find_command(char *, struct cmdentry *, int); +int find_builtin(char *); +void hashcd(void); +void changepath(char *); +void defun(char *, union node *); +void unsetfunc(char *); +#else +void shellexec(); +char *padvance(); +void find_command(); +int find_builtin(); +void hashcd(); +void changepath(); +void defun(); +void unsetfunc(); +#endif diff --git a/bin/sh/expand.c b/bin/sh/expand.c new file mode 100644 index 000000000000..1d5864d233bf --- /dev/null +++ b/bin/sh/expand.c @@ -0,0 +1,1111 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)expand.c 5.1 (Berkeley) 3/7/91";*/ +static char rcsid[] = "expand.c,v 1.5 1993/08/01 18:58:16 mycroft Exp"; +#endif /* not lint */ + +/* + * Routines to expand arguments to commands. We have to deal with + * backquotes, shell variables, and file metacharacters. + */ + +#include "shell.h" +#include "main.h" +#include "nodes.h" +#include "eval.h" +#include "expand.h" +#include "syntax.h" +#include "parser.h" +#include "jobs.h" +#include "options.h" +#include "var.h" +#include "input.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <dirent.h> + +/* + * Structure specifying which parts of the string should be searched + * for IFS characters. + */ + +struct ifsregion { + struct ifsregion *next; /* next region in list */ + int begoff; /* offset of start of region */ + int endoff; /* offset of end of region */ + int nulonly; /* search for nul bytes only */ +}; + + +char *expdest; /* output of current string */ +struct nodelist *argbackq; /* list of back quote expressions */ +struct ifsregion ifsfirst; /* first struct in list of ifs regions */ +struct ifsregion *ifslastp; /* last struct in list */ +struct arglist exparg; /* holds expanded arg list */ +#if UDIR +/* + * Set if the last argument processed had /u/logname expanded. This + * variable is read by the cd command. + */ +int didudir; +#endif + +#ifdef __STDC__ +STATIC void argstr(char *, int); +STATIC void expbackq(union node *, int, int); +STATIC char *evalvar(char *, int); +STATIC int varisset(int); +STATIC void varvalue(int, int, int); +STATIC void recordregion(int, int, int); +STATIC void ifsbreakup(char *, struct arglist *); +STATIC void expandmeta(struct strlist *); +STATIC void expmeta(char *, char *); +STATIC void addfname(char *); +STATIC struct strlist *expsort(struct strlist *); +STATIC struct strlist *msort(struct strlist *, int); +STATIC int pmatch(char *, char *); +#else +STATIC void argstr(); +STATIC void expbackq(); +STATIC char *evalvar(); +STATIC int varisset(); +STATIC void varvalue(); +STATIC void recordregion(); +STATIC void ifsbreakup(); +STATIC void expandmeta(); +STATIC void expmeta(); +STATIC void addfname(); +STATIC struct strlist *expsort(); +STATIC struct strlist *msort(); +STATIC int pmatch(); +#endif +#if UDIR +#ifdef __STDC__ +STATIC char *expudir(char *); +#else +STATIC char *expudir(); +#endif +#endif /* UDIR */ + + + +/* + * Expand shell variables and backquotes inside a here document. + */ + +void +expandhere(arg, fd) + union node *arg; /* the document */ + int fd; /* where to write the expanded version */ + { + herefd = fd; + expandarg(arg, (struct arglist *)NULL, 0); + xwrite(fd, stackblock(), expdest - stackblock()); +} + + +/* + * Perform variable substitution and command substitution on an argument, + * placing the resulting list of arguments in arglist. If full is true, + * perform splitting and file name expansion. When arglist is NULL, perform + * here document expansion. + */ + +void +expandarg(arg, arglist, full) + union node *arg; + struct arglist *arglist; + { + struct strlist *sp; + char *p; + +#if UDIR + didudir = 0; +#endif + argbackq = arg->narg.backquote; + STARTSTACKSTR(expdest); + ifsfirst.next = NULL; + ifslastp = NULL; + argstr(arg->narg.text, full); + if (arglist == NULL) + return; /* here document expanded */ + STPUTC('\0', expdest); + p = grabstackstr(expdest); + exparg.lastp = &exparg.list; + if (full) { + ifsbreakup(p, &exparg); + *exparg.lastp = NULL; + exparg.lastp = &exparg.list; + expandmeta(exparg.list); + } else { + sp = (struct strlist *)stalloc(sizeof (struct strlist)); + sp->text = p; + *exparg.lastp = sp; + exparg.lastp = &sp->next; + } + while (ifsfirst.next != NULL) { + struct ifsregion *ifsp; + INTOFF; + ifsp = ifsfirst.next->next; + ckfree(ifsfirst.next); + ifsfirst.next = ifsp; + INTON; + } + *exparg.lastp = NULL; + if (exparg.list) { + *arglist->lastp = exparg.list; + arglist->lastp = exparg.lastp; + } +} + + + +/* + * Perform variable and command substitution. If full is set, output CTLESC + * characters to allow for further processing. If full is not set, treat + * $@ like $* since no splitting will be performed. + */ + +STATIC void +argstr(p, full) + register char *p; + { + char c; + + for (;;) { + switch (c = *p++) { + case '\0': + case CTLENDVAR: + goto breakloop; + case CTLESC: + if (full) + STPUTC(c, expdest); + c = *p++; + STPUTC(c, expdest); + break; + case CTLVAR: + p = evalvar(p, full); + break; + case CTLBACKQ: + case CTLBACKQ|CTLQUOTE: + expbackq(argbackq->n, c & CTLQUOTE, full); + argbackq = argbackq->next; + break; + default: + STPUTC(c, expdest); + } + } +breakloop:; +} + + +/* + * Expand stuff in backwards quotes. + */ + +STATIC void +expbackq(cmd, quoted, full) + union node *cmd; + { + struct backcmd in; + int i; + char buf[128]; + char *p; + char *dest = expdest; + struct ifsregion saveifs, *savelastp; + struct nodelist *saveargbackq; + char lastc; + int startloc = dest - stackblock(); + char const *syntax = quoted? DQSYNTAX : BASESYNTAX; + int saveherefd; + + INTOFF; + saveifs = ifsfirst; + savelastp = ifslastp; + saveargbackq = argbackq; + saveherefd = herefd; + herefd = -1; + p = grabstackstr(dest); + evalbackcmd(cmd, &in); + ungrabstackstr(p, dest); + ifsfirst = saveifs; + ifslastp = savelastp; + argbackq = saveargbackq; + herefd = saveherefd; + + p = in.buf; + lastc = '\0'; + for (;;) { + if (--in.nleft < 0) { + if (in.fd < 0) + break; + while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR); + TRACE(("expbackq: read returns %d\n", i)); + if (i <= 0) + break; + p = buf; + in.nleft = i - 1; + } + lastc = *p++; + if (lastc != '\0') { + if (full && syntax[lastc] == CCTL) + STPUTC(CTLESC, dest); + STPUTC(lastc, dest); + } + } + if (lastc == '\n') { + STUNPUTC(dest); + } + if (in.fd >= 0) + close(in.fd); + if (in.buf) + ckfree(in.buf); + if (in.jp) + exitstatus = waitforjob(in.jp); + if (quoted == 0) + recordregion(startloc, dest - stackblock(), 0); + TRACE(("evalbackq: size=%d: \"%.*s\"\n", + (dest - stackblock()) - startloc, + (dest - stackblock()) - startloc, + stackblock() + startloc)); + expdest = dest; + INTON; +} + + + +/* + * Expand a variable, and return a pointer to the next character in the + * input string. + */ + +STATIC char * +evalvar(p, full) + char *p; + { + int subtype; + int flags; + char *var; + char *val; + int c; + int set; + int special; + int startloc; + + flags = *p++; + subtype = flags & VSTYPE; + var = p; + special = 0; + if (! is_name(*p)) + special = 1; + p = strchr(p, '=') + 1; +again: /* jump here after setting a variable with ${var=text} */ + if (special) { + set = varisset(*var); + val = NULL; + } else { + val = lookupvar(var); + if (val == NULL || (flags & VSNUL) && val[0] == '\0') { + val = NULL; + set = 0; + } else + set = 1; + } + startloc = expdest - stackblock(); + if (set && subtype != VSPLUS) { + /* insert the value of the variable */ + if (special) { + varvalue(*var, flags & VSQUOTE, full); + } else { + char const *syntax = (flags & VSQUOTE)? DQSYNTAX : BASESYNTAX; + + while (*val) { + if (full && syntax[*val] == CCTL) + STPUTC(CTLESC, expdest); + STPUTC(*val++, expdest); + } + } + } + if (subtype == VSPLUS) + set = ! set; + if (((flags & VSQUOTE) == 0 || (*var == '@' && shellparam.nparam != 1)) + && (set || subtype == VSNORMAL)) + recordregion(startloc, expdest - stackblock(), flags & VSQUOTE); + if (! set && subtype != VSNORMAL) { + if (subtype == VSPLUS || subtype == VSMINUS) { + argstr(p, full); + } else { + char *startp; + int saveherefd = herefd; + herefd = -1; + argstr(p, 0); + STACKSTRNUL(expdest); + herefd = saveherefd; + startp = stackblock() + startloc; + if (subtype == VSASSIGN) { + setvar(var, startp, 0); + STADJUST(startp - expdest, expdest); + flags &=~ VSNUL; + goto again; + } + /* subtype == VSQUESTION */ + if (*p != CTLENDVAR) { + outfmt(&errout, "%s\n", startp); + error((char *)NULL); + } + error("%.*s: parameter %snot set", p - var - 1, + var, (flags & VSNUL)? "null or " : nullstr); + } + } + if (subtype != VSNORMAL) { /* skip to end of alternative */ + int nesting = 1; + for (;;) { + if ((c = *p++) == CTLESC) + p++; + else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { + if (set) + argbackq = argbackq->next; + } else if (c == CTLVAR) { + if ((*p++ & VSTYPE) != VSNORMAL) + nesting++; + } else if (c == CTLENDVAR) { + if (--nesting == 0) + break; + } + } + } + return p; +} + + + +/* + * Test whether a specialized variable is set. + */ + +STATIC int +varisset(name) + char name; + { + char **ap; + + if (name == '!') { + if (backgndpid == -1) + return 0; + } else if (name == '@' || name == '*') { + if (*shellparam.p == NULL) + return 0; + } else if ((unsigned)(name -= '1') <= '9' - '1') { + ap = shellparam.p; + do { + if (*ap++ == NULL) + return 0; + } while (--name >= 0); + } + return 1; +} + + + +/* + * Add the value of a specialized variable to the stack string. + */ + +STATIC void +varvalue(name, quoted, allow_split) + char name; + { + int num; + char temp[32]; + char *p; + int i; + extern int exitstatus; + char sep; + char **ap; + char const *syntax; + + switch (name) { + case '$': + num = rootpid; + goto numvar; + case '?': + num = exitstatus; + goto numvar; + case '#': + num = shellparam.nparam; + goto numvar; + case '!': + num = backgndpid; +numvar: + p = temp + 31; + temp[31] = '\0'; + do { + *--p = num % 10 + '0'; + } while ((num /= 10) != 0); + while (*p) + STPUTC(*p++, expdest); + break; + case '-': + for (i = 0 ; optchar[i] ; i++) { + if (optval[i]) + STPUTC(optchar[i], expdest); + } + break; + case '@': + if (allow_split) { + sep = '\0'; + goto allargs; + } + /* fall through */ + case '*': + sep = ' '; +allargs: + /* Only emit CTLESC if we will do further processing, + i.e. if allow_split is set. */ + syntax = quoted && allow_split ? DQSYNTAX : BASESYNTAX; + for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { + /* should insert CTLESC characters */ + while (*p) { + if (syntax[*p] == CCTL) + STPUTC(CTLESC, expdest); + STPUTC(*p++, expdest); + } + if (*ap) + STPUTC(sep, expdest); + } + break; + case '0': + p = arg0; +string: + /* Only emit CTLESC if we will do further processing, + i.e. if allow_split is set. */ + syntax = quoted && allow_split ? DQSYNTAX : BASESYNTAX; + while (*p) { + if (syntax[*p] == CCTL) + STPUTC(CTLESC, expdest); + STPUTC(*p++, expdest); + } + break; + default: + if ((unsigned)(name -= '1') <= '9' - '1') { + p = shellparam.p[name]; + goto string; + } + break; + } +} + + + +/* + * Record the the fact that we have to scan this region of the + * string for IFS characters. + */ + +STATIC void +recordregion(start, end, nulonly) { + register struct ifsregion *ifsp; + + if (ifslastp == NULL) { + ifsp = &ifsfirst; + } else { + ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); + ifslastp->next = ifsp; + } + ifslastp = ifsp; + ifslastp->next = NULL; + ifslastp->begoff = start; + ifslastp->endoff = end; + ifslastp->nulonly = nulonly; +} + + + +/* + * Break the argument string into pieces based upon IFS and add the + * strings to the argument list. The regions of the string to be + * searched for IFS characters have been stored by recordregion. + */ + +STATIC void +ifsbreakup(string, arglist) + char *string; + struct arglist *arglist; + { + struct ifsregion *ifsp; + struct strlist *sp; + char *start; + register char *p; + char *q; + char *ifs; + + start = string; + if (ifslastp != NULL) { + ifsp = &ifsfirst; + do { + p = string + ifsp->begoff; + ifs = ifsp->nulonly? nullstr : ifsval(); + while (p < string + ifsp->endoff) { + q = p; + if (*p == CTLESC) + p++; + if (strchr(ifs, *p++)) { + if (q > start || *ifs != ' ') { + *q = '\0'; + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = start; + *arglist->lastp = sp; + arglist->lastp = &sp->next; + } + if (*ifs == ' ') { + for (;;) { + if (p >= string + ifsp->endoff) + break; + q = p; + if (*p == CTLESC) + p++; + if (strchr(ifs, *p++) == NULL) { + p = q; + break; + } + } + } + start = p; + } + } + } while ((ifsp = ifsp->next) != NULL); + if (*start || (*ifs != ' ' && start > string)) { + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = start; + *arglist->lastp = sp; + arglist->lastp = &sp->next; + } + } else { + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = start; + *arglist->lastp = sp; + arglist->lastp = &sp->next; + } +} + + + +/* + * Expand shell metacharacters. At this point, the only control characters + * should be escapes. The results are stored in the list exparg. + */ + +char *expdir; + + +STATIC void +expandmeta(str) + struct strlist *str; + { + char *p; + struct strlist **savelastp; + struct strlist *sp; + char c; + + while (str) { + if (fflag) + goto nometa; + p = str->text; +#if UDIR + if (p[0] == '/' && p[1] == 'u' && p[2] == '/') + str->text = p = expudir(p); +#endif + for (;;) { /* fast check for meta chars */ + if ((c = *p++) == '\0') + goto nometa; + if (c == '*' || c == '?' || c == '[' || c == '!') + break; + } + savelastp = exparg.lastp; + INTOFF; + if (expdir == NULL) + expdir = ckmalloc(1024); /* I hope this is big enough */ + expmeta(expdir, str->text); + ckfree(expdir); + expdir = NULL; + INTON; + if (exparg.lastp == savelastp) { + if (! zflag) { +nometa: + *exparg.lastp = str; + rmescapes(str->text); + exparg.lastp = &str->next; + } + } else { + *exparg.lastp = NULL; + *savelastp = sp = expsort(*savelastp); + while (sp->next != NULL) + sp = sp->next; + exparg.lastp = &sp->next; + } + str = str->next; + } +} + + +#if UDIR +/* + * Expand /u/username into the home directory for the specified user. + * We could use the getpw stuff here, but then we would have to load + * in stdio and who knows what else. + */ + +#define MAXLOGNAME 32 +#define MAXPWLINE 128 + +char *pfgets(); + + +STATIC char * +expudir(path) + char *path; + { + register char *p, *q, *r; + char name[MAXLOGNAME]; + char line[MAXPWLINE]; + int i; + + r = path; /* result on failure */ + p = r + 3; /* the 3 skips "/u/" */ + q = name; + while (*p && *p != '/') { + if (q >= name + MAXLOGNAME - 1) + return r; /* fail, name too long */ + *q++ = *p++; + } + *q = '\0'; + setinputfile("/etc/passwd", 1); + q = line + strlen(name); + while (pfgets(line, MAXPWLINE) != NULL) { + if (line[0] == name[0] && prefix(name, line) && *q == ':') { + /* skip to start of home directory */ + i = 4; + do { + while (*++q && *q != ':'); + } while (--i > 0); + if (*q == '\0') + break; /* fail, corrupted /etc/passwd */ + q++; + for (r = q ; *r && *r != '\n' && *r != ':' ; r++); + *r = '\0'; /* nul terminate home directory */ + i = r - q; /* i = strlen(q) */ + r = stalloc(i + strlen(p) + 1); + scopy(q, r); + scopy(p, r + i); + TRACE(("expudir converts %s to %s\n", path, r)); + didudir = 1; + path = r; /* succeed */ + break; + } + } + popfile(); + return r; +} +#endif + + +/* + * Do metacharacter (i.e. *, ?, [...]) expansion. + */ + +STATIC void +expmeta(enddir, name) + char *enddir; + char *name; + { + register char *p; + char *q; + char *start; + char *endname; + int metaflag; + struct stat statb; + DIR *dirp; + struct dirent *dp; + int atend; + int matchdot; + + metaflag = 0; + start = name; + for (p = name ; ; p++) { + if (*p == '*' || *p == '?') + metaflag = 1; + else if (*p == '[') { + q = p + 1; + if (*q == '!') + q++; + for (;;) { + if (*q == CTLESC) + q++; + if (*q == '/' || *q == '\0') + break; + if (*++q == ']') { + metaflag = 1; + break; + } + } + } else if (*p == '!' && p[1] == '!' && (p == name || p[-1] == '/')) { + metaflag = 1; + } else if (*p == '\0') + break; + else if (*p == CTLESC) + p++; + if (*p == '/') { + if (metaflag) + break; + start = p + 1; + } + } + if (metaflag == 0) { /* we've reached the end of the file name */ + if (enddir != expdir) + metaflag++; + for (p = name ; ; p++) { + if (*p == CTLESC) + p++; + *enddir++ = *p; + if (*p == '\0') + break; + } + if (metaflag == 0 || stat(expdir, &statb) >= 0) + addfname(expdir); + return; + } + endname = p; + if (start != name) { + p = name; + while (p < start) { + if (*p == CTLESC) + p++; + *enddir++ = *p++; + } + } + if (enddir == expdir) { + p = "."; + } else if (enddir == expdir + 1 && *expdir == '/') { + p = "/"; + } else { + p = expdir; + enddir[-1] = '\0'; + } + if ((dirp = opendir(p)) == NULL) + return; + if (enddir != expdir) + enddir[-1] = '/'; + if (*endname == 0) { + atend = 1; + } else { + atend = 0; + *endname++ = '\0'; + } + matchdot = 0; + if (start[0] == '.' || start[0] == CTLESC && start[1] == '.') + matchdot++; + while (! int_pending() && (dp = readdir(dirp)) != NULL) { + if (dp->d_name[0] == '.' && ! matchdot) + continue; + if (patmatch(start, dp->d_name)) { + if (atend) { + scopy(dp->d_name, enddir); + addfname(expdir); + } else { + char *q; + for (p = enddir, q = dp->d_name ; *p++ = *q++ ;); + p[-1] = '/'; + expmeta(p, endname); + } + } + } + closedir(dirp); + if (! atend) + endname[-1] = '/'; +} + + +/* + * Add a file name to the list. + */ + +STATIC void +addfname(name) + char *name; + { + char *p; + struct strlist *sp; + + p = stalloc(strlen(name) + 1); + scopy(name, p); + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = p; + *exparg.lastp = sp; + exparg.lastp = &sp->next; +} + + +/* + * Sort the results of file name expansion. It calculates the number of + * strings to sort and then calls msort (short for merge sort) to do the + * work. + */ + +STATIC struct strlist * +expsort(str) + struct strlist *str; + { + int len; + struct strlist *sp; + + len = 0; + for (sp = str ; sp ; sp = sp->next) + len++; + return msort(str, len); +} + + +STATIC struct strlist * +msort(list, len) + struct strlist *list; + { + struct strlist *p, *q; + struct strlist **lpp; + int half; + int n; + + if (len <= 1) + return list; + half = len >> 1; + p = list; + for (n = half ; --n >= 0 ; ) { + q = p; + p = p->next; + } + q->next = NULL; /* terminate first half of list */ + q = msort(list, half); /* sort first half of list */ + p = msort(p, len - half); /* sort second half */ + lpp = &list; + for (;;) { + if (strcmp(p->text, q->text) < 0) { + *lpp = p; + lpp = &p->next; + if ((p = *lpp) == NULL) { + *lpp = q; + break; + } + } else { + *lpp = q; + lpp = &q->next; + if ((q = *lpp) == NULL) { + *lpp = p; + break; + } + } + } + return list; +} + + + +/* + * Returns true if the pattern matches the string. + */ + +int +patmatch(pattern, string) + char *pattern; + char *string; + { + if (pattern[0] == '!' && pattern[1] == '!') + return 1 - pmatch(pattern + 2, string); + else + return pmatch(pattern, string); +} + + +STATIC int +pmatch(pattern, string) + char *pattern; + char *string; + { + register char *p, *q; + register char c; + + p = pattern; + q = string; + for (;;) { + switch (c = *p++) { + case '\0': + goto breakloop; + case CTLESC: + if (*q++ != *p++) + return 0; + break; + case '?': + if (*q++ == '\0') + return 0; + break; + case '*': + c = *p; + if (c != CTLESC && c != '?' && c != '*' && c != '[') { + while (*q != c) { + if (*q == '\0') + return 0; + q++; + } + } + do { + if (pmatch(p, q)) + return 1; + } while (*q++ != '\0'); + return 0; + case '[': { + char *endp; + int invert, found; + char chr; + + endp = p; + if (*endp == '!') + endp++; + for (;;) { + if (*endp == '\0') + goto dft; /* no matching ] */ + if (*endp == CTLESC) + endp++; + if (*++endp == ']') + break; + } + invert = 0; + if (*p == '!') { + invert++; + p++; + } + found = 0; + chr = *q++; + c = *p++; + do { + if (c == CTLESC) + c = *p++; + if (*p == '-' && p[1] != ']') { + p++; + if (*p == CTLESC) + p++; + if (chr >= c && chr <= *p) + found = 1; + p++; + } else { + if (chr == c) + found = 1; + } + } while ((c = *p++) != ']'); + if (found == invert) + return 0; + break; + } +dft: default: + if (*q++ != c) + return 0; + break; + } + } +breakloop: + if (*q != '\0') + return 0; + return 1; +} + + + +/* + * Remove any CTLESC characters from a string. + */ + +void +rmescapes(str) + char *str; + { + register char *p, *q; + + p = str; + while (*p != CTLESC) { + if (*p++ == '\0') + return; + } + q = p; + while (*p) { + if (*p == CTLESC) + p++; + *q++ = *p++; + } + *q = '\0'; +} + + + +/* + * See if a pattern matches in a case statement. + */ + +int +casematch(pattern, val) + union node *pattern; + char *val; + { + struct stackmark smark; + int result; + char *p; + + setstackmark(&smark); + argbackq = pattern->narg.backquote; + STARTSTACKSTR(expdest); + ifslastp = NULL; + /* Preserve any CTLESC characters inserted previously, so that + we won't expand reg exps which are inside strings. */ + argstr(pattern->narg.text, 1); + STPUTC('\0', expdest); + p = grabstackstr(expdest); + result = patmatch(p, val); + popstackmark(&smark); + return result; +} diff --git a/bin/sh/expand.h b/bin/sh/expand.h new file mode 100644 index 000000000000..9d8eacf9e2c5 --- /dev/null +++ b/bin/sh/expand.h @@ -0,0 +1,64 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * from: @(#)expand.h 5.1 (Berkeley) 3/7/91 + * expand.h,v 1.4 1993/08/01 18:58:29 mycroft Exp + */ + +struct strlist { + struct strlist *next; + char *text; +}; + + +struct arglist { + struct strlist *list; + struct strlist **lastp; +}; + +#ifdef __STDC__ +union node; +void expandarg(union node *, struct arglist *, int); +void expandhere(union node *, int); +int patmatch(char *, char *); +void rmescapes(char *); +int casematch(union node *, char *); +#else +void expandarg(); +void expandhere(); +int patmatch(); +void rmescapes(); +int casematch(); +#endif diff --git a/bin/sh/funcs/cmv b/bin/sh/funcs/cmv new file mode 100644 index 000000000000..ecd53084a126 --- /dev/null +++ b/bin/sh/funcs/cmv @@ -0,0 +1,51 @@ +# Copyright (c) 1991 The Regents of the University of California. +# All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. 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. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. +# +# @(#)cmv 5.1 (Berkeley) 3/7/91 +# +# /b/source/CVS/src/bin/sh/funcs/cmv,v 1.3 1993/03/23 00:29:45 cgd Exp + +# Conditional move--don't replace an existing file. + +cmv() { + if test $# != 2 + then echo "cmv: arg count" + return 2 + fi + if test -f "$2" -o -w "$2" + then echo "$2 exists" + return 2 + fi + /bin/mv "$1" "$2" +} diff --git a/bin/sh/funcs/dirs b/bin/sh/funcs/dirs new file mode 100644 index 000000000000..e8a85fca74d4 --- /dev/null +++ b/bin/sh/funcs/dirs @@ -0,0 +1,75 @@ +# Copyright (c) 1991 The Regents of the University of California. +# All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. 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. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. +# +# @(#)dirs 5.1 (Berkeley) 3/7/91 +# +# /b/source/CVS/src/bin/sh/funcs/dirs,v 1.3 1993/03/23 00:29:47 cgd Exp + +# pushd, popd, and dirs --- written by Chris Bertin +# Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris +# as modified by Patrick Elam of GTRI and Kenneth Almquist at UW + +pushd () { + SAVE=`pwd` + if [ "$1" = "" ] + then if [ "$DSTACK" = "" ] + then echo "pushd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 || return + shift 1 + DSTACK="$*" + else cd $1 > /dev/null || return + fi + DSTACK="$SAVE $DSTACK" + dirs +} + +popd () { + if [ "$DSTACK" = "" ] + then echo "popd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 + shift + DSTACK=$* + dirs +} + +dirs () { + echo "`pwd` $DSTACK" + return 0 +} diff --git a/bin/sh/funcs/kill b/bin/sh/funcs/kill new file mode 100644 index 000000000000..c58c3408bf97 --- /dev/null +++ b/bin/sh/funcs/kill @@ -0,0 +1,51 @@ +# Copyright (c) 1991 The Regents of the University of California. +# All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. 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. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. +# +# @(#)kill 5.1 (Berkeley) 3/7/91 +# +# /b/source/CVS/src/bin/sh/funcs/kill,v 1.3 1993/03/23 00:29:49 cgd Exp + +# Convert job names to process ids and then run /bin/kill. + +kill() { + local args x + args= + for x in "$@" + do case $x in + %*) x=`jobid "$x"` ;; + esac + args="$args $x" + done + /bin/kill $args +} diff --git a/bin/sh/funcs/login b/bin/sh/funcs/login new file mode 100644 index 000000000000..a07badc75acf --- /dev/null +++ b/bin/sh/funcs/login @@ -0,0 +1,40 @@ +# Copyright (c) 1991 The Regents of the University of California. +# All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. 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. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. +# +# @(#)login 5.1 (Berkeley) 3/7/91 +# +# /b/source/CVS/src/bin/sh/funcs/login,v 1.3 1993/03/23 00:29:51 cgd Exp + +# replaces the login builtin in the BSD shell +login () exec login "$@" diff --git a/bin/sh/funcs/newgrp b/bin/sh/funcs/newgrp new file mode 100644 index 000000000000..d7c2b92d7229 --- /dev/null +++ b/bin/sh/funcs/newgrp @@ -0,0 +1,39 @@ +# Copyright (c) 1991 The Regents of the University of California. +# All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. 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. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. +# +# @(#)newgrp 5.1 (Berkeley) 3/7/91 +# +# /b/source/CVS/src/bin/sh/funcs/newgrp,v 1.3 1993/03/23 00:29:53 cgd Exp + +newgrp() exec newgrp "$@" diff --git a/bin/sh/funcs/popd b/bin/sh/funcs/popd new file mode 100644 index 000000000000..2ce4346ba536 --- /dev/null +++ b/bin/sh/funcs/popd @@ -0,0 +1,75 @@ +# Copyright (c) 1991 The Regents of the University of California. +# All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. 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. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. +# +# @(#)popd 5.1 (Berkeley) 3/7/91 +# +# /b/source/CVS/src/bin/sh/funcs/popd,v 1.3 1993/03/23 00:29:55 cgd Exp + +# pushd, popd, and dirs --- written by Chris Bertin +# Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris +# as modified by Patrick Elam of GTRI and Kenneth Almquist at UW + +pushd () { + SAVE=`pwd` + if [ "$1" = "" ] + then if [ "$DSTACK" = "" ] + then echo "pushd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 || return + shift 1 + DSTACK="$*" + else cd $1 > /dev/null || return + fi + DSTACK="$SAVE $DSTACK" + dirs +} + +popd () { + if [ "$DSTACK" = "" ] + then echo "popd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 + shift + DSTACK=$* + dirs +} + +dirs () { + echo "`pwd` $DSTACK" + return 0 +} diff --git a/bin/sh/funcs/pushd b/bin/sh/funcs/pushd new file mode 100644 index 000000000000..9b46b319d420 --- /dev/null +++ b/bin/sh/funcs/pushd @@ -0,0 +1,75 @@ +# Copyright (c) 1991 The Regents of the University of California. +# All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. 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. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. +# +# @(#)pushd 5.1 (Berkeley) 3/7/91 +# +# /b/source/CVS/src/bin/sh/funcs/pushd,v 1.3 1993/03/23 00:29:58 cgd Exp + +# pushd, popd, and dirs --- written by Chris Bertin +# Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris +# as modified by Patrick Elam of GTRI and Kenneth Almquist at UW + +pushd () { + SAVE=`pwd` + if [ "$1" = "" ] + then if [ "$DSTACK" = "" ] + then echo "pushd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 || return + shift 1 + DSTACK="$*" + else cd $1 > /dev/null || return + fi + DSTACK="$SAVE $DSTACK" + dirs +} + +popd () { + if [ "$DSTACK" = "" ] + then echo "popd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 + shift + DSTACK=$* + dirs +} + +dirs () { + echo "`pwd` $DSTACK" + return 0 +} diff --git a/bin/sh/funcs/suspend b/bin/sh/funcs/suspend new file mode 100644 index 000000000000..61b6562e3b0a --- /dev/null +++ b/bin/sh/funcs/suspend @@ -0,0 +1,43 @@ +# Copyright (c) 1991 The Regents of the University of California. +# All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. 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. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. +# +# @(#)suspend 5.1 (Berkeley) 3/7/91 +# +# /b/source/CVS/src/bin/sh/funcs/suspend,v 1.3 1993/03/23 00:30:00 cgd Exp + +suspend() { + local - + set +j + kill -TSTP 0 +} diff --git a/bin/sh/init.h b/bin/sh/init.h new file mode 100644 index 000000000000..e520a106714d --- /dev/null +++ b/bin/sh/init.h @@ -0,0 +1,48 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * from: @(#)init.h 5.1 (Berkeley) 3/7/91 + * init.h,v 1.4 1993/08/01 18:58:28 mycroft Exp + */ + +#ifdef __STDC__ +void init(void); +void reset(void); +void initshellproc(void); +#else +void init(); +void reset(); +void initshellproc(); +#endif diff --git a/bin/sh/input.c b/bin/sh/input.c new file mode 100644 index 000000000000..0aea0209dd1c --- /dev/null +++ b/bin/sh/input.c @@ -0,0 +1,382 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)input.c 5.4 (Berkeley) 7/1/91";*/ +static char rcsid[] = "input.c,v 1.4 1993/08/01 18:58:15 mycroft Exp"; +#endif /* not lint */ + +/* + * This file implements the input routines used by the parser. + */ + +#include <stdio.h> /* defines BUFSIZ */ +#include "shell.h" +#include <fcntl.h> +#include <errno.h> +#include "syntax.h" +#include "input.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" + +#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */ + + +/* + * The parsefile structure pointed to by the global variable parsefile + * contains information about the current file being read. + */ + +MKINIT +struct parsefile { + int linno; /* current line */ + int fd; /* file descriptor (or -1 if string) */ + int nleft; /* number of chars left in buffer */ + char *nextc; /* next char in buffer */ + struct parsefile *prev; /* preceding file on stack */ + char *buf; /* input buffer */ +}; + + +int plinno = 1; /* input line number */ +MKINIT int parsenleft; /* copy of parsefile->nleft */ +char *parsenextc; /* copy of parsefile->nextc */ +MKINIT struct parsefile basepf; /* top level input file */ +char basebuf[BUFSIZ]; /* buffer for top level input file */ +struct parsefile *parsefile = &basepf; /* current input file */ +char *pushedstring; /* copy of parsenextc when text pushed back */ +int pushednleft; /* copy of parsenleft when text pushed back */ + +#ifdef __STDC__ +STATIC void pushfile(void); +#else +STATIC void pushfile(); +#endif + + + +#ifdef mkinit +INCLUDE "input.h" +INCLUDE "error.h" + +INIT { + extern char basebuf[]; + + basepf.nextc = basepf.buf = basebuf; +} + +RESET { + if (exception != EXSHELLPROC) + parsenleft = 0; /* clear input buffer */ + popallfiles(); +} + +SHELLPROC { + popallfiles(); +} +#endif + + +/* + * Read a line from the script. + */ + +char * +pfgets(line, len) + char *line; + { + register char *p = line; + int nleft = len; + int c; + + while (--nleft > 0) { + c = pgetc_macro(); + if (c == PEOF) { + if (p == line) + return NULL; + break; + } + *p++ = c; + if (c == '\n') + break; + } + *p = '\0'; + return line; +} + + + +/* + * Read a character from the script, returning PEOF on end of file. + * Nul characters in the input are silently discarded. + */ + +int +pgetc() { + return pgetc_macro(); +} + + +/* + * Refill the input buffer and return the next input character: + * + * 1) If a string was pushed back on the input, switch back to the regular + * buffer. + * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading + * from a string so we can't refill the buffer, return EOF. + * 3) Call read to read in the characters. + * 4) Delete all nul characters from the buffer. + */ + +int +preadbuffer() { + register char *p, *q; + register int i; + + if (pushedstring) { + parsenextc = pushedstring; + pushedstring = NULL; + parsenleft = pushednleft; + if (--parsenleft >= 0) + return *parsenextc++; + } + if (parsenleft == EOF_NLEFT || parsefile->buf == NULL) + return PEOF; + flushout(&output); + flushout(&errout); +retry: + p = parsenextc = parsefile->buf; + i = read(parsefile->fd, p, BUFSIZ); + if (i <= 0) { + if (i < 0) { + if (errno == EINTR) + goto retry; + if (parsefile->fd == 0 && errno == EWOULDBLOCK) { + int flags = fcntl(0, F_GETFL, 0); + if (flags >= 0 && flags & O_NONBLOCK) { + flags &=~ O_NONBLOCK; + if (fcntl(0, F_SETFL, flags) >= 0) { + out2str("sh: turning off NDELAY mode\n"); + goto retry; + } + } + } + } + parsenleft = EOF_NLEFT; + return PEOF; + } + parsenleft = i - 1; + + /* delete nul characters */ + for (;;) { + if (*p++ == '\0') + break; + if (--i <= 0) + return *parsenextc++; /* no nul characters */ + } + q = p - 1; + while (--i > 0) { + if (*p != '\0') + *q++ = *p; + p++; + } + if (q == parsefile->buf) + goto retry; /* buffer contained nothing but nuls */ + parsenleft = q - parsefile->buf - 1; + return *parsenextc++; +} + + +/* + * Undo the last call to pgetc. Only one character may be pushed back. + * PEOF may be pushed back. + */ + +void +pungetc() { + parsenleft++; + parsenextc--; +} + + +/* + * Push a string back onto the input. This code doesn't work if the user + * tries to push back more than one string at once. + */ + +void +ppushback(string, length) + char *string; + { + pushedstring = parsenextc; + pushednleft = parsenleft; + parsenextc = string; + parsenleft = length; +} + + + +/* + * Set the input to take input from a file. If push is set, push the + * old input onto the stack first. + */ + +void +setinputfile(fname, push) + char *fname; + { + int fd; + int fd2; + + INTOFF; + if ((fd = open(fname, O_RDONLY)) < 0) + error("Can't open %s", fname); + if (fd < 10) { + fd2 = copyfd(fd, 10); + close(fd); + if (fd2 < 0) + error("Out of file descriptors"); + fd = fd2; + } + setinputfd(fd, push); + INTON; +} + + +/* + * Like setinputfile, but takes an open file descriptor. Call this with + * interrupts off. + */ + +void +setinputfd(fd, push) { + if (push) { + pushfile(); + parsefile->buf = ckmalloc(BUFSIZ); + } + if (parsefile->fd > 0) + close(parsefile->fd); + parsefile->fd = fd; + if (parsefile->buf == NULL) + parsefile->buf = ckmalloc(BUFSIZ); + parsenleft = 0; + plinno = 1; +} + + +/* + * Like setinputfile, but takes input from a string. + */ + +void +setinputstring(string, push) + char *string; + { + INTOFF; + if (push) + pushfile(); + parsenextc = string; + parsenleft = strlen(string); + parsefile->buf = NULL; + plinno = 1; + INTON; +} + + + +/* + * To handle the "." command, a stack of input files is used. Pushfile + * adds a new entry to the stack and popfile restores the previous level. + */ + +STATIC void +pushfile() { + struct parsefile *pf; + + parsefile->nleft = parsenleft; + parsefile->nextc = parsenextc; + parsefile->linno = plinno; + pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile)); + pf->prev = parsefile; + pf->fd = -1; + parsefile = pf; +} + + +void +popfile() { + struct parsefile *pf = parsefile; + + INTOFF; + if (pf->fd >= 0) + close(pf->fd); + if (pf->buf) + ckfree(pf->buf); + parsefile = pf->prev; + ckfree(pf); + parsenleft = parsefile->nleft; + parsenextc = parsefile->nextc; + plinno = parsefile->linno; + INTON; +} + + +/* + * Return to top level. + */ + +void +popallfiles() { + while (parsefile != &basepf) + popfile(); +} + + + +/* + * Close the file(s) that the shell is reading commands from. Called + * after a fork is done. + */ + +void +closescript() { + popallfiles(); + if (parsefile->fd > 0) { + close(parsefile->fd); + parsefile->fd = 0; + } +} diff --git a/bin/sh/input.h b/bin/sh/input.h new file mode 100644 index 000000000000..90fcbdb54ecb --- /dev/null +++ b/bin/sh/input.h @@ -0,0 +1,78 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * from: @(#)input.h 5.1 (Berkeley) 3/7/91 + * input.h,v 1.4 1993/08/01 18:58:27 mycroft Exp + */ + +/* PEOF (the end of file marker) is defined in syntax.h */ + +/* + * The input line number. Input.c just defines this variable, and saves + * and restores it when files are pushed and popped. The user of this + * package must set its value. + */ +extern int plinno; +extern int parsenleft; /* number of characters left in input buffer */ +extern char *parsenextc; /* next character in input buffer */ + + +#ifdef __STDC__ +char *pfgets(char *, int); +int pgetc(void); +int preadbuffer(void); +void pungetc(void); +void ppushback(char *, int); +void setinputfile(char *, int); +void setinputfd(int, int); +void setinputstring(char *, int); +void popfile(void); +void popallfiles(void); +void closescript(void); +#else +char *pfgets(); +int pgetc(); +int preadbuffer(); +void pungetc(); +void ppushback(); +void setinputfile(); +void setinputfd(); +void setinputstring(); +void popfile(); +void popallfiles(); +void closescript(); +#endif + +#define pgetc_macro() (--parsenleft >= 0? *parsenextc++ : preadbuffer()) diff --git a/bin/sh/jobs.c b/bin/sh/jobs.c new file mode 100644 index 000000000000..dfc4a27b3160 --- /dev/null +++ b/bin/sh/jobs.c @@ -0,0 +1,1020 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)jobs.c 5.1 (Berkeley) 3/7/91";*/ +static char rcsid[] = "jobs.c,v 1.7 1993/08/06 21:50:16 mycroft Exp"; +#endif /* not lint */ + +#include "shell.h" +#if JOBS +#include "sgtty.h" +#undef CEOF /* syntax.h redefines this */ +#endif +#include "main.h" +#include "parser.h" +#include "nodes.h" +#include "jobs.h" +#include "options.h" +#include "trap.h" +#include "syntax.h" +#include "input.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" +#include "redir.h" +#include <fcntl.h> +#include <signal.h> +#include <errno.h> +#include <unistd.h> +#ifdef BSD +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/time.h> +#include <sys/resource.h> +#endif + + + +struct job *jobtab; /* array of jobs */ +int njobs; /* size of array */ +MKINIT short backgndpid = -1; /* pid of last background process */ +#if JOBS +int initialpgrp; /* pgrp of shell on invocation */ +short curjob; /* current job */ +#endif + +#ifdef __STDC__ +STATIC void restartjob(struct job *); +STATIC struct job *getjob(char *); +STATIC void freejob(struct job *); +STATIC int procrunning(int); +STATIC int dowait(int, struct job *); +STATIC int waitproc(int, int *); +STATIC char *commandtext(union node *); +#else +STATIC void restartjob(); +STATIC struct job *getjob(); +STATIC void freejob(); +STATIC int procrunning(); +STATIC int dowait(); +STATIC int waitproc(); +STATIC char *commandtext(); +#endif + + + +#if JOBS +/* + * Turn job control on and off. + * + * Note: This code assumes that the third arg to ioctl is a character + * pointer, which is true on Berkeley systems but not System V. Since + * System V doesn't have job control yet, this isn't a problem now. + */ + +MKINIT int jobctl; + +void +setjobctl(on) { + int ldisc; + + if (on == jobctl || rootshell == 0) + return; + if (on) { + do { /* while we are in the background */ + if (ioctl(2, TIOCGPGRP, (char *)&initialpgrp) < 0) { + out2str("ash: can't access tty; job control turned off\n"); + jflag = 0; + return; + } + if (initialpgrp == -1) + initialpgrp = getpgrp(); + else if (initialpgrp != getpgrp()) { + killpg(initialpgrp, SIGTTIN); + continue; + } + } while (0); + if (ioctl(2, TIOCGETD, (char *)&ldisc) < 0 || ldisc != NTTYDISC) { + out2str("ash: need new tty driver to run job control; job control turned off\n"); + jflag = 0; + return; + } + setsignal(SIGTSTP); + setsignal(SIGTTOU); + setpgrp(0, rootpid); + ioctl(2, TIOCSPGRP, (char *)&rootpid); + } else { /* turning job control off */ + setpgrp(0, initialpgrp); + ioctl(2, TIOCSPGRP, (char *)&initialpgrp); + setsignal(SIGTSTP); + setsignal(SIGTTOU); + } + jobctl = on; +} +#endif + + +#ifdef mkinit + +SHELLPROC { + backgndpid = -1; +#if JOBS + jobctl = 0; +#endif +} + +#endif + + + +#if JOBS +fgcmd(argc, argv) char **argv; { + struct job *jp; + int pgrp; + int status; + + jp = getjob(argv[1]); + if (jp->jobctl == 0) + error("job not created under job control"); + pgrp = jp->ps[0].pid; + ioctl(2, TIOCSPGRP, (char *)&pgrp); + restartjob(jp); + INTOFF; + status = waitforjob(jp); + INTON; + return status; +} + + +bgcmd(argc, argv) char **argv; { + struct job *jp; + + do { + jp = getjob(*++argv); + if (jp->jobctl == 0) + error("job not created under job control"); + restartjob(jp); + } while (--argc > 1); + return 0; +} + + +STATIC void +restartjob(jp) + struct job *jp; + { + struct procstat *ps; + int i; + + if (jp->state == JOBDONE) + return; + INTOFF; + killpg(jp->ps[0].pid, SIGCONT); + for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) { + if ((ps->status & 0377) == 0177) { + ps->status = -1; + jp->state = 0; + } + } + INTON; +} +#endif + + +int +jobscmd(argc, argv) char **argv; { + showjobs(0); + return 0; +} + + +/* + * Print a list of jobs. If "change" is nonzero, only print jobs whose + * statuses have changed since the last call to showjobs. + * + * If the shell is interrupted in the process of creating a job, the + * result may be a job structure containing zero processes. Such structures + * will be freed here. + */ + +void +showjobs(change) { + int jobno; + int procno; + int i; + struct job *jp; + struct procstat *ps; + int col; + char s[64]; + + TRACE(("showjobs(%d) called\n", change)); + while (dowait(0, (struct job *)NULL) > 0); + for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) { + if (! jp->used) + continue; + if (jp->nprocs == 0) { + freejob(jp); + continue; + } + if (change && ! jp->changed) + continue; + procno = jp->nprocs; + for (ps = jp->ps ; ; ps++) { /* for each process */ + if (ps == jp->ps) + fmtstr(s, 64, "[%d] %d ", jobno, ps->pid); + else + fmtstr(s, 64, " %d ", ps->pid); + out1str(s); + col = strlen(s); + s[0] = '\0'; + if (ps->status == -1) { + /* don't print anything */ + } else if ((ps->status & 0xFF) == 0) { + fmtstr(s, 64, "Exit %d", ps->status >> 8); + } else { + i = ps->status; +#if JOBS + if ((i & 0xFF) == 0177) + i >>= 8; +#endif + if ((i & 0x7F) < NSIG && sys_siglist[i & 0x7F]) + scopy(sys_siglist[i & 0x7F], s); + else + fmtstr(s, 64, "Signal %d", i & 0x7F); + if (i & 0x80) + strcat(s, " (core dumped)"); + } + out1str(s); + col += strlen(s); + do { + out1c(' '); + col++; + } while (col < 30); + out1str(ps->cmd); + out1c('\n'); + if (--procno <= 0) + break; + } + jp->changed = 0; + if (jp->state == JOBDONE) { + freejob(jp); + } + } +} + + +/* + * Mark a job structure as unused. + */ + +STATIC void +freejob(jp) + struct job *jp; + { + struct procstat *ps; + int i; + + INTOFF; + for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) { + if (ps->cmd != nullstr) + ckfree(ps->cmd); + } + if (jp->ps != &jp->ps0) + ckfree(jp->ps); + jp->used = 0; +#if JOBS + if (curjob == jp - jobtab + 1) + curjob = 0; +#endif + INTON; +} + + + +int +waitcmd(argc, argv) char **argv; { + struct job *job; + int status; + struct job *jp; + + if (argc > 1) { + job = getjob(argv[1]); + } else { + job = NULL; + } + for (;;) { /* loop until process terminated or stopped */ + if (job != NULL) { + if (job->state) { + status = job->ps[job->nprocs - 1].status; + if ((status & 0xFF) == 0) + status = status >> 8 & 0xFF; +#if JOBS + else if ((status & 0xFF) == 0177) + status = (status >> 8 & 0x7F) + 128; +#endif + else + status = (status & 0x7F) + 128; + if (! iflag) + freejob(job); + return status; + } + } else { + for (jp = jobtab ; ; jp++) { + if (jp >= jobtab + njobs) { /* no running procs */ + return 0; + } + if (jp->used && jp->state == 0) + break; + } + } + dowait(1, (struct job *)NULL); + } +} + + + +jobidcmd(argc, argv) char **argv; { + struct job *jp; + int i; + + jp = getjob(argv[1]); + for (i = 0 ; i < jp->nprocs ; ) { + out1fmt("%d", jp->ps[i].pid); + out1c(++i < jp->nprocs? ' ' : '\n'); + } + return 0; +} + + + +/* + * Convert a job name to a job structure. + */ + +STATIC struct job * +getjob(name) + char *name; + { + int jobno; + register struct job *jp; + int pid; + int i; + + if (name == NULL) { +#if JOBS +currentjob: + if ((jobno = curjob) == 0 || jobtab[jobno - 1].used == 0) + error("No current job"); + return &jobtab[jobno - 1]; +#else + error("No current job"); +#endif + } else if (name[0] == '%') { + if (is_digit(name[1])) { + jobno = number(name + 1); + if (jobno > 0 && jobno <= njobs + && jobtab[jobno - 1].used != 0) + return &jobtab[jobno - 1]; +#if JOBS + } else if (name[1] == '%' && name[2] == '\0') { + goto currentjob; +#endif + } else { + register struct job *found = NULL; + for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { + if (jp->used && jp->nprocs > 0 + && prefix(name + 1, jp->ps[0].cmd)) { + if (found) + error("%s: ambiguous", name); + found = jp; + } + } + if (found) + return found; + } + } else if (is_number(name)) { + pid = number(name); + for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { + if (jp->used && jp->nprocs > 0 + && jp->ps[jp->nprocs - 1].pid == pid) + return jp; + } + } + error("No such job: %s", name); +} + + + +/* + * Return a new job structure, + */ + +struct job * +makejob(node, nprocs) + union node *node; + { + int i; + struct job *jp; + + for (i = njobs, jp = jobtab ; ; jp++) { + if (--i < 0) { + INTOFF; + if (njobs == 0) { + jobtab = ckmalloc(4 * sizeof jobtab[0]); + } else { + jp = ckmalloc((njobs + 4) * sizeof jobtab[0]); + bcopy(jobtab, jp, njobs * sizeof jp[0]); + ckfree(jobtab); + jobtab = jp; + } + jp = jobtab + njobs; + for (i = 4 ; --i >= 0 ; jobtab[njobs++].used = 0); + INTON; + break; + } + if (jp->used == 0) + break; + } + INTOFF; + jp->state = 0; + jp->used = 1; + jp->changed = 0; + jp->nprocs = 0; +#if JOBS + jp->jobctl = jobctl; +#endif + if (nprocs > 1) { + jp->ps = ckmalloc(nprocs * sizeof (struct procstat)); + } else { + jp->ps = &jp->ps0; + } + INTON; + TRACE(("makejob(0x%x, %d) returns %%%d\n", (int)node, nprocs, jp - jobtab + 1)); + return jp; +} + + +/* + * Fork of a subshell. If we are doing job control, give the subshell its + * own process group. Jp is a job structure that the job is to be added to. + * N is the command that will be evaluated by the child. Both jp and n may + * be NULL. The mode parameter can be one of the following: + * FORK_FG - Fork off a foreground process. + * FORK_BG - Fork off a background process. + * FORK_NOJOB - Like FORK_FG, but don't give the process its own + * process group even if job control is on. + * + * When job control is turned off, background processes have their standard + * input redirected to /dev/null (except for the second and later processes + * in a pipeline). + */ + +int +forkshell(jp, n, mode) + union node *n; + struct job *jp; + { + int pid; + int pgrp; + + TRACE(("forkshell(%%%d, 0x%x, %d) called\n", jp - jobtab, (int)n, mode)); + INTOFF; + pid = fork(); + if (pid == -1) { + TRACE(("Fork failed, errno=%d\n", errno)); + INTON; + error("Cannot fork"); + } + if (pid == 0) { + struct job *p; + int wasroot; + int i; + + TRACE(("Child shell %d\n", getpid())); + wasroot = rootshell; + rootshell = 0; + for (i = njobs, p = jobtab ; --i >= 0 ; p++) + if (p->used) + freejob(p); + closescript(); + INTON; + clear_traps(); +#if JOBS + jobctl = 0; /* do job control only in root shell */ + if (wasroot && mode != FORK_NOJOB && jflag) { + if (jp == NULL || jp->nprocs == 0) + pgrp = getpid(); + else + pgrp = jp->ps[0].pid; + setpgrp(0, pgrp); + if (mode == FORK_FG) { + /*** this causes superfluous TIOCSPGRPS ***/ + if (ioctl(2, TIOCSPGRP, (char *)&pgrp) < 0) + error("TIOCSPGRP failed, errno=%d\n", errno); + } + setsignal(SIGTSTP); + setsignal(SIGTTOU); + } else if (mode == FORK_BG) { + ignoresig(SIGINT); + ignoresig(SIGQUIT); + if ((jp == NULL || jp->nprocs == 0) + && ! fd0_redirected_p ()) { + close(0); + if (open("/dev/null", O_RDONLY) != 0) + error("Can't open /dev/null"); + } + } +#else + if (mode == FORK_BG) { + ignoresig(SIGINT); + ignoresig(SIGQUIT); + if ((jp == NULL || jp->nprocs == 0) + && ! fd0_redirected_p ()) { + close(0); + if (open("/dev/null", O_RDONLY) != 0) + error("Can't open /dev/null"); + } + } +#endif + if (wasroot && iflag) { + setsignal(SIGINT); + setsignal(SIGQUIT); + setsignal(SIGTERM); + } + return pid; + } + if (rootshell && mode != FORK_NOJOB && jflag) { + if (jp == NULL || jp->nprocs == 0) + pgrp = pid; + else + pgrp = jp->ps[0].pid; + setpgrp(pid, pgrp); + } + if (mode == FORK_BG) + backgndpid = pid; /* set $! */ + if (jp) { + struct procstat *ps = &jp->ps[jp->nprocs++]; + ps->pid = pid; + ps->status = -1; + ps->cmd = nullstr; + if (iflag && rootshell && n) + ps->cmd = commandtext(n); + } + INTON; + TRACE(("In parent shell: child = %d\n", pid)); + return pid; +} + + + +/* + * Wait for job to finish. + * + * Under job control we have the problem that while a child process is + * running interrupts generated by the user are sent to the child but not + * to the shell. This means that an infinite loop started by an inter- + * active user may be hard to kill. With job control turned off, an + * interactive user may place an interactive program inside a loop. If + * the interactive program catches interrupts, the user doesn't want + * these interrupts to also abort the loop. The approach we take here + * is to have the shell ignore interrupt signals while waiting for a + * forground process to terminate, and then send itself an interrupt + * signal if the child process was terminated by an interrupt signal. + * Unfortunately, some programs want to do a bit of cleanup and then + * exit on interrupt; unless these processes terminate themselves by + * sending a signal to themselves (instead of calling exit) they will + * confuse this approach. + */ + +int +waitforjob(jp) + register struct job *jp; + { +#if JOBS + int mypgrp = getpgrp(); +#endif + int status; + int st; + + INTOFF; + TRACE(("waitforjob(%%%d) called\n", jp - jobtab + 1)); + while (jp->state == 0) { + dowait(1, jp); + } +#if JOBS + if (jp->jobctl) { + if (ioctl(2, TIOCSPGRP, (char *)&mypgrp) < 0) + error("TIOCSPGRP failed, errno=%d\n", errno); + } + if (jp->state == JOBSTOPPED) + curjob = jp - jobtab + 1; +#endif + status = jp->ps[jp->nprocs - 1].status; + /* convert to 8 bits */ + if ((status & 0xFF) == 0) + st = status >> 8 & 0xFF; +#if JOBS + else if ((status & 0xFF) == 0177) + st = (status >> 8 & 0x7F) + 128; +#endif + else + st = (status & 0x7F) + 128; + if (! JOBS || jp->state == JOBDONE) + freejob(jp); + CLEAR_PENDING_INT; + if ((status & 0x7F) == SIGINT) + kill(getpid(), SIGINT); + INTON; + return st; +} + + + +/* + * Wait for a process to terminate. + */ + +STATIC int +dowait(block, job) + struct job *job; + { + int pid; + int status; + struct procstat *sp; + struct job *jp; + struct job *thisjob; + int done; + int stopped; + int core; + + TRACE(("dowait(%d) called\n", block)); + do { + pid = waitproc(block, &status); + TRACE(("wait returns %d, status=%d\n", pid, status)); + } while (pid == -1 && errno == EINTR); + if (pid <= 0) + return pid; + INTOFF; + thisjob = NULL; + for (jp = jobtab ; jp < jobtab + njobs ; jp++) { + if (jp->used) { + done = 1; + stopped = 1; + for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) { + if (sp->pid == -1) + continue; + if (sp->pid == pid) { + TRACE(("Changin status of proc %d from 0x%x to 0x%x\n", pid, sp->status, status)); + sp->status = status; + thisjob = jp; + } + if (sp->status == -1) + stopped = 0; + else if ((sp->status & 0377) == 0177) + done = 0; + } + if (stopped) { /* stopped or done */ + int state = done? JOBDONE : JOBSTOPPED; + if (jp->state != state) { + TRACE(("Job %d: changing state from %d to %d\n", jp - jobtab + 1, jp->state, state)); + jp->state = state; +#if JOBS + if (done && curjob == jp - jobtab + 1) + curjob = 0; /* no current job */ +#endif + } + } + } + } + INTON; + if (! rootshell || ! iflag || (job && thisjob == job)) { +#if JOBS + if ((status & 0xFF) == 0177) + status >>= 8; +#endif + core = status & 0x80; + status &= 0x7F; + if (status != 0 && status != SIGINT && status != SIGPIPE) { + if (thisjob != job) + outfmt(out2, "%d: ", pid); +#if JOBS + if (status == SIGTSTP && rootshell && iflag) + outfmt(out2, "%%%d ", job - jobtab + 1); +#endif + if (status < NSIG && sys_siglist[status]) + out2str(sys_siglist[status]); + else + outfmt(out2, "Signal %d", status); + if (core) + out2str(" - core dumped"); + out2c('\n'); + flushout(&errout); + } else { + TRACE(("Not printing status: status=%d\n", status)); + } + } else { + TRACE(("Not printing status, rootshell=%d, job=0x%x\n", rootshell, job)); + if (thisjob) + thisjob->changed = 1; + } + return pid; +} + + + +/* + * Do a wait system call. If job control is compiled in, we accept + * stopped processes. If block is zero, we return a value of zero + * rather than blocking. + * + * System V doesn't have a non-blocking wait system call. It does + * have a SIGCLD signal that is sent to a process when one of it's + * children dies. The obvious way to use SIGCLD would be to install + * a handler for SIGCLD which simply bumped a counter when a SIGCLD + * was received, and have waitproc bump another counter when it got + * the status of a process. Waitproc would then know that a wait + * system call would not block if the two counters were different. + * This approach doesn't work because if a process has children that + * have not been waited for, System V will send it a SIGCLD when it + * installs a signal handler for SIGCLD. What this means is that when + * a child exits, the shell will be sent SIGCLD signals continuously + * until is runs out of stack space, unless it does a wait call before + * restoring the signal handler. The code below takes advantage of + * this (mis)feature by installing a signal handler for SIGCLD and + * then checking to see whether it was called. If there are any + * children to be waited for, it will be. + * + * If neither SYSV nor BSD is defined, we don't implement nonblocking + * waits at all. In this case, the user will not be informed when + * a background process until the next time she runs a real program + * (as opposed to running a builtin command or just typing return), + * and the jobs command may give out of date information. + */ + +#ifdef SYSV +STATIC int gotsigchild; + +STATIC int onsigchild() { + gotsigchild = 1; +} +#endif + + +STATIC int +waitproc(block, status) + int *status; + { +#ifdef BSD + int flags; + +#if JOBS + flags = WUNTRACED; +#else + flags = 0; +#endif + if (block == 0) + flags |= WNOHANG; + return wait3((int *)status, flags, (struct rusage *)NULL); +#else +#ifdef SYSV + int (*save)(); + + if (block == 0) { + gotsigchild = 0; + save = signal(SIGCLD, onsigchild); + signal(SIGCLD, save); + if (gotsigchild == 0) + return 0; + } + return wait(status); +#else + if (block == 0) + return 0; + return wait(status); +#endif +#endif +} + + + +/* + * Return a string identifying a command (to be printed by the + * jobs command. + */ + +STATIC char *cmdnextc; +STATIC int cmdnleft; +STATIC void cmdtxt(), cmdputs(); + +STATIC char * +commandtext(n) + union node *n; + { + char *name; + + cmdnextc = name = ckmalloc(50); + cmdnleft = 50 - 4; + cmdtxt(n); + *cmdnextc = '\0'; + return name; +} + + +STATIC void +cmdtxt(n) + union node *n; + { + union node *np; + struct nodelist *lp; + char *p; + int i; + char s[2]; + + switch (n->type) { + case NSEMI: + cmdtxt(n->nbinary.ch1); + cmdputs("; "); + cmdtxt(n->nbinary.ch2); + break; + case NAND: + cmdtxt(n->nbinary.ch1); + cmdputs(" && "); + cmdtxt(n->nbinary.ch2); + break; + case NOR: + cmdtxt(n->nbinary.ch1); + cmdputs(" || "); + cmdtxt(n->nbinary.ch2); + break; + case NPIPE: + for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { + cmdtxt(lp->n); + if (lp->next) + cmdputs(" | "); + } + break; + case NSUBSHELL: + cmdputs("("); + cmdtxt(n->nredir.n); + cmdputs(")"); + break; + case NREDIR: + case NBACKGND: + cmdtxt(n->nredir.n); + break; + case NIF: + cmdputs("if "); + cmdtxt(n->nif.test); + cmdputs("; then "); + cmdtxt(n->nif.ifpart); + cmdputs("..."); + break; + case NWHILE: + cmdputs("while "); + goto until; + case NUNTIL: + cmdputs("until "); +until: + cmdtxt(n->nbinary.ch1); + cmdputs("; do "); + cmdtxt(n->nbinary.ch2); + cmdputs("; done"); + break; + case NFOR: + cmdputs("for "); + cmdputs(n->nfor.var); + cmdputs(" in ..."); + break; + case NCASE: + cmdputs("case "); + cmdputs(n->ncase.expr->narg.text); + cmdputs(" in ..."); + break; + case NDEFUN: + cmdputs(n->narg.text); + cmdputs("() ..."); + break; + case NCMD: + for (np = n->ncmd.args ; np ; np = np->narg.next) { + cmdtxt(np); + if (np->narg.next) + cmdputs(" "); + } + for (np = n->ncmd.redirect ; np ; np = np->nfile.next) { + cmdputs(" "); + cmdtxt(np); + } + break; + case NARG: + cmdputs(n->narg.text); + break; + case NTO: + p = ">"; i = 1; goto redir; + case NAPPEND: + p = ">>"; i = 1; goto redir; + case NTOFD: + p = ">&"; i = 1; goto redir; + case NFROM: + p = "<"; i = 0; goto redir; + case NFROMFD: + p = "<&"; i = 0; goto redir; +redir: + if (n->nfile.fd != i) { + s[0] = n->nfile.fd + '0'; + s[1] = '\0'; + cmdputs(s); + } + cmdputs(p); + if (n->type == NTOFD || n->type == NFROMFD) { + s[0] = n->ndup.dupfd + '0'; + s[1] = '\0'; + cmdputs(s); + } else { + cmdtxt(n->nfile.fname); + } + break; + case NHERE: + case NXHERE: + cmdputs("<<..."); + break; + default: + cmdputs("???"); + break; + } +} + + + +STATIC void +cmdputs(s) + char *s; + { + register char *p, *q; + register char c; + int subtype = 0; + + if (cmdnleft <= 0) + return; + p = s; + q = cmdnextc; + while ((c = *p++) != '\0') { + if (c == CTLESC) + *q++ = *p++; + else if (c == CTLVAR) { + *q++ = '$'; + if (--cmdnleft > 0) + *q++ = '{'; + subtype = *p++; + } else if (c == '=' && subtype != 0) { + *q++ = "}-+?="[(subtype & VSTYPE) - VSNORMAL]; + subtype = 0; + } else if (c == CTLENDVAR) { + *q++ = '}'; + } else if (c == CTLBACKQ | c == CTLBACKQ+CTLQUOTE) + cmdnleft++; /* ignore it */ + else + *q++ = c; + if (--cmdnleft <= 0) { + *q++ = '.'; + *q++ = '.'; + *q++ = '.'; + break; + } + } + cmdnextc = q; +} diff --git a/bin/sh/jobs.h b/bin/sh/jobs.h new file mode 100644 index 000000000000..d5d5f3488841 --- /dev/null +++ b/bin/sh/jobs.h @@ -0,0 +1,97 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * from: @(#)jobs.h 5.1 (Berkeley) 3/7/91 + * jobs.h,v 1.4 1993/08/01 18:58:26 mycroft Exp + */ + +/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ +#define FORK_FG 0 +#define FORK_BG 1 +#define FORK_NOJOB 2 + + +/* + * A job structure contains information about a job. A job is either a + * single process or a set of processes contained in a pipeline. In the + * latter case, pidlist will be non-NULL, and will point to a -1 terminated + * array of pids. + */ + +struct procstat { + short pid; /* process id */ + short status; /* status flags (defined above) */ + char *cmd; /* text of command being run */ +}; + + +/* states */ +#define JOBSTOPPED 1 /* all procs are stopped */ +#define JOBDONE 2 /* all procs are completed */ + + +struct job { + struct procstat ps0; /* status of process */ + struct procstat *ps; /* status or processes when more than one */ + short nprocs; /* number of processes */ + short pgrp; /* process group of this job */ + char state; /* true if job is finished */ + char used; /* true if this entry is in used */ + char changed; /* true if status has changed */ +#if JOBS + char jobctl; /* job running under job control */ +#endif +}; + +extern short backgndpid; /* pid of last background process */ + + +#ifdef __STDC__ +void setjobctl(int); +void showjobs(int); +struct job *makejob(union node *, int); +int forkshell(struct job *, union node *, int); +int waitforjob(struct job *); +#else +void setjobctl(); +void showjobs(); +struct job *makejob(); +int forkshell(); +int waitforjob(); +#endif + +#if ! JOBS +#define setjobctl(on) /* do nothing */ +#endif diff --git a/bin/sh/machdep.h b/bin/sh/machdep.h new file mode 100644 index 000000000000..1696c87a3b8f --- /dev/null +++ b/bin/sh/machdep.h @@ -0,0 +1,52 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * from: @(#)machdep.h 5.1 (Berkeley) 3/7/91 + * machdep.h,v 1.4 1993/08/01 18:58:26 mycroft Exp + */ + +/* + * Most machines require the value returned from malloc to be aligned + * in some way. The following macro will get this right on many machines. + */ + +#ifndef ALIGN +union align { + int i; + char *cp; +}; + +#define ALIGN(nbytes) ((nbytes) + sizeof(union align) - 1 &~ (sizeof(union align) - 1)) +#endif diff --git a/bin/sh/mail.c b/bin/sh/mail.c new file mode 100644 index 000000000000..18d151ff632e --- /dev/null +++ b/bin/sh/mail.c @@ -0,0 +1,115 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)mail.c 5.1 (Berkeley) 3/7/91";*/ +static char rcsid[] = "mail.c,v 1.4 1993/08/01 18:58:13 mycroft Exp"; +#endif /* not lint */ + +/* + * Routines to check for mail. (Perhaps make part of main.c?) + */ + +#include "shell.h" +#include "exec.h" /* defines padvance() */ +#include "var.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include <sys/types.h> +#include <sys/stat.h> + + +#define MAXMBOXES 10 + + +STATIC int nmboxes; /* number of mailboxes */ +STATIC time_t mailtime[MAXMBOXES]; /* times of mailboxes */ + + + +/* + * Print appropriate message(s) if mail has arrived. If the argument is + * nozero, then the value of MAIL has changed, so we just update the + * values. + */ + +void +chkmail(silent) { + register int i; + char *mpath; + char *p; + register char *q; + struct stackmark smark; + struct stat statb; + + if (silent) + nmboxes = 10; + if (nmboxes == 0) + return; + setstackmark(&smark); + mpath = mpathset()? mpathval() : mailval(); + for (i = 0 ; i < nmboxes ; i++) { + p = padvance(&mpath, nullstr); + if (p == NULL) + break; + if (*p == '\0') + continue; + for (q = p ; *q ; q++); + if (q[-1] != '/') + abort(); + q[-1] = '\0'; /* delete trailing '/' */ +#ifdef notdef /* this is what the System V shell claims to do (it lies) */ + if (stat(p, &statb) < 0) + statb.st_mtime = 0; + if (statb.st_mtime > mailtime[i] && ! silent) { + out2str(pathopt? pathopt : "you have mail"); + out2c('\n'); + } + mailtime[i] = statb.st_mtime; +#else /* this is what it should do */ + if (stat(p, &statb) < 0) + statb.st_size = 0; + if (statb.st_size > mailtime[i] && ! silent) { + out2str(pathopt? pathopt : "you have mail"); + out2c('\n'); + } + mailtime[i] = statb.st_size; +#endif + } + nmboxes = i; + popstackmark(&smark); +} diff --git a/bin/sh/mail.h b/bin/sh/mail.h new file mode 100644 index 000000000000..64efe4e2bfb4 --- /dev/null +++ b/bin/sh/mail.h @@ -0,0 +1,44 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * from: @(#)mail.h 5.1 (Berkeley) 3/7/91 + * mail.h,v 1.4 1993/08/01 18:58:25 mycroft Exp + */ + +#ifdef __STDC__ +void chkmail(int); +#else +void chkmail(); +#endif diff --git a/bin/sh/main.c b/bin/sh/main.c new file mode 100644 index 000000000000..ec6f32559184 --- /dev/null +++ b/bin/sh/main.c @@ -0,0 +1,326 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1991 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)main.c 5.2 (Berkeley) 3/13/91";*/ +static char rcsid[] = "main.c,v 1.4 1993/08/01 18:58:12 mycroft Exp"; +#endif /* not lint */ + +#include <signal.h> +#include <fcntl.h> +#include "shell.h" +#include "main.h" +#include "mail.h" +#include "options.h" +#include "output.h" +#include "parser.h" +#include "nodes.h" +#include "eval.h" +#include "jobs.h" +#include "input.h" +#include "trap.h" +#if ATTY +#include "var.h" +#endif +#include "memalloc.h" +#include "error.h" +#include "init.h" +#include "mystring.h" + +#define PROFILE 0 + +int rootpid; +int rootshell; +STATIC union node *curcmd; +STATIC union node *prevcmd; +extern int errno; +#if PROFILE +short profile_buf[16384]; +extern int etext(); +#endif + +#ifdef __STDC__ +STATIC void read_profile(char *); +char *getenv(char *); +#else +STATIC void read_profile(); +char *getenv(); +#endif + + +/* + * Main routine. We initialize things, parse the arguments, execute + * profiles if we're a login shell, and then call cmdloop to execute + * commands. The setjmp call sets up the location to jump to when an + * exception occurs. When an exception occurs the variable "state" + * is used to figure out how far we had gotten. + */ + +main(argc, argv) char **argv; { + struct jmploc jmploc; + struct stackmark smark; + volatile int state; + char *shinit; + +#if PROFILE + monitor(4, etext, profile_buf, sizeof profile_buf, 50); +#endif + state = 0; + if (setjmp(jmploc.loc)) { + /* + * When a shell procedure is executed, we raise the + * exception EXSHELLPROC to clean up before executing + * the shell procedure. + */ + if (exception == EXSHELLPROC) { + rootpid = getpid(); + rootshell = 1; + minusc = NULL; + state = 3; + } else if (state == 0 || iflag == 0 || ! rootshell) + exitshell(2); + reset(); +#if ATTY + if (exception == EXINT + && (! attyset() || equal(termval(), "emacs"))) { +#else + if (exception == EXINT) { +#endif + out2c('\n'); + flushout(&errout); + } + popstackmark(&smark); + FORCEINTON; /* enable interrupts */ + if (state == 1) + goto state1; + else if (state == 2) + goto state2; + else + goto state3; + } + handler = &jmploc; +#ifdef DEBUG + opentrace(); + trputs("Shell args: "); trargs(argv); +#endif + rootpid = getpid(); + rootshell = 1; + init(); + setstackmark(&smark); + procargs(argc, argv); + if (argv[0] && argv[0][0] == '-') { + state = 1; + read_profile("/etc/profile"); +state1: + state = 2; + read_profile(".profile"); + } else if ((sflag || minusc) && (shinit = getenv("SHINIT")) != NULL) { + state = 2; + evalstring(shinit); + } +state2: + state = 3; + if (minusc) { + evalstring(minusc); + } + if (sflag || minusc == NULL) { +state3: + cmdloop(1); + } +#if PROFILE + monitor(0); +#endif + exitshell(exitstatus); +} + + +/* + * Read and execute commands. "Top" is nonzero for the top level command + * loop; it turns on prompting if the shell is interactive. + */ + +void +cmdloop(top) { + union node *n; + struct stackmark smark; + int inter; + int numeof; + + TRACE(("cmdloop(%d) called\n", top)); + setstackmark(&smark); + numeof = 0; + for (;;) { + if (pendingsigs) + dotrap(); + inter = 0; + if (iflag && top) { + inter++; + showjobs(1); + chkmail(0); + flushout(&output); + } + n = parsecmd(inter); +#ifdef DEBUG + /* showtree(n); */ +#endif + if (n == NEOF) { + if (Iflag == 0 || numeof >= 50) + break; + out2str("\nUse \"exit\" to leave shell.\n"); + numeof++; + } else if (n != NULL && nflag == 0) { + if (inter) { + INTOFF; + if (prevcmd) + freefunc(prevcmd); + prevcmd = curcmd; + curcmd = copyfunc(n); + INTON; + } + evaltree(n, 0); +#ifdef notdef + if (exitstatus) /*DEBUG*/ + outfmt(&errout, "Exit status 0x%X\n", exitstatus); +#endif + } + popstackmark(&smark); + } + popstackmark(&smark); /* unnecessary */ +} + + + +/* + * Read /etc/profile or .profile. Return on error. + */ + +STATIC void +read_profile(name) + char *name; + { + int fd; + + INTOFF; + if ((fd = open(name, O_RDONLY)) >= 0) + setinputfd(fd, 1); + INTON; + if (fd < 0) + return; + cmdloop(0); + popfile(); +} + + + +/* + * Read a file containing shell functions. + */ + +void +readcmdfile(name) + char *name; + { + int fd; + + INTOFF; + if ((fd = open(name, O_RDONLY)) >= 0) + setinputfd(fd, 1); + else + error("Can't open %s", name); + INTON; + cmdloop(0); + popfile(); +} + + + +/* + * Take commands from a file. To be compatable we should do a path + * search for the file, but a path search doesn't make any sense. + */ + +dotcmd(argc, argv) char **argv; { + exitstatus = 0; + if (argc >= 2) { /* That's what SVR2 does */ + setinputfile(argv[1], 1); + commandname = argv[1]; + cmdloop(0); + popfile(); + } + return exitstatus; +} + + +exitcmd(argc, argv) char **argv; { + if (argc > 1) + exitstatus = number(argv[1]); + exitshell(exitstatus); +} + + +lccmd(argc, argv) char **argv; { + if (argc > 1) { + defun(argv[1], prevcmd); + return 0; + } else { + INTOFF; + freefunc(curcmd); + curcmd = prevcmd; + prevcmd = NULL; + INTON; + evaltree(curcmd, 0); + return exitstatus; + } +} + + + +#ifdef notdef +/* + * Should never be called. + */ + +void +exit(exitstatus) { + _exit(exitstatus); +} +#endif diff --git a/bin/sh/main.h b/bin/sh/main.h new file mode 100644 index 000000000000..4aa0aa312147 --- /dev/null +++ b/bin/sh/main.h @@ -0,0 +1,49 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * from: @(#)main.h 5.1 (Berkeley) 3/7/91 + * main.h,v 1.4 1993/08/01 18:58:24 mycroft Exp + */ + +extern int rootpid; /* pid of main shell */ +extern int rootshell; /* true if we aren't a child of the main shell */ + +#ifdef __STDC__ +void readcmdfile(char *); +void cmdloop(int); +#else +void readcmdfile(); +void cmdloop(); +#endif diff --git a/bin/sh/memalloc.c b/bin/sh/memalloc.c new file mode 100644 index 000000000000..caae59110b56 --- /dev/null +++ b/bin/sh/memalloc.c @@ -0,0 +1,293 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)memalloc.c 5.2 (Berkeley) 3/13/91";*/ +static char rcsid[] = "memalloc.c,v 1.4 1993/08/01 18:58:10 mycroft Exp"; +#endif /* not lint */ + +#include "shell.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "machdep.h" +#include "mystring.h" + +/* + * Like malloc, but returns an error when out of space. + */ + +pointer +ckmalloc(nbytes) { + register pointer p; + pointer malloc(); + + if ((p = malloc(nbytes)) == NULL) + error("Out of space"); + return p; +} + + +/* + * Same for realloc. + */ + +pointer +ckrealloc(p, nbytes) + register pointer p; + { + pointer realloc(); + + if ((p = realloc(p, nbytes)) == NULL) + error("Out of space"); + return p; +} + + +/* + * Make a copy of a string in safe storage. + */ + +char * +savestr(s) + char *s; + { + register char *p; + + p = ckmalloc(strlen(s) + 1); + scopy(s, p); + return p; +} + + +/* + * Parse trees for commands are allocated in lifo order, so we use a stack + * to make this more efficient, and also to avoid all sorts of exception + * handling code to handle interrupts in the middle of a parse. + * + * The size 504 was chosen because the Ultrix malloc handles that size + * well. + */ + +#define MINSIZE 504 /* minimum size of a block */ + + +struct stack_block { + struct stack_block *prev; + char space[MINSIZE]; +}; + +struct stack_block stackbase; +struct stack_block *stackp = &stackbase; +char *stacknxt = stackbase.space; +int stacknleft = MINSIZE; +int sstrnleft; +int herefd = -1; + + + +pointer +stalloc(nbytes) { + register char *p; + + nbytes = ALIGN(nbytes); + if (nbytes > stacknleft) { + int blocksize; + struct stack_block *sp; + + blocksize = nbytes; + if (blocksize < MINSIZE) + blocksize = MINSIZE; + INTOFF; + sp = ckmalloc(sizeof(struct stack_block) - MINSIZE + blocksize); + sp->prev = stackp; + stacknxt = sp->space; + stacknleft = blocksize; + stackp = sp; + INTON; + } + p = stacknxt; + stacknxt += nbytes; + stacknleft -= nbytes; + return p; +} + + +void +stunalloc(p) + pointer p; + { + if (p == NULL) { /*DEBUG */ + write(2, "stunalloc\n", 10); + abort(); + } + stacknleft += stacknxt - (char *)p; + stacknxt = p; +} + + + +void +setstackmark(mark) + struct stackmark *mark; + { + mark->stackp = stackp; + mark->stacknxt = stacknxt; + mark->stacknleft = stacknleft; +} + + +void +popstackmark(mark) + struct stackmark *mark; + { + struct stack_block *sp; + + INTOFF; + while (stackp != mark->stackp) { + sp = stackp; + stackp = sp->prev; + ckfree(sp); + } + stacknxt = mark->stacknxt; + stacknleft = mark->stacknleft; + INTON; +} + + +/* + * When the parser reads in a string, it wants to stick the string on the + * stack and only adjust the stack pointer when it knows how big the + * string is. Stackblock (defined in stack.h) returns a pointer to a block + * of space on top of the stack and stackblocklen returns the length of + * this block. Growstackblock will grow this space by at least one byte, + * possibly moving it (like realloc). Grabstackblock actually allocates the + * part of the block that has been used. + */ + +void +growstackblock() { + char *p; + int newlen = stacknleft * 2 + 100; + char *oldspace = stacknxt; + int oldlen = stacknleft; + struct stack_block *sp; + + if (stacknxt == stackp->space && stackp != &stackbase) { + INTOFF; + sp = stackp; + stackp = sp->prev; + sp = ckrealloc((pointer)sp, sizeof(struct stack_block) - MINSIZE + newlen); + sp->prev = stackp; + stackp = sp; + stacknxt = sp->space; + stacknleft = newlen; + INTON; + } else { + p = stalloc(newlen); + bcopy(oldspace, p, oldlen); + stacknxt = p; /* free the space */ + stacknleft += newlen; /* we just allocated */ + } +} + + + +void +grabstackblock(len) { + len = ALIGN(len); + stacknxt += len; + stacknleft -= len; +} + + + +/* + * The following routines are somewhat easier to use that the above. + * The user declares a variable of type STACKSTR, which may be declared + * to be a register. The macro STARTSTACKSTR initializes things. Then + * the user uses the macro STPUTC to add characters to the string. In + * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is + * grown as necessary. When the user is done, she can just leave the + * string there and refer to it using stackblock(). Or she can allocate + * the space for it using grabstackstr(). If it is necessary to allow + * someone else to use the stack temporarily and then continue to grow + * the string, the user should use grabstack to allocate the space, and + * then call ungrabstr(p) to return to the previous mode of operation. + * + * USTPUTC is like STPUTC except that it doesn't check for overflow. + * CHECKSTACKSPACE can be called before USTPUTC to ensure that there + * is space for at least one character. + */ + + +char * +growstackstr() { + int len = stackblocksize(); + if (herefd >= 0 && len >= 1024) { + xwrite(herefd, stackblock(), len); + sstrnleft = len - 1; + return stackblock(); + } + growstackblock(); + sstrnleft = stackblocksize() - len - 1; + return stackblock() + len; +} + + +/* + * Called from CHECKSTRSPACE. + */ + +char * +makestrspace() { + int len = stackblocksize() - sstrnleft; + growstackblock(); + sstrnleft = stackblocksize() - len; + return stackblock() + len; +} + + + +void +ungrabstackstr(s, p) + char *s; + char *p; + { + stacknleft += stacknxt - s; + stacknxt = s; + sstrnleft = stacknleft - (p - s); +} diff --git a/bin/sh/memalloc.h b/bin/sh/memalloc.h new file mode 100644 index 000000000000..841220cf3bce --- /dev/null +++ b/bin/sh/memalloc.h @@ -0,0 +1,96 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * from: @(#)memalloc.h 5.1 (Berkeley) 3/7/91 + * memalloc.h,v 1.4 1993/08/01 18:58:23 mycroft Exp + */ + +struct stackmark { + struct stack_block *stackp; + char *stacknxt; + int stacknleft; +}; + + +extern char *stacknxt; +extern int stacknleft; +extern int sstrnleft; +extern int herefd; + +#ifdef __STDC__ +pointer ckmalloc(int); +pointer ckrealloc(pointer, int); +void free(pointer); /* defined in C library */ +char *savestr(char *); +pointer stalloc(int); +void stunalloc(pointer); +void setstackmark(struct stackmark *); +void popstackmark(struct stackmark *); +void growstackblock(void); +void grabstackblock(int); +char *growstackstr(void); +char *makestrspace(void); +void ungrabstackstr(char *, char *); +#else +pointer ckmalloc(); +pointer ckrealloc(); +void free(); /* defined in C library */ +char *savestr(); +pointer stalloc(); +void stunalloc(); +void setstackmark(); +void popstackmark(); +void growstackblock(); +void grabstackblock(); +char *growstackstr(); +char *makestrspace(); +void ungrabstackstr(); +#endif + + + +#define stackblock() stacknxt +#define stackblocksize() stacknleft +#define STARTSTACKSTR(p) p = stackblock(), sstrnleft = stackblocksize() +#define STPUTC(c, p) (--sstrnleft >= 0? (*p++ = (c)) : (p = growstackstr(), *p++ = (c))) +#define CHECKSTRSPACE(n, p) if (sstrnleft < n) p = makestrspace(); else +#define USTPUTC(c, p) (--sstrnleft, *p++ = (c)) +#define STACKSTRNUL(p) (sstrnleft == 0? (p = growstackstr(), *p = '\0') : (*p = '\0')) +#define STUNPUTC(p) (++sstrnleft, --p) +#define STTOPC(p) p[-1] +#define STADJUST(amount, p) (p += (amount), sstrnleft -= (amount)) +#define grabstackstr(p) stalloc(stackblocksize() - sstrnleft) + +#define ckfree(p) free((pointer)(p)) diff --git a/bin/sh/miscbltin.c b/bin/sh/miscbltin.c new file mode 100644 index 000000000000..b14da674a166 --- /dev/null +++ b/bin/sh/miscbltin.c @@ -0,0 +1,219 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)miscbltin.c 5.2 (Berkeley) 3/13/91";*/ +static char rcsid[] = "miscbltin.c,v 1.5 1993/08/01 18:57:56 mycroft Exp"; +#endif /* not lint */ + +/* + * Miscelaneous builtins. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include "shell.h" +#include "options.h" +#include "var.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" + +#undef eflag + +extern char **argptr; /* argument list for builtin command */ + + +/* + * The read builtin. The -e option causes backslashes to escape the + * following character. + * + * This uses unbuffered input, which may be avoidable in some cases. + */ + +readcmd(argc, argv) char **argv; { + char **ap; + int backslash; + char c; + int eflag; + char *prompt; + char *ifs; + char *p; + int startword; + int status; + int i; + + eflag = 0; + prompt = NULL; + while ((i = nextopt("ep:")) != '\0') { + if (i == 'p') + prompt = optarg; + else + eflag = 1; + } + if (prompt && isatty(0)) { + out2str(prompt); + flushall(); + } + if ((ap = argptr) == NULL) + error("arg count"); + if ((ifs = bltinlookup("IFS", 1)) == NULL) + ifs = nullstr; + status = 0; + startword = 1; + backslash = 0; + STARTSTACKSTR(p); + for (;;) { + if (read(0, &c, 1) != 1) { + status = 1; + break; + } + if (c == '\0') + continue; + if (backslash) { + backslash = 0; + if (c != '\n') + STPUTC(c, p); + continue; + } + if (eflag && c == '\\') { + backslash++; + continue; + } + if (c == '\n') + break; + if (startword && *ifs == ' ' && strchr(ifs, c)) { + continue; + } + startword = 0; + if (backslash && c == '\\') { + if (read(0, &c, 1) != 1) { + status = 1; + break; + } + STPUTC(c, p); + } else if (ap[1] != NULL && strchr(ifs, c) != NULL) { + STACKSTRNUL(p); + setvar(*ap, stackblock(), 0); + ap++; + startword = 1; + STARTSTACKSTR(p); + } else { + STPUTC(c, p); + } + } + STACKSTRNUL(p); + setvar(*ap, stackblock(), 0); + while (*++ap != NULL) + setvar(*ap, nullstr, 0); + return status; +} + + + +umaskcmd(argc, argv) char **argv; { + extern void *setmode(); + extern mode_t getmode(); + char *ap; + int mask; + int i; + int symbolic_mode = 0; + + while ((i = nextopt("S")) != '\0') { + symbolic_mode = 1; + } + + INTOFF; + mask = umask(0); + umask(mask); + INTON; + + if ((ap = *argptr) == NULL) { + if (symbolic_mode) { + char u[4], g[4], o[4]; + + i = 0; + if ((mask & S_IRUSR) == 0) + u[i++] = 'r'; + if ((mask & S_IWUSR) == 0) + u[i++] = 'w'; + if ((mask & S_IXUSR) == 0) + u[i++] = 'x'; + u[i] = '\0'; + + i = 0; + if ((mask & S_IRGRP) == 0) + g[i++] = 'r'; + if ((mask & S_IWGRP) == 0) + g[i++] = 'w'; + if ((mask & S_IXGRP) == 0) + g[i++] = 'x'; + g[i] = '\0'; + + i = 0; + if ((mask & S_IROTH) == 0) + o[i++] = 'r'; + if ((mask & S_IWOTH) == 0) + o[i++] = 'w'; + if ((mask & S_IXOTH) == 0) + o[i++] = 'x'; + o[i] = '\0'; + + out1fmt("u=%s,g=%s,o=%s\n", u, g, o); + } else { + out1fmt("%.4o\n", mask); + } + } else { + if (isdigit(*ap)) { + mask = 0; + do { + if (*ap >= '8' || *ap < '0') + error("Illegal number: %s", argv[1]); + mask = (mask << 3) + (*ap - '0'); + } while (*++ap != '\0'); + umask(mask); + } else { + void *set; + if ((set = setmode (ap)) == 0) + error("Illegal number: %s", ap); + + mask = getmode (set, ~mask & 0777); + umask(~mask & 0777); + } + } + return 0; +} diff --git a/bin/sh/mkbuiltins b/bin/sh/mkbuiltins new file mode 100644 index 000000000000..42d72b73284e --- /dev/null +++ b/bin/sh/mkbuiltins @@ -0,0 +1,90 @@ +#!/bin/sh - +# +# Copyright (c) 1991 The Regents of the University of California. +# All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. 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. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. +# +# @(#)mkbuiltins 5.2 (Berkeley) 3/8/91 +# +# /b/source/CVS/src/bin/sh/mkbuiltins,v 1.4 1993/04/18 17:37:21 mycroft Exp + +temp=/tmp/ka$$ +havejobs=0 +if grep '^#define JOBS[ ]*1' shell.h > /dev/null +then havejobs=1 +fi +if [ -d obj ]; then objdir=obj; else objdir=.; fi +exec > ${objdir}/builtins.c +cat <<\! +/* + * This file was generated by the mkbuiltins program. + */ + +#include "shell.h" +#include "builtins.h" + +! +awk '/^[^#]/ {if('$havejobs' || $2 != "-j") print $0}' builtins | + sed 's/-j//' > $temp +awk '{ printf "int %s();\n", $1}' $temp +echo ' +int (*const builtinfunc[])() = {' +awk '/^[^#]/ { printf "\t%s,\n", $1}' $temp +echo '}; + +const struct builtincmd builtincmd[] = {' +awk '{ for (i = 2 ; i <= NF ; i++) { + printf "\t\"%s\", %d,\n", $i, NR-1 + }}' $temp +echo ' NULL, 0 +};' + +exec > ${objdir}/builtins.h +cat <<\! +/* + * This file was generated by the mkbuiltins program. + */ + +#include <sys/cdefs.h> +! +tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ < $temp | + awk '{ printf "#define %s %d\n", $1, NR-1}' +echo ' +struct builtincmd { + char *name; + int code; +}; + +extern int (*const builtinfunc[])(); +extern const struct builtincmd builtincmd[];' +rm -f $temp diff --git a/bin/sh/mkinit.c b/bin/sh/mkinit.c new file mode 100644 index 000000000000..02e9491cb983 --- /dev/null +++ b/bin/sh/mkinit.c @@ -0,0 +1,546 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1991 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)mkinit.c 5.3 (Berkeley) 3/13/91";*/ +static char rcsid[] = "mkinit.c,v 1.4 1993/08/01 18:58:09 mycroft Exp"; +#endif /* not lint */ + +/* + * This program scans all the source files for code to handle various + * special events and combines this code into one file. This (allegedly) + * improves the structure of the program since there is no need for + * anyone outside of a module to know that that module performs special + * operations on particular events. The command is executed iff init.c + * is actually changed. + * + * Usage: mkinit command sourcefile... + */ + + +#include <sys/cdefs.h> +#include <stdio.h> +#include <fcntl.h> + + +/* + * OUTFILE is the name of the output file. Output is initially written + * to the file OUTTEMP, which is then moved to OUTFILE if OUTTEMP and + * OUTFILE are different. + */ + +#define OUTFILE "init.c" +#define OUTTEMP "init.c.new" +#define OUTOBJ "init.o" + + +/* + * A text structure is basicly just a string that grows as more characters + * are added onto the end of it. It is implemented as a linked list of + * blocks of characters. The routines addstr and addchar append a string + * or a single character, respectively, to a text structure. Writetext + * writes the contents of a text structure to a file. + */ + +#define BLOCKSIZE 512 + +struct text { + char *nextc; + int nleft; + struct block *start; + struct block *last; +}; + +struct block { + struct block *next; + char text[BLOCKSIZE]; +}; + + +/* + * There is one event structure for each event that mkinit handles. + */ + +struct event { + char *name; /* name of event (e.g. INIT) */ + char *routine; /* name of routine called on event */ + char *comment; /* comment describing routine */ + struct text code; /* code for handling event */ +}; + + +char writer[] = "\ +/*\n\ + * This file was generated by the mkinit program.\n\ + */\n\ +\n"; + +char init[] = "\ +/*\n\ + * Initialization code.\n\ + */\n"; + +char reset[] = "\ +/*\n\ + * This routine is called when an error or an interrupt occurs in an\n\ + * interactive shell and control is returned to the main command loop.\n\ + */\n"; + +char shellproc[] = "\ +/*\n\ + * This routine is called to initialize the shell to run a shell procedure.\n\ + */\n"; + + +struct event event[] = { + {"INIT", "init", init}, + {"RESET", "reset", reset}, + {"SHELLPROC", "initshellproc", shellproc}, + {NULL, NULL} +}; + + +char *curfile; /* current file */ +int linno; /* current line */ +char *header_files[200]; /* list of header files */ +struct text defines; /* #define statements */ +struct text decls; /* declarations */ +int amiddecls; /* for formatting */ + + +void readfile(), doevent(), doinclude(), dodecl(), output(); +void addstr(), addchar(), writetext(); + +#define equal(s1, s2) (strcmp(s1, s2) == 0) + +FILE *ckfopen(); +char *savestr(); +void *ckmalloc __P((int)); +void error(); + +main(argc, argv) + char **argv; + { + char **ap; + int fd; + char c; + + if (argc < 2) + error("Usage: mkinit command file..."); + header_files[0] = "\"shell.h\""; + header_files[1] = "\"mystring.h\""; + for (ap = argv + 2 ; *ap ; ap++) + readfile(*ap); + output(); + if (file_changed()) { + unlink(OUTFILE); + link(OUTTEMP, OUTFILE); + unlink(OUTTEMP); + } else { + unlink(OUTTEMP); + if (touch(OUTOBJ)) + exit(0); /* no compilation necessary */ + } + printf("%s\n", argv[1]); + execl("/bin/sh", "sh", "-c", argv[1], (char *)0); + error("Can't exec shell"); +} + + +/* + * Parse an input file. + */ + +void +readfile(fname) + char *fname; + { + FILE *fp; + char line[1024]; + struct event *ep; + + fp = ckfopen(fname, "r"); + curfile = fname; + linno = 0; + amiddecls = 0; + while (fgets(line, sizeof line, fp) != NULL) { + linno++; + for (ep = event ; ep->name ; ep++) { + if (line[0] == ep->name[0] && match(ep->name, line)) { + doevent(ep, fp, fname); + break; + } + } + if (line[0] == 'I' && match("INCLUDE", line)) + doinclude(line); + if (line[0] == 'M' && match("MKINIT", line)) + dodecl(line, fp); + if (line[0] == '#' && gooddefine(line)) + addstr(line, &defines); + } + fclose(fp); +} + + +int +match(name, line) + char *name; + char *line; + { + register char *p, *q; + + p = name, q = line; + while (*p) { + if (*p++ != *q++) + return 0; + } + if (*q != '{' && *q != ' ' && *q != '\t' && *q != '\n') + return 0; + return 1; +} + + +int +gooddefine(line) + char *line; + { + register char *p; + + if (! match("#define", line)) + return 0; /* not a define */ + p = line + 7; + while (*p == ' ' || *p == '\t') + p++; + while (*p != ' ' && *p != '\t') { + if (*p == '(') + return 0; /* macro definition */ + p++; + } + while (*p != '\n' && *p != '\0') + p++; + if (p[-1] == '\\') + return 0; /* multi-line definition */ + return 1; +} + + +void +doevent(ep, fp, fname) + register struct event *ep; + FILE *fp; + char *fname; + { + char line[1024]; + int indent; + char *p; + + sprintf(line, "\n /* from %s: */\n", fname); + addstr(line, &ep->code); + addstr(" {\n", &ep->code); + for (;;) { + linno++; + if (fgets(line, sizeof line, fp) == NULL) + error("Unexpected EOF"); + if (equal(line, "}\n")) + break; + indent = 6; + for (p = line ; *p == '\t' ; p++) + indent += 8; + for ( ; *p == ' ' ; p++) + indent++; + if (*p == '\n' || *p == '#') + indent = 0; + while (indent >= 8) { + addchar('\t', &ep->code); + indent -= 8; + } + while (indent > 0) { + addchar(' ', &ep->code); + indent--; + } + addstr(p, &ep->code); + } + addstr(" }\n", &ep->code); +} + + +void +doinclude(line) + char *line; + { + register char *p; + char *name; + register char **pp; + + for (p = line ; *p != '"' && *p != '<' && *p != '\0' ; p++); + if (*p == '\0') + error("Expecting '\"' or '<'"); + name = p; + while (*p != ' ' && *p != '\t' && *p != '\n') + p++; + if (p[-1] != '"' && p[-1] != '>') + error("Missing terminator"); + *p = '\0'; + + /* name now contains the name of the include file */ + for (pp = header_files ; *pp && ! equal(*pp, name) ; pp++); + if (*pp == NULL) + *pp = savestr(name); +} + + +void +dodecl(line1, fp) + char *line1; + FILE *fp; + { + char line[1024]; + register char *p, *q; + + if (strcmp(line1, "MKINIT\n") == 0) { /* start of struct/union decl */ + addchar('\n', &decls); + do { + linno++; + if (fgets(line, sizeof line, fp) == NULL) + error("Unterminated structure declaration"); + addstr(line, &decls); + } while (line[0] != '}'); + amiddecls = 0; + } else { + if (! amiddecls) + addchar('\n', &decls); + q = NULL; + for (p = line1 + 6 ; *p != '=' && *p != '/' ; p++); + if (*p == '=') { /* eliminate initialization */ + for (q = p ; *q && *q != ';' ; q++); + if (*q == '\0') + q = NULL; + else { + while (p[-1] == ' ') + p--; + *p = '\0'; + } + } + addstr("extern", &decls); + addstr(line1 + 6, &decls); + if (q != NULL) + addstr(q, &decls); + amiddecls = 1; + } +} + + + +/* + * Write the output to the file OUTTEMP. + */ + +void +output() { + FILE *fp; + char **pp; + struct event *ep; + + fp = ckfopen(OUTTEMP, "w"); + fputs(writer, fp); + for (pp = header_files ; *pp ; pp++) + fprintf(fp, "#include %s\n", *pp); + fputs("\n\n\n", fp); + writetext(&defines, fp); + fputs("\n\n", fp); + writetext(&decls, fp); + for (ep = event ; ep->name ; ep++) { + fputs("\n\n\n", fp); + fputs(ep->comment, fp); + fprintf(fp, "\nvoid\n%s() {\n", ep->routine); + writetext(&ep->code, fp); + fprintf(fp, "}\n"); + } + fclose(fp); +} + + +/* + * Return true if the new output file is different from the old one. + */ + +int +file_changed() { + register FILE *f1, *f2; + register int c; + + if ((f1 = fopen(OUTFILE, "r")) == NULL + || (f2 = fopen(OUTTEMP, "r")) == NULL) + return 1; + while ((c = getc(f1)) == getc(f2)) { + if (c == EOF) + return 0; + } + return 1; +} + + +/* + * Touch a file. Returns 0 on failure, 1 on success. + */ + +int +touch(file) + char *file; + { + int fd; + char c; + + if ((fd = open(file, O_RDWR)) < 0) + return 0; + if (read(fd, &c, 1) != 1) { + close(fd); + return 0; + } + lseek(fd, 0L, 0); + write(fd, &c, 1); + close(fd); + return 1; +} + + + +/* + * A text structure is simply a block of text that is kept in memory. + * Addstr appends a string to the text struct, and addchar appends a single + * character. + */ + +void +addstr(s, text) + register char *s; + register struct text *text; + { + while (*s) { + if (--text->nleft < 0) + addchar(*s++, text); + else + *text->nextc++ = *s++; + } +} + + +void +addchar(c, text) + register struct text *text; + { + struct block *bp; + + if (--text->nleft < 0) { + bp = ckmalloc(sizeof *bp); + if (text->start == NULL) + text->start = bp; + else + text->last->next = bp; + text->last = bp; + text->nextc = bp->text; + text->nleft = BLOCKSIZE - 1; + } + *text->nextc++ = c; +} + +/* + * Write the contents of a text structure to a file. + */ +void +writetext(text, fp) + struct text *text; + FILE *fp; + { + struct block *bp; + + if (text->start != NULL) { + for (bp = text->start ; bp != text->last ; bp = bp->next) + fwrite(bp->text, sizeof (char), BLOCKSIZE, fp); + fwrite(bp->text, sizeof (char), BLOCKSIZE - text->nleft, fp); + } +} + +FILE * +ckfopen(file, mode) + char *file; + char *mode; + { + FILE *fp; + + if ((fp = fopen(file, mode)) == NULL) { + fprintf(stderr, "Can't open %s\n", file); + exit(2); + } + return fp; +} + +void * +ckmalloc(nbytes) { + register char *p; + char *malloc(); + + if ((p = malloc(nbytes)) == NULL) + error("Out of space"); + return p; +} + +char * +savestr(s) + char *s; + { + register char *p; + + p = ckmalloc(strlen(s) + 1); + strcpy(p, s); + return p; +} + +void +error(msg) + char *msg; + { + if (curfile != NULL) + fprintf(stderr, "%s:%d: ", curfile, linno); + fprintf(stderr, "%s\n", msg); + exit(2); +} diff --git a/bin/sh/mknodes.c b/bin/sh/mknodes.c new file mode 100644 index 000000000000..53c8508ee1ec --- /dev/null +++ b/bin/sh/mknodes.c @@ -0,0 +1,432 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1991 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)mknodes.c 5.1 (Berkeley) 3/7/91";*/ +static char rcsid[] = "mknodes.c,v 1.4 1993/08/01 18:58:08 mycroft Exp"; +#endif /* not lint */ + +/* + * This program reads the nodetypes file and nodes.c.pat file. It generates + * the files nodes.h and nodes.c. + */ + +#include <stdio.h> + + +#define MAXTYPES 50 /* max number of node types */ +#define MAXFIELDS 20 /* max fields in a structure */ +#define BUFLEN 100 /* size of character buffers */ + +/* field types */ +#define T_NODE 1 /* union node *field */ +#define T_NODELIST 2 /* struct nodelist *field */ +#define T_STRING 3 +#define T_INT 4 /* int field */ +#define T_OTHER 5 /* other */ +#define T_TEMP 6 /* don't copy this field */ + + +struct field { /* a structure field */ + char *name; /* name of field */ + int type; /* type of field */ + char *decl; /* declaration of field */ +}; + + +struct str { /* struct representing a node structure */ + char *tag; /* structure tag */ + int nfields; /* number of fields in the structure */ + struct field field[MAXFIELDS]; /* the fields of the structure */ + int done; /* set if fully parsed */ +}; + + +int ntypes; /* number of node types */ +char *nodename[MAXTYPES]; /* names of the nodes */ +struct str *nodestr[MAXTYPES]; /* type of structure used by the node */ +int nstr; /* number of structures */ +struct str str[MAXTYPES]; /* the structures */ +struct str *curstr; /* current structure */ + + +FILE *infp = stdin; +char line[1024]; +int linno; +char *linep; + + +char *savestr(); +#define equal(s1, s2) (strcmp(s1, s2) == 0) + + +main(argc, argv) + char **argv; + { + if (argc != 3) + error("usage: mknodes file\n"); + if ((infp = fopen(argv[1], "r")) == NULL) + error("Can't open %s", argv[1]); + while (readline()) { + if (line[0] == ' ' || line[0] == '\t') + parsefield(); + else if (line[0] != '\0') + parsenode(); + } + output(argv[2]); + return 0; +} + + + +parsenode() { + char name[BUFLEN]; + char tag[BUFLEN]; + struct str *sp; + + if (curstr && curstr->nfields > 0) + curstr->done = 1; + nextfield(name); + if (! nextfield(tag)) + error("Tag expected"); + if (*linep != '\0') + error("Garbage at end of line"); + nodename[ntypes] = savestr(name); + for (sp = str ; sp < str + nstr ; sp++) { + if (equal(sp->tag, tag)) + break; + } + if (sp >= str + nstr) { + sp->tag = savestr(tag); + sp->nfields = 0; + curstr = sp; + nstr++; + } + nodestr[ntypes] = sp; + ntypes++; +} + + +parsefield() { + char name[BUFLEN]; + char type[BUFLEN]; + char decl[2 * BUFLEN]; + struct field *fp; + + if (curstr == NULL || curstr->done) + error("No current structure to add field to"); + if (! nextfield(name)) + error("No field name"); + if (! nextfield(type)) + error("No field type"); + fp = &curstr->field[curstr->nfields]; + fp->name = savestr(name); + if (equal(type, "nodeptr")) { + fp->type = T_NODE; + sprintf(decl, "union node *%s", name); + } else if (equal(type, "nodelist")) { + fp->type = T_NODELIST; + sprintf(decl, "struct nodelist *%s", name); + } else if (equal(type, "string")) { + fp->type = T_STRING; + sprintf(decl, "char *%s", name); + } else if (equal(type, "int")) { + fp->type = T_INT; + sprintf(decl, "int %s", name); + } else if (equal(type, "other")) { + fp->type = T_OTHER; + } else if (equal(type, "temp")) { + fp->type = T_TEMP; + } else { + error("Unknown type %s", type); + } + if (fp->type == T_OTHER || fp->type == T_TEMP) { + skipbl(); + fp->decl = savestr(linep); + } else { + if (*linep) + error("Garbage at end of line"); + fp->decl = savestr(decl); + } + curstr->nfields++; +} + + +char writer[] = "\ +/*\n\ + * This file was generated by the mknodes program.\n\ + */\n\ +\n"; + +output(file) + char *file; + { + FILE *hfile; + FILE *cfile; + FILE *patfile; + int i; + struct str *sp; + struct field *fp; + char *p; + + if ((patfile = fopen(file, "r")) == NULL) + error("Can't open %s", file); + if ((hfile = fopen("nodes.h", "w")) == NULL) + error("Can't create nodes.h"); + if ((cfile = fopen("nodes.c", "w")) == NULL) + error("Can't create nodes.c"); + fputs(writer, hfile); + for (i = 0 ; i < ntypes ; i++) + fprintf(hfile, "#define %s %d\n", nodename[i], i); + fputs("\n\n\n", hfile); + for (sp = str ; sp < &str[nstr] ; sp++) { + fprintf(hfile, "struct %s {\n", sp->tag); + for (i = sp->nfields, fp = sp->field ; --i >= 0 ; fp++) { + fprintf(hfile, " %s;\n", fp->decl); + } + fputs("};\n\n\n", hfile); + } + fputs("union node {\n", hfile); + fprintf(hfile, " int type;\n"); + for (sp = str ; sp < &str[nstr] ; sp++) { + fprintf(hfile, " struct %s %s;\n", sp->tag, sp->tag); + } + fputs("};\n\n\n", hfile); + fputs("struct nodelist {\n", hfile); + fputs("\tstruct nodelist *next;\n", hfile); + fputs("\tunion node *n;\n", hfile); + fputs("};\n\n\n", hfile); + fputs("#ifdef __STDC__\n", hfile); + fputs("union node *copyfunc(union node *);\n", hfile); + fputs("void freefunc(union node *);\n", hfile); + fputs("#else\n", hfile); + fputs("union node *copyfunc();\n", hfile); + fputs("void freefunc();\n", hfile); + fputs("#endif\n", hfile); + + fputs(writer, cfile); + while (fgets(line, sizeof line, patfile) != NULL) { + for (p = line ; *p == ' ' || *p == '\t' ; p++); + if (equal(p, "%SIZES\n")) + outsizes(cfile); + else if (equal(p, "%CALCSIZE\n")) + outfunc(cfile, 1); + else if (equal(p, "%COPY\n")) + outfunc(cfile, 0); + else + fputs(line, cfile); + } +} + + + +outsizes(cfile) + FILE *cfile; + { + int i; + + fprintf(cfile, "static const short nodesize[%d] = {\n", ntypes); + for (i = 0 ; i < ntypes ; i++) { + fprintf(cfile, " ALIGN(sizeof (struct %s)),\n", nodestr[i]->tag); + } + fprintf(cfile, "};\n"); +} + + +outfunc(cfile, calcsize) + FILE *cfile; + { + struct str *sp; + struct field *fp; + int i; + + fputs(" if (n == NULL)\n", cfile); + if (calcsize) + fputs(" return;\n", cfile); + else + fputs(" return NULL;\n", cfile); + if (calcsize) + fputs(" funcblocksize += nodesize[n->type];\n", cfile); + else { + fputs(" new = funcblock;\n", cfile); + fputs(" funcblock += nodesize[n->type];\n", cfile); + } + fputs(" switch (n->type) {\n", cfile); + for (sp = str ; sp < &str[nstr] ; sp++) { + for (i = 0 ; i < ntypes ; i++) { + if (nodestr[i] == sp) + fprintf(cfile, " case %s:\n", nodename[i]); + } + for (i = sp->nfields ; --i >= 1 ; ) { + fp = &sp->field[i]; + switch (fp->type) { + case T_NODE: + if (calcsize) { + indent(12, cfile); + fprintf(cfile, "calcsize(n->%s.%s);\n", + sp->tag, fp->name); + } else { + indent(12, cfile); + fprintf(cfile, "new->%s.%s = copynode(n->%s.%s);\n", + sp->tag, fp->name, sp->tag, fp->name); + } + break; + case T_NODELIST: + if (calcsize) { + indent(12, cfile); + fprintf(cfile, "sizenodelist(n->%s.%s);\n", + sp->tag, fp->name); + } else { + indent(12, cfile); + fprintf(cfile, "new->%s.%s = copynodelist(n->%s.%s);\n", + sp->tag, fp->name, sp->tag, fp->name); + } + break; + case T_STRING: + if (calcsize) { + indent(12, cfile); + fprintf(cfile, "funcstringsize += strlen(n->%s.%s) + 1;\n", + sp->tag, fp->name); + } else { + indent(12, cfile); + fprintf(cfile, "new->%s.%s = nodesavestr(n->%s.%s);\n", + sp->tag, fp->name, sp->tag, fp->name); + } + break; + case T_INT: + case T_OTHER: + if (! calcsize) { + indent(12, cfile); + fprintf(cfile, "new->%s.%s = n->%s.%s;\n", + sp->tag, fp->name, sp->tag, fp->name); + } + break; + } + } + indent(12, cfile); + fputs("break;\n", cfile); + } + fputs(" };\n", cfile); + if (! calcsize) + fputs(" new->type = n->type;\n", cfile); +} + + +indent(amount, fp) + FILE *fp; + { + while (amount >= 8) { + putc('\t', fp); + amount -= 8; + } + while (--amount >= 0) { + putc(' ', fp); + } +} + + +int +nextfield(buf) + char *buf; + { + register char *p, *q; + + p = linep; + while (*p == ' ' || *p == '\t') + p++; + q = buf; + while (*p != ' ' && *p != '\t' && *p != '\0') + *q++ = *p++; + *q = '\0'; + linep = p; + return (q > buf); +} + + +skipbl() { + while (*linep == ' ' || *linep == '\t') + linep++; +} + + +int +readline() { + register char *p; + + if (fgets(line, 1024, infp) == NULL) + return 0; + for (p = line ; *p != '#' && *p != '\n' && *p != '\0' ; p++); + while (p > line && (p[-1] == ' ' || p[-1] == '\t')) + p--; + *p = '\0'; + linep = line; + linno++; + if (p - line > BUFLEN) + error("Line too long"); + return 1; +} + + + +error(msg, a1, a2, a3, a4, a5, a6) + char *msg; + { + fprintf(stderr, "line %d: ", linno); + fprintf(stderr, msg, a1, a2, a3, a4, a5, a6); + putc('\n', stderr); + exit(2); +} + + + +char * +savestr(s) + char *s; + { + register char *p; + char *malloc(); + + if ((p = malloc(strlen(s) + 1)) == NULL) + error("Out of space"); + strcpy(p, s); + return p; +} diff --git a/bin/sh/mksignames.c b/bin/sh/mksignames.c new file mode 100644 index 000000000000..b308e03bdb00 --- /dev/null +++ b/bin/sh/mksignames.c @@ -0,0 +1,198 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1991 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)mksignames.c 5.1 (Berkeley) 3/7/91";*/ +static char rcsid[] = "mksignames.c,v 1.4 1993/08/01 18:58:07 mycroft Exp"; +#endif /* not lint */ + +/* + * This program generates the signames.h and signames.c files. + */ +#include <stdio.h> +#include <signal.h> + + + +struct sig { + int signo; /* signal number */ + char *name; /* signal name (without leading "SIG") */ + char *mesg; /* description */ +}; + + +struct sig sigtab[] = { + SIGHUP, "HUP", "Hangup", + SIGINT, "INT", "Interrupt", /* normally don't print message */ + SIGQUIT, "QUIT", "Quit", + SIGILL, "ILL", "Illegal instruction", + SIGTRAP, "TRAP", "Trace/BPT trap", +#ifdef SIGABRT + SIGABRT, "ABRT", "abort", +#endif +#if defined(SIGIOT) && (! defined(SIGABRT) || SIGABRT != SIGIOT) + SIGIOT, "IOT", "abort", +#endif +#ifdef SIGEMT + SIGEMT, "EMT", "EMT trap", +#endif + SIGFPE, "FPE", "Floating exception", + SIGKILL, "KILL", "Killed", + SIGBUS, "BUS", "Bus error", + SIGSEGV, "SEGV", "Memory fault", + SIGSYS, "SYS", "Bad system call", + SIGPIPE, "PIPE", "Broken pipe", /* normally don't print message */ + SIGALRM, "ALRM", "Alarm call", + SIGTERM, "TERM", "Terminated", +#ifdef SIGUSR1 + SIGUSR1, "USR1", "User signal 1", +#endif +#ifdef SIGUSR2 + SIGUSR2, "USR2", "User signal 2", +#endif +#ifdef SIGCLD + SIGCLD, "CLD", NULL, +#endif +#if defined(SIGCHLD) && ! defined(SIGCLD) + SIGCHLD, "CLD", NULL, +#endif +#ifdef SIGPWR + SIGPWR, "PWR", "Power fail", +#endif +#ifdef SIGPOLL + SIGPOLL, "POLL", "Poll", +#endif + /* Now for the BSD signals */ +#ifdef SIGURG + SIGURG, "URG", NULL, +#endif +#ifdef SIGSTOP + SIGSTOP, "STOP", "Stopped", +#endif +#ifdef SIGTSTP + SIGTSTP, "TSTP", "Stopped", +#endif +#ifdef SIGCONT + SIGCONT, "CONT", NULL, +#endif +#ifdef SIGTTIN + SIGTTIN, "TTIN", "Stopped (input)", +#endif +#ifdef SIGTTOU + SIGTTOU, "TTOU", "Stopped (output)", +#endif +#ifdef SIGIO + SIGIO, "IO", NULL, +#endif +#ifdef SIGXCPU + SIGXCPU, "XCPU", "Time limit exceeded", +#endif +#ifdef SIGXFSZ + SIGXFSZ, "XFSZ", NULL, +#endif +#ifdef SIGVTALARM + SIGVTALARM, "VTALARM", "Virtual alarm", +#endif +#ifdef SIGPROF + SIGPROF, "PROF", "Profiling alarm", +#endif +#ifdef SIGWINCH + SIGWINCH, "WINCH", NULL, +#endif + 0, NULL, NULL +}; + + +#define MAXSIG 64 + + +char *sigmesg[MAXSIG + 1]; + + +char writer[] = "\ +/*\n\ + * This file was generated by the mksignames program.\n\ + */\n\ +\n"; + + + +main(argc, argv) char **argv; { + FILE *cfile, *hfile; + struct sig *sigp; + int maxsig; + int i; + + if ((cfile = fopen("signames.c", "w")) == NULL) { + fputs("Can't create signames.c\n", stderr); + exit(2); + } + if ((hfile = fopen("signames.h", "w")) == NULL) { + fputs("Can't create signames.h\n", stderr); + exit(2); + } + maxsig = 0; + for (sigp = sigtab ; sigp->signo != 0 ; sigp++) { + if (sigp->signo < 0 || sigp->signo > MAXSIG) + continue; + sigmesg[sigp->signo] = sigp->mesg; + if (maxsig < sigp->signo) + maxsig = sigp->signo; + } + + fputs(writer, hfile); + fprintf(hfile, "#define MAXSIG %d\n\n", maxsig); + fprintf(hfile, "extern char *const sigmesg[MAXSIG+1];\n"); + + fputs(writer, cfile); + fprintf(cfile, "#include \"shell.h\"\n\n"); + fprintf(cfile, "char *const sigmesg[%d] = {\n", maxsig + 1); + for (i = 0 ; i <= maxsig ; i++) { + if (sigmesg[i] == NULL) { + fprintf(cfile, " 0,\n"); + } else { + fprintf(cfile, " \"%s\",\n", sigmesg[i]); + } + } + fprintf(cfile, "};\n"); + exit(0); +} diff --git a/bin/sh/mksyntax.c b/bin/sh/mksyntax.c new file mode 100644 index 000000000000..e0301d66d71b --- /dev/null +++ b/bin/sh/mksyntax.c @@ -0,0 +1,357 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1991 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)mksyntax.c 5.2 (Berkeley) 3/8/91";*/ +static char rcsid[] = "mksyntax.c,v 1.4 1993/08/01 18:58:06 mycroft Exp"; +#endif /* not lint */ + +/* + * This program creates syntax.h and syntax.c. + */ + +#include <stdio.h> +#include "parser.h" + + +struct synclass { + char *name; + char *comment; +}; + +/* Syntax classes */ +struct synclass synclass[] = { + "CWORD", "character is nothing special", + "CNL", "newline character", + "CBACK", "a backslash character", + "CSQUOTE", "single quote", + "CDQUOTE", "double quote", + "CENDQUOTE", "a terminating quote", + "CBQUOTE", "backwards single quote", + "CVAR", "a dollar sign", + "CENDVAR", "a '}' character", + "CEOF", "end of file", + "CCTL", "like CWORD, except it must be escaped", + "CSPCL", "these terminate a word", + NULL, NULL +}; + + +/* + * Syntax classes for is_ functions. Warning: if you add new classes + * you may have to change the definition of the is_in_name macro. + */ +struct synclass is_entry[] = { + "ISDIGIT", "a digit", + "ISUPPER", "an upper case letter", + "ISLOWER", "a lower case letter", + "ISUNDER", "an underscore", + "ISSPECL", "the name of a special parameter", + NULL, NULL, +}; + +char writer[] = "\ +/*\n\ + * This file was generated by the mksyntax program.\n\ + */\n\ +\n"; + + +FILE *cfile; +FILE *hfile; +char *syntax[513]; +int base; +int size; /* number of values which a char variable can have */ +int nbits; /* number of bits in a character */ +int digit_contig; /* true if digits are contiguous */ + + +main() { + char c; + char d; + int sign; + int i; + char buf[80]; + int pos; + static char digit[] = "0123456789"; + + /* Create output files */ + if ((cfile = fopen("syntax.c", "w")) == NULL) { + perror("syntax.c"); + exit(2); + } + if ((hfile = fopen("syntax.h", "w")) == NULL) { + perror("syntax.h"); + exit(2); + } + fputs(writer, hfile); + fputs(writer, cfile); + + /* Determine the characteristics of chars. */ + c = -1; + if (c < 0) + sign = 1; + else + sign = 0; + for (nbits = 1 ; ; nbits++) { + d = (1 << nbits) - 1; + if (d == c) + break; + } + printf("%s %d bit chars\n", sign? "signed" : "unsigned", nbits); + if (nbits > 9) { + fputs("Characters can't have more than 9 bits\n", stderr); + exit(2); + } + size = (1 << nbits) + 1; + base = 1; + if (sign) + base += 1 << (nbits - 1); + digit_contig = 1; + for (i = 0 ; i < 10 ; i++) { + if (digit[i] != '0' + i) + digit_contig = 0; + } + + fputs("#include <sys/cdefs.h>\n", hfile); + + /* Generate the #define statements in the header file */ + fputs("/* Syntax classes */\n", hfile); + for (i = 0 ; synclass[i].name ; i++) { + sprintf(buf, "#define %s %d", synclass[i].name, i); + fputs(buf, hfile); + for (pos = strlen(buf) ; pos < 32 ; pos = pos + 8 &~ 07) + putc('\t', hfile); + fprintf(hfile, "/* %s */\n", synclass[i].comment); + } + putc('\n', hfile); + fputs("/* Syntax classes for is_ functions */\n", hfile); + for (i = 0 ; is_entry[i].name ; i++) { + sprintf(buf, "#define %s %#o", is_entry[i].name, 1 << i); + fputs(buf, hfile); + for (pos = strlen(buf) ; pos < 32 ; pos = pos + 8 &~ 07) + putc('\t', hfile); + fprintf(hfile, "/* %s */\n", is_entry[i].comment); + } + putc('\n', hfile); + fprintf(hfile, "#define SYNBASE %d\n", base); + fprintf(hfile, "#define PEOF %d\n\n", -base); + putc('\n', hfile); + fputs("#define BASESYNTAX (basesyntax + SYNBASE)\n", hfile); + fputs("#define DQSYNTAX (dqsyntax + SYNBASE)\n", hfile); + fputs("#define SQSYNTAX (sqsyntax + SYNBASE)\n", hfile); + putc('\n', hfile); + output_type_macros(); /* is_digit, etc. */ + putc('\n', hfile); + + /* Generate the syntax tables. */ + fputs("#include \"shell.h\"\n", cfile); + fputs("#include \"syntax.h\"\n\n", cfile); + init(); + fputs("/* syntax table used when not in quotes */\n", cfile); + add("\n", "CNL"); + add("\\", "CBACK"); + add("'", "CSQUOTE"); + add("\"", "CDQUOTE"); + add("`", "CBQUOTE"); + add("$", "CVAR"); + add("}", "CENDVAR"); + add("<>();&| \t", "CSPCL"); + print("basesyntax"); + init(); + fputs("\n/* syntax table used when in double quotes */\n", cfile); + add("\n", "CNL"); + add("\\", "CBACK"); + add("\"", "CENDQUOTE"); + add("`", "CBQUOTE"); + add("$", "CVAR"); + add("}", "CENDVAR"); + add("!*?[=", "CCTL"); + print("dqsyntax"); + init(); + fputs("\n/* syntax table used when in single quotes */\n", cfile); + add("\n", "CNL"); + add("'", "CENDQUOTE"); + add("!*?[=", "CCTL"); + print("sqsyntax"); + filltable("0"); + fputs("\n/* character classification table */\n", cfile); + add("0123456789", "ISDIGIT"); + add("abcdefghijklmnopqrstucvwxyz", "ISLOWER"); + add("ABCDEFGHIJKLMNOPQRSTUCVWXYZ", "ISUPPER"); + add("_", "ISUNDER"); + add("#?$!-*@", "ISSPECL"); + print("is_type"); + if (! digit_contig) + digit_convert(); + exit(0); +} + + + +/* + * Clear the syntax table. + */ + +filltable(dftval) + char *dftval; + { + int i; + + for (i = 0 ; i < size ; i++) + syntax[i] = dftval; +} + + +/* + * Initialize the syntax table with default values. + */ + +init() { + filltable("CWORD"); + syntax[0] = "CEOF"; + syntax[base + CTLESC] = "CCTL"; + syntax[base + CTLVAR] = "CCTL"; + syntax[base + CTLENDVAR] = "CCTL"; + syntax[base + CTLBACKQ] = "CCTL"; + syntax[base + CTLBACKQ + CTLQUOTE] = "CCTL"; +} + + +/* + * Add entries to the syntax table. + */ + +add(p, type) + char *p, *type; + { + while (*p) + syntax[*p++ + base] = type; +} + + + +/* + * Output the syntax table. + */ + +print(name) + char *name; + { + int i; + int col; + + fprintf(hfile, "extern const char %s[];\n", name); + fprintf(cfile, "const char %s[%d] = {\n", name, size); + col = 0; + for (i = 0 ; i < size ; i++) { + if (i == 0) { + fputs(" ", cfile); + } else if ((i & 03) == 0) { + fputs(",\n ", cfile); + col = 0; + } else { + putc(',', cfile); + while (++col < 9 * (i & 03)) + putc(' ', cfile); + } + fputs(syntax[i], cfile); + col += strlen(syntax[i]); + } + fputs("\n};\n", cfile); +} + + + +/* + * Output character classification macros (e.g. is_digit). If digits are + * contiguous, we can test for them quickly. + */ + +char *macro[] = { + "#define is_digit(c)\t((is_type+SYNBASE)[c] & ISDIGIT)", + "#define is_alpha(c)\t((is_type+SYNBASE)[c] & (ISUPPER|ISLOWER))", + "#define is_name(c)\t((is_type+SYNBASE)[c] & (ISUPPER|ISLOWER|ISUNDER))", + "#define is_in_name(c)\t((is_type+SYNBASE)[c] & (ISUPPER|ISLOWER|ISUNDER|ISDIGIT))", + "#define is_special(c)\t((is_type+SYNBASE)[c] & (ISSPECL|ISDIGIT))", + NULL +}; + +output_type_macros() { + char **pp; + + if (digit_contig) + macro[0] = "#define is_digit(c)\t((unsigned)((c) - '0') <= 9)"; + for (pp = macro ; *pp ; pp++) + fprintf(hfile, "%s\n", *pp); + if (digit_contig) + fputs("#define digit_val(c)\t((c) - '0')\n", hfile); + else + fputs("#define digit_val(c)\t(digit_value[c])\n", hfile); +} + + + +/* + * Output digit conversion table (if digits are not contiguous). + */ + +digit_convert() { + int maxdigit; + static char digit[] = "0123456789"; + char *p; + int i; + + maxdigit = 0; + for (p = digit ; *p ; p++) + if (*p > maxdigit) + maxdigit = *p; + fputs("extern const char digit_value[];\n", hfile); + fputs("\n\nconst char digit_value[] = {\n", cfile); + for (i = 0 ; i <= maxdigit ; i++) { + for (p = digit ; *p && *p != i ; p++); + if (*p == '\0') + p = digit; + fprintf(cfile, " %d,\n", p - digit); + } + fputs("};\n", cfile); +} diff --git a/bin/sh/mktokens b/bin/sh/mktokens new file mode 100644 index 000000000000..0e36b258c21a --- /dev/null +++ b/bin/sh/mktokens @@ -0,0 +1,95 @@ +#!/bin/sh - +# +# Copyright (c) 1991 The Regents of the University of California. +# All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. 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. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. +# +# @(#)mktokens 5.1 (Berkeley) 3/7/91 +# +# /b/source/CVS/src/bin/sh/mktokens,v 1.3 1993/03/23 00:28:45 cgd Exp + +# The following is a list of tokens. The second column is nonzero if the +# token marks the end of a list. The third column is the name to print in +# error messages. + +cat > /tmp/ka$$ <<\! +TEOF 1 end of file +TNL 0 newline +TSEMI 0 ";" +TBACKGND 0 "&" +TAND 0 "&&" +TOR 0 "||" +TPIPE 0 "|" +TLP 0 "(" +TRP 1 ")" +TENDCASE 1 ";;" +TENDBQUOTE 1 "`" +TREDIR 0 redirection +TWORD 0 word +TIF 0 "if" +TTHEN 1 "then" +TELSE 1 "else" +TELIF 1 "elif" +TFI 1 "fi" +TWHILE 0 "while" +TUNTIL 0 "until" +TFOR 0 "for" +TDO 1 "do" +TDONE 1 "done" +TBEGIN 0 "{" +TEND 1 "}" +TCASE 0 "case" +TESAC 1 "esac" +! +nl=`wc -l /tmp/ka$$` +exec > token.def +awk '{print "#define " $1 " " NR-1}' /tmp/ka$$ +echo ' +/* Array indicating which tokens mark the end of a list */ +const char tokendlist[] = {' +awk '{print "\t" $2 ","}' /tmp/ka$$ +echo '}; + +char *const tokname[] = {' +sed -e 's/"/\\"/g' \ + -e 's/[^ ]*[ ][ ]*[^ ]*[ ][ ]*\(.*\)/ "\1",/' \ + /tmp/ka$$ +echo '}; +' +sed 's/"//g' /tmp/ka$$ | awk ' +/TIF/{print "#define KWDOFFSET " NR-1; print ""; print "char *const parsekwd[] = {"} +/TIF/,/neverfound/{print " \"" $3 "\","}' +echo ' 0 +};' + +rm /tmp/ka$$ diff --git a/bin/sh/mt b/bin/sh/mt new file mode 100644 index 000000000000..f07a32e28d7c --- /dev/null +++ b/bin/sh/mt @@ -0,0 +1,78 @@ +#!/bin/sh - +# +# Copyright (c) 1991 The Regents of the University of California. +# All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. 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. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. +# +# @(#)mktokens 5.1 (Berkeley) 3/7/91 +# +# /b/source/CVS/src/bin/sh/mt,v 1.3 1993/03/23 00:28:48 cgd Exp + +# The following is a list of tokens. The second column is nonzero if the +# token marks the end of a list. The third column is the name to print in +# error messages. + +cat > /tmp/ka$$ <<\! +TEOF 1 end of file +TNL 0 newline +TSEMI 0 ";" +TBACKGND 0 "&" +TAND 0 "&&" +TOR 0 "||" +TPIPE 0 "|" +TLP 0 "(" +TRP 1 ")" +TENDCASE 1 ";;" +TENDBQUOTE 1 "`" +TREDIR 0 redirection +TWORD 0 word +TIF 0 "if" +TTHEN 1 "then" +TELSE 1 "else" +TELIF 1 "elif" +TFI 1 "fi" +TWHILE 0 "while" +TUNTIL 0 "until" +TFOR 0 "for" +TDO 1 "do" +TDONE 1 "done" +TBEGIN 0 "{" +TEND 1 "}" +TCASE 0 "case" +TESAC 1 "esac" +! +nl=`wc -l /tmp/ka$$` +exec +awk "-F " '{print $1 "#define " NR-1}' /tmp/ka$$ + +rm /tmp/ka$$ diff --git a/bin/sh/mystring.c b/bin/sh/mystring.c new file mode 100644 index 000000000000..eb9d1db37dc2 --- /dev/null +++ b/bin/sh/mystring.c @@ -0,0 +1,175 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)mystring.c 5.1 (Berkeley) 3/7/91";*/ +static char rcsid[] = "mystring.c,v 1.4 1993/08/01 18:58:05 mycroft Exp"; +#endif /* not lint */ + +/* + * String functions. + * + * equal(s1, s2) Return true if strings are equal. + * scopy(from, to) Copy a string. + * scopyn(from, to, n) Like scopy, but checks for overflow. + * strchr(s, c) Find first occurance of c in s. + * bcopy(from, to, n) Copy a block of memory. + * number(s) Convert a string of digits to an integer. + * is_number(s) Return true if s is a string of digits. + */ + +#include "shell.h" +#include "syntax.h" +#include "error.h" +#include "mystring.h" + + +char nullstr[1]; /* zero length string */ + + +/* + * scopyn - copy a string from "from" to "to", truncating the string + * if necessary. "To" is always nul terminated, even if + * truncation is performed. "Size" is the size of "to". + */ + +void +scopyn(from, to, size) + register char const *from; + register char *to; + register int size; + { + + while (--size > 0) { + if ((*to++ = *from++) == '\0') + return; + } + *to = '\0'; +} + + +/* + * strchr - find first occurrence of a character in a string. + */ + +#ifndef SYS5 +char * +mystrchr(s, charwanted) + char const *s; + register char charwanted; + { + register char const *scan; + + /* + * The odd placement of the two tests is so NUL is findable. + */ + for (scan = s ; *scan != charwanted ; ) /* ++ moved down for opt. */ + if (*scan++ == '\0') + return NULL; + return (char *)scan; +} +#endif + + + +/* + * bcopy - copy bytes + * + * This routine was derived from code by Henry Spencer. + */ + +void +mybcopy(src, dst, length) + pointer dst; + const pointer src; + register int length; + { + register char *d = dst; + register char *s = src; + + while (--length >= 0) + *d++ = *s++; +} + + +/* + * prefix -- see if pfx is a prefix of string. + */ + +int +prefix(pfx, string) + register char const *pfx; + register char const *string; + { + while (*pfx) { + if (*pfx++ != *string++) + return 0; + } + return 1; +} + + +/* + * Convert a string of digits to an integer, printing an error message on + * failure. + */ + +int +number(s) + const char *s; + { + + if (! is_number(s)) + error2("Illegal number", (char *)s); + return atoi(s); +} + + + +/* + * Check for a valid number. This should be elsewhere. + */ + +int +is_number(p) + register const char *p; + { + do { + if (! is_digit(*p)) + return 0; + } while (*++p != '\0'); + return 1; +} diff --git a/bin/sh/mystring.h b/bin/sh/mystring.h new file mode 100644 index 000000000000..9620e7ac7238 --- /dev/null +++ b/bin/sh/mystring.h @@ -0,0 +1,70 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * from: @(#)mystring.h 5.1 (Berkeley) 3/7/91 + * mystring.h,v 1.4 1993/08/01 18:58:39 mycroft Exp + */ + +#ifndef SYSV +#define strchr mystrchr +#endif + +#ifdef __STDC__ +void scopyn(const char *, char *, int); +char *strchr(const char *, int); +void mybcopy(const pointer, pointer, int); +int prefix(const char *, const char *); +int number(const char *); +int is_number(const char *); +int strcmp(const char *, const char *); /* from C library */ +char *strcpy(char *, const char *); /* from C library */ +int strlen(const char *); /* from C library */ +char *strcat(char *, const char *); /* from C library */ +#else +void scopyn(); +char *strchr(); +void mybcopy(); +int prefix(); +int number(); +int is_number(); +int strcmp(); +char *strcpy(); +int strlen(); +char *strcat(); +#endif + +#define equal(s1, s2) (strcmp(s1, s2) == 0) +#define scopy(s1, s2) ((void)strcpy(s2, s1)) +#define bcopy(src, dst, n) mybcopy((pointer)(src), (pointer)(dst), n) diff --git a/bin/sh/nodes.c.pat b/bin/sh/nodes.c.pat new file mode 100644 index 000000000000..5283a811147f --- /dev/null +++ b/bin/sh/nodes.c.pat @@ -0,0 +1,179 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * @(#)nodes.c.pat 5.2 (Berkeley) 3/8/91 + * + * /b/source/CVS/src/bin/sh/nodes.c.pat,v 1.3 1993/03/23 00:28:55 cgd Exp + */ + +/* + * Routine for dealing with parsed shell commands. + */ + +#include "shell.h" +#include "nodes.h" +#include "memalloc.h" +#include "machdep.h" +#include "mystring.h" + + +int funcblocksize; /* size of structures in function */ +int funcstringsize; /* size of strings in node */ +#ifdef __STDC__ +pointer funcblock; /* block to allocate function from */ +#else +char *funcblock; /* block to allocate function from */ +#endif +char *funcstring; /* block to allocate strings from */ + +%SIZES + + +#ifdef __STDC__ +STATIC void calcsize(union node *); +STATIC void sizenodelist(struct nodelist *); +STATIC union node *copynode(union node *); +STATIC struct nodelist *copynodelist(struct nodelist *); +STATIC char *nodesavestr(char *); +#else +STATIC void calcsize(); +STATIC void sizenodelist(); +STATIC union node *copynode(); +STATIC struct nodelist *copynodelist(); +STATIC char *nodesavestr(); +#endif + + + +/* + * Make a copy of a parse tree. + */ + +union node * +copyfunc(n) + union node *n; + { + if (n == NULL) + return NULL; + funcblocksize = 0; + funcstringsize = 0; + calcsize(n); + funcblock = ckmalloc(funcblocksize + funcstringsize); + funcstring = funcblock + funcblocksize; + return copynode(n); +} + + + +STATIC void +calcsize(n) + union node *n; + { + %CALCSIZE +} + + + +STATIC void +sizenodelist(lp) + struct nodelist *lp; + { + while (lp) { + funcblocksize += ALIGN(sizeof (struct nodelist)); + calcsize(lp->n); + lp = lp->next; + } +} + + + +STATIC union node * +copynode(n) + union node *n; + { + union node *new; + + %COPY + return new; +} + + +STATIC struct nodelist * +copynodelist(lp) + struct nodelist *lp; + { + struct nodelist *start; + struct nodelist **lpp; + + lpp = &start; + while (lp) { + *lpp = funcblock; + funcblock += ALIGN(sizeof (struct nodelist)); + (*lpp)->n = copynode(lp->n); + lp = lp->next; + lpp = &(*lpp)->next; + } + *lpp = NULL; + return start; +} + + + +STATIC char * +nodesavestr(s) + char *s; + { + register char *p = s; + register char *q = funcstring; + char *rtn = funcstring; + + while (*q++ = *p++); + funcstring = q; + return rtn; +} + + + +/* + * Free a parse tree. + */ + +void +freefunc(n) + union node *n; + { + if (n) + ckfree(n); +} diff --git a/bin/sh/nodetypes b/bin/sh/nodetypes new file mode 100644 index 000000000000..6226176ce692 --- /dev/null +++ b/bin/sh/nodetypes @@ -0,0 +1,142 @@ +#!/bin/sh - +# +# Copyright (c) 1991 The Regents of the University of California. +# All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. 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. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. +# +# @(#)nodetypes 5.1 (Berkeley) 3/7/91 +# +# /b/source/CVS/src/bin/sh/nodetypes,v 1.3 1993/03/23 00:28:58 cgd Exp + +# This file describes the nodes used in parse trees. Unindented lines +# contain a node type followed by a structure tag. Subsequent indented +# lines specify the fields of the structure. Several node types can share +# the same structure, in which case the fields of the structure should be +# specified only once. +# +# A field of a structure is described by the name of the field followed +# by a type. The currently implemented types are: +# nodeptr - a pointer to a node +# nodelist - a pointer to a list of nodes +# string - a pointer to a nul terminated string +# int - an integer +# other - any type that can be copied by assignment +# temp - a field that doesn't have to be copied when the node is copied +# The last two types should be followed by the text of a C declaration for +# the field. + +NSEMI nbinary # two commands separated by a semicolon + type int + ch1 nodeptr # the first child + ch2 nodeptr # the second child + +NCMD ncmd # a simple command + type int + backgnd int # set to run command in background + args nodeptr # the arguments + redirect nodeptr # list of file redirections + +NPIPE npipe # a pipeline + type int + backgnd int # set to run pipeline in background + cmdlist nodelist # the commands in the pipeline + +NREDIR nredir # redirection (of a compex command) + type int + n nodeptr # the command + redirect nodeptr # list of file redirections + +NBACKGND nredir # run command in background +NSUBSHELL nredir # run command in a subshell + +NAND nbinary # the && operator +NOR nbinary # the || operator + +NIF nif # the if statement. Elif clauses are handled + type int # using multiple if nodes. + test nodeptr # if test + ifpart nodeptr # then ifpart + elsepart nodeptr # else elsepart + +NWHILE nbinary # the while statement. First child is the test +NUNTIL nbinary # the until statement + +NFOR nfor # the for statement + type int + args nodeptr # for var in args + body nodeptr # do body; done + var string # the for variable + +NCASE ncase # a case statement + type int + expr nodeptr # the word to switch on + cases nodeptr # the list of cases (NCLIST nodes) + +NCLIST nclist # a case + type int + next nodeptr # the next case in list + pattern nodeptr # list of patterns for this case + body nodeptr # code to execute for this case + + +NDEFUN narg # define a function. The "next" field contains + # the body of the function. + +NARG narg # represents a word + type int + next nodeptr # next word in list + text string # the text of the word + backquote nodelist # list of commands in back quotes + +NTO nfile # fd> fname +NFROM nfile # fd< fname +NAPPEND nfile # fd>> fname + type int + next nodeptr # next redirection in list + fd int # file descriptor being redirected + fname nodeptr # file name, in a NARG node + expfname temp char *expfname # actual file name + +NTOFD ndup # fd<&dupfd +NFROMFD ndup # fd>&dupfd + type int + next nodeptr # next redirection in list + fd int # file descriptor being redirected + dupfd int # file descriptor to duplicate + +NHERE nhere # fd<<\! +NXHERE nhere # fd<<! + type int + next nodeptr # next redirection in list + fd int # file descriptor being redirected + doc nodeptr # input to command (NARG node) diff --git a/bin/sh/options.c b/bin/sh/options.c new file mode 100644 index 000000000000..560e433c4e0e --- /dev/null +++ b/bin/sh/options.c @@ -0,0 +1,389 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)options.c 5.2 (Berkeley) 3/13/91";*/ +static char rcsid[] = "options.c,v 1.4 1993/08/01 18:58:04 mycroft Exp"; +#endif /* not lint */ + +#include "shell.h" +#define DEFINE_OPTIONS +#include "options.h" +#undef DEFINE_OPTIONS +#include "nodes.h" /* for other header files */ +#include "eval.h" +#include "jobs.h" +#include "input.h" +#include "output.h" +#include "trap.h" +#include "var.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" + +char *arg0; /* value of $0 */ +struct shparam shellparam; /* current positional parameters */ +char **argptr; /* argument list for builtin commands */ +char *optarg; /* set by nextopt (like getopt) */ +char *optptr; /* used by nextopt */ + +char *minusc; /* argument to -c option */ + + +#ifdef __STDC__ +STATIC void options(int); +STATIC void setoption(int, int); +#else +STATIC void options(); +STATIC void setoption(); +#endif + + + +/* + * Process the shell command line arguments. + */ + +void +procargs(argc, argv) + char **argv; + { + char *p; + + argptr = argv; + if (argc > 0) + argptr++; + for (p = optval ; p < optval + sizeof optval - 1 ; p++) + *p = 2; + options(1); + if (*argptr == NULL && minusc == NULL) + sflag = 1; + if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1)) + iflag = 1; + if (jflag == 2) + jflag = iflag; + for (p = optval ; p < optval + sizeof optval - 1 ; p++) + if (*p == 2) + *p = 0; + arg0 = argv[0]; + if (sflag == 0 && minusc == NULL) { + commandname = arg0 = *argptr++; + setinputfile(commandname, 0); + } + shellparam.p = argptr; + /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */ + while (*argptr) { + shellparam.nparam++; + argptr++; + } + setinteractive(iflag); + setjobctl(jflag); +} + + + +/* + * Process shell options. The global variable argptr contains a pointer + * to the argument list; we advance it past the options. + */ + +STATIC void +options(cmdline) { + register char *p; + int val; + int c; + + if (cmdline) + minusc = NULL; + while ((p = *argptr) != NULL) { + argptr++; + if ((c = *p++) == '-') { + val = 1; + if (p[0] == '\0' || p[0] == '-' && p[1] == '\0') { + if (!cmdline) { + /* "-" means turn off -x and -v */ + if (p[0] == '\0') + xflag = vflag = 0; + /* "--" means reset params */ + else if (*argptr == NULL) + setparam(argptr); + } + break; /* "-" or "--" terminates options */ + } + } else if (c == '+') { + val = 0; + } else { + argptr--; + break; + } + while ((c = *p++) != '\0') { + if (c == 'c' && cmdline) { + char *q; +#ifdef NOHACK /* removing this code allows sh -ce 'foo' for compat */ + if (*p == '\0') +#endif + q = *argptr++; + if (q == NULL || minusc != NULL) + error("Bad -c option"); + minusc = q; +#ifdef NOHACK + break; +#endif + } else { + setoption(c, val); + } + } + if (! cmdline) + break; + } +} + + +STATIC void +setoption(flag, val) + char flag; + int val; + { + register char *p; + + if ((p = strchr(optchar, flag)) == NULL) + error("Illegal option -%c", flag); + optval[p - optchar] = val; +} + + + +#ifdef mkinit +INCLUDE "options.h" + +SHELLPROC { + char *p; + + for (p = optval ; p < optval + sizeof optval ; p++) + *p = 0; +} +#endif + + +/* + * Set the shell parameters. + */ + +void +setparam(argv) + char **argv; + { + char **newparam; + char **ap; + int nparam; + + for (nparam = 0 ; argv[nparam] ; nparam++); + ap = newparam = ckmalloc((nparam + 1) * sizeof *ap); + while (*argv) { + *ap++ = savestr(*argv++); + } + *ap = NULL; + freeparam(&shellparam); + shellparam.malloc = 1; + shellparam.nparam = nparam; + shellparam.p = newparam; + shellparam.optnext = NULL; +} + + +/* + * Free the list of positional parameters. + */ + +void +freeparam(param) + struct shparam *param; + { + char **ap; + + if (param->malloc) { + for (ap = param->p ; *ap ; ap++) + ckfree(*ap); + ckfree(param->p); + } +} + + + +/* + * The shift builtin command. + */ + +shiftcmd(argc, argv) char **argv; { + int n; + char **ap1, **ap2; + + n = 1; + if (argc > 1) + n = number(argv[1]); + if (n > shellparam.nparam) + n = shellparam.nparam; + INTOFF; + shellparam.nparam -= n; + for (ap1 = shellparam.p ; --n >= 0 ; ap1++) { + if (shellparam.malloc) + ckfree(*ap1); + } + ap2 = shellparam.p; + while ((*ap2++ = *ap1++) != NULL); + shellparam.optnext = NULL; + INTON; + return 0; +} + + + +/* + * The set command builtin. + */ + +setcmd(argc, argv) char **argv; { + if (argc == 1) + return showvarscmd(argc, argv); + INTOFF; + options(0); + setinteractive(iflag); + setjobctl(jflag); + if (*argptr != NULL) { + setparam(argptr); + } + INTON; + return 0; +} + + +/* + * The getopts builtin. Shellparam.optnext points to the next argument + * to be processed. Shellparam.optptr points to the next character to + * be processed in the current argument. If shellparam.optnext is NULL, + * then it's the first time getopts has been called. + */ + +getoptscmd(argc, argv) char **argv; { + register char *p, *q; + char c; + char s[10]; + + if (argc != 3) + error("Usage: getopts optstring var"); + if (shellparam.optnext == NULL) { + shellparam.optnext = shellparam.p; + shellparam.optptr = NULL; + } + if ((p = shellparam.optptr) == NULL || *p == '\0') { + p = *shellparam.optnext; + if (p == NULL || *p != '-' || *++p == '\0') { +atend: + fmtstr(s, 10, "%d", shellparam.optnext - shellparam.p + 1); + setvar("OPTIND", s, 0); + shellparam.optnext = NULL; + return 1; + } + shellparam.optnext++; + if (p[0] == '-' && p[1] == '\0') /* check for "--" */ + goto atend; + } + c = *p++; + for (q = argv[1] ; *q != c ; ) { + if (*q == '\0') { + out1fmt("Illegal option -%c\n", c); + c = '?'; + goto out; + } + if (*++q == ':') + q++; + } + if (*++q == ':') { + if (*p == '\0' && (p = *shellparam.optnext) == NULL) { + out1fmt("No arg for -%c option\n", c); + c = '?'; + goto out; + } + shellparam.optnext++; + setvar("OPTARG", p, 0); + p = NULL; + } +out: + shellparam.optptr = p; + s[0] = c; + s[1] = '\0'; + setvar(argv[2], s, 0); + return 0; +} + +/* + * Standard option processing (a la getopt) for builtin routines. The + * only argument that is passed to nextopt is the option string; the + * other arguments are unnecessary. It return the character, or '\0' on + * end of input. + */ + +int +nextopt(optstring) + char *optstring; + { + register char *p, *q; + char c; + + if ((p = optptr) == NULL || *p == '\0') { + p = *argptr; + if (p == NULL || *p != '-' || *++p == '\0') + return '\0'; + argptr++; + if (p[0] == '-' && p[1] == '\0') /* check for "--" */ + return '\0'; + } + c = *p++; + for (q = optstring ; *q != c ; ) { + if (*q == '\0') + error("Illegal option -%c", c); + if (*++q == ':') + q++; + } + if (*++q == ':') { + if (*p == '\0' && (p = *argptr++) == NULL) + error("No arg for -%c option", c); + optarg = p; + p = NULL; + } + optptr = p; + return c; +} diff --git a/bin/sh/options.h b/bin/sh/options.h new file mode 100644 index 000000000000..c7c94a32d3f6 --- /dev/null +++ b/bin/sh/options.h @@ -0,0 +1,90 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * from: @(#)options.h 5.1 (Berkeley) 3/7/91 + * options.h,v 1.4 1993/08/01 18:58:39 mycroft Exp + */ + +struct shparam { + int nparam; /* number of positional parameters (without $0) */ + char malloc; /* true if parameter list dynamicly allocated */ + char **p; /* parameter list */ + char **optnext; /* next parameter to be processed by getopts */ + char *optptr; /* used by getopts */ +}; + + + +#define eflag optval[0] +#define fflag optval[1] +#define Iflag optval[2] +#define iflag optval[3] +#define jflag optval[4] +#define nflag optval[5] +#define sflag optval[6] +#define xflag optval[7] +#define zflag optval[8] +#define vflag optval[9] + +#define NOPTS 10 + +#ifdef DEFINE_OPTIONS +const char optchar[NOPTS+1] = "efIijnsxzv"; /* shell flags */ +char optval[NOPTS+1]; /* values of option flags */ +#else +extern const char optchar[NOPTS+1]; +extern char optval[NOPTS+1]; +#endif + + +extern char *minusc; /* argument to -c option */ +extern char *arg0; /* $0 */ +extern struct shparam shellparam; /* $@ */ +extern char **argptr; /* argument list for builtin commands */ +extern char *optarg; /* set by nextopt */ +extern char *optptr; /* used by nextopt */ + + +#ifdef __STDC__ +void procargs(int, char **); +void setparam(char **); +void freeparam(struct shparam *); +int nextopt(char *); +#else +void procargs(); +void setparam(); +void freeparam(); +int nextopt(); +#endif diff --git a/bin/sh/output.c b/bin/sh/output.c new file mode 100644 index 000000000000..af024baf4ccc --- /dev/null +++ b/bin/sh/output.c @@ -0,0 +1,531 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)output.c 5.1 (Berkeley) 3/7/91";*/ +static char rcsid[] = "output.c,v 1.4 1993/08/01 18:58:03 mycroft Exp"; +#endif /* not lint */ + +/* + * Shell output routines. We use our own output routines because: + * When a builtin command is interrupted we have to discard + * any pending output. + * When a builtin command appears in back quotes, we want to + * save the output of the command in a region obtained + * via malloc, rather than doing a fork and reading the + * output of the command via a pipe. + * Our output routines may be smaller than the stdio routines. + */ + +#include <stdio.h> /* defines BUFSIZ */ +#include "shell.h" +#include "syntax.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#ifdef __STDC__ +#include "stdarg.h" +#else +#include <varargs.h> +#endif +#include <errno.h> + + +#define OUTBUFSIZ BUFSIZ +#define BLOCK_OUT -2 /* output to a fixed block of memory */ +#define MEM_OUT -3 /* output to dynamically allocated memory */ +#define OUTPUT_ERR 01 /* error occurred on output */ + + +struct output output = {NULL, 0, NULL, OUTBUFSIZ, 1, 0}; +struct output errout = {NULL, 0, NULL, 100, 2, 0}; +struct output memout = {NULL, 0, NULL, 0, MEM_OUT, 0}; +struct output *out1 = &output; +struct output *out2 = &errout; + + + +#ifdef mkinit + +INCLUDE "output.h" +INCLUDE "memalloc.h" + +RESET { + out1 = &output; + out2 = &errout; + if (memout.buf != NULL) { + ckfree(memout.buf); + memout.buf = NULL; + } +} + +#endif + + +#ifdef notdef /* no longer used */ +/* + * Set up an output file to write to memory rather than a file. + */ + +void +open_mem(block, length, file) + char *block; + int length; + struct output *file; + { + file->nextc = block; + file->nleft = --length; + file->fd = BLOCK_OUT; + file->flags = 0; +} +#endif + + +void +out1str(p) + char *p; + { + outstr(p, out1); +} + + +void +out2str(p) + char *p; + { + outstr(p, out2); +} + + +void +outstr(p, file) + register char *p; + register struct output *file; + { + while (*p) + outc(*p++, file); +} + + +char out_junk[16]; + + +void +emptyoutbuf(dest) + struct output *dest; + { + int offset; + + if (dest->fd == BLOCK_OUT) { + dest->nextc = out_junk; + dest->nleft = sizeof out_junk; + dest->flags |= OUTPUT_ERR; + } else if (dest->buf == NULL) { + INTOFF; + dest->buf = ckmalloc(dest->bufsize); + dest->nextc = dest->buf; + dest->nleft = dest->bufsize; + INTON; + } else if (dest->fd == MEM_OUT) { + offset = dest->bufsize; + INTOFF; + dest->bufsize <<= 1; + dest->buf = ckrealloc(dest->buf, dest->bufsize); + dest->nleft = dest->bufsize - offset; + dest->nextc = dest->buf + offset; + INTON; + } else { + flushout(dest); + } + dest->nleft--; +} + + +void +flushall() { + flushout(&output); + flushout(&errout); +} + + +void +flushout(dest) + struct output *dest; + { + + if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0) + return; + if (xwrite(dest->fd, dest->buf, dest->nextc - dest->buf) < 0) + dest->flags |= OUTPUT_ERR; + dest->nextc = dest->buf; + dest->nleft = dest->bufsize; +} + + +void +freestdout() { + INTOFF; + if (output.buf) { + ckfree(output.buf); + output.buf = NULL; + output.nleft = 0; + } + INTON; +} + + +#ifdef __STDC__ +void +outfmt(struct output *file, char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + doformat(file, fmt, ap); + va_end(ap); +} + + +void +out1fmt(char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + doformat(out1, fmt, ap); + va_end(ap); +} + + +void +fmtstr(char *outbuf, int length, char *fmt, ...) { + va_list ap; + struct output strout; + + va_start(ap, fmt); + strout.nextc = outbuf; + strout.nleft = length; + strout.fd = BLOCK_OUT; + strout.flags = 0; + doformat(&strout, fmt, ap); + outc('\0', &strout); + if (strout.flags & OUTPUT_ERR) + outbuf[length - 1] = '\0'; +} + +#else /* not __STDC__ */ + +void +outfmt(va_alist) + va_dcl + { + va_list ap; + struct output *file; + char *fmt; + + va_start(ap); + file = va_arg(ap, struct output *); + fmt = va_arg(ap, char *); + doformat(file, fmt, ap); + va_end(ap); +} + + +void +out1fmt(va_alist) + va_dcl + { + va_list ap; + char *fmt; + + va_start(ap); + fmt = va_arg(ap, char *); + doformat(out1, fmt, ap); + va_end(ap); +} + + +void +fmtstr(va_alist) + va_dcl + { + va_list ap; + struct output strout; + char *outbuf; + int length; + char *fmt; + + va_start(ap); + outbuf = va_arg(ap, char *); + length = va_arg(ap, int); + fmt = va_arg(ap, char *); + strout.nextc = outbuf; + strout.nleft = length; + strout.fd = BLOCK_OUT; + strout.flags = 0; + doformat(&strout, fmt, ap); + outc('\0', &strout); + if (strout.flags & OUTPUT_ERR) + outbuf[length - 1] = '\0'; +} +#endif /* __STDC__ */ + + +/* + * Formatted output. This routine handles a subset of the printf formats: + * - Formats supported: d, u, o, X, s, and c. + * - The x format is also accepted but is treated like X. + * - The l modifier is accepted. + * - The - and # flags are accepted; # only works with the o format. + * - Width and precision may be specified with any format except c. + * - An * may be given for the width or precision. + * - The obsolete practice of preceding the width with a zero to get + * zero padding is not supported; use the precision field. + * - A % may be printed by writing %% in the format string. + */ + +#define TEMPSIZE 24 + +#ifdef __STDC__ +static const char digit[16] = "0123456789ABCDEF"; +#else +static const char digit[17] = "0123456789ABCDEF"; +#endif + + +void +doformat(dest, f, ap) + register struct output *dest; + register char *f; /* format string */ + va_list ap; + { + register char c; + char temp[TEMPSIZE]; + int flushleft; + int sharp; + int width; + int prec; + int islong; + char *p; + int sign; + long l; + unsigned long num; + unsigned base; + int len; + int size; + int pad; + + while ((c = *f++) != '\0') { + if (c != '%') { + outc(c, dest); + continue; + } + flushleft = 0; + sharp = 0; + width = 0; + prec = -1; + islong = 0; + for (;;) { + if (*f == '-') + flushleft++; + else if (*f == '#') + sharp++; + else + break; + f++; + } + if (*f == '*') { + width = va_arg(ap, int); + f++; + } else { + while (is_digit(*f)) { + width = 10 * width + digit_val(*f++); + } + } + if (*f == '.') { + if (*++f == '*') { + prec = va_arg(ap, int); + f++; + } else { + prec = 0; + while (is_digit(*f)) { + prec = 10 * prec + digit_val(*f++); + } + } + } + if (*f == 'l') { + islong++; + f++; + } + switch (*f) { + case 'd': + if (islong) + l = va_arg(ap, long); + else + l = va_arg(ap, int); + sign = 0; + num = l; + if (l < 0) { + num = -l; + sign = 1; + } + base = 10; + goto number; + case 'u': + base = 10; + goto uns_number; + case 'o': + base = 8; + goto uns_number; + case 'x': + /* we don't implement 'x'; treat like 'X' */ + case 'X': + base = 16; +uns_number: /* an unsigned number */ + sign = 0; + if (islong) + num = va_arg(ap, unsigned long); + else + num = va_arg(ap, unsigned int); +number: /* process a number */ + p = temp + TEMPSIZE - 1; + *p = '\0'; + while (num) { + *--p = digit[num % base]; + num /= base; + } + len = (temp + TEMPSIZE - 1) - p; + if (prec < 0) + prec = 1; + if (sharp && *f == 'o' && prec <= len) + prec = len + 1; + pad = 0; + if (width) { + size = len; + if (size < prec) + size = prec; + size += sign; + pad = width - size; + if (flushleft == 0) { + while (--pad >= 0) + outc(' ', dest); + } + } + if (sign) + outc('-', dest); + prec -= len; + while (--prec >= 0) + outc('0', dest); + while (*p) + outc(*p++, dest); + while (--pad >= 0) + outc(' ', dest); + break; + case 's': + p = va_arg(ap, char *); + pad = 0; + if (width) { + len = strlen(p); + if (prec >= 0 && len > prec) + len = prec; + pad = width - len; + if (flushleft == 0) { + while (--pad >= 0) + outc(' ', dest); + } + } + prec++; + while (--prec != 0 && *p) + outc(*p++, dest); + while (--pad >= 0) + outc(' ', dest); + break; + case 'c': + c = va_arg(ap, int); + outc(c, dest); + break; + default: + outc(*f, dest); + break; + } + f++; + } +} + + + +/* + * Version of write which resumes after a signal is caught. + */ + +int +xwrite(fd, buf, nbytes) + int fd; + char *buf; + int nbytes; + { + int ntry; + int i; + int n; + + n = nbytes; + ntry = 0; + for (;;) { + i = write(fd, buf, n); + if (i > 0) { + if ((n -= i) <= 0) + return nbytes; + buf += i; + ntry = 0; + } else if (i == 0) { + if (++ntry > 10) + return nbytes - n; + } else if (errno != EINTR) { + return -1; + } + } +} + + +/* + * Version of ioctl that retries after a signal is caught. + */ + +int +xioctl(fd, request, arg) { + int i; + + while ((i = ioctl(fd, request, arg)) == -1 && errno == EINTR); + return i; +} diff --git a/bin/sh/output.h b/bin/sh/output.h new file mode 100644 index 000000000000..c633fe9be7c0 --- /dev/null +++ b/bin/sh/output.h @@ -0,0 +1,95 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * from: @(#)output.h 5.1 (Berkeley) 3/7/91 + * output.h,v 1.4 1993/08/01 18:58:38 mycroft Exp + */ + +#ifndef OUTPUT_INCL + +struct output { + char *nextc; + int nleft; + char *buf; + int bufsize; + short fd; + short flags; +}; + +extern struct output output; +extern struct output errout; +extern struct output memout; +extern struct output *out1; +extern struct output *out2; + + +#ifdef __STDC__ +void outstr(char *, struct output *); +void out1str(char *); +void out2str(char *); +void outfmt(struct output *, char *, ...); +void out1fmt(char *, ...); +void fmtstr(char *, int, char *, ...); +/* void doformat(struct output *, char *, va_list); */ +void doformat(); +void emptyoutbuf(struct output *); +void flushall(void); +void flushout(struct output *); +void freestdout(void); +int xwrite(int, char *, int); +int xioctl(int, int, int); +#else +void outstr(); +void out1str(); +void out2str(); +void outfmt(); +void out1fmt(); +void fmtstr(); +/* void doformat(); */ +void doformat(); +void emptyoutbuf(); +void flushall(); +void flushout(); +void freestdout(); +int xwrite(); +int xioctl(); +#endif + +#define outc(c, file) (--(file)->nleft < 0? (emptyoutbuf(file), *(file)->nextc++ = (c)) : (*(file)->nextc++ = (c))) +#define out1c(c) outc(c, out1); +#define out2c(c) outc(c, out2); + +#define OUTPUT_INCL +#endif diff --git a/bin/sh/parser.c b/bin/sh/parser.c new file mode 100644 index 000000000000..dc0e046ea171 --- /dev/null +++ b/bin/sh/parser.c @@ -0,0 +1,1298 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)parser.c 5.3 (Berkeley) 4/12/91";*/ +static char rcsid[] = "parser.c,v 1.8 1993/08/01 18:58:02 mycroft Exp"; +#endif /* not lint */ + +#include "shell.h" +#include "parser.h" +#include "nodes.h" +#include "expand.h" /* defines rmescapes() */ +#include "redir.h" /* defines copyfd() */ +#include "syntax.h" +#include "options.h" +#include "input.h" +#include "output.h" +#include "var.h" +#include "error.h" +#include "memalloc.h" +#include "mystring.h" + + +/* + * Shell command parser. + */ + +#define EOFMARKLEN 79 + +/* values returned by readtoken */ +#include "token.def" + + + +struct heredoc { + struct heredoc *next; /* next here document in list */ + union node *here; /* redirection node */ + char *eofmark; /* string indicating end of input */ + int striptabs; /* if set, strip leading tabs */ +}; + + + +struct heredoc *heredoclist; /* list of here documents to read */ +int parsebackquote; /* nonzero if we are inside backquotes */ +int doprompt; /* if set, prompt the user */ +int needprompt; /* true if interactive and at start of line */ +int lasttoken; /* last token read */ +MKINIT int tokpushback; /* last token pushed back */ +char *wordtext; /* text of last word returned by readtoken */ +int checkkwd; /* 1 == check for kwds, 2 == also eat newlines */ +struct nodelist *backquotelist; +union node *redirnode; +struct heredoc *heredoc; +int quoteflag; /* set if (part of) last token was quoted */ +int startlinno; /* line # where last token started */ + + +#define GDB_HACK 1 /* avoid local declarations which gdb can't handle */ +#ifdef GDB_HACK +static const char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'}; +static const char types[] = "}-+?="; +#endif + + +STATIC union node *list __P((int)); +STATIC union node *andor __P((void)); +STATIC union node *pipeline __P((void)); +STATIC union node *command __P((void)); +STATIC union node *simplecmd __P((union node **, union node *)); +STATIC void parsefname __P((void)); +STATIC void parseheredoc __P((void)); +STATIC int readtoken __P((void)); +STATIC int readtoken1 __P((int, char const *, char *, int)); +STATIC void attyline __P((void)); +STATIC int noexpand __P((char *)); +STATIC void synexpect __P((int)); +STATIC void synerror __P((char *)); + +#if ATTY +STATIC void putprompt __P((char *)); +#else /* not ATTY */ +#define putprompt(s) out2str(s) +#endif + + + + +/* + * Read and parse a command. Returns NEOF on end of file. (NULL is a + * valid parse tree indicating a blank line.) + */ + +union node * +parsecmd(interact) { + int t; + + doprompt = interact; + if (doprompt) + putprompt(ps1val()); + needprompt = 0; + if ((t = readtoken()) == TEOF) + return NEOF; + if (t == TNL) + return NULL; + tokpushback++; + return list(1); +} + + +STATIC union node * +list(nlflag) { + union node *n1, *n2, *n3; + + checkkwd = 2; + if (nlflag == 0 && tokendlist[peektoken()]) + return NULL; + n1 = andor(); + for (;;) { + switch (readtoken()) { + case TBACKGND: + if (n1->type == NCMD || n1->type == NPIPE) { + n1->ncmd.backgnd = 1; + } else if (n1->type == NREDIR) { + n1->type = NBACKGND; + } else { + n3 = (union node *)stalloc(sizeof (struct nredir)); + n3->type = NBACKGND; + n3->nredir.n = n1; + n3->nredir.redirect = NULL; + n1 = n3; + } + goto tsemi; + case TNL: + tokpushback++; + /* fall through */ +tsemi: case TSEMI: + if (readtoken() == TNL) { + parseheredoc(); + if (nlflag) + return n1; + } else { + tokpushback++; + } + checkkwd = 2; + if (tokendlist[peektoken()]) + return n1; + n2 = andor(); + n3 = (union node *)stalloc(sizeof (struct nbinary)); + n3->type = NSEMI; + n3->nbinary.ch1 = n1; + n3->nbinary.ch2 = n2; + n1 = n3; + break; + case TEOF: + if (heredoclist) + parseheredoc(); + else + pungetc(); /* push back EOF on input */ + return n1; + default: + if (nlflag) + synexpect(-1); + tokpushback++; + return n1; + } + } +} + + + +STATIC union node * +andor() { + union node *n1, *n2, *n3; + int t; + + n1 = pipeline(); + for (;;) { + if ((t = readtoken()) == TAND) { + t = NAND; + } else if (t == TOR) { + t = NOR; + } else { + tokpushback++; + return n1; + } + n2 = pipeline(); + n3 = (union node *)stalloc(sizeof (struct nbinary)); + n3->type = t; + n3->nbinary.ch1 = n1; + n3->nbinary.ch2 = n2; + n1 = n3; + } +} + + + +STATIC union node * +pipeline() { + union node *n1, *pipenode; + struct nodelist *lp, *prev; + + n1 = command(); + if (readtoken() == TPIPE) { + pipenode = (union node *)stalloc(sizeof (struct npipe)); + pipenode->type = NPIPE; + pipenode->npipe.backgnd = 0; + lp = (struct nodelist *)stalloc(sizeof (struct nodelist)); + pipenode->npipe.cmdlist = lp; + lp->n = n1; + do { + prev = lp; + lp = (struct nodelist *)stalloc(sizeof (struct nodelist)); + lp->n = command(); + prev->next = lp; + } while (readtoken() == TPIPE); + lp->next = NULL; + n1 = pipenode; + } + tokpushback++; + return n1; +} + + + +STATIC union node * +command() { + union node *n1, *n2; + union node *ap, **app; + union node *cp, **cpp; + union node *redir, **rpp; + int t; + + checkkwd = 2; + redir = 0; + rpp = &redir; + /* Check for redirection which may precede command */ + while (readtoken() == TREDIR) { + *rpp = n2 = redirnode; + rpp = &n2->nfile.next; + parsefname(); + } + tokpushback++; + + switch (readtoken()) { + case TIF: + n1 = (union node *)stalloc(sizeof (struct nif)); + n1->type = NIF; + n1->nif.test = list(0); + if (readtoken() != TTHEN) + synexpect(TTHEN); + n1->nif.ifpart = list(0); + n2 = n1; + while (readtoken() == TELIF) { + n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif)); + n2 = n2->nif.elsepart; + n2->type = NIF; + n2->nif.test = list(0); + if (readtoken() != TTHEN) + synexpect(TTHEN); + n2->nif.ifpart = list(0); + } + if (lasttoken == TELSE) + n2->nif.elsepart = list(0); + else { + n2->nif.elsepart = NULL; + tokpushback++; + } + if (readtoken() != TFI) + synexpect(TFI); + checkkwd = 1; + break; + case TWHILE: + case TUNTIL: { + int got; + n1 = (union node *)stalloc(sizeof (struct nbinary)); + n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL; + n1->nbinary.ch1 = list(0); + if ((got=readtoken()) != TDO) { +TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : "")); + synexpect(TDO); + } + n1->nbinary.ch2 = list(0); + if (readtoken() != TDONE) + synexpect(TDONE); + checkkwd = 1; + break; + } + case TFOR: + if (readtoken() != TWORD || quoteflag || ! goodname(wordtext)) + synerror("Bad for loop variable"); + n1 = (union node *)stalloc(sizeof (struct nfor)); + n1->type = NFOR; + n1->nfor.var = wordtext; + if (readtoken() == TWORD && ! quoteflag && equal(wordtext, "in")) { + app = ≈ + while (readtoken() == TWORD) { + n2 = (union node *)stalloc(sizeof (struct narg)); + n2->type = NARG; + n2->narg.text = wordtext; + n2->narg.backquote = backquotelist; + *app = n2; + app = &n2->narg.next; + } + *app = NULL; + n1->nfor.args = ap; + /* A newline or semicolon is required here to end + the list. */ + if (lasttoken != TNL && lasttoken != TSEMI) + synexpect(-1); + } else { +#ifndef GDB_HACK + static const char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE, + '@', '=', '\0'}; +#endif + n2 = (union node *)stalloc(sizeof (struct narg)); + n2->type = NARG; + n2->narg.text = (char *)argvars; + n2->narg.backquote = NULL; + n2->narg.next = NULL; + n1->nfor.args = n2; + /* A newline or semicolon is optional here. Anything + else gets pushed back so we can read it again. */ + if (lasttoken != TNL && lasttoken != TSEMI) + tokpushback++; + } + checkkwd = 2; + if ((t = readtoken()) == TDO) + t = TDONE; + else if (t == TBEGIN) + t = TEND; + else + synexpect(-1); + n1->nfor.body = list(0); + if (readtoken() != t) + synexpect(t); + checkkwd = 1; + break; + case TCASE: + n1 = (union node *)stalloc(sizeof (struct ncase)); + n1->type = NCASE; + if (readtoken() != TWORD) + synexpect(TWORD); + n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg)); + n2->type = NARG; + n2->narg.text = wordtext; + n2->narg.backquote = backquotelist; + n2->narg.next = NULL; + while (readtoken() == TNL); + if (lasttoken != TWORD || ! equal(wordtext, "in")) + synerror("expecting \"in\""); + cpp = &n1->ncase.cases; + while (checkkwd = 2, readtoken() == TWORD) { + *cpp = cp = (union node *)stalloc(sizeof (struct nclist)); + cp->type = NCLIST; + app = &cp->nclist.pattern; + for (;;) { + *app = ap = (union node *)stalloc(sizeof (struct narg)); + ap->type = NARG; + ap->narg.text = wordtext; + ap->narg.backquote = backquotelist; + if (readtoken() != TPIPE) + break; + app = &ap->narg.next; + if (readtoken() != TWORD) + synexpect(TWORD); + } + ap->narg.next = NULL; + if (lasttoken != TRP) + synexpect(TRP); + cp->nclist.body = list(0); + if ((t = readtoken()) == TESAC) + tokpushback++; + else if (t != TENDCASE) + synexpect(TENDCASE); + cpp = &cp->nclist.next; + } + *cpp = NULL; + if (lasttoken != TESAC) + synexpect(TESAC); + checkkwd = 1; + break; + case TLP: + n1 = (union node *)stalloc(sizeof (struct nredir)); + n1->type = NSUBSHELL; + n1->nredir.n = list(0); + n1->nredir.redirect = NULL; + if (readtoken() != TRP) + synexpect(TRP); + checkkwd = 1; + break; + case TBEGIN: + n1 = list(0); + if (readtoken() != TEND) + synexpect(TEND); + checkkwd = 1; + break; + /* Handle an empty command like other simple commands. */ + case TNL: + case TWORD: + tokpushback++; + return simplecmd(rpp, redir); + default: + synexpect(-1); + } + + /* Now check for redirection which may follow command */ + while (readtoken() == TREDIR) { + *rpp = n2 = redirnode; + rpp = &n2->nfile.next; + parsefname(); + } + tokpushback++; + *rpp = NULL; + if (redir) { + if (n1->type != NSUBSHELL) { + n2 = (union node *)stalloc(sizeof (struct nredir)); + n2->type = NREDIR; + n2->nredir.n = n1; + n1 = n2; + } + n1->nredir.redirect = redir; + } + return n1; +} + + +STATIC union node * +simplecmd(rpp, redir) + union node **rpp, *redir; + { + union node *args, **app; + union node **orig_rpp = rpp; + union node *n; + + /* If we don't have any redirections already, then we must reset + rpp to be the address of the local redir variable. */ + if (redir == 0) + rpp = &redir; + + args = NULL; + app = &args; + /* We save the incoming value, because we need this for shell + functions. There can not be a redirect or an argument between + the function name and the open parenthesis. */ + orig_rpp = rpp; + for (;;) { + if (readtoken() == TWORD) { + n = (union node *)stalloc(sizeof (struct narg)); + n->type = NARG; + n->narg.text = wordtext; + n->narg.backquote = backquotelist; + *app = n; + app = &n->narg.next; + } else if (lasttoken == TREDIR) { + *rpp = n = redirnode; + rpp = &n->nfile.next; + parsefname(); /* read name of redirection file */ + } else if (lasttoken == TLP && app == &args->narg.next + && rpp == orig_rpp) { + /* We have a function */ + if (readtoken() != TRP) + synexpect(TRP); +#ifdef notdef + if (! goodname(n->narg.text)) + synerror("Bad function name"); +#endif + n->type = NDEFUN; + n->narg.next = command(); + return n; + } else { + tokpushback++; + break; + } + } + *app = NULL; + *rpp = NULL; + n = (union node *)stalloc(sizeof (struct ncmd)); + n->type = NCMD; + n->ncmd.backgnd = 0; + n->ncmd.args = args; + n->ncmd.redirect = redir; + return n; +} + + +STATIC void +parsefname() { + union node *n = redirnode; + + if (readtoken() != TWORD) + synexpect(-1); + if (n->type == NHERE) { + struct heredoc *here = heredoc; + struct heredoc *p; + int i; + + if (quoteflag == 0) + n->type = NXHERE; + TRACE(("Here document %d\n", n->type)); + if (here->striptabs) { + while (*wordtext == '\t') + wordtext++; + } + if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN) + synerror("Illegal eof marker for << redirection"); + rmescapes(wordtext); + here->eofmark = wordtext; + here->next = NULL; + if (heredoclist == NULL) + heredoclist = here; + else { + for (p = heredoclist ; p->next ; p = p->next); + p->next = here; + } + } else if (n->type == NTOFD || n->type == NFROMFD) { + if (is_digit(wordtext[0])) + n->ndup.dupfd = digit_val(wordtext[0]); + else if (wordtext[0] == '-') + n->ndup.dupfd = -1; + else + goto bad; + if (wordtext[1] != '\0') { +bad: + synerror("Bad fd number"); + } + } else { + n->nfile.fname = (union node *)stalloc(sizeof (struct narg)); + n = n->nfile.fname; + n->type = NARG; + n->narg.next = NULL; + n->narg.text = wordtext; + n->narg.backquote = backquotelist; + } +} + + +/* + * Input any here documents. + */ + +STATIC void +parseheredoc() { + struct heredoc *here; + union node *n; + + while (heredoclist) { + here = heredoclist; + heredoclist = here->next; + if (needprompt) { + putprompt(ps2val()); + needprompt = 0; + } + readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX, + here->eofmark, here->striptabs); + n = (union node *)stalloc(sizeof (struct narg)); + n->narg.type = NARG; + n->narg.next = NULL; + n->narg.text = wordtext; + n->narg.backquote = backquotelist; + here->here->nhere.doc = n; + } +} + +STATIC int +peektoken() { + int t; + + t = readtoken(); + tokpushback++; + return (t); +} + +STATIC int xxreadtoken(); + +STATIC int +readtoken() { + int t; +#ifdef DEBUG + int alreadyseen = tokpushback; +#endif + + t = xxreadtoken(); + + if (checkkwd) { + /* + * eat newlines + */ + if (checkkwd == 2) { + checkkwd = 0; + while (t == TNL) { + parseheredoc(); + t = xxreadtoken(); + } + } else + checkkwd = 0; + /* + * check for keywords + */ + if (t == TWORD && !quoteflag) { + register char *const *pp; + + for (pp = parsekwd; *pp; pp++) { + if (**pp == *wordtext && equal(*pp, wordtext)) { + lasttoken = t = pp - parsekwd + KWDOFFSET; + TRACE(("keyword %s recognized\n", tokname[t])); + break; + } + } + } + } +#ifdef DEBUG + if (!alreadyseen) + TRACE(("token %s %s\n", tokname[t], t == TWORD ? wordtext : "")); + else + TRACE(("reread token %s %s\n", tokname[t], t == TWORD ? wordtext : "")); +#endif + return (t); +} + + +/* + * Read the next input token. + * If the token is a word, we set backquotelist to the list of cmds in + * backquotes. We set quoteflag to true if any part of the word was + * quoted. + * If the token is TREDIR, then we set redirnode to a structure containing + * the redirection. + * In all cases, the variable startlinno is set to the number of the line + * on which the token starts. + * + * [Change comment: here documents and internal procedures] + * [Readtoken shouldn't have any arguments. Perhaps we should make the + * word parsing code into a separate routine. In this case, readtoken + * doesn't need to have any internal procedures, but parseword does. + * We could also make parseoperator in essence the main routine, and + * have parseword (readtoken1?) handle both words and redirection.] + */ + +#define RETURN(token) return lasttoken = token + +STATIC int +xxreadtoken() { + register c; + + if (tokpushback) { + tokpushback = 0; + return lasttoken; + } + if (needprompt) { + putprompt(ps2val()); + needprompt = 0; + } + startlinno = plinno; + for (;;) { /* until token or start of word found */ + c = pgetc_macro(); + if (c == ' ' || c == '\t') + continue; /* quick check for white space first */ + switch (c) { + case ' ': case '\t': + continue; + case '#': + while ((c = pgetc()) != '\n' && c != PEOF); + pungetc(); + continue; + case '\\': + if (pgetc() == '\n') { + startlinno = ++plinno; + if (doprompt) + putprompt(ps2val()); + continue; + } + pungetc(); + goto breakloop; + case '\n': + plinno++; + needprompt = doprompt; + RETURN(TNL); + case PEOF: + RETURN(TEOF); + case '&': + if (pgetc() == '&') + RETURN(TAND); + pungetc(); + RETURN(TBACKGND); + case '|': + if (pgetc() == '|') + RETURN(TOR); + pungetc(); + RETURN(TPIPE); + case ';': + if (pgetc() == ';') + RETURN(TENDCASE); + pungetc(); + RETURN(TSEMI); + case '(': + RETURN(TLP); + case ')': + RETURN(TRP); + default: + goto breakloop; + } + } +breakloop: + return readtoken1(c, BASESYNTAX, (char *)NULL, 0); +#undef RETURN +} + + + +/* + * If eofmark is NULL, read a word or a redirection symbol. If eofmark + * is not NULL, read a here document. In the latter case, eofmark is the + * word which marks the end of the document and striptabs is true if + * leading tabs should be stripped from the document. The argument firstc + * is the first character of the input token or document. + * + * Because C does not have internal subroutines, I have simulated them + * using goto's to implement the subroutine linkage. The following macros + * will run code that appears at the end of readtoken1. + */ + +#define CHECKEND() {goto checkend; checkend_return:;} +#define PARSEREDIR() {goto parseredir; parseredir_return:;} +#define PARSESUB() {goto parsesub; parsesub_return:;} +#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;} +#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;} + +STATIC int +readtoken1(firstc, syntax, eofmark, striptabs) + int firstc; + char const *syntax; + char *eofmark; + int striptabs; + { + register c = firstc; + register char *out; + int len; + char line[EOFMARKLEN + 1]; + struct nodelist *bqlist; + int quotef; + int dblquote; + int varnest; + int oldstyle; + + startlinno = plinno; + dblquote = 0; + if (syntax == DQSYNTAX) + dblquote = 1; + quotef = 0; + bqlist = NULL; + varnest = 0; + STARTSTACKSTR(out); + loop: { /* for each line, until end of word */ +#if ATTY + if (c == '\034' && doprompt + && attyset() && ! equal(termval(), "emacs")) { + attyline(); + if (syntax == BASESYNTAX) + return readtoken(); + c = pgetc(); + goto loop; + } +#endif + CHECKEND(); /* set c to PEOF if at end of here document */ + for (;;) { /* until end of line or end of word */ + CHECKSTRSPACE(3, out); /* permit 3 calls to USTPUTC */ + switch(syntax[c]) { + case CNL: /* '\n' */ + if (syntax == BASESYNTAX) + goto endword; /* exit outer loop */ + USTPUTC(c, out); + plinno++; + if (doprompt) { + putprompt(ps2val()); + } + c = pgetc(); + goto loop; /* continue outer loop */ + case CWORD: + USTPUTC(c, out); + break; + case CCTL: + if (eofmark == NULL || dblquote) + USTPUTC(CTLESC, out); + USTPUTC(c, out); + break; + case CBACK: /* backslash */ + c = pgetc(); + if (c == PEOF) { + USTPUTC('\\', out); + pungetc(); + } else if (c == '\n') { + if (doprompt) + putprompt(ps2val()); + } else { + if (dblquote && c != '\\' && c != '`' && c != '$' + && (c != '"' || eofmark != NULL)) + USTPUTC('\\', out); + if (SQSYNTAX[c] == CCTL) + USTPUTC(CTLESC, out); + USTPUTC(c, out); + quotef++; + } + break; + case CSQUOTE: + syntax = SQSYNTAX; + break; + case CDQUOTE: + syntax = DQSYNTAX; + dblquote = 1; + break; + case CENDQUOTE: + if (eofmark) { + USTPUTC(c, out); + } else { + syntax = BASESYNTAX; + quotef++; + dblquote = 0; + } + break; + case CVAR: /* '$' */ + PARSESUB(); /* parse substitution */ + break; + case CENDVAR: /* '}' */ + if (varnest > 0) { + varnest--; + USTPUTC(CTLENDVAR, out); + } else { + USTPUTC(c, out); + } + break; + case CBQUOTE: /* '`' */ + PARSEBACKQOLD(); + break; + case CEOF: + goto endword; /* exit outer loop */ + default: + if (varnest == 0) + goto endword; /* exit outer loop */ + USTPUTC(c, out); + } + c = pgetc_macro(); + } + } +endword: + if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL) + synerror("Unterminated quoted string"); + if (varnest != 0) { + startlinno = plinno; + synerror("Missing '}'"); + } + USTPUTC('\0', out); + len = out - stackblock(); + out = stackblock(); + if (eofmark == NULL) { + if ((c == '>' || c == '<') + && quotef == 0 + && len <= 2 + && (*out == '\0' || is_digit(*out))) { + PARSEREDIR(); + return lasttoken = TREDIR; + } else { + pungetc(); + } + } + quoteflag = quotef; + backquotelist = bqlist; + grabstackblock(len); + wordtext = out; + return lasttoken = TWORD; +/* end of readtoken routine */ + + + +/* + * Check to see whether we are at the end of the here document. When this + * is called, c is set to the first character of the next input line. If + * we are at the end of the here document, this routine sets the c to PEOF. + */ + +checkend: { + if (eofmark) { + if (striptabs) { + while (c == '\t') + c = pgetc(); + } + if (c == *eofmark) { + if (pfgets(line, sizeof line) != NULL) { + register char *p, *q; + + p = line; + for (q = eofmark + 1 ; *q && *p == *q ; p++, q++); + if (*p == '\n' && *q == '\0') { + c = PEOF; + plinno++; + needprompt = doprompt; + } else { + ppushback(line, strlen(line)); + } + } + } + } + goto checkend_return; +} + + +/* + * Parse a redirection operator. The variable "out" points to a string + * specifying the fd to be redirected. The variable "c" contains the + * first character of the redirection operator. + */ + +parseredir: { + char fd = *out; + union node *np; + + np = (union node *)stalloc(sizeof (struct nfile)); + if (c == '>') { + np->nfile.fd = 1; + c = pgetc(); + if (c == '>') + np->type = NAPPEND; + else if (c == '&') + np->type = NTOFD; + else { + np->type = NTO; + pungetc(); + } + } else { /* c == '<' */ + np->nfile.fd = 0; + c = pgetc(); + if (c == '<') { + if (sizeof (struct nfile) != sizeof (struct nhere)) { + np = (union node *)stalloc(sizeof (struct nhere)); + np->nfile.fd = 0; + } + np->type = NHERE; + heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc)); + heredoc->here = np; + if ((c = pgetc()) == '-') { + heredoc->striptabs = 1; + } else { + heredoc->striptabs = 0; + pungetc(); + } + } else if (c == '&') + np->type = NFROMFD; + else { + np->type = NFROM; + pungetc(); + } + } + if (fd != '\0') + np->nfile.fd = digit_val(fd); + redirnode = np; + goto parseredir_return; +} + + +/* + * Parse a substitution. At this point, we have read the dollar sign + * and nothing else. + */ + +parsesub: { + int subtype; + int typeloc; + int flags; + char *p; +#ifndef GDB_HACK + static const char types[] = "}-+?="; +#endif + + c = pgetc(); + if (c != '(' && c != '{' && !is_name(c) && !is_special(c)) { + USTPUTC('$', out); + pungetc(); + } else if (c == '(') { /* $(command) */ + PARSEBACKQNEW(); + } else { + USTPUTC(CTLVAR, out); + typeloc = out - stackblock(); + USTPUTC(VSNORMAL, out); + subtype = VSNORMAL; + if (c == '{') { + c = pgetc(); + subtype = 0; + } + if (is_name(c)) { + do { + STPUTC(c, out); + c = pgetc(); + } while (is_in_name(c)); + } else { + if (! is_special(c)) +badsub: synerror("Bad substitution"); + USTPUTC(c, out); + c = pgetc(); + } + STPUTC('=', out); + flags = 0; + if (subtype == 0) { + if (c == ':') { + flags = VSNUL; + c = pgetc(); + } + p = strchr(types, c); + if (p == NULL) + goto badsub; + subtype = p - types + VSNORMAL; + } else { + pungetc(); + } + if (dblquote) + flags |= VSQUOTE; + *(stackblock() + typeloc) = subtype | flags; + if (subtype != VSNORMAL) + varnest++; + } + goto parsesub_return; +} + + +/* + * Called to parse command substitutions. Newstyle is set if the command + * is enclosed inside $(...); nlpp is a pointer to the head of the linked + * list of commands (passed by reference), and savelen is the number of + * characters on the top of the stack which must be preserved. + */ + +parsebackq: { + struct nodelist **nlpp; + int savepbq; + union node *n; + char *volatile str; + struct jmploc jmploc; + struct jmploc *volatile savehandler; + int savelen; + + savepbq = parsebackquote; + if (setjmp(jmploc.loc)) { + if (str) + ckfree(str); + parsebackquote = 0; + handler = savehandler; + longjmp(handler->loc, 1); + } + INTOFF; + str = NULL; + savelen = out - stackblock(); + if (savelen > 0) { + str = ckmalloc(savelen); + bcopy(stackblock(), str, savelen); + } + savehandler = handler; + handler = &jmploc; + INTON; + if (oldstyle) { + /* We must read until the closing backquote, giving special + treatment to some slashes, and then push the string and + reread it as input, interpreting it normally. */ + register char *out; + register c; + int savelen; + char *str; + + STARTSTACKSTR(out); + while ((c = pgetc ()) != '`') { + if (c == '\\') { + c = pgetc (); + if (c != '\\' && c != '`' && c != '$' + && (!dblquote || c != '"')) + STPUTC('\\', out); + } + STPUTC(c, out); + } + STPUTC('\0', out); + savelen = out - stackblock(); + if (savelen > 0) { + str = ckmalloc(savelen); + bcopy(stackblock(), str, savelen); + } + setinputstring(str, 1); + } + nlpp = &bqlist; + while (*nlpp) + nlpp = &(*nlpp)->next; + *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist)); + (*nlpp)->next = NULL; + parsebackquote = oldstyle; + n = list(0); + if (!oldstyle && (readtoken() != TRP)) + synexpect(TRP); + (*nlpp)->n = n; + /* Start reading from old file again, and clear tokpushback since + any pushed back token from the string is no longer relevant. */ + if (oldstyle) { + popfile(); + tokpushback = 0; + } + while (stackblocksize() <= savelen) + growstackblock(); + STARTSTACKSTR(out); + if (str) { + bcopy(str, out, savelen); + STADJUST(savelen, out); + INTOFF; + ckfree(str); + str = NULL; + INTON; + } + parsebackquote = savepbq; + handler = savehandler; + USTPUTC(CTLBACKQ + dblquote, out); + if (oldstyle) + goto parsebackq_oldreturn; + else + goto parsebackq_newreturn; +} + +} /* end of readtoken */ + + + +#ifdef mkinit +RESET { + tokpushback = 0; +} +#endif + + +#if ATTY +/* + * Called to process a command generated by atty. We execute the line, + * and catch any errors that occur so they don't propagate outside of + * this routine. + */ + +STATIC void +attyline() { + char line[256]; + struct stackmark smark; + struct jmploc jmploc; + struct jmploc *volatile savehandler; + + if (pfgets(line, sizeof line) == NULL) + return; /* "can't happen" */ + if (setjmp(jmploc.loc)) { + if (exception == EXERROR) + out2str("\033]D\n"); + handler = savehandler; + longjmp(handler->loc, 1); + } + savehandler = handler; + handler = &jmploc; + setstackmark(&smark); + evalstring(line); + popstackmark(&smark); + handler = savehandler; + doprompt = 1; +} + + +/* + * Output a prompt for atty. We output the prompt as part of the + * appropriate escape sequence. + */ + +STATIC void +putprompt(s) + char *s; + { + register char *p; + + if (attyset() && ! equal(termval(), "emacs")) { + if (strchr(s, '\7')) + out2c('\7'); + out2str("\033]P1;"); + for (p = s ; *p ; p++) { + if ((unsigned)(*p - ' ') <= '~' - ' ') + out2c(*p); + } + out2c('\n'); + } else { + out2str(s); + } +} +#endif + + + +/* + * Returns true if the text contains nothing to expand (no dollar signs + * or backquotes). + */ + +STATIC int +noexpand(text) + char *text; + { + register char *p; + register char c; + + p = text; + while ((c = *p++) != '\0') { + if (c == CTLESC) + p++; + else if (BASESYNTAX[c] == CCTL) + return 0; + } + return 1; +} + + +/* + * Return true if the argument is a legal variable name (a letter or + * underscore followed by zero or more letters, underscores, and digits). + */ + +int +goodname(name) + char *name; + { + register char *p; + + p = name; + if (! is_name(*p)) + return 0; + while (*++p) { + if (! is_in_name(*p)) + return 0; + } + return 1; +} + + +/* + * Called when an unexpected token is read during the parse. The argument + * is the token that is expected, or -1 if more than one type of token can + * occur at this point. + */ + +STATIC void +synexpect(token) { + char msg[64]; + + if (token >= 0) { + fmtstr(msg, 64, "%s unexpected (expecting %s)", + tokname[lasttoken], tokname[token]); + } else { + fmtstr(msg, 64, "%s unexpected", tokname[lasttoken]); + } + synerror(msg); +} + + +STATIC void +synerror(msg) + char *msg; + { + if (commandname) + outfmt(&errout, "%s: %d: ", commandname, startlinno); + outfmt(&errout, "Syntax error: %s\n", msg); + error((char *)NULL); +} diff --git a/bin/sh/parser.h b/bin/sh/parser.h new file mode 100644 index 000000000000..a4dd897f4db6 --- /dev/null +++ b/bin/sh/parser.h @@ -0,0 +1,75 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * from: @(#)parser.h 5.1 (Berkeley) 3/7/91 + * parser.h,v 1.4 1993/08/01 18:58:37 mycroft Exp + */ + +/* control characters in argument strings */ +#define CTLESC '\201' +#define CTLVAR '\202' +#define CTLENDVAR '\203' +#define CTLBACKQ '\204' +#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */ + +/* variable substitution byte (follows CTLVAR) */ +#define VSTYPE 07 /* type of variable substitution */ +#define VSNUL 040 /* colon--treat the empty string as unset */ +#define VSQUOTE 0100 /* inside double quotes--suppress splitting */ + +/* values of VSTYPE field */ +#define VSNORMAL 1 /* normal variable: $var or ${var} */ +#define VSMINUS 2 /* ${var-text} */ +#define VSPLUS 3 /* ${var+text} */ +#define VSQUESTION 4 /* ${var?message} */ +#define VSASSIGN 5 /* ${var=text} */ + + +/* + * NEOF is returned by parsecmd when it encounters an end of file. It + * must be distinct from NULL, so we use the address of a variable that + * happens to be handy. + */ +extern int tokpushback; +#define NEOF ((union node *)&tokpushback) + + +#ifdef __STDC__ +union node *parsecmd(int); +int goodname(char *); +#else +union node *parsecmd(); +int goodname(); +#endif diff --git a/bin/sh/redir.c b/bin/sh/redir.c new file mode 100644 index 000000000000..1d15bd6b6823 --- /dev/null +++ b/bin/sh/redir.c @@ -0,0 +1,365 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)redir.c 5.1 (Berkeley) 3/7/91";*/ +static char rcsid[] = "redir.c,v 1.5 1993/08/01 18:58:01 mycroft Exp"; +#endif /* not lint */ + +/* + * Code for dealing with input/output redirection. + */ + +#include "shell.h" +#include "nodes.h" +#include "jobs.h" +#include "expand.h" +#include "redir.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include <signal.h> +#include <fcntl.h> +#include <errno.h> + + +#define EMPTY -2 /* marks an unused slot in redirtab */ +#define PIPESIZE 4096 /* amount of buffering in a pipe */ + + +MKINIT +struct redirtab { + struct redirtab *next; + short renamed[10]; +}; + + +MKINIT struct redirtab *redirlist; + +/* We keep track of whether or not fd0 has been redirected. This is for + background commands, where we want to redirect fd0 to /dev/null only + if it hasn't already been redirected. */ +int fd0_redirected = 0; + +#ifdef __STDC__ +STATIC void openredirect(union node *, char *); +STATIC int openhere(union node *); +#else +STATIC void openredirect(); +STATIC int openhere(); +#endif + + + +/* + * Process a list of redirection commands. If the REDIR_PUSH flag is set, + * old file descriptors are stashed away so that the redirection can be + * undone by calling popredir. If the REDIR_BACKQ flag is set, then the + * standard output, and the standard error if it becomes a duplicate of + * stdout, is saved in memory. + */ + +void +redirect(redir, flags) + union node *redir; + int flags; + { + union node *n; + struct redirtab *sv; + int i; + int fd; + char memory[10]; /* file descriptors to write to memory */ + + for (i = 10 ; --i >= 0 ; ) + memory[i] = 0; + memory[1] = flags & REDIR_BACKQ; + if (flags & REDIR_PUSH) { + sv = ckmalloc(sizeof (struct redirtab)); + for (i = 0 ; i < 10 ; i++) + sv->renamed[i] = EMPTY; + sv->next = redirlist; + redirlist = sv; + } + for (n = redir ; n ; n = n->nfile.next) { + fd = n->nfile.fd; + if ((flags & REDIR_PUSH) && sv->renamed[fd] == EMPTY) { + INTOFF; + if ((i = copyfd(fd, 10)) != EMPTY) { + sv->renamed[fd] = i; + close(fd); + } + INTON; + if (i == EMPTY) + error("Out of file descriptors"); + } else { + close(fd); + } + if (fd == 0) + fd0_redirected++; + openredirect(n, memory); + } + if (memory[1]) + out1 = &memout; + if (memory[2]) + out2 = &memout; +} + + +STATIC void +openredirect(redir, memory) + union node *redir; + char memory[10]; + { + int fd = redir->nfile.fd; + char *fname; + int f; + + /* + * We suppress interrupts so that we won't leave open file + * descriptors around. This may not be such a good idea because + * an open of a device or a fifo can block indefinitely. + */ + INTOFF; + memory[fd] = 0; + switch (redir->nfile.type) { + case NFROM: + fname = redir->nfile.expfname; + if ((f = open(fname, O_RDONLY)) < 0) + error("cannot open %s: %s", fname, errmsg(errno, E_OPEN)); +movefd: + if (f != fd) { + copyfd(f, fd); + close(f); + } + break; + case NTO: + fname = redir->nfile.expfname; +#ifdef O_CREAT + if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) + error("cannot create %s: %s", fname, errmsg(errno, E_CREAT)); +#else + if ((f = creat(fname, 0666)) < 0) + error("cannot create %s: %s", fname, errmsg(errno, E_CREAT)); +#endif + goto movefd; + case NAPPEND: + fname = redir->nfile.expfname; +#ifdef O_APPEND + if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0) + error("cannot create %s: %s", fname, errmsg(errno, E_CREAT)); +#else + if ((f = open(fname, O_WRONLY)) < 0 + && (f = creat(fname, 0666)) < 0) + error("cannot create %s: %s", fname, errmsg(errno, E_CREAT)); + lseek(f, 0L, 2); +#endif + goto movefd; + case NTOFD: + case NFROMFD: + if (redir->ndup.dupfd >= 0) { /* if not ">&-" */ + if (memory[redir->ndup.dupfd]) + memory[fd] = 1; + else + copyfd(redir->ndup.dupfd, fd); + } + break; + case NHERE: + case NXHERE: + f = openhere(redir); + goto movefd; + default: + abort(); + } + INTON; +} + + +/* + * Handle here documents. Normally we fork off a process to write the + * data to a pipe. If the document is short, we can stuff the data in + * the pipe without forking. + */ + +STATIC int +openhere(redir) + union node *redir; + { + int pip[2]; + int len; + + if (pipe(pip) < 0) + error("Pipe call failed"); + if (redir->type == NHERE) { + len = strlen(redir->nhere.doc->narg.text); + if (len <= PIPESIZE) { + xwrite(pip[1], redir->nhere.doc->narg.text, len); + goto out; + } + } + if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) { + close(pip[0]); + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + signal(SIGHUP, SIG_IGN); +#ifdef SIGTSTP + signal(SIGTSTP, SIG_IGN); +#endif + signal(SIGPIPE, SIG_DFL); + if (redir->type == NHERE) + xwrite(pip[1], redir->nhere.doc->narg.text, len); + else + expandhere(redir->nhere.doc, pip[1]); + _exit(0); + } +out: + close(pip[1]); + return pip[0]; +} + + + +/* + * Undo the effects of the last redirection. + */ + +void +popredir() { + register struct redirtab *rp = redirlist; + int i; + + for (i = 0 ; i < 10 ; i++) { + if (rp->renamed[i] != EMPTY) { + if (i == 0) + fd0_redirected--; + close(i); + if (rp->renamed[i] >= 0) { + copyfd(rp->renamed[i], i); + close(rp->renamed[i]); + } + } + } + INTOFF; + redirlist = rp->next; + ckfree(rp); + INTON; +} + + + +/* + * Undo all redirections. Called on error or interrupt. + */ + +#ifdef mkinit + +INCLUDE "redir.h" + +RESET { + while (redirlist) + popredir(); +} + +SHELLPROC { + clearredir(); +} + +#endif + + +/* + * Discard all saved file descriptors. + */ + +void +clearredir() { + register struct redirtab *rp; + int i; + + for (rp = redirlist ; rp ; rp = rp->next) { + for (i = 0 ; i < 10 ; i++) { + if (rp->renamed[i] >= 0) { + close(rp->renamed[i]); + } + rp->renamed[i] = EMPTY; + } + } +} + + + +/* + * Copy a file descriptor, like the F_DUPFD option of fcntl. Returns -1 + * if the source file descriptor is closed, EMPTY if there are no unused + * file descriptors left. + */ + +int +copyfd(from, to) { +#ifdef F_DUPFD + int newfd; + + newfd = fcntl(from, F_DUPFD, to); + if (newfd < 0 && errno == EMFILE) + return EMPTY; + return newfd; +#else + char toclose[32]; + int i; + int newfd; + int e; + + for (i = 0 ; i < to ; i++) + toclose[i] = 0; + INTOFF; + while ((newfd = dup(from)) >= 0 && newfd < to) + toclose[newfd] = 1; + e = errno; + for (i = 0 ; i < to ; i++) { + if (toclose[i]) + close(i); + } + INTON; + if (newfd < 0 && e == EMFILE) + return EMPTY; + return newfd; +#endif +} + +/* Return true if fd 0 has already been redirected at least once. */ +int +fd0_redirected_p () { + return fd0_redirected != 0; +} diff --git a/bin/sh/redir.h b/bin/sh/redir.h new file mode 100644 index 000000000000..9f1908fcb65e --- /dev/null +++ b/bin/sh/redir.h @@ -0,0 +1,57 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * from: @(#)redir.h 5.1 (Berkeley) 3/7/91 + * redir.h,v 1.5 1993/08/01 18:58:36 mycroft Exp + */ + +/* flags passed to redirect */ +#define REDIR_PUSH 01 /* save previous values of file descriptors */ +#define REDIR_BACKQ 02 /* save the command output in memory */ + +#ifdef __STDC__ +union node; +void redirect(union node *, int); +void popredir(void); +void clearredir(void); +int copyfd(int, int); +int fd0_redirected_p(void); +#else +void redirect(); +void popredir(); +void clearredir(); +int copyfd(); +int fd0_redirected_p(); +#endif diff --git a/bin/sh/sh.1 b/bin/sh/sh.1 new file mode 100644 index 000000000000..415aa0e42fc3 --- /dev/null +++ b/bin/sh/sh.1 @@ -0,0 +1,1127 @@ +.\" Copyright (c) 1991 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Kenneth Almquist. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. +.\" +.\" from: @(#)sh.1 5.1 (Berkeley) 3/7/91 +.\" sh.1,v 1.6 1993/08/01 07:58:14 mycroft Exp +.\" +.TH SH 1 "March 7, 1991" +.UC 7 +.de h \" subheading +.sp +.ti -0.3i +.B "\\$1" +.PP +.. +.de d \" begin display +.sp +.in +4 +.nf +.. +.de e \" end display +.in -4 +.fi +.sp +.. +.de c \" command, etc. +.br +.HP 5 +\fB\\$1\fR +.br +.. +.de b \" begin builtin command +.HP 5 +.B \\$1 +.. +.SH NAME +ash \- a shell +.SH SYNOPSIS +.B ash +[ +.B -efIijnsxz +] [ +.B +efIijnsxz +] [ +.B -c +.I command +] [ +.I arg +] ... +.SH COPYRIGHT +Copyright 1989 by Kenneth Almquist. +.SH DESCRIPTION +.I Ash +is a version of +.I sh +with features similar to those of the System V shell. +This manual page lists all the features of +.I ash +but concentrates on the ones not in other shells. +.h "Invocation" +If the +.B -c +options is given, then the shell executes the specified shell command. +The +.B -s +flag cause the shell to read commands from the standard input (after +executing any command specified with the +.B -c +option. +If neither the +.B -s +or +.B -c +options are set, then the first +.I arg +is taken as the name of a file to read commands from. +If this is impossible because there are no arguments following +the options, then +.I ash +will set the +.B -s +flag and will read commands from the standard input. +.PP +The shell sets the initial value of the positional parameters from the +.IR arg s +remaining after any +.I arg +used as the name of a file of commands is deleted. +.PP +The flags (other than +.BR -c ) +are set by preceding them with ``-'' and cleared by preceding them +with ``+''; see the +.I set +builtin command for a list of flags. +If no value is specified for the +.B -i +flag, the +.B -s +flag is set, and the standard input and output of the shell +are connected to terminals, then the +.B -i +flag will be set. +If no value is specified for the +.B -j +flag, then the +.B -j +flag will be set if the +.B -i +flag is set. +.PP +When the shell is invoked with the +.B -c +option, it is good practice to include the +.I -i +flag if the command was entered interactively by a user. +For compatibility with the System V shell, the +.I -i +option should come after the +.B -c +option. +.PP +If the first character of argument zero to the shell is ``-'', +the shell is assumed to be a login shell, and the files +.B /etc/profile +and +.B .profile +are read if they exist. +If the environment variable SHINIT is set on entry to the shell, +the commands in SHINIT are normally parsed and executed. SHINIT is +not examined if the shell is a login shell, or if it the shell is running a +shell procedure. (A shell is considered to be running a shell +procedure if neither the +.B -s +nor the +.B -c +options are set.) +.h "Control Structures" +A +.I list +is a sequence of zero or more commands separated by newlines, +semicolons, or ampersands, and optionally terminated by one of these +three characters. (This differs from the System V shell, which +requires a list to contain at least one command in most cases.) The +commands in a list are executed in the order they are written. +If command is followed by an ampersand, the shell starts the command +and immediately proceed onto the next command; otherwise it waits +for the command to terminate before proceeding to the next one. +.PP +``&&'' and ``||'' are binary operators. +``&&'' executes the first command, and then executes the second command +iff the exit status of the first command is zero. ``||'' is similar, +but executes the second command iff the exit status of the first command +is nonzero. ``&&'' and ``||'' both have the same priority. +.PP +The ``|'' operator is a binary operator which feeds the standard output +of the first command into the standard input of the second command. +The exit status of the ``|'' operator is the exit status of the second +command. ``|'' has a higher priority than ``||'' or ``&&''. +.PP +An +.I if +command looks like +.d +\fBif\fR list +\fBthen\fR list +.ti -\w'[ 'u +[ \fBelif\fR list + \fBthen\fR list ] ... +.ti -\w'[ 'u +[ \fBelse\fR list ] +\fBfi\fR +.e +.PP +A +.I while +command looks like +.d +\fBwhile\fR list +\fBdo\fR list +\fBdone\fR +.e +The two lists are executed repeatedly while the exit status of the first +list is zero. The +.I until +command is similar, but has the word +.B until +in place of +.B while + repeats until the exit status of the first list +is zero. +.PP +The +.I for +command looks like +.d +\fBfor\fR variable \fBin\fR word... +\fBdo\fR list +\fBdone\fR +.e +The words are expanded, and then the list is executed repeatedly with +the variable set to each word in turn. +.B do +and +.B done +may be replaced with +``{'' and ``}''. +.PP +The +.I break +and +.I continue +commands look like +.d +\fBbreak\fR [ num ] +\fBcontinue\fR [ num ] +.e +.I Break +terminates the +.I num +innermost +.I for +or +.I while +loops. +.I Continue +continues with the next iteration of the +.IR num'th +innermost loop. +These are implemented as builtin commands. +.PP +The +.I case +command looks like +.d +\fBcase\fR word \fBin\fR +pattern\fB)\fR list \fB;;\fR +\&... +\fBesac\fR +.e +The pattern can actually be one or more patterns (see +.I Patterns +below), separated by ``|'' characters. +.PP +Commands may be grouped by writing either +.d +\fB(\fRlist\fB)\fR +.e +or +.d +\fB{\fR list; \fB}\fR +.e +The first of these executes the commands in a subshell. +.PP +A function definition looks like +.d +name \fB( )\fR command +.e +A function definition is an executable statement; when executed it installs +a function named +.B name +and returns an exit status of zero. +The command is normally a list enclosed between ``{'' and ``}''. +.PP +Variables may be declared to be local to a function by using a +.I local +command. This should appear as the first staement of a function, +and looks like +.d +\fBlocal\fR [ variable | \fB-\fR ] ... +.e +.I Local +is implemented as a builtin command. +.PP +When a variable is made local, it inherits the initial value and +exported and readonly flags from the variable with the same name in the +surrounding scope, if there is one. Otherwise, the variable is +initially unset. +.I Ash +uses dynamic scoping, so that if you make the variable +.B x +local to function +.IR f , +which then calls function +.IR g , +references to the variable +.B x +made inside +.I g +will refer to the variable +.B x +declared inside +.IR f , +not to the global variable named +.BR x . +.PP +The only special parameter than can be made local is ``\fB-\fR''. +Making ``\fB-\fR'' local any shell options that are changed via the +.I set +command inside the function to be restored to their original values +when the function returns. +.PP +The +.I return +command looks like +.d +\fBreturn\fR [ exitstatus ] +.e +It terminates the currently executing function. +.I Return +is implemented as a builtin command. +.h "Simple Commands" +A simple command is a sequence of words. The execution of a simple +command proceeds as follows. First, the leading words of the form +``name=value'' are stripped off and assigned to the environment of +the command. Second, the words are expanded. Third, the first +remaining word is taken as the command name that command is located. +Fourth, any redirections are performed. Fifth, the command is +executed. We look at these operations in reverse order. +.PP +The execution of the command varies with the type of command. +There are three types of commands: shell functions, builtin commands, +and normal programs. +.PP +When a shell function is executed, all of the shell positional parameters +(except $0, which remains unchanged) are set to the parameters to the shell +function. The variables which are explicitly placed in the environment +of the command (by placing assignments to them before the function name) +are made local to the function and are set to values given. +Then the command given in the function definition is executed. +The positional parameters are restored to their original values when +the command completes. +.PP +Shell builtins are executed internally to the shell, without spawning +a new process. +.PP +When a normal program is executed, the shell runs the program, passing +the parameters and the environment to the program. If the program is +a shell procedure, the shell will interpret the program in a subshell. +The shell will reinitialize itself in this case, so that the effect +will be as if a new shell had been invoked to handle the shell procedure, +except that the location of commands located in the parent shell will +be remembered by the child. If the program is a file beginning with +``#!'', the remainder of the first line specifies an interpreter for +the program. The shell (or the operating system, under Berkeley UNIX) +will run the interpreter in this case. The arguments to the interpreter +will consist of any arguments given on the first line of the program, +followed by the name of the program, followed by the arguments passed +to the program. +.h "Redirection" +Input/output redirections can be intermixed with the words in a simple +command and can be placed following any of the other commands. When +redirection occurs, the shell saves the old values of the file descriptors +and restores them when the command completes. The ``<'', ``>'', and ``>>'' +redirections open a file for input, output, and appending, respectively. +The ``<&digit'' and ``>&digit'' makes the input or output a duplicate +of the file descriptor numbered by the digit. If a minus sign is used +in place of a digit, the standard input or standard output are closed. +.PP +The ``<<\ word'' redirection +takes input from a +.I here +document. +As the shell encounters ``<<'' redirections, it collects them. The +next time it encounters an unescaped newline, it reads the documents +in turn. The word following the ``<<'' specifies the contents of the +line that terminates the document. If none of the quoting methods +('', "", or \e) are used to enter the word, then the document is treated +like a word inside double quotes: ``$'' and backquote are expanded +and backslash can be used to escape these and to continue long lines. +The word cannot contain any variable or command substitutions, and +its length (after quoting) must be in the range of 1 to 79 characters. +If ``<<-'' is used in place of ``<<'', then leading tabs are deleted +from the lines of the document. (This is to allow you do indent shell +procedures containing here documents in a natural fashion.) +.PP +Any of the preceding redirection operators may be preceded by a single +digit specifying the file descriptor to be redirected. There cannot +be any white space between the digit and the redirection operator. +.h "Path Search" +When locating a command, the shell first looks to see if it has a +shell function by that name. Then, if PATH does not contain an +entry for "%builtin", it looks for a builtin command by that name. +Finally, it searches each entry in PATH in turn for the command. +.PP +The value of the PATH variable should be a series of entries separated +by colons. +Each entry consists of a directory name, or a directory name followed +by a flag beginning with a percent sign. +The current directory should be indicated by an empty directory name. +.PP +If no percent sign is present, then the entry causes the shell to +search for the command in the specified directory. If the flag is +``%builtin'' then the list of shell builtin commands is searched. +If the flag is ``%func'' then the directory is searched for a file which +is read as input to the shell. This file should define a function +whose name is the name of the command being searched for. +.PP +Command names containing a slash are simply executed without performing +any of the above searches. +.h "The Environment" +The environment of a command is a set of name/value pairs. When the +shell is invoked, it reads these names and values, sets the shell +variables with these names to the corresponding values, and marks +the variables as exported. The +.I export +command can be used to mark additional variables as exported. +.PP +The environment of a command is constructed by constructing name/value +pairs from all the exported shell variables, and then modifying this +set by the assignments which precede the command, if any. +.h "Expansion" +The process of evaluating words when a shell procedure is executed is +called +.IR expansion . +Expansion consists of four steps: variable substitution, command +substitution, word splitting, and file name generation. If a word +is the expression following the word +.B case +in a case statement, the file name +which follows a redirection symbol, or an assignment to the environment +of a command, then the word cannot be split into multiple words. In +these cases, the last two steps of the expansion process are omitted. +.h "Variable Substitution" +To be written. +.h "Command Substitution" +.I Ash +accepts two syntaxes for command substitution: +.b +`\fIlist\fR` +.e +and +.b +$(\fIlist\fR) +.e +Either of these may be included in a word. +During the command substitution process, the command (syntactly a +.IR list ) +will be executed and anything that the command writes to the standard +output will be captured by the shell. The final newline (if any) of +the output will be deleted; the rest of the output will be substituted +for the command in the word. +.h "Word Splitting" +When the value of a variable or the output of a command is substituted, +the resulting text is subject to word splitting, unless the dollar sign +introducing the variable or backquotes containing the text were enclosed +in double quotes. In addition, ``$@'' is subject to a special type of +splitting, even in the presence of double quotes. +.PP +Ash uses two different splitting algorithms. The normal approach, which +is intended for splitting text separated by which space, is used if the +first character of the shell variable IFS is a space. Otherwise an alternative +experimental algorithm, which is useful for splitting (possibly empty) +fields separated by a separator character, is used. +.PP +When performing splitting, the shell scans the replacement text looking +for a character (when IFS does not begin with a space) or a sequence of +characters (when IFS does begin with a space), deletes the character or +sequence of characters, and spits the word into two strings at that +point. When IFS begins with a space, the shell deletes either of the +strings if they are null. As a special case, if the word containing +the replacement text is the null string, the word is deleted. +.PP +The variable ``$@'' is special in two ways. First, splitting takes +place between the positional parameters, even if the text is enclosed +in double quotes. Second, if the word containing the replacement +text is the null string and there are no positional parameters, then +the word is deleted. The result of these rules is that "$@" is +equivalent to "$1" "$2" ... "$\fIn\fR", where \fIn\fR is the number of +positional parameters. (Note that this differs from the System V shell. +The System V documentation claims that "$@" behaves this way; in fact +on the System V shell "$@" is equivalent to "" when there are no +positional paramteters.) +.h "File Name Generation" +Unless the +.B -f +flag is set, file name generation is performed after word splitting is +complete. Each word is viewed as a series of patterns, separated by +slashes. The process of expansion replaces the word with the names of +all existing files whose names can be formed by replacing each pattern +with a string that matches the specified pattern. There are two +restrictions on this: first, a pattern cannot match a string containing +a slash, and second, a pattern cannot match a string starting with a +period unless the first character of the pattern is a period. +.PP +If a word fails to match any files and the +.B -z +flag is not set, then the word will be left unchanged (except that the +meta-characters will be converted to normal characters). If the +.B -z +flag is set, then the word is only left unchanged if none +of the patterns contain a character that can match anything besides +itself. Otherwise the +.B -z +flag forces the word to be replaced with the names of the files that it +matches, even if there are zero names. +.h "Patterns" +A +.I pattern +consists of normal characters, which match themselves, and meta-characters. +The meta-characters are ``!'', ``*'', ``?'', and ``[''. These characters lose +there special meanings if they are quoted. When command or variable +substitution is performed and the dollar sign or back quotes are not +double quoted, the value of the variable or the output of the command +is scanned for these characters and they are turned into meta-characters. +.PP +Two exclamation points at the beginning of a pattern function as a ``not'' +operator, causing the pattern to match any string that the remainder of +the pattern does +.I not +match. Other occurances of exclamation points in a pattern match +exclamation points. Two exclamation points are required rather than one +to decrease the incompatibility with the System V shell (which does not +treat exclamation points specially). +.PP +An asterisk (``*'') matches any string of characters. +A question mark matches any single character. +A left bracket (``['') introduces a character class. The end of the +character class is indicated by a ``]''; if the ``]'' is missing then +the ``['' matches a ``['' rather than introducing a character class. +A character class matches any of the characters between the square +brackets. A range of characters may be specified using a minus sign. +The character class may be complemented by making an exclamation point +the first character of the character class. +.PP +To include a ``]'' in a character class, make it the first character listed +(after the ``!'', if any). +To include a minus sign, make it the first or last character listed. +.h "The /u Directory" +By convention, the name ``/u/user'' refers to the home directory of the +specified user. There are good reasons why this feature should be supported +by the file system (using a feature such as symbolic links) rather than +by the shell, but +.I ash +is capable of performing this mapping if the file system doesn't. +If the mapping is done by +.IR ash , +setting the +.B -f +flag will turn it off. +.h "Character Set" +.I Ash +silently discards nul characters. Any other character will be handled +correctly by +.IR ash , +including characters with the high order bit set. +.h "Job Names and Job Control" +The term +.I job +refers to a process created by a shell command, or in the case of a +pipeline, to the set of processes in the pipeline. The ways to refer +to a job are: +.b +%\fInumber\fR +%\fIstring\fR +%% +\fIprocess_id\fR +.e +The first form identifies a job by job number. +When a command is run, +.I ash +assigns it a job number +(the lowest unused number is assigned). +The second form identifies a job by giving a prefix of the command used +to create the job. The prefix must be unique. If there is only one job, +then the null prefix will identify the job, so you can refer to the job +by writing ``%''. The third form refers to the \fIcurrent job\fR. The +current job is the last job to be stopped while it was in the foreground. +(See the next paragraph.) The last form identifies a job by giving the +process id of the last process in the job. +.PP +If the operating system that +.I ash +is running on supports job control, +.I ash +will allow you to use it. +In this case, typing the suspend character (typically ^Z) while running +a command will return you to +.I ash +and will make the suspended command the current job. You can then continue +the job in the background by typing +.IR bg , +or you can continue it in the foreground by typing +.IR fg . +.h "Atty" +If the shell variable ATTY is set, and the shell variable TERM is not +set to ``emacs'', then \fIash\fR generates appropriate escape sequences +to talk to +.IR atty (1). +.h "Exit Statuses" +By tradition, an exit status of zero means that a command has succeeded +and a nonzero exit status indicates that the command failed. This is +better than no convention at all, but in practice it is extremely useful +to allow commands that succeed to use the exit status to return information +to the caller. A variety of better conventions have been proposed, but +none of them has met with universal approval. The convention used by +\fIash\fR and all the programs included in the \fIash\fR distribution is +as follows: +.ta 1i 2i +.nf + 0 Success. + 1 Alternate success. + 2 Failure. + 129-... Command terminated by a signal. +.fi +The \fIalternate success\fR return is used by commands to indicate various +conditions which are not errors but which can, with a little imagination, +be conceived of as less successful than plain success. For example, +.I test +returns 1 when the tested condition is false and +.I getopts +returns 1 when there are no more options. +Because this convention is not used universally, the +.B -e +option of +.I ash +causes the shell to exit when a command returns 1 even though that +contradicts the convention described here. +.PP +When a command is terminated by a signal, the uses 128 plus the signal +number as the exit code for the command. +.h "Builtin Commands" +This concluding section lists the builtin commands which are builtin +because they need to perform some operation that can't be performed by a +separate process. In addition to these, there are several other commands +.RI ( catf , +.IR echo , +.IR expr , +.IR line , +.IR nlecho , +.IR test , +.RI `` : '', +and +.IR true ) +which can optionally be compiled into the shell. The builtin +commands described below that accept options use the System V Release 2 +.IR getopt (3) +syntax. +.sp +.b bg +[ +.I job +] ... +.br +Continue the specified jobs (or the current job if no jobs are given) +in the background. +This command is only available on systems with Bekeley job control. +.b bltin +.IR "command arg" ... +.br +Execute the specified builtin command. (This is useful when you have a +shell function with the same name as a builtin command.) +.b cd +[ +.I directory +] +.br +Switch to the specified directory (default $HOME). +If the an entry for CDPATH appears in the environment of the cd command +or the shell variable CDPATH is set and the directory name does not +begin with a slash, then the directories listed in CDPATH will be +searched for the specified directory. The format of CDPATH is the +same as that of PATH. +In an interactive shell, the cd command will print out the name of the +directory that it actually switched to if this is different from the +name that the user gave. These may be different either because +the CDPATH mechanism was used or because a symbolic link was crossed. +.b ".\fI\h'0.1i'file" +.br +The commands in the specified file are read and executed by the shell. +A path search is not done to find the file because the directories in +PATH generally contain files that are intended to be executed, not read. +.b eval +.IR string ... +.br +The strings are parsed as shell commands and executed. +(This differs from the System V shell, which concatenates the arguments +(separated by spaces) and parses the result as a single command.) +.b exec +[ +.IR "command arg" ... +] +.br +Unless +.I command +is omitted, +the shell process is replaced with the specified program (which must be a real +program, not a shell builtin or function). +Any redirections on the exec command are marked as permanent, so that they +are not undone when the exec command finishes. +If the command is not found, the exec command causes the shell to exit. +.b exit +[ +.I exitstatus +] +.br +Terminate the shell process. If +.I exitstatus +is given it is used as the +exit status of the shell; otherwise the exit status of the preceding +command is used. +.b export +.IR name ... +.br +The specified names are exported so that they will appear in the environment +of subsequent commands. The only way to un-export a variable is to unset it. +.I Ash +allows the value of a variable to be set at the same time it is exported +by writing +.d +\fBexport\fR name=value +.e +With no arguments the export command lists the names of all exported variables. +.b fg +[ +.I job +] +.br +Move the specified job or the current job to the foreground. +This command is only available on systems with Bekeley job control. +.b getopts +.I optstring +.I var +.br +The System V +.I getopts +command. +.b hash +.B -rv +.IR command ... +.br +The shell maintains a hash table which remembers the locations of +commands. With no arguments whatsoever, the hash command prints +out the contents of this table. Entries which have not been looked +at since the last +.I cd +command are marked with an asterisk; it is possible for these entries +to be invalid. +.sp +With arguments, the hash command removes the specified commands from +the hash table (unless they are functions) and then locates them. +With the +.B -v +option, +.I hash +prints the locations of the commands as it finds them. +The +.B -r +option causes the +.I hash +command to delete all the entries in the hash table except for +functions. +.b jobid +[ +.I job +] +.br +Print the process id's of the processes in the job. If the job argument +is omitted, use the current job. +.b jobs +.br +This command lists out all the background processes which are children +of the current shell process. +.b lc +[ +.I function-name +] +.br +The function name is defined to execute the last command entered. +If the function name is omitted, the last command executed is +executed again. This command only works if the +.B -i +flag is set. +.b pwd +.br +Print the current directory. The builtin command may differ from the +program of the same name because the builtin command remembers what +the current directory is rather than recomputing it each time. This +makes it faster. However, if the current directory is renamed, the +builtin version of pwd will continue to print the old name for the +directory. +.b read +[ +.B -p +.I prompt +] +[ +.B -e +] +.IR variable ... +.br +The prompt is printed if the +.B -p +option is specified and the standard input is a terminal. Then a +line is read from the standard input. The trailing newline is deleted +from the line and the line is split as described +in the section on word splitting above, and the pieces are assigned to +the variables in order. If there are more pieces than variables, the +remaining pieces (along with the characters in IFS that separated them) +are assigned to the last variable. If there are more variables than +pieces, the remaining variables are assigned the null string. +.sp +The +.B -e +option causes any backslashes in the input to be treated specially. +If a backslash is followed by a newline, the backslash and the newline +will be deleted. If a backslash is followed by any other character, +the backslash will be deleted and the following character will be treated +as though it were not in IFS, even if it is. +.b readonly +.IR name ... +.br +The specified names are marked as read only, so that they cannot be +subsequently modified or unset. +.I Ash +allows the value of a variable to be set at the same time it is marked +read only by writing +.d +\fBreadonly\fR name=value +.e +With no arguments the readonly command lists the names of all +read only variables. +.b set +[ +{ +.BI - options +| +.BI + options +| +.B -- +} +] +.IR arg ... +.br +The +.I set +command performs three different functions. +.sp +With no arguments, it lists the values of all shell variables. +.sp +If options are given, it sets the specified option flags, or clears +them if the option flags are introduced with a +.B + +rather than a +.BR - . +Only the first argument to +.I set +can contain options. +The possible options are: +.sp +.ta 0.4i +.in +0.4i +.ti -0.4i +\fB-e\fR Causes the shell to exit when a command terminates with +a nonzero exit status, except when the exit status of the command is +explicitly tested. The exit status of a command is considered to be +explicitly tested if the command is used to control an +.IR if , +.IR elif , +.IR while , +or +.IR until ; +or if the command is the left hand operand of an ``&&'' or ``||'' +operator. +.sp +.ti -0.4i +\fB-f\fR Turn off file name generation. +.sp +.ti -0.4i +\fB-I\fR Cause the shell to ignore end of file conditions. +(This doesn't apply when the shell a script sourced using the ``.'' +command.) The shell will in fact exit if it gets 50 eof's in a +row. +.sp +.ti -0.4i +\fB-i\fR Make the shell interactive. This causes the shell to +prompt for input, to trap interrupts, to ignore quit and terminate signals, +and to return to the main command loop rather than exiting on error. +.sp +.ti -0.4i +\fB-j\fR Turns on Berkeley job control, on systems that support it. +When the shell starts up, the +.B -j +is set by default if the +.B -i +flag is set. +.sp +.ti -0.4i +\fB-n\fR Causes the shell to read commands but not execute them. +(This is marginally useful for checking the syntax of scripts.) +.sp +.ti -0.4i +\fB-s\fR If this flag is set when the shell starts up, the shell +reads commands from its standard input. The shell doesn't examine the +value of this flag any other time. +.sp +.ti -0.4i +\fB-x\fR If this flag is set, the shell will print out each +command before executing it. +.sp +.ti -0.4i +\fB-z\fR If this flag is set, the file name generation process +may generate zero files. If it is not set, then a pattern which does +not match any files will be replaced by a quoted version of the pattern. +.in -0.4i +.sp +The third use of the set command is to set the values of the shell's +positional parameters to the specified +.IR args . +To change the positional parameters without changing any options, +use ``\fB--\fR'' as the first argument to +.IR set . +If no args are present, the set command will leave the value of the +positional parameters unchanged, so to set the positional parameters +to set of values that may be empty, execute the command +.d +shift $# +.e +first to clear out the old values of the positional parameters. +.b setvar +.I variable +.I value +.br +Assigns +.I value +to +.IR variable . +(In general it is better to write +.I variable=value +rather than using +.IR setvar . +.I Setvar +is intended to be used in functions that assign values to variables whose +names are passed as parameters.) +.b shift +[ +.I n +] +.br +Shift the positional parameters +.I n +times. +A shift sets the value of $1 to the value of $2, the value of $2 to +the value of $3, and so on, decreasing the value of $# by one. +If there are zero positional parameters, shifting doesn't do anything. +.b trap +[ +.I action +] +.IR signal ... +.br +Cause the shell to parse and execute +.I action +when any of the specified signals are received. +The signals are specified by signal number. +.I Action +may be null or omitted; +the former causes the specified signal to be ignored and the latter +causes the default action to be taken. +When the shell forks off a subshell, it resets trapped (but not ignored) +signals to the default action. +The trap command has no effect on signals that were ignored on entry +to the shell. +.b umask +[ +.I mask +] +.br +Set the value of umask (see +.IR umask (2)) +to the specified octal value. If the argument is omitted, the umask +value is printed. +.b unset +.IR name ... +.br +The specified variables and functions are unset and unexported. +If a given name corresponds to both a variable and a function, both the +variable and the function are unset. +.b wait +[ +.I job +] +.br +Wait for the specified job to complete and return the exit status of the +last process in the job. If the argument is omitted, wait for all jobs +to complete and the return an exit status of zero. +.SH EXAMPLES +The following function redefines the \fIcd\fR command: +.d +cd() { + if bltin cd "$@" + then if test -f .enter + then . .enter + else return 0 + fi + fi +} +.e +This function causes the file ``.enter'' to be read when you enter a +directory, if it exists. The \fIbltin\fR command is used to access the +real \fIcd\fR command. The ``return 0'' ensures that the function will +return an exit status of zero if it successfully changes to a directory +that does not contain a ``.enter'' file. Redefining existing commands +is not always a good idea, but this example shows that you can do it if +you want to. +.PP +The suspend function distributed with +.I ash +looks like +.d +# Copyright (C) 1989 by Kenneth Almquist. All rights reserved. +# This file is part of ash, which is distributed under the terms +# specified by the Ash General Public License. + +suspend() { + local - + set +j + kill -TSTP 0 +} +.e +This turns off job control and then sends a stop signal to the current +process group, which suspends the shell. (When job control is turned +on, the shell ignores the TSTP signal.) Job control will be turned back +on when the function returns because ``-'' is local to the function. +As an example of what \fInot\fR to do, consider an earlier version of +\fIsuspend\fR: +.d +suspend() { + suspend_flag=$- + set +j + kill -TSTP 0 + set -$suspend_flag +} +.e +There are two problems with this. First, \fBsuspend_flag\fR is a global +variable rather than a local one, which will cause problems in the +(unlikely) circumstance that the user is using that variable for some +other purpose. Second, consider what happens if shell received an interrupt +signal after it executes the first \fIset\fR command but before it executes +the second one. The interrupt signal will abort the shell function, so +that the second \fIset\fR command will never be executed and job control +will be left off. The first version of \fIsuspend\fR avoids this problem +by turning job control off only in a local copy of the shell options. The +local copy of the shell options is discarded when the function is terminated, +no matter how it is terminated. +.SH HINTS +Shell variables can be used to provide abbreviations for things which +you type frequently. For example, I set +.br +\h'1i'export h=$HOME +.br +in my .profile so that I can type the name of my home directory simply +by typing ``$h''. +.PP +When writing shell procedures, try not to make assumptions about what is +imported from the environment. Explicitly unset or initialize all variables, +rather than assuming they will be unset. If you use cd, it is a good idea +to unset CDPATH. +.PP +People sometimes use ``<&-'' or ``>&-'' to provide no input to a command +or to discard the output of a command. A better way to do this is +to redirect the input or output of the command to +.BR /dev/null . +.PP +Word splitting and file name generation are performed by default, +and you have to explicitly use double quotes to suppress it. This is +backwards, but you can learn to live with it. Just get in the habit of +writing double quotes around variable and command substitutions, and +omit them only when you really want word splitting and file name generation. +If you want word splitting but not file name generation, use the +.B -f +option. +.SH AUTHORS +Kenneth Almquist +.SH "SEE ALSO" +echo(1), expr(1), line(1), pwd(1), true(1). +.SH BUGS +When command substitution occurs inside a here document, the commands inside +the here document are run with their standard input closed. For example, +the following will not word because the standard input of the +.I line +command will be closed when the command is run: +.d +cat <<-! +Line 1: $(line) +Line 2: $(line) +! +.e +.PP +Unsetting a function which is currently being executed may cause strange +behavior. +.PP +The shell syntax allows a here document to be terminated by an end of file +as well as by a line containing the terminator word which follows the ``<<''. +What this means is that if you mistype the terminator line, the shell +will silently swallow up the rest of your shell script and stick it +in the here document. diff --git a/bin/sh/shell.h b/bin/sh/shell.h new file mode 100644 index 000000000000..740c941d3fd6 --- /dev/null +++ b/bin/sh/shell.h @@ -0,0 +1,89 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * from: @(#)shell.h 5.4 (Berkeley) 4/12/91 + * shell.h,v 1.4 1993/08/01 18:58:35 mycroft Exp + */ + +/* + * The follow should be set to reflect the type of system you have: + * JOBS -> 1 if you have Berkeley job control, 0 otherwise. + * SYMLINKS -> 1 if your system includes symbolic links, 0 otherwise. + * DIRENT -> 1 if your system has the SVR3 directory(3X) routines. + * UDIR -> 1 if you want the shell to simulate the /u directory. + * ATTY -> 1 to include code for atty(1). + * SHORTNAMES -> 1 if your linker cannot handle long names. + * define BSD if you are running 4.2 BSD or later. + * define SYSV if you are running under System V. + * define DEBUG=1 to compile in debugging (set global "debug" to turn on) + * define DEBUG=2 to compile in and turn on debugging. + * + * When debugging is on, debugging info will be written to $HOME/trace and + * a quit signal will generate a core dump. + */ + + +#define JOBS 1 +#define SYMLINKS 1 +#define DIRENT 1 +#define UDIR 0 +#define ATTY 0 +#define BSD +#define DEBUG 1 + +#ifdef __STDC__ +typedef void *pointer; +#ifndef NULL +#define NULL (void *)0 +#endif +#else /* not __STDC__ */ +typedef char *pointer; +#ifndef NULL +#define NULL 0 +#endif +#endif /* not __STDC__ */ +#define STATIC /* empty */ +#define MKINIT /* empty */ + +#include <sys/cdefs.h> + +extern char nullstr[1]; /* null string */ + + +#ifdef DEBUG +#define TRACE(param) trace param +#else +#define TRACE(param) +#endif diff --git a/bin/sh/show.c b/bin/sh/show.c new file mode 100644 index 000000000000..edcd5f3a1943 --- /dev/null +++ b/bin/sh/show.c @@ -0,0 +1,375 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)show.c 5.2 (Berkeley) 4/12/91";*/ +static char rcsid[] = "show.c,v 1.4 1993/08/01 18:58:00 mycroft Exp"; +#endif /* not lint */ + +#include <stdio.h> +#include "shell.h" +#include "parser.h" +#include "nodes.h" +#include "mystring.h" + + +#ifdef DEBUG +static shtree(), shcmd(), sharg(), indent(); + + +showtree(n) + union node *n; + { + trputs("showtree called\n"); + shtree(n, 1, NULL, stdout); +} + + +static +shtree(n, ind, pfx, fp) + union node *n; + char *pfx; + FILE *fp; + { + struct nodelist *lp; + char *s; + + indent(ind, pfx, fp); + switch(n->type) { + case NSEMI: + s = "; "; + goto binop; + case NAND: + s = " && "; + goto binop; + case NOR: + s = " || "; +binop: + shtree(n->nbinary.ch1, ind, NULL, fp); + /* if (ind < 0) */ + fputs(s, fp); + shtree(n->nbinary.ch2, ind, NULL, fp); + break; + case NCMD: + shcmd(n, fp); + if (ind >= 0) + putc('\n', fp); + break; + case NPIPE: + for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { + shcmd(lp->n, fp); + if (lp->next) + fputs(" | ", fp); + } + if (n->npipe.backgnd) + fputs(" &", fp); + if (ind >= 0) + putc('\n', fp); + break; + default: + fprintf(fp, "<node type %d>", n->type); + if (ind >= 0) + putc('\n', fp); + break; + } +} + + + +static +shcmd(cmd, fp) + union node *cmd; + FILE *fp; + { + union node *np; + int first; + char *s; + int dftfd; + + first = 1; + for (np = cmd->ncmd.args ; np ; np = np->narg.next) { + if (! first) + putchar(' '); + sharg(np, fp); + first = 0; + } + for (np = cmd->ncmd.redirect ; np ; np = np->nfile.next) { + if (! first) + putchar(' '); + switch (np->nfile.type) { + case NTO: s = ">"; dftfd = 1; break; + case NAPPEND: s = ">>"; dftfd = 1; break; + case NTOFD: s = ">&"; dftfd = 1; break; + case NFROM: s = "<"; dftfd = 0; break; + case NFROMFD: s = "<&"; dftfd = 0; break; + } + if (np->nfile.fd != dftfd) + fprintf(fp, "%d", np->nfile.fd); + fputs(s, fp); + if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) { + fprintf(fp, "%d", np->ndup.dupfd); + } else { + sharg(np->nfile.fname, fp); + } + first = 0; + } +} + + + +static +sharg(arg, fp) + union node *arg; + FILE *fp; + { + char *p; + struct nodelist *bqlist; + int subtype; + + if (arg->type != NARG) { + printf("<node type %d>\n", arg->type); + fflush(stdout); + abort(); + } + bqlist = arg->narg.backquote; + for (p = arg->narg.text ; *p ; p++) { + switch (*p) { + case CTLESC: + putc(*++p, fp); + break; + case CTLVAR: + putc('$', fp); + putc('{', fp); + subtype = *++p; + while (*p != '=') + putc(*p++, fp); + if (subtype & VSNUL) + putc(':', fp); + switch (subtype & VSTYPE) { + case VSNORMAL: + putc('}', fp); + break; + case VSMINUS: + putc('-', fp); + break; + case VSPLUS: + putc('+', fp); + break; + case VSQUESTION: + putc('?', fp); + break; + case VSASSIGN: + putc('=', fp); + break; + default: + printf("<subtype %d>", subtype); + } + break; + case CTLENDVAR: + putc('}', fp); + break; + case CTLBACKQ: + case CTLBACKQ|CTLQUOTE: + putc('$', fp); + putc('(', fp); + shtree(bqlist->n, -1, NULL, fp); + putc(')', fp); + break; + default: + putc(*p, fp); + break; + } + } +} + + +static +indent(amount, pfx, fp) + char *pfx; + FILE *fp; + { + int i; + + for (i = 0 ; i < amount ; i++) { + if (pfx && i == amount - 1) + fputs(pfx, fp); + putc('\t', fp); + } +} +#endif + + + +/* + * Debugging stuff. + */ + + +FILE *tracefile; + +#if DEBUG == 2 +int debug = 1; +#else +int debug = 0; +#endif + + +trputc(c) { +#ifdef DEBUG + if (tracefile == NULL) + return; + putc(c, tracefile); + if (c == '\n') + fflush(tracefile); +#endif +} + + +trace(fmt, a1, a2, a3, a4, a5, a6, a7, a8) + char *fmt; + { +#ifdef DEBUG + if (tracefile == NULL) + return; + fprintf(tracefile, fmt, a1, a2, a3, a4, a5, a6, a7, a8); + if (strchr(fmt, '\n')) + fflush(tracefile); +#endif +} + + +trputs(s) + char *s; + { +#ifdef DEBUG + if (tracefile == NULL) + return; + fputs(s, tracefile); + if (strchr(s, '\n')) + fflush(tracefile); +#endif +} + + +trstring(s) + char *s; + { + register char *p; + char c; + +#ifdef DEBUG + if (tracefile == NULL) + return; + putc('"', tracefile); + for (p = s ; *p ; p++) { + switch (*p) { + case '\n': c = 'n'; goto backslash; + case '\t': c = 't'; goto backslash; + case '\r': c = 'r'; goto backslash; + case '"': c = '"'; goto backslash; + case '\\': c = '\\'; goto backslash; + case CTLESC: c = 'e'; goto backslash; + case CTLVAR: c = 'v'; goto backslash; + case CTLVAR+CTLQUOTE: c = 'V'; goto backslash; + case CTLBACKQ: c = 'q'; goto backslash; + case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash; +backslash: putc('\\', tracefile); + putc(c, tracefile); + break; + default: + if (*p >= ' ' && *p <= '~') + putc(*p, tracefile); + else { + putc('\\', tracefile); + putc(*p >> 6 & 03, tracefile); + putc(*p >> 3 & 07, tracefile); + putc(*p & 07, tracefile); + } + break; + } + } + putc('"', tracefile); +#endif +} + + +trargs(ap) + char **ap; + { +#ifdef DEBUG + if (tracefile == NULL) + return; + while (*ap) { + trstring(*ap++); + if (*ap) + putc(' ', tracefile); + else + putc('\n', tracefile); + } + fflush(tracefile); +#endif +} + + +opentrace() { + char s[100]; + char *p; + char *getenv(); + int flags; + +#ifdef DEBUG + if (!debug) + return; + if ((p = getenv("HOME")) == NULL) { + if (getuid() == 0) + p = "/"; + else + p = "/tmp"; + } + scopy(p, s); + strcat(s, "/trace"); + if ((tracefile = fopen(s, "a")) == NULL) { + fprintf(stderr, "Can't open %s\n", s); + return; + } +#ifdef O_APPEND + if ((flags = fcntl(fileno(tracefile), F_GETFL, 0)) >= 0) + fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND); +#endif + fputs("\nTracing started.\n", tracefile); + fflush(tracefile); +#endif +} diff --git a/bin/sh/trap.c b/bin/sh/trap.c new file mode 100644 index 000000000000..f1a4e82d2473 --- /dev/null +++ b/bin/sh/trap.c @@ -0,0 +1,327 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)trap.c 5.2 (Berkeley) 4/12/91";*/ +static char rcsid[] = "trap.c,v 1.5 1993/08/06 21:50:18 mycroft Exp"; +#endif /* not lint */ + +#include "shell.h" +#include "main.h" +#include "nodes.h" /* for other headers */ +#include "eval.h" +#include "jobs.h" +#include "options.h" +#include "syntax.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "trap.h" +#include "mystring.h" +#include <signal.h> + + +/* + * Sigmode records the current value of the signal handlers for the various + * modes. A value of zero means that the current handler is not known. + * S_HARD_IGN indicates that the signal was ignored on entry to the shell, + */ + +#define S_DFL 1 /* default signal handling (SIG_DFL) */ +#define S_CATCH 2 /* signal is caught */ +#define S_IGN 3 /* signal is ignored (SIG_IGN) */ +#define S_HARD_IGN 4 /* signal is ignored permenantly */ + + +extern char nullstr[1]; /* null string */ + +char *trap[NSIG]; /* trap handler commands */ +MKINIT char sigmode[NSIG]; /* current value of signal */ +char gotsig[NSIG]; /* indicates specified signal received */ +int pendingsigs; /* indicates some signal received */ + +/* + * The trap builtin. + */ + +trapcmd(argc, argv) char **argv; { + char *action; + char **ap; + int signo; + + if (argc <= 1) { + for (signo = 0 ; signo < NSIG ; signo++) { + if (trap[signo] != NULL) + out1fmt("%d: %s\n", signo, trap[signo]); + } + return 0; + } + ap = argv + 1; + if (is_number(*ap)) + action = NULL; + else + action = *ap++; + while (*ap) { + if ((signo = number(*ap)) < 0 || signo >= NSIG) + error("%s: bad trap", *ap); + INTOFF; + if (action) + action = savestr(action); + if (trap[signo]) + ckfree(trap[signo]); + trap[signo] = action; + if (signo != 0) + setsignal(signo); + INTON; + ap++; + } + return 0; +} + + + +/* + * Clear traps on a fork. + */ + +void +clear_traps() { + char **tp; + + for (tp = trap ; tp < &trap[NSIG] ; tp++) { + if (*tp && **tp) { /* trap not NULL or SIG_IGN */ + INTOFF; + ckfree(*tp); + *tp = NULL; + if (tp != &trap[0]) + setsignal(tp - trap); + INTON; + } + } +} + + + +/* + * Set the signal handler for the specified signal. The routine figures + * out what it should be set to. + */ + +int +setsignal(signo) { + int action; + sig_t sigact; + char *t; + extern void onsig(); + + if ((t = trap[signo]) == NULL) + action = S_DFL; + else if (*t != '\0') + action = S_CATCH; + else + action = S_IGN; + if (rootshell && action == S_DFL) { + switch (signo) { + case SIGINT: + if (iflag) + action = S_CATCH; + break; + case SIGQUIT: +#ifdef DEBUG + { + extern int debug; + + if (debug) + break; + } +#endif + /* FALLTHROUGH */ + case SIGTERM: + if (iflag) + action = S_IGN; + break; +#if JOBS + case SIGTSTP: + case SIGTTOU: + if (jflag) + action = S_IGN; + break; +#endif + } + } + t = &sigmode[signo]; + if (*t == 0) { /* current setting unknown */ + /* + * There is a race condition here if action is not S_IGN. + * A signal can be ignored that shouldn't be. + */ + if ((int)(sigact = signal(signo, SIG_IGN)) == -1) + error("Signal system call failed"); + if (sigact == SIG_IGN) { + *t = S_HARD_IGN; + } else { + *t = S_IGN; + } + } + if (*t == S_HARD_IGN || *t == action) + return 0; + switch (action) { + case S_DFL: sigact = SIG_DFL; break; + case S_CATCH: sigact = onsig; break; + case S_IGN: sigact = SIG_IGN; break; + } + *t = action; + return (int)signal(signo, sigact); +} + + +/* + * Ignore a signal. + */ + +void +ignoresig(signo) { + if (sigmode[signo] != S_IGN && sigmode[signo] != S_HARD_IGN) { + signal(signo, SIG_IGN); + } + sigmode[signo] = S_HARD_IGN; +} + + +#ifdef mkinit +INCLUDE <sys/signal.h> +INCLUDE "trap.h" + +SHELLPROC { + char *sm; + + clear_traps(); + for (sm = sigmode ; sm < sigmode + NSIG ; sm++) { + if (*sm == S_IGN) + *sm = S_HARD_IGN; + } +} +#endif + + + +/* + * Signal handler. + */ + +void +onsig(signo) { + signal(signo, onsig); + if (signo == SIGINT && trap[SIGINT] == NULL) { + onint(); + return; + } + gotsig[signo] = 1; + pendingsigs++; +} + + + +/* + * Called to execute a trap. Perhaps we should avoid entering new trap + * handlers while we are executing a trap handler. + */ + +void +dotrap() { + int i; + int savestatus; + + for (;;) { + for (i = 1 ; ; i++) { + if (i >= NSIG) + goto done; + if (gotsig[i]) + break; + } + gotsig[i] = 0; + savestatus=exitstatus; + evalstring(trap[i]); + exitstatus=savestatus; + } +done: + pendingsigs = 0; +} + + + +/* + * Controls whether the shell is interactive or not. + */ + +int is_interactive; + +void +setinteractive(on) { + if (on == is_interactive) + return; + setsignal(SIGINT); + setsignal(SIGQUIT); + setsignal(SIGTERM); + is_interactive = on; +} + + + +/* + * Called to exit the shell. + */ + +void +exitshell(status) { + struct jmploc loc1, loc2; + char *p; + + TRACE(("exitshell(%d) pid=%d\n", status, getpid())); + if (setjmp(loc1.loc)) goto l1; + if (setjmp(loc2.loc)) goto l2; + handler = &loc1; + if ((p = trap[0]) != NULL && *p != '\0') { + trap[0] = NULL; + evalstring(p); + } +l1: handler = &loc2; /* probably unnecessary */ + flushall(); +#if JOBS + setjobctl(0); +#endif +l2: _exit(status); +} diff --git a/bin/sh/trap.h b/bin/sh/trap.h new file mode 100644 index 000000000000..16e8d874063e --- /dev/null +++ b/bin/sh/trap.h @@ -0,0 +1,56 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * from: @(#)trap.h 5.1 (Berkeley) 3/7/91 + * trap.h,v 1.4 1993/08/01 18:58:34 mycroft Exp + */ + +extern int pendingsigs; + +#ifdef __STDC__ +void clear_traps(void); +int setsignal(int); +void ignoresig(int); +void dotrap(void); +void setinteractive(int); +void exitshell(int); +#else +void clear_traps(); +int setsignal(); +void ignoresig(); +void dotrap(); +void setinteractive(); +void exitshell(); +#endif diff --git a/bin/sh/var.c b/bin/sh/var.c new file mode 100644 index 000000000000..8adbf5df1614 --- /dev/null +++ b/bin/sh/var.c @@ -0,0 +1,650 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)var.c 5.3 (Berkeley) 4/12/91";*/ +static char rcsid[] = "var.c,v 1.4 1993/08/01 18:57:58 mycroft Exp"; +#endif /* not lint */ + +/* + * Shell variables. + */ + +#include "shell.h" +#include "output.h" +#include "expand.h" +#include "nodes.h" /* for other headers */ +#include "eval.h" /* defines cmdenviron */ +#include "exec.h" +#include "syntax.h" +#include "options.h" +#include "mail.h" +#include "var.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" + + +#define VTABSIZE 39 + + +struct varinit { + struct var *var; + int flags; + char *text; +}; + + +#if ATTY +struct var vatty; +#endif +struct var vifs; +struct var vmail; +struct var vmpath; +struct var vpath; +struct var vps1; +struct var vps2; +struct var vvers; +#if ATTY +struct var vterm; +#endif + +const struct varinit varinit[] = { +#if ATTY + {&vatty, VSTRFIXED|VTEXTFIXED|VUNSET, "ATTY="}, +#endif + {&vifs, VSTRFIXED|VTEXTFIXED, "IFS= \t\n"}, + {&vmail, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL="}, + {&vmpath, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH="}, + {&vpath, VSTRFIXED|VTEXTFIXED, "PATH=:/bin:/usr/bin"}, + /* + * vps1 depends on uid + */ + {&vps2, VSTRFIXED|VTEXTFIXED, "PS2=> "}, +#if ATTY + {&vterm, VSTRFIXED|VTEXTFIXED|VUNSET, "TERM="}, +#endif + {NULL, 0, NULL} +}; + +struct var *vartab[VTABSIZE]; + +STATIC void unsetvar __P((char *)); +STATIC struct var **hashvar __P((char *)); +STATIC int varequal __P((char *, char *)); + +/* + * Initialize the varable symbol tables and import the environment + */ + +#ifdef mkinit +INCLUDE "var.h" +INIT { + char **envp; + extern char **environ; + + initvar(); + for (envp = environ ; *envp ; envp++) { + if (strchr(*envp, '=')) { + setvareq(*envp, VEXPORT|VTEXTFIXED); + } + } +} +#endif + + +/* + * This routine initializes the builtin variables. It is called when the + * shell is initialized and again when a shell procedure is spawned. + */ + +void +initvar() { + const struct varinit *ip; + struct var *vp; + struct var **vpp; + + for (ip = varinit ; (vp = ip->var) != NULL ; ip++) { + if ((vp->flags & VEXPORT) == 0) { + vpp = hashvar(ip->text); + vp->next = *vpp; + *vpp = vp; + vp->text = ip->text; + vp->flags = ip->flags; + } + } + /* + * PS1 depends on uid + */ + if ((vps1.flags & VEXPORT) == 0) { + vpp = hashvar("PS1="); + vps1.next = *vpp; + *vpp = &vps1; + vps1.text = getuid() ? "PS1=$ " : "PS1=# "; + vps1.flags = VSTRFIXED|VTEXTFIXED; + } +} + +/* + * Set the value of a variable. The flags argument is ored with the + * flags of the variable. If val is NULL, the variable is unset. + */ + +void +setvar(name, val, flags) + char *name, *val; + { + char *p, *q; + int len; + int namelen; + char *nameeq; + int isbad; + + isbad = 0; + p = name; + if (! is_name(*p++)) + isbad = 1; + for (;;) { + if (! is_in_name(*p)) { + if (*p == '\0' || *p == '=') + break; + isbad = 1; + } + p++; + } + namelen = p - name; + if (isbad) + error("%.*s: is read only", namelen, name); + len = namelen + 2; /* 2 is space for '=' and '\0' */ + if (val == NULL) { + flags |= VUNSET; + } else { + len += strlen(val); + } + p = nameeq = ckmalloc(len); + q = name; + while (--namelen >= 0) + *p++ = *q++; + *p++ = '='; + *p = '\0'; + if (val) + scopy(val, p); + setvareq(nameeq, flags); +} + + + +/* + * Same as setvar except that the variable and value are passed in + * the first argument as name=value. Since the first argument will + * be actually stored in the table, it should not be a string that + * will go away. + */ + +void +setvareq(s, flags) + char *s; + { + struct var *vp, **vpp; + + vpp = hashvar(s); + for (vp = *vpp ; vp ; vp = vp->next) { + if (varequal(s, vp->text)) { + if (vp->flags & VREADONLY) { + int len = strchr(s, '=') - s; + error("%.*s: is read only", len, s); + } + INTOFF; + if (vp == &vpath) + changepath(s + 5); /* 5 = strlen("PATH=") */ + if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) + ckfree(vp->text); + vp->flags &=~ (VTEXTFIXED|VSTACK|VUNSET); + vp->flags |= flags; + vp->text = s; + if (vp == &vmpath || (vp == &vmail && ! mpathset())) + chkmail(1); + INTON; + return; + } + } + /* not found */ + vp = ckmalloc(sizeof (*vp)); + vp->flags = flags; + vp->text = s; + vp->next = *vpp; + *vpp = vp; +} + + + +/* + * Process a linked list of variable assignments. + */ + +void +listsetvar(list) + struct strlist *list; + { + struct strlist *lp; + + INTOFF; + for (lp = list ; lp ; lp = lp->next) { + setvareq(savestr(lp->text), 0); + } + INTON; +} + + + +/* + * Find the value of a variable. Returns NULL if not set. + */ + +char * +lookupvar(name) + char *name; + { + struct var *v; + + for (v = *hashvar(name) ; v ; v = v->next) { + if (varequal(v->text, name)) { + if (v->flags & VUNSET) + return NULL; + return strchr(v->text, '=') + 1; + } + } + return NULL; +} + + + +/* + * Search the environment of a builtin command. If the second argument + * is nonzero, return the value of a variable even if it hasn't been + * exported. + */ + +char * +bltinlookup(name, doall) + char *name; + { + struct strlist *sp; + struct var *v; + + for (sp = cmdenviron ; sp ; sp = sp->next) { + if (varequal(sp->text, name)) + return strchr(sp->text, '=') + 1; + } + for (v = *hashvar(name) ; v ; v = v->next) { + if (varequal(v->text, name)) { + if (v->flags & VUNSET + || ! doall && (v->flags & VEXPORT) == 0) + return NULL; + return strchr(v->text, '=') + 1; + } + } + return NULL; +} + + + +/* + * Generate a list of exported variables. This routine is used to construct + * the third argument to execve when executing a program. + */ + +char ** +environment() { + int nenv; + struct var **vpp; + struct var *vp; + char **env, **ep; + + nenv = 0; + for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { + for (vp = *vpp ; vp ; vp = vp->next) + if (vp->flags & VEXPORT) + nenv++; + } + ep = env = stalloc((nenv + 1) * sizeof *env); + for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { + for (vp = *vpp ; vp ; vp = vp->next) + if (vp->flags & VEXPORT) + *ep++ = vp->text; + } + *ep = NULL; + return env; +} + + +/* + * Called when a shell procedure is invoked to clear out nonexported + * variables. It is also necessary to reallocate variables of with + * VSTACK set since these are currently allocated on the stack. + */ + +#ifdef mkinit +MKINIT void shprocvar(); + +SHELLPROC { + shprocvar(); +} +#endif + +void +shprocvar() { + struct var **vpp; + struct var *vp, **prev; + + for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { + for (prev = vpp ; (vp = *prev) != NULL ; ) { + if ((vp->flags & VEXPORT) == 0) { + *prev = vp->next; + if ((vp->flags & VTEXTFIXED) == 0) + ckfree(vp->text); + if ((vp->flags & VSTRFIXED) == 0) + ckfree(vp); + } else { + if (vp->flags & VSTACK) { + vp->text = savestr(vp->text); + vp->flags &=~ VSTACK; + } + prev = &vp->next; + } + } + } + initvar(); +} + + + +/* + * Command to list all variables which are set. Currently this command + * is invoked from the set command when the set command is called without + * any variables. + */ + +int +showvarscmd(argc, argv) char **argv; { + struct var **vpp; + struct var *vp; + + for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { + for (vp = *vpp ; vp ; vp = vp->next) { + if ((vp->flags & VUNSET) == 0) + out1fmt("%s\n", vp->text); + } + } + return 0; +} + + + +/* + * The export and readonly commands. + */ + +int +exportcmd(argc, argv) char **argv; { + struct var **vpp; + struct var *vp; + char *name; + char *p; + int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT; + + listsetvar(cmdenviron); + if (argc > 1) { + while ((name = *argptr++) != NULL) { + if ((p = strchr(name, '=')) != NULL) { + p++; + } else { + vpp = hashvar(name); + for (vp = *vpp ; vp ; vp = vp->next) { + if (varequal(vp->text, name)) { + vp->flags |= flag; + goto found; + } + } + } + setvar(name, p, flag); +found:; + } + } else { + for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { + for (vp = *vpp ; vp ; vp = vp->next) { + if (vp->flags & flag) { + for (p = vp->text ; *p != '=' ; p++) + out1c(*p); + out1c('\n'); + } + } + } + } + return 0; +} + + +/* + * The "local" command. + */ + +localcmd(argc, argv) char **argv; { + char *name; + + if (! in_function()) + error("Not in a function"); + while ((name = *argptr++) != NULL) { + mklocal(name); + } + return 0; +} + + +/* + * Make a variable a local variable. When a variable is made local, it's + * value and flags are saved in a localvar structure. The saved values + * will be restored when the shell function returns. We handle the name + * "-" as a special case. + */ + +void +mklocal(name) + char *name; + { + struct localvar *lvp; + struct var **vpp; + struct var *vp; + + INTOFF; + lvp = ckmalloc(sizeof (struct localvar)); + if (name[0] == '-' && name[1] == '\0') { + lvp->text = ckmalloc(sizeof optval); + bcopy(optval, lvp->text, sizeof optval); + vp = NULL; + } else { + vpp = hashvar(name); + for (vp = *vpp ; vp && ! varequal(vp->text, name) ; vp = vp->next); + if (vp == NULL) { + if (strchr(name, '=')) + setvareq(savestr(name), VSTRFIXED); + else + setvar(name, NULL, VSTRFIXED); + vp = *vpp; /* the new variable */ + lvp->text = NULL; + lvp->flags = VUNSET; + } else { + lvp->text = vp->text; + lvp->flags = vp->flags; + vp->flags |= VSTRFIXED|VTEXTFIXED; + if (strchr(name, '=')) + setvareq(savestr(name), 0); + } + } + lvp->vp = vp; + lvp->next = localvars; + localvars = lvp; + INTON; +} + + +/* + * Called after a function returns. + */ + +void +poplocalvars() { + struct localvar *lvp; + struct var *vp; + + while ((lvp = localvars) != NULL) { + localvars = lvp->next; + vp = lvp->vp; + if (vp == NULL) { /* $- saved */ + bcopy(lvp->text, optval, sizeof optval); + ckfree(lvp->text); + } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) { + unsetvar(vp->text); + } else { + if ((vp->flags & VTEXTFIXED) == 0) + ckfree(vp->text); + vp->flags = lvp->flags; + vp->text = lvp->text; + } + ckfree(lvp); + } +} + + +setvarcmd(argc, argv) char **argv; { + if (argc <= 2) + return unsetcmd(argc, argv); + else if (argc == 3) + setvar(argv[1], argv[2], 0); + else + error("List assignment not implemented"); + return 0; +} + + +/* + * The unset builtin command. We unset the function before we unset the + * variable to allow a function to be unset when there is a readonly variable + * with the same name. + */ + +unsetcmd(argc, argv) char **argv; { + char **ap; + + for (ap = argv + 1 ; *ap ; ap++) { + unsetfunc(*ap); + unsetvar(*ap); + } + return 0; +} + + +/* + * Unset the specified variable. + */ + +STATIC void +unsetvar(s) + char *s; + { + struct var **vpp; + struct var *vp; + + vpp = hashvar(s); + for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) { + if (varequal(vp->text, s)) { + INTOFF; + if (*(strchr(vp->text, '=') + 1) != '\0' + || vp->flags & VREADONLY) { + setvar(s, nullstr, 0); + } + vp->flags &=~ VEXPORT; + vp->flags |= VUNSET; + if ((vp->flags & VSTRFIXED) == 0) { + if ((vp->flags & VTEXTFIXED) == 0) + ckfree(vp->text); + *vpp = vp->next; + ckfree(vp); + } + INTON; + return; + } + } +} + + + +/* + * Find the appropriate entry in the hash table from the name. + */ + +STATIC struct var ** +hashvar(p) + register char *p; + { + unsigned int hashval; + + hashval = *p << 4; + while (*p && *p != '=') + hashval += *p++; + return &vartab[hashval % VTABSIZE]; +} + + + +/* + * Returns true if the two strings specify the same varable. The first + * variable name is terminated by '='; the second may be terminated by + * either '=' or '\0'. + */ + +STATIC int +varequal(p, q) + register char *p, *q; + { + while (*p == *q++) { + if (*p++ == '=') + return 1; + } + if (*p == '=' && *(q - 1) == '\0') + return 1; + return 0; +} diff --git a/bin/sh/var.h b/bin/sh/var.h new file mode 100644 index 000000000000..b2c74172d88b --- /dev/null +++ b/bin/sh/var.h @@ -0,0 +1,128 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * from: @(#)var.h 5.1 (Berkeley) 3/7/91 + * var.h,v 1.4 1993/08/01 18:58:33 mycroft Exp + */ + +/* + * Shell variables. + */ + +/* flags */ +#define VEXPORT 01 /* variable is exported */ +#define VREADONLY 02 /* variable cannot be modified */ +#define VSTRFIXED 04 /* variable struct is staticly allocated */ +#define VTEXTFIXED 010 /* text is staticly allocated */ +#define VSTACK 020 /* text is allocated on the stack */ +#define VUNSET 040 /* the variable is not set */ + + +struct var { + struct var *next; /* next entry in hash list */ + int flags; /* flags are defined above */ + char *text; /* name=value */ +}; + + +struct localvar { + struct localvar *next; /* next local variable in list */ + struct var *vp; /* the variable that was made local */ + int flags; /* saved flags */ + char *text; /* saved text */ +}; + + +struct localvar *localvars; + +#if ATTY +extern struct var vatty; +#endif +extern struct var vifs; +extern struct var vmail; +extern struct var vmpath; +extern struct var vpath; +extern struct var vps1; +extern struct var vps2; +#if ATTY +extern struct var vterm; +#endif + +/* + * The following macros access the values of the above variables. + * They have to skip over the name. They return the null string + * for unset variables. + */ + +#define ifsval() (vifs.text + 4) +#define mailval() (vmail.text + 5) +#define mpathval() (vmpath.text + 9) +#define pathval() (vpath.text + 5) +#define ps1val() (vps1.text + 4) +#define ps2val() (vps2.text + 4) +#if ATTY +#define termval() (vterm.text + 5) +#endif + +#if ATTY +#define attyset() ((vatty.flags & VUNSET) == 0) +#endif +#define mpathset() ((vmpath.flags & VUNSET) == 0) + + +#ifdef __STDC__ +void initvar(); +void setvar(char *, char *, int); +void setvareq(char *, int); +struct strlist; +void listsetvar(struct strlist *); +char *lookupvar(char *); +char *bltinlookup(char *, int); +char **environment(); +int showvarscmd(int, char **); +void mklocal(char *); +void poplocalvars(void); +#else +void initvar(); +void setvar(); +void setvareq(); +void listsetvar(); +char *lookupvar(); +char *bltinlookup(); +char **environment(); +int showvarscmd(); +void mklocal(); +void poplocalvars(); +#endif |
