diff options
Diffstat (limited to 'bin')
245 files changed, 75315 insertions, 0 deletions
diff --git a/bin/Makefile b/bin/Makefile new file mode 100644 index 000000000000..3ae43c418438 --- /dev/null +++ b/bin/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 + +SUBDIR= cat chmod cp csh date dd df echo ed expr hostname kill ln ls \ + mkdir mv pax ps pwd rcp rm rmail rmdir sh sleep stty sync test + +.include <bsd.subdir.mk> diff --git a/bin/Makefile.inc b/bin/Makefile.inc new file mode 100644 index 000000000000..b569b70540d9 --- /dev/null +++ b/bin/Makefile.inc @@ -0,0 +1,3 @@ +# @(#)Makefile.inc 8.1 (Berkeley) 5/31/93 + +BINDIR?= /bin diff --git a/bin/cat/Makefile b/bin/cat/Makefile new file mode 100644 index 000000000000..c2d7cb576655 --- /dev/null +++ b/bin/cat/Makefile @@ -0,0 +1,5 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 + +PROG= cat + +.include <bsd.prog.mk> diff --git a/bin/cat/cat.1 b/bin/cat/cat.1 new file mode 100644 index 000000000000..102b22ec4c47 --- /dev/null +++ b/bin/cat/cat.1 @@ -0,0 +1,121 @@ +.\" Copyright (c) 1989, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" 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. +.\" +.\" @(#)cat.1 8.1 (Berkeley) 6/29/93 +.\" +.Dd June 29, 1993 +.Dt CAT 1 +.Os BSD 3 +.Sh NAME +.Nm cat +.Nd concatenate and print files +.Sh SYNOPSIS +.Nm cat +.Op Fl benstuv +.Op Fl +.Op Ar +.Sh DESCRIPTION +The +.Nm cat +utility reads files sequentially, writing them to the standard output. +The +.Ar file +operands are processed in command line order. +A single dash represents the standard input. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl b +Implies the +.Fl n +option but doesn't number blank lines. +.It Fl e +Implies the +.Fl v +option, and displays a dollar sign +.Pq Ql \&$ +at the end of each line +as well. +.It Fl n +Number the +.Ar output +lines, starting at 1. +.It Fl s +Squeeze multiple adjacent empty lines, causing the output to be +single spaced. +.It Fl t +Implies the +.Fl v +option, and displays tab characters as +.Pq Ql ^I +as well. +.It Fl u +The +.Fl u +option guarantees that the output is unbuffered. +.It Fl v +Displays non-printing characters so they are visible. +Control characters print as +.Ql ^X +for control-X; the delete +character (octal 0177) prints as +.Ql ^? +Non-ascii characters (with the high bit set) are printed as +.Ql M- +(for meta) followed by the character for the low 7 bits. +.El +.Pp +The +.Nm cat +utility exits 0 on success, and >0 if an error occurs. +.Sh BUGS +Because of the shell language mechanism used to perform output +redirection, the command +.Dq Li cat file1 file 2 > file1 +will cause the original data in file1 to be destroyed! +.Sh SEE ALSO +.Xr head 1 , +.Xr more 1 , +.Xr pr 1 , +.Xr tail 1 +.Rs +.%A Rob Pike +.%T "UNIX Style, or cat -v Considered Harmful" +.%J "USENIX Summer Conference Proceedings" +.%D 1983 +.Re +.Sh HISTORY +A +.Nm +command appeared in Version 6 AT&T UNIX. diff --git a/bin/cat/cat.c b/bin/cat/cat.c new file mode 100644 index 000000000000..30e5bfce7779 --- /dev/null +++ b/bin/cat/cat.c @@ -0,0 +1,251 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kevin Fall. + * + * 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 copyright[] = +"@(#) Copyright (c) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)cat.c 8.1 (Berkeley) 7/19/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +int bflag, eflag, nflag, sflag, tflag, vflag; +int rval; +char *filename; + +void cook_args __P((char *argv[])); +void cook_buf __P((FILE *)); +void raw_args __P((char *argv[])); +void raw_cat __P((int)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern int optind; + int ch; + + while ((ch = getopt(argc, argv, "benstuv")) != EOF) + switch (ch) { + case 'b': + bflag = nflag = 1; /* -b implies -n */ + break; + case 'e': + eflag = vflag = 1; /* -e implies -v */ + break; + case 'n': + nflag = 1; + break; + case 's': + sflag = 1; + break; + case 't': + tflag = vflag = 1; /* -t implies -v */ + break; + case 'u': + setbuf(stdout, (char *)NULL); + break; + case 'v': + vflag = 1; + break; + case '?': + (void)fprintf(stderr, + "usage: cat [-benstuv] [-] [file ...]\n"); + exit(1); + } + argv += optind; + + if (bflag || eflag || nflag || sflag || tflag || vflag) + cook_args(argv); + else + raw_args(argv); + if (fclose(stdout)) + err(1, "stdout"); + exit(rval); +} + +void +cook_args(argv) + char **argv; +{ + register FILE *fp; + + fp = stdin; + filename = "stdin"; + do { + if (*argv) { + if (!strcmp(*argv, "-")) + fp = stdin; + else if ((fp = fopen(*argv, "r")) == NULL) { + warn("%s", *argv); + ++argv; + continue; + } + filename = *argv++; + } + cook_buf(fp); + if (fp != stdin) + (void)fclose(fp); + } while (*argv); +} + +void +cook_buf(fp) + register FILE *fp; +{ + register int ch, gobble, line, prev; + + line = gobble = 0; + for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) { + if (prev == '\n') { + if (ch == '\n') { + if (sflag) { + if (!gobble && putchar(ch) == EOF) + break; + gobble = 1; + continue; + } + if (nflag && !bflag) { + (void)fprintf(stdout, "%6d\t", ++line); + if (ferror(stdout)) + break; + } + } else if (nflag) { + (void)fprintf(stdout, "%6d\t", ++line); + if (ferror(stdout)) + break; + } + } + gobble = 0; + if (ch == '\n') { + if (eflag) + if (putchar('$') == EOF) + break; + } else if (ch == '\t') { + if (tflag) { + if (putchar('^') == EOF || putchar('I') == EOF) + break; + continue; + } + } else if (vflag) { + if (!isascii(ch)) { + if (putchar('M') == EOF || putchar('-') == EOF) + break; + ch = toascii(ch); + } + if (iscntrl(ch)) { + if (putchar('^') == EOF || + putchar(ch == '\177' ? '?' : + ch | 0100) == EOF) + break; + continue; + } + } + if (putchar(ch) == EOF) + break; + } + if (ferror(fp)) { + warn("%s", filename); + clearerr(fp); + } + if (ferror(stdout)) + err(1, "stdout"); +} + +void +raw_args(argv) + char **argv; +{ + register int fd; + + fd = fileno(stdin); + filename = "stdin"; + do { + if (*argv) { + if (!strcmp(*argv, "-")) + fd = fileno(stdin); + else if ((fd = open(*argv, O_RDONLY, 0)) < 0) { + warn("%s", *argv); + ++argv; + continue; + } + filename = *argv++; + } + raw_cat(fd); + if (fd != fileno(stdin)) + (void)close(fd); + } while (*argv); +} + +void +raw_cat(rfd) + register int rfd; +{ + register int nr, nw, off, wfd; + static int bsize; + static char *buf; + struct stat sbuf; + + wfd = fileno(stdout); + if (buf == NULL) { + if (fstat(wfd, &sbuf)) + err(1, "%s", filename); + bsize = MAX(sbuf.st_blksize, 1024); + if ((buf = malloc((u_int)bsize)) == NULL) + err(1, ""); + } + while ((nr = read(rfd, buf, bsize)) > 0) + for (off = 0; nr; nr -= nw, off += nw) + if ((nw = write(wfd, buf + off, nr)) < 0) + err(1, "stdout"); + if (nr < 0) + warn("%s", filename); +} diff --git a/bin/chmod/Makefile b/bin/chmod/Makefile new file mode 100644 index 000000000000..f28bcfeda971 --- /dev/null +++ b/bin/chmod/Makefile @@ -0,0 +1,5 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 + +PROG= chmod + +.include <bsd.prog.mk> diff --git a/bin/chmod/chmod.1 b/bin/chmod/chmod.1 new file mode 100644 index 000000000000..ce92124b05c3 --- /dev/null +++ b/bin/chmod/chmod.1 @@ -0,0 +1,294 @@ +.\" Copyright (c) 1989, 1990, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" 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. +.\" +.\" @(#)chmod.1 8.4 (Berkeley) 3/31/94 +.\" +.Dd March 31, 1994 +.Dt CHMOD 1 +.Os +.Sh NAME +.Nm chmod +.Nd change file modes +.Sh SYNOPSIS +.Nm chmod +.Oo +.Fl R +.Op Fl H | Fl L | Fl P +.Oc +.Ar mode +.Ar file ... +.Sh DESCRIPTION +The +.Nm chmod +utility modifies the file mode bits of the listed files +as specified by the +.Ar mode +operand. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl H +If the +.Fl R +option is specified, symbolic links on the command line are followed. +(Symbolic links encountered in the tree traversal are not followed.) +.It Fl L +If the +.Fl R +option is specified, all symbolic links are followed. +.It Fl P +If the +.Fl R +option is specified, no symbolic links are followed. +.It Fl R +Change the modes of the file hierarchies rooted in the files +instead of just the files themselves. +.El +.Pp +Symbolic links do not have modes, so unless the +.Fl H +or +.Fl L +option is set, +.Nm chmod +on a symbolic link always succeeds and has no effect. +The +.Fl H , +.Fl L +and +.Fl P +options are ignored unless the +.Fl R +option is specified. +In addition, these options override each other and the +command's actions are determined by the last one specified. +.Pp +Only the owner of a file or the super-user is permitted to change +the mode of a file. +.Pp +The +.Nm chmod +utility exits 0 on success, and >0 if an error occurs. +.Sh MODES +Modes may be absolute or symbolic. +An absolute mode is an octal number constructed by +.Ar or-ing +the following values: +.Pp +.Bl -tag -width 6n -compact -offset indent +.It Li 4000 +set-user-ID-on-execution +.It Li 2000 +set-group-ID-on-execution +.It Li 1000 +sticky bit, see chmod(2) +.It Li 0400 +read by owner +.It Li 0200 +write by owner +.It Li 0100 +execute (or search for directories) by owner +.It Li 0070 +read, write, execute/search by group +.It Li 0007 +read, write, execute/search by others +.El +.Pp +The read, write, and execute/search values for group and others +are encoded as described for owner. +.Pp +The symbolic mode is described by the following grammar: +.Bd -literal -offset indent +mode ::= clause [, clause ...] +clause ::= [who ...] [action ...] last_action +action ::= op [perm ...] +last_action ::= op [perm ...] +who ::= a | u | g | o +op ::= + | \- | = +perm ::= r | s | t | w | x | X | u | g | o +.Ed +.Pp +The +.Ar who +symbols ``u'', ``g'', and ``o'' specify the user, group, and other parts +of the mode bits, respectively. +The +.Ar who +symbol ``a'' is equivalent to ``ugo''. +.Pp +.ne 1i +The +.Ar perm +symbols represent the portions of the mode bits as follows: +.Pp +.Bl -tag -width Ds -compact -offset indent +.It r +The read bits. +.It s +The set-user-ID-on-execution and set-group-ID-on-execution bits. +.It t +The sticky bit. +.It w +The write bits. +.It x +The execute/search bits. +.It X +The execute/search bits if the file is a directory or any of the +execute/search bits are set in the original (unmodified) mode. +Operations with the +.Ar perm +symbol ``X'' are only meaningful in conjunction with the +.Ar op +symbol ``+'', and are ignored in all other cases. +.It u +The user permission bits in the mode of the original file. +.It g +The group permission bits in the mode of the original file. +.It o +The other permission bits in the mode of the original file. +.El +.Pp +The +.Ar op +symbols represent the operation performed, as follows: +.Bl -tag -width 4n +.It + +If no value is supplied for +.Ar perm , +the ``+'' operation has no effect. +If no value is supplied for +.Ar who , +each permission bit specified in +.Ar perm , +for which the corresponding bit in the file mode creation mask +is clear, is set. +Otherwise, the mode bits represented by the specified +.Ar who +and +.Ar perm +values are set. +.It \&\- +If no value is supplied for +.Ar perm , +the ``\-'' operation has no effect. +If no value is supplied for +.Ar who , +each permission bit specified in +.Ar perm , +for which the corresponding bit in the file mode creation mask +is clear, is cleared. +Otherwise, the mode bits represented by the specified +.Ar who +and +.Ar perm +values are cleared. +.It = +The mode bits specified by the +.Ar who +value are cleared, or, if no who value is specified, the owner, group +and other mode bits are cleared. +Then, if no value is supplied for +.Ar who , +each permission bit specified in +.Ar perm , +for which the corresponding bit in the file mode creation mask +is clear, is set. +Otherwise, the mode bits represented by the specified +.Ar who +and +.Ar perm +values are set. +.El +.Pp +Each +.Ar clause +specifies one or more operations to be performed on the mode +bits, and each operation is applied to the mode bits in the +order specified. +.Pp +Operations upon the other permissions only (specified by the symbol +``o'' by itself), in combination with the +.Ar perm +symbols ``s'' or ``t'', are ignored. +.Sh EXAMPLES +.Bl -tag -width "u=rwx,go=u-w" -compact +.It Li 644 +make a file readable by anyone and writable by the owner only. +.Pp +.It Li go-w +deny write permission to group and others. +.Pp +.It Li =rw,+X +set the read and write permissions to the usual defaults, but +retain any execute permissions that are currently set. +.Pp +.It Li +X +make a directory or file searchable/executable by everyone if it is +already searchable/executable by anyone. +.Pp +.It Li 755 +.It Li u=rwx,go=rx +.It Li u=rwx,go=u-w +make a file readable/executable by everyone and writable by the owner only. +.Pp +.It Li go= +clear all mode bits for group and others. +.Pp +.It Li g=u-w +set the group bits equal to the user bits, but clear the group write bit. +.El +.Sh BUGS +There's no +.Ar perm +option for the naughty bits. +.Sh SEE ALSO +.Xr install 1 , +.Xr chmod 2 , +.Xr stat 2 , +.Xr umask 2 , +.Xr fts 3 , +.Xr setmode 3 , +.Xr symlink 7 , +.Xr chown 8 +.Sh STANDARDS +The +.Nm chmod +utility is expected to be POSIX 1003.2 +compatible with the exception of the +.Ar perm +symbols +.Dq t +and +.Dq X +which are not included in that standard. diff --git a/bin/chmod/chmod.c b/bin/chmod/chmod.c new file mode 100644 index 000000000000..f6251a35c222 --- /dev/null +++ b/bin/chmod/chmod.c @@ -0,0 +1,201 @@ +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * 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 copyright[] = +"@(#) Copyright (c) 1989, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)chmod.c 8.8 (Berkeley) 4/1/94"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <err.h> +#include <errno.h> +#include <fts.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + FTS *ftsp; + FTSENT *p; + mode_t *set; + long val; + int oct, omode; + int Hflag, Lflag, Pflag, Rflag, ch, fflag, fts_options, hflag, rval; + char *ep, *mode; + + Hflag = Lflag = Pflag = Rflag = fflag = hflag = 0; + while ((ch = getopt(argc, argv, "HLPRXfgorstuwx")) != EOF) + switch (ch) { + case 'H': + Hflag = 1; + Lflag = Pflag = 0; + break; + case 'L': + Lflag = 1; + Hflag = Pflag = 0; + break; + case 'P': + Pflag = 1; + Hflag = Lflag = 0; + break; + case 'R': + Rflag = 1; + break; + case 'f': /* XXX: undocumented. */ + fflag = 1; + break; + case 'h': + /* + * In System V (and probably POSIX.2) the -h option + * causes chmod to change the mode of the symbolic + * link. 4.4BSD's symbolic links don't have modes, + * so it's an undocumented noop. Do syntax checking, + * though. + */ + hflag = 1; + break; + /* + * XXX + * "-[rwx]" are valid mode commands. If they are the entire + * argument, getopt has moved past them, so decrement optind. + * Regardless, we're done argument processing. + */ + case 'g': case 'o': case 'r': case 's': + case 't': case 'u': case 'w': case 'X': case 'x': + if (argv[optind - 1][0] == '-' && + argv[optind - 1][1] == ch && + argv[optind - 1][2] == '\0') + --optind; + goto done; + case '?': + default: + usage(); + } +done: argv += optind; + argc -= optind; + + if (argc < 2) + usage(); + + fts_options = FTS_PHYSICAL; + if (Rflag) { + if (hflag) + errx(1, + "the -R and -h options may not be specified together."); + if (Hflag) + fts_options |= FTS_COMFOLLOW; + if (Lflag) { + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + } + } + + mode = *argv; + if (*mode >= '0' && *mode <= '7') { + errno = 0; + val = strtol(mode, &ep, 8); + if (val > INT_MAX || val < 0) + errno = ERANGE; + if (errno) + err(1, "invalid file mode: %s", mode); + if (*ep) + errx(1, "invalid file mode: %s", mode); + omode = val; + oct = 1; + } else { + if ((set = setmode(mode)) == NULL) + errx(1, "invalid file mode: %s", mode); + oct = 0; + } + + if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL) + err(1, NULL); + for (rval = 0; (p = fts_read(ftsp)) != NULL;) { + switch (p->fts_info) { + case FTS_D: + if (Rflag) /* Change it at FTS_DP. */ + continue; + fts_set(ftsp, p, FTS_SKIP); + break; + case FTS_DNR: /* Warn, chmod, continue. */ + warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); + rval = 1; + break; + case FTS_ERR: /* Warn, continue. */ + case FTS_NS: + warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); + rval = 1; + continue; + case FTS_SL: /* Ignore. */ + case FTS_SLNONE: + /* + * The only symlinks that end up here are ones that + * don't point to anything and ones that we found + * doing a physical walk. + */ + continue; + default: + break; + } + if (chmod(p->fts_accpath, oct ? omode : + getmode(set, p->fts_statp->st_mode)) && !fflag) { + warn(p->fts_path); + rval = 1; + } + } + if (errno) + err(1, "fts_read"); + exit(rval); +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: chmod [-R [-H | -L | -P]] mode file ...\n"); + exit(1); +} diff --git a/bin/cp/Makefile b/bin/cp/Makefile new file mode 100644 index 000000000000..ad07b6874f07 --- /dev/null +++ b/bin/cp/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 + +PROG= cp +SRCS= cp.c utils.c + +.include <bsd.prog.mk> diff --git a/bin/cp/cp.1 b/bin/cp/cp.1 new file mode 100644 index 000000000000..f04d37bbfdf5 --- /dev/null +++ b/bin/cp/cp.1 @@ -0,0 +1,211 @@ +.\" Copyright (c) 1989, 1990, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" 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. +.\" +.\" @(#)cp.1 8.3 (Berkeley) 4/18/94 +.\" +.Dd April 18, 1994 +.Dt CP 1 +.Os BSD 4 +.Sh NAME +.Nm cp +.Nd copy files +.Sh SYNOPSIS +.Nm cp +.Oo +.Fl R +.Op Fl H | Fl L | Fl P +.Oc +.Op Fl fip +.Ar source_file target_file +.Nm cp +.Oo +.Fl R +.Op Fl H | Fl L | Fl P +.Oc +.Op Fl fip +.Ar source_file ... target_directory +.Sh DESCRIPTION +In the first synopsis form, the +.Nm cp +utility copies the contents of the +.Ar source_file +to the +.Ar target_file . +In the second synopsis form, +the contents of each named +.Ar source_file +is copied to the destination +.Ar target_directory . +The names of the files themselves are not changed. +If +.Nm cp +detects an attempt to copy a file to itself, the copy will fail. +.Pp +The following options are available: +.Bl -tag -width flag +.It Fl H +If the +.Fl R +option is specified, symbolic links on the command line are followed. +(Symbolic links encountered in the tree traversal are not followed.) +.It Fl L +If the +.Fl R +option is specified, all symbolic links are followed. +.It Fl P +If the +.Fl R +option is specified, no symbolic links are followed. +.It Fl R +If +.Ar source_file +designates a directory, +.Nm cp +copies the directory and the entire subtree connected at that point. +This option also causes symbolic links to be copied, rather than +indirected through, and for +.Nm cp +to create special files rather than copying them as normal files. +Created directories have the same mode as the corresponding source +directory, unmodified by the process' umask. +.It Fl f +For each existing destination pathname, remove it and +create a new file, without prompting for confirmation +regardless of its permissions. +(The +.Fl i +option is ignored if the +.Fl f +option is specified.) +.It Fl i +Causes +.Nm cp +to write a prompt to the standard error output before copying a file +that would overwrite an existing file. +If the response from the standard input begins with the character +.Sq Li y , +the file copy is attempted. +.It Fl p +Causes +.Nm cp +to preserve in the copy as many of the modification time, access time, +file flags, file mode, user ID, and group ID as allowed by permissions. +.Pp +If the user ID and group ID cannot be preserved, no error message +is displayed and the exit value is not altered. +.Pp +If the source file has its set user ID bit on and the user ID cannot +be preserved, the set user ID bit is not preserved +in the copy's permissions. +If the source file has its set group ID bit on and the group ID cannot +be preserved, the set group ID bit is not preserved +in the copy's permissions. +If the source file has both its set user ID and set group ID bits on, +and either the user ID or group ID cannot be preserved, neither +the set user ID or set group ID bits are preserved in the copy's +permissions. +.El +.Pp +For each destination file that already exists, its contents are +overwritten if permissions allow, but its mode, user ID, and group +ID are unchanged. +.Pp +In the second synopsis form, +.Ar target_directory +must exist unless there is only one named +.Ar source_file +which is a directory and the +.Fl R +flag is specified. +.Pp +If the destination file does not exist, the mode of the source file is +used as modified by the file mode creation mask +.Pf ( Ic umask , +see +.Xr csh 1 ) . +If the source file has its set user ID bit on, that bit is removed +unless both the source file and the destination file are owned by the +same user. +If the source file has its set group ID bit on, that bit is removed +unless both the source file and the destination file are in the same +group and the user is a member of that group. +If both the set user ID and set group ID bits are set, all of the above +conditions must be fulfilled or both bits are removed. +.Pp +Appropriate permissions are required for file creation or overwriting. +.Pp +Symbolic links are always followed unless the +.Fl R +flag is set, in which case symbolic links are not followed, by default. +The +.Fl H +or +.Fl L +flags (in conjunction with the +.Fl R +flag) cause symbolic links to be followed as described above. +The +.Fl H , +.Fl L +and +.Fl P +options are ignored unless the +.Fl R +option is specified. +In addition, these options override each other and the +command's actions are determined by the last one specified. +.Pp +.Nm Cp +exits 0 on success, >0 if an error occurred. +.Sh COMPATIBILITY +Historic versions of the +.Nm cp +utility had a +.Fl r +option. +This implementation supports that option, however, its use is strongly +discouraged, as it does not correctly copy special files, symbolic links +or fifo's. +.Sh SEE ALSO +.Xr mv 1 , +.Xr rcp 1 , +.Xr umask 2 , +.Xr fts 3 , +.Xr symlink 7 +.Sh HISTORY +The +.Nm cp +command is expected to be +.St -p1003.2 +compatible. diff --git a/bin/cp/cp.c b/bin/cp/cp.c new file mode 100644 index 000000000000..b58bb99c02cd --- /dev/null +++ b/bin/cp/cp.c @@ -0,0 +1,441 @@ +/* + * Copyright (c) 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * David Hitz of Auspex Systems Inc. + * + * 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 copyright[] = +"@(#) Copyright (c) 1988, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)cp.c 8.2 (Berkeley) 4/1/94"; +#endif /* not lint */ + +/* + * Cp copies source files to target files. + * + * The global PATH_T structure "to" always contains the path to the + * current target file. Since fts(3) does not change directories, + * this path can be either absolute or dot-realative. + * + * The basic algorithm is to initialize "to" and use fts(3) to traverse + * the file hierarchy rooted in the argument list. A trivial case is the + * case of 'cp file1 file2'. The more interesting case is the case of + * 'cp file1 file2 ... fileN dir' where the hierarchy is traversed and the + * path (relative to the root of the traversal) is appended to dir (stored + * in "to") to form the final target path. + */ + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/time.h> + +#include <dirent.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <fts.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "extern.h" + +#define STRIP_TRAILING_SLASH(p) { \ + while ((p).p_end > (p).p_path && (p).p_end[-1] == '/') \ + *--(p).p_end = 0; \ +} + +PATH_T to = { to.p_path, "" }; + +uid_t myuid; +int Rflag, iflag, pflag, rflag; +int myumask; + +enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE }; + +int copy __P((char *[], enum op, int)); +int mastercmp __P((const FTSENT **, const FTSENT **)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct stat to_stat, tmp_stat; + enum op type; + int Hflag, Lflag, Pflag, ch, fts_options, r; + char *target; + + Hflag = Lflag = Pflag = Rflag = 0; + while ((ch = getopt(argc, argv, "HLPRfipr")) != EOF) + switch (ch) { + case 'H': + Hflag = 1; + Lflag = Pflag = 0; + break; + case 'L': + Lflag = 1; + Hflag = Pflag = 0; + break; + case 'P': + Pflag = 1; + Hflag = Lflag = 0; + break; + case 'R': + Rflag = 1; + break; + case 'f': + iflag = 0; + break; + case 'i': + iflag = isatty(fileno(stdin)); + break; + case 'p': + pflag = 1; + break; + case 'r': + rflag = 1; + break; + case '?': + default: + usage(); + break; + } + argc -= optind; + argv += optind; + + if (argc < 2) + usage(); + + fts_options = FTS_NOCHDIR | FTS_PHYSICAL; + if (rflag) { + if (Rflag) + errx(1, + "the -R and -r options may not be specified together."); + if (Hflag || Lflag || Pflag) + errx(1, + "the -H, -L, and -P options may not be specified with the -r option."); + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + } + if (Rflag) { + if (Hflag) + fts_options |= FTS_COMFOLLOW; + if (Lflag) { + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + } + } else { + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + } + + myuid = getuid(); + + /* Copy the umask for explicit mode setting. */ + myumask = umask(0); + (void)umask(myumask); + + /* Save the target base in "to". */ + target = argv[--argc]; + if (strlen(target) > MAXPATHLEN) + errx(1, "%s: name too long", target); + (void)strcpy(to.p_path, target); + to.p_end = to.p_path + strlen(to.p_path); + if (to.p_path == to.p_end) { + *to.p_end++ = '.'; + *to.p_end = 0; + } + STRIP_TRAILING_SLASH(to); + to.target_end = to.p_end; + + /* Set end of argument list for fts(3). */ + argv[argc] = NULL; + + /* + * Cp has two distinct cases: + * + * cp [-R] source target + * cp [-R] source1 ... sourceN directory + * + * In both cases, source can be either a file or a directory. + * + * In (1), the target becomes a copy of the source. That is, if the + * source is a file, the target will be a file, and likewise for + * directories. + * + * In (2), the real target is not directory, but "directory/source". + */ + r = stat(to.p_path, &to_stat); + if (r == -1 && errno != ENOENT) + err(1, "%s", to.p_path); + if (r == -1 || !S_ISDIR(to_stat.st_mode)) { + /* + * Case (1). Target is not a directory. + */ + if (argc > 1) { + usage(); + exit(1); + } + /* + * Need to detect the case: + * cp -R dir foo + * Where dir is a directory and foo does not exist, where + * we want pathname concatenations turned on but not for + * the initial mkdir(). + */ + if (r == -1) { + if (rflag || (Rflag && (Lflag || Hflag))) + stat(*argv, &tmp_stat); + else + lstat(*argv, &tmp_stat); + + if (S_ISDIR(tmp_stat.st_mode) && (Rflag || rflag)) + type = DIR_TO_DNE; + else + type = FILE_TO_FILE; + } else + type = FILE_TO_FILE; + } else + /* + * Case (2). Target is a directory. + */ + type = FILE_TO_DIR; + + exit (copy(argv, type, fts_options)); +} + +int +copy(argv, type, fts_options) + char *argv[]; + enum op type; + int fts_options; +{ + struct stat to_stat; + FTS *ftsp; + FTSENT *curr; + int base, dne, nlen, rval; + char *p; + + if ((ftsp = fts_open(argv, fts_options, mastercmp)) == NULL) + err(1, NULL); + for (rval = 0; (curr = fts_read(ftsp)) != NULL;) { + switch (curr->fts_info) { + case FTS_NS: + case FTS_ERR: + warnx("%s: %s", + curr->fts_path, strerror(curr->fts_errno)); + rval = 1; + continue; + case FTS_DC: /* Warn, continue. */ + warnx("%s: directory causes a cycle", curr->fts_path); + rval = 1; + continue; + case FTS_DP: /* Ignore, continue. */ + continue; + } + + /* + * If we are in case (2) or (3) above, we need to append the + * source name to the target name. + */ + if (type != FILE_TO_FILE) { + if ((curr->fts_namelen + + to.target_end - to.p_path + 1) > MAXPATHLEN) { + warnx("%s/%s: name too long (not copied)", + to.p_path, curr->fts_name); + rval = 1; + continue; + } + + /* + * Need to remember the roots of traversals to create + * correct pathnames. If there's a directory being + * copied to a non-existent directory, e.g. + * cp -R a/dir noexist + * the resulting path name should be noexist/foo, not + * noexist/dir/foo (where foo is a file in dir), which + * is the case where the target exists. + * + * Also, check for "..". This is for correct path + * concatentation for paths ending in "..", e.g. + * cp -R .. /tmp + * Paths ending in ".." are changed to ".". This is + * tricky, but seems the easiest way to fix the problem. + * + * XXX + * Since the first level MUST be FTS_ROOTLEVEL, base + * is always initialized. + */ + if (curr->fts_level == FTS_ROOTLEVEL) + if (type != DIR_TO_DNE) { + p = strrchr(curr->fts_path, '/'); + base = (p == NULL) ? 0 : + (int)(p - curr->fts_path + 1); + + if (!strcmp(&curr->fts_path[base], + "..")) + base += 1; + } else + base = curr->fts_pathlen; + + if (to.target_end[-1] != '/') { + *to.target_end = '/'; + *(to.target_end + 1) = 0; + } + p = &curr->fts_path[base]; + nlen = curr->fts_pathlen - base; + + (void)strncat(to.target_end + 1, p, nlen); + to.p_end = to.target_end + nlen + 1; + *to.p_end = 0; + STRIP_TRAILING_SLASH(to); + } + + /* Not an error but need to remember it happened */ + if (stat(to.p_path, &to_stat) == -1) + dne = 1; + else { + if (to_stat.st_dev == curr->fts_statp->st_dev && + to_stat.st_ino == curr->fts_statp->st_ino) { + warnx("%s and %s are identical (not copied).", + to.p_path, curr->fts_path); + rval = 1; + if (S_ISDIR(curr->fts_statp->st_mode)) + (void)fts_set(ftsp, curr, FTS_SKIP); + continue; + } + dne = 0; + } + + switch (curr->fts_statp->st_mode & S_IFMT) { + case S_IFLNK: + if (copy_link(curr, !dne)) + rval = 1; + break; + case S_IFDIR: + if (!Rflag && !rflag) { + warnx("%s is a directory (not copied).", + curr->fts_path); + (void)fts_set(ftsp, curr, FTS_SKIP); + rval = 1; + break; + } + /* + * If the directory doesn't exist, create the new + * one with the from file mode plus owner RWX bits, + * modified by the umask. Trade-off between being + * able to write the directory (if from directory is + * 555) and not causing a permissions race. If the + * umask blocks owner writes, we fail.. + */ + if (dne) { + if (mkdir(to.p_path, + curr->fts_statp->st_mode | S_IRWXU) < 0) + err(1, "%s", to.p_path); + } else if (!S_ISDIR(to_stat.st_mode)) { + errno = ENOTDIR; + err(1, "%s: %s", to.p_path); + } + /* + * If not -p and directory didn't exist, set it to be + * the same as the from directory, umodified by the + * umask; arguably wrong, but it's been that way + * forever. + */ + if (pflag && setfile(curr->fts_statp, 0)) + rval = 1; + else if (dne) + (void)chmod(to.p_path, + curr->fts_statp->st_mode); + break; + case S_IFBLK: + case S_IFCHR: + if (Rflag) { + if (copy_special(curr->fts_statp, !dne)) + rval = 1; + } else + if (copy_file(curr, dne)) + rval = 1; + break; + case S_IFIFO: + if (Rflag) + if (copy_fifo(curr->fts_statp, !dne)) + rval = 1; + else + if (copy_file(curr, dne)) + rval = 1; + break; + default: + if (copy_file(curr, dne)) + rval = 1; + break; + } + } + if (errno) + err(1, "fts_read"); + return (rval); +} + +/* + * mastercmp -- + * The comparison function for the copy order. The order is to copy + * non-directory files before directory files. The reason for this + * is because files tend to be in the same cylinder group as their + * parent directory, whereas directories tend not to be. Copying the + * files first reduces seeking. + */ +int +mastercmp(a, b) + const FTSENT **a, **b; +{ + int a_info, b_info; + + a_info = (*a)->fts_info; + if (a_info == FTS_ERR || a_info == FTS_NS || a_info == FTS_DNR) + return (0); + b_info = (*b)->fts_info; + if (b_info == FTS_ERR || b_info == FTS_NS || b_info == FTS_DNR) + return (0); + if (a_info == FTS_D) + return (-1); + if (b_info == FTS_D) + return (1); + return (0); +} diff --git a/bin/cp/extern.h b/bin/cp/extern.h new file mode 100644 index 000000000000..6c7c6e2db50f --- /dev/null +++ b/bin/cp/extern.h @@ -0,0 +1,55 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * 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. + * + * @(#)extern.h 8.2 (Berkeley) 4/1/94 + */ + +typedef struct { + char *p_end; /* pointer to NULL at end of path */ + char *target_end; /* pointer to end of target base */ + char p_path[MAXPATHLEN + 1]; /* pointer to the start of a path */ +} PATH_T; + +extern PATH_T to; +extern uid_t myuid; +extern int iflag, pflag, myumask; + +#include <sys/cdefs.h> + +__BEGIN_DECLS +int copy_fifo __P((struct stat *, int)); +int copy_file __P((FTSENT *, int)); +int copy_link __P((FTSENT *, int)); +int copy_special __P((struct stat *, int)); +int setfile __P((struct stat *, int)); +void usage __P((void)); +__END_DECLS diff --git a/bin/cp/utils.c b/bin/cp/utils.c new file mode 100644 index 000000000000..5a5edcbfb03f --- /dev/null +++ b/bin/cp/utils.c @@ -0,0 +1,285 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * 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[] = "@(#)utils.c 8.3 (Berkeley) 4/1/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/time.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <fts.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "extern.h" + +int +copy_file(entp, dne) + FTSENT *entp; + int dne; +{ + static char buf[MAXBSIZE]; + struct stat to_stat, *fs; + int ch, checkch, from_fd, rcount, rval, to_fd, wcount; +#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED + char *p; +#endif + + if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) { + warn("%s", entp->fts_path); + return (1); + } + + fs = entp->fts_statp; + + /* + * If the file exists and we're interactive, verify with the user. + * If the file DNE, set the mode to be the from file, minus setuid + * bits, modified by the umask; arguably wrong, but it makes copying + * executables work right and it's been that way forever. (The + * other choice is 666 or'ed with the execute bits on the from file + * modified by the umask.) + */ + if (!dne) { + if (iflag) { + (void)fprintf(stderr, "overwrite %s? ", to.p_path); + checkch = ch = getchar(); + while (ch != '\n' && ch != EOF) + ch = getchar(); + if (checkch != 'y') { + (void)close(from_fd); + return (0); + } + } + to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0); + } else + to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, + fs->st_mode & ~(S_ISUID | S_ISGID)); + + if (to_fd == -1) { + warn("%s", to.p_path); + (void)close(from_fd); + return (1);; + } + + rval = 0; + + /* + * Mmap and write if less than 8M (the limit is so we don't totally + * trash memory on big files. This is really a minor hack, but it + * wins some CPU back. + */ +#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED + if (fs->st_size <= 8 * 1048576) { + if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ, + 0, from_fd, (off_t)0)) == (char *)-1) { + warn("%s", entp->fts_path); + rval = 1; + } else { + if (write(to_fd, p, fs->st_size) != fs->st_size) { + warn("%s", to.p_path); + rval = 1; + } + /* Some systems don't unmap on close(2). */ + if (munmap(p, fs->st_size) < 0) { + warn("%s", entp->fts_path); + rval = 1; + } + } + } else +#endif + { + while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) { + wcount = write(to_fd, buf, rcount); + if (rcount != wcount || wcount == -1) { + warn("%s", to.p_path); + rval = 1; + break; + } + } + if (rcount < 0) { + warn("%s", entp->fts_path); + rval = 1; + } + } + + /* If the copy went bad, lose the file. */ + if (rval == 1) { + (void)unlink(to.p_path); + (void)close(from_fd); + (void)close(to_fd); + return (1); + } + + if (pflag && setfile(fs, to_fd)) + rval = 1; + /* + * If the source was setuid or setgid, lose the bits unless the + * copy is owned by the same user and group. + */ +#define RETAINBITS \ + (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO) + else if (fs->st_mode & (S_ISUID | S_ISGID) && fs->st_uid == myuid) + if (fstat(to_fd, &to_stat)) { + warn("%s", to.p_path); + rval = 1; + } else if (fs->st_gid == to_stat.st_gid && + fchmod(to_fd, fs->st_mode & RETAINBITS & ~myumask)) { + warn("%s", to.p_path); + rval = 1; + } + (void)close(from_fd); + if (close(to_fd)) { + warn("%s", to.p_path); + rval = 1; + } + return (rval); +} + +int +copy_link(p, exists) + FTSENT *p; + int exists; +{ + int len; + char link[MAXPATHLEN]; + + if ((len = readlink(p->fts_path, link, sizeof(link))) == -1) { + warn("readlink: %s", p->fts_path); + return (1); + } + link[len] = '\0'; + if (exists && unlink(to.p_path)) { + warn("unlink: %s", to.p_path); + return (1); + } + if (symlink(link, to.p_path)) { + warn("symlink: %s", link); + return (1); + } + return (0); +} + +int +copy_fifo(from_stat, exists) + struct stat *from_stat; + int exists; +{ + if (exists && unlink(to.p_path)) { + warn("unlink: %s", to.p_path); + return (1); + } + if (mkfifo(to.p_path, from_stat->st_mode)) { + warn("mkfifo: %s", to.p_path); + return (1); + } + return (pflag ? setfile(from_stat, 0) : 0); +} + +int +copy_special(from_stat, exists) + struct stat *from_stat; + int exists; +{ + if (exists && unlink(to.p_path)) { + warn("unlink: %s", to.p_path); + return (1); + } + if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) { + warn("mknod: %s", to.p_path); + return (1); + } + return (pflag ? setfile(from_stat, 0) : 0); +} + + +int +setfile(fs, fd) + register struct stat *fs; + int fd; +{ + static struct timeval tv[2]; + int rval; + + rval = 0; + fs->st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO; + + TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec); + TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec); + if (utimes(to.p_path, tv)) { + warn("utimes: %s", to.p_path); + rval = 1; + } + /* + * Changing the ownership probably won't succeed, unless we're root + * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting + * the mode; current BSD behavior is to remove all setuid bits on + * chown. If chown fails, lose setuid/setgid bits. + */ + if (fd ? fchown(fd, fs->st_uid, fs->st_gid) : + chown(to.p_path, fs->st_uid, fs->st_gid)) { + if (errno != EPERM) { + warn("chown: %s", to.p_path); + rval = 1; + } + fs->st_mode &= ~(S_ISUID | S_ISGID); + } + if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) { + warn("chown: %s", to.p_path); + rval = 1; + } + + if (fd ? + fchflags(fd, fs->st_flags) : chflags(to.p_path, fs->st_flags)) { + warn("chflags: %s", to.p_path); + rval = 1; + } + return (rval); +} + +void +usage() +{ + (void)fprintf(stderr, "%s\n%s\n", +"usage: cp [-R [-H | -L | -P] [-fip] src target", +" cp [-R [-H | -L | -P] [-fip] src1 ... srcN directory"); + exit(1); +} diff --git a/bin/csh/Makefile b/bin/csh/Makefile new file mode 100644 index 000000000000..e29f5492bd49 --- /dev/null +++ b/bin/csh/Makefile @@ -0,0 +1,44 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 +# +# C Shell with process control; VM/UNIX VAX Makefile +# Bill Joy UC Berkeley; Jim Kulp IIASA, Austria +# +# To profile, put -DPROF in DEFS and -pg in CFLAGS, and recompile. + +PROG= csh +DFLAGS=-DBUILTIN -DFILEC -DNLS -DSHORT_STRINGS +#CFLAGS+=-g +#CFLAGS+=-Wall -ansi -pedantic +CFLAGS+=-I${.CURDIR} -I. ${DFLAGS} +SRCS= alloc.c char.c const.c csh.c dir.c dol.c err.c exec.c exp.c file.c \ + func.c glob.c hist.c init.c lex.c misc.c parse.c printf.c proc.c \ + sem.c set.c str.c time.c +.PATH: ${.CURDIR}/../../usr.bin/printf + +MAN1= csh.0 +MLINKS= csh.1 limit.1 csh.1 alias.1 csh.1 bg.1 csh.1 dirs.1 csh.1 fg.1 \ + csh.1 foreach.1 csh.1 history.1 csh.1 jobs.1 csh.1 popd.1 \ + csh.1 pushd.1 csh.1 rehash.1 csh.1 repeat.1 csh.1 suspend.1 \ + csh.1 stop.1 csh.1 source.1 +CLEANFILES+=err.h const.h + +const.h: err.h + +err.h: err.c + @rm -f $@ + @echo '/* Do not edit this file, make creates it. */' > $@ + @echo '#ifndef _h_sh_err' >> $@ + @echo '#define _h_sh_err' >> $@ + egrep 'ERR_' ${.CURDIR}/$*.c | egrep '^#define' >> $@ + @echo '#endif /* _h_sh_err */' >> $@ + +const.h: const.c + @rm -f $@ + @echo '/* Do not edit this file, make creates it. */' > $@ + ${CC} -E ${CFLAGS} ${.CURDIR}/$*.c | egrep 'Char STR' | \ + sed -e 's/Char \([a-zA-Z0-9_]*\)\(.*\)/extern Char \1[];/' | \ + sort >> $@ + +.depend: const.h err.h + +.include <bsd.prog.mk> diff --git a/bin/csh/USD.doc/Makefile b/bin/csh/USD.doc/Makefile new file mode 100644 index 000000000000..e73be633d116 --- /dev/null +++ b/bin/csh/USD.doc/Makefile @@ -0,0 +1,7 @@ +# @(#)Makefile 8.1 (Berkeley) 8/14/93 + +DIR= usd/04.csh +SRCS= tabs csh.1 csh.2 csh.3 csh.4 csh.a csh.g +MACROS= -ms + +.include <bsd.doc.mk> diff --git a/bin/csh/USD.doc/csh.1 b/bin/csh/USD.doc/csh.1 new file mode 100644 index 000000000000..9b8e39491e21 --- /dev/null +++ b/bin/csh/USD.doc/csh.1 @@ -0,0 +1,1012 @@ +.\" Copyright (c) 1980, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" 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. +.\" +.\" @(#)csh.1 8.1 (Berkeley) 6/8/93 +.\" +.EH 'USD:4-%''An Introduction to the C shell' +.OH 'An Introduction to the C shell''USD:4-%' +.\".RP +.TL +An Introduction to the C shell +.AU +William Joy +(revised for 4.3BSD by Mark Seiden) +.AI +Computer Science Division +.br +Department of Electrical Engineering and Computer Science +.br +University of California, Berkeley +.br +Berkeley, California 94720 +.AB +.I Csh +is a new command language interpreter for +.UX +systems. +It incorporates good features of other shells and a +.I history +mechanism similar to the +.I redo +of \s-2INTERLISP\s0. +While incorporating many features of other shells which make +writing shell programs (shell scripts) easier, +most of the features unique to +.I csh +are designed more for the interactive \s-2UNIX\s0 user. +.PP +\s-2UNIX\s0 +users who have read a general introduction to the system +will find a valuable basic explanation of the shell here. +Simple terminal interaction with +.I csh +is possible after reading just the first section of this document. +The second section describes the shell's capabilities which you can +explore after you have begun to become acquainted with the shell. +Later sections introduce features which are useful, but not necessary +for all users of the shell. +.PP +Additional information includes an appendix listing special characters of the shell +and a glossary of terms and commands introduced in this manual. +.AE +.SH +.if n .ND +Introduction +.PP +A +.I shell +is a command language interpreter. +.I Csh +is the name of one particular command interpreter on +\s-2UNIX\s0. +The primary purpose of +.I csh +is to translate command lines typed at a terminal into +system actions, such as invocation of other programs. +.I Csh +is a user program just like any you might write. +Hopefully, +.I csh +will be a very useful program for you +in interacting with the \s-2UNIX\s0 system. +.PP +In addition to this document, you will want to refer to a copy +of the \s-2UNIX\s0 User Reference Manual. +The +.I csh +documentation in section 1 of the manual provides a full description of all +features of the shell and is the definitive reference for questions +about the shell. +.PP +Many words in this document are shown in +.I italics. +These are important words; +names of commands, and words which have special meaning in discussing +the shell and \s-2UNIX\s0. +Many of the words are defined in a glossary at the end of this document. +If you don't know what is meant by a word, you should look +for it in the glossary. +.SH +Acknowledgements +.PP +Numerous people have provided good input about previous versions +of +.I csh +and aided in its debugging and in the debugging of its documentation. +I would especially like to thank Michael Ubell +who made the crucial observation that history commands could be +done well over the word structure of input text, and implemented +a prototype history mechanism in an older version of the shell. +Eric Allman has also provided a large number of useful comments on the +shell, helping to unify those concepts which are present and to identify +and eliminate useless and marginally useful features. +Mike O'Brien suggested the pathname hashing +mechanism which speeds command execution. +Jim Kulp added the job control and directory stack primitives and +added their documentation to this introduction. +.br +.bp +.NH +Terminal usage of the shell +.NH 2 +The basic notion of commands +.PP +A +.I shell +in +\s-2UNIX\s0 +acts mostly as a medium through which other +.I programs +are invoked. +While it has a set of +.I builtin +functions which it performs directly, +most commands cause execution of programs that are, in fact, +external to the shell. +The shell is thus distinguished from the command interpreters of other +systems both by the fact that it is just a user program, and by the fact +that it is used almost exclusively as a mechanism for invoking other programs. +.PP +.I Commands +in the \s-2UNIX\s0 system consist of a list of strings or +.I words +interpreted as a +.I "command name" +followed by +.I arguments. +Thus the command +.DS +mail bill +.DE +consists of two words. +The first word +.I mail +names the command to be executed, in this case the +mail program which sends messages to other users. +The shell uses the name of the command in attempting to execute it for you. +It will look in a number of +.I directories +for a file with the name +.I mail +which is expected to contain the mail program. +.PP +The rest of the words of the command are given as +.I arguments +to the command itself when it is executed. +In this case we specified also the argument +.I bill +which is interpreted by the +.I mail +program to be the name of a user to whom mail is to be sent. +In normal terminal usage we might use the +.I mail +command as follows. +.DS +% mail bill +I have a question about the csh documentation. +My document seems to be missing page 5. +Does a page five exist? + Bill +EOT +% +.DE +.PP +Here we typed a message to send to +.I bill +and ended this message with a ^D which sent an end-of-file to +the mail program. +(Here and throughout this document, the notation ``^\fIx\fR'' +is to be read ``control-\fIx\fR'' and represents the striking of the \fIx\fR +key while the control key is held down.) +The mail program +then echoed the characters `EOT' and transmitted our message. +The characters `% ' were printed before and after the mail command +by the shell to indicate that input was needed. +.PP +After typing the `% ' prompt the shell was reading command input from +our terminal. +We typed a complete command `mail bill'. +The shell then executed the +.I mail +program with argument +.I bill +and went dormant waiting for it to complete. +The mail program then read input from our terminal until we signalled +an end-of-file via typing a ^D after which the shell noticed +that mail had completed +and signaled us that it was ready to read from the terminal again by +printing another `% ' prompt. +.PP +This is the essential pattern of all interaction with \s-2UNIX\s0 +through the shell. +A complete command is typed at the terminal, the shell executes +the command and when this execution completes, it prompts for a new command. +If you run the editor for an hour, the shell will patiently wait for +you to finish editing and obediently prompt you again whenever you finish +editing. +.PP +An example of a useful command you can execute now is the +.I tset +command, which sets the default +.I erase +and +.I kill +characters on your terminal \- the erase character erases the last +character you typed and the kill character erases the entire line you +have entered so far. +By default, the erase character is the delete key (equivalent to `^?') +and the kill character is `^U'. Some people prefer to make the erase character +the backspace key (equivalent to `^H'). +You can make this be true by typing +.DS +tset \-e +.DE +which tells the program +.I tset +to set the erase character to tset's default setting for this character +(a backspace). +.NH 2 +Flag arguments +.PP +A useful notion in \s-2UNIX\s0 is that of a +.I flag +argument. +While many arguments to commands specify file names or user names, +some arguments rather specify an optional capability of the command +which you wish to invoke. +By convention, such arguments begin with the character `\-' (hyphen). +Thus the command +.DS +ls +.DE +will produce a list of the files in the current +.I "working directory" . +The option +.I \-s +is the size option, and +.DS +ls \-s +.DE +causes +.I ls +to also give, for each file the size of the file in blocks of 512 +characters. +The manual section for each command in the \s-2UNIX\s0 reference manual +gives the available options for each command. +The +.I ls +command has a large number of useful and interesting options. +Most other commands have either no options or only one or two options. +It is hard to remember options of commands which are not used very +frequently, so most \s-2UNIX\s0 utilities perform only one or two functions +rather than having a large number of hard to remember options. +.NH 2 +Output to files +.PP +Commands that normally read input or write output on the terminal +can also be executed with this input and/or output done to +a file. +.PP +Thus suppose we wish to save the current date in a file called `now'. +The command +.DS +date +.DE +will print the current date on our terminal. +This is because our terminal is the default +.I "standard output" +for the date command and the date command prints the date on its +standard output. +The shell lets us +.I redirect +the +.I "standard output" +of a command through a +notation using the +.I metacharacter +`>' and the name of the file where output is to be placed. +Thus the command +.DS +date > now +.DE +runs the +.I date +command such that its standard output is +the file `now' rather than the terminal. +Thus this command places the current date and time into the file `now'. +It is important to know that the +.I date +command was unaware that its output was going to a file rather than +to the terminal. +The shell performed this +.I redirection +before the command began executing. +.PP +One other thing to note here is that the file `now' +need not have existed before the +.I date +command was executed; the shell would have created the file if it did +not exist. +And if the file did exist? +If it had existed previously these previous contents would have been discarded! +A shell option +.I noclobber +exists to prevent this from happening accidentally; +it is discussed in section 2.2. +.PP +The system normally keeps files which you create with `>' and all other files. +Thus the default is for files to be permanent. If you wish to create a file +which will be removed automatically, you can begin its name with a `#' +character, this `scratch' character denotes the fact that the file will +be a scratch file.* +.FS +*Note that if your erase character is a `#', you will have to precede the +`#' with a `\e'. The fact that the `#' character is the old (pre-\s-2CRT\s0) +standard erase character means that it seldom appears in a file name, and +allows this convention to be used for scratch files. If you are using a +\s-2CRT\s0, your erase character should be a ^H, as we demonstrated +in section 1.1 how this could be set up. +.FE +The system will remove such files after a couple of days, +or sooner if file space becomes very tight. +Thus, in running the +.I date +command above, we don't really want to save the output forever, so we +would more likely do +.DS +date > #now +.DE +.NH 2 +Metacharacters in the shell +.PP +The shell has a large number of +special characters (like `>') +which indicate special functions. +We say that these notations have +.I syntactic +and +.I semantic +meaning to the shell. +In general, most characters which are neither letters nor digits +have special meaning to the shell. +We shall shortly learn a means of +.I quotation +which allows us to use +.I metacharacters +without the shell treating them in any special way. +.PP +Metacharacters normally have effect only when the shell is reading +our input. +We need not worry about placing shell metacharacters in a letter +we are sending via +.I mail, +or when we are typing in text or data to some other program. +Note that the shell is only reading input when it has prompted with +`% ' (although we can type our input even before it prompts). +.NH 2 +Input from files; pipelines +.PP +We learned above how to +.I redirect +the +.I "standard output" +of a command +to a file. +It is also possible to redirect the +.I "standard input" +of a command from a file. +This is not often necessary since most commands will read from +a file whose name is given as an argument. +We can give the command +.DS +sort < data +.DE +to run the +.I sort +command with standard input, where the command normally +reads its input, from the file +`data'. +We would more likely say +.DS +sort data +.DE +letting the +.I sort +command open the file +`data' +for input itself since this is less to type. +.PP +We should note that if we just typed +.DS +sort +.DE +then the sort program would sort lines from its +.I "standard input." +Since we did not +.I redirect +the standard input, it would sort lines as we typed them on the terminal +until we typed a ^D to indicate an end-of-file. +.PP +A most useful capability is the ability to combine the standard output +of one command with the standard input of another, i.e. to run the +commands in a sequence known as a +.I pipeline. +For instance the command +.DS +ls \-s +.DE +normally produces a list of the files in our directory with the size +of each in blocks of 512 characters. +If we are interested in learning which of our files is largest we +may wish to have this sorted by size rather than by name, which is +the default way in which +.I ls +sorts. +We could look at the many options of +.I ls +to see if there was an option to do this but would eventually discover +that there is not. +Instead we can use a couple of simple options of the +.I sort +command, combining it with +.I ls +to get what we want. +.PP +The +.I \-n +option of sort specifies a numeric sort rather than an alphabetic sort. +Thus +.DS +ls \-s | sort \-n +.DE +specifies that the output of the +.I ls +command run with the option +.I \-s +is to be +.I piped +to the command +.I sort +run with the numeric sort option. +This would give us a sorted list of our files by size, but with the +smallest first. +We could then use the +.I \-r +reverse sort option and the +.I head +command in combination with the previous command doing +.DS +ls \-s | sort \-n \-r | head \-5 +.DE +Here we have taken a list of our files sorted alphabetically, +each with the size in blocks. +We have run this to the standard input of the +.I sort +command asking it to sort numerically in reverse order (largest first). +This output has then been run into the command +.I head +which gives us the first few lines. +In this case we have asked +.I head +for the first 5 lines. +Thus this command gives us the names and sizes of our 5 largest files. +.PP +The notation introduced above is called the +.I pipe +mechanism. +Commands separated by `\||\|' characters are connected together by the +shell and the standard output of each is run into the standard input of the +next. +The leftmost command in a pipeline will normally take its standard +input from the terminal and the rightmost will place its standard +output on the terminal. +Other examples of pipelines will be given later when we discuss the +history mechanism; +one important use of pipes which is illustrated there is in the +routing of information to the line printer. +.NH 2 +Filenames +.PP +Many commands to be executed will need the names of files as arguments. +\s-2UNIX\s0 +.I pathnames +consist of a number of +.I components +separated by `/'. +Each component except the last names a directory in which the next +component resides, in effect specifying the +.I path +of directories to follow to reach the file. +Thus the pathname +.DS +/etc/motd +.DE +specifies a file in the directory +`etc' +which is a subdirectory of the +.I root +directory `/'. +Within this directory the file named is `motd' which stands +for `message of the day'. +A +.I pathname +that begins with a slash is said to be an +.I absolute +pathname since it is specified from the absolute top of the entire +directory hierarchy of the system (the +.I root ). +.I Pathnames +which do not begin with `/' are interpreted as starting in the current +.I "working directory" , +which is, by default, your +.I home +directory and can be changed dynamically by the +.I cd +change directory command. +Such pathnames are said to be +.I relative +to the working directory since they are found by starting +in the working directory and descending to lower levels of directories +for each +.I component +of the pathname. If the pathname contains no slashes at all then the +file is contained in the working directory itself and the pathname is merely +the name of the file in this directory. +Absolute pathnames have no relation +to the working directory. +.PP +Most filenames consist of a number of alphanumeric characters and +`.'s (periods). +In fact, all printing characters except `/' (slash) may appear in filenames. +It is inconvenient to have most non-alphabetic characters in filenames +because many of these have special meaning to the shell. +The character `.' (period) is not a shell-metacharacter and is often used +to separate the +.I extension +of a file name from the base of the name. +Thus +.DS +prog.c prog.o prog.errs prog.output +.DE +are four related files. +They share a +.I base +portion of a name +(a base portion being that part of the name that is left when a trailing +`.' and following characters which are not `.' are stripped off). +The file +`prog.c' +might be the source for a C program, +the file `prog.o' the corresponding object file, +the file +`prog.errs' the errors resulting from a compilation of the program +and the file +`prog.output' the output of a run of the program. +.PP +If we wished to refer to all four of these files in a command, we could +use the notation +.DS +prog.* +.DE +This expression is expanded by the shell, before the command to which it is +an argument is executed, into a list of names which begin with `prog.'. +The character `*' here matches any sequence (including the empty sequence) +of characters in a file name. +The names which match are alphabetically sorted and placed in the +.I "argument list" +of the command. +Thus the command +.DS +echo prog.* +.DE +will echo the names +.DS +prog.c prog.errs prog.o prog.output +.DE +Note that the names are in sorted order here, and a different +order than we listed them above. +The +.I echo +command receives four words as arguments, even though we only typed +one word as as argument directly. +The four words were generated by +.I "filename expansion" +of the one input word. +.PP +Other notations for +.I "filename expansion" +are also available. +The character `?' matches any single character in a filename. +Thus +.DS +echo ? \|?? \|??? +.DE +will echo a line of filenames; first those with one character names, +then those with two character names, and finally those with three +character names. +The names of each length will be independently sorted. +.PP +Another mechanism consists of a sequence of characters between `[' and `]'. +This metasequence matches any single character from the enclosed set. +Thus +.DS +prog.[co] +.DE +will match +.DS +prog.c prog.o +.DE +in the example above. +We can also place two characters around a `\-' in this notation to denote +a range. +Thus +.DS +chap.[1\-5] +.DE +might match files +.DS +chap.1 chap.2 chap.3 chap.4 chap.5 +.DE +if they existed. +This is shorthand for +.DS +chap.[12345] +.DE +and otherwise equivalent. +.PP +An important point to note is that if a list of argument words to +a command (an +.I "argument list)" +contains filename expansion syntax, and if this filename expansion syntax +fails to match any existing file names, then the shell considers this +to be an error and prints a diagnostic +.DS +No match. +.DE +and does not execute the command. +.PP +Another very important point is that files with the character `.' at the +beginning are treated specially. +Neither `*' or `?' or the `[' `]' mechanism will match it. +This prevents accidental matching of the filenames `.' and `..' +in the working directory which have special meaning to the system, +as well as other files such as +.I \&.cshrc +which are not normally +visible. +We will discuss the special role of the file +.I \&.cshrc +later. +.PP +Another filename expansion mechanism gives access to the pathname of +the +.I home +directory of other users. +This notation consists of the character `~' (tilde) followed by another user's +login name. +For instance the word `~bill' would map to the pathname `/usr/bill' +if the home directory for `bill' was `/usr/bill'. +Since, on large systems, users may have login directories scattered over +many different disk volumes with different prefix directory names, +this notation provides a convenient way of accessing the files +of other users. +.PP +A special case of this notation consists of a `~' alone, e.g. `~/mbox'. +This notation is expanded by the shell into the file `mbox' in your +.I home +directory, i.e. into `/usr/bill/mbox' for me on Ernie Co-vax, the UCB +Computer Science Department VAX machine, where this document was prepared. +This can be very useful if you have used +.I cd +to change to another directory and have found a file you wish to +copy using +.I cp. +If I give the command +.DS +cp thatfile ~ +.DE +the shell will expand this command to +.DS +cp thatfile /usr/bill +.DE +since my home directory is /usr/bill. +.PP +There also exists a mechanism using the characters `{' and `}' for +abbreviating a set of words which have common parts but cannot +be abbreviated by the above mechanisms because they are not files, +are the names of files which do not yet exist, +are not thus conveniently described. +This mechanism will be described much later, +in section 4.2, +as it is used less frequently. +.NH 2 +Quotation +.PP +We have already seen a number of metacharacters used by the shell. +These metacharacters pose a problem in that we cannot use them directly +as parts of words. +Thus the command +.DS +echo * +.DE +will not echo the character `*'. +It will either echo an sorted list of filenames in the +current +.I "working directory," +or print the message `No match' if there are +no files in the working directory. +.PP +The recommended mechanism for placing characters which are neither numbers, +digits, `/', `.' or `\-' in an argument word to a command is to enclose +it with single quotation characters `\'', i.e. +.DS +echo \'*\' +.DE +There is one special character `!' which is used by the +.I history +mechanism of the shell and which cannot be +.I escaped +by placing it within `\'' characters. +It and the character `\'' itself can be preceded by a single `\e' +to prevent their special meaning. +Thus +.DS +echo \e\'\e! +.DE +prints +.DS +\'! +.DE +These two mechanisms suffice to place any printing character into a word +which is an argument to a shell command. They can be combined, as in +.DS +echo \e\'\'*\' +.DE +which prints +.DS +\'* +.DE +since the first `\e' escaped the first `\'' and the `*' was enclosed +between `\'' characters. +.NH 2 +Terminating commands +.PP +When you are executing a command and the shell is +waiting for it to complete there are several ways +to force it to stop. +For instance if you type the command +.DS +cat /etc/passwd +.DE +the system will print a copy of a list of all users of the system +on your terminal. +This is likely to continue for several minutes unless you stop it. +You can send an +\s-2INTERRUPT\s0 +.I signal +to the +.I cat +command by typing ^C on your terminal.* +.FS +*On some older Unix systems the \s-2DEL\s0 or \s-2RUBOUT\s0 key +has the same effect. "stty all" will tell you the INTR key value. +.FE +Since +.I cat +does not take any precautions to avoid or otherwise handle this signal +the +\s-2INTERRUPT\s0 +will cause it to terminate. +The shell notices that +.I cat +has terminated and prompts you again with `% '. +If you hit \s-2INTERRUPT\s0 again, the shell will just +repeat its prompt since it handles \s-2INTERRUPT\s0 signals +and chooses to continue to execute commands rather than terminating +like +.I cat +did, which would have the effect of logging you out. +.PP +Another way in which many programs terminate is when they get an end-of-file +from their standard input. +Thus the +.I mail +program in the first example above was terminated when we typed a ^D +which generates an end-of-file from the standard input. +The shell also terminates when it gets an end-of-file printing `logout'; +\s-2UNIX\s0 then logs you off the system. +Since this means that typing too many ^D's can accidentally log us off, +the shell has a mechanism for preventing this. +This +.I ignoreeof +option will be discussed in section 2.2. +.PP +If a command has its standard input redirected from a file, then it will +normally terminate when it reaches the end of this file. +Thus if we execute +.DS +mail bill < prepared.text +.DE +the mail command will terminate without our typing a ^D. +This is because it read to the end-of-file of our file +`prepared.text' in which we placed a message for `bill' with an editor program. +We could also have done +.DS +cat prepared.text \||\| mail bill +.DE +since the +.I cat +command would then have written the text through the pipe to the +standard input of the mail command. +When the +.I cat +command completed it would have terminated, +closing down the pipeline +and the +.I mail +command would have received an end-of-file from it and terminated. +Using a pipe here is more complicated than redirecting input +so we would more likely use the first form. +These commands could also have been stopped by sending an \s-2INTERRUPT\s0. +.PP +Another possibility for stopping a command is to suspend its execution +temporarily, with the possibility of continuing execution later. This is +done by sending a \s-2STOP\s0 signal via typing a ^Z. +This signal causes all commands running on the terminal +(usually one but more if a pipeline is executing) to become suspended. +The shell notices that the command(s) have been suspended, types +`Stopped' and then prompts for a new command. +The previously executing command has been suspended, but otherwise +unaffected by the \s-2STOP\s0 signal. Any other commands can be executed +while the original command remains suspended. The suspended command can +be continued using the +.I fg +command with no arguments. The shell will then retype the command +to remind you which command is being continued, and cause the command +to resume execution. Unless any input files in use by the suspended +command have been changed in the meantime, the suspension has no effect +whatsoever on the execution of the command. This feature can be very useful +during editing, when you need to look at another file before continuing. An +example of command suspension follows. +.DS +% mail harold +Someone just copied a big file into my directory and its name is +^Z +Stopped +% ls +funnyfile +prog.c +prog.o +% jobs +.ta 1.75i +[1] + Stopped mail harold +% fg +mail harold +funnyfile. Do you know who did it? +EOT +% +.so tabs +.DE +In this example someone was sending a message to Harold and forgot the +name of the file he wanted to mention. The mail command was suspended +by typing ^Z. When the shell noticed that the mail program was +suspended, it typed `Stopped' and prompted for a new command. Then the +.I ls +command was typed to find out the name of the file. The +.I jobs +command was run to find out which command was suspended. At this time the +.I fg +command was typed to continue execution of the mail program. Input +to the mail program was then continued and ended with a ^D +which indicated the end of the message at which time the mail +program typed EOT. The +.I jobs +command will show which commands are suspended. +The ^Z should only be typed at the beginning of a line since +everything typed on the current line is discarded when a signal is sent +from the keyboard. This also happens on \s-2INTERRUPT\s0, and \s-2QUIT\s0 +signals. More information on +suspending jobs and controlling them is given in +section 2.6. +.PP +If you write or run programs which are not fully debugged then it may +be necessary to stop them somewhat ungracefully. +This can be done by sending them a \s-2QUIT\s0 +signal, sent by typing a ^\e. +This will usually provoke the shell to produce a message like: +.DS +Quit (Core dumped) +.DE +indicating that a file +`core' has been created containing information about the running program's +state when it terminated due to the \s-2QUIT\s0 signal. +You can examine this file yourself, or forward information to the +maintainer of the program telling him/her where the +.I "core file" +is. +.PP +If you run background commands (as explained in section 2.6) then these +commands will ignore \s-2INTERRUPT\s0 and \s-2QUIT\s0 signals at the +terminal. To stop them you must use the +.I kill +command. See section 2.6 for an example. +.PP +If you want to examine the output of a command without having it move +off the screen as the output of the +.DS +cat /etc/passwd +.DE +command will, you can use the command +.DS +more /etc/passwd +.DE +The +.I more +program pauses after each complete screenful and types `\-\-More\-\-' +at which point you can hit a space to get another screenful, a return +to get another line, a `?' to get some help on other commands, or a `q' to end the +.I more +program. You can also use more as a filter, i.e. +.DS +cat /etc/passwd | more +.DE +works just like the more simple more command above. +.PP +For stopping output of commands not involving +.I more +you can use the +^S key to stop the typeout. The typeout will resume when you +hit ^Q or any other key, but ^Q is normally used because +it only restarts the output and does not become input to the program +which is running. This works well on low-speed terminals, but at 9600 +baud it is hard to type ^S and ^Q fast enough to paginate +the output nicely, and a program like +.I more +is usually used. +.PP +An additional possibility is to use the ^O flush output +character; when this character is typed, all output from the current +command is thrown away (quickly) until the next input read occurs +or until the next shell prompt. This can be used to allow a command +to complete without having to suffer through the output on a slow +terminal; ^O is a toggle, so flushing can be turned off by +typing ^O again while output is being flushed. +.NH 2 +What now? +.PP +We have so far seen a number of mechanisms of the shell and learned a lot +about the way in which it operates. +The remaining sections will go yet further into the internals of the +shell, but you will surely want to try using the +shell before you go any further. +To try it you can log in to \s-2UNIX\s0 and type the following +command to the system: +.DS +chsh myname /bin/csh +.DE +Here `myname' should be replaced by the name you typed to +the system prompt of `login:' to get onto the system. +Thus I would use `chsh bill /bin/csh'. +.B +You only have to do this once; it takes effect at next login. +.R +You are now ready to try using +.I csh. +.PP +Before you do the `chsh' command, the shell you are using when +you log into the system is `/bin/sh'. +In fact, much of the above discussion is applicable to `/bin/sh'. +The next section will introduce many features particular to +.I csh +so you should change your shell to +.I csh +before you begin reading it. +.bp diff --git a/bin/csh/USD.doc/csh.2 b/bin/csh/USD.doc/csh.2 new file mode 100644 index 000000000000..610105c829fa --- /dev/null +++ b/bin/csh/USD.doc/csh.2 @@ -0,0 +1,1305 @@ +.\" Copyright (c) 1980, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" 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. +.\" +.\" @(#)csh.2 8.1 (Berkeley) 6/8/93 +.\" +.nr H1 1 +.NH +Details on the shell for terminal users +.NH 2 +Shell startup and termination +.PP +When you login, the shell is started by the system in your +.I home +directory and begins by reading commands from a file +.I \&.cshrc +in this directory. +All shells which you may start during your terminal session will +read from this file. +We will later see what kinds of commands are usefully placed there. +For now we need not have this file and the shell does not complain about +its absence. +.PP +A +.I "login shell" , +executed after you login to the system, +will, after it reads commands from +.I \&.cshrc, +read commands from a file +.I \&.login +also in your home directory. +This file contains commands which you wish to do each time you login +to the \s-2UNIX\s0 system. +My +.I \&.login +file looks something like: +.DS +set ignoreeof +set mail=(/usr/spool/mail/bill) +echo "${prompt}users" ; users +alias ts \e + \'set noglob ; eval \`tset \-s \-m dialup:c100rv4pna \-m plugboard:?hp2621nl \!*\`\'; +ts; stty intr ^C kill ^U crt +set time=15 history=10 +msgs \-f +if (\-e $mail) then + echo "${prompt}mail" + mail +endif +.DE +.PP +This file contains several commands to be executed by \s-2UNIX\s0 +each time I login. +The first is a +.I set +command which is interpreted directly by the shell. It sets the shell +variable +.I ignoreeof +which causes the shell to not log me off if I hit ^D. Rather, +I use the +.I logout +command to log off of the system. +By setting the +.I mail +variable, I ask the shell to watch for incoming mail to me. Every 5 minutes +the shell looks for this file and tells me if more mail has arrived there. +An alternative to this is to put the command +.DS +biff y +.DE +in place of this +.I set; +this will cause me to be notified immediately when mail arrives, and to +be shown the first few lines of the new message. +.PP +Next I set the shell variable `time' to `15' causing the shell to automatically +print out statistics lines for commands which execute for at least 15 seconds +of \s-2CPU\s+2 time. The variable `history' is set to 10 indicating that +I want the shell to remember the last 10 commands I type in its +.I "history list" , +(described later). +.PP +I create an +.I alias +``ts'' which executes a +\fItset\fR\|(1) command setting up the modes of the terminal. +The parameters to +.I tset +indicate the kinds of terminal which I usually use when not on a hardwired +port. I then execute ``ts'' and also use the +.I stty +command to change the interrupt character to ^C and the line kill +character to ^U. +.PP +I then run the `msgs' program, which provides me with any +system messages which I have not seen before; the `\-f' option here prevents +it from telling me anything if there are no new messages. +Finally, if my mailbox file exists, then I run the `mail' program to +process my mail. +.PP +When the `mail' and `msgs' programs finish, the shell will finish +processing my +.I \&.login +file and begin reading commands from the terminal, prompting for each with +`% '. +When I log off (by giving the +.I logout +command) the shell +will print `logout' and execute commands from the file `.logout' +if it exists in my home directory. +After that the shell will terminate and \s-2UNIX\s0 will log +me off the system. +If the system is not going down, I will receive a new login message. +In any case, after the `logout' message the shell is committed to terminating +and will take no further input from my terminal. +.NH 2 +Shell variables +.PP +The shell maintains a set of +.I variables. +We saw above the variables +.I history +and +.I time +which had values `10' and `15'. +In fact, each shell variable has as value an array of +zero or more +.I strings. +Shell variables may be assigned values by the set command. It has +several forms, the most useful of which was given above and is +.DS +set name=value +.DE +.PP +Shell variables may be used to store values which are to +be used in commands later through a substitution mechanism. +The shell variables most commonly referenced are, however, those which the +shell itself refers to. +By changing the values of these variables one can directly affect the +behavior of the shell. +.PP +One of the most important variables is the variable +.I path. +This variable contains a sequence of directory names where the shell +searches for commands. +The +.I set +command with no arguments +shows the value of all variables currently defined (we usually say +.I set) +in the shell. +The default value for path will be shown by +.I set +to be +.DS +% set +.ta .75i +argv () +cwd /usr/bill +home /usr/bill +path (. /usr/ucb /bin /usr/bin) +prompt % +shell /bin/csh +status 0 +term c100rv4pna +user bill +% +.so tabs +.DE +This output indicates that the variable path points to the current +directory `.' and then `/usr/ucb', `/bin' and `/usr/bin'. +Commands which you may write might be in `.' (usually one of +your directories). +Commands developed at Berkeley, live in `/usr/ucb' +while commands developed at Bell Laboratories live in `/bin' and `/usr/bin'. +.PP +A number of locally developed programs on the system live in the directory +`/usr/local'. +If we wish that all shells which we invoke to have +access to these new programs we can place the command +.DS +set path=(. /usr/ucb /bin /usr/bin /usr/local) +.DE +in our file +.I \&.cshrc +in our home directory. +Try doing this and then logging out and back in and do +.DS +set +.DE +again to see that the value assigned to +.I path +has changed. +.FS \(dg +Another directory that might interest you is /usr/new, which contains +many useful user-contributed programs provided with Berkeley Unix. +.FE +.PP +One thing you should be aware of is that the shell examines each directory +which you insert into your path and determines which commands are contained +there. Except for the current directory `.', which the shell treats specially, +this means that if commands are added to a directory in your search path after +you have started the shell, they will not necessarily be found by the shell. +If you wish to use a command which has been added in this way, you should +give the command +.DS +rehash +.DE +to the shell, which will cause it to recompute its internal table of command +locations, so that it will find the newly added command. +Since the shell has to look in the current directory `.' on each command, +placing it at the end of the path specification usually works equivalently +and reduces overhead. +.PP +Other useful built in variables are the variable +.I home +which shows your home directory, +.I cwd +which contains your current working directory, +the variable +.I ignoreeof +which can be set in your +.I \&.login +file to tell the shell not to exit when it receives an end-of-file from +a terminal (as described above). +The variable `ignoreeof' +is one of several variables which the shell does not care about the +value of, only whether they are +.I set +or +.I unset. +Thus to set this variable you simply do +.DS +set ignoreeof +.DE +and to unset it do +.DS +unset ignoreeof +.DE +These give the variable `ignoreeof' no value, but none is desired or required. +.PP +Finally, some other built-in shell variables of use are the +variables +.I noclobber +and +.I mail. +The metasyntax +.DS +> filename +.DE +which redirects the standard output of a command +will overwrite and destroy the previous contents of the named file. +In this way you may accidentally overwrite a file which is valuable. +If you would prefer that the shell not overwrite files in this +way you can +.DS +set noclobber +.DE +in your +.I \&.login +file. +Then trying to do +.DS +date > now +.DE +would cause a diagnostic if `now' existed already. +You could type +.DS +date >! now +.DE +if you really wanted to overwrite the contents of `now'. +The `>!' is a special metasyntax indicating that clobbering the +file is ok.\(dg +.FS +\(dgThe space between the `!' and the word `now' is critical here, as `!now' +would be an invocation of the +.I history +mechanism, and have a totally different effect. +.FE +.NH 2 +The shell's history list +.PP +The shell can maintain a +.I "history list" +into which it places the words +of previous commands. +It is possible to use a notation to reuse commands or words +from commands in forming new commands. +This mechanism can be used to repeat previous commands or to +correct minor typing mistakes in commands. +.PP +The following figure gives a sample session involving typical usage of the +history mechanism of the shell. +.KF +.DS +% cat bug.c +main() + +{ + printf("hello); +} +% cc !$ +cc bug.c +"bug.c", line 4: newline in string or char constant +"bug.c", line 5: syntax error +% ed !$ +ed bug.c +29 +4s/);/"&/p + printf("hello"); +w +30 +q +% !c +cc bug.c +% a.out +hello% !e +ed bug.c +30 +4s/lo/lo\e\en/p + printf("hello\en"); +w +32 +q +% !c \-o bug +cc bug.c \-o bug +% size a.out bug +a.out: 2784+364+1028 = 4176b = 0x1050b +bug: 2784+364+1028 = 4176b = 0x1050b +% ls \-l !* +ls \-l a.out bug +\(mirwxr\(mixr\(mix 1 bill 3932 Dec 19 09:41 a.out +\(mirwxr\(mixr\(mix 1 bill 3932 Dec 19 09:42 bug +% bug +hello +% num bug.c | spp +spp: Command not found. +% ^spp^ssp +num bug.c | ssp + 1 main() + 3 { + 4 printf("hello\en"); + 5 } +% !! | lpr +num bug.c | ssp | lpr +% +.DE +.KE +In this example we have a very simple C program which has a bug (or two) +in it in the file `bug.c', which we `cat' out on our terminal. We then +try to run the C compiler on it, referring to the file again as `!$', +meaning the last argument to the previous command. Here the `!' is the +history mechanism invocation metacharacter, and the `$' stands for the last +argument, by analogy to `$' in the editor which stands for the end of the line. +The shell echoed the command, as it would have been typed without use of +the history mechanism, and then executed it. +The compilation yielded error diagnostics so we now run the editor on the +file we were trying to compile, fix the bug, and run the C compiler again, +this time referring to this command simply as `!c', which repeats the last +command which started with the letter `c'. If there were other +commands starting with `c' done recently we could have said `!cc' or even +`!cc:p' which would have printed the last command starting with `cc' +without executing it. +.PP +After this recompilation, we ran the resulting `a.out' file, and then +noting that there still was a bug, ran the editor again. After fixing +the program we ran the C compiler again, but tacked onto the command +an extra `\-o bug' telling the compiler to place the resultant binary in +the file `bug' rather than `a.out'. In general, the history mechanisms +may be used anywhere in the formation of new commands and other characters +may be placed before and after the substituted commands. +.PP +We then ran the `size' command to see how large the binary program images +we have created were, and then an `ls \-l' command with the same argument +list, denoting the argument list `\!*'. +Finally we ran the program `bug' to see that its output is indeed correct. +.PP +To make a numbered listing of the program we ran the `num' command on the file `bug.c'. +In order to compress out blank lines in the output of `num' we ran the +output through the filter `ssp', but misspelled it as spp. To correct this +we used a shell substitute, placing the old text and new text between `^' +characters. This is similar to the substitute command in the editor. +Finally, we repeated the same command with `!!', but sent its output to the +line printer. +.PP +There are other mechanisms available for repeating commands. The +.I history +command prints out a number of previous commands with numbers by which +they can be referenced. There is a way to refer to a previous command +by searching for a string which appeared in it, and there are other, +less useful, ways to select arguments to include in a new command. +A complete description of all these mechanisms +is given in the C shell manual pages in the \s-2UNIX\s0 Programmer's Manual. +.NH 2 +Aliases +.PP +The shell has an +.I alias +mechanism which can be used to make transformations on input commands. +This mechanism can be used to simplify the commands you type, +to supply default arguments to commands, +or to perform transformations on commands and their arguments. +The alias facility is similar to a macro facility. +Some of the features obtained by aliasing can be obtained also +using shell command files, but these take place in another instance +of the shell and cannot directly affect the current shells environment +or involve commands such as +.I cd +which must be done in the current shell. +.PP +As an example, suppose that there is a new version of the mail program +on the system called `newmail' +you wish to use, rather than the standard mail program which is called +`mail'. +If you place the shell command +.DS +alias mail newmail +.DE +in your +.I \&.cshrc +file, the shell will transform an input line of the form +.DS +mail bill +.DE +into a call on `newmail'. +More generally, suppose we wish the command `ls' to always show +sizes of files, that is to always do `\-s'. +We can do +.DS +alias ls ls \-s +.DE +or even +.DS +alias dir ls \-s +.DE +creating a new command syntax `dir' +which does an `ls \-s'. +If we say +.DS +dir ~bill +.DE +then the shell will translate this to +.DS +ls \-s /mnt/bill +.DE +.PP +Thus the +.I alias +mechanism can be used to provide short names for commands, +to provide default arguments, +and to define new short commands in terms of other commands. +It is also possible to define aliases which contain multiple +commands or pipelines, showing where the arguments to the original +command are to be substituted using the facilities of the +history mechanism. +Thus the definition +.DS +alias cd \'cd \e!* ; ls \' +.DE +would do an +.I ls +command after each change directory +.I cd +command. +We enclosed the entire alias definition in `\'' characters to prevent +most substitutions from occurring and the character `;' from being +recognized as a metacharacter. +The `!' here is escaped with a `\e' to prevent it from being interpreted +when the alias command is typed in. +The `\e!*' here substitutes the entire argument list to the pre-aliasing +.I cd +command, without giving an error if there were no arguments. +The `;' separating commands is used here +to indicate that one command is to be done and then the next. +Similarly the definition +.DS +alias whois \'grep \e!^ /etc/passwd\' +.DE +defines a command which looks up its first argument in the password file. +.PP +.B Warning: +The shell currently reads the +.I \&.cshrc +file each time it starts up. If you place a large number of commands +there, shells will tend to start slowly. A mechanism for saving the shell +environment after reading the \fI\&.cshrc\fR file and quickly restoring it is +under development, but for now you should try to limit the number of +aliases you have to a reasonable number... 10 or 15 is reasonable, +50 or 60 will cause a noticeable delay in starting up shells, and make +the system seem sluggish when you execute commands from within the editor +and other programs. +.NH 2 +More redirection; >> and >& +.PP +There are a few more notations useful to the terminal user +which have not been introduced yet. +.PP +In addition to the standard output, commands also have a +.I "diagnostic output" +which is normally directed to the terminal even when the standard output +is redirected to a file or a pipe. +It is occasionally desirable to direct the diagnostic output along with +the standard output. +For instance if you want to redirect the output of a long running command +into a file and wish to have a record of any error diagnostic it produces +you can do +.DS +command >& file +.DE +The `>&' here tells the shell to route both the diagnostic output and the +standard output into `file'. +Similarly you can give the command +.DS +command |\|& lpr +.DE +to route both standard and diagnostic output through the pipe +to the line printer daemon +.I lpr.\(dd +.FS +\(dd A command of the form +.br +.ti +5 +command >&! file +.br +exists, and is used when +.I noclobber +is set and +.I file +already exists. +.FE +.PP +Finally, it is possible to use the form +.DS +command >> file +.DE +to place output at the end of an existing file.\(dg +.FS +\(dg If +.I noclobber +is set, then an error will result if +.I file +does not exist, otherwise the shell will create +.I file +if it doesn't exist. +A form +.br +.ti +5 +command >>! file +.br +makes it not be an error for file to not exist when +.I noclobber +is set. +.FE +.NH 2 +Jobs; Background, Foreground, or Suspended +.PP +When one or more commands +are typed together as a pipeline or as a sequence of commands separated by +semicolons, a single +.I job +is created by the shell consisting of these commands together as a unit. +Single commands without pipes or semicolons create the simplest jobs. +Usually, every line typed to the shell creates a job. +Some lines that create jobs (one per line) are +.DS +sort < data +ls \-s | sort \-n | head \-5 +mail harold +.DE +.PP +If the metacharacter `&' is typed +at the end of the commands, then the job is started as a +.I background +job. This means that the shell does not wait for it to complete but +immediately prompts and is ready for another command. The job runs +.I "in the background" +at the same time that normal jobs, called +.I foreground +jobs, continue to be read and executed by the shell one at a time. +Thus +.DS +du > usage & +.DE +would run the +.I du +program, which reports on the disk usage of your working directory (as well as +any directories below it), put the output into the file `usage' and return +immediately with a prompt for the next command without out waiting for +.I du +to finish. The +.I du +program would continue executing in the background +until it finished, even though you can type and execute more commands in the +mean time. +When a background +job terminates, a message is typed by the shell just before the next prompt +telling you that the job has completed. +In the following example the +.I du +job finishes sometime during the +execution of the +.I mail +command and its completion is reported just before +the prompt after the +.I mail +job is finished. +.DS +% du > usage & +[1] 503 +% mail bill +How do you know when a background job is finished? +EOT +.ta 1.75i +[1] \- Done du > usage +% +.so tabs +.DE +If the job did not terminate normally the `Done' message might say +something else like `Killed'. +If you want the +terminations of background jobs to be reported at the time they occur +(possibly interrupting the output of other foreground jobs), you can set +the +.I notify +variable. In the previous example this would mean that the +`Done' message might have come right in the middle of the message to +Bill. +Background jobs are unaffected by any signals from the keyboard like +the \s-2STOP\s0, \s-2INTERRUPT\s0, or \s-2QUIT\s0 signals mentioned earlier. +.PP +Jobs are recorded in a table inside the shell until they terminate. +In this table, the shell remembers the command names, arguments and the +.I "process numbers" +of all commands in the job as well as the working directory where the job was +started. +Each job in the table is either running +.I "in the foreground" +with the shell waiting for it to terminate, running +.I "in the background," +or +.I suspended. +Only one job can be running in the foreground at one time, but several +jobs can be suspended or running in the background at once. As each job +is started, it is assigned a small identifying +number called the +.I "job number" +which can be used later to refer to the job in the commands described below. +Job numbers remain +the same until the job terminates and then are re-used. +.PP +When a job is started in the backgound using `&', its number, as well +as the process numbers of all its (top level) commands, is typed by the shell +before prompting you for another command. For example, +.DS +% ls \-s | sort \-n > usage & +[2] 2034 2035 +% +.DE +runs the `ls' program with the `\-s' options, pipes this output into +the `sort' program with the `\-n' option which puts its output into the +file `usage'. +Since the `&' was at the end of the line, these two programs were started +together as a background job. After starting the job, the shell prints +the job number in brackets (2 in this case) followed by the process number +of each program started in the job. Then the shell immediates prompts for +a new command, leaving the job running simultaneously. +.PP +As mentioned in section 1.8, foreground jobs become +.I suspended +by typing ^Z +which sends a \s-2STOP\s0 signal to the currently running +foreground job. A background job can become suspended by using the +.I stop +command described below. When jobs are suspended they merely stop +any further progress until started again, either in the foreground +or the backgound. The shell notices when a job becomes stopped and +reports this fact, much like it reports the termination of background jobs. +For foreground jobs this looks like +.DS +% du > usage +^Z +Stopped +% +.DE +`Stopped' message is typed by the shell when it notices that the +.I du +program stopped. +For background jobs, using the +.I stop +command, it is +.DS +% sort usage & +[1] 2345 +% stop %1 +.ta 1.75i +[1] + Stopped (signal) sort usage +% +.so tabs +.DE +Suspending foreground jobs can be very useful when you need to temporarily +change what you are doing (execute other commands) and then return to +the suspended job. Also, foreground jobs can be suspended and then +continued as background jobs using the +.I bg +command, allowing you to continue other work and +stop waiting for the foreground job to finish. Thus +.DS +% du > usage +^Z +Stopped +% bg +[1] du > usage & +% +.DE +starts `du' in the foreground, stops it before it finishes, then continues +it in the background allowing more foreground commands to be executed. +This is especially helpful +when a foreground job ends up taking longer than you expected and you +wish you had started it in the backgound in the beginning. +.PP +All +.I "job control" +commands can take an argument that identifies a particular +job. +All job name arguments begin with the character `%', since some of the +job control commands also accept process numbers (printed by the +.I ps +command.) +The default job (when no argument is given) is called the +.I current +job and is identified by a `+' in the output of the +.I jobs +command, which shows you which jobs you have. +When only one job is stopped or running in the background (the usual case) +it is always the current job thus no argument is needed. +If a job is stopped while running in the foreground it becomes the +.I current +job and the existing current job becomes the +.I previous +job \- identified by a `\-' in the output of +.I jobs. +When the current job terminates, the previous job becomes the current job. +When given, the argument is either `%\-' (indicating +the previous job); `%#', where # is the job number; +`%pref' where pref is some unique prefix of the command name +and arguments of one of the jobs; or `%?' followed by some string found +in only one of the jobs. +.PP +The +.I jobs +command types the table of jobs, giving the job number, +commands and status (`Stopped' or `Running') of each backgound or +suspended job. With the `\-l' option the process numbers are also +typed. +.DS +% du > usage & +[1] 3398 +% ls \-s | sort \-n > myfile & +[2] 3405 +% mail bill +^Z +Stopped +% jobs +.ta 1.75i +[1] \(mi Running du > usage +[2] Running ls \-s | sort \-n > myfile +[3] \(pl Stopped mail bill +% fg %ls +ls \-s | sort \-n > myfile +% more myfile +.so tabs +.DE +.PP +The +.I fg +command runs a suspended or background job in the foreground. It is +used to restart a previously suspended job or change a background job +to run in the foreground (allowing signals or input from the terminal). +In the above example we used +.I fg +to change the `ls' job from the +background to the foreground since we wanted to wait for it to +finish before looking at its output file. +The +.I bg +command runs a suspended job in the background. It is usually used +after stopping the currently running foreground job with the +\s-2STOP\s0 signal. The combination of the \s-2STOP\s0 signal and the +.I bg +command changes a foreground job into a background job. +The +.I stop +command suspends a background job. +.PP +The +.I kill +command terminates a background or suspended job immediately. +In addition to jobs, it may be given process numbers as arguments, +as printed by +.I ps. +Thus, in the example above, the running +.I du +command could have been terminated by the command +.DS +% kill %1 +.ta 1.75i +[1] Terminated du > usage +% +.so tabs +.DE +.PP +The +.I notify +command (not the variable mentioned earlier) indicates that the termination +of a specific job should be +reported at the time it finishes instead of waiting for the next prompt. +.PP +If a job running in the background tries to read input from the terminal +it is automatically stopped. When such a job is then run in the +foreground, input can be given to the job. If desired, the job can +be run in the background again until it requests input again. +This is illustrated in the following sequence where the `s' command in the +text editor might take a long time. +.ID +.nf +% ed bigfile +120000 +1,$s/thisword/thatword/ +^Z +Stopped +% bg +[1] ed bigfile & +% + . . . some foreground commands +.ta 1.75i +[1] Stopped (tty input) ed bigfile +% fg +ed bigfile +w +120000 +q +% +.so tabs +.DE +So after the `s' command was issued, the `ed' job was stopped with ^Z +and then put in the background using +.I bg. +Some time later when the `s' command was finished, +.I ed +tried to read another command and was stopped because jobs +in the backgound cannot read from the terminal. The +.I fg +command returned the `ed' job to the foreground where it could once again +accept commands from the terminal. +.PP +The command +.DS +stty tostop +.DE +causes all background jobs run on your terminal to stop +when they are about to +write output to the terminal. This prevents messages from background +jobs from interrupting foreground job output and allows you to run +a job in the background without losing terminal output. It also +can be used for interactive programs that sometimes have long +periods without interaction. Thus each time it outputs a prompt for more +input it will stop before the prompt. It can then be run in the +foreground using +.I fg, +more input can be given and, if necessary stopped and returned to +the background. This +.I stty +command might be a good thing to put in your +.I \&.login +file if you do not like output from background jobs interrupting +your work. It also can reduce the need for redirecting the output +of background jobs if the output is not very big: +.DS +% stty tostop +% wc hugefile & +[1] 10387 +% ed text +\&. . . some time later +q +.ta 1.75i +[1] Stopped (tty output) wc hugefile +% fg wc +wc hugefile + 13371 30123 302577 +% stty \-tostop +.so tabs +.DE +Thus after some time the `wc' command, which counts the lines, words +and characters in a file, had one line of output. When it tried to +write this to the terminal it stopped. By restarting it in the +foreground we allowed it to write on the terminal exactly when we were +ready to look at its output. +Programs which attempt to change the mode of the terminal will also +block, whether or not +.I tostop +is set, when they are not in the foreground, as +it would be very unpleasant to have a background job change the state +of the terminal. +.PP +Since the +.I jobs +command only prints jobs started in the currently executing shell, +it knows nothing about background jobs started in other login sessions +or within shell files. The +.I ps +can be used in this case to find out about background jobs not started +in the current shell. +.NH 2 +Working Directories +.PP +As mentioned in section 1.6, the shell is always in a particular +.I "working directory." +The `change directory' command +.I chdir +(its +short form +.I cd +may also be used) +changes the working directory of the shell, +that is, changes the directory you +are located in. +.PP +It is useful to make a directory for each project you wish to work on +and to place all files related to that project in that directory. +The `make directory' command, +.I mkdir, +creates a new directory. +The +.I pwd +(`print working directory') command +reports the absolute pathname of the working directory of the shell, +that is, the directory you are +located in. +Thus in the example below: +.DS +% pwd +/usr/bill +% mkdir newpaper +% chdir newpaper +% pwd +/usr/bill/newpaper +% +.DE +the user has created and moved to the +directory +.I newpaper. +where, for example, he might +place a group of related files. +.PP +No matter where you have moved to in a directory hierarchy, +you can return to your `home' login directory by doing just +.DS +cd +.DE +with no arguments. +The name `..' always means the directory above the current one in +the hierarchy, thus +.DS +cd .. +.DE +changes the shell's working directory to the one directly above the +current one. +The name `..' can be used in any +pathname, thus, +.DS +cd ../programs +.DE +means +change to the directory `programs' contained in the directory +above the current one. +If you have several directories for different +projects under, say, your home directory, +this shorthand notation +permits you to switch easily between them. +.PP +The shell always remembers the pathname of its current working directory in +the variable +.I cwd. +The shell can also be requested to remember the previous directory when +you change to a new working directory. If the `push directory' command +.I pushd +is used in place of the +.I cd +command, the shell saves the name of the current working directory +on a +.I "directory stack" +before changing to the new one. +You can see this list at any time by typing the `directories' +command +.I dirs. +.ID +.nf +% pushd newpaper/references +~/newpaper/references ~ +% pushd /usr/lib/tmac +/usr/lib/tmac ~/newpaper/references ~ +% dirs +/usr/lib/tmac ~/newpaper/references ~ +% popd +~/newpaper/references ~ +% popd +~ +% +.DE +The list is printed in a horizontal line, reading left to right, +with a tilde (~) as +shorthand for your home directory\(emin this case `/usr/bill'. +The directory stack is printed whenever there is more than one +entry on it and it changes. +It is also printed by a +.I dirs +command. +.I Dirs +is usually faster and more informative than +.I pwd +since it shows the current working directory as well as any +other directories remembered in the stack. +.PP +The +.I pushd +command with no argument +alternates the current directory with the first directory in the +list. +The `pop directory' +.I popd +command without an argument returns you to the directory you were in prior to +the current one, discarding the previous current directory from the +stack (forgetting it). +Typing +.I popd +several times in a series takes you backward through the directories +you had been in (changed to) by +.I pushd +command. +There are other options to +.I pushd +and +.I popd +to manipulate the contents of the directory stack and to change +to directories not at the top of the stack; see the +.I csh +manual page for details. +.PP +Since the shell remembers the working directory in which each job +was started, it warns you when you might be confused by restarting +a job in the foreground which has a different working directory than the +current working directory of the shell. Thus if you start a background +job, then change the shell's working directory and then cause the +background job to run in the foreground, the shell warns you that the +working directory of the currently running foreground job is different +from that of the shell. +.DS +% dirs \-l +/mnt/bill +% cd myproject +% dirs +~/myproject +% ed prog.c +1143 +^Z +Stopped +% cd .. +% ls +myproject +textfile +% fg +ed prog.c (wd: ~/myproject) +.DE +This way the shell warns you when there +is an implied change of working directory, even though no cd command was +issued. In the above example the `ed' job was still in `/mnt/bill/project' +even though the shell had changed to `/mnt/bill'. +A similar warning is given when such a foreground job +terminates or is suspended (using the \s-2STOP\s0 signal) since +the return to the shell again implies a change of working directory. +.DS +% fg +ed prog.c (wd: ~/myproject) + . . . after some editing +q +(wd now: ~) +% +.DE +These messages are sometimes confusing if you use programs that change +their own working directories, since the shell only remembers which +directory a job is started in, and assumes it stays there. +The `\-l' option of +.I jobs +will type the working directory +of suspended or background jobs when it is different +from the current working directory of the shell. +.NH 2 +Useful built-in commands +.PP +We now give a few of the useful built-in commands of the shell describing +how they are used. +.PP +The +.I alias +command described above is used to assign new aliases and to show the +existing aliases. +With no arguments it prints the current aliases. +It may also be given only one argument such as +.DS +alias ls +.DE +to show the current alias for, e.g., `ls'. +.PP +The +.I echo +command prints its arguments. +It is often used in +.I "shell scripts" +or as an interactive command +to see what filename expansions will produce. +.PP +The +.I history +command will show the contents of the history list. +The numbers given with the history events can be used to reference +previous events which are difficult to reference using the +contextual mechanisms introduced above. +There is also a shell variable called +.I prompt. +By placing a `!' character in its value the shell will there substitute +the number of the current command in the history list. +You can use this number to refer to this command in a history substitution. +Thus you could +.DS +set prompt=\'\e! % \' +.DE +Note that the `!' character had to be +.I escaped +here even within `\'' characters. +.PP +The +.I limit +command is used to restrict use of resources. +With no arguments it prints the current limitations: +.DS +.ta 1i +cputime unlimited +filesize unlimited +datasize 5616 kbytes +stacksize 512 kbytes +coredumpsize unlimited +.so tabs +.DE +Limits can be set, e.g.: +.DS +limit coredumpsize 128k +.DE +Most reasonable units abbreviations will work; see the +.I csh +manual page for more details. +.PP +The +.I logout +command can be used to terminate a login shell which has +.I ignoreeof +set. +.PP +The +.I rehash +command causes the shell to recompute a table of where commands are +located. This is necessary if you add a command to a directory +in the current shell's search path and wish the shell to find it, +since otherwise the hashing algorithm may tell the shell that the +command wasn't in that directory when the hash table was computed. +.PP +The +.I repeat +command can be used to repeat a command several times. +Thus to make 5 copies of the file +.I one +in the file +.I five +you could do +.DS +repeat 5 cat one >> five +.DE +.PP +The +.I setenv +command can be used +to set variables in the environment. +Thus +.DS +setenv TERM adm3a +.DE +will set the value of the environment variable \s-2TERM\s0 +to +`adm3a'. +A user program +.I printenv +exists which will print out the environment. +It might then show: +.DS +% printenv +HOME=/usr/bill +SHELL=/bin/csh +PATH=:/usr/ucb:/bin:/usr/bin:/usr/local +TERM=adm3a +USER=bill +% +.DE +.PP +The +.I source +command can be used to force the current shell to read commands from +a file. +Thus +.DS +source .cshrc +.DE +can be used after editing in a change to the +.I \&.cshrc +file which you wish to take effect right away. +.PP +The +.I time +command can be used to cause a command to be timed no matter how much +\s-2CPU\s0 time it takes. +Thus +.DS +% time cp /etc/rc /usr/bill/rc +0.0u 0.1s 0:01 8% 2+1k 3+2io 1pf+0w +% time wc /etc/rc /usr/bill/rc + 52 178 1347 /etc/rc + 52 178 1347 /usr/bill/rc + 104 356 2694 total +0.1u 0.1s 0:00 13% 3+3k 5+3io 7pf+0w +% +.DE +indicates that the +.I cp +command used a negligible amount of user time (u) +and about 1/10th of a system time (s); the elapsed time was 1 second (0:01), +there was an average memory usage of 2k bytes of program space and 1k +bytes of data space over the cpu time involved (2+1k); the program +did three disk reads and two disk writes (3+2io), and took one page fault +and was not swapped (1pf+0w). +The word count command +.I wc +on the other hand used 0.1 seconds of user time and 0.1 seconds of system +time in less than a second of elapsed time. +The percentage `13%' indicates that over the period when it was active +the command `wc' used an average of 13 percent of the available \s-2CPU\s0 +cycles of the machine. +.PP +The +.I unalias +and +.I unset +commands can be used +to remove aliases and variable definitions from the shell, and +.I unsetenv +removes variables from the environment. +.NH 2 +What else? +.PP +This concludes the basic discussion of the shell for terminal users. +There are more features of the shell to be discussed here, and all +features of the shell are discussed in its manual pages. +One useful feature which is discussed later is the +.I foreach +built-in command which can be used to run the same command +sequence with a number of different arguments. +.PP +If you intend to use \s-2UNIX\s0 a lot you you should look through +the rest of this document and the csh manual pages (section1) to become familiar +with the other facilities which are available to you. +.bp diff --git a/bin/csh/USD.doc/csh.3 b/bin/csh/USD.doc/csh.3 new file mode 100644 index 000000000000..cf8af73b0cc4 --- /dev/null +++ b/bin/csh/USD.doc/csh.3 @@ -0,0 +1,650 @@ +.\" Copyright (c) 1980, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" 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. +.\" +.\" @(#)csh.3 8.1 (Berkeley) 6/8/93 +.\" +.nr H1 2 +.NH +Shell control structures and command scripts +.NH 2 +Introduction +.PP +It is possible to place commands in files and to cause shells to be +invoked to read and execute commands from these files, +which are called +.I "shell scripts." +We here detail those features of the shell useful to the writers of such +scripts. +.NH 2 +Make +.PP +It is important to first note what shell scripts are +.I not +useful for. +There is a program called +.I make +which is very useful for maintaining a group of related files +or performing sets of operations on related files. +For instance a large program consisting of one or more files +can have its dependencies described in a +.I makefile +which contains definitions of the commands used to create these +different files when changes occur. +Definitions of the means for printing listings, cleaning up the directory +in which the files reside, and installing the resultant programs +are easily, and most appropriately placed in this +.I makefile. +This format is superior and preferable to maintaining a group of shell +procedures to maintain these files. +.PP +Similarly when working on a document a +.I makefile +may be created which defines how different versions of the document +are to be created and which options of +.I nroff +or +.I troff +are appropriate. +.NH 2 +Invocation and the argv variable +.PP +A +.I csh +command script may be interpreted by saying +.DS +% csh script ... +.DE +where +.I script +is the name of the file containing a group of +.I csh +commands and +`\&...' is replaced by a sequence of arguments. +The shell places these arguments in the variable +.I argv +and then begins to read commands from the script. +These parameters are then available through the same mechanisms +which are used to reference any other shell variables. +.PP +If you make the file +`script' +executable by doing +.DS +chmod 755 script +.DE +and place a shell comment at the beginning of the shell script +(i.e. begin the file with a `#' character) +then a `/bin/csh' will automatically be invoked to execute `script' when +you type +.DS +script +.DE +If the file does not begin with a `#' then the standard shell +`/bin/sh' will be used to execute it. +This allows you to convert your older shell scripts to use +.I csh +at your convenience. +.NH 2 +Variable substitution +.PP +After each input line is broken into words and history substitutions +are done on it, the input line is parsed into distinct commands. +Before each command is executed a mechanism know as +.I "variable substitution" +is done on these words. +Keyed by the character `$' this substitution replaces the names +of variables by their values. +Thus +.DS +echo $argv +.DE +when placed in a command script would cause the current value of the +variable +.I argv +to be echoed to the output of the shell script. +It is an error for +.I argv +to be unset at this point. +.PP +A number of notations are provided for accessing components and attributes +of variables. +The notation +.DS +$?name +.DE +expands to `1' if name is +.I set +or to `0' +if name is not +.I set. +It is the fundamental mechanism used for checking whether particular +variables have been assigned values. +All other forms of reference to undefined variables cause errors. +.PP +The notation +.DS +$#name +.DE +expands to the number of elements in the variable +.I name. +Thus +.DS +% set argv=(a b c) +% echo $?argv +1 +% echo $#argv +3 +% unset argv +% echo $?argv +0 +% echo $argv +Undefined variable: argv. +% +.DE +.PP +It is also possible to access the components of a variable +which has several values. +Thus +.DS +$argv[1] +.DE +gives the first component of +.I argv +or in the example above `a'. +Similarly +.DS +$argv[$#argv] +.DE +would give `c', +and +.DS +$argv[1\-2] +.DE +would give `a b'. Other notations useful in shell scripts are +.DS +$\fIn\fR +.DE +where +.I n +is an integer as a shorthand for +.DS +$argv[\fIn\fR\|] +.DE +the +.I n\|th +parameter and +.DS +$* +.DE +which is a shorthand for +.DS +$argv +.DE +The form +.DS +$$ +.DE +expands to the process number of the current shell. +Since this process number is unique in the system it can +be used in generation of unique temporary file names. +The form +.DS +$< +.DE +is quite special and is replaced by the next line of input read from +the shell's standard input (not the script it is reading). This is +useful for writing shell scripts that are interactive, reading +commands from the terminal, or even writing a shell script that +acts as a filter, reading lines from its input file. Thus the sequence +.DS +echo 'yes or no?\ec' +set a=($<) +.DE +would write out the prompt `yes or no?' without a newline and then +read the answer into the variable `a'. In this case `$#a' would be +`0' if either a blank line or end-of-file (^D) was typed. +.PP +One minor difference between `$\fIn\fR\|' and `$argv[\fIn\fR\|]' +should be noted here. +The form +`$argv[\fIn\fR\|]' +will yield an error if +.I n +is not in the range +`1\-$#argv' +while `$n' +will never yield an out of range subscript error. +This is for compatibility with the way older shells handled parameters. +.PP +Another important point is that it is never an error to give a subrange +of the form `n\-'; if there are less than +.I n +components of the given variable then no words are substituted. +A range of the form `m\-n' likewise returns an empty vector without giving +an error when \fIm\fR exceeds the number of elements of the given variable, +provided the subscript \fIn\fR is in range. +.NH 2 +Expressions +.PP +In order for interesting shell scripts to be constructed it +must be possible to evaluate expressions in the shell based on the +values of variables. +In fact, all the arithmetic operations of the language C are available +in the shell +with the same precedence that they have in C. +In particular, the operations `==' and `!=' compare strings +and the operators `&&' and `|\|\||' implement the boolean and/or operations. +The special operators `=~' and `!~' are similar to `==' and `!=' except +that the string on the right side can have pattern matching characters +(like *, ? or []) and the test is whether the string on the left matches +the pattern on the right. +.PP +The shell also allows file enquiries of the form +.DS +\-? filename +.DE +where `?' is replace by a number of single characters. +For instance the expression primitive +.DS +\-e filename +.DE +tell whether the file +`filename' +exists. +Other primitives test for read, write and execute access to the file, +whether it is a directory, or has non-zero length. +.PP +It is possible to test whether a command terminates normally, +by a primitive of the +form `{ command }' which returns true, i.e. `1' if the command +succeeds exiting normally with exit status 0, or `0' if the command +terminates abnormally or with exit status non-zero. +If more detailed information about the execution status of a command +is required, it can be executed and the variable `$status' examined +in the next command. +Since `$status' is set by every command, it is very transient. +It can be saved if it is inconvenient to use it only in the single +immediately following command. +.PP +For a full list of expression components available see the manual +section for the shell. +.NH 2 +Sample shell script +.PP +A sample shell script which makes use of the expression mechanism +of the shell and some of its control structure follows: +.DS +% cat copyc +# +# Copyc copies those C programs in the specified list +# to the directory ~/backup if they differ from the files +# already in ~/backup +# +set noglob +foreach i ($argv) + + if ($i !~ *.c) continue # not a .c file so do nothing + + if (! \-r ~/backup/$i:t) then + echo $i:t not in backup... not cp\e\'ed + continue + endif + + cmp \-s $i ~/backup/$i:t # to set $status + + if ($status != 0) then + echo new backup of $i + cp $i ~/backup/$i:t + endif +end +.DE +.PP +This script makes use of the +.I foreach +command, which causes the shell to execute the commands between the +.I foreach +and the matching +.I end +for each of the values given between `(' and `)' with the named +variable, in this case `i' set to successive values in the list. +Within this loop we may use the command +.I break +to stop executing the loop +and +.I continue +to prematurely terminate one iteration +and begin the next. +After the +.I foreach +loop the iteration variable +(\fIi\fR in this case) +has the value at the last iteration. +.PP +We set the variable +.I noglob +here to prevent filename expansion of the members of +.I argv. +This is a good idea, in general, if the arguments to a shell script +are filenames which have already been expanded or if the arguments +may contain filename expansion metacharacters. +It is also possible to quote each use of a `$' variable expansion, +but this is harder and less reliable. +.PP +The other control construct used here is a statement of the form +.DS +\fBif\fR ( expression ) \fBthen\fR + command + ... +\fBendif\fR +.DE +The placement of the keywords here is +.B not +flexible due to the current implementation of the shell.\(dg +.FS +\(dgThe following two formats are not currently acceptable to the shell: +.sp +.in +5 +.nf +\fBif\fR ( expression ) # \fBWon't work!\fR +\fBthen\fR + command + ... +\fBendif\fR +.fi +.in -5 +.sp +and +.sp +.in +5 +.nf +\fBif\fR ( expression ) \fBthen\fR command \fBendif\fR # \fBWon't work\fR +.in -5 +.fi +.FE +.PP +The shell does have another form of the if statement of the form +.DS +\fBif\fR ( expression ) \fBcommand\fR +.DE +which can be written +.DS +\fBif\fR ( expression ) \e + command +.DE +Here we have escaped the newline for the sake of appearance. +The command must not involve `\||\|', `&' or `;' +and must not be another control command. +The second form requires the final `\e' to +.B immediately +precede the end-of-line. +.PP +The more general +.I if +statements above also admit a sequence of +.I else\-if +pairs followed by a single +.I else +and an +.I endif, +e.g.: +.DS +\fBif\fR ( expression ) \fBthen\fR + commands +\fBelse\fR \fBif\fR (expression ) \fBthen\fR + commands +\&... + +\fBelse\fR + commands +\fBendif\fR +.DE +.PP +Another important mechanism used in shell scripts is the `:' modifier. +We can use the modifier `:r' here to extract a root of a filename or +`:e' to extract the +.I extension. +Thus if the variable +.I i +has the value +`/mnt/foo.bar' +then +.sp +.in +5 +.nf +% echo $i $i:r $i:e +/mnt/foo.bar /mnt/foo bar +% +.sp +.in -5 +.fi +shows how the `:r' modifier strips off the trailing `.bar' and the +the `:e' modifier leaves only the `bar'. +Other modifiers will take off the last component of a pathname leaving +the head `:h' or all but the last component of a pathname leaving the +tail `:t'. +These modifiers are fully described in the +.I csh +manual pages in the User's Reference Manual. +It is also possible to use the +.I "command substitution" +mechanism described in the next major section to perform modifications +on strings to then reenter the shell's environment. +Since each usage of this mechanism involves the creation of a new process, +it is much more expensive to use than the `:' modification mechanism.\(dd +.FS +\(dd It is also important to note that +the current implementation of the shell limits the number of `:' modifiers +on a `$' substitution to 1. +Thus +.sp +.nf +.in +5 +% echo $i $i:h:t +/a/b/c /a/b:t +% +.in -5 +.fi +.sp +does not do what one would expect. +.FE +Finally, we note that the character `#' lexically introduces a shell +comment in shell scripts (but not from the terminal). +All subsequent characters on the input line after a `#' are discarded +by the shell. +This character can be quoted using `\'' or `\e' to place it in +an argument word. +.NH 2 +Other control structures +.PP +The shell also has control structures +.I while +and +.I switch +similar to those of C. +These take the forms +.DS +\fBwhile\fR ( expression ) + commands +\fBend\fR +.DE +and +.DS +\fBswitch\fR ( word ) + +\fBcase\fR str1: + commands + \fBbreaksw\fR + +\& ... + +\fBcase\fR strn: + commands + \fBbreaksw\fR + +\fBdefault:\fR + commands + \fBbreaksw\fR + +\fBendsw\fR +.DE +For details see the manual section for +.I csh. +C programmers should note that we use +.I breaksw +to exit from a +.I switch +while +.I break +exits a +.I while +or +.I foreach +loop. +A common mistake to make in +.I csh +scripts is to use +.I break +rather than +.I breaksw +in switches. +.PP +Finally, +.I csh +allows a +.I goto +statement, with labels looking like they do in C, i.e.: +.DS +loop: + commands + \fBgoto\fR loop +.DE +.NH 2 +Supplying input to commands +.PP +Commands run from shell scripts receive by default the standard +input of the shell which is running the script. +This is different from previous shells running +under \s-2UNIX\s0. It allows shell scripts to fully participate +in pipelines, but mandates extra notation for commands which are to take +inline data. +.PP +Thus we need a metanotation for supplying inline data to commands in +shell scripts. +As an example, consider this script which runs the editor to +delete leading blanks from the lines in each argument file: +.DS +% cat deblank +# deblank \-\- remove leading blanks +foreach i ($argv) +ed \- $i << \'EOF\' +1,$s/^[ ]*// +w +q +\&\'EOF\' +end +% +.DE +The notation `<< \'EOF\'' +means that the standard input for the +.I ed +command is to come from the text in the shell script file +up to the next line consisting of exactly `\'EOF\''. +The fact that the `EOF' is enclosed in `\'' characters, i.e. quoted, +causes the shell to not perform variable substitution on the +intervening lines. +In general, if any part of the word following the `<<' which the +shell uses to terminate the text to be given to the command is quoted +then these substitutions will not be performed. +In this case since we used the form `1,$' in our editor script +we needed to insure that this `$' was not variable substituted. +We could also have insured this by preceding the `$' here with a `\e', +i.e.: +.DS +1,\e$s/^[ ]*// +.DE +but quoting the `EOF' terminator is a more reliable way of achieving the +same thing. +.NH 2 +Catching interrupts +.PP +If our shell script creates temporary files, we may wish to catch +interruptions of the shell script so that we can clean up +these files. +We can then do +.DS +onintr label +.DE +where +.I label +is a label in our program. +If an interrupt is received the shell will do a +`goto label' +and we can remove the temporary files and then do an +.I exit +command (which is built in to the shell) +to exit from the shell script. +If we wish to exit with a non-zero status we can do +.DS +exit(1) +.DE +e.g. to exit with status `1'. +.NH 2 +What else? +.PP +There are other features of the shell useful to writers of shell +procedures. +The +.I verbose +and +.I echo +options and the related +.I \-v +and +.I \-x +command line options can be used to help trace the actions of the shell. +The +.I \-n +option causes the shell only to read commands and not to execute +them and may sometimes be of use. +.PP +One other thing to note is that +.I csh +will not execute shell scripts which do not begin with the +character `#', that is shell scripts that do not begin with a comment. +Similarly, the `/bin/sh' on your system may well defer to `csh' +to interpret shell scripts which begin with `#'. +This allows shell scripts for both shells to live in harmony. +.PP +There is also another quotation mechanism using `"' which allows +only some of the expansion mechanisms we have so far discussed to occur +on the quoted string and serves to make this string into a single word +as `\'' does. +.bp diff --git a/bin/csh/USD.doc/csh.4 b/bin/csh/USD.doc/csh.4 new file mode 100644 index 000000000000..ee862df07b93 --- /dev/null +++ b/bin/csh/USD.doc/csh.4 @@ -0,0 +1,178 @@ +.\" Copyright (c) 1980, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" 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. +.\" +.\" @(#)csh.4 8.1 (Berkeley) 6/8/93 +.\" +.nr H1 3 +.NH +Other, less commonly used, shell features +.NH 2 +Loops at the terminal; variables as vectors +.PP +It is occasionally useful to use the +.I foreach +control structure at the terminal to aid in performing a number +of similar commands. +For instance, there were at one point three shells in use on the Cory \s-2UNIX\s0 +system at Cory Hall, +`/bin/sh', +`/bin/nsh', +and +`/bin/csh'. +To count the number of persons using each shell one could have issued +the commands +.DS +% grep \-c csh$ /etc/passwd +27 +% grep \-c nsh$ /etc/passwd +128 +% grep \-c \-v sh$ /etc/passwd +430 +% +.DE +Since these commands are very similar we can use +.I foreach +to do this more easily. +.DS +% foreach i (\'sh$\' \'csh$\' \'\-v sh$\') +? grep \-c $i /etc/passwd +? end +27 +128 +430 +% +.DE +Note here that the shell prompts for +input with `? ' when reading the body of the loop. +.PP +Very useful with loops are variables which contain lists of filenames +or other words. +You can, for example, do +.DS +% set a=(\`ls\`) +% echo $a +csh.n csh.rm +% ls +csh.n +csh.rm +% echo $#a +2 +% +.DE +The +.I set +command here gave the variable +.I a +a list of all the filenames in the current directory as value. +We can then iterate over these names to perform any chosen function. +.PP +The output of a command within `\`' characters is converted by +the shell to a list of words. +You can also place the `\`' quoted string within `"' characters +to take each (non-empty) line as a component of the variable; +preventing the lines from being split into words at blanks and tabs. +A modifier `:x' exists which can be used later to expand each component +of the variable into another variable splitting it into separate words +at embedded blanks and tabs. +.NH 2 +Braces { ... } in argument expansion +.PP +Another form of filename expansion, alluded +to before involves the characters `{' and `}'. +These characters specify that the contained strings, separated by `,' +are to be consecutively substituted into the containing characters +and the results expanded left to right. +Thus +.DS +A{str1,str2,...strn}B +.DE +expands to +.DS +Astr1B Astr2B ... AstrnB +.DE +This expansion occurs before the other filename expansions, and may +be applied recursively (i.e. nested). +The results of each expanded string are sorted separately, left +to right order being preserved. +The resulting filenames are not required to exist if no other expansion +mechanisms are used. +This means that this mechanism can be used to generate arguments which are +not filenames, but which have common parts. +.PP +A typical use of this would be +.DS +mkdir ~/{hdrs,retrofit,csh} +.DE +to make subdirectories `hdrs', `retrofit' and `csh' +in your home directory. +This mechanism is most useful when the common prefix is longer +than in this example, i.e. +.DS +chown root /usr/{ucb/{ex,edit},lib/{ex?.?*,how_ex}} +.DE +.NH 2 +Command substitution +.PP +A command enclosed in `\`' characters is replaced, just before +filenames are expanded, by the output from that command. +Thus it is possible to do +.DS +set pwd=\`pwd\` +.DE +to save the current directory in the variable +.I pwd +or to do +.DS +ex \`grep \-l TRACE *.c\` +.DE +to run the editor +.I ex +supplying as arguments those files whose names end in `.c' +which have the string `TRACE' in them.* +.FS +*Command expansion also occurs in input redirected with `<<' +and within `"' quotations. +Refer to the shell manual section for full details. +.FE +.NH 2 +Other details not covered here +.PP +In particular circumstances it may be necessary to know the exact +nature and order of different substitutions performed by the shell. +The exact meaning of certain combinations of quotations is also +occasionally important. +These are detailed fully in its manual section. +.PP +The shell has a number of command line option flags mostly of use +in writing \s-2UNIX\s0 programs, +and debugging shell scripts. +See the csh(1) manual section for a list of these options. +.bp diff --git a/bin/csh/USD.doc/csh.a b/bin/csh/USD.doc/csh.a new file mode 100644 index 000000000000..1026faf990b2 --- /dev/null +++ b/bin/csh/USD.doc/csh.a @@ -0,0 +1,95 @@ +.\" Copyright (c) 1980, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" 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. +.\" +.\" @(#)csh.a 8.1 (Berkeley) 6/8/93 +.\" +.SH +Appendix \- Special characters +.LP +The following table lists the special characters of +.I csh +and the \s-2UNIX\s0 system, giving for each the section(s) in which it +is discussed. +A number of these characters also have special meaning in expressions. +See the +.I csh +manual section +for a complete list. +.ta .75i 1.5i 2.25i +.LP +Syntactic metacharacters +.DS +; 2.4 separates commands to be executed sequentially +| 1.5 separates commands in a pipeline +( ) 2.2,3.6 brackets expressions and variable values +& 2.5 follows commands to be executed without waiting for completion +.DE +.LP +Filename metacharacters +.DS +/ 1.6 separates components of a file's pathname +\. 1.6 separates root parts of a file name from extensions +? 1.6 expansion character matching any single character +* 1.6 expansion character matching any sequence of characters +[ ] 1.6 expansion sequence matching any single character from a set +~ 1.6 used at the beginning of a filename to indicate home directories +{ } 4.2 used to specify groups of arguments with common parts +.DE +.LP +Quotation metacharacters +.DS +\e 1.7 prevents meta-meaning of following single character +\' 1.7 prevents meta-meaning of a group of characters +" 4.3 like \', but allows variable and command expansion +.DE +.LP +Input/output metacharacters +.DS +< 1.5 indicates redirected input +> 1.3 indicates redirected output +.DE +.LP +Expansion/substitution metacharacters +.DS +$ 3.4 indicates variable substitution +! 2.3 indicates history substitution +: 3.6 precedes substitution modifiers +^ 2.3 used in special forms of history substitution +\` 4.3 indicates command substitution +.DE +.LP +Other metacharacters +.DS +# 1.3,3.6 begins scratch file names; indicates shell comments +\- 1.2 prefixes option (flag) arguments to commands +% 2.6 prefixes job name specifications +.DE +.bp diff --git a/bin/csh/USD.doc/csh.g b/bin/csh/USD.doc/csh.g new file mode 100644 index 000000000000..0641769047a0 --- /dev/null +++ b/bin/csh/USD.doc/csh.g @@ -0,0 +1,1721 @@ +.\" Copyright (c) 1980, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" 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. +.\" +.\" @(#)csh.g 8.1 (Berkeley) 6/8/93 +.\" +.SH +Glossary +.PP +This glossary lists the most important terms introduced in the +introduction to the +shell and gives references to sections of the shell +document for further information about them. +References of the form +`pr (1)' +indicate that the command +.I pr +is in the \s-2UNIX\s0 User Reference manual in section 1. +You can look at an online copy of its manual page by doing +.DS +man 1 pr +.DE +References of the form (2.5) +indicate that more information can be found in section 2.5 of this +manual. +.IP \&\fB.\fR 15n +Your current directory has the name `.' as well as the name printed +by the command +.I pwd; +see also +.I dirs. +The current directory `.' is usually the first +.I component +of the search path contained in the variable +.I path , +thus commands which are in `.' are found first (2.2). +The character `.' is also used in separating +.I components +of filenames +(1.6). +The character `.' at the beginning of a +.I component +of a +.I pathname +is treated specially and not matched by the +.I "filename expansion" +metacharacters `?', `*', and `[' `]' pairs (1.6). +.IP \&\fB..\fR +Each directory has a file `..' in it which is a reference to its +parent directory. +After changing into the directory with +.I chdir , +i.e. +.DS +chdir paper +.DE +you can return to the parent directory by doing +.DS +chdir .. +.DE +The current directory is printed by +.I pwd +(2.7). +.IP a.out +Compilers which create executable images create them, by default, in the +file +.I a.out. +for historical reasons (2.3). +.IP "absolute pathname" +.br +A +.I pathname +which begins with a `/' is +.I absolute +since it specifies the +.I path +of directories from the beginning +of the entire directory system \- called the +.I root +directory. +.I Pathname s +which are not +.I absolute +are called +.I relative +(see definition of +.I "relative pathname" ) +(1.6). +.IP alias +An +.I alias +specifies a shorter or different name for a \s-2UNIX\s0 +command, or a transformation on a command to be performed in +the shell. +The shell has a command +.I alias +which establishes +.I aliases +and can print their current values. +The command +.I unalias +is used to remove +.I aliases +(2.4). +.IP argument +Commands in \s-2UNIX\s0 receive a list of +.I argument +words. +Thus the command +.DS +echo a b c +.DE +consists of the +.I "command name" +`echo' and three +.I argument +words `a', `b' and `c'. +The set of +.I arguments +after the +.I "command name" +is said to be the +.I "argument list" +of the command (1.1). +.IP argv +The list of arguments to a command written in the shell language +(a shell script or shell procedure) is stored in a variable called +.I argv +within the shell. +This name is taken from the conventional name in the +C programming language (3.4). +.IP background +Commands started without waiting for them to complete are called +.I background +commands (2.6). +.IP base +A filename is sometimes thought of as consisting of a +.I base +part, before any `.' character, and an +.I extension +\- the part after +the `.'. See +.I filename +and +.I extension +(1.6) and basename (1). +.IP bg +The +.I bg +command causes a +.I suspended +job to continue execution in the +.I background +(2.6). +.IP bin +A directory containing binaries of programs and shell scripts to be +executed is typically called a +.I bin +directory. +The standard system +.I bin +directories are `/bin' containing the most +heavily used commands and `/usr/bin' which contains most other user +programs. +Programs developed at UC Berkeley live in `/usr/ucb', while locally +written programs live in `/usr/local'. Games are kept in the directory +`/usr/games'. +You can place binaries in any directory. +If you wish to execute them often, the name of the directories +should be a +.I component +of the variable +.I path . +.IP break +.I Break +is a builtin command used to exit from loops within the control +structure of the shell (3.7). +.IP breaksw +The +.I breaksw +builtin command is used to exit from a +.I switch +control structure, like a +.I break +exits from loops (3.7). +.IP builtin +A command executed directly by the shell is called a +.I builtin +command. +Most commands in \s-2UNIX\s0 are not built into the shell, +but rather exist as files in +.I bin +directories. +These commands are accessible because the directories in which +they reside are named in the +.I path +variable. +.IP case +A +.I case +command is used as a label in a +.I switch +statement in the shell's control structure, similar to that of the +language C. +Details are given in the shell documentation `csh (1)' (3.7). +.IP cat +The +.I cat +program catenates a list of specified files on the +.I "standard output" . +It is usually used to look at the contents of a single file on the terminal, +to `cat a file' (1.8, 2.3). +.IP cd +The +.I cd +command is used to change the +.I "working directory" . +With no arguments, +.I cd +changes your +.I "working directory" +to be your +.I home +directory (2.4, 2.7). +.IP chdir +The +.I chdir +command is a synonym for +.I cd . +.I Cd +is usually used because it is easier to type. +.IP chsh +The +.I chsh +command is used to change the shell which you use on \s-2UNIX\s0. +By default, you use an different version of the shell +which resides in `/bin/sh'. +You can change your shell to `/bin/csh' by doing +.DS +chsh your-login-name /bin/csh +.DE +Thus I would do +.DS +chsh bill /bin/csh +.DE +It is only necessary to do this once. +The next time you log in to \s-2UNIX\s0 after doing this command, +you will be using +.I csh +rather than the shell in `/bin/sh' (1.9). +.IP cmp +.I Cmp +is a program which compares files. +It is usually used on binary files, or to see if two files are identical (3.6). +For comparing text files the program +.I diff , +described in `diff (1)' is used. +.IP command +A function performed by the system, either by the shell +(a builtin +.I command ) +or by a program residing in a file in +a directory within the \s-2UNIX\s0 system, is called a +.I command +(1.1). +.IP "command name" +.br +When a command is issued, it consists of a +.I "command name" , +which is the first word of the command, +followed by arguments. +The convention on \s-2UNIX\s0 is that the first word of a +command names the function to be performed (1.1). +.IP "command substitution" +.br +The replacement of a command enclosed in `\`' characters +by the text output by that command +is called +.I "command substitution" +(4.3). +.IP component +A part of a +.I pathname +between `/' characters is called a +.I component +of that +.I pathname . +A variable +which has multiple strings as value is said to have +several +.I component s; +each string is a +.I component +of the variable. +.IP continue +A builtin command which causes execution of the enclosing +.I foreach +or +.I while +loop to cycle prematurely. +Similar to the +.I continue +command in the programming language C (3.6). +.IP control- +Certain special characters, called +.I control +characters, are produced by holding down the \s-2CONTROL\s0 key +on your terminal and simultaneously pressing another character, much like +the \s-2SHIFT\s0 key is used to produce upper case characters. Thus +.I control- c +is produced by holding down the \s-2CONTROL\s0 key while pressing the +`c' key. Usually \s-2UNIX\s0 prints an caret (^) followed by the +corresponding letter when you type a +.I control +character (e.g. `^C' for +.I control- c +(1.8). +.IP "core\ dump" +When a program terminates abnormally, the system places an image +of its current state in a file named `core'. +This +.I "core dump" +can be examined with the system debugger `adb (1)' +or `sdb (1)' in order to determine what went wrong with the program (1.8). +If the shell produces a message of the form +.DS +Illegal instruction (core dumped) +.DE +(where `Illegal instruction' is only one of several possible +messages), you should report this to the author of the program +or a system administrator, +saving the `core' file. +.IP cp +The +.I cp +(copy) program is used to copy the contents of one file into another +file. +It is one of the most commonly used \s-2UNIX\s0 commands (1.6). +.IP csh +The name of the shell +program that this document describes. +.IP \&.cshrc +The file +.I \&.cshrc +in your +.I home +directory is read by each shell as it begins execution. +It is usually used to change the setting of the variable +.I path +and to set +.I alias +parameters which are to take effect globally (2.1). +.IP cwd +The +.I cwd +variable in the shell holds the +.I "absolute pathname" +of the current +.I "working directory" \&. +It is changed by the shell whenever your current +.I "working directory" +changes and should not be changed otherwise (2.2). +.IP date +The +.I date +command prints the current date and time (1.3). +.IP debugging +.I Debugging +is the process of correcting mistakes in programs and shell scripts. +The shell has several options and variables which may be used +to aid in shell +.I debugging +(4.4). +.IP default: +The label +.I default: +is used within shell +.I switch +statements, as it is in the C language +to label the code to be executed if none of the +.I case +labels matches the value switched on (3.7). +.IP \s-2DELETE\s0 +The +\s-2DELETE\s0 +or +\s-2RUBOUT\s0 +key on the terminal normally causes an interrupt to be sent to the current job. +Many users change the interrupt character to be ^C. +.IP detached +A command that continues running in the +.I background +after you logout is said to be +.I detached . +.IP diagnostic +An error message produced by a program is often referred to as a +.I diagnostic . +Most error messages are not written to the +.I "standard output" , +since that is often directed away from the terminal (1.3, 1.5). +Error messsages are instead written to the +.I "diagnostic output" +which may be directed away from the terminal, but usually is not. +Thus +.I diagnostics +will usually appear on the terminal (2.5). +.IP directory +A structure which contains files. +At any time you are in one particular +.I directory +whose names can be printed by the command +.I pwd . +The +.I chdir +command will change you to another +.I directory , +and make the files +in that +.I directory +visible. The +.I directory +in which you are when you first login is your +.I home +directory (1.1, 2.7). +.IP "directory\ stack" +The shell saves the names of previous +.I "working directories" +in the +.I "directory stack" +when you change your current +.I "working directory" +via the +.I pushd +command. The +.I "directory stack" +can be printed by using the +.I dirs +command, which includes your current +.I "working directory" +as the first directory name on the left (2.7). +.IP dirs +The +.I dirs +command prints the shell's +.I "directory stack" +(2.7). +.IP du +The +.I du +command is a program (described in `du (1)') which +prints the number of disk blocks is all directories below +and including your current +.I "working directory" +(2.6). +.IP echo +The +.I echo +command prints its arguments (1.6, 3.6). +.IP else +The +.I else +command is part of the `if-then-else-endif' control +command construct (3.6). +.IP endif +If an +.I if +statement is ended with the word +.I then , +all lines following the +.I if +up to a line starting with the word +.I endif +or +.I else +are executed if the condition between parentheses after the +.I if +is true (3.6). +.IP \s-2EOF\s0 +An +.I "end\f1-\fPof\f1-\fPfile" +is generated by the terminal by a control-d, +and whenever a command reads to the end of a file which +it has been given as input. +Commands receiving input from a +.I pipe +receive an +.I "end\f1-\fPof\f1-\fPfile" +when the command sending them input completes. +Most commands terminate when they receive an +.I "end\f1-\fPof\f1-\fPfile" . +The shell has an option to ignore +.I "end\f1-\fPof\f1-\fPfile" +from a terminal +input which may help you keep from logging out accidentally +by typing too many control-d's (1.1, 1.8, 3.8). +.IP escape +A character `\e' used to prevent the special meaning of a metacharacter +is said to +.I escape +the character from its special meaning. +Thus +.DS +echo \e* +.DE +will echo the character `*' while just +.DS +echo * +.DE +will echo the names of the file in the current directory. +In this example, \e +.I escape s +`*' (1.7). +There is also a non-printing character called +.I escape , +usually labelled +\s-2ESC\s0 +or +\s-2ALTMODE\s0 +on terminal keyboards. +Some older \s-2UNIX\s0 systems use this character to indicate that +output is to be +.I suspended . +Most systems use control-s to stop the output and control-q to start it. +.IP /etc/passwd +This file contains information about the accounts currently on the +system. +It consists of a line for each account with fields separated by +`:' characters (1.8). +You can look at this file by saying +.DS +cat /etc/passwd +.DE +The commands +.I finger +and +.I grep +are often used to search for information in this file. +See `finger (1)', `passwd(5)', and `grep (1)' for more details. +.IP exit +The +.I exit +command is used to force termination of a shell script, +and is built into the shell (3.9). +.IP "exit\ status" +A command which discovers a problem may reflect this back to the command +(such as a shell) which invoked (executed) it. +It does this by returning a non-zero number as its +.I "exit status" , +a status of zero being considered +`normal termination'. +The +.I exit +command can be used to force a shell command script to give a non-zero +.I "exit status" +(3.6). +.IP expansion +The replacement of strings in the shell input which contain metacharacters +by other strings is referred to as the process of +.I expansion . +Thus the replacement of the word `*' by a sorted list of files +in the current directory is a `filename expansion'. +Similarly the replacement of the characters `!!' by the text of +the last command is a `history expansion'. +.I Expansions +are also referred to as +.I substitutions +(1.6, 3.4, 4.2). +.IP expressions +.I Expressions +are used in the shell +to control the conditional structures used in the writing of shell +scripts and in calculating values for these scripts. +The operators available in shell +.I expressions +are those of the language +C (3.5). +.IP extension +Filenames often consist of a +.I base +name and an +.I extension +separated by the character `.'. +By convention, groups of related files often share the same +.I root +name. +Thus if `prog.c' were a C program, then the object file for this +program would be stored in `prog.o'. +Similarly a paper written with the +`\-me' +nroff macro package might be stored in +`paper.me' +while a formatted version of this paper might be kept in +`paper.out' and a list of spelling errors in +`paper.errs' (1.6). +.IP fg +The +.I "job control" +command +.I fg +is used to run a +.I background +or +.I suspended +job in the +.I foreground +(1.8, 2.6). +.IP filename +Each file in \s-2UNIX\s0 has a name consisting of up to 14 characters +and not including the character `/' which is used in +.I pathname +building. Most +.I filenames +do not begin with the character `.', and contain +only letters and digits with perhaps a `.' separating the +.I base +portion of the +.I filename +from an +.I extension +(1.6). +.IP "filename expansion" +.br +.I "Filename expansion" +uses the metacharacters `*', `?' and `[' and `]' +to provide a convenient mechanism for naming files. +Using +.I "filename expansion" +it is easy to name all the files in +the current directory, or all files which have a common +.I root +name. Other +.I "filename expansion" +mechanisms use the metacharacter `~' and allow +files in other users' directories to be named easily (1.6, 4.2). +.IP flag +Many \s-2UNIX\s0 commands accept arguments which are not the names +of files or other users but are used to modify the action of the commands. +These are referred to as +.I flag +options, and by convention consist of one or more letters preceded by +the character `\-' (1.2). +Thus the +.I ls +(list files) command has an option +`\-s' to list the sizes of files. +This is specified +.DS +ls \-s +.DE +.IP foreach +The +.I foreach +command is used in shell scripts and at the terminal to specify +repetition of a sequence of commands while the value of a certain +shell variable ranges through a specified list (3.6, 4.1). +.IP foreground +When commands are executing in the normal way such that the +shell is waiting for them to finish before prompting for another +command they are said to be +.I "foreground jobs" +or +.I "running in the foreground" \&. +This is as opposed to +.I background . +.I Foreground +jobs can be stopped by signals +from the terminal caused by typing different +control characters at the keyboard (1.8, 2.6). +.IP goto +The shell has a command +.I goto +used in shell scripts to transfer control to a given label (3.7). +.IP grep +The +.I grep +command searches through a list of argument files for a specified string. +Thus +.DS +grep bill /etc/passwd +.DE +will print each line in the file +.I "/etc/passwd" +which contains the string `bill'. +Actually, +.I grep +scans for +.I "regular expressions" +in the sense of the editors +`ed (1)' and `ex (1)'. +.I Grep +stands for +`globally find +.I "regular expression" +and print' (2.4). +.IP head +The +.I head +command prints the first few lines of one or more files. +If you have a bunch of files containing text which you are wondering +about it is sometimes useful to run +.I head +with these files as arguments. +This will usually show enough of what is in these files to let you decide +which you are interested in (1.5). +.br +.I Head +is also used to describe the part of a +.I pathname +before and including the last `/' character. The +.I tail +of a +.I pathname +is the part after the last `/'. The `:h' and `:t' modifiers allow the +.I head +or +.I tail +of a +.I pathname +stored in a shell variable to be used (3.6). +.IP history +The +.I history +mechanism of the shell allows previous commands to be repeated, +possibly after modification to correct typing mistakes or to change +the meaning of the command. +The shell has a +.I "history list" +where these commands are kept, and a +.I history +variable which controls how large this list is (2.3). +.IP "home\ directory" +.br +Each user has a +.I "home directory" , +which is given in your entry +in the password file, +.I /etc/passwd . +This is the directory which you are placed in when you first login. +The +.I cd +or +.I chdir +command with no arguments takes you back to this directory, whose +name is recorded in the shell variable +.I home . +You can also access the +.I "home directories" +of other users in forming +filenames using a +.I "filename expansion" +notation and the character `~' (1.6). +.IP if +A conditional command within the shell, the +.I if +command is used in shell command scripts to make decisions +about what course of action to take next (3.6). +.IP ignoreeof +Normally, your shell will exit, printing +`logout' +if you type a control-d at a prompt of `% '. +This is the way you usually log off the system. +You can +.I set +the +.I ignoreeof +variable if you wish in your +.I \&.login +file and then use the command +.I logout +to logout. +This is useful if you sometimes accidentally type too many control-d +characters, logging yourself off +(2.2). +.IP input +Many commands on \s-2UNIX\s0 take information from the terminal or from +files which they then act on. +This information is called +.I input . +Commands normally read for +.I input +from their +.I "standard input" +which is, by default, the terminal. +This +.I "standard input" +can be redirected from a file using a shell metanotation +with the character `<'. +Many commands will also read from a file specified as argument. +Commands placed in +.I pipelines +will read from the output of the previous +command in the +.I pipeline . +The leftmost command in a +.I pipeline +reads from the terminal if +you neither redirect its +.I input +nor give it a filename to use as +.I "standard input" . +Special mechanisms exist for supplying input to commands in shell +scripts (1.5, 3.8). +.IP interrupt +An +.I interrupt +is a signal to a program that is generated by typing ^C. (On older versions +of UNIX the \s-2RUBOUT\s0 or \s-2DELETE\s0 key were used for this purpose.) +It causes most programs to stop execution. +Certain programs, such as the shell and the editors, +handle an +.I interrupt +in special ways, usually by stopping what they +are doing and prompting for another command. +While the shell is executing another command and waiting for it +to finish, the shell does not listen to +.I interrupts. +The shell often wakes up when you hit +.I interrupt +because many commands +die when they receive an +.I interrupt +(1.8, 3.9). +.IP job +One or more commands +typed on the same input line separated by `|' or `;' characters +are run together and are called a +.I job \&. +Simple commands run by themselves without any `|' or `;' characters +are the simplest +.I jobs. +.I Jobs +are classified as +.I foreground , +.I background , +or +.I suspended +(2.6). +.IP "job\ control" +The builtin functions that control the execution of +jobs are called +.I "job control" +commands. These are +.I "bg, fg, stop, kill" +(2.6). +.IP "job\ number" +When each job +is started it is assigned a small number called a +.I "job number" +which is printed next to the job in the output of the +.I jobs +command. This number, preceded by a `%' character, can be used as an argument +to +.I "job control" +commands to indicate +a specific job (2.6). +.IP jobs +The +.I jobs +command prints a table showing +jobs that are either running in the +.I background +or are +.I suspended +(2.6). +.IP kill +A command which sends a +signal +to a job causing it to terminate (2.6). +.IP \&.login +The file +.I \&.login +in your +.I home +directory is read by the shell each time you login to \s-2UNIX\s0 +and the commands there are executed. +There are a number of commands which are usefully placed here, +especially +.I set +commands to the shell itself (2.1). +.IP "login\ shell" +The shell that is started on your terminal when you login is called +your +.I "login shell" . +It is different from other shells which you may run (e.g. on +shell scripts) +in that it reads the +.I \&.login +file before reading commands from the terminal and it reads the +.I \&.logout +file after you logout +(2.1). +.IP logout +The +.I logout +command causes a login shell to exit. +Normally, a login shell will exit when you hit control-d +generating an +.I end\f1-\fPof\f1-\fPfile, +but if you have set +.I ignoreeof +in you +.I \&.login +file then this will not work and you must use +.I logout +to log off the \s-2UNIX\s0 system (2.8). +.IP \&.logout +When you log off of \s-2UNIX\s0 the shell will execute commands from +the file +.I \&.logout +in your +.I home +directory after it prints `logout'. +.IP lpr +The command +.I lpr +is the line printer daemon. +The standard input of +.I lpr +spooled and printed on the \s-2UNIX\s0 line printer. +You can also give +.I lpr +a list of filenames as arguments to be printed. +It is most common to use +.I lpr +as the last component of a +.I pipeline +(2.3). +.IP ls +The +.I ls +(list files) command is one of the most commonly used \s-2UNIX\s0 +commands. +With no argument filenames it prints the names of the files in the +current directory. +It has a number of useful +.I flag +arguments, and can also be given the names of directories +as arguments, in which case it lists the names of the files in these +directories (1.2). +.IP mail +The +.I mail +program is used to send and receive messages from other \s-2UNIX\s0 +users (1.1, 2.1), whether they are logged on or not. +.IP make +The +.I make +command is used to maintain one or more related files and to +organize functions to be performed on these files. +In many ways +.I make +is easier to use, and more helpful than +shell command scripts (3.2). +.IP makefile +The file containing commands for +.I make +is called +.I makefile +or +.I Makefile +(3.2). +.IP manual +The +.I manual +often referred to is the +`\s-2UNIX\s0 manual'. +It contains 8 numbered sections with a description of each \s-2UNIX\s0 +program (section 1), system call (section 2), subroutine (section 3), +device (section 4), special data structure (section 5), game (section 6), +miscellaneous item (section 7) and system administration program (section 8). +There are also supplementary documents (tutorials and reference guides) +for individual programs which require explanation in more detail. +An online version of the +.I manual +is accessible through the +.I man +command. +Its documentation can be obtained online via +.DS +man man +.DE +If you can't decide what manual page to look in, try the +.I apropos (1) +command. +The supplementary documents are in subdirectories of /usr/doc. +.IP metacharacter +.br +Many characters which are neither letters nor digits have special meaning +either to the shell or to \s-2UNIX\s0. +These characters are called +.I metacharacters . +If it is necessary to place these characters in arguments to commands +without them having their special meaning then they must be +.I quoted . +An example of a +.I metacharacter +is the character `>' which is used +to indicate placement of output into a file. +For the purposes of the +.I history +mechanism, +most unquoted +.I metacharacters +form separate words (1.4). +The appendix to this user's manual lists the +.I metacharacters +in groups by their function. +.IP mkdir +The +.I mkdir +command is used to create a new directory. +.IP modifier +Substitutions with the +.I history +mechanism, keyed by the character `!' +or of variables using the metacharacter `$', are often subjected +to modifications, indicated by placing the character `:' after the +substitution and following this with the +.I modifier +itself. +The +.I "command substitution" +mechanism can also be used to perform modification in a similar way, +but this notation is less clear (3.6). +.IP more +The program +.I more +writes a file on your terminal allowing you to control how much text +is displayed at a time. +.I More +can move through the file screenful by screenful, line by line, +search forward for a string, or start again at the beginning of the file. +It is generally the easiest way of viewing a file (1.8). +.IP noclobber +The shell has a variable +.I noclobber +which may be set in the file +.I \&.login +to prevent accidental destruction of files by the `>' output redirection +metasyntax of the shell (2.2, 2.5). +.IP noglob +The shell variable +.I noglob +is set to suppress the +.I "filename expansion" +of arguments containing the metacharacters `~', `*', `?', `[' and `]' (3.6). +.IP notify +The +.I notify +command tells the shell to report on the termination of a specific +.I "background job" +at the exact time it occurs as opposed to waiting +until just before the next prompt to report the termination. +The +.I notify +variable, if set, causes the shell to always report the termination +of +.I background +jobs exactly when they occur (2.6). +.IP onintr +The +.I onintr +command is built into the shell and is used to control the action +of a shell command script when an +.I interrupt +signal is received (3.9). +.IP output +Many commands in \s-2UNIX\s0 result in some lines of text which are +called their +.I output. +This +.I output +is usually placed on what is known as the +.I "standard output" +which is normally connected to the user's terminal. +The shell has a syntax using the metacharacter `>' for redirecting +the +.I "standard output" +of a command to a file (1.3). +Using the +.I pipe +mechanism and the metacharacter `|' it is also possible for +the +.I "standard output" +of one command to become the +.I "standard input" +of another command (1.5). +Certain commands such as the line printer daemon +.I p +do not place their results on the +.I "standard output" +but rather in more +useful places such as on the line printer (2.3). +Similarly the +.I write +command places its output on another user's terminal rather than its +.I "standard output" +(2.3). +Commands also have a +.I "diagnostic output" +where they write their error messages. +Normally these go to the terminal even if the +.I "standard output" +has been sent to a file or another command, but it is possible +to direct error diagnostics along with +.I "standard output" +using a special metanotation (2.5). +.IP path +The shell has a variable +.I path +which gives the names of the directories in which it searches for +the commands which it is given. +It always checks first to see if the command it is given is +built into the shell. +If it is, then it need not search for the command as it can do it internally. +If the command is not builtin, then the shell searches for a file +with the name given in each of the directories in the +.I path +variable, left to right. +Since the normal definition of the +.I path +variable is +.DS +path (. /usr/ucb /bin /usr/bin) +.DE +the shell normally looks in the current directory, and then in +the standard system directories `/usr/ucb', `/bin' and `/usr/bin' for the named +command (2.2). +If the command cannot be found the shell will print an error diagnostic. +Scripts of shell commands will be executed using another shell to interpret +them if they have `execute' permission set. +This is normally true because a command of the form +.DS +chmod 755 script +.DE +was executed to turn this execute permission on (3.3). +If you add new commands to a directory in the +.I path , +you should issue +the command +.I rehash +(2.2). +.IP pathname +A list of names, separated by `/' characters, forms a +.I pathname. +Each +.I component, +between successive `/' characters, names a directory +in which the next +.I component +file resides. +.I Pathnames +which begin with the character `/' are interpreted relative +to the +.I root +directory in the filesystem. +Other +.I pathnames +are interpreted relative to the current directory +as reported by +.I pwd. +The last component of a +.I pathname +may name a directory, but +usually names a file. +.IP pipeline +A group of commands which are connected together, the +.I "standard output" +of each connected to the +.I "standard input" +of the next, +is called a +.I pipeline. +The +.I pipe +mechanism used to connect these commands is indicated by +the shell metacharacter `|' (1.5, 2.3). +.IP popd +The +.I popd +command changes the shell's +.I "working directory" +to the directory you most recently left using the +.I pushd +command. It returns to the directory without having to type its name, +forgetting the name of the current +.I "working directory" +before doing so (2.7). +.IP port +The part of a computer system to which each terminal is +connected is called a +.I port . +Usually the system has a fixed number of +.I ports , +some of which are connected to telephone lines +for dial-up access, and some of which are permanently +wired directly to specific terminals. +.IP pr +The +.I pr +command is used to prepare listings of the contents of files +with headers giving the name of the file and the date and +time at which the file was last modified (2.3). +.IP printenv +The +.I printenv +command is used +to print the current setting of variables in the environment +(2.8). +.IP process +An instance of a running program is called a +.I process +(2.6). +\s-2UNIX\s0 assigns each +.I process +a unique number when it is +started \- called the +.I "process number" . +.I "Process numbers" +can be used to stop individual +.I processes +using the +.I kill +or +.I stop +commands when the +.I processes +are part of a detached +.I background +job. +.IP program +Usually synonymous with +.I command ; +a binary file or shell command script +which performs a useful function is often +called a +.I program . +.IP prompt +Many programs will print a +.I prompt +on the terminal when they expect input. +Thus the editor +`ex (1)' will print a `:' when it expects input. +The shell +.I prompts +for input with `% ' and occasionally with `? ' when +reading commands from the terminal (1.1). +The shell has a variable +.I prompt +which may be set to a different value to change the shell's main +.I prompt . +This is mostly used when debugging the shell (2.8). +.IP pushd +The +.I pushd +command, which means `push directory', changes the shell's +.I "working directory" +and also remembers the current +.I "working directory" +before the change is made, allowing you to return to the same +directory via the +.I popd +command later without retyping its name (2.7). +.IP ps +The +.I ps +command is used to show the processes you are currently running. +Each process is shown with its unique process number, +an indication of the terminal name it is attached to, +an indication of the state of the process (whether it is running, +stopped, awaiting some event (sleeping), and whether it is swapped out), +and the amount of \s-2CPU\s0 time it has used so far. +The command is identified by printing some of the words used +when it was invoked (2.6). +Shells, such as the +.I csh +you use to run the +.I ps +command, are not normally shown in the output. +.IP pwd +The +.I pwd +command prints the full +.I pathname +of the current +.I "working directory" \&. +The +.I dirs +builtin command is usually a better and faster choice. +.IP quit +The +.I quit +signal, generated by a control-\e, +is used to terminate programs which are behaving unreasonably. +It normally produces a core image file (1.8). +.IP quotation +The process by which metacharacters are prevented their special +meaning, usually by using the character `\' in pairs, or by +using the character `\e', is referred to as +.I quotation +(1.7). +.IP redirection +The routing of input or output from or to a file is known +as +.I redirection +of input or output (1.3). +.IP rehash +The +.I rehash +command tells the shell to rebuild its internal table of which commands +are found in which directories in your +.I path . +This is necessary when a new program is installed in one of these +directories (2.8). +.IP "relative pathname" +.br +A +.I pathname +which does not begin with a `/' is called a +.I "relative pathname" +since it is interpreted +.I relative +to the current +.I "working directory" . +The first +.I component +of such a +.I pathname +refers to some file or directory in the +.I "working directory" , +and subsequent +.I components +between `/' characters refer to directories below the +.I "working directory" . +.I Pathnames +that are not +.I relative +are called +.I "absolute pathnames" +(1.6). +.IP repeat +The +.I repeat +command iterates another command a specified number of times. +.IP root +The directory +that is at the top of the entire directory structure is called the +.I root +directory since it is the `root' of the entire tree structure of +directories. The name used in +.I pathnames +to indicate the +.I root +is `/'. +.I Pathnames +starting with `/' are said to be +.I absolute +since they start at the +.I root +directory. +.I Root +is also used as the part of a +.I pathname +that is left after removing +the +.I extension . +See +.I filename +for a further explanation (1.6). +.IP \s-2RUBOUT\s0 +The \s-2RUBOUT\s0 or \s-2DELETE\s0 +key is often used to erase the previously typed character; some users +prefer the \s-2BACKSPACE\s0 for this purpose. On older versions of \s-2UNIX\s0 +this key served as the \s-2INTR\s0 character. +.IP "scratch file" +Files whose names begin with a `#' are referred to as +.I "scratch files" , +since they are automatically removed by the system after a couple of +days of non-use, or more frequently if disk space becomes tight (1.3). +.IP script +Sequences of shell commands placed in a file are called shell command +.I scripts . +It is often possible to perform simple tasks using these +.I scripts +without writing a program in a language such as C, by +using the shell to selectively run other programs (3.3, 3.10). +.IP set +The builtin +.I set +command is used to assign new values to shell variables +and to show the values of the current variables. +Many shell variables have special meaning to the shell itself. +Thus by using the +.I set +command the behavior of the shell can be affected (2.1). +.IP setenv +Variables in the environment `environ (5)' +can be changed by using the +.I setenv +builtin command (2.8). +The +.I printenv +command can be used to print the value of the variables in the environment. +.IP shell +A +.I shell +is a command language interpreter. +It is possible to write and run your own +.I shell , +as +.I shells +are no different than any other programs as far as the +system is concerned. +This manual deals with the details of one particular +.I shell , +called +.I csh. +.IP "shell script" +See +.I script +(3.3, 3.10). +.IP signal +A +.I signal +in \s-2UNIX\s0 is a short message that is sent to a running program +which causes something to happen to that process. +.I Signals +are sent either by typing special +.I control +characters on the keyboard or by using the +.I kill +or +.I stop +commands (1.8, 2.6). +.IP sort +The +.I sort +program sorts a sequence of lines in ways that can be controlled +by argument +.I flags +(1.5). +.IP source +The +.I source +command causes the shell to read commands from a specified file. +It is most useful for reading files such as +.I \&.cshrc +after changing them (2.8). +.IP "special character" +.br +See +.I metacharacters +and the +appendix to this manual. +.IP standard +We refer often to the +.I "standard input" +and +.I "standard output" +of commands. +See +.I input +and +.I output +(1.3, 3.8). +.IP status +A command normally returns a +.I status +when it finishes. +By convention a +.I status +of zero indicates that the command succeeded. +Commands may return non-zero +.I status +to indicate that some abnormal event has occurred. +The shell variable +.I status +is set to the +.I status +returned by the last command. +It is most useful in shell commmand scripts (3.6). +.IP stop +The +.I stop +command causes a +.I background +job to become +.I suspended +(2.6). +.IP string +A sequential group of characters taken together is called a +.I string \&. +.I Strings +can contain any printable characters (2.2). +.IP stty +The +.I stty +program changes certain parameters inside \s-2UNIX\s0 which determine +how your terminal is handled. See `stty (1)' for a complete description (2.6). +.IP substitution +The shell implements a number of +.I substitutions +where sequences indicated by metacharacters are replaced by other sequences. +Notable examples of this are history +.I substitution +keyed by the +metacharacter `!' and variable +.I substitution +indicated by `$'. +We also refer to +.I substitutions +as +.I expansions +(3.4). +.IP suspended +A job becomes +.I suspended +after a \s-2STOP\s0 signal is sent to it, either by typing a +.I control -z +at the terminal (for +.I foreground +jobs) or by using the +.I stop +command (for +.I background +jobs). When +.I suspended , +a job temporarily stops running until it is restarted by either the +.I fg +or +.I bg +command (2.6). +.IP switch +The +.I switch +command of the shell allows the shell +to select one of a number of sequences of commands based on an +argument string. +It is similar to the +.I switch +statement in the language C (3.7). +.IP termination +When a command which is being executed finishes we say it undergoes +.I termination +or +.I terminates. +Commands normally terminate when they read an +.I end\f1-\fPof\f1-\fPfile +from their +.I "standard input" . +It is also possible to terminate commands by sending them +an +.I interrupt +or +.I quit +signal (1.8). +The +.I kill +program terminates specified jobs (2.6). +.IP then +The +.I then +command is part of the shell's +`if-then-else-endif' control construct used in command scripts (3.6). +.IP time +The +.I time +command can be used to measure the amount of \s-2CPU\s0 +and real time consumed by a specified command as well +as the amount of disk i/o, memory utilized, and number +of page faults and swaps taken by the command (2.1, 2.8). +.IP tset +The +.I tset +program is used to set standard erase and kill characters +and to tell the system what kind of terminal you are using. +It is often invoked in a +.I \&.login +file (2.1). +.IP tty +The word +.I tty +is a historical abbreviation for `teletype' which is frequently used +in \s-2UNIX\s0 to indicate the +.I port +to which a given terminal is connected. The +.I tty +command will print the name of the +.I tty +or +.I port +to which your terminal is presently connected. +.IP unalias +The +.I unalias +command removes aliases (2.8). +.IP \s-2UNIX\s0 +\s-2UNIX\s0 is an operating system on which +.I csh +runs. +\s-2UNIX\s0 provides facilities which allow +.I csh +to invoke other programs such as editors and text formatters which +you may wish to use. +.IP unset +The +.I unset +command removes the definitions of shell variables (2.2, 2.8). +.IP "variable expansion" +.br +See +.I variables +and +.I expansion +(2.2, 3.4). +.IP variables +.I Variables +in +.I csh +hold one or more strings as value. +The most common use of +.I variables +is in controlling the behavior +of the shell. +See +.I path , +.I noclobber , +and +.I ignoreeof +for examples. +.I Variables +such as +.I argv +are also used in writing shell programs (shell command scripts) +(2.2). +.IP verbose +The +.I verbose +shell variable can be set to cause commands to be echoed +after they are history expanded. +This is often useful in debugging shell scripts. +The +.I verbose +variable is set by the shell's +.I \-v +command line option (3.10). +.IP wc +The +.I wc +program calculates the number of characters, words, and lines in the +files whose names are given as arguments (2.6). +.IP while +The +.I while +builtin control construct is used in shell command scripts (3.7). +.IP word +A sequence of characters which forms an argument to a command is called +a +.I word . +Many characters which are neither letters, digits, `\-', `.' nor `/' +form +.I words +all by themselves even if they are not surrounded +by blanks. +Any sequence of characters may be made into a +.I word +by surrounding it +with `\'' characters +except for the characters `\'' and `!' which require special treatment +(1.1). +This process of placing special characters in +.I words +without their special meaning is called +.I quoting . +.IP "working directory" +.br +At any given time you are in one particular directory, called +your +.I "working directory" . +This directory's name is printed by the +.I pwd +command and the files listed by +.I ls +are the ones in this directory. +You can change +.I "working directories" +using +.I chdir . +.IP write +The +.I write +command is an obsolete way of communicating with other users who are logged in to +\s-2UNIX\s0 (you have to take turns typing). If you are both using display +terminals, use \fItalk\fP(1), which is much more pleasant. diff --git a/bin/csh/USD.doc/tabs b/bin/csh/USD.doc/tabs new file mode 100644 index 000000000000..9d274a14caf8 --- /dev/null +++ b/bin/csh/USD.doc/tabs @@ -0,0 +1,34 @@ +.\" Copyright (c) 1980, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" 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. +.\" +.\" @(#)tabs 8.1 (Berkeley) 6/8/93 +.\" +.ta 5n 10n 15n 20n 25n 30n 35n 40n 45n 50n 55n 60n 65n 70n 75n 80n diff --git a/bin/csh/alloc.c b/bin/csh/alloc.c new file mode 100644 index 000000000000..6fc8cf594301 --- /dev/null +++ b/bin/csh/alloc.c @@ -0,0 +1,124 @@ +/*- + * Copyright (c) 1983, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * 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[] = "@(#)alloc.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <unistd.h> +#include <stdlib.h> +#if __STDC__ +# include <stdarg.h> +#else +# include <varargs.h> +#endif + +#include "csh.h" +#include "extern.h" + +char *memtop = NULL; /* PWP: top of current memory */ +char *membot = NULL; /* PWP: bottom of allocatable memory */ + +ptr_t +Malloc(n) + size_t n; +{ + ptr_t ptr; + + if (membot == NULL) + memtop = membot = sbrk(0); + if ((ptr = malloc(n)) == (ptr_t) 0) { + child++; + stderror(ERR_NOMEM); + } + return (ptr); +} + +ptr_t +Realloc(p, n) + ptr_t p; + size_t n; +{ + ptr_t ptr; + + if (membot == NULL) + memtop = membot = sbrk(0); + if ((ptr = realloc(p, n)) == (ptr_t) 0) { + child++; + stderror(ERR_NOMEM); + } + return (ptr); +} + +ptr_t +Calloc(s, n) + size_t s, n; +{ + ptr_t ptr; + + if (membot == NULL) + memtop = membot = sbrk(0); + if ((ptr = calloc(s, n)) == (ptr_t) 0) { + child++; + stderror(ERR_NOMEM); + } + + return (ptr); +} + +void +Free(p) + ptr_t p; +{ + if (p) + free(p); +} + +/* + * mstats - print out statistics about malloc + * + * Prints two lines of numbers, one showing the length of the free list + * for each size category, the second showing the number of mallocs - + * frees for each size category. + */ +void +/*ARGSUSED*/ +showall(v, t) + Char **v; + struct command *t; +{ + memtop = (char *) sbrk(0); + (void) fprintf(cshout, "Allocated memory from 0x%lx to 0x%lx (%ld).\n", + (unsigned long) membot, (unsigned long) memtop, memtop - membot); +} diff --git a/bin/csh/char.c b/bin/csh/char.c new file mode 100644 index 000000000000..7da611fc6c76 --- /dev/null +++ b/bin/csh/char.c @@ -0,0 +1,311 @@ +/*- + * Copyright (c) 1980, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * 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[] = "@(#)char.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include "char.h" + +unsigned short _cmap[256] = { +/* nul soh stx etx */ + _CTR, _CTR, _CTR, _CTR, + +/* eot enq ack bel */ + _CTR, _CTR, _CTR, _CTR, + +/* bs ht nl vt */ + _CTR, _CTR|_SP|_META, _CTR|_NL|_META, _CTR, + +/* np cr so si */ + _CTR, _CTR, _CTR, _CTR, + +/* dle dc1 dc2 dc3 */ + _CTR, _CTR, _CTR, _CTR, + +/* dc4 nak syn etb */ + _CTR, _CTR, _CTR, _CTR, + +/* can em sub esc */ + _CTR, _CTR, _CTR, _CTR, + +/* fs gs rs us */ + _CTR, _CTR, _CTR, _CTR, + +/* sp ! " # */ + _SP|_META, 0, _QF, _META, + +/* $ % & ' */ + _DOL, 0, _META|_CMD, _QF, + +/* ( ) * + */ + _META|_CMD, _META, _GLOB, 0, + +/* , - . / */ + 0, 0, 0, 0, + +/* 0 1 2 3 */ + _DIG|_XD, _DIG|_XD, _DIG|_XD, _DIG|_XD, + +/* 4 5 6 7 */ + _DIG|_XD, _DIG|_XD, _DIG|_XD, _DIG|_XD, + +/* 8 9 : ; */ + _DIG|_XD, _DIG|_XD, 0, _META|_CMD, + +/* < = > ? */ + _META, 0, _META, _GLOB, + +/* @ A B C */ + 0, _LET|_UP|_XD, _LET|_UP|_XD, _LET|_UP|_XD, + +/* D E F G */ + _LET|_UP|_XD, _LET|_UP|_XD, _LET|_UP|_XD, _LET|_UP, + +/* H I J K */ + _LET|_UP, _LET|_UP, _LET|_UP, _LET|_UP, + +/* L M N O */ + _LET|_UP, _LET|_UP, _LET|_UP, _LET|_UP, + +/* P Q R S */ + _LET|_UP, _LET|_UP, _LET|_UP, _LET|_UP, + +/* T U V W */ + _LET|_UP, _LET|_UP, _LET|_UP, _LET|_UP, + +/* X Y Z [ */ + _LET|_UP, _LET|_UP, _LET|_UP, _GLOB, + +/* \ ] ^ _ */ + _ESC, 0, 0, 0, + +/* ` a b c */ + _QB|_GLOB|_META, _LET|_LOW|_XD, _LET|_LOW|_XD, _LET|_LOW|_XD, + +/* d e f g */ + _LET|_LOW|_XD, _LET|_LOW|_XD, _LET|_LOW|_XD, _LET|_LOW, + +/* h i j k */ + _LET|_LOW, _LET|_LOW, _LET|_LOW, _LET|_LOW, + +/* l m n o */ + _LET|_LOW, _LET|_LOW, _LET|_LOW, _LET|_LOW, + +/* p q r s */ + _LET|_LOW, _LET|_LOW, _LET|_LOW, _LET|_LOW, + +/* t u v w */ + _LET|_LOW, _LET|_LOW, _LET|_LOW, _LET|_LOW, + +/* x y z { */ + _LET|_LOW, _LET|_LOW, _LET|_LOW, _GLOB, + +/* | } ~ del */ + _META|_CMD, 0, 0, _CTR, + +#if defined(SHORT_STRINGS) && !defined(KANJI) +/****************************************************************/ +/* 128 - 255 The below is supposedly ISO 8859/1 */ +/****************************************************************/ +/* (undef) (undef) (undef) (undef) */ + _CTR, _CTR, _CTR, _CTR, + +/* (undef) (undef) (undef) (undef) */ + _CTR, _CTR, _CTR, _CTR, + +/* (undef) (undef) (undef) (undef) */ + _CTR, _CTR, _CTR, _CTR, + +/* (undef) (undef) (undef) (undef) */ + _CTR, _CTR, _CTR, _CTR, + +/* (undef) (undef) (undef) (undef) */ + _CTR, _CTR, _CTR, _CTR, + +/* (undef) (undef) (undef) (undef) */ + _CTR, _CTR, _CTR, _CTR, + +/* (undef) (undef) (undef) (undef) */ + _CTR, _CTR, _CTR, _CTR, + +/* (undef) (undef) (undef) (undef) */ + _CTR, _CTR, _CTR, _CTR, + +/* nobreakspace exclamdown cent sterling */ + _SP, 0, 0, 0, + +/* currency yen brokenbar section */ + 0, 0, 0, 0, + +/* diaeresis copyright ordfeminine guillemotleft */ + 0, 0, 0, 0, + +/* notsign hyphen registered macron */ + 0, 0, 0, 0, + +/* degree plusminus twosuperior threesuperior */ + 0, 0, 0, 0, + +/* acute mu paragraph periodcentered */ + 0, 0, 0, 0, + +/* cedilla onesuperior masculine guillemotright */ + 0, 0, 0, 0, + +/* onequarter onehalf threequarters questiondown */ + 0, 0, 0, 0, + +/* Agrave Aacute Acircumflex Atilde */ + _LET|_UP, _LET|_UP, _LET|_UP, _LET|_UP, + +/* Adiaeresis Aring AE Ccedilla */ + _LET|_UP, _LET|_UP, _LET|_UP, _LET|_UP, + +/* Egrave Eacute Ecircumflex Ediaeresis */ + _LET|_UP, _LET|_UP, _LET|_UP, _LET|_UP, + +/* Igrave Iacute Icircumflex Idiaeresis */ + _LET|_UP, _LET|_UP, _LET|_UP, _LET|_UP, + +/* ETH Ntilde Ograve Oacute */ + _LET|_UP, _LET|_UP, _LET|_UP, _LET|_UP, + +/* Ocircumflex Otilde Odiaeresis multiply */ + _LET|_UP, _LET|_UP, _LET|_UP, 0, + +/* Ooblique Ugrave Uacute Ucircumflex */ + _LET|_UP, _LET|_UP, _LET|_UP, _LET|_UP, + +/* Udiaeresis Yacute THORN ssharp */ + _LET|_UP, _LET|_UP, _LET|_UP, _LET|_LOW, + +/* agrave aacute acircumflex atilde */ + _LET|_LOW, _LET|_LOW, _LET|_LOW, _LET|_LOW, + +/* adiaeresis aring ae ccedilla */ + _LET|_LOW, _LET|_LOW, _LET|_LOW, _LET|_LOW, + +/* egrave eacute ecircumflex ediaeresis */ + _LET|_LOW, _LET|_LOW, _LET|_LOW, _LET|_LOW, + +/* igrave iacute icircumflex idiaeresis */ + _LET|_LOW, _LET|_LOW, _LET|_LOW, _LET|_LOW, + +/* eth ntilde ograve oacute */ + _LET|_LOW, _LET|_LOW, _LET|_LOW, _LET|_LOW, + +/* ocircumflex otilde odiaeresis division */ + _LET|_LOW, _LET|_LOW, _LET|_LOW, 0, + +/* oslash ugrave uacute ucircumflex */ + _LET|_LOW, _LET|_LOW, _LET|_LOW, _LET|_LOW, + +/* udiaeresis yacute thorn ydiaeresis */ + _LET|_LOW, _LET|_LOW, _LET|_LOW, _LET|_LOW, +#endif /* SHORT_STRINGS && !KANJI */ +}; + +#ifndef NLS +/* _cmap_lower, _cmap_upper for ISO 8859/1 */ + +unsigned char _cmap_lower[256] = { + 0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007, + 0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017, + 0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027, + 0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037, + 0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047, + 0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057, + 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067, + 0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077, + 0100, 0141, 0142, 0143, 0144, 0145, 0146, 0147, + 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157, + 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167, + 0170, 0171, 0172, 0133, 0134, 0135, 0136, 0137, + 0140, 0141, 0142, 0143, 0144, 0145, 0146, 0147, + 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157, + 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167, + 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177, + 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207, + 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217, + 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227, + 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237, + 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247, + 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257, + 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, + 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, + 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347, + 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357, + 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0327, + 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0337, + 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347, + 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357, + 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, + 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377, +}; + +unsigned char _cmap_upper[256] = { + 0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007, + 0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017, + 0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027, + 0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037, + 0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047, + 0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057, + 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067, + 0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077, + 0100, 0101, 0102, 0103, 0104, 0105, 0106, 0107, + 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117, + 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127, + 0130, 0131, 0132, 0133, 0134, 0135, 0136, 0137, + 0140, 0101, 0102, 0103, 0104, 0105, 0106, 0107, + 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117, + 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127, + 0130, 0131, 0132, 0173, 0174, 0175, 0176, 0177, + 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207, + 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217, + 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227, + 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237, + 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247, + 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257, + 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, + 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, + 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307, + 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317, + 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327, + 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, + 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307, + 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317, + 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0367, + 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0377, +}; +#endif /* NLS */ diff --git a/bin/csh/char.h b/bin/csh/char.h new file mode 100644 index 000000000000..a791de45a49c --- /dev/null +++ b/bin/csh/char.h @@ -0,0 +1,96 @@ +/*- + * Copyright (c) 1980, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * 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. + * + * @(#)char.h 8.1 (Berkeley) 5/31/93 + */ + +#include <ctype.h> + +extern unsigned short _cmap[]; + +#ifndef NLS +extern unsigned char _cmap_lower[], _cmap_upper[]; + +#endif + +#define _QF 0x0001 /* '" (Forward quotes) */ +#define _QB 0x0002 /* ` (Backquote) */ +#define _SP 0x0004 /* space and tab */ +#define _NL 0x0008 /* \n */ +#define _META 0x0010 /* lex meta characters, sp #'`";&<>()|\t\n */ +#define _GLOB 0x0020 /* glob characters, *?{[` */ +#define _ESC 0x0040 /* \ */ +#define _DOL 0x0080 /* $ */ +#define _DIG 0x0100 /* 0-9 */ +#define _LET 0x0200 /* a-z, A-Z, _ */ +#define _UP 0x0400 /* A-Z */ +#define _LOW 0x0800 /* a-z */ +#define _XD 0x1000 /* 0-9, a-f, A-F */ +#define _CMD 0x2000 /* lex end of command chars, ;&(|` */ +#define _CTR 0x4000 /* control */ + +#define cmap(c, bits) \ + (((c) & QUOTE) ? 0 : (_cmap[(unsigned char)(c)] & (bits))) + +#define isglob(c) cmap(c, _GLOB) +#define isspc(c) cmap(c, _SP) +#define ismeta(c) cmap(c, _META) +#define iscmdmeta(c) cmap(c, _CMD) +#define letter(c) (((c) & QUOTE) ? 0 : \ + (isalpha((unsigned char) (c)) || (c) == '_')) +#define alnum(c) (((c) & QUOTE) ? 0 : \ + (isalnum((unsigned char) (c)) || (c) == '_')) +#ifdef NLS +#define Isspace(c) (((c) & QUOTE) ? 0 : isspace((unsigned char) (c))) +#define Isdigit(c) (((c) & QUOTE) ? 0 : isdigit((unsigned char) (c))) +#define Isalpha(c) (((c) & QUOTE) ? 0 : isalpha((unsigned char) (c))) +#define Islower(c) (((c) & QUOTE) ? 0 : islower((unsigned char) (c))) +#define Isupper(c) (((c) & QUOTE) ? 0 : isupper((unsigned char) (c))) +#define Tolower(c) (((c) & QUOTE) ? 0 : tolower((unsigned char) (c))) +#define Toupper(c) (((c) & QUOTE) ? 0 : toupper((unsigned char) (c))) +#define Isxdigit(c) (((c) & QUOTE) ? 0 : isxdigit((unsigned char) (c))) +#define Isalnum(c) (((c) & QUOTE) ? 0 : isalnum((unsigned char) (c))) +#define Iscntrl(c) (((c) & QUOTE) ? 0 : iscntrl((unsigned char) (c))) +#define Isprint(c) (((c) & QUOTE) ? 0 : isprint((unsigned char) (c))) +#else +#define Isspace(c) cmap(c, _SP|_NL) +#define Isdigit(c) cmap(c, _DIG) +#define Isalpha(c) (cmap(c,_LET) && !(((c) & META) && AsciiOnly)) +#define Islower(c) (cmap(c,_LOW) && !(((c) & META) && AsciiOnly)) +#define Isupper(c) (cmap(c, _UP) && !(((c) & META) && AsciiOnly)) +#define Tolower(c) (_cmap_lower[(unsigned char)(c)]) +#define Toupper(c) (_cmap_upper[(unsigned char)(c)]) +#define Isxdigit(c) cmap(c, _XD) +#define Isalnum(c) (cmap(c, _DIG|_LET) && !(((c) & META) && AsciiOnly)) +#define Iscntrl(c) (cmap(c,_CTR) && !(((c) & META) && AsciiOnly)) +#define Isprint(c) (!cmap(c,_CTR) && !(((c) & META) && AsciiOnly)) +#endif diff --git a/bin/csh/const.c b/bin/csh/const.c new file mode 100644 index 000000000000..25a02e0c7a5f --- /dev/null +++ b/bin/csh/const.c @@ -0,0 +1,160 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * 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[] = "@(#)const.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +/* + * tc.const.c: String constants for csh. + */ + +#include "csh.h" + +Char STR0[] = { '0', '\0' }; +Char STR1[] = { '1', '\0' }; +Char STRHOME[] = { 'H', 'O', 'M', 'E', '\0' }; +Char STRLANG[] = { 'L', 'A', 'N', 'G', '\0' }; +Char STRLC_CTYPE[] = { 'L', 'C', '_', 'C', 'T', 'Y', 'P', 'E' ,'\0' }; +Char STRLOGNAME[] = { 'L', 'O', 'G', 'N', 'A', 'M', 'E', '\0' }; +Char STRLbrace[] = { '{', '\0' }; +Char STRLparen[] = { '(', '\0' }; +Char STRLparensp[] = { '(', ' ', '\0' }; +Char STRNULL[] = { '\0' }; +Char STRPATH[] = { 'P', 'A', 'T', 'H', '\0' }; +Char STRPWD[] = { 'P', 'W', 'D', '\0' }; +Char STRQNULL[] = { '\0' | QUOTE, '\0' }; +Char STRRbrace[] = { '}', '\0' }; +Char STRspRparen[] = { ' ', ')', '\0' }; +Char STRTERM[] = { 'T', 'E', 'R', 'M', '\0' }; +Char STRUSER[] = { 'U', 'S', 'E', 'R', '\0' }; +Char STRalias[] = { 'a', 'l', 'i', 'a', 's', '\0' }; +Char STRand[] = { '&', '\0' }; +Char STRand2[] = { '&', '&', '\0' }; +Char STRaout[] = { 'a', '.', 'o', 'u', 't', '\0' }; +Char STRargv[] = { 'a', 'r', 'g', 'v', '\0' }; +Char STRbang[] = { '!', '\0' }; +Char STRcaret[] = { '^', '\0' }; +Char STRcdpath[] = { 'c', 'd', 'p', 'a', 't', 'h', '\0' }; +Char STRcent2[] = { '%', '%', '\0' }; +Char STRcenthash[] = { '%', '#', '\0' }; +Char STRcentplus[] = { '%', '+', '\0' }; +Char STRcentminus[] = { '%', '-', '\0' }; +Char STRchase_symlinks[] = { 'c', 'h', 'a', 's', 'e', '_', 's', 'y', 'm', 'l', + 'i', 'n', 'k', 's', '\0' }; +Char STRchild[] = { 'c', 'h', 'i', 'l', 'd', '\0' }; +Char STRcolon[] = { ':', '\0' }; +Char STRcwd[] = { 'c', 'w', 'd', '\0' }; +Char STRdefault[] = { 'd', 'e', 'f', 'a', 'u', 'l', 't', '\0' }; +Char STRdot[] = { '.', '\0' }; +Char STRdotdotsl[] = { '.', '.', '/', '\0' }; +Char STRdotsl[] = { '.', '/', '\0' }; +Char STRecho[] = { 'e', 'c', 'h', 'o', '\0' }; +Char STRequal[] = { '=', '\0' }; +Char STRfakecom[] = { '{', ' ', '.', '.', '.', ' ', '}', '\0' }; +Char STRfakecom1[] = { '`', ' ', '.', '.', '.', ' ', '`', '\0' }; +Char STRfignore[] = { 'f', 'i', 'g', 'n', 'o', 'r', 'e', '\0' }; +#ifdef FILEC +Char STRfilec[] = { 'f', 'i', 'l', 'e', 'c', '\0' }; +#endif /* FILEC */ +Char STRhistchars[] = { 'h', 'i', 's', 't', 'c', 'h', 'a', 'r', 's', '\0' }; +Char STRtildothist[] = { '~', '/', '.', 'h', 'i', 's', 't', 'o', 'r', + 'y', '\0' }; +Char STRhistfile[] = { 'h', 'i', 's', 't', 'f', 'i', 'l', 'e', '\0' }; +Char STRhistory[] = { 'h', 'i', 's', 't', 'o', 'r', 'y', '\0' }; +Char STRhome[] = { 'h', 'o', 'm', 'e', '\0' }; +Char STRignore_symlinks[] = { 'i', 'g', 'n', 'o', 'r', 'e', '_', 's', 'y', 'm', + 'l', 'i', 'n', 'k', 's', '\0' }; +Char STRignoreeof[] = { 'i', 'g', 'n', 'o', 'r', 'e', 'e', 'o', 'f', '\0' }; +Char STRjobs[] = { 'j', 'o', 'b', 's', '\0' }; +Char STRlistjobs[] = { 'l', 'i', 's', 't', 'j', 'o', 'b', 's', '\0' }; +Char STRlogout[] = { 'l', 'o', 'g', 'o', 'u', 't', '\0' }; +Char STRlong[] = { 'l', 'o', 'n', 'g', '\0' }; +Char STRmail[] = { 'm', 'a', 'i', 'l', '\0' }; +Char STRmh[] = { '-', 'h', '\0' }; +Char STRminus[] = { '-', '\0' }; +Char STRml[] = { '-', 'l', '\0' }; +Char STRmn[] = { '-', 'n', '\0' }; +Char STRmquestion[] = { '?' | QUOTE, ' ', '\0' }; +Char STRnice[] = { 'n', 'i', 'c', 'e', '\0' }; +Char STRnoambiguous[] = { 'n', 'o', 'a', 'm', 'b', 'i', 'g', 'u', 'o', 'u', + 's', '\0' }; +Char STRnobeep[] = { 'n', 'o', 'b', 'e', 'e', 'p', '\0' }; +Char STRnoclobber[] = { 'n', 'o', 'c', 'l', 'o', 'b', 'b', 'e', 'r', '\0' }; +Char STRnoglob[] = { 'n', 'o', 'g', 'l', 'o', 'b', '\0' }; +Char STRnohup[] = { 'n', 'o', 'h', 'u', 'p', '\0' }; +Char STRnonomatch[] = { 'n', 'o', 'n', 'o', 'm', 'a', 't', 'c', 'h', '\0' }; +Char STRnormal[] = { 'n', 'o', 'r', 'm', 'a', 'l', '\0' }; +Char STRnotify[] = { 'n', 'o', 't', 'i', 'f', 'y', '\0' }; +Char STRor[] = { '|', '\0' }; +Char STRor2[] = { '|', '|', '\0' }; +Char STRpath[] = { 'p', 'a', 't', 'h', '\0' }; +Char STRprintexitvalue[] = { 'p', 'r', 'i', 'n', 't', 'e', 'x', 'i', 't', 'v', + 'a', 'l', 'u', 'e', '\0' }; +Char STRprompt[] = { 'p', 'r', 'o', 'm', 'p', 't', '\0' }; +Char STRprompt2[] = { 'p', 'r', 'o', 'm', 'p', 't', '2', '\0' }; +Char STRpushdsilent[] = { 'p', 'u', 's', 'h', 'd', 's', 'i', 'l', 'e', 'n', + 't', '\0' }; +Char STRret[] = { '\n', '\0' }; +Char STRsavehist[] = { 's', 'a', 'v', 'e', 'h', 'i', 's', 't', '\0' }; +Char STRsemisp[] = { ';', ' ', '\0' }; +Char STRshell[] = { 's', 'h', 'e', 'l', 'l', '\0' }; +Char STRslash[] = { '/', '\0' }; +Char STRsldotcshrc[] = { '/', '.', 'c', 's', 'h', 'r', 'c', '\0' }; +Char STRsldotlogin[] = { '/', '.', 'l', 'o', 'g', 'i', 'n', '\0' }; +Char STRsldthist[] = { '/', '.', 'h', 'i', 's', 't', 'o', 'r', 'y', '\0' }; +Char STRsldtlogout[] = { '/', '.', 'l', 'o', 'g', 'o', 'u', 't', '\0' }; +Char STRsource[] = { 's', 'o', 'u', 'r', 'c', 'e', '\0' }; +Char STRsp3dots[] = { ' ', '.', '.', '.', '\0' }; +Char STRspLarrow2sp[] = { ' ', '<', '<', ' ', '\0' }; +Char STRspLarrowsp[] = { ' ', '<', ' ', '\0' }; +Char STRspRarrow[] = { ' ', '>', '\0' }; +Char STRspRarrow2[] = { ' ', '>', '>', '\0' }; +Char STRRparen[] = { ')', '\0' }; +Char STRspace[] = { ' ', '\0' }; +Char STRspand2sp[] = { ' ', '&', '&', ' ', '\0' }; +Char STRspor2sp[] = { ' ', '|', '|', ' ', '\0' }; +Char STRsporsp[] = { ' ', '|', ' ', '\0' }; +Char STRstar[] = { '*', '\0' }; +Char STRstatus[] = { 's', 't', 'a', 't', 'u', 's', '\0' }; +Char STRsymcent[] = { '%', ' ', '\0' }; +Char STRsymhash[] = { '#', ' ', '\0' }; +Char STRterm[] = { 't', 'e', 'r', 'm', '\0' }; +Char STRthen[] = { 't', 'h', 'e', 'n', '\0' }; +Char STRtilde[] = { '~', '\0' }; +Char STRtime[] = { 't', 'i', 'm', 'e', '\0' }; +Char STRtmpsh[] = { '/', 't', 'm', 'p', '/', 's', 'h', '\0' }; +Char STRunalias[] = { 'u', 'n', 'a', 'l', 'i', 'a', 's', '\0' }; +Char STRuser[] = { 'u', 's', 'e', 'r', '\0' }; +Char STRverbose[] = { 'v', 'e', 'r', 'b', 'o', 's', 'e', '\0' }; +Char STRwordchars[] = { 'w', 'o', 'r', 'd', 'c', 'h', 'a', 'r', 's', '\0' }; diff --git a/bin/csh/csh.1 b/bin/csh/csh.1 new file mode 100644 index 000000000000..58a9ddb191a1 --- /dev/null +++ b/bin/csh/csh.1 @@ -0,0 +1,2175 @@ +.\" Copyright (c) 1980, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" 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. +.\" +.\" @(#)csh.1 8.2 (Berkeley) 1/21/94 +.\" +.Dd January 21, 1994 +.Dt CSH 1 +.Os BSD 4 +.Sh NAME +.Nm csh +.Nd a shell (command interpreter) with C-like syntax +.Sh SYNOPSIS +.Nm csh +.Op Fl bcefinstvVxX +.Op arg ... +.Nm csh +.Op Fl l +.Sh DESCRIPTION +The +.Nm csh +is a command language interpreter +incorporating a history mechanism (see +.Nm History Substitutions ) , +job control facilities (see +.Nm Jobs ) , +interactive file name +and user name completion (see +.Nm File Name Completion ) , +and a C-like syntax. It is used both as an interactive +login shell and a shell script command processor. +.Ss Argument list processing +If the first argument (argument 0) to the shell is +.Ql Fl \& , +then this is a login shell. +A login shell also can be specified by invoking the shell with the +.Ql Fl l +flag as the only argument. +.Pp +The rest of the flag arguments are interpreted as follows: +.Bl -tag -width 5n +.It Fl b +This flag forces a ``break'' from option processing, causing any further +shell arguments to be treated as non-option arguments. +The remaining arguments will not be interpreted as shell options. +This may be used to pass options to a shell script without confusion +or possible subterfuge. +The shell will not run a set-user ID script without this option. +.It Fl c +Commands are read from the (single) following argument which must +be present. +Any remaining arguments are placed in +.Ar argv . +.It Fl e +The shell exits if any invoked command terminates abnormally +or yields a non-zero exit status. +.It Fl f +The shell will start faster, because it will neither search for nor +execute commands from the file +.Pa \&.cshrc +in the invoker's home directory. +.It Fl i +The shell is interactive and prompts for its top-level input, +even if it appears not to be a terminal. +Shells are interactive without this option if their inputs +and outputs are terminals. +.It Fl l +The shell is a login shell (only applicable if +.Fl l +is the only flag specified). +.It Fl n +Commands are parsed, but not executed. +This aids in syntactic checking of shell scripts. +.It Fl s +Command input is taken from the standard input. +.It Fl t +A single line of input is read and executed. +A +.Ql \e +may be used to escape the newline at the end of this +line and continue onto another line. +.It Fl v +Causes the +.Ar verbose +variable to be set, with the effect +that command input is echoed after history substitution. +.It Fl x +Causes the +.Ar echo +variable to be set, so that commands are echoed immediately before execution. +.It Fl V +Causes the +.Ar verbose +variable to be set even before +.Pa .cshrc +is executed. +.It Fl X +Is to +.Fl x +as +.Fl V +is to +.Fl v . +.El +.Pp +After processing of flag arguments, if arguments remain but none of the +.Fl c , +.Fl i , +.Fl s , +or +.Fl t +options were given, the first argument is taken as the name of a file of +commands to be executed. +The shell opens this file, and saves its name for possible resubstitution +by `$0'. +Since many systems use either the standard version 6 or version 7 shells +whose shell scripts are not compatible with this shell, the shell will +execute such a `standard' shell if the first character of a script +is not a `#', i.e., if the script does not start with a comment. +Remaining arguments initialize the variable +.Ar argv . +.Pp +An instance of +.Nm csh +begins by executing commands from the file +.Pa /etc/csh.cshrc +and, +if this is a login shell, +.Pa \&/etc/csh.login . +It then executes +commands from +.Pa \&.cshrc +in the +.Ar home +directory of the invoker, and, if this is a login shell, the file +.Pa \&.login +in the same location. +It is typical for users on crt's to put the command ``stty crt'' +in their +.Pa \&.login +file, and to also invoke +.Xr tset 1 +there. +.Pp +In the normal case, the shell will begin reading commands from the +terminal, prompting with `% '. +Processing of arguments and the use of the shell to process files +containing command scripts will be described later. +.Pp +The shell repeatedly performs the following actions: +a line of command input is read and broken into +.Ar words . +This sequence of words is placed on the command history list and parsed. +Finally each command in the current line is executed. +.Pp +When a login shell terminates it executes commands from the files +.Pa .logout +in the user's +.Ar home +directory and +.Pa /etc/csh.logout . +.Ss Lexical structure +The shell splits input lines into words at blanks and tabs with the +following exceptions. +The characters +`&' `\&|' `;' `<' `>' `(' `)' +form separate words. +If doubled in `&&', `\&|\&|', `<<' or `>>' these pairs form single words. +These parser metacharacters may be made part of other words, or prevented their +special meaning, by preceding them with `\e'. +A newline preceded by a `\e' is equivalent to a blank. +.Pp +Strings enclosed in matched pairs of quotations, +`'\|', `\*(ga' or `"', +form parts of a word; metacharacters in these strings, including blanks +and tabs, do not form separate words. +These quotations have semantics to be described later. +Within pairs of `\'' or `"' characters, a newline preceded by a `\e' gives +a true newline character. +.Pp +When the shell's input is not a terminal, +the character `#' introduces a comment that continues to the end of the +input line. +It is prevented this special meaning when preceded by `\e' +and in quotations using `\`', `\'', and `"'. +.Ss Commands +A simple command is a sequence of words, the first of which +specifies the command to be executed. +A simple command or +a sequence of simple commands separated by `\&|' characters +forms a pipeline. +The output of each command in a pipeline is connected to the input of the next. +Sequences of pipelines may be separated by `;', and are then executed +sequentially. +A sequence of pipelines may be executed without immediately +waiting for it to terminate by following it with an `&'. +.Pp +Any of the above may be placed in `(' `)' to form a simple command (that +may be a component of a pipeline, etc.). +It is also possible to separate pipelines with `\&|\&|' or `&&' showing, +as in the C language, +that the second is to be executed only if the first fails or succeeds +respectively. (See +.Em Expressions . ) +.Ss Jobs +The shell associates a +.Ar job +with each pipeline. It keeps +a table of current jobs, printed by the +.Ar jobs +command, and assigns them small integer numbers. When +a job is started asynchronously with `&', the shell prints a line that looks +like: +.Bd -filled -offset indent +.Op 1 +1234 +.Ed +.Pp +showing that the job which was started asynchronously was job number +1 and had one (top-level) process, whose process id was 1234. +.Pp +If you are running a job and wish to do something else you may hit the key +.Nm ^Z +(control-Z) which sends a STOP signal to the current job. +The shell will then normally show that the job has been `Stopped', +and print another prompt. You can then manipulate the state of this job, +putting it in the +.Em background +with the +.Ar bg +command, or run some other +commands and eventually bring the job back into the foreground with +the +.Em foreground +command +.Ar fg . +A +.Nm ^Z +takes effect immediately and +is like an interrupt in that pending output and unread input are discarded +when it is typed. There is another special key +.Nm ^Y +that does not generate a STOP signal until a program attempts to +.Xr read 2 +it. +This request can usefully be typed ahead when you have prepared some commands +for a job that you wish to stop after it has read them. +.Pp +A job being run in the background will stop if it tries to read +from the terminal. Background jobs are normally allowed to produce output, +but this can be disabled by giving the command ``stty tostop''. +If you set this +tty option, then background jobs will stop when they try to produce +output like they do when they try to read input. +.Pp +There are several ways to refer to jobs in the shell. The character +`%' introduces a job name. If you wish to refer to job number 1, you can +name it as `%1'. Just naming a job brings it to the foreground; thus +`%1' is a synonym for `fg %1', bringing job number 1 back into the foreground. +Similarly saying `%1 &' resumes job number 1 in the background. +Jobs can also be named by prefixes of the string typed in to start them, +if these prefixes are unambiguous, thus `%ex' would normally restart +a suspended +.Xr ex 1 +job, if there were only one suspended job whose name began with +the string `ex'. It is also possible to say `%?string' +which specifies a job whose text contains +.Ar string , +if there is only one such job. +.Pp +The shell maintains a notion of the current and previous jobs. +In output about jobs, the current job is marked with a `+' +and the previous job with a `\-'. The abbreviation `%+' refers +to the current job and `%\-' refers to the previous job. For close +analogy with the syntax of the +.Ar history +mechanism (described below), +`%%' is also a synonym for the current job. +.Pp +The job control mechanism requires that the +.Xr stty 1 +option +.Ic new +be set. It is an artifact from a +.Em new +implementation +of the +tty driver that allows generation of interrupt characters from +the keyboard to tell jobs to stop. See stty(1) for details +on setting options in the new tty driver. +.Ss Status reporting +This shell learns immediately whenever a process changes state. +It normally informs you whenever a job becomes blocked so that +no further progress is possible, but only just before it prints +a prompt. This is done so that it does not otherwise disturb your work. +If, however, you set the shell variable +.Ar notify , +the shell will notify you immediately of changes of status in background +jobs. +There is also a shell command +.Ar notify +that marks a single process so that its status changes will be immediately +reported. By default +.Ar notify +marks the current process; +simply say `notify' after starting a background job to mark it. +.Pp +When you try to leave the shell while jobs are stopped, you will +be warned that `You have stopped jobs.' You may use the +.Ar jobs +command to see what they are. If you do this or immediately try to +exit again, the shell will not warn you a second time, and the suspended +jobs will be terminated. +.Ss File Name Completion +When the file name completion feature is enabled by setting +the shell variable +.Ar filec +(see +.Ic set ) , +.Nm csh +will +interactively complete file names and user names from unique +prefixes, when they are input from the terminal followed by +the escape character (the escape key, or control-[) +For example, +if the current directory looks like +.Bd -literal -offset indent +DSC.OLD bin cmd lib xmpl.c +DSC.NEW chaosnet cmtest mail xmpl.o +bench class dev mbox xmpl.out +.Ed +.Pp +and the input is +.Pp +.Dl % vi ch<escape> +.Pp +.Nm csh +will complete the prefix ``ch'' +to the only matching file name ``chaosnet'', changing the input +line to +.Pp +.Dl % vi chaosnet +.Pp +However, given +.Pp +.Dl % vi D<escape> +.Pp +.Nm csh +will only expand the input to +.Pp +.Dl % vi DSC. +.Pp +and will sound the terminal bell to indicate that the expansion is +incomplete, since there are two file names matching the prefix ``D''. +.Pp +If a partial file name is followed by the end-of-file character +(usually control-D), then, instead of completing the name, +.Nm csh +will list all file names matching the prefix. For example, +the input +.Pp +.Dl % vi D<control-D> +.Pp +causes all files beginning with ``D'' to be listed: +.Pp +.Dl DSC.NEW DSC.OLD +.Pp +while the input line remains unchanged. +.Pp +The same system of escape and end-of-file can also be used to +expand partial user names, if the word to be completed +(or listed) begins with the character ``~''. For example, +typing +.Pp +.Dl cd ~ro<escape> +.Pp +may produce the expansion +.Pp +.Dl cd ~root +.Pp +The use of the terminal bell to signal errors or multiple matches +can be inhibited by setting the variable +.Ar nobeep . +.Pp +Normally, all files in the particular directory are candidates +for name completion. Files with certain suffixes can be excluded +from consideration by setting the variable +.Ar fignore +to the +list of suffixes to be ignored. Thus, if +.Ar fignore +is set by +the command +.Pp +.Dl % set fignore = (.o .out) +.Pp +then typing +.Pp +.Dl % vi x<escape> +.Pp +would result in the completion to +.Pp +.Dl % vi xmpl.c +.Pp +ignoring the files "xmpl.o" and "xmpl.out". +However, if the only completion possible requires not ignoring these +suffixes, then they are not ignored. In addition, +.Ar fignore +does not affect the listing of file names by control-D. All files +are listed regardless of their suffixes. +.Ss Substitutions +We now describe the various transformations the shell performs on the +input in the order in which they occur. +.Ss History substitutions +History substitutions place words from previous command input as portions +of new commands, making it easy to repeat commands, repeat arguments +of a previous command in the current command, or fix spelling mistakes +in the previous command with little typing and a high degree of confidence. +History substitutions begin with the character `!' and may begin +.Ar anywhere +in the input stream (with the proviso that they +.Nm "do not" +nest.) +This `!' may be preceded by a `\e' to prevent its special meaning; for +convenience, an `!' is passed unchanged when it is followed by a blank, +tab, newline, `=' or `('. +(History substitutions also occur when an input line begins with `\*(ua'. +This special abbreviation will be described later.) +Any input line that contains history substitution is echoed on the terminal +before it is executed as it could have been typed without history substitution. +.Pp +Commands input from the terminal that consist of one or more words +are saved on the history list. +The history substitutions reintroduce sequences of words from these +saved commands into the input stream. +The size of the history list is controlled by the +.Ar history +variable; the previous command is always retained, +regardless of the value of the history variable. +Commands are numbered sequentially from 1. +.Pp +For definiteness, consider the following output from the +.Ar history +command: +.Bd -literal -offset indent +\09 write michael +10 ex write.c +11 cat oldwrite.c +12 diff *write.c +.Ed +.Pp +The commands are shown with their event numbers. +It is not usually necessary to use event numbers, but the current event +number can be made part of the +.Ar prompt +by placing an `!' in the prompt string. +.Pp +With the current event 13 we can refer to previous events by event +number `!11', relatively as in `!\-2' (referring to the same event), +by a prefix of a command word +as in `!d' for event 12 or `!wri' for event 9, or by a string contained in +a word in the command as in `!?mic?' also referring to event 9. +These forms, without further change, simply reintroduce the words +of the specified events, each separated by a single blank. +As a special case, `!!' refers to the previous command; thus `!!' +alone is a +.Ar redo . +.Pp +To select words from an event we can follow the event specification by +a `:' and a designator for the desired words. +The words of an input line are numbered from 0, +the first (usually command) word being 0, the second word (first argument) +being 1, etc. +The basic word designators are: +.Pp +.Bl -tag -width Ds -compact -offset indent +.It \&0 +first (command) word +.It Ar n +.Ar n Ns 'th +argument +.It \*(ua +first argument, i.e., `1' +.It $ +last argument +.It % +word matched by (immediately preceding) +.No \&? Ns Ar s Ns \&? +search +.It Ar \&x\-y +range of words +.It Ar \&\-y +abbreviates +.Ar `\&0\-y\' +.It * +abbreviates `\*(ua\-$', or nothing if only 1 word in event +.It Ar x* +abbreviates +.Ar `x\-$\' +.It Ar x\- +like +.Ar `x*\' +but omitting word `$' +.El +.Pp +The `:' separating the event specification from the word designator +can be omitted if the argument selector begins with a `\*(ua', `$', `*' +`\-' or `%'. +After the optional word designator can be +placed a sequence of modifiers, each preceded by a `:'. +The following modifiers are defined: +.Pp +.Bl -tag -width Ds -compact -offset indent +.It h +Remove a trailing pathname component, leaving the head. +.It r +Remove a trailing `.xxx' component, leaving the root name. +.It e +Remove all but the extension `.xxx' part. +.It s Ns Ar /l/r/ +Substitute +.Ar l +for +.Ar r +.It t +Remove all leading pathname components, leaving the tail. +.It \&& +Repeat the previous substitution. +.It g +Apply the change once on each word, prefixing the above, e.g., `g&'. +.It a +Apply the change as many times as possible on a single word, prefixing +the above. It can be used together with `g' to apply a substitution +globally. +.It p +Print the new command line but do not execute it. +.It q +Quote the substituted words, preventing further substitutions. +.It x +Like q, but break into words at blanks, tabs and newlines. +.El +.Pp +Unless preceded by a `g' the change is applied only to the first +modifiable word. With substitutions, it is an error for no word to be +applicable. +.Pp +The left hand side of substitutions are not regular expressions in the sense +of the editors, but instead strings. +Any character may be used as the delimiter in place of `/'; +a `\e' quotes the delimiter into the +.Ar l " " +and +.Ar r " " +strings. +The character `&' in the right hand side is replaced by the text from +the left. +A `\e' also quotes `&'. +A null +.Ar l +(`//') +uses the previous string either from an +.Ar l +or from a +contextual scan string +.Ar s +in +.No \&`!? Ns Ar s Ns \e?' . +The trailing delimiter in the substitution may be omitted if a newline +follows immediately as may the trailing `?' in a contextual scan. +.Pp +A history reference may be given without an event specification, e.g., `!$'. +Here, the reference is to the previous command unless a previous +history reference occurred on the same line in which case this form repeats +the previous reference. +Thus `!?foo?\*(ua !$' gives the first and last arguments +from the command matching `?foo?'. +.Pp +A special abbreviation of a history reference occurs when the first +non-blank character of an input line is a `\*(ua'. +This is equivalent to `!:s\*(ua' providing a convenient shorthand for substitutions +on the text of the previous line. +Thus `\*(ualb\*(ualib' fixes the spelling of +`lib' +in the previous command. +Finally, a history substitution may be surrounded with `{' and `}' +if necessary to insulate it from the characters that follow. +Thus, after `ls \-ld ~paul' we might do `!{l}a' to do `ls \-ld ~paula', +while `!la' would look for a command starting with `la'. +.Pp +.Ss Quotations with \' and \&" +The quotation of strings by `\'' and `"' can be used +to prevent all or some of the remaining substitutions. +Strings enclosed in `\'' are prevented any further interpretation. +Strings enclosed in `"' may be expanded as described below. +.Pp +In both cases the resulting text becomes (all or part of) a single word; +only in one special case (see +.Em Command Substitution +below) does a `"' quoted string yield parts of more than one word; +`\'' quoted strings never do. +.Ss Alias substitution +The shell maintains a list of aliases that can be established, displayed +and modified by the +.Ar alias +and +.Ar unalias +commands. +After a command line is scanned, it is parsed into distinct commands and +the first word of each command, left-to-right, is checked to see if it +has an alias. +If it does, then the text that is the alias for that command is reread +with the history mechanism available +as though that command were the previous input line. +The resulting words replace the +command and argument list. +If no reference is made to the history list, then the argument list is +left unchanged. +.Pp +Thus if the alias for `ls' is `ls \-l' the command `ls /usr' would map to +`ls \-l /usr', the argument list here being undisturbed. +Similarly if the alias for `lookup' was `grep !\*(ua /etc/passwd' then +`lookup bill' would map to `grep bill /etc/passwd'. +.Pp +If an alias is found, the word transformation of the input text +is performed and the aliasing process begins again on the reformed input line. +Looping is prevented if the first word of the new text is the same as the old +by flagging it to prevent further aliasing. +Other loops are detected and cause an error. +.Pp +Note that the mechanism allows aliases to introduce parser metasyntax. +Thus, we can `alias print \'pr \e!* \&| lpr\'' to make a command that +.Ar pr Ns 's +its arguments to the line printer. +.Ss Variable substitution +The shell maintains a set of variables, each of which has as value a list +of zero or more words. +Some of these variables are set by the shell or referred to by it. +For instance, the +.Ar argv +variable is an image of the shell's argument list, and words of this +variable's value are referred to in special ways. +.Pp +The values of variables may be displayed and changed by using the +.Ar set +and +.Ar unset +commands. +Of the variables referred to by the shell a number are toggles; +the shell does not care what their value is, +only whether they are set or not. +For instance, the +.Ar verbose +variable is a toggle that causes command input to be echoed. +The setting of this variable results from the +.Fl v +command line option. +.Pp +Other operations treat variables numerically. +The `@' command permits numeric calculations to be performed and the result +assigned to a variable. +Variable values are, however, always represented as (zero or more) strings. +For the purposes of numeric operations, the null string is considered to be +zero, and the second and additional words of multiword values are ignored. +.Pp +After the input line is aliased and parsed, and before each command +is executed, variable substitution +is performed keyed by `$' characters. +This expansion can be prevented by preceding the `$' with a `\e' except +within `"'s where it +.Em always +occurs, and within `\''s where it +.Em never +occurs. +Strings quoted by `\*(ga' are interpreted later (see +.Nm "Command substitution" +below) so `$' substitution does not occur there until later, if at all. +A `$' is passed unchanged if followed by a blank, tab, or end-of-line. +.Pp +Input/output redirections are recognized before variable expansion, +and are variable expanded separately. +Otherwise, the command name and entire argument list are expanded together. +It is thus possible for the first (command) word (to this point) to generate +more than one word, the first of which becomes the command name, +and the rest of which become arguments. +.Pp +Unless enclosed in `"' or given the `:q' modifier the results of variable +substitution may eventually be command and filename substituted. +Within `"', a variable whose value consists of multiple words expands to a +(portion of) a single word, with the words of the variables value +separated by blanks. +When the `:q' modifier is applied to a substitution +the variable will expand to multiple words with each word separated +by a blank and quoted to prevent later command or filename substitution. +.Pp +The following metasequences are provided for introducing variable values into +the shell input. +Except as noted, it is an error to reference a variable that is not set. +.Pp +.Bl -tag -width Ds -compact -offset indent +.It $name +.It ${name} +Are replaced by the words of the value of variable +.Ar name , +each separated by a blank. +Braces insulate +.Ar name +from following characters that would otherwise be part of it. +Shell variables have names consisting of up to 20 letters and digits +starting with a letter. The underscore character is considered a letter. +If +.Ar name +is not a shell variable, but is set in the environment, then +that value is returned (but +.Nm : +modifiers and the other forms +given below are not available here). +.It $name Ns Op selector +.It ${name Ns [ selector ] } +May be used to select only some of the words from the value of +.Ar name . +The selector is subjected to `$' substitution and may consist of a single +number or two numbers separated by a `\-'. +The first word of a variables value is numbered `1'. +If the first number of a range is omitted it defaults to `1'. +If the last number of a range is omitted it defaults to `$#name'. +The selector `*' selects all words. +It is not an error for a range to be empty if the second argument is omitted +or in range. +.It $#name +.It ${#name} +Gives the number of words in the variable. +This is useful for later use in a +`$argv[selector]'. +.It $0 +Substitutes the name of the file from which command input is being read. +An error occurs if the name is not known. +.It $number +.It ${number} +Equivalent to +`$argv[number]'. +.It $* +Equivalent to +`$argv[*]'. +The modifiers `:e', `:h', `:t', `:r', `:q' and `:x' may be applied to +the substitutions above as may `:gh', `:gt' and `:gr'. +If braces `{' '}' appear in the command form then the modifiers +must appear within the braces. +The current implementation allows only one `:' modifier on each `$' expansion. +.El +.Pp +The following substitutions may not be modified with `:' modifiers. +.Bl -tag -width Ds -compact -offset indent +.It $?name +.It ${?name} +Substitutes the string `1' if name is set, `0' if it is not. +.It $?0 +Substitutes `1' if the current input filename is known, `0' if it is not. +.It \&$\&$\& +Substitute the (decimal) process number of the (parent) shell. +.It $! +Substitute the (decimal) process number of the last background process +started by this shell. +.It $< +Substitutes a line from the standard +input, with no further interpretation. +It can be used to read from the keyboard in a shell script. +.El +.Ss Command and filename substitution +The remaining substitutions, command and filename substitution, +are applied selectively to the arguments of builtin commands. +By selectively, we mean that portions of expressions which are +not evaluated are not subjected to these expansions. +For commands that are not internal to the shell, the command +name is substituted separately from the argument list. +This occurs very late, +after input-output redirection is performed, and in a child +of the main shell. +.Ss Command substitution +Command substitution is shown by a command enclosed in `\*(ga'. +The output from such a command is normally broken into separate words +at blanks, tabs and newlines, with null words being discarded; +this text then replaces the original string. +Within `"'s, only newlines force new words; blanks and tabs are preserved. +.Pp +In any case, the single final newline does not force a new word. +Note that it is thus possible for a command substitution to yield +only part of a word, even if the command outputs a complete line. +.Ss Filename substitution +If a word contains any of the characters `*', `?', `[' or `{' +or begins with the character `~', then that word is a candidate for +filename substitution, also known as `globbing'. +This word is then regarded as a pattern, and replaced with an alphabetically +sorted list of file names that match the pattern. +In a list of words specifying filename substitution it is an error for +no pattern to match an existing file name, but it is not required +for each pattern to match. +Only the metacharacters `*', `?' and `[' imply pattern matching, +the characters `~' and `{' being more akin to abbreviations. +.Pp +In matching filenames, the character `.' at the beginning of a filename +or immediately following a `/', as well as the character `/' must +be matched explicitly. +The character `*' matches any string of characters, including the null +string. +The character `?' matches any single character. +The sequence +.Sq Op ... +matches any one of the characters enclosed. +Within +.Sq Op ... , +a pair of characters separated by `\-' matches any character lexically between +the two (inclusive). +.Pp +The character `~' at the beginning of a filename refers to home +directories. +Standing alone, i.e., `~' it expands to the invokers home directory as reflected +in the value of the variable +.Ar home . +When followed by a name consisting of letters, digits and `\-' characters, +the shell searches for a user with that name and substitutes their +home directory; thus `~ken' might expand to `/usr/ken' and `~ken/chmach' +to `/usr/ken/chmach'. +If the character `~' is followed by a character other than a letter or `/' +or does not appear at the beginning of a word, +it is left undisturbed. +.Pp +The metanotation `a{b,c,d}e' is a shorthand for `abe ace ade'. +Left to right order is preserved, with results of matches being sorted +separately at a low level to preserve this order. +This construct may be nested. +Thus, `~source/s1/{oldls,ls}.c' expands to +`/usr/source/s1/oldls.c /usr/source/s1/ls.c' +without chance of error +if the home directory for `source' is `/usr/source'. +Similarly `../{memo,*box}' might expand to `../memo ../box ../mbox'. +(Note that `memo' was not sorted with the results of the match to `*box'.) +As a special case `{', `}' and `{}' are passed undisturbed. +.Ss Input/output +The standard input and the standard output of a command may be redirected +with the following syntax: +.Pp +.Bl -tag -width Ds -compact -offset indent +.It < name +Open file +.Ar name +(which is first variable, command and filename expanded) as the standard +input. +.It << word +Read the shell input up to a line that is identical to +.Ar word . +.Ar Word +is not subjected to variable, filename or command substitution, +and each input line is compared to +.Ar word +before any substitutions are done on the input line. +Unless a quoting `\e', `"', `\*(aa' or `\*(ga' appears in +.Ar word , +variable and command substitution is performed on the intervening lines, +allowing `\e' to quote `$', `\e' and `\*(ga'. +Commands that are substituted have all blanks, tabs, and newlines +preserved, except for the final newline which is dropped. +The resultant text is placed in an anonymous temporary file that +is given to the command as its standard input. +.It > name +.It >! name +.It >& name +.It >&! name +The file +.Ar name +is used as the standard output. +If the file does not exist then it is created; +if the file exists, it is truncated; its previous contents are lost. +.Pp +If the variable +.Ar noclobber +is set, then the file must not exist or be a character special file (e.g., a +terminal or `/dev/null') or an error results. +This helps prevent accidental destruction of files. +Here, the `!' forms can be used to suppress this check. +.Pp +The forms involving `&' route the standard error output into the specified +file as well as the standard output. +.Ar Name +is expanded in the same way as `<' input filenames are. +.It >> name +.It >>& name +.It >>! name +.It >>&! name +Uses file +.Ar name +as the standard output; +like `>' but places output at the end of the file. +If the variable +.Ar noclobber +is set, then it is an error for the file not to exist unless +one of the `!' forms is given. +Otherwise similar to `>'. +.El +.Pp +A command receives the environment in which the shell was +invoked as modified by the input-output parameters and +the presence of the command in a pipeline. +Thus, unlike some previous shells, commands run from a file of shell commands +have no access to the text of the commands by default; +instead they receive the original standard input of the shell. +The `<<' mechanism should be used to present inline data. +This permits shell command scripts to function as components of pipelines +and allows the shell to block read its input. +Note that the default standard input for a command run detached is +.Ar not +modified to be the empty file +.Pa /dev/null ; +instead the standard input +remains as the original standard input of the shell. If this is a terminal +and if the process attempts to read from the terminal, then the process +will block and the user will be notified (see +.Sx Jobs +above). +.Pp +The standard error output may be directed through +a pipe with the standard output. +Simply use the form `\&|&' instead of just `\&|'. +.Ss Expressions +Several of the builtin commands (to be described later) +take expressions, in which the operators are similar to those of C, with +the same precedence. +These expressions appear in the +.Nm @, +.Ar exit , +.Ar if , +and +.Ar while +commands. +The following operators are available: +.Bd -ragged -offset indent +\&|\&| && \&| \*(ua & == != =~ !~ <= >= +< > << >> + \- * / % ! ~ ( ) +.Ed +.Pp +Here the precedence increases to the right, +`==' `!=' `=~' and `!~', `<=' `>=' `<' and `>', `<<' and `>>', `+' and `\-', +`*' `/' and `%' being, in groups, at the same level. +The `==' `!=' `=~' and `!~' operators compare their arguments as strings; +all others operate on numbers. +The operators `=~' and `!~' are like `!=' and `==' except that the right +hand side is a +.Ar pattern +(containing, e.g., `*'s, `?'s and instances of `[...]') +against which the left hand operand is matched. This reduces the +need for use of the +.Ar switch +statement in shell scripts when all that is really needed is pattern matching. +.Pp +Strings that begin with `0' are considered octal numbers. +Null or missing arguments are considered `0'. +The result of all expressions are strings, +which represent decimal numbers. +It is important to note that no two components of an expression can appear +in the same word; except when adjacent to components of expressions that +are syntactically significant to the parser (`&' `\&|' `<' `>' `(' `)'), +they should be surrounded by spaces. +.Pp +Also available in expressions as primitive operands are command executions +enclosed in `{' and `}' +and file enquiries of the form +.Fl l +.Ar name +where +.Ic l +is one of: +.Bd -literal -offset indent +r read access +w write access +x execute access +e existence +o ownership +z zero size +f plain file +d directory +.Ed +.Pp +The specified name is command and filename expanded and then tested +to see if it has the specified relationship to the real user. +If the file does not exist or is inaccessible then all enquiries return +false, i.e., `0'. +Command executions succeed, returning true, i.e., `1', +if the command exits with status 0, otherwise they fail, returning +false, i.e., `0'. +If more detailed status information is required then the command +should be executed outside an expression and the variable +.Ar status +examined. +.Ss Control flow +The shell contains several commands that can be used to regulate the +flow of control in command files (shell scripts) and +(in limited but useful ways) from terminal input. +These commands all operate by forcing the shell to reread or skip in its +input and, because of the implementation, restrict the placement of some +of the commands. +.Pp +The +.Ic foreach , +.Ic switch , +and +.Ic while +statements, as well as the +.Ic if\-then\-else +form of the +.Ic if +statement require that the major keywords appear in a single simple command +on an input line as shown below. +.Pp +If the shell's input is not seekable, +the shell buffers up input whenever a loop is being read +and performs seeks in this internal buffer to accomplish the rereading +implied by the loop. +(To the extent that this allows, backward goto's will succeed on +non-seekable inputs.) +.Ss Builtin commands +Builtin commands are executed within the shell. +If a builtin command occurs as any component of a pipeline +except the last then it is executed in a subshell. +.Pp +.Bl -tag -width Ds -compact -offset indent +.It Ic alias +.It Ic alias Ar name +.It Ic alias Ar name wordlist +The first form prints all aliases. +The second form prints the alias for name. +The final form assigns the specified +.Ar wordlist +as the alias of +.Ar name ; +.Ar wordlist +is command and filename substituted. +.Ar Name +is not allowed to be +.Ar alias +or +.Ar unalias . +.Pp +.It Ic alloc +Shows the amount of dynamic memory acquired, broken down into used and +free memory. +With an argument shows the number of free and used blocks in each size +category. The categories start at size 8 and double at each step. +This command's output may vary across system types, since +systems other than the VAX may use a different memory allocator. +.Pp +.It Ic bg +.It Ic bg \&% Ns Ar job ... +Puts the current or specified jobs into the background, continuing them +if they were stopped. +.Pp +.It Ic break +Causes execution to resume after the +.Ic end +of the nearest enclosing +.Ic foreach +or +.Ic while . +The remaining commands on the current line are executed. +Multi-level breaks are thus possible by writing them all on one line. +.Pp +.It Ic breaksw +Causes a break from a +.Ic switch , +resuming after the +.Ic endsw . +.Pp +.It Ic case Ar label : +A label in a +.Ic switch +statement as discussed below. +.Pp +.It Ic cd +.It Ic cd Ar name +.It Ic chdir +.It Ic chdir Ar name +Change the shell's working directory to directory +.Ar name . +If no argument is given then change to the home directory of the user. +If +.Ar name +is not found as a subdirectory of the current directory (and does not begin +with `/', `./' or `../'), then each +component of the variable +.Ic cdpath +is checked to see if it has a subdirectory +.Ar name . +Finally, if all else fails but +.Ar name +is a shell variable whose value begins with `/', then this +is tried to see if it is a directory. +.Pp +.It Ic continue +Continue execution of the nearest enclosing +.Ic while +or +.Ic foreach . +The rest of the commands on the current line are executed. +.Pp +.It Ic default : +Labels the default case in a +.Ic switch +statement. +The default should come after all +.Ic case +labels. +.Pp +.It Ic dirs +Prints the directory stack; the top of the stack is at the left, +the first directory in the stack being the current directory. +.Pp +.It Ic echo Ar wordlist +.It Ic echo Fl n Ar wordlist +The specified words are written to the shell's standard output, separated +by spaces, and terminated with a newline unless the +.Fl n +option is specified. +.Pp +.It Ic else +.It Ic end +.It Ic endif +.It Ic endsw +See the description of the +.Ic foreach , +.Ic if , +.Ic switch , +and +.Ic while +statements below. +.Pp +.It Ic eval Ar arg ... +(As in +.Xr sh 1 . ) +The arguments are read as input to the shell and the resulting +command(s) executed in the context of the current shell. +This is usually used to execute commands +generated as the result of command or variable substitution, since +parsing occurs before these substitutions. See +.Xr tset 1 +for an example of using +.Ic eval . +.Pp +.It Ic exec Ar command +The specified command is executed in place of the current shell. +.Pp +.It Ic exit +.It Ic exit Ar (expr ) +The shell exits either with the value of the +.Ic status +variable (first form) or with the value of the specified +.Ic expr +(second form). +.Pp +.It Ic fg +.It Ic fg % Ns Ar job ... +Brings the current or specified jobs into the foreground, continuing them if +they were stopped. +.Pp +.It Ic foreach Ar name (wordlist) +.It ... +.It Ic end +The variable +.Ic name +is successively set to each member of +.Ic wordlist +and the sequence of commands between this command and the matching +.Ic end +are executed. +(Both +.Ic foreach +and +.Ic end +must appear alone on separate lines.) +The builtin command +.Ic continue +may be used to continue the loop prematurely and the builtin +command +.Ic break +to terminate it prematurely. +When this command is read from the terminal, the loop is read once +prompting with `?' before any statements in the loop are executed. +If you make a mistake typing in a loop at the terminal you can rub it out. +.Pp +.It Ic glob Ar wordlist +Like +.Ic echo +but no `\e' escapes are recognized and words are delimited +by null characters in the output. +Useful for programs that wish to use the shell to filename expand a list +of words. +.Pp +.It Ic goto Ar word +The specified +.Ic word +is filename and command expanded to yield a string of the form `label'. +The shell rewinds its input as much as possible +and searches for a line of the form `label:' +possibly preceded by blanks or tabs. +Execution continues after the specified line. +.Pp +.It Ic hashstat +Print a statistics line showing how effective the internal hash +table has been at locating commands (and avoiding +.Ic exec Ns \'s ) . +An +.Ic exec +is attempted for each component of the +.Em path +where the hash function indicates a possible hit, and in each component +that does not begin with a `/'. +.Pp +.It Ic history +.It Ic history Ar n +.It Ic history Fl r Ar n +.It Ic history Fl h Ar n +Displays the history event list; if +.Ar n +is given only the +.Ar n +most recent events are printed. +The +.Fl r +option reverses the order of printout to be most recent first +instead of oldest first. +The +.Fl h +option causes the history list to be printed without leading numbers. +This format produces files suitable for sourcing using the \-h +option to +.Ic source . +.Pp +.It Ic if ( Ar expr ) No command +If the specified expression evaluates true, then the single +.Ar command +with arguments is executed. +Variable substitution on +.Ar command +happens early, at the same +time it does for the rest of the +.Ic if +command. +.Ar Command +must be a simple command, not +a pipeline, a command list, or a parenthesized command list. +Input/output redirection occurs even if +.Ar expr +is false, i.e., when command is +.Sy not +executed (this is a bug). +.Pp +.It Ic if ( Ar expr ) Ic then +.It ... +.It Ic else if ( Ar expr2 ) Ic then +.It ... +.It Ic else +.It ... +.It Ic endif +If the specified +.Ar expr +is true then the commands up to the first +.Ic else +are executed; otherwise if +.Ar expr2 +is true then the commands up to the +second +.Ic else +are executed, etc. +Any number of +.Ic else-if +pairs are possible; only one +.Ic endif +is needed. +The +.Ic else +part is likewise optional. +(The words +.Ic else +and +.Ic endif +must appear at the beginning of input lines; +the +.Ic if +must appear alone on its input line or after an +.Ic else . ) +.Pp +.It Ic jobs +.It Ic jobs Fl l +Lists the active jobs; the +.Fl l +option lists process id's in addition to the normal information. +.Pp +.It Ic kill % Ns Ar job +.It Ic kill Ar pid +.It Ic kill Fl sig Ar pid ... +.It Ic kill Fl l +Sends either the TERM (terminate) signal or the +specified signal to the specified jobs or processes. +Signals are either given by number or by names (as given in +.Pa /usr/include/signal.h, +stripped of the prefix ``SIG''). +The signal names are listed by ``kill \-l''. +There is no default, just saying `kill' does not +send a signal to the current job. +If the signal being sent is TERM (terminate) or HUP (hangup), +then the job or process will be sent a CONT (continue) signal as well. +.Pp +.It Ic limit +.It Ic limit Ar resource +.It Ic limit Ar resource maximum-use +.It Ic limit Fl h +.It Ic limit Fl h Ar resource +.It Ic limit Fl h Ar resource maximum-use +Limits the consumption by the current process and each process +it creates to not individually exceed +.Ar maximum-use +on the +specified +.Ar resource . +If no +.Ar maximum-use +is given, then +the current limit is printed; if no +.Ar resource +is given, then +all limitations are given. If the +.Fl h +flag is given, the hard limits are used instead of the current +limits. The hard limits impose a ceiling on the values of +the current limits. Only the super-user may raise the hard limits, +but a user may lower or raise the current limits within the legal range. +.Pp +Resources controllable currently include +.Ar cputime +(the maximum +number of cpu-seconds to be used by each process), +.Ar filesize +(the largest single file that can be created), +.Ar datasize +(the maximum growth of the data+stack region via +.Xr sbrk 2 +beyond the end of the program text), +.Ar stacksize +(the maximum +size of the automatically-extended stack region), and +.Ar coredumpsize +(the size of the largest core dump that will be created). +.Pp +The +.Ar maximum-use +may be given as a (floating point or integer) +number followed by a scale factor. For all limits other than +.Ar cputime +the default scale is `k' or `kilobytes' (1024 bytes); +a scale factor of `m' or `megabytes' may also be used. +For +.Ar cputime +the default scale is `seconds'; +a scale factor of `m' for minutes +or `h' for hours, or a time of the form `mm:ss' giving minutes +and seconds also may be used. +.Pp +For both +.Ar resource +names and scale factors, unambiguous prefixes +of the names suffice. +.Pp +.It Ic login +Terminate a login shell, replacing it with an instance of +.Pa /bin/login. +This is one way to log off, included for compatibility with +.Xr sh 1 . +.Pp +.It Ic logout +Terminate a login shell. +Especially useful if +.Ic ignoreeof +is set. +.Pp +.It Ic nice +.It Ic nice Ar +number +.It Ic nice Ar command +.It Ic nice Ar +number command +The first form sets the +scheduling priority +for this shell to 4. +The second form sets the +priority +to the given +.Ar number . +The final two forms run command at priority 4 and +.Ar number +respectively. +The greater the number, the less cpu the process will get. +The super-user may specify negative priority by using `nice \-number ...'. +.Ar Command +is always executed in a sub-shell, and the restrictions +placed on commands in simple +.Ic if +statements apply. +.Pp +.It Ic nohup +.It Ic nohup Ar command +The first form can be used in shell scripts to cause hangups to be +ignored for the remainder of the script. +The second form causes the specified command to be run with hangups +ignored. +All processes detached with `&' are effectively +.Ic nohup Ns \'ed . +.Pp +.It Ic notify +.It Ic notify % Ns Ar job ... +Causes the shell to notify the user asynchronously when the status of the +current or specified jobs change; normally notification is presented +before a prompt. This is automatic if the shell variable +.Ic notify +is set. +.Pp +.It Ic onintr +.It Ic onintr Fl +.It Ic onintr Ar label +Control the action of the shell on interrupts. +The first form restores the default action of the shell on interrupts +which is to terminate shell scripts or to return to the terminal command +input level. +The second form `onintr \-' causes all interrupts to be ignored. +The final form causes the shell to execute a `goto label' when +an interrupt is received or a child process terminates because +it was interrupted. +.Pp +In any case, if the shell is running detached and interrupts are +being ignored, all forms of +.Ic onintr +have no meaning and interrupts +continue to be ignored by the shell and all invoked commands. +Finally +.Ic onintr +statements are ignored in the system startup files where interrupts +are disabled (/etc/csh.cshrc, /etc/csh.login). +.Pp +.It Ic popd +.It Ic popd Ar +n +Pops the directory stack, returning to the new top directory. +With an argument +.Ns \`+ Ar n Ns \' +discards the +.Ar n Ns \'th +entry in the stack. +The members of the directory stack are numbered from the top starting at 0. +.Pp +.It Ic pushd +.It Ic pushd Ar name +.It Ic pushd Ar n +With no arguments, +.Ic pushd +exchanges the top two elements of the directory stack. +Given a +.Ar name +argument, +.Ic pushd +changes to the new directory (ala +.Ic cd ) +and pushes the old current working directory +(as in +.Ic csw ) +onto the directory stack. +With a numeric argument, +.Ic pushd +rotates the +.Ar n Ns \'th +argument of the directory +stack around to be the top element and changes to it. The members +of the directory stack are numbered from the top starting at 0. +.Pp +.It Ic rehash +Causes the internal hash table of the contents of the directories in +the +.Ic path +variable to be recomputed. This is needed if new commands are added +to directories in the +.Ic path +while you are logged in. This should only be necessary if you add +commands to one of your own directories, or if a systems programmer +changes the contents of a system directory. +.Pp +.It Ic repeat Ar count command +The specified +.Ar command +which is subject to the same restrictions +as the +.Ar command +in the one line +.Ic if +statement above, +is executed +.Ar count +times. +I/O redirections occur exactly once, even if +.Ar count +is 0. +.Pp +.It Ic set +.It Ic set Ar name +.It Ic set Ar name Ns =word +.It Ic set Ar name[index] Ns =word +.It Ic set Ar name Ns =(wordlist) +The first form of the command shows the value of all shell variables. +Variables that have other than a single word as their +value print as a parenthesized word list. +The second form sets +.Ar name +to the null string. +The third form sets +.Ar name +to the single +.Ar word . +The fourth form sets +the +.Ar index Ns 'th +component of +.Ar name +to +.Ar word ; +this component must already exist. +The final form sets +.Ar name +to the list of words in +.Ar wordlist . +The value is always command and filename expanded. +.Pp +These arguments may be repeated to set multiple values in a single set command. +Note however, that variable expansion happens for all arguments before any +setting occurs. +.Pp +.It Ic setenv +.It Ic setenv Ar name +.It Ic setenv Ar name value +The first form lists all current environment variables. +It is equivalent to +.Xr printenv 1 . +The last form sets the value of environment variable +.Ar name +to be +.Ar value , +a single string. The second form sets +.Ar name +to an empty string. +The most commonly used environment variables +.Ev USER , +.Ev TERM , +and +.Ev PATH +are automatically imported to and exported from the +.Nm csh +variables +.Ar user , +.Ar term , +and +.Ar path ; +there is no need to use +.Ic setenv +for these. +.Pp +.It Ic shift +.It Ic shift Ar variable +The members of +.Ic argv +are shifted to the left, discarding +.Ic argv Ns Bq 1 . +It is an error for +.Ic argv +not to be set or to have less than one word as value. +The second form performs the same function on the specified variable. +.Pp +.It Ic source Ar name +.It Ic source Fl h Ar name +The shell reads commands from +.Ar name . +.Ic Source +commands may be nested; if they are nested too deeply the shell may +run out of file descriptors. +An error in a +.Ic source +at any level terminates all nested +.Ic source +commands. +Normally input during +.Ic source +commands is not placed on the history list; +the \-h option causes the commands to be placed on the +history list without being executed. +.Pp +.It Ic stop +.It Ic stop % Ns Ar job ... +Stops the current or specified jobs that are executing in the background. +.Pp +.It Ic suspend +Causes the shell to stop in its tracks, much as if it had been sent a stop +signal with +.Ic ^Z . +This is most often used to stop shells started by +.Xr su 1 . +.Pp +.It Ic switch Ar (string) +.It Ic case Ar str1 : +.It \ \ \ \ \&... +.It Ic \ \ \ \ breaksw +.It \ \ \ \ \&... +.It Ic default : +.It \ \ \ \ \&... +.It Ic \ \ \ \ breaksw +.It Ic endsw +Each case label is successively matched against the specified +.Ar string +which is first command and filename expanded. +The file metacharacters `*', `?' and `[...]' +may be used in the case labels, +which are variable expanded. +If none of the labels match before the `default' label is found, then +the execution begins after the default label. +Each case label and the default label must appear at the beginning of a line. +The command +.Ic breaksw +causes execution to continue after the +.Ic endsw . +Otherwise control may fall through case labels and the default label as in C. +If no label matches and there is no default, execution continues after +the +.Ic endsw . +.Pp +.It Ic time +.It Ic time Ar command +With no argument, a summary of time used by this shell and its children +is printed. +If arguments are given +the specified simple command is timed and a time summary +as described under the +.Ic time +variable is printed. If necessary, an extra shell is created to print the time +statistic when the command completes. +.Pp +.It Ic umask +.It Ic umask Ar value +The file creation mask is displayed (first form) or set to the specified +value (second form). The mask is given in octal. Common values for +the mask are 002 giving all access to the group and read and execute +access to others or 022 giving all access except write access for +users in the group or others. +.Pp +.It Ic unalias Ar pattern +All aliases whose names match the specified pattern are discarded. +Thus all aliases are removed by `unalias *'. +It is not an error for nothing to be +.Ic unaliased . +.Pp +.It Ic unhash +Use of the internal hash table to speed location of executed programs +is disabled. +.Pp +.It Ic unlimit +.It Ic unlimit Ar resource +.It Ic unlimit Fl h +.It Ic unlimit Fl h Ar resource +Removes the limitation on +.Ar resource . +If no +.Ar resource +is specified, then all +.Ar resource +limitations are removed. If +.Fl h +is given, the corresponding hard limits are removed. Only the +super-user may do this. +.Pp +.It Ic unset Ar pattern +All variables whose names match the specified pattern are removed. +Thus all variables are removed by `unset *'; this has noticeably +distasteful side-effects. +It is not an error for nothing to be +.Ic unset . +.Pp +.It Ic unsetenv Ar pattern +Removes all variables whose name match the specified pattern from the +environment. See also the +.Ic setenv +command above and +.Xr printenv 1 . +.Pp +.It Ic wait +Wait for all background jobs. +If the shell is interactive, then an interrupt can disrupt the wait. +After the interrupt, the shell prints names and job numbers of all jobs +known to be outstanding. +.It Ic which Ar command +Displays the resolved command that will be executed by the shell. +.Pp +.It Ic while Ar (expr) +.It \&... +.It Ic end +While the specified expression evaluates non-zero, the commands between +the +.Ic while +and the matching +.Ic end +are evaluated. +.Ic Break +and +.Ic continue +may be used to terminate or continue the loop prematurely. +(The +.Ic while +and +.Ic end +must appear alone on their input lines.) +Prompting occurs here the first time through the loop as for the +.Ic foreach +statement if the input is a terminal. +.Pp +.It Ic % Ns Ar job +Brings the specified job into the foreground. +.Pp +.It Ic % Ns Ar job Ic & +Continues the specified job in the background. +.Pp +.It Ic @ +.It Ic @ Ns Ar name Ns = expr +.It Ic @ Ns Ar name[index] Ns = expr +The first form prints the values of all the shell variables. +The second form sets the specified +.Ar name +to the value of +.Ar expr . +If the expression contains `<', `>', `&' or `|' then at least +this part of the expression must be placed within `(' `)'. +The third form assigns the value of +.Ar expr +to the +.Ar index Ns 'th +argument of +.Ar name . +Both +.Ar name +and its +.Ar index Ns 'th +component must already exist. +.El +.Pp +The operators `*=', `+=', etc are available as in C. +The space separating the name from the assignment operator is optional. +Spaces are, however, mandatory in separating components of +.Ar expr +which would otherwise be single words. +.Pp +Special postfix `+\|+' and `\-\|\-' operators increment and decrement +.Ar name +respectively, i.e., `@ i++'. +.Ss Pre-defined and environment variables +The following variables have special meaning to the shell. +Of these, +.Ar argv , +.Ar cwd, +.Ar home , +.Ar path, +.Ar prompt , +.Ar shell +and +.Ar status +are always set by the shell. +Except for +.Ar cwd +and +.Ar status , +this setting occurs only at initialization; +these variables will not then be modified unless done +explicitly by the user. +.Pp +The shell copies the environment variable +.Ev USER +into the variable +.Ar user , +.Ev TERM +into +.Ar term , +and +.Ev HOME +into +.Ar home , +and copies these back into the environment whenever the normal +shell variables are reset. +The environment variable +.Ev PATH +is likewise handled; it is not +necessary to worry about its setting other than in the file +.Ar \&.cshrc +as inferior +.Nm csh +processes will import the definition of +.Ar path +from the environment, and re-export it if you then change it. +.Bl -tag -width histchars +.It Ic argv +Set to the arguments to the shell, it is from this variable that +positional parameters are substituted, i.e., `$1' is replaced by +`$argv[1]', +etc. +.It Ic cdpath +Gives a list of alternate directories searched to find subdirectories +in +.Ar chdir +commands. +.It Ic cwd +The full pathname of the current directory. +.It Ic echo +Set when the +.Fl x +command line option is given. +Causes each command and its arguments +to be echoed just before it is executed. +For non-builtin commands all expansions occur before echoing. +Builtin commands are echoed before command and filename substitution, +since these substitutions are then done selectively. +.It Ic filec +Enable file name completion. +.It Ic histchars +Can be given a string value to change the characters used in history +substitution. The first character of its value is used as the +history substitution character, replacing the default character `!'. +The second character of its value replaces the character `\(ua' in +quick substitutions. +.It Ic histfile +Can be set to the pathname where history is going to be saved/restored. +.It Ic history +Can be given a numeric value to control the size of the history list. +Any command that has been referenced in this many events will not be +discarded. +Too large values of +.Ar history +may run the shell out of memory. +The last executed command is always saved on the history list. +.It Ic home +The home directory of the invoker, initialized from the environment. +The filename expansion of +.Sq Pa ~ +refers to this variable. +.It Ic ignoreeof +If set the shell ignores +end-of-file from input devices which are terminals. +This prevents shells from accidentally being killed by control-D's. +.It Ic mail +The files where the shell checks for mail. +This checking is done after each command completion that will +result in a prompt, +if a specified interval has elapsed. +The shell says `You have new mail.' +if the file exists with an access time not greater than its modify time. +.Pp +If the first word of the value of +.Ar mail +is numeric it specifies a different mail checking interval, in seconds, +than the default, which is 10 minutes. +.Pp +If multiple mail files are specified, then the shell says +`New mail in +.Ar name Ns ' +when there is mail in the file +.Ar name . +.It Ic noclobber +As described in the section on +.Sx input/output , +restrictions are placed on output redirection to insure that +files are not accidentally destroyed, and that `>>' redirections +refer to existing files. +.It Ic noglob +If set, filename expansion is inhibited. +This inhibition is most useful in shell scripts that + are not dealing with filenames, +or after a list of filenames has been obtained and further expansions +are not desirable. +.It Ic nonomatch +If set, it is not an error for a filename expansion to not match any +existing files; instead the primitive pattern is returned. +It is still an error for the primitive pattern to be malformed, i.e., +`echo [' +still gives an error. +.It Ic notify +If set, the shell notifies asynchronously of job completions; +the default is to present job completions just before printing +a prompt. +.It Ic path +Each word of the path variable specifies a directory in which +commands are to be sought for execution. +A null word specifies the current directory. +If there is no +.Ar path +variable then only full path names will execute. +The usual search path is `.', `/bin' and `/usr/bin', but this +may vary from system to system. +For the super-user the default search path is `/etc', `/bin' and `/usr/bin'. +A shell that is given neither the +.Fl c +nor the +.Fl t +option will normally hash the contents of the directories in the +.Ar path +variable after reading +.Ar \&.cshrc , +and each time the +.Ar path +variable is reset. If new commands are added to these directories +while the shell is active, it may be necessary to do a +.Ic rehash +or the commands may not be found. +.It Ic prompt +The string that is printed before each command is read from +an interactive terminal input. +If a `!' appears in the string it will be replaced by the current event number +unless a preceding `\e' is given. +Default is `% ', or `# ' for the super-user. +.It Ic savehist +Is given a numeric value to control the number of entries of the +history list that are saved in ~/.history when the user logs out. +Any command that has been referenced in this many events will be saved. +During start up the shell sources ~/.history into the history list +enabling history to be saved across logins. +Too large values of +.Ar savehist +will slow down the shell during start up. +If +.Ar savehist +is just set, the shell will use the value of +.Ar history. +.It Ic shell +The file in which the shell resides. +This variable is used in forking shells to interpret files that have execute +bits set, but which are not executable by the system. +(See the description of +.Sx Non-builtin Command Execution +below.) +Initialized to the (system-dependent) home of the shell. +.It Ic status +The status returned by the last command. +If it terminated abnormally, then 0200 is added to the status. +Builtin commands that fail return exit status `1', +all other builtin commands set status to `0'. +.It Ic time +Controls automatic timing of commands. +If set, then any command that takes more than this many cpu seconds +will cause a line giving user, system, and real times and a utilization +percentage which is the ratio of user plus system times to real time +to be printed when it terminates. +.It Ic verbose +Set by the +.Fl v +command line option, causes the words of each command to be printed +after history substitution. +.El +.Ss Non-builtin command execution +When a command to be executed is found to not be a builtin command +the shell attempts to execute the command via +.Xr execve 2 . +Each word in the variable +.Ar path +names a directory from which the shell will attempt to execute the command. +If it is given neither a +.Fl c +nor a +.Fl t +option, the shell will hash the names in these directories into an internal +table so that it will only try an +.Ic exec +in a directory if there is a possibility that the command resides there. +This shortcut greatly speeds command location when many directories +are present in the search path. +If this mechanism has been turned off (via +.Ic unhash ) , +or if the shell was given a +.Fl c +or +.Fl t +argument, and in any case for each directory component of +.Ar path +that does not begin with a `/', +the shell concatenates with the given command name to form a path name +of a file which it then attempts to execute. +.Pp +Parenthesized commands are always executed in a subshell. +Thus +.Pp +.Dl (cd ; pwd) ; pwd +.Pp +prints the +.Ar home +directory; leaving you where you were (printing this after the home directory), +while +.Pp +.Dl cd ; pwd +.Pp +leaves you in the +.Ar home +directory. +Parenthesized commands are most often used to prevent +.Ic chdir +from affecting the current shell. +.Pp +If the file has execute permissions but is not an +executable binary to the system, then it is assumed to be a +file containing shell commands and a new shell is spawned to read it. +.Pp +If there is an +.Ic alias +for +.Ic shell +then the words of the alias will be prepended to the argument list to form +the shell command. +The first word of the +.Ic alias +should be the full path name of the shell +(e.g., `$shell'). +Note that this is a special, late occurring, case of +.Ic alias +substitution, +and only allows words to be prepended to the argument list without change. +.Ss Signal handling +The shell normally ignores +.Ar quit +signals. +Jobs running detached (either by +.Ic \&& +or the +.Ic bg +or +.Ic %... & +commands) are immune to signals generated from the keyboard, including +hangups. +Other signals have the values which the shell inherited from its parent. +The shell's handling of interrupts and terminate signals +in shell scripts can be controlled by +.Ic onintr . +Login shells catch the +.Ar terminate +signal; otherwise this signal is passed on to children from the state in the +shell's parent. +Interrupts are not allowed when a login shell is reading the file +.Pa \&.logout . +.Sh AUTHOR +William Joy. +Job control and directory stack features first implemented by J.E. Kulp of +IIASA, Laxenburg, Austria, +with different syntax than that used now. +File name completion code written by Ken Greer, HP Labs. +Eight-bit implementation Christos S. Zoulas, Cornell University. +.Sh FILES +.Bl -tag -width /etc/passwd -compact +.It Pa ~/.cshrc +Read at beginning of execution by each shell. +.It Pa ~/.login +Read by login shell, after `.cshrc' at login. +.It Pa ~/.logout +Read by login shell, at logout. +.It Pa /bin/sh +Standard shell, for shell scripts not starting with a `#'. +.It Pa /tmp/sh* +Temporary file for `<<'. +.It Pa /etc/passwd +Source of home directories for `~name'. +.El +.Sh LIMITATIONS +Word lengths \- +Words can be no longer than 1024 characters. +The system limits argument lists to 10240 characters. +The number of arguments to a command that involves filename expansion +is limited to 1/6'th the number of characters allowed in an argument list. +Command substitutions may substitute no more characters than are +allowed in an argument list. +To detect looping, the shell restricts the number of +.Ic alias +substitutions on a single line to 20. +.Sh SEE ALSO +.Xr sh 1 , +.Xr access 2 , +.Xr execve 2 , +.Xr fork 2 , +.Xr killpg 2 , +.Xr pipe 2 , +.Xr sigvec 2 , +.Xr umask 2 , +.Xr setrlimit 2 , +.Xr wait 2 , +.Xr tty 4 , +.Xr a.out 5 , +.Xr environ 7 , +.br +.Em An introduction to the C shell +.Sh HISTORY +.Nm Csh +appeared in +.Bx 3 . +It +was a first implementation of a command language interpreter +incorporating a history mechanism (see +.Sx History Substitutions ) , +job control facilities (see +.Sx Jobs ) , +interactive file name +and user name completion (see +.Sx File Name Completion ) , +and a C-like syntax. +There are now many shells that also have these mechanisms, plus +a few more (and maybe some bugs too), which are available through the +usenet. +.Sh BUGS +When a command is restarted from a stop, +the shell prints the directory it started in if this is different +from the current directory; this can be misleading (i.e., wrong) +as the job may have changed directories internally. +.Pp +Shell builtin functions are not stoppable/restartable. +Command sequences of the form `a ; b ; c' are also not handled gracefully +when stopping is attempted. If you suspend `b', the shell will +immediately execute `c'. This is especially noticeable if this +expansion results from an +.Ar alias . +It suffices to place the sequence of commands in ()'s to force it to +a subshell, i.e., `( a ; b ; c )'. +.Pp +Control over tty output after processes are started is primitive; +perhaps this will inspire someone to work on a good virtual +terminal interface. In a virtual terminal interface much more +interesting things could be done with output control. +.Pp +Alias substitution is most often used to clumsily simulate shell procedures; +shell procedures should be provided instead of aliases. +.Pp +Commands within loops, prompted for by `?', are not placed on the +.Ic history +list. +Control structure should be parsed instead of being recognized as built-in +commands. This would allow control commands to be placed anywhere, +to be combined with `\&|', and to be used with `&' and `;' metasyntax. +.Pp +It should be possible to use the `:' modifiers on the output of command +substitutions. +.Pp +The way the +.Ic filec +facility is implemented is ugly and expensive. diff --git a/bin/csh/csh.c b/bin/csh/csh.c new file mode 100644 index 000000000000..8f70d3204d98 --- /dev/null +++ b/bin/csh/csh.c @@ -0,0 +1,1352 @@ +/*- + * Copyright (c) 1980, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * 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 copyright[] = +"@(#) Copyright (c) 1980, 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)csh.c 8.2 (Berkeley) 10/12/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <pwd.h> +#include <stdlib.h> +#include <string.h> +#include <locale.h> +#include <unistd.h> +#include <vis.h> +#if __STDC__ +# include <stdarg.h> +#else +# include <varargs.h> +#endif + +#include "csh.h" +#include "proc.h" +#include "extern.h" +#include "pathnames.h" + +extern bool MapsAreInited; +extern bool NLSMapsAreInited; + +/* + * C Shell + * + * Bill Joy, UC Berkeley, California, USA + * October 1978, May 1980 + * + * Jim Kulp, IIASA, Laxenburg, Austria + * April 1980 + * + * Christos Zoulas, Cornell University + * June, 1991 + */ + +Char *dumphist[] = {STRhistory, STRmh, 0, 0}; +Char *loadhist[] = {STRsource, STRmh, STRtildothist, 0}; + +int nofile = 0; +bool reenter = 0; +bool nverbose = 0; +bool nexececho = 0; +bool quitit = 0; +bool fast = 0; +bool batch = 0; +bool mflag = 0; +bool prompt = 1; +bool enterhist = 0; +bool tellwhat = 0; + +extern char **environ; + +static int readf __P((void *, char *, int)); +static fpos_t seekf __P((void *, fpos_t, int)); +static int writef __P((void *, const char *, int)); +static int closef __P((void *)); +static int srccat __P((Char *, Char *)); +static int srcfile __P((char *, bool, bool)); +static void phup __P((int)); +static void srcunit __P((int, bool, bool)); +static void mailchk __P((void)); +static Char **defaultpath __P((void)); + +int +main(argc, argv) + int argc; + char **argv; +{ + register Char *cp; + register char *tcp; + register int f; + register char **tempv; + struct sigvec osv; + + cshin = stdin; + cshout = stdout; + csherr = stderr; + + settimes(); /* Immed. estab. timing base */ + + /* + * Initialize non constant strings + */ +#ifdef _PATH_BSHELL + STR_BSHELL = SAVE(_PATH_BSHELL); +#endif +#ifdef _PATH_CSHELL + STR_SHELLPATH = SAVE(_PATH_CSHELL); +#endif + STR_environ = blk2short(environ); + environ = short2blk(STR_environ); /* So that we can free it */ + STR_WORD_CHARS = SAVE(WORD_CHARS); + + HIST = '!'; + HISTSUB = '^'; + word_chars = STR_WORD_CHARS; + + tempv = argv; + if (eq(str2short(tempv[0]), STRaout)) /* A.out's are quittable */ + quitit = 1; + uid = getuid(); + gid = getgid(); + euid = geteuid(); + egid = getegid(); + /* + * We are a login shell if: 1. we were invoked as -<something> and we had + * no arguments 2. or we were invoked only with the -l flag + */ + loginsh = (**tempv == '-' && argc == 1) || + (argc == 2 && tempv[1][0] == '-' && tempv[1][1] == 'l' && + tempv[1][2] == '\0'); + + if (loginsh && **tempv != '-') { + /* + * Mangle the argv space + */ + tempv[1][0] = '\0'; + tempv[1][1] = '\0'; + tempv[1] = NULL; + for (tcp = *tempv; *tcp++;) + continue; + for (tcp--; tcp >= *tempv; tcp--) + tcp[1] = tcp[0]; + *++tcp = '-'; + argc--; + } + if (loginsh) + (void) time(&chktim); + + AsciiOnly = 1; +#ifdef NLS + (void) setlocale(LC_ALL, ""); + { + int k; + + for (k = 0200; k <= 0377 && !Isprint(k); k++) + continue; + AsciiOnly = k > 0377; + } +#else + AsciiOnly = getenv("LANG") == NULL && getenv("LC_CTYPE") == NULL; +#endif /* NLS */ + + /* + * Move the descriptors to safe places. The variable didfds is 0 while we + * have only FSH* to work with. When didfds is true, we have 0,1,2 and + * prefer to use these. + */ + initdesc(); + /* + * XXX: This is to keep programs that use stdio happy. + * what we really want is freunopen() .... + * Closing cshin cshout and csherr (which are really stdin stdout + * and stderr at this point and then reopening them in the same order + * gives us again stdin == cshin stdout == cshout and stderr == csherr. + * If that was not the case builtins like printf that use stdio + * would break. But in any case we could fix that with memcpy and + * a bit of pointer manipulation... + * Fortunately this is not needed under the current implementation + * of stdio. + */ + (void) fclose(cshin); + (void) fclose(cshout); + (void) fclose(csherr); + if (!(cshin = funopen((void *) &SHIN, readf, writef, seekf, closef))) + exit(1); + if (!(cshout = funopen((void *) &SHOUT, readf, writef, seekf, closef))) + exit(1); + if (!(csherr = funopen((void *) &SHERR, readf, writef, seekf, closef))) + exit(1); + (void) setvbuf(cshin, NULL, _IOLBF, 0); + (void) setvbuf(cshout, NULL, _IOLBF, 0); + (void) setvbuf(csherr, NULL, _IOLBF, 0); + + /* + * Initialize the shell variables. ARGV and PROMPT are initialized later. + * STATUS is also munged in several places. CHILD is munged when + * forking/waiting + */ + set(STRstatus, Strsave(STR0)); + + if ((tcp = getenv("HOME")) != NULL) + cp = SAVE(tcp); + else + cp = NULL; + + if (cp == NULL) + fast = 1; /* No home -> can't read scripts */ + else + set(STRhome, cp); + dinit(cp); /* dinit thinks that HOME == cwd in a login + * shell */ + /* + * Grab other useful things from the environment. Should we grab + * everything?? + */ + if ((tcp = getenv("LOGNAME")) != NULL || + (tcp = getenv("USER")) != NULL) + set(STRuser, SAVE(tcp)); + if ((tcp = getenv("TERM")) != NULL) + set(STRterm, SAVE(tcp)); + + /* + * Re-initialize path if set in environment + */ + if ((tcp = getenv("PATH")) == NULL) + set1(STRpath, defaultpath(), &shvhed); + else + importpath(SAVE(tcp)); + + set(STRshell, Strsave(STR_SHELLPATH)); + + doldol = putn((int) getpid()); /* For $$ */ + shtemp = Strspl(STRtmpsh, doldol); /* For << */ + + /* + * Record the interrupt states from the parent process. If the parent is + * non-interruptible our hand must be forced or we (and our children) won't + * be either. Our children inherit termination from our parent. We catch it + * only if we are the login shell. + */ + /* parents interruptibility */ + (void) sigvec(SIGINT, NULL, &osv); + parintr = (void (*) ()) osv.sv_handler; + (void) sigvec(SIGTERM, NULL, &osv); + parterm = (void (*) ()) osv.sv_handler; + + if (loginsh) { + (void) signal(SIGHUP, phup); /* exit processing on HUP */ + (void) signal(SIGXCPU, phup); /* ...and on XCPU */ + (void) signal(SIGXFSZ, phup); /* ...and on XFSZ */ + } + + /* + * Process the arguments. + * + * Note that processing of -v/-x is actually delayed till after script + * processing. + * + * We set the first character of our name to be '-' if we are a shell + * running interruptible commands. Many programs which examine ps'es + * use this to filter such shells out. + */ + argc--, tempv++; + while (argc > 0 && (tcp = tempv[0])[0] == '-' && *++tcp != '\0' && !batch) { + do + switch (*tcp++) { + + case 0: /* - Interruptible, no prompt */ + prompt = 0; + setintr = 1; + nofile = 1; + break; + + case 'b': /* -b Next arg is input file */ + batch = 1; + break; + + case 'c': /* -c Command input from arg */ + if (argc == 1) + xexit(0); + argc--, tempv++; + arginp = SAVE(tempv[0]); + prompt = 0; + nofile = 1; + break; + + case 'e': /* -e Exit on any error */ + exiterr = 1; + break; + + case 'f': /* -f Fast start */ + fast = 1; + break; + + case 'i': /* -i Interactive, even if !intty */ + intact = 1; + nofile = 1; + break; + + case 'm': /* -m read .cshrc (from su) */ + mflag = 1; + break; + + case 'n': /* -n Don't execute */ + noexec = 1; + break; + + case 'q': /* -q (Undoc'd) ... die on quit */ + quitit = 1; + break; + + case 's': /* -s Read from std input */ + nofile = 1; + break; + + case 't': /* -t Read one line from input */ + onelflg = 2; + prompt = 0; + nofile = 1; + break; + + case 'v': /* -v Echo hist expanded input */ + nverbose = 1; /* ... later */ + break; + + case 'x': /* -x Echo just before execution */ + nexececho = 1; /* ... later */ + break; + + case 'V': /* -V Echo hist expanded input */ + setNS(STRverbose); /* NOW! */ + break; + + case 'X': /* -X Echo just before execution */ + setNS(STRecho); /* NOW! */ + break; + + } while (*tcp); + tempv++, argc--; + } + + if (quitit) /* With all due haste, for debugging */ + (void) signal(SIGQUIT, SIG_DFL); + + /* + * Unless prevented by -, -c, -i, -s, or -t, if there are remaining + * arguments the first of them is the name of a shell file from which to + * read commands. + */ + if (nofile == 0 && argc > 0) { + nofile = open(tempv[0], O_RDONLY); + if (nofile < 0) { + child = 1; /* So this doesn't return */ + stderror(ERR_SYSTEM, tempv[0], strerror(errno)); + } + ffile = SAVE(tempv[0]); + /* + * Replace FSHIN. Handle /dev/std{in,out,err} specially + * since once they are closed we cannot open them again. + * In that case we use our own saved descriptors + */ + if ((SHIN = dmove(nofile, FSHIN)) < 0) + switch(nofile) { + case 0: + SHIN = FSHIN; + break; + case 1: + SHIN = FSHOUT; + break; + case 2: + SHIN = FSHERR; + break; + default: + stderror(ERR_SYSTEM, tempv[0], strerror(errno)); + break; + } + (void) ioctl(SHIN, FIOCLEX, NULL); + prompt = 0; + /* argc not used any more */ tempv++; + } + + intty = isatty(SHIN); + intty |= intact; + if (intty || (intact && isatty(SHOUT))) { + if (!batch && (uid != euid || gid != egid)) { + errno = EACCES; + child = 1; /* So this doesn't return */ + stderror(ERR_SYSTEM, "csh", strerror(errno)); + } + } + /* + * Decide whether we should play with signals or not. If we are explicitly + * told (via -i, or -) or we are a login shell (arg0 starts with -) or the + * input and output are both the ttys("csh", or "csh</dev/ttyx>/dev/ttyx") + * Note that in only the login shell is it likely that parent may have set + * signals to be ignored + */ + if (loginsh || intact || (intty && isatty(SHOUT))) + setintr = 1; + settell(); + /* + * Save the remaining arguments in argv. + */ + setq(STRargv, blk2short(tempv), &shvhed); + + /* + * Set up the prompt. + */ + if (prompt) { + set(STRprompt, Strsave(uid == 0 ? STRsymhash : STRsymcent)); + /* that's a meta-questionmark */ + set(STRprompt2, Strsave(STRmquestion)); + } + + /* + * If we are an interactive shell, then start fiddling with the signals; + * this is a tricky game. + */ + shpgrp = getpgrp(); + opgrp = tpgrp = -1; + if (setintr) { + **argv = '-'; + if (!quitit) /* Wary! */ + (void) signal(SIGQUIT, SIG_IGN); + (void) signal(SIGINT, pintr); + (void) sigblock(sigmask(SIGINT)); + (void) signal(SIGTERM, SIG_IGN); + if (quitit == 0 && arginp == 0) { + (void) signal(SIGTSTP, SIG_IGN); + (void) signal(SIGTTIN, SIG_IGN); + (void) signal(SIGTTOU, SIG_IGN); + /* + * Wait till in foreground, in case someone stupidly runs csh & + * dont want to try to grab away the tty. + */ + if (isatty(FSHERR)) + f = FSHERR; + else if (isatty(FSHOUT)) + f = FSHOUT; + else if (isatty(OLDSTD)) + f = OLDSTD; + else + f = -1; + retry: + if ((tpgrp = tcgetpgrp(f)) != -1) { + if (tpgrp != shpgrp) { + sig_t old = signal(SIGTTIN, SIG_DFL); + (void) kill(0, SIGTTIN); + (void) signal(SIGTTIN, old); + goto retry; + } + opgrp = shpgrp; + shpgrp = getpid(); + tpgrp = shpgrp; + /* + * Setpgid will fail if we are a session leader and + * mypid == mypgrp (POSIX 4.3.3) + */ + if (opgrp != shpgrp) + if (setpgid(0, shpgrp) == -1) + goto notty; + /* + * We do that after we set our process group, to make sure + * that the process group belongs to a process in the same + * session as the tty (our process and our group) (POSIX 7.2.4) + */ + if (tcsetpgrp(f, shpgrp) == -1) + goto notty; + (void) ioctl(dcopy(f, FSHTTY), FIOCLEX, NULL); + } + if (tpgrp == -1) { +notty: + (void) fprintf(csherr, "Warning: no access to tty (%s).\n", + strerror(errno)); + (void) fprintf(csherr, "Thus no job control in this shell.\n"); + } + } + } + if ((setintr == 0) && (parintr == SIG_DFL)) + setintr = 1; + (void) signal(SIGCHLD, pchild); /* while signals not ready */ + + /* + * Set an exit here in case of an interrupt or error reading the shell + * start-up scripts. + */ + reenter = setexit(); /* PWP */ + haderr = 0; /* In case second time through */ + if (!fast && reenter == 0) { + /* Will have value(STRhome) here because set fast if don't */ + { + int osetintr = setintr; + sig_t oparintr = parintr; + sigset_t omask = sigblock(sigmask(SIGINT)); + + setintr = 0; + parintr = SIG_IGN; /* Disable onintr */ +#ifdef _PATH_DOTCSHRC + (void) srcfile(_PATH_DOTCSHRC, 0, 0); +#endif + if (!fast && !arginp && !onelflg) + dohash(NULL, NULL); +#ifdef _PATH_DOTLOGIN + if (loginsh) + (void) srcfile(_PATH_DOTLOGIN, 0, 0); +#endif + (void) sigsetmask(omask); + setintr = osetintr; + parintr = oparintr; + } + (void) srccat(value(STRhome), STRsldotcshrc); + + if (!fast && !arginp && !onelflg && !havhash) + dohash(NULL, NULL); + /* + * Source history before .login so that it is available in .login + */ + if ((cp = value(STRhistfile)) != STRNULL) + loadhist[2] = cp; + dosource(loadhist, NULL); + if (loginsh) + (void) srccat(value(STRhome), STRsldotlogin); + } + + /* + * Now are ready for the -v and -x flags + */ + if (nverbose) + setNS(STRverbose); + if (nexececho) + setNS(STRecho); + + /* + * All the rest of the world is inside this call. The argument to process + * indicates whether it should catch "error unwinds". Thus if we are a + * interactive shell our call here will never return by being blown past on + * an error. + */ + process(setintr); + + /* + * Mop-up. + */ + if (intty) { + if (loginsh) { + (void) fprintf(cshout, "logout\n"); + (void) close(SHIN); + child = 1; + goodbye(); + } + else { + (void) fprintf(cshout, "exit\n"); + } + } + rechist(); + exitstat(); + return (0); +} + +void +untty() +{ + if (tpgrp > 0) { + (void) setpgid(0, opgrp); + (void) tcsetpgrp(FSHTTY, opgrp); + } +} + +void +importpath(cp) + Char *cp; +{ + register int i = 0; + register Char *dp; + register Char **pv; + int c; + + for (dp = cp; *dp; dp++) + if (*dp == ':') + i++; + /* + * i+2 where i is the number of colons in the path. There are i+1 + * directories in the path plus we need room for a zero terminator. + */ + pv = (Char **) xcalloc((size_t) (i + 2), sizeof(Char **)); + dp = cp; + i = 0; + if (*dp) + for (;;) { + if ((c = *dp) == ':' || c == 0) { + *dp = 0; + if ((*cp != '/' || *cp == '\0') && (euid == 0 || uid == 0)) + (void) fprintf(csherr, + "Warning: imported path contains relative components\n"); + pv[i++] = Strsave(*cp ? cp : STRdot); + if (c) { + cp = dp + 1; + *dp = ':'; + } + else + break; + } + dp++; + } + pv[i] = 0; + set1(STRpath, pv, &shvhed); +} + +/* + * Source to the file which is the catenation of the argument names. + */ +static int +srccat(cp, dp) + Char *cp, *dp; +{ + register Char *ep = Strspl(cp, dp); + char *ptr = short2str(ep); + + xfree((ptr_t) ep); + return srcfile(ptr, mflag ? 0 : 1, 0); +} + +/* + * Source to a file putting the file descriptor in a safe place (> 2). + */ +static int +srcfile(f, onlyown, flag) + char *f; + bool onlyown, flag; +{ + register int unit; + + if ((unit = open(f, O_RDONLY)) == -1) + return 0; + unit = dmove(unit, -1); + + (void) ioctl(unit, FIOCLEX, NULL); + srcunit(unit, onlyown, flag); + return 1; +} + +/* + * Source to a unit. If onlyown it must be our file or our group or + * we don't chance it. This occurs on ".cshrc"s and the like. + */ +int insource; +static void +srcunit(unit, onlyown, hflg) + register int unit; + bool onlyown, hflg; +{ + /* We have to push down a lot of state here */ + /* All this could go into a structure */ + int oSHIN = -1, oldintty = intty, oinsource = insource; + struct whyle *oldwhyl = whyles; + Char *ogointr = gointr, *oarginp = arginp; + Char *oevalp = evalp, **oevalvec = evalvec; + int oonelflg = onelflg; + bool oenterhist = enterhist; + char OHIST = HIST; + bool otell = cantell; + + struct Bin saveB; + volatile sigset_t omask; + jmp_buf oldexit; + + /* The (few) real local variables */ + int my_reenter; + + if (unit < 0) + return; + if (didfds) + donefds(); + if (onlyown) { + struct stat stb; + + if (fstat(unit, &stb) < 0) { + (void) close(unit); + return; + } + } + + /* + * There is a critical section here while we are pushing down the input + * stream since we have stuff in different structures. If we weren't + * careful an interrupt could corrupt SHIN's Bin structure and kill the + * shell. + * + * We could avoid the critical region by grouping all the stuff in a single + * structure and pointing at it to move it all at once. This is less + * efficient globally on many variable references however. + */ + insource = 1; + getexit(oldexit); + omask = 0; + + if (setintr) + omask = sigblock(sigmask(SIGINT)); + /* Setup the new values of the state stuff saved above */ + bcopy((char *) &B, (char *) &(saveB), sizeof(B)); + fbuf = NULL; + fseekp = feobp = fblocks = 0; + oSHIN = SHIN, SHIN = unit, arginp = 0, onelflg = 0; + intty = isatty(SHIN), whyles = 0, gointr = 0; + evalvec = 0; + evalp = 0; + enterhist = hflg; + if (enterhist) + HIST = '\0'; + + /* + * Now if we are allowing commands to be interrupted, we let ourselves be + * interrupted. + */ + if (setintr) + (void) sigsetmask(omask); + settell(); + + if ((my_reenter = setexit()) == 0) + process(0); /* 0 -> blow away on errors */ + + if (setintr) + (void) sigsetmask(omask); + if (oSHIN >= 0) { + register int i; + + /* We made it to the new state... free up its storage */ + /* This code could get run twice but xfree doesn't care */ + for (i = 0; i < fblocks; i++) + xfree((ptr_t) fbuf[i]); + xfree((ptr_t) fbuf); + + /* Reset input arena */ + bcopy((char *) &(saveB), (char *) &B, sizeof(B)); + + (void) close(SHIN), SHIN = oSHIN; + arginp = oarginp, onelflg = oonelflg; + evalp = oevalp, evalvec = oevalvec; + intty = oldintty, whyles = oldwhyl, gointr = ogointr; + if (enterhist) + HIST = OHIST; + enterhist = oenterhist; + cantell = otell; + } + + resexit(oldexit); + /* + * If process reset() (effectively an unwind) then we must also unwind. + */ + if (my_reenter) + stderror(ERR_SILENT); + insource = oinsource; +} + +void +rechist() +{ + Char buf[BUFSIZ], hbuf[BUFSIZ], *hfile; + int fp, ftmp, oldidfds; + struct varent *shist; + + if (!fast) { + /* + * If $savehist is just set, we use the value of $history + * else we use the value in $savehist + */ + if ((shist = adrof(STRsavehist)) != NULL) { + if (shist->vec[0][0] != '\0') + (void) Strcpy(hbuf, shist->vec[0]); + else if ((shist = adrof(STRhistory)) && shist->vec[0][0] != '\0') + (void) Strcpy(hbuf, shist->vec[0]); + else + return; + } + else + return; + + if ((hfile = value(STRhistfile)) == STRNULL) { + hfile = Strcpy(buf, value(STRhome)); + (void) Strcat(buf, STRsldthist); + } + + if ((fp = creat(short2str(hfile), 0600)) == -1) + return; + + oldidfds = didfds; + didfds = 0; + ftmp = SHOUT; + SHOUT = fp; + dumphist[2] = hbuf; + dohist(dumphist, NULL); + SHOUT = ftmp; + (void) close(fp); + didfds = oldidfds; + } +} + +void +goodbye() +{ + rechist(); + + if (loginsh) { + (void) signal(SIGQUIT, SIG_IGN); + (void) signal(SIGINT, SIG_IGN); + (void) signal(SIGTERM, SIG_IGN); + setintr = 0; /* No interrupts after "logout" */ + if (!(adrof(STRlogout))) + set(STRlogout, STRnormal); +#ifdef _PATH_DOTLOGOUT + (void) srcfile(_PATH_DOTLOGOUT, 0, 0); +#endif + if (adrof(STRhome)) + (void) srccat(value(STRhome), STRsldtlogout); + } + exitstat(); +} + +void +exitstat() +{ + Char *s; +#ifdef PROF + monitor(0); +#endif + /* + * Note that if STATUS is corrupted (i.e. getn bombs) then error will exit + * directly because we poke child here. Otherwise we might continue + * unwarrantedly (sic). + */ + child = 1; + s = value(STRstatus); + xexit(s ? getn(s) : 0); +} + +/* + * in the event of a HUP we want to save the history + */ +static void +phup(sig) +int sig; +{ + rechist(); + + /* + * We kill the last foreground process group. It then becomes + * responsible to propagate the SIGHUP to its progeny. + */ + { + struct process *pp, *np; + + for (pp = proclist.p_next; pp; pp = pp->p_next) { + np = pp; + /* + * Find if this job is in the foreground. It could be that + * the process leader has exited and the foreground flag + * is cleared for it. + */ + do + /* + * If a process is in the foreground; we try to kill + * it's process group. If we succeed, then the + * whole job is gone. Otherwise we keep going... + * But avoid sending HUP to the shell again. + */ + if ((np->p_flags & PFOREGND) != 0 && np->p_jobid != shpgrp && + killpg(np->p_jobid, SIGHUP) != -1) { + /* In case the job was suspended... */ + (void) killpg(np->p_jobid, SIGCONT); + break; + } + while ((np = np->p_friends) != pp); + } + } + _exit(sig); +} + +Char *jobargv[2] = {STRjobs, 0}; + +/* + * Catch an interrupt, e.g. during lexical input. + * If we are an interactive shell, we reset the interrupt catch + * immediately. In any case we drain the shell output, + * and finally go through the normal error mechanism, which + * gets a chance to make the shell go away. + */ +/* ARGSUSED */ +void +pintr(notused) + int notused; +{ + pintr1(1); +} + +void +pintr1(wantnl) + bool wantnl; +{ + Char **v; + sigset_t omask; + + omask = sigblock((sigset_t) 0); + if (setintr) { + (void) sigsetmask(omask & ~sigmask(SIGINT)); + if (pjobs) { + pjobs = 0; + (void) fprintf(cshout, "\n"); + dojobs(jobargv, NULL); + stderror(ERR_NAME | ERR_INTR); + } + } + (void) sigsetmask(omask & ~sigmask(SIGCHLD)); + (void) fpurge(cshout); + (void) endpwent(); + + /* + * If we have an active "onintr" then we search for the label. Note that if + * one does "onintr -" then we shan't be interruptible so we needn't worry + * about that here. + */ + if (gointr) { + gotolab(gointr); + timflg = 0; + if ((v = pargv) != NULL) + pargv = 0, blkfree(v); + if ((v = gargv) != NULL) + gargv = 0, blkfree(v); + reset(); + } + else if (intty && wantnl) { + (void) fputc('\r', cshout); + (void) fputc('\n', cshout); + } + stderror(ERR_SILENT); +} + +/* + * Process is the main driving routine for the shell. + * It runs all command processing, except for those within { ... } + * in expressions (which is run by a routine evalav in sh.exp.c which + * is a stripped down process), and `...` evaluation which is run + * also by a subset of this code in sh.glob.c in the routine backeval. + * + * The code here is a little strange because part of it is interruptible + * and hence freeing of structures appears to occur when none is necessary + * if this is ignored. + * + * Note that if catch is not set then we will unwind on any error. + * If an end-of-file occurs, we return. + */ +static struct command *savet = NULL; +void +process(catch) + bool catch; +{ + jmp_buf osetexit; + struct command *t = savet; + + savet = NULL; + getexit(osetexit); + for (;;) { + pendjob(); + paraml.next = paraml.prev = ¶ml; + paraml.word = STRNULL; + (void) setexit(); + justpr = enterhist; /* execute if not entering history */ + + /* + * Interruptible during interactive reads + */ + if (setintr) + (void) sigsetmask(sigblock((sigset_t) 0) & ~sigmask(SIGINT)); + + /* + * For the sake of reset() + */ + freelex(¶ml); + if (savet) + freesyn(savet), savet = NULL; + + if (haderr) { + if (!catch) { + /* unwind */ + doneinp = 0; + resexit(osetexit); + savet = t; + reset(); + } + haderr = 0; + /* + * Every error is eventually caught here or the shell dies. It is + * at this point that we clean up any left-over open files, by + * closing all but a fixed number of pre-defined files. Thus + * routines don't have to worry about leaving files open due to + * deeper errors... they will get closed here. + */ + closem(); + continue; + } + if (doneinp) { + doneinp = 0; + break; + } + if (chkstop) + chkstop--; + if (neednote) + pnote(); + if (intty && prompt && evalvec == 0) { + mailchk(); + /* + * If we are at the end of the input buffer then we are going to + * read fresh stuff. Otherwise, we are rereading input and don't + * need or want to prompt. + */ + if (aret == F_SEEK && fseekp == feobp) + printprompt(); + (void) fflush(cshout); + } + if (seterr) { + xfree((ptr_t) seterr); + seterr = NULL; + } + + /* + * Echo not only on VERBOSE, but also with history expansion. If there + * is a lexical error then we forego history echo. + */ + if ((lex(¶ml) && !seterr && intty) || adrof(STRverbose)) { + prlex(csherr, ¶ml); + } + + /* + * The parser may lose space if interrupted. + */ + if (setintr) + (void) sigblock(sigmask(SIGINT)); + + /* + * Save input text on the history list if reading in old history, or it + * is from the terminal at the top level and not in a loop. + * + * PWP: entry of items in the history list while in a while loop is done + * elsewhere... + */ + if (enterhist || (catch && intty && !whyles)) + savehist(¶ml); + + /* + * Print lexical error messages, except when sourcing history lists. + */ + if (!enterhist && seterr) + stderror(ERR_OLD); + + /* + * If had a history command :p modifier then this is as far as we + * should go + */ + if (justpr) + reset(); + + alias(¶ml); + + /* + * Parse the words of the input into a parse tree. + */ + savet = syntax(paraml.next, ¶ml, 0); + if (seterr) + stderror(ERR_OLD); + + execute(savet, (tpgrp > 0 ? tpgrp : -1), NULL, NULL); + + /* + * Made it! + */ + freelex(¶ml); + freesyn((struct command *) savet), savet = NULL; + } + resexit(osetexit); + savet = t; +} + +void +/*ARGSUSED*/ +dosource(v, t) + Char **v; + struct command *t; + +{ + register Char *f; + bool hflg = 0; + Char buf[BUFSIZ]; + + v++; + if (*v && eq(*v, STRmh)) { + if (*++v == NULL) + stderror(ERR_NAME | ERR_HFLAG); + hflg++; + } + (void) Strcpy(buf, *v); + f = globone(buf, G_ERROR); + (void) strcpy((char *) buf, short2str(f)); + xfree((ptr_t) f); + if (!srcfile((char *) buf, 0, hflg) && !hflg) + stderror(ERR_SYSTEM, (char *) buf, strerror(errno)); +} + +/* + * Check for mail. + * If we are a login shell, then we don't want to tell + * about any mail file unless its been modified + * after the time we started. + * This prevents us from telling the user things he already + * knows, since the login program insists on saying + * "You have mail." + */ +static void +mailchk() +{ + register struct varent *v; + register Char **vp; + time_t t; + int intvl, cnt; + struct stat stb; + bool new; + + v = adrof(STRmail); + if (v == 0) + return; + (void) time(&t); + vp = v->vec; + cnt = blklen(vp); + intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL; + if (intvl < 1) + intvl = 1; + if (chktim + intvl > t) + return; + for (; *vp; vp++) { + if (stat(short2str(*vp), &stb) < 0) + continue; + new = stb.st_mtime > time0.tv_sec; + if (stb.st_size == 0 || stb.st_atime > stb.st_mtime || + (stb.st_atime < chktim && stb.st_mtime < chktim) || + (loginsh && !new)) + continue; + if (cnt == 1) + (void) fprintf(cshout, "You have %smail.\n", new ? "new " : ""); + else + (void) fprintf(cshout, "%s in %s.\n", new ? "New mail" : "Mail", + vis_str(*vp)); + } + chktim = t; +} + +/* + * Extract a home directory from the password file + * The argument points to a buffer where the name of the + * user whose home directory is sought is currently. + * We write the home directory of the user back there. + */ +int +gethdir(home) + Char *home; +{ + Char *h; + struct passwd *pw; + + /* + * Is it us? + */ + if (*home == '\0') { + if ((h = value(STRhome)) != NULL) { + (void) Strcpy(home, h); + return 0; + } + else + return 1; + } + + if ((pw = getpwnam(short2str(home))) != NULL) { + (void) Strcpy(home, str2short(pw->pw_dir)); + return 0; + } + else + return 1; +} + +/* + * When didfds is set, we do I/O from 0, 1, 2 otherwise from 15, 16, 17 + * We also check if the shell has already changed the decriptor to point to + * 0, 1, 2 when didfds is set. + */ +#define DESC(a) (*((int *) (a)) - (didfds && *((int *) a) >= FSHIN ? FSHIN : 0)) + +static int +readf(oreo, buf, siz) + void *oreo; + char *buf; + int siz; +{ + return read(DESC(oreo), buf, siz); +} + + +static int +writef(oreo, buf, siz) + void *oreo; + const char *buf; + int siz; +{ + return write(DESC(oreo), buf, siz); +} + +static fpos_t +seekf(oreo, off, whence) + void *oreo; + fpos_t off; + int whence; +{ + return lseek(DESC(oreo), off, whence); +} + + +static int +closef(oreo) + void *oreo; +{ + return close(DESC(oreo)); +} + + +/* + * Print the visible version of a string. + */ +int +vis_fputc(ch, fp) + int ch; + FILE *fp; +{ + char uenc[5]; /* 4 + NULL */ + + if (ch & QUOTE) + return fputc(ch & TRIM, fp); + /* + * XXX: When we are in AsciiOnly we want all characters >= 0200 to + * be encoded, but currently there is no way in vis to do that. + */ + (void) vis(uenc, ch & TRIM, VIS_NOSLASH, 0); + return fputs(uenc, fp); +} + +/* + * Move the initial descriptors to their eventual + * resting places, closin all other units. + */ +void +initdesc() +{ + + didfds = 0; /* 0, 1, 2 aren't set up */ + (void) ioctl(SHIN = dcopy(0, FSHIN), FIOCLEX, NULL); + (void) ioctl(SHOUT = dcopy(1, FSHOUT), FIOCLEX, NULL); + (void) ioctl(SHERR = dcopy(2, FSHERR), FIOCLEX, NULL); + (void) ioctl(OLDSTD = dcopy(SHIN, FOLDSTD), FIOCLEX, NULL); + closem(); +} + + +void +#ifdef PROF +done(i) +#else +xexit(i) +#endif + int i; +{ + untty(); + _exit(i); +} + +static Char ** +defaultpath() +{ + char *ptr; + Char **blk, **blkp; + struct stat stb; + + blkp = blk = (Char **) xmalloc((size_t) sizeof(Char *) * 10); + +#define DIRAPPEND(a) \ + if (stat(ptr = a, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR) \ + *blkp++ = SAVE(ptr) + + DIRAPPEND(_PATH_BIN); + DIRAPPEND(_PATH_USRBIN); + +#undef DIRAPPEND + + if (euid != 0 && uid != 0) + *blkp++ = Strsave(STRdot); + *blkp = NULL; + return (blk); +} + +void +printprompt() +{ + register Char *cp; + + if (!whyles) { + for (cp = value(STRprompt); *cp; cp++) + if (*cp == HIST) + (void) fprintf(cshout, "%d", eventno + 1); + else { + if (*cp == '\\' && cp[1] == HIST) + cp++; + (void) vis_fputc(*cp | QUOTE, cshout); + } + } + else + /* + * Prompt for forward reading loop body content. + */ + (void) fprintf(cshout, "? "); + (void) fflush(cshout); +} diff --git a/bin/csh/csh.h b/bin/csh/csh.h new file mode 100644 index 000000000000..367b51ef7d24 --- /dev/null +++ b/bin/csh/csh.h @@ -0,0 +1,552 @@ +/*- + * Copyright (c) 1980, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * 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. + * + * @(#)csh.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * Fundamental definitions which may vary from system to system. + * + * BUFSIZ The i/o buffering size; also limits word size + * MAILINTVL How often to mailcheck; more often is more expensive + */ +#ifndef BUFSIZ +#define BUFSIZ 1024 /* default buffer size */ +#endif /* BUFSIZ */ + +#define FORKSLEEP 10 /* delay loop on non-interactive fork failure */ +#define MAILINTVL 600 /* 10 minutes */ + +/* + * The shell moves std in/out/diag and the old std input away from units + * 0, 1, and 2 so that it is easy to set up these standards for invoked + * commands. + */ +#define FSHTTY 15 /* /dev/tty when manip pgrps */ +#define FSHIN 16 /* Preferred desc for shell input */ +#define FSHOUT 17 /* ... shell output */ +#define FSHERR 18 /* ... shell diagnostics */ +#define FOLDSTD 19 /* ... old std input */ + +#ifdef PROF +#define xexit(n) done(n) +#endif + +#ifdef SHORT_STRINGS +typedef short Char; + +#define SAVE(a) (Strsave(str2short(a))) +#else +typedef char Char; + +#define SAVE(a) (strsave(a)) +#endif + +/* + * Make sure a variable is not stored in a register by taking its address + * This is used where variables might be clobbered by longjmp. + */ +#define UNREGISTER(a) (void) &a + +typedef void *ioctl_t; /* Third arg of ioctl */ + +typedef void *ptr_t; + +#include "const.h" +#include "char.h" +#include "err.h" + +#define xmalloc(i) Malloc(i) +#define xrealloc(p, i) Realloc(p, i) +#define xcalloc(n, s) Calloc(n, s) +#define xfree(p) Free(p) + +#include <stdio.h> +FILE *cshin, *cshout, *csherr; + +#define isdir(d) ((d.st_mode & S_IFMT) == S_IFDIR) + +typedef int bool; + +#define eq(a, b) (Strcmp(a, b) == 0) + +/* globone() flags */ +#define G_ERROR 0 /* default action: error if multiple words */ +#define G_IGNORE 1 /* ignore the rest of the words */ +#define G_APPEND 2 /* make a sentence by cat'ing the words */ + +/* + * Global flags + */ +bool chkstop; /* Warned of stopped jobs... allow exit */ +bool didfds; /* Have setup i/o fd's for child */ +bool doneinp; /* EOF indicator after reset from readc */ +bool exiterr; /* Exit if error or non-zero exit status */ +bool child; /* Child shell ... errors cause exit */ +bool haderr; /* Reset was because of an error */ +bool intty; /* Input is a tty */ +bool intact; /* We are interactive... therefore prompt */ +bool justpr; /* Just print because of :p hist mod */ +bool loginsh; /* We are a loginsh -> .login/.logout */ +bool neednote; /* Need to pnotify() */ +bool noexec; /* Don't execute, just syntax check */ +bool pjobs; /* want to print jobs if interrupted */ +bool setintr; /* Set interrupts on/off -> Wait intr... */ +bool timflg; /* Time the next waited for command */ +bool havhash; /* path hashing is available */ + +#ifdef FILEC +bool filec; /* doing filename expansion */ +#endif + +/* + * Global i/o info + */ +Char *arginp; /* Argument input for sh -c and internal `xx` */ +int onelflg; /* 2 -> need line for -t, 1 -> exit on read */ +Char *ffile; /* Name of shell file for $0 */ + +char *seterr; /* Error message from scanner/parser */ +Char *shtemp; /* Temp name for << shell files in /tmp */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/resource.h> + +struct timeval time0; /* Time at which the shell started */ +struct rusage ru0; + +/* + * Miscellany + */ +Char *doldol; /* Character pid for $$ */ +int backpid; /* Pid of the last background process */ +int uid, euid; /* Invokers uid */ +int gid, egid; /* Invokers gid */ +time_t chktim; /* Time mail last checked */ +int shpgrp; /* Pgrp of shell */ +int tpgrp; /* Terminal process group */ + +/* If tpgrp is -1, leave tty alone! */ +int opgrp; /* Initial pgrp and tty pgrp */ + + +/* + * To be able to redirect i/o for builtins easily, the shell moves the i/o + * descriptors it uses away from 0,1,2. + * Ideally these should be in units which are closed across exec's + * (this saves work) but for version 6, this is not usually possible. + * The desired initial values for these descriptors are F{SHIN,...}. + */ +int SHIN; /* Current shell input (script) */ +int SHOUT; /* Shell output */ +int SHERR; /* Diagnostic output... shell errs go here */ +int OLDSTD; /* Old standard input (def for cmds) */ + +/* + * Error control + * + * Errors in scanning and parsing set up an error message to be printed + * at the end and complete. Other errors always cause a reset. + * Because of source commands and .cshrc we need nested error catches. + */ + +#include <setjmp.h> +jmp_buf reslab; + +#define setexit() (setjmp(reslab)) +#define reset() longjmp(reslab, 1) + /* Should use structure assignment here */ +#define getexit(a) bcopy((char *)reslab, ((char *)(a)), sizeof reslab) +#define resexit(a) bcopy((char *)(a), (char *)reslab, sizeof reslab) + +Char *gointr; /* Label for an onintr transfer */ + +#include <signal.h> +sig_t parintr; /* Parents interrupt catch */ +sig_t parterm; /* Parents terminate catch */ + +/* + * Lexical definitions. + * + * All lexical space is allocated dynamically. + * The eighth/sixteenth bit of characters is used to prevent recognition, + * and eventually stripped. + */ +#define META 0200 +#define ASCII 0177 +#ifdef SHORT_STRINGS +#define CHAR 0377 +#define QUOTE 0100000 /* 16nth char bit used for 'ing */ +#define TRIM 0077777 /* Mask to strip quote bit */ +#else +#define CHAR 0177 +#define QUOTE 0200 /* Eighth char bit used for 'ing */ +#define TRIM 0177 /* Mask to strip quote bit */ +#endif + +int AsciiOnly; /* If set only 7 bits is expected in characters */ + +/* + * Each level of input has a buffered input structure. + * There are one or more blocks of buffered input for each level, + * exactly one if the input is seekable and tell is available. + * In other cases, the shell buffers enough blocks to keep all loops + * in the buffer. + */ +struct Bin { + off_t Bfseekp; /* Seek pointer */ + off_t Bfbobp; /* Seekp of beginning of buffers */ + off_t Bfeobp; /* Seekp of end of buffers */ + int Bfblocks; /* Number of buffer blocks */ + Char **Bfbuf; /* The array of buffer blocks */ +} B; + +/* + * This structure allows us to seek inside aliases + */ +struct Ain { + int type; +#define I_SEEK -1 /* Invalid seek */ +#define A_SEEK 0 /* Alias seek */ +#define F_SEEK 1 /* File seek */ +#define E_SEEK 2 /* Eval seek */ + union { + off_t _f_seek; + Char* _c_seek; + } fc; +#define f_seek fc._f_seek +#define c_seek fc._c_seek + Char **a_seek; +} ; +extern int aret; /* What was the last character returned */ +#define SEEKEQ(a, b) ((a)->type == (b)->type && \ + (a)->f_seek == (b)->f_seek && \ + (a)->a_seek == (b)->a_seek) + +#define fseekp B.Bfseekp +#define fbobp B.Bfbobp +#define feobp B.Bfeobp +#define fblocks B.Bfblocks +#define fbuf B.Bfbuf + +/* + * The shell finds commands in loops by reseeking the input + * For whiles, in particular, it reseeks to the beginning of the + * line the while was on; hence the while placement restrictions. + */ +struct Ain lineloc; + +bool cantell; /* Is current source tellable ? */ + +/* + * Input lines are parsed into doubly linked circular + * lists of words of the following form. + */ +struct wordent { + Char *word; + struct wordent *prev; + struct wordent *next; +}; + +/* + * During word building, both in the initial lexical phase and + * when expanding $ variable substitutions, expansion by `!' and `$' + * must be inhibited when reading ahead in routines which are themselves + * processing `!' and `$' expansion or after characters such as `\' or in + * quotations. The following flags are passed to the getC routines + * telling them which of these substitutions are appropriate for the + * next character to be returned. + */ +#define DODOL 1 +#define DOEXCL 2 +#define DOALL DODOL|DOEXCL + +/* + * Labuf implements a general buffer for lookahead during lexical operations. + * Text which is to be placed in the input stream can be stuck here. + * We stick parsed ahead $ constructs during initial input, + * process id's from `$$', and modified variable values (from qualifiers + * during expansion in sh.dol.c) here. + */ +Char *lap; + +/* + * Parser structure + * + * Each command is parsed to a tree of command structures and + * flags are set bottom up during this process, to be propagated down + * as needed during the semantics/exeuction pass (sh.sem.c). + */ +struct command { + short t_dtyp; /* Type of node */ +#define NODE_COMMAND 1 /* t_dcom <t_dlef >t_drit */ +#define NODE_PAREN 2 /* ( t_dspr ) <t_dlef >t_drit */ +#define NODE_PIPE 3 /* t_dlef | t_drit */ +#define NODE_LIST 4 /* t_dlef ; t_drit */ +#define NODE_OR 5 /* t_dlef || t_drit */ +#define NODE_AND 6 /* t_dlef && t_drit */ + short t_dflg; /* Flags, e.g. F_AMPERSAND|... */ +#define F_SAVE (F_NICE|F_TIME|F_NOHUP) /* save these when re-doing */ + +#define F_AMPERSAND (1<<0) /* executes in background */ +#define F_APPEND (1<<1) /* output is redirected >> */ +#define F_PIPEIN (1<<2) /* input is a pipe */ +#define F_PIPEOUT (1<<3) /* output is a pipe */ +#define F_NOFORK (1<<4) /* don't fork, last ()ized cmd */ +#define F_NOINTERRUPT (1<<5) /* should be immune from intr's */ +/* spare */ +#define F_STDERR (1<<7) /* redirect unit 2 with unit 1 */ +#define F_OVERWRITE (1<<8) /* output was ! */ +#define F_READ (1<<9) /* input redirection is << */ +#define F_REPEAT (1<<10) /* reexec aft if, repeat,... */ +#define F_NICE (1<<11) /* t_nice is meaningful */ +#define F_NOHUP (1<<12) /* nohup this command */ +#define F_TIME (1<<13) /* time this command */ + union { + Char *T_dlef; /* Input redirect word */ + struct command *T_dcar; /* Left part of list/pipe */ + } L; + union { + Char *T_drit; /* Output redirect word */ + struct command *T_dcdr; /* Right part of list/pipe */ + } R; +#define t_dlef L.T_dlef +#define t_dcar L.T_dcar +#define t_drit R.T_drit +#define t_dcdr R.T_dcdr + Char **t_dcom; /* Command/argument vector */ + struct command *t_dspr; /* Pointer to ()'d subtree */ + int t_nice; +}; + + +/* + * These are declared here because they want to be + * initialized in sh.init.c (to allow them to be made readonly) + */ + +extern struct biltins { + char *bname; + void (*bfunct) __P((Char **, struct command *)); + short minargs, maxargs; +} bfunc[]; +extern int nbfunc; + +extern struct srch { + char *s_name; + short s_value; +} srchn[]; +extern int nsrchn; + +/* + * The keywords for the parser + */ +#define T_BREAK 0 +#define T_BRKSW 1 +#define T_CASE 2 +#define T_DEFAULT 3 +#define T_ELSE 4 +#define T_END 5 +#define T_ENDIF 6 +#define T_ENDSW 7 +#define T_EXIT 8 +#define T_FOREACH 9 +#define T_GOTO 10 +#define T_IF 11 +#define T_LABEL 12 +#define T_LET 13 +#define T_SET 14 +#define T_SWITCH 15 +#define T_TEST 16 +#define T_THEN 17 +#define T_WHILE 18 + +/* + * Structure defining the existing while/foreach loops at this + * source level. Loops are implemented by seeking back in the + * input. For foreach (fe), the word list is attached here. + */ +struct whyle { + struct Ain w_start; /* Point to restart loop */ + struct Ain w_end; /* End of loop (0 if unknown) */ + Char **w_fe, **w_fe0; /* Current/initial wordlist for fe */ + Char *w_fename; /* Name for fe */ + struct whyle *w_next; /* Next (more outer) loop */ +} *whyles; + +/* + * Variable structure + * + * Aliases and variables are stored in AVL balanced binary trees. + */ +struct varent { + Char **vec; /* Array of words which is the value */ + Char *v_name; /* Name of variable/alias */ + struct varent *v_link[3]; /* The links, see below */ + int v_bal; /* Balance factor */ +} shvhed, aliases; + +#define v_left v_link[0] +#define v_right v_link[1] +#define v_parent v_link[2] + +struct varent *adrof1(); + +#define adrof(v) adrof1(v, &shvhed) +#define value(v) value1(v, &shvhed) + +/* + * The following are for interfacing redo substitution in + * aliases to the lexical routines. + */ +struct wordent *alhistp; /* Argument list (first) */ +struct wordent *alhistt; /* Node after last in arg list */ +Char **alvec, *alvecp; /* The (remnants of) alias vector */ + +/* + * Filename/command name expansion variables + */ +int gflag; /* After tglob -> is globbing needed? */ + +#define MAXVARLEN 30 /* Maximum number of char in a variable name */ + +/* + * Variables for filename expansion + */ +extern Char **gargv; /* Pointer to the (stack) arglist */ +extern long gargc; /* Number args in gargv */ + +/* + * Variables for command expansion. + */ +extern Char **pargv; /* Pointer to the argv list space */ +extern long pargc; /* Count of arguments in pargv */ +Char *pargs; /* Pointer to start current word */ +long pnleft; /* Number of chars left in pargs */ +Char *pargcp; /* Current index into pargs */ + +/* + * History list + * + * Each history list entry contains an embedded wordlist + * from the scanner, a number for the event, and a reference count + * to aid in discarding old entries. + * + * Essentially "invisible" entries are put on the history list + * when history substitution includes modifiers, and thrown away + * at the next discarding since their event numbers are very negative. + */ +struct Hist { + struct wordent Hlex; + int Hnum; + int Href; + struct Hist *Hnext; +} Histlist; + +struct wordent paraml; /* Current lexical word list */ +int eventno; /* Next events number */ +int lastev; /* Last event reference (default) */ + +Char HIST; /* history invocation character */ +Char HISTSUB; /* auto-substitute character */ + +/* + * strings.h: + */ +#ifndef SHORT_STRINGS +#define Strchr(a, b) strchr(a, b) +#define Strrchr(a, b) strrchr(a, b) +#define Strcat(a, b) strcat(a, b) +#define Strncat(a, b, c) strncat(a, b, c) +#define Strcpy(a, b) strcpy(a, b) +#define Strncpy(a, b, c) strncpy(a, b, c) +#define Strlen(a) strlen(a) +#define Strcmp(a, b) strcmp(a, b) +#define Strncmp(a, b, c) strncmp(a, b, c) + +#define Strspl(a, b) strspl(a, b) +#define Strsave(a) strsave(a) +#define Strend(a) strend(a) +#define Strstr(a, b) strstr(a, b) + +#define str2short(a) (a) +#define blk2short(a) saveblk(a) +#define short2blk(a) saveblk(a) +#define short2str(a) strip(a) +#else +#define Strchr(a, b) s_strchr(a, b) +#define Strrchr(a, b) s_strrchr(a, b) +#define Strcat(a, b) s_strcat(a, b) +#define Strncat(a, b, c) s_strncat(a, b, c) +#define Strcpy(a, b) s_strcpy(a, b) +#define Strncpy(a, b, c) s_strncpy(a, b, c) +#define Strlen(a) s_strlen(a) +#define Strcmp(a, b) s_strcmp(a, b) +#define Strncmp(a, b, c) s_strncmp(a, b, c) + +#define Strspl(a, b) s_strspl(a, b) +#define Strsave(a) s_strsave(a) +#define Strend(a) s_strend(a) +#define Strstr(a, b) s_strstr(a, b) +#endif + +/* + * setname is a macro to save space (see sh.err.c) + */ +char *bname; + +#define setname(a) (bname = (a)) + +Char *Vsav; +Char *Vdp; +Char *Vexpath; +char **Vt; + +Char **evalvec; +Char *evalp; + +/* word_chars is set by default to WORD_CHARS but can be overridden by + the worchars variable--if unset, reverts to WORD_CHARS */ + +Char *word_chars; + +#define WORD_CHARS "*?_-.[]~=" /* default chars besides alnums in words */ + +Char *STR_SHELLPATH; + +#include <paths.h> +#ifdef _PATH_BSHELL +Char *STR_BSHELL; +#endif +Char *STR_WORD_CHARS; +Char **STR_environ; diff --git a/bin/csh/dir.c b/bin/csh/dir.c new file mode 100644 index 000000000000..04efd3f31336 --- /dev/null +++ b/bin/csh/dir.c @@ -0,0 +1,930 @@ +/*- + * Copyright (c) 1980, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * 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[] = "@(#)dir.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#if __STDC__ +# include <stdarg.h> +#else +# include <varargs.h> +#endif + +#include "csh.h" +#include "dir.h" +#include "extern.h" + +/* Directory management. */ + +static struct directory + *dfind __P((Char *)); +static Char *dfollow __P((Char *)); +static void printdirs __P((void)); +static Char *dgoto __P((Char *)); +static void dnewcwd __P((struct directory *)); +static void dset __P((Char *)); + +struct directory dhead; /* "head" of loop */ +int printd; /* force name to be printed */ + +static int dirflag = 0; + +/* + * dinit - initialize current working directory + */ +void +dinit(hp) + Char *hp; +{ + register char *tcp; + register Char *cp; + register struct directory *dp; + char path[MAXPATHLEN]; + static char *emsg = "csh: Trying to start from \"%s\"\n"; + + /* Don't believe the login shell home, because it may be a symlink */ + tcp = getwd(path); /* see ngetwd.c for System V version */ + if (tcp == NULL || *tcp == '\0') { + (void) fprintf(csherr, "csh: %s\n", path); + if (hp && *hp) { + tcp = short2str(hp); + if (chdir(tcp) == -1) + cp = NULL; + else + cp = hp; + (void) fprintf(csherr, emsg, vis_str(hp)); + } + else + cp = NULL; + if (cp == NULL) { + (void) fprintf(csherr, emsg, "/"); + if (chdir("/") == -1) + /* I am not even try to print an error message! */ + xexit(1); + cp = SAVE("/"); + } + } + else { + struct stat swd, shp; + + /* + * See if $HOME is the working directory we got and use that + */ + if (hp && *hp && + stat(tcp, &swd) != -1 && stat(short2str(hp), &shp) != -1 && + swd.st_dev == shp.st_dev && swd.st_ino == shp.st_ino) + cp = hp; + else { + char *cwd; + + /* + * use PWD if we have it (for subshells) + */ + if ((cwd = getenv("PWD")) != NULL) { + if (stat(cwd, &shp) != -1 && swd.st_dev == shp.st_dev && + swd.st_ino == shp.st_ino) + tcp = cwd; + } + cp = dcanon(SAVE(tcp), STRNULL); + } + } + + dp = (struct directory *) xcalloc(sizeof(struct directory), 1); + dp->di_name = Strsave(cp); + dp->di_count = 0; + dhead.di_next = dhead.di_prev = dp; + dp->di_next = dp->di_prev = &dhead; + printd = 0; + dnewcwd(dp); +} + +static void +dset(dp) +Char *dp; +{ + /* + * Don't call set() directly cause if the directory contains ` or + * other junk characters glob will fail. + */ + register Char **vec = (Char **) xmalloc((size_t) (2 * sizeof(Char **))); + + vec[0] = Strsave(dp); + vec[1] = 0; + setq(STRcwd, vec, &shvhed); + Setenv(STRPWD, dp); +} + +#define DIR_LONG 1 +#define DIR_VERT 2 +#define DIR_LINE 4 + +static void +skipargs(v, str) + Char ***v; + char *str; +{ + Char **n = *v, *s; + + dirflag = 0; + for (n++; *n != NULL && (*n)[0] == '-'; n++) + for (s = &((*n)[1]); *s; s++) + switch (*s) { + case 'l': + dirflag |= DIR_LONG; + break; + case 'v': + dirflag |= DIR_VERT; + break; + case 'n': + dirflag |= DIR_LINE; + break; + default: + stderror(ERR_DIRUS, vis_str(**v), str); + break; + } + *v = n; +} + +/* + * dodirs - list all directories in directory loop + */ +void +/*ARGSUSED*/ +dodirs(v, t) + Char **v; + struct command *t; +{ + skipargs(&v, ""); + + if (*v != NULL) + stderror(ERR_DIRUS, "dirs", ""); + printdirs(); +} + +static void +printdirs() +{ + register struct directory *dp; + Char *s, *hp = value(STRhome); + int idx, len, cur; + + if (*hp == '\0') + hp = NULL; + dp = dcwd; + idx = 0; + cur = 0; + do { + if (dp == &dhead) + continue; + if (dirflag & DIR_VERT) { + (void) fprintf(cshout, "%d\t", idx++); + cur = 0; + } + if (!(dirflag & DIR_LONG) && hp != NULL && !eq(hp, STRslash) && + (len = Strlen(hp), Strncmp(hp, dp->di_name, len) == 0) && + (dp->di_name[len] == '\0' || dp->di_name[len] == '/')) + len = Strlen(s = (dp->di_name + len)) + 2; + else + len = Strlen(s = dp->di_name) + 1; + + cur += len; + if ((dirflag & DIR_LINE) && cur >= 80 - 1 && len < 80) { + (void) fprintf(cshout, "\n"); + cur = len; + } + (void) fprintf(cshout, s != dp->di_name ? "~%s%c" : "%s%c", + vis_str(s), (dirflag & DIR_VERT) ? '\n' : ' '); + } while ((dp = dp->di_prev) != dcwd); + if (!(dirflag & DIR_VERT)) + (void) fprintf(cshout, "\n"); +} + +void +dtildepr(home, dir) + register Char *home, *dir; +{ + + if (!eq(home, STRslash) && prefix(home, dir)) + (void) fprintf(cshout, "~%s", vis_str(dir + Strlen(home))); + else + (void) fprintf(cshout, "%s", vis_str(dir)); +} + +void +dtilde() +{ + struct directory *d = dcwd; + + do { + if (d == &dhead) + continue; + d->di_name = dcanon(d->di_name, STRNULL); + } while ((d = d->di_prev) != dcwd); + + dset(dcwd->di_name); +} + + +/* dnormalize(): + * If the name starts with . or .. then we might need to normalize + * it depending on the symbolic link flags + */ +Char * +dnormalize(cp) + Char *cp; +{ + +#define UC (unsigned char) +#define ISDOT(c) (UC(c)[0] == '.' && ((UC(c)[1] == '\0') || (UC(c)[1] == '/'))) +#define ISDOTDOT(c) (UC(c)[0] == '.' && ISDOT(&((c)[1]))) + + if ((unsigned char) cp[0] == '/') + return (Strsave(cp)); + + if (adrof(STRignore_symlinks)) { + int dotdot = 0; + Char *dp, *cwd; + + cwd = (Char *) xmalloc((size_t) ((Strlen(dcwd->di_name) + 3) * + sizeof(Char))); + (void) Strcpy(cwd, dcwd->di_name); + + /* + * Ignore . and count ..'s + */ + while (*cp) { + if (ISDOT(cp)) { + if (*++cp) + cp++; + } + else if (ISDOTDOT(cp)) { + dotdot++; + cp += 2; + if (*cp) + cp++; + } + else + break; + } + while (dotdot > 0) + if ((dp = Strrchr(cwd, '/'))) { + *dp = '\0'; + dotdot--; + } + else + break; + + if (*cp) { + cwd[dotdot = Strlen(cwd)] = '/'; + cwd[dotdot + 1] = '\0'; + dp = Strspl(cwd, cp); + xfree((ptr_t) cwd); + return dp; + } + else { + if (!*cwd) { + cwd[0] = '/'; + cwd[1] = '\0'; + } + return cwd; + } + } + return Strsave(cp); +} + +/* + * dochngd - implement chdir command. + */ +void +/*ARGSUSED*/ +dochngd(v, t) + Char **v; + struct command *t; +{ + register Char *cp; + register struct directory *dp; + + skipargs(&v, " [<dir>]"); + printd = 0; + if (*v == NULL) { + if ((cp = value(STRhome)) == NULL || *cp == 0) + stderror(ERR_NAME | ERR_NOHOMEDIR); + if (chdir(short2str(cp)) < 0) + stderror(ERR_NAME | ERR_CANTCHANGE); + cp = Strsave(cp); + } + else if (v[1] != NULL) { + stderror(ERR_NAME | ERR_TOOMANY); + /* NOTREACHED */ + return; + } + else if ((dp = dfind(*v)) != 0) { + char *tmp; + + printd = 1; + if (chdir(tmp = short2str(dp->di_name)) < 0) + stderror(ERR_SYSTEM, tmp, strerror(errno)); + dcwd->di_prev->di_next = dcwd->di_next; + dcwd->di_next->di_prev = dcwd->di_prev; + dfree(dcwd); + dnewcwd(dp); + return; + } + else + cp = dfollow(*v); + dp = (struct directory *) xcalloc(sizeof(struct directory), 1); + dp->di_name = cp; + dp->di_count = 0; + dp->di_next = dcwd->di_next; + dp->di_prev = dcwd->di_prev; + dp->di_prev->di_next = dp; + dp->di_next->di_prev = dp; + dfree(dcwd); + dnewcwd(dp); +} + +static Char * +dgoto(cp) + Char *cp; +{ + Char *dp; + + if (*cp != '/') { + register Char *p, *q; + int cwdlen; + + for (p = dcwd->di_name; *p++;) + continue; + if ((cwdlen = p - dcwd->di_name - 1) == 1) /* root */ + cwdlen = 0; + for (p = cp; *p++;) + continue; + dp = (Char *) xmalloc((size_t)((cwdlen + (p - cp) + 1) * sizeof(Char))); + for (p = dp, q = dcwd->di_name; (*p++ = *q++) != '\0';) + continue; + if (cwdlen) + p[-1] = '/'; + else + p--; /* don't add a / after root */ + for (q = cp; (*p++ = *q++) != '\0';) + continue; + xfree((ptr_t) cp); + cp = dp; + dp += cwdlen; + } + else + dp = cp; + + cp = dcanon(cp, dp); + return cp; +} + +/* + * dfollow - change to arg directory; fall back on cdpath if not valid + */ +static Char * +dfollow(cp) + register Char *cp; +{ + register Char *dp; + struct varent *c; + char ebuf[MAXPATHLEN]; + int serrno; + + cp = globone(cp, G_ERROR); + /* + * if we are ignoring symlinks, try to fix relatives now. + */ + dp = dnormalize(cp); + if (chdir(short2str(dp)) >= 0) { + xfree((ptr_t) cp); + return dgoto(dp); + } + else { + xfree((ptr_t) dp); + if (chdir(short2str(cp)) >= 0) + return dgoto(cp); + serrno = errno; + } + + if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp) + && (c = adrof(STRcdpath))) { + Char **cdp; + register Char *p; + Char buf[MAXPATHLEN]; + + for (cdp = c->vec; *cdp; cdp++) { + for (dp = buf, p = *cdp; (*dp++ = *p++) != '\0';) + continue; + dp[-1] = '/'; + for (p = cp; (*dp++ = *p++) != '\0';) + continue; + if (chdir(short2str(buf)) >= 0) { + printd = 1; + xfree((ptr_t) cp); + cp = Strsave(buf); + return dgoto(cp); + } + } + } + dp = value(cp); + if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) { + xfree((ptr_t) cp); + cp = Strsave(dp); + printd = 1; + return dgoto(cp); + } + (void) strcpy(ebuf, short2str(cp)); + xfree((ptr_t) cp); + stderror(ERR_SYSTEM, ebuf, strerror(serrno)); + return (NULL); +} + + +/* + * dopushd - push new directory onto directory stack. + * with no arguments exchange top and second. + * with numeric argument (+n) bring it to top. + */ +void +/*ARGSUSED*/ +dopushd(v, t) + Char **v; + struct command *t; +{ + register struct directory *dp; + + skipargs(&v, " [<dir>|+<n>]"); + printd = 1; + if (*v == NULL) { + char *tmp; + + if ((dp = dcwd->di_prev) == &dhead) + dp = dhead.di_prev; + if (dp == dcwd) + stderror(ERR_NAME | ERR_NODIR); + if (chdir(tmp = short2str(dp->di_name)) < 0) + stderror(ERR_SYSTEM, tmp, strerror(errno)); + dp->di_prev->di_next = dp->di_next; + dp->di_next->di_prev = dp->di_prev; + dp->di_next = dcwd->di_next; + dp->di_prev = dcwd; + dcwd->di_next->di_prev = dp; + dcwd->di_next = dp; + } + else if (v[1] != NULL) { + stderror(ERR_NAME | ERR_TOOMANY); + /* NOTREACHED */ + return; + } + else if ((dp = dfind(*v)) != NULL) { + char *tmp; + + if (chdir(tmp = short2str(dp->di_name)) < 0) + stderror(ERR_SYSTEM, tmp, strerror(errno)); + } + else { + register Char *ccp; + + ccp = dfollow(*v); + dp = (struct directory *) xcalloc(sizeof(struct directory), 1); + dp->di_name = ccp; + dp->di_count = 0; + dp->di_prev = dcwd; + dp->di_next = dcwd->di_next; + dcwd->di_next = dp; + dp->di_next->di_prev = dp; + } + dnewcwd(dp); +} + +/* + * dfind - find a directory if specified by numeric (+n) argument + */ +static struct directory * +dfind(cp) + register Char *cp; +{ + register struct directory *dp; + register int i; + register Char *ep; + + if (*cp++ != '+') + return (0); + for (ep = cp; Isdigit(*ep); ep++) + continue; + if (*ep) + return (0); + i = getn(cp); + if (i <= 0) + return (0); + for (dp = dcwd; i != 0; i--) { + if ((dp = dp->di_prev) == &dhead) + dp = dp->di_prev; + if (dp == dcwd) + stderror(ERR_NAME | ERR_DEEP); + } + return (dp); +} + +/* + * dopopd - pop a directory out of the directory stack + * with a numeric argument just discard it. + */ +void +/*ARGSUSED*/ +dopopd(v, t) + Char **v; + struct command *t; +{ + register struct directory *dp, *p = NULL; + + skipargs(&v, " [+<n>]"); + printd = 1; + if (*v == NULL) + dp = dcwd; + else if (v[1] != NULL) { + stderror(ERR_NAME | ERR_TOOMANY); + /* NOTREACHED */ + return; + } + else if ((dp = dfind(*v)) == 0) + stderror(ERR_NAME | ERR_BADDIR); + if (dp->di_prev == &dhead && dp->di_next == &dhead) + stderror(ERR_NAME | ERR_EMPTY); + if (dp == dcwd) { + char *tmp; + + if ((p = dp->di_prev) == &dhead) + p = dhead.di_prev; + if (chdir(tmp = short2str(p->di_name)) < 0) + stderror(ERR_SYSTEM, tmp, strerror(errno)); + } + dp->di_prev->di_next = dp->di_next; + dp->di_next->di_prev = dp->di_prev; + if (dp == dcwd) + dnewcwd(p); + else { + printdirs(); + } + dfree(dp); +} + +/* + * dfree - free the directory (or keep it if it still has ref count) + */ +void +dfree(dp) + register struct directory *dp; +{ + + if (dp->di_count != 0) { + dp->di_next = dp->di_prev = 0; + } + else { + xfree((char *) dp->di_name); + xfree((ptr_t) dp); + } +} + +/* + * dcanon - canonicalize the pathname, removing excess ./ and ../ etc. + * we are of course assuming that the file system is standardly + * constructed (always have ..'s, directories have links) + */ +Char * +dcanon(cp, p) + register Char *cp, *p; +{ + register Char *sp; + register Char *p1, *p2; /* general purpose */ + bool slash; + + Char link[MAXPATHLEN]; + char tlink[MAXPATHLEN]; + int cc; + Char *newcp; + + /* + * christos: if the path given does not start with a slash prepend cwd. If + * cwd does not start with a path or the result would be too long abort(). + */ + if (*cp != '/') { + Char tmpdir[MAXPATHLEN]; + + p1 = value(STRcwd); + if (p1 == NULL || *p1 != '/') + abort(); + if (Strlen(p1) + Strlen(cp) + 1 >= MAXPATHLEN) + abort(); + (void) Strcpy(tmpdir, p1); + (void) Strcat(tmpdir, STRslash); + (void) Strcat(tmpdir, cp); + xfree((ptr_t) cp); + cp = p = Strsave(tmpdir); + } + + while (*p) { /* for each component */ + sp = p; /* save slash address */ + while (*++p == '/') /* flush extra slashes */ + continue; + if (p != ++sp) + for (p1 = sp, p2 = p; (*p1++ = *p2++) != '\0';) + continue; + p = sp; /* save start of component */ + slash = 0; + while (*++p) /* find next slash or end of path */ + if (*p == '/') { + slash = 1; + *p = 0; + break; + } + + if (*sp == '\0') /* if component is null */ + if (--sp == cp) /* if path is one char (i.e. /) */ + break; + else + *sp = '\0'; + else if (sp[0] == '.' && sp[1] == 0) { + if (slash) { + for (p1 = sp, p2 = p + 1; (*p1++ = *p2++) != '\0';) + continue; + p = --sp; + } + else if (--sp != cp) + *sp = '\0'; + } + else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) { + /* + * We have something like "yyy/xxx/..", where "yyy" can be null or + * a path starting at /, and "xxx" is a single component. Before + * compressing "xxx/..", we want to expand "yyy/xxx", if it is a + * symbolic link. + */ + *--sp = 0; /* form the pathname for readlink */ + if (sp != cp && !adrof(STRignore_symlinks) && + (cc = readlink(short2str(cp), tlink, + sizeof tlink)) >= 0) { + (void) Strcpy(link, str2short(tlink)); + link[cc] = '\0'; + + if (slash) + *p = '/'; + /* + * Point p to the '/' in "/..", and restore the '/'. + */ + *(p = sp) = '/'; + /* + * find length of p + */ + for (p1 = p; *p1++;) + continue; + if (*link != '/') { + /* + * Relative path, expand it between the "yyy/" and the + * "/..". First, back sp up to the character past "yyy/". + */ + while (*--sp != '/') + continue; + sp++; + *sp = 0; + /* + * New length is "yyy/" + link + "/.." and rest + */ + p1 = newcp = (Char *) xmalloc((size_t) + (((sp - cp) + cc + (p1 - p)) * + sizeof(Char))); + /* + * Copy new path into newcp + */ + for (p2 = cp; (*p1++ = *p2++) != '\0';) + continue; + for (p1--, p2 = link; (*p1++ = *p2++) != '\0';) + continue; + for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) + continue; + /* + * Restart canonicalization at expanded "/xxx". + */ + p = sp - cp - 1 + newcp; + } + else { + /* + * New length is link + "/.." and rest + */ + p1 = newcp = (Char *) xmalloc((size_t) + ((cc + (p1 - p)) * sizeof(Char))); + /* + * Copy new path into newcp + */ + for (p2 = link; (*p1++ = *p2++) != '\0';) + continue; + for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) + continue; + /* + * Restart canonicalization at beginning + */ + p = newcp; + } + xfree((ptr_t) cp); + cp = newcp; + continue; /* canonicalize the link */ + } + *sp = '/'; + if (sp != cp) + while (*--sp != '/') + continue; + if (slash) { + for (p1 = sp + 1, p2 = p + 1; (*p1++ = *p2++) != '\0';) + continue; + p = sp; + } + else if (cp == sp) + *++sp = '\0'; + else + *sp = '\0'; + } + else { /* normal dir name (not . or .. or nothing) */ + + if (sp != cp && adrof(STRchase_symlinks) && + !adrof(STRignore_symlinks) && + (cc = readlink(short2str(cp), tlink, + sizeof tlink)) >= 0) { + (void) Strcpy(link, str2short(tlink)); + link[cc] = '\0'; + + /* + * restore the '/'. + */ + if (slash) + *p = '/'; + + /* + * point sp to p (rather than backing up). + */ + sp = p; + + /* + * find length of p + */ + for (p1 = p; *p1++;) + continue; + if (*link != '/') { + /* + * Relative path, expand it between the "yyy/" and the + * remainder. First, back sp up to the character past + * "yyy/". + */ + while (*--sp != '/') + continue; + sp++; + *sp = 0; + /* + * New length is "yyy/" + link + "/.." and rest + */ + p1 = newcp = (Char *) xmalloc((size_t) + (((sp - cp) + cc + (p1 - p)) + * sizeof(Char))); + /* + * Copy new path into newcp + */ + for (p2 = cp; (*p1++ = *p2++) != '\0';) + continue; + for (p1--, p2 = link; (*p1++ = *p2++) != '\0';) + continue; + for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) + continue; + /* + * Restart canonicalization at expanded "/xxx". + */ + p = sp - cp - 1 + newcp; + } + else { + /* + * New length is link + the rest + */ + p1 = newcp = (Char *) xmalloc((size_t) + ((cc + (p1 - p)) * sizeof(Char))); + /* + * Copy new path into newcp + */ + for (p2 = link; (*p1++ = *p2++) != '\0';) + continue; + for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) + continue; + /* + * Restart canonicalization at beginning + */ + p = newcp; + } + xfree((ptr_t) cp); + cp = newcp; + continue; /* canonicalize the link */ + } + if (slash) + *p = '/'; + } + } + + /* + * fix home... + */ + p1 = value(STRhome); + cc = Strlen(p1); + /* + * See if we're not in a subdir of STRhome + */ + if (p1 && *p1 == '/' && + (Strncmp(p1, cp, cc) != 0 || (cp[cc] != '/' && cp[cc] != '\0'))) { + static ino_t home_ino = -1; + static dev_t home_dev = -1; + static Char *home_ptr = NULL; + struct stat statbuf; + + /* + * Get dev and ino of STRhome + */ + if (home_ptr != p1 && + stat(short2str(p1), &statbuf) != -1) { + home_dev = statbuf.st_dev; + home_ino = statbuf.st_ino; + home_ptr = p1; + } + /* + * Start comparing dev & ino backwards + */ + p2 = Strcpy(link, cp); + for (sp = NULL; *p2 && stat(short2str(p2), &statbuf) != -1;) { + if (statbuf.st_dev == home_dev && + statbuf.st_ino == home_ino) { + sp = (Char *) - 1; + break; + } + if ((sp = Strrchr(p2, '/')) != NULL) + *sp = '\0'; + } + /* + * See if we found it + */ + if (*p2 && sp == (Char *) -1) { + /* + * Use STRhome to make '~' work + */ + newcp = Strspl(p1, cp + Strlen(p2)); + xfree((ptr_t) cp); + cp = newcp; + } + } + return cp; +} + + +/* + * dnewcwd - make a new directory in the loop the current one + */ +static void +dnewcwd(dp) + register struct directory *dp; +{ + dcwd = dp; + dset(dcwd->di_name); + if (printd && !(adrof(STRpushdsilent))) + printdirs(); +} diff --git a/bin/csh/dir.h b/bin/csh/dir.h new file mode 100644 index 000000000000..dcd155b67b5b --- /dev/null +++ b/bin/csh/dir.h @@ -0,0 +1,45 @@ +/*- + * Copyright (c) 1980, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * 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. + * + * @(#)dir.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * Structure for entries in directory stack. + */ +struct directory { + struct directory *di_next; /* next in loop */ + struct directory *di_prev; /* prev in loop */ + unsigned short *di_count; /* refcount of processes */ + Char *di_name; /* actual name */ +}; +struct directory *dcwd; /* the one we are in now */ diff --git a/bin/csh/dol.c b/bin/csh/dol.c new file mode 100644 index 000000000000..fcd4c652896b --- /dev/null +++ b/bin/csh/dol.c @@ -0,0 +1,985 @@ +/*- + * Copyright (c) 1980, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * 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[] = "@(#)dol.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <fcntl.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#if __STDC__ +# include <stdarg.h> +#else +# include <varargs.h> +#endif + +#include "csh.h" +#include "extern.h" + +/* + * These routines perform variable substitution and quoting via ' and ". + * To this point these constructs have been preserved in the divided + * input words. Here we expand variables and turn quoting via ' and " into + * QUOTE bits on characters (which prevent further interpretation). + * If the `:q' modifier was applied during history expansion, then + * some QUOTEing may have occurred already, so we dont "trim()" here. + */ + +static int Dpeekc, Dpeekrd; /* Peeks for DgetC and Dreadc */ +static Char *Dcp, **Dvp; /* Input vector for Dreadc */ + +#define DEOF -1 + +#define unDgetC(c) Dpeekc = c + +#define QUOTES (_QF|_QB|_ESC) /* \ ' " ` */ + +/* + * The following variables give the information about the current + * $ expansion, recording the current word position, the remaining + * words within this expansion, the count of remaining words, and the + * information about any : modifier which is being applied. + */ +#define MAXWLEN (BUFSIZ - 4) +#define MAXMOD MAXWLEN /* This cannot overflow */ +static Char *dolp; /* Remaining chars from this word */ +static Char **dolnxt; /* Further words */ +static int dolcnt; /* Count of further words */ +static Char dolmod[MAXMOD]; /* : modifier character */ +static int dolnmod; /* Number of modifiers */ +static int dolmcnt; /* :gx -> 10000, else 1 */ +static int dolwcnt; /* :wx -> 10000, else 1 */ + +static void Dfix2 __P((Char **)); +static Char *Dpack __P((Char *, Char *)); +static int Dword __P((void)); +static void dolerror __P((Char *)); +static int DgetC __P((int)); +static void Dgetdol __P((void)); +static void fixDolMod __P((void)); +static void setDolp __P((Char *)); +static void unDredc __P((int)); +static int Dredc __P((void)); +static void Dtestq __P((int)); + + +/* + * Fix up the $ expansions and quotations in the + * argument list to command t. + */ +void +Dfix(t) + register struct command *t; +{ + register Char **pp; + register Char *p; + + if (noexec) + return; + /* Note that t_dcom isn't trimmed thus !...:q's aren't lost */ + for (pp = t->t_dcom; (p = *pp++) != NULL;) + for (; *p; p++) { + if (cmap(*p, _DOL | QUOTES)) { /* $, \, ', ", ` */ + Dfix2(t->t_dcom); /* found one */ + blkfree(t->t_dcom); + t->t_dcom = gargv; + gargv = 0; + return; + } + } +} + +/* + * $ substitute one word, for i/o redirection + */ +Char * +Dfix1(cp) + register Char *cp; +{ + Char *Dv[2]; + + if (noexec) + return (0); + Dv[0] = cp; + Dv[1] = NULL; + Dfix2(Dv); + if (gargc != 1) { + setname(vis_str(cp)); + stderror(ERR_NAME | ERR_AMBIG); + } + cp = Strsave(gargv[0]); + blkfree(gargv), gargv = 0; + return (cp); +} + +/* + * Subroutine to do actual fixing after state initialization. + */ +static void +Dfix2(v) + Char **v; +{ + ginit(); /* Initialize glob's area pointers */ + Dvp = v; + Dcp = STRNULL; /* Setup input vector for Dreadc */ + unDgetC(0); + unDredc(0); /* Clear out any old peeks (at error) */ + dolp = 0; + dolcnt = 0; /* Clear out residual $ expands (...) */ + while (Dword()) + continue; +} + +/* + * Pack up more characters in this word + */ +static Char * +Dpack(wbuf, wp) + Char *wbuf, *wp; +{ + register int c; + register int i = MAXWLEN - (wp - wbuf); + + for (;;) { + c = DgetC(DODOL); + if (c == '\\') { + c = DgetC(0); + if (c == DEOF) { + unDredc(c); + *wp = 0; + Gcat(STRNULL, wbuf); + return (NULL); + } + if (c == '\n') + c = ' '; + else + c |= QUOTE; + } + if (c == DEOF) { + unDredc(c); + *wp = 0; + Gcat(STRNULL, wbuf); + return (NULL); + } + if (cmap(c, _SP | _NL | _QF | _QB)) { /* sp \t\n'"` */ + unDgetC(c); + if (cmap(c, QUOTES)) + return (wp); + *wp++ = 0; + Gcat(STRNULL, wbuf); + return (NULL); + } + if (--i <= 0) + stderror(ERR_WTOOLONG); + *wp++ = c; + } +} + +/* + * Get a word. This routine is analogous to the routine + * word() in sh.lex.c for the main lexical input. One difference + * here is that we don't get a newline to terminate our expansion. + * Rather, DgetC will return a DEOF when we hit the end-of-input. + */ +static int +Dword() +{ + register int c, c1; + Char wbuf[BUFSIZ]; + register Char *wp = wbuf; + register int i = MAXWLEN; + register bool dolflg; + bool sofar = 0, done = 0; + + while (!done) { + done = 1; + c = DgetC(DODOL); + switch (c) { + + case DEOF: + if (sofar == 0) + return (0); + /* finish this word and catch the code above the next time */ + unDredc(c); + /* fall into ... */ + + case '\n': + *wp = 0; + Gcat(STRNULL, wbuf); + return (1); + + case ' ': + case '\t': + done = 0; + break; + + case '`': + /* We preserve ` quotations which are done yet later */ + *wp++ = c, --i; + case '\'': + case '"': + /* + * Note that DgetC never returns a QUOTES character from an + * expansion, so only true input quotes will get us here or out. + */ + c1 = c; + dolflg = c1 == '"' ? DODOL : 0; + for (;;) { + c = DgetC(dolflg); + if (c == c1) + break; + if (c == '\n' || c == DEOF) + stderror(ERR_UNMATCHED, c1); + if ((c & (QUOTE | TRIM)) == ('\n' | QUOTE)) + --wp, ++i; + if (--i <= 0) + stderror(ERR_WTOOLONG); + switch (c1) { + + case '"': + /* + * Leave any `s alone for later. Other chars are all + * quoted, thus `...` can tell it was within "...". + */ + *wp++ = c == '`' ? '`' : c | QUOTE; + break; + + case '\'': + /* Prevent all further interpretation */ + *wp++ = c | QUOTE; + break; + + case '`': + /* Leave all text alone for later */ + *wp++ = c; + break; + + default: + break; + } + } + if (c1 == '`') + *wp++ = '`' /* i--; eliminated */; + sofar = 1; + if ((wp = Dpack(wbuf, wp)) == NULL) + return (1); + else { + i = MAXWLEN - (wp - wbuf); + done = 0; + } + break; + + case '\\': + c = DgetC(0); /* No $ subst! */ + if (c == '\n' || c == DEOF) { + done = 0; + break; + } + c |= QUOTE; + break; + + default: + break; + } + if (done) { + unDgetC(c); + sofar = 1; + if ((wp = Dpack(wbuf, wp)) == NULL) + return (1); + else { + i = MAXWLEN - (wp - wbuf); + done = 0; + } + } + } + /* Really NOTREACHED */ + return (0); +} + + +/* + * Get a character, performing $ substitution unless flag is 0. + * Any QUOTES character which is returned from a $ expansion is + * QUOTEd so that it will not be recognized above. + */ +static int +DgetC(flag) + register int flag; +{ + register int c; + +top: + if ((c = Dpeekc) != '\0') { + Dpeekc = 0; + return (c); + } + if (lap) { + c = *lap++ & (QUOTE | TRIM); + if (c == 0) { + lap = 0; + goto top; + } +quotspec: + if (cmap(c, QUOTES)) + return (c | QUOTE); + return (c); + } + if (dolp) { + if ((c = *dolp++ & (QUOTE | TRIM)) != '\0') + goto quotspec; + if (dolcnt > 0) { + setDolp(*dolnxt++); + --dolcnt; + return (' '); + } + dolp = 0; + } + if (dolcnt > 0) { + setDolp(*dolnxt++); + --dolcnt; + goto top; + } + c = Dredc(); + if (c == '$' && flag) { + Dgetdol(); + goto top; + } + return (c); +} + +static Char *nulvec[] = {0}; +static struct varent nulargv = {nulvec, STRargv, { NULL, NULL, NULL }, 0}; + +static void +dolerror(s) + Char *s; +{ + setname(vis_str(s)); + stderror(ERR_NAME | ERR_RANGE); +} + +/* + * Handle the multitudinous $ expansion forms. + * Ugh. + */ +static void +Dgetdol() +{ + register Char *np; + register struct varent *vp = NULL; + Char name[4 * MAXVARLEN + 1]; + int c, sc; + int subscr = 0, lwb = 1, upb = 0; + bool dimen = 0, bitset = 0; + char tnp; + Char wbuf[BUFSIZ]; + static Char *dolbang = NULL; + + dolnmod = dolmcnt = dolwcnt = 0; + c = sc = DgetC(0); + if (c == '{') + c = DgetC(0); /* sc is { to take } later */ + if ((c & TRIM) == '#') + dimen++, c = DgetC(0); /* $# takes dimension */ + else if (c == '?') + bitset++, c = DgetC(0); /* $? tests existence */ + switch (c) { + + case '!': + if (dimen || bitset) + stderror(ERR_SYNTAX); + if (backpid != 0) { + if (dolbang) + xfree((ptr_t) dolbang); + setDolp(dolbang = putn(backpid)); + } + goto eatbrac; + + case '$': + if (dimen || bitset) + stderror(ERR_SYNTAX); + setDolp(doldol); + goto eatbrac; + + case '<' | QUOTE: + if (bitset) + stderror(ERR_NOTALLOWED, "$?<"); + if (dimen) + stderror(ERR_NOTALLOWED, "$?#"); + for (np = wbuf; read(OLDSTD, &tnp, 1) == 1; np++) { + *np = (unsigned char) tnp; + if (np >= &wbuf[BUFSIZ - 1]) + stderror(ERR_LTOOLONG); + if (tnp == '\n') + break; + } + *np = 0; + /* + * KLUDGE: dolmod is set here because it will cause setDolp to call + * domod and thus to copy wbuf. Otherwise setDolp would use it + * directly. If we saved it ourselves, no one would know when to free + * it. The actual function of the 'q' causes filename expansion not to + * be done on the interpolated value. + */ + dolmod[dolnmod++] = 'q'; + dolmcnt = 10000; + setDolp(wbuf); + goto eatbrac; + + case DEOF: + case '\n': + stderror(ERR_SYNTAX); + /* NOTREACHED */ + break; + + case '*': + (void) Strcpy(name, STRargv); + vp = adrof(STRargv); + subscr = -1; /* Prevent eating [...] */ + break; + + default: + np = name; + if (Isdigit(c)) { + if (dimen) + stderror(ERR_NOTALLOWED, "$#<num>"); + subscr = 0; + do { + subscr = subscr * 10 + c - '0'; + c = DgetC(0); + } while (Isdigit(c)); + unDredc(c); + if (subscr < 0) { + dolerror(vp->v_name); + return; + } + if (subscr == 0) { + if (bitset) { + dolp = ffile ? STR1 : STR0; + goto eatbrac; + } + if (ffile == 0) + stderror(ERR_DOLZERO); + fixDolMod(); + setDolp(ffile); + goto eatbrac; + } + if (bitset) + stderror(ERR_DOLQUEST); + vp = adrof(STRargv); + if (vp == 0) { + vp = &nulargv; + goto eatmod; + } + break; + } + if (!alnum(c)) + stderror(ERR_VARALNUM); + for (;;) { + *np++ = c; + c = DgetC(0); + if (!alnum(c)) + break; + if (np >= &name[MAXVARLEN]) + stderror(ERR_VARTOOLONG); + } + *np++ = 0; + unDredc(c); + vp = adrof(name); + } + if (bitset) { + dolp = (vp || getenv(short2str(name))) ? STR1 : STR0; + goto eatbrac; + } + if (vp == 0) { + np = str2short(getenv(short2str(name))); + if (np) { + fixDolMod(); + setDolp(np); + goto eatbrac; + } + udvar(name); + /* NOTREACHED */ + } + c = DgetC(0); + upb = blklen(vp->vec); + if (dimen == 0 && subscr == 0 && c == '[') { + np = name; + for (;;) { + c = DgetC(DODOL); /* Allow $ expand within [ ] */ + if (c == ']') + break; + if (c == '\n' || c == DEOF) + stderror(ERR_INCBR); + if (np >= &name[sizeof(name) / sizeof(Char) - 2]) + stderror(ERR_VARTOOLONG); + *np++ = c; + } + *np = 0, np = name; + if (dolp || dolcnt) /* $ exp must end before ] */ + stderror(ERR_EXPORD); + if (!*np) + stderror(ERR_SYNTAX); + if (Isdigit(*np)) { + int i; + + for (i = 0; Isdigit(*np); i = i * 10 + *np++ - '0') + continue; + if ((i < 0 || i > upb) && !any("-*", *np)) { + dolerror(vp->v_name); + return; + } + lwb = i; + if (!*np) + upb = lwb, np = STRstar; + } + if (*np == '*') + np++; + else if (*np != '-') + stderror(ERR_MISSING, '-'); + else { + register int i = upb; + + np++; + if (Isdigit(*np)) { + i = 0; + while (Isdigit(*np)) + i = i * 10 + *np++ - '0'; + if (i < 0 || i > upb) { + dolerror(vp->v_name); + return; + } + } + if (i < lwb) + upb = lwb - 1; + else + upb = i; + } + if (lwb == 0) { + if (upb != 0) { + dolerror(vp->v_name); + return; + } + upb = -1; + } + if (*np) + stderror(ERR_SYNTAX); + } + else { + if (subscr > 0) + if (subscr > upb) + lwb = 1, upb = 0; + else + lwb = upb = subscr; + unDredc(c); + } + if (dimen) { + Char *cp = putn(upb - lwb + 1); + + addla(cp); + xfree((ptr_t) cp); + } + else { +eatmod: + fixDolMod(); + dolnxt = &vp->vec[lwb - 1]; + dolcnt = upb - lwb + 1; + } +eatbrac: + if (sc == '{') { + c = Dredc(); + if (c != '}') + stderror(ERR_MISSING, '}'); + } +} + +static void +fixDolMod() +{ + register int c; + + c = DgetC(0); + if (c == ':') { + do { + c = DgetC(0), dolmcnt = 1, dolwcnt = 1; + if (c == 'g' || c == 'a') { + if (c == 'g') + dolmcnt = 10000; + else + dolwcnt = 10000; + c = DgetC(0); + } + if ((c == 'g' && dolmcnt != 10000) || + (c == 'a' && dolwcnt != 10000)) { + if (c == 'g') + dolmcnt = 10000; + else + dolwcnt = 10000; + c = DgetC(0); + } + + if (c == 's') { /* [eichin:19910926.0755EST] */ + int delimcnt = 2; + int delim = DgetC(0); + dolmod[dolnmod++] = c; + dolmod[dolnmod++] = delim; + + if (!delim || letter(delim) + || Isdigit(delim) || any(" \t\n", delim)) { + seterror(ERR_BADSUBST); + break; + } + while ((c = DgetC(0)) != (-1)) { + dolmod[dolnmod++] = c; + if(c == delim) delimcnt--; + if(!delimcnt) break; + } + if(delimcnt) { + seterror(ERR_BADSUBST); + break; + } + continue; + } + if (!any("htrqxes", c)) + stderror(ERR_BADMOD, c); + dolmod[dolnmod++] = c; + if (c == 'q') + dolmcnt = 10000; + } + while ((c = DgetC(0)) == ':'); + unDredc(c); + } + else + unDredc(c); +} + +static void +setDolp(cp) + register Char *cp; +{ + register Char *dp; + int i; + + if (dolnmod == 0 || dolmcnt == 0) { + dolp = cp; + return; + } + dp = cp = Strsave(cp); + for (i = 0; i < dolnmod; i++) { + /* handle s// [eichin:19910926.0510EST] */ + if(dolmod[i] == 's') { + int delim; + Char *lhsub, *rhsub, *np; + size_t lhlen = 0, rhlen = 0; + int didmod = 0; + + delim = dolmod[++i]; + if (!delim || letter(delim) + || Isdigit(delim) || any(" \t\n", delim)) { + seterror(ERR_BADSUBST); + break; + } + lhsub = &dolmod[++i]; + while(dolmod[i] != delim && dolmod[++i]) { + lhlen++; + } + dolmod[i] = 0; + rhsub = &dolmod[++i]; + while(dolmod[i] != delim && dolmod[++i]) { + rhlen++; + } + dolmod[i] = 0; + + do { + dp = Strstr(cp, lhsub); + if (dp) { + np = (Char *) xmalloc((size_t) + ((Strlen(cp) + 1 - lhlen + rhlen) * + sizeof(Char))); + (void) Strncpy(np, cp, dp - cp); + (void) Strcpy(np + (dp - cp), rhsub); + (void) Strcpy(np + (dp - cp) + rhlen, dp + lhlen); + + xfree((ptr_t) cp); + dp = cp = np; + didmod = 1; + } else { + /* should this do a seterror? */ + break; + } + } + while (dolwcnt == 10000); + /* + * restore dolmod for additional words + */ + dolmod[i] = rhsub[-1] = delim; + if (didmod) + dolmcnt--; + else + break; + } else { + int didmod = 0; + + do { + if ((dp = domod(cp, dolmod[i]))) { + didmod = 1; + if (Strcmp(cp, dp) == 0) { + xfree((ptr_t) cp); + cp = dp; + break; + } + else { + xfree((ptr_t) cp); + cp = dp; + } + } + else + break; + } + while (dolwcnt == 10000); + dp = cp; + if (didmod) + dolmcnt--; + else + break; + } + } + + if (dp) { + addla(dp); + xfree((ptr_t) dp); + } + else + addla(cp); + + dolp = STRNULL; + if (seterr) + stderror(ERR_OLD); +} + +static void +unDredc(c) + int c; +{ + + Dpeekrd = c; +} + +static int +Dredc() +{ + register int c; + + if ((c = Dpeekrd) != '\0') { + Dpeekrd = 0; + return (c); + } + if (Dcp && (c = *Dcp++)) + return (c & (QUOTE | TRIM)); + if (*Dvp == 0) { + Dcp = 0; + return (DEOF); + } + Dcp = *Dvp++; + return (' '); +} + +static void +Dtestq(c) + register int c; +{ + + if (cmap(c, QUOTES)) + gflag = 1; +} + +/* + * Form a shell temporary file (in unit 0) from the words + * of the shell input up to EOF or a line the same as "term". + * Unit 0 should have been closed before this call. + */ +void +/*ARGSUSED*/ +heredoc(term) + Char *term; +{ + register int c; + Char *Dv[2]; + Char obuf[BUFSIZ], lbuf[BUFSIZ], mbuf[BUFSIZ]; + int ocnt, lcnt, mcnt; + register Char *lbp, *obp, *mbp; + Char **vp; + bool quoted; + char *tmp; + + if (creat(tmp = short2str(shtemp), 0600) < 0) + stderror(ERR_SYSTEM, tmp, strerror(errno)); + (void) close(0); + if (open(tmp, O_RDWR) < 0) { + int oerrno = errno; + + (void) unlink(tmp); + errno = oerrno; + stderror(ERR_SYSTEM, tmp, strerror(errno)); + } + (void) unlink(tmp); /* 0 0 inode! */ + Dv[0] = term; + Dv[1] = NULL; + gflag = 0; + trim(Dv); + rscan(Dv, Dtestq); + quoted = gflag; + ocnt = BUFSIZ; + obp = obuf; + for (;;) { + /* + * Read up a line + */ + lbp = lbuf; + lcnt = BUFSIZ - 4; + for (;;) { + c = readc(1); /* 1 -> Want EOF returns */ + if (c < 0 || c == '\n') + break; + if ((c &= TRIM) != '\0') { + *lbp++ = c; + if (--lcnt < 0) { + setname("<<"); + stderror(ERR_NAME | ERR_OVERFLOW); + } + } + } + *lbp = 0; + + /* + * Check for EOF or compare to terminator -- before expansion + */ + if (c < 0 || eq(lbuf, term)) { + (void) write(0, short2str(obuf), (size_t) (BUFSIZ - ocnt)); + (void) lseek(0, 0l, L_SET); + return; + } + + /* + * If term was quoted or -n just pass it on + */ + if (quoted || noexec) { + *lbp++ = '\n'; + *lbp = 0; + for (lbp = lbuf; (c = *lbp++) != '\0';) { + *obp++ = c; + if (--ocnt == 0) { + (void) write(0, short2str(obuf), BUFSIZ); + obp = obuf; + ocnt = BUFSIZ; + } + } + continue; + } + + /* + * Term wasn't quoted so variable and then command expand the input + * line + */ + Dcp = lbuf; + Dvp = Dv + 1; + mbp = mbuf; + mcnt = BUFSIZ - 4; + for (;;) { + c = DgetC(DODOL); + if (c == DEOF) + break; + if ((c &= TRIM) == 0) + continue; + /* \ quotes \ $ ` here */ + if (c == '\\') { + c = DgetC(0); + if (!any("$\\`", c)) + unDgetC(c | QUOTE), c = '\\'; + else + c |= QUOTE; + } + *mbp++ = c; + if (--mcnt == 0) { + setname("<<"); + stderror(ERR_NAME | ERR_OVERFLOW); + } + } + *mbp++ = 0; + + /* + * If any ` in line do command substitution + */ + mbp = mbuf; + if (any(short2str(mbp), '`')) { + /* + * 1 arg to dobackp causes substitution to be literal. Words are + * broken only at newlines so that all blanks and tabs are + * preserved. Blank lines (null words) are not discarded. + */ + vp = dobackp(mbuf, 1); + } + else + /* Setup trivial vector similar to return of dobackp */ + Dv[0] = mbp, Dv[1] = NULL, vp = Dv; + + /* + * Resurrect the words from the command substitution each separated by + * a newline. Note that the last newline of a command substitution + * will have been discarded, but we put a newline after the last word + * because this represents the newline after the last input line! + */ + for (; *vp; vp++) { + for (mbp = *vp; *mbp; mbp++) { + *obp++ = *mbp & TRIM; + if (--ocnt == 0) { + (void) write(0, short2str(obuf), BUFSIZ); + obp = obuf; + ocnt = BUFSIZ; + } + } + *obp++ = '\n'; + if (--ocnt == 0) { + (void) write(0, short2str(obuf), BUFSIZ); + obp = obuf; + ocnt = BUFSIZ; + } + } + if (pargv) + blkfree(pargv), pargv = 0; + } +} diff --git a/bin/csh/err.c b/bin/csh/err.c new file mode 100644 index 000000000000..745adc82e476 --- /dev/null +++ b/bin/csh/err.c @@ -0,0 +1,407 @@ +/*- + * Copyright (c) 1980, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * 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[] = "@(#)err.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <stdlib.h> +#include <unistd.h> +#if __STDC__ +# include <stdarg.h> +#else +# include <varargs.h> +#endif + +#include "csh.h" +#include "extern.h" + +char *seterr = NULL; /* Holds last error if there was one */ + +#define ERR_FLAGS 0xf0000000 +#define ERR_NAME 0x10000000 +#define ERR_SILENT 0x20000000 +#define ERR_OLD 0x40000000 + +static char *errorlist[] = +{ +#define ERR_SYNTAX 0 + "Syntax Error", +#define ERR_NOTALLOWED 1 + "%s is not allowed", +#define ERR_WTOOLONG 2 + "Word too long", +#define ERR_LTOOLONG 3 + "$< line too long", +#define ERR_DOLZERO 4 + "No file for $0", +#define ERR_DOLQUEST 5 + "$? not allowed here", +#define ERR_INCBR 6 + "Incomplete [] modifier", +#define ERR_EXPORD 7 + "$ expansion must end before ]", +#define ERR_BADMOD 8 + "Bad : modifier in $ (%c)", +#define ERR_SUBSCRIPT 9 + "Subscript error", +#define ERR_BADNUM 10 + "Badly formed number", +#define ERR_NOMORE 11 + "No more words", +#define ERR_FILENAME 12 + "Missing file name", +#define ERR_GLOB 13 + "Internal glob error", +#define ERR_COMMAND 14 + "Command not found", +#define ERR_TOOFEW 15 + "Too few arguments", +#define ERR_TOOMANY 16 + "Too many arguments", +#define ERR_DANGER 17 + "Too dangerous to alias that", +#define ERR_EMPTYIF 18 + "Empty if", +#define ERR_IMPRTHEN 19 + "Improper then", +#define ERR_NOPAREN 20 + "Words not parenthesized", +#define ERR_NOTFOUND 21 + "%s not found", +#define ERR_MASK 22 + "Improper mask", +#define ERR_LIMIT 23 + "No such limit", +#define ERR_TOOLARGE 24 + "Argument too large", +#define ERR_SCALEF 25 + "Improper or unknown scale factor", +#define ERR_UNDVAR 26 + "Undefined variable", +#define ERR_DEEP 27 + "Directory stack not that deep", +#define ERR_BADSIG 28 + "Bad signal number", +#define ERR_UNKSIG 29 + "Unknown signal; kill -l lists signals", +#define ERR_VARBEGIN 30 + "Variable name must begin with a letter", +#define ERR_VARTOOLONG 31 + "Variable name too long", +#define ERR_VARALNUM 32 + "Variable name must contain alphanumeric characters", +#define ERR_JOBCONTROL 33 + "No job control in this shell", +#define ERR_EXPRESSION 34 + "Expression Syntax", +#define ERR_NOHOMEDIR 35 + "No home directory", +#define ERR_CANTCHANGE 36 + "Can't change to home directory", +#define ERR_NULLCOM 37 + "Invalid null command", +#define ERR_ASSIGN 38 + "Assignment missing expression", +#define ERR_UNKNOWNOP 39 + "Unknown operator", +#define ERR_AMBIG 40 + "Ambiguous", +#define ERR_EXISTS 41 + "%s: File exists", +#define ERR_INTR 42 + "Interrupted", +#define ERR_RANGE 43 + "Subscript out of range", +#define ERR_OVERFLOW 44 + "Line overflow", +#define ERR_VARMOD 45 + "Unknown variable modifier", +#define ERR_NOSUCHJOB 46 + "No such job", +#define ERR_TERMINAL 47 + "Can't from terminal", +#define ERR_NOTWHILE 48 + "Not in while/foreach", +#define ERR_NOPROC 49 + "No more processes", +#define ERR_NOMATCH 50 + "No match", +#define ERR_MISSING 51 + "Missing %c", +#define ERR_UNMATCHED 52 + "Unmatched %c", +#define ERR_NOMEM 53 + "Out of memory", +#define ERR_PIPE 54 + "Can't make pipe", +#define ERR_SYSTEM 55 + "%s: %s", +#define ERR_STRING 56 + "%s", +#define ERR_JOBS 57 + "Usage: jobs [ -l ]", +#define ERR_JOBARGS 58 + "Arguments should be jobs or process id's", +#define ERR_JOBCUR 59 + "No current job", +#define ERR_JOBPREV 60 + "No previous job", +#define ERR_JOBPAT 61 + "No job matches pattern", +#define ERR_NESTING 62 + "Fork nesting > %d; maybe `...` loop", +#define ERR_JOBCTRLSUB 63 + "No job control in subshells", +#define ERR_BADPLPS 64 + "Badly placed ()'s", +#define ERR_STOPPED 65 + "%sThere are suspended jobs", +#define ERR_NODIR 66 + "No other directory", +#define ERR_EMPTY 67 + "Directory stack empty", +#define ERR_BADDIR 68 + "Bad directory", +#define ERR_DIRUS 69 + "Usage: %s [-lvn]%s", +#define ERR_HFLAG 70 + "No operand for -h flag", +#define ERR_NOTLOGIN 71 + "Not a login shell", +#define ERR_DIV0 72 + "Division by 0", +#define ERR_MOD0 73 + "Mod by 0", +#define ERR_BADSCALE 74 + "Bad scaling; did you mean \"%s\"?", +#define ERR_SUSPLOG 75 + "Can't suspend a login shell (yet)", +#define ERR_UNKUSER 76 + "Unknown user: %s", +#define ERR_NOHOME 77 + "No $home variable set", +#define ERR_HISTUS 78 + "Usage: history [-rh] [# number of events]", +#define ERR_SPDOLLT 79 + "$, ! or < not allowed with $# or $?", +#define ERR_NEWLINE 80 + "Newline in variable name", +#define ERR_SPSTAR 81 + "* not allowed with $# or $?", +#define ERR_DIGIT 82 + "$?<digit> or $#<digit> not allowed", +#define ERR_VARILL 83 + "Illegal variable name", +#define ERR_NLINDEX 84 + "Newline in variable index", +#define ERR_EXPOVFL 85 + "Expansion buffer overflow", +#define ERR_VARSYN 86 + "Variable syntax", +#define ERR_BADBANG 87 + "Bad ! form", +#define ERR_NOSUBST 88 + "No previous substitute", +#define ERR_BADSUBST 89 + "Bad substitute", +#define ERR_LHS 90 + "No previous left hand side", +#define ERR_RHSLONG 91 + "Right hand side too long", +#define ERR_BADBANGMOD 92 + "Bad ! modifier: %c", +#define ERR_MODFAIL 93 + "Modifier failed", +#define ERR_SUBOVFL 94 + "Substitution buffer overflow", +#define ERR_BADBANGARG 95 + "Bad ! arg selector", +#define ERR_NOSEARCH 96 + "No prev search", +#define ERR_NOEVENT 97 + "%s: Event not found", +#define ERR_TOOMANYRP 98 + "Too many )'s", +#define ERR_TOOMANYLP 99 + "Too many ('s", +#define ERR_BADPLP 100 + "Badly placed (", +#define ERR_MISRED 101 + "Missing name for redirect", +#define ERR_OUTRED 102 + "Ambiguous output redirect", +#define ERR_REDPAR 103 + "Can't << within ()'s", +#define ERR_INRED 104 + "Ambiguous input redirect", +#define ERR_ALIASLOOP 105 + "Alias loop", +#define ERR_HISTLOOP 106 + "!# History loop", +#define ERR_ARCH 107 + "%s: %s. Wrong Architecture", +#define ERR_FILEINQ 108 + "Malformed file inquiry", +#define ERR_SELOVFL 109 + "Selector overflow", +#define ERR_INVALID 110 + "Invalid Error" +}; + +/* + * The parser and scanner set up errors for later by calling seterr, + * which sets the variable err as a side effect; later to be tested, + * e.g. in process. + */ +void +#if __STDC__ +seterror(int id, ...) +#else +seterror(id, va_alist) + int id; + va_dcl +#endif +{ + if (seterr == 0) { + char berr[BUFSIZ]; + va_list va; + +#if __STDC__ + va_start(va, id); +#else + va_start(va); +#endif + if (id < 0 || id > sizeof(errorlist) / sizeof(errorlist[0])) + id = ERR_INVALID; + vsprintf(berr, errorlist[id], va); + va_end(va); + + seterr = strsave(berr); + } +} + +/* + * Print the error with the given id. + * + * Special ids: + * ERR_SILENT: Print nothing. + * ERR_OLD: Print the previously set error if one was there. + * otherwise return. + * ERR_NAME: If this bit is set, print the name of the function + * in bname + * + * This routine always resets or exits. The flag haderr + * is set so the routine who catches the unwind can propogate + * it if they want. + * + * Note that any open files at the point of error will eventually + * be closed in the routine process in sh.c which is the only + * place error unwinds are ever caught. + */ +void +#if __STDC__ +stderror(int id, ...) +#else +stderror(id, va_alist) + int id; + va_dcl +#endif +{ + va_list va; + register Char **v; + int flags = id & ERR_FLAGS; + + id &= ~ERR_FLAGS; + + if ((flags & ERR_OLD) && seterr == NULL) + return; + + if (id < 0 || id > sizeof(errorlist) / sizeof(errorlist[0])) + id = ERR_INVALID; + + (void) fflush(cshout); + (void) fflush(csherr); + haderr = 1; /* Now to diagnostic output */ + timflg = 0; /* This isn't otherwise reset */ + + + if (!(flags & ERR_SILENT)) { + if (flags & ERR_NAME) + (void) fprintf(csherr, "%s: ", bname); + if ((flags & ERR_OLD)) + /* Old error. */ + (void) fprintf(csherr, "%s.\n", seterr); + else { +#if __STDC__ + va_start(va, id); +#else + va_start(va); +#endif + (void) vfprintf(csherr, errorlist[id], va); + va_end(va); + (void) fprintf(csherr, ".\n"); + } + } + + if (seterr) { + xfree((ptr_t) seterr); + seterr = NULL; + } + + if ((v = pargv) != NULL) + pargv = 0, blkfree(v); + if ((v = gargv) != NULL) + gargv = 0, blkfree(v); + + (void) fflush(cshout); + (void) fflush(csherr); + didfds = 0; /* Forget about 0,1,2 */ + /* + * Go away if -e or we are a child shell + */ + if (exiterr || child) + xexit(1); + + /* + * Reset the state of the input. This buffered seek to end of file will + * also clear the while/foreach stack. + */ + btoeof(); + + set(STRstatus, Strsave(STR1)); + if (tpgrp > 0) + (void) tcsetpgrp(FSHTTY, tpgrp); + reset(); /* Unwind */ +} diff --git a/bin/csh/exec.c b/bin/csh/exec.c new file mode 100644 index 000000000000..c9799cbaa34e --- /dev/null +++ b/bin/csh/exec.c @@ -0,0 +1,736 @@ +/*- + * Copyright (c) 1980, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * 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[] = "@(#)exec.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/param.h> +#include <dirent.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#if __STDC__ +# include <stdarg.h> +#else +# include <varargs.h> +#endif + +#include "csh.h" +#include "extern.h" + +/* + * System level search and execute of a command. We look in each directory + * for the specified command name. If the name contains a '/' then we + * execute only the full path name. If there is no search path then we + * execute only full path names. + */ +extern char **environ; + +/* + * As we search for the command we note the first non-trivial error + * message for presentation to the user. This allows us often + * to show that a file has the wrong mode/no access when the file + * is not in the last component of the search path, so we must + * go on after first detecting the error. + */ +static char *exerr; /* Execution error message */ +static Char *expath; /* Path for exerr */ + +/* + * Xhash is an array of HSHSIZ bits (HSHSIZ / 8 chars), which are used + * to hash execs. If it is allocated (havhash true), then to tell + * whether ``name'' is (possibly) present in the i'th component + * of the variable path, you look at the bit in xhash indexed by + * hash(hashname("name"), i). This is setup automatically + * after .login is executed, and recomputed whenever ``path'' is + * changed. + * The two part hash function is designed to let texec() call the + * more expensive hashname() only once and the simple hash() several + * times (once for each path component checked). + * Byte size is assumed to be 8. + */ +#define HSHSIZ 8192 /* 1k bytes */ +#define HSHMASK (HSHSIZ - 1) +#define HSHMUL 243 +static char xhash[HSHSIZ / 8]; + +#define hash(a, b) (((a) * HSHMUL + (b)) & HSHMASK) +#define bit(h, b) ((h)[(b) >> 3] & 1 << ((b) & 7)) /* bit test */ +#define bis(h, b) ((h)[(b) >> 3] |= 1 << ((b) & 7)) /* bit set */ +static int hits, misses; + +/* Dummy search path for just absolute search when no path */ +static Char *justabs[] = {STRNULL, 0}; + +static void pexerr __P((void)); +static void texec __P((Char *, Char **)); +static int hashname __P((Char *)); +static void tellmewhat __P((struct wordent *)); +static int executable __P((Char *, Char *, bool)); +static int iscommand __P((Char *)); + + +void +/*ARGSUSED*/ +doexec(v, t) + Char **v; + struct command *t; +{ + register Char *dp, **pv, **av, *sav; + register struct varent *pathv; + register bool slash; + register int hashval = 0, hashval1, i; + Char *blk[2]; + + /* + * Glob the command name. We will search $path even if this does something, + * as in sh but not in csh. One special case: if there is no PATH, then we + * execute only commands which start with '/'. + */ + blk[0] = t->t_dcom[0]; + blk[1] = 0; + gflag = 0, tglob(blk); + if (gflag) { + pv = globall(blk); + if (pv == 0) { + setname(vis_str(blk[0])); + stderror(ERR_NAME | ERR_NOMATCH); + } + gargv = 0; + } + else + pv = saveblk(blk); + + trim(pv); + + exerr = 0; + expath = Strsave(pv[0]); + Vexpath = expath; + + pathv = adrof(STRpath); + if (pathv == 0 && expath[0] != '/') { + blkfree(pv); + pexerr(); + } + slash = any(short2str(expath), '/'); + + /* + * Glob the argument list, if necessary. Otherwise trim off the quote bits. + */ + gflag = 0; + av = &t->t_dcom[1]; + tglob(av); + if (gflag) { + av = globall(av); + if (av == 0) { + blkfree(pv); + setname(vis_str(expath)); + stderror(ERR_NAME | ERR_NOMATCH); + } + gargv = 0; + } + else + av = saveblk(av); + + blkfree(t->t_dcom); + t->t_dcom = blkspl(pv, av); + xfree((ptr_t) pv); + xfree((ptr_t) av); + av = t->t_dcom; + trim(av); + + if (*av == NULL || **av == '\0') + pexerr(); + + xechoit(av); /* Echo command if -x */ + /* + * Since all internal file descriptors are set to close on exec, we don't + * need to close them explicitly here. Just reorient ourselves for error + * messages. + */ + SHIN = 0; + SHOUT = 1; + SHERR = 2; + OLDSTD = 0; + /* + * We must do this AFTER any possible forking (like `foo` in glob) so that + * this shell can still do subprocesses. + */ + (void) sigsetmask((sigset_t) 0); + /* + * If no path, no words in path, or a / in the filename then restrict the + * command search. + */ + if (pathv == 0 || pathv->vec[0] == 0 || slash) + pv = justabs; + else + pv = pathv->vec; + sav = Strspl(STRslash, *av);/* / command name for postpending */ + Vsav = sav; + if (havhash) + hashval = hashname(*av); + i = 0; + hits++; + do { + /* + * Try to save time by looking at the hash table for where this command + * could be. If we are doing delayed hashing, then we put the names in + * one at a time, as the user enters them. This is kinda like Korn + * Shell's "tracked aliases". + */ + if (!slash && pv[0][0] == '/' && havhash) { + hashval1 = hash(hashval, i); + if (!bit(xhash, hashval1)) + goto cont; + } + if (pv[0][0] == 0 || eq(pv[0], STRdot)) /* don't make ./xxx */ + texec(*av, av); + else { + dp = Strspl(*pv, sav); + Vdp = dp; + texec(dp, av); + Vdp = 0; + xfree((ptr_t) dp); + } + misses++; +cont: + pv++; + i++; + } while (*pv); + hits--; + Vsav = 0; + xfree((ptr_t) sav); + pexerr(); +} + +static void +pexerr() +{ + /* Couldn't find the damn thing */ + if (expath) { + setname(vis_str(expath)); + Vexpath = 0; + xfree((ptr_t) expath); + expath = 0; + } + else + setname(""); + if (exerr) + stderror(ERR_NAME | ERR_STRING, exerr); + stderror(ERR_NAME | ERR_COMMAND); +} + +/* + * Execute command f, arg list t. + * Record error message if not found. + * Also do shell scripts here. + */ +static void +texec(sf, st) + Char *sf; + register Char **st; +{ + register char **t; + register char *f; + register struct varent *v; + register Char **vp; + Char *lastsh[2]; + int fd; + unsigned char c; + Char *st0, **ost; + + /* The order for the conversions is significant */ + t = short2blk(st); + f = short2str(sf); + Vt = t; + errno = 0; /* don't use a previous error */ + (void) execve(f, t, environ); + Vt = 0; + blkfree((Char **) t); + switch (errno) { + + case ENOEXEC: + /* + * From: casper@fwi.uva.nl (Casper H.S. Dik) If we could not execute + * it, don't feed it to the shell if it looks like a binary! + */ + if ((fd = open(f, O_RDONLY)) != -1) { + if (read(fd, (char *) &c, 1) == 1) { + if (!Isprint(c) && (c != '\n' && c != '\t')) { + (void) close(fd); + /* + * We *know* what ENOEXEC means. + */ + stderror(ERR_ARCH, f, strerror(errno)); + } + } +#ifdef _PATH_BSHELL + else + c = '#'; +#endif + (void) close(fd); + } + /* + * If there is an alias for shell, then put the words of the alias in + * front of the argument list replacing the command name. Note no + * interpretation of the words at this point. + */ + v = adrof1(STRshell, &aliases); + if (v == 0) { + vp = lastsh; + vp[0] = adrof(STRshell) ? value(STRshell) : STR_SHELLPATH; + vp[1] = NULL; +#ifdef _PATH_BSHELL + if (fd != -1 && c != '#') + vp[0] = STR_BSHELL; +#endif + } + else + vp = v->vec; + st0 = st[0]; + st[0] = sf; + ost = st; + st = blkspl(vp, st); /* Splice up the new arglst */ + ost[0] = st0; + sf = *st; + /* The order for the conversions is significant */ + t = short2blk(st); + f = short2str(sf); + xfree((ptr_t) st); + Vt = t; + (void) execve(f, t, environ); + Vt = 0; + blkfree((Char **) t); + /* The sky is falling, the sky is falling! */ + + case ENOMEM: + stderror(ERR_SYSTEM, f, strerror(errno)); + + case ENOENT: + break; + + default: + if (exerr == 0) { + exerr = strerror(errno); + if (expath) + xfree((ptr_t) expath); + expath = Strsave(sf); + Vexpath = expath; + } + } +} + +/*ARGSUSED*/ +void +execash(t, kp) + Char **t; + register struct command *kp; +{ + int saveIN, saveOUT, saveDIAG, saveSTD; + int oSHIN; + int oSHOUT; + int oSHERR; + int oOLDSTD; + jmp_buf osetexit; + int my_reenter; + int odidfds; + sig_t osigint, osigquit, osigterm; + + if (chkstop == 0 && setintr) + panystop(0); + /* + * Hmm, we don't really want to do that now because we might + * fail, but what is the choice + */ + rechist(); + + osigint = signal(SIGINT, parintr); + osigquit = signal(SIGQUIT, parintr); + osigterm = signal(SIGTERM, parterm); + + odidfds = didfds; + oSHIN = SHIN; + oSHOUT = SHOUT; + oSHERR = SHERR; + oOLDSTD = OLDSTD; + + saveIN = dcopy(SHIN, -1); + saveOUT = dcopy(SHOUT, -1); + saveDIAG = dcopy(SHERR, -1); + saveSTD = dcopy(OLDSTD, -1); + + lshift(kp->t_dcom, 1); + + getexit(osetexit); + + if ((my_reenter = setexit()) == 0) { + SHIN = dcopy(0, -1); + SHOUT = dcopy(1, -1); + SHERR = dcopy(2, -1); + didfds = 0; + doexec(t, kp); + } + + (void) signal(SIGINT, osigint); + (void) signal(SIGQUIT, osigquit); + (void) signal(SIGTERM, osigterm); + + doneinp = 0; + didfds = odidfds; + (void) close(SHIN); + (void) close(SHOUT); + (void) close(SHERR); + (void) close(OLDSTD); + SHIN = dmove(saveIN, oSHIN); + SHOUT = dmove(saveOUT, oSHOUT); + SHERR = dmove(saveDIAG, oSHERR); + OLDSTD = dmove(saveSTD, oOLDSTD); + + resexit(osetexit); + if (my_reenter) + stderror(ERR_SILENT); +} + +void +xechoit(t) + Char **t; +{ + if (adrof(STRecho)) { + (void) fflush(csherr); + blkpr(csherr, t); + (void) fputc('\n', csherr); + } +} + +void +/*ARGSUSED*/ +dohash(v, t) + Char **v; + struct command *t; +{ + DIR *dirp; + register struct dirent *dp; + register int cnt; + int i = 0; + struct varent *pathv = adrof(STRpath); + Char **pv; + int hashval; + + havhash = 1; + for (cnt = 0; cnt < sizeof xhash; cnt++) + xhash[cnt] = 0; + if (pathv == 0) + return; + for (pv = pathv->vec; *pv; pv++, i++) { + if (pv[0][0] != '/') + continue; + dirp = opendir(short2str(*pv)); + if (dirp == NULL) + continue; + while ((dp = readdir(dirp)) != NULL) { + if (dp->d_ino == 0) + continue; + if (dp->d_name[0] == '.' && + (dp->d_name[1] == '\0' || + (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) + continue; + hashval = hash(hashname(str2short(dp->d_name)), i); + bis(xhash, hashval); + /* tw_add_comm_name (dp->d_name); */ + } + (void) closedir(dirp); + } +} + +void +/*ARGSUSED*/ +dounhash(v, t) + Char **v; + struct command *t; +{ + havhash = 0; +} + +void +/*ARGSUSED*/ +hashstat(v, t) + Char **v; + struct command *t; +{ + if (hits + misses) + (void) fprintf(cshout, "%d hits, %d misses, %d%%\n", + hits, misses, 100 * hits / (hits + misses)); +} + +/* + * Hash a command name. + */ +static int +hashname(cp) + register Char *cp; +{ + register long h = 0; + + while (*cp) + h = hash(h, *cp++); + return ((int) h); +} + +static int +iscommand(name) + Char *name; +{ + register Char **pv; + register Char *sav; + register struct varent *v; + register bool slash = any(short2str(name), '/'); + register int hashval = 0, hashval1, i; + + v = adrof(STRpath); + if (v == 0 || v->vec[0] == 0 || slash) + pv = justabs; + else + pv = v->vec; + sav = Strspl(STRslash, name); /* / command name for postpending */ + if (havhash) + hashval = hashname(name); + i = 0; + do { + if (!slash && pv[0][0] == '/' && havhash) { + hashval1 = hash(hashval, i); + if (!bit(xhash, hashval1)) + goto cont; + } + if (pv[0][0] == 0 || eq(pv[0], STRdot)) { /* don't make ./xxx */ + if (executable(NULL, name, 0)) { + xfree((ptr_t) sav); + return i + 1; + } + } + else { + if (executable(*pv, sav, 0)) { + xfree((ptr_t) sav); + return i + 1; + } + } +cont: + pv++; + i++; + } while (*pv); + xfree((ptr_t) sav); + return 0; +} + +/* Also by: + * Andreas Luik <luik@isaak.isa.de> + * I S A GmbH - Informationssysteme fuer computerintegrierte Automatisierung + * Azenberstr. 35 + * D-7000 Stuttgart 1 + * West-Germany + * is the executable() routine below and changes to iscommand(). + * Thanks again!! + */ + +/* + * executable() examines the pathname obtained by concatenating dir and name + * (dir may be NULL), and returns 1 either if it is executable by us, or + * if dir_ok is set and the pathname refers to a directory. + * This is a bit kludgy, but in the name of optimization... + */ +static int +executable(dir, name, dir_ok) + Char *dir, *name; + bool dir_ok; +{ + struct stat stbuf; + Char path[MAXPATHLEN + 1], *dp, *sp; + char *strname; + + if (dir && *dir) { + for (dp = path, sp = dir; *sp; *dp++ = *sp++) + if (dp == &path[MAXPATHLEN + 1]) { + *--dp = '\0'; + break; + } + for (sp = name; *sp; *dp++ = *sp++) + if (dp == &path[MAXPATHLEN + 1]) { + *--dp = '\0'; + break; + } + *dp = '\0'; + strname = short2str(path); + } + else + strname = short2str(name); + return (stat(strname, &stbuf) != -1 && + ((S_ISREG(stbuf.st_mode) && + /* save time by not calling access() in the hopeless case */ + (stbuf.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) && + access(strname, X_OK) == 0) || + (dir_ok && S_ISDIR(stbuf.st_mode)))); +} + +/* The dowhich() is by: + * Andreas Luik <luik@isaak.isa.de> + * I S A GmbH - Informationssysteme fuer computerintegrierte Automatisierung + * Azenberstr. 35 + * D-7000 Stuttgart 1 + * West-Germany + * Thanks!! + */ +/*ARGSUSED*/ +void +dowhich(v, c) + register Char **v; + struct command *c; +{ + struct wordent lex[3]; + struct varent *vp; + + lex[0].next = &lex[1]; + lex[1].next = &lex[2]; + lex[2].next = &lex[0]; + + lex[0].prev = &lex[2]; + lex[1].prev = &lex[0]; + lex[2].prev = &lex[1]; + + lex[0].word = STRNULL; + lex[2].word = STRret; + + while (*++v) { + if ((vp = adrof1(*v, &aliases)) != NULL) { + (void) fprintf(cshout, "%s: \t aliased to ", vis_str(*v)); + blkpr(cshout, vp->vec); + (void) fputc('\n', cshout); + } + else { + lex[1].word = *v; + tellmewhat(lex); + } + } +} + +static void +tellmewhat(lex) + struct wordent *lex; +{ + register int i; + register struct biltins *bptr; + register struct wordent *sp = lex->next; + bool aliased = 0; + Char *s0, *s1, *s2; + Char qc; + + if (adrof1(sp->word, &aliases)) { + alias(lex); + sp = lex->next; + aliased = 1; + } + + s0 = sp->word; /* to get the memory freeing right... */ + + /* handle quoted alias hack */ + if ((*(sp->word) & (QUOTE | TRIM)) == QUOTE) + (sp->word)++; + + /* do quoting, if it hasn't been done */ + s1 = s2 = sp->word; + while (*s2) + switch (*s2) { + case '\'': + case '"': + qc = *s2++; + while (*s2 && *s2 != qc) + *s1++ = *s2++ | QUOTE; + if (*s2) + s2++; + break; + case '\\': + if (*++s2) + *s1++ = *s2++ | QUOTE; + break; + default: + *s1++ = *s2++; + } + *s1 = '\0'; + + for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++) { + if (eq(sp->word, str2short(bptr->bname))) { + if (aliased) + prlex(cshout, lex); + (void) fprintf(cshout, "%s: shell built-in command.\n", + vis_str(sp->word)); + sp->word = s0; /* we save and then restore this */ + return; + } + } + + if ((i = iscommand(strip(sp->word))) != 0) { + register Char **pv; + register struct varent *v; + bool slash = any(short2str(sp->word), '/'); + + v = adrof(STRpath); + if (v == 0 || v->vec[0] == 0 || slash) + pv = justabs; + else + pv = v->vec; + + while (--i) + pv++; + if (pv[0][0] == 0 || eq(pv[0], STRdot)) { + sp->word = Strspl(STRdotsl, sp->word); + prlex(cshout, lex); + xfree((ptr_t) sp->word); + sp->word = s0; /* we save and then restore this */ + return; + } + s1 = Strspl(*pv, STRslash); + sp->word = Strspl(s1, sp->word); + xfree((ptr_t) s1); + prlex(cshout, lex); + xfree((ptr_t) sp->word); + } + else { + if (aliased) + prlex(cshout, lex); + (void) fprintf(csherr, "%s: Command not found.\n", vis_str(sp->word)); + } + sp->word = s0; /* we save and then restore this */ +} diff --git a/bin/csh/exp.c b/bin/csh/exp.c new file mode 100644 index 000000000000..59b6964b9793 --- /dev/null +++ b/bin/csh/exp.c @@ -0,0 +1,711 @@ +/*- + * Copyright (c) 1980, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * 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[] = "@(#)exp.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdlib.h> +#include <unistd.h> +#ifndef SHORT_STRINGS +#include <string.h> +#endif /* SHORT_STRINGS */ +#if __STDC__ +# include <stdarg.h> +#else +# include <varargs.h> +#endif + +#include "csh.h" +#include "extern.h" + +#define IGNORE 1 /* in ignore, it means to ignore value, just parse */ +#define NOGLOB 2 /* in ignore, it means not to globone */ + +#define ADDOP 1 +#define MULOP 2 +#define EQOP 4 +#define RELOP 8 +#define RESTOP 16 +#define ANYOP 31 + +#define EQEQ 1 +#define GTR 2 +#define LSS 4 +#define NOTEQ 6 +#define EQMATCH 7 +#define NOTEQMATCH 8 + +static int exp1 __P((Char ***, bool)); +static int exp2 __P((Char ***, bool)); +static int exp2a __P((Char ***, bool)); +static int exp2b __P((Char ***, bool)); +static int exp2c __P((Char ***, bool)); +static Char * exp3 __P((Char ***, bool)); +static Char * exp3a __P((Char ***, bool)); +static Char * exp4 __P((Char ***, bool)); +static Char * exp5 __P((Char ***, bool)); +static Char * exp6 __P((Char ***, bool)); +static void evalav __P((Char **)); +static int isa __P((Char *, int)); +static int egetn __P((Char *)); + +#ifdef EDEBUG +static void etracc __P((char *, Char *, Char ***)); +static void etraci __P((char *, int, Char ***)); +#endif + +int +expr(vp) + register Char ***vp; +{ + return (exp0(vp, 0)); +} + +int +exp0(vp, ignore) + register Char ***vp; + bool ignore; +{ + register int p1 = exp1(vp, ignore); + +#ifdef EDEBUG + etraci("exp0 p1", p1, vp); +#endif + if (**vp && eq(**vp, STRor2)) { + register int p2; + + (*vp)++; + p2 = exp0(vp, (ignore & IGNORE) || p1); +#ifdef EDEBUG + etraci("exp0 p2", p2, vp); +#endif + return (p1 || p2); + } + return (p1); +} + +static int +exp1(vp, ignore) + register Char ***vp; + bool ignore; +{ + register int p1 = exp2(vp, ignore); + +#ifdef EDEBUG + etraci("exp1 p1", p1, vp); +#endif + if (**vp && eq(**vp, STRand2)) { + register int p2; + + (*vp)++; + p2 = exp1(vp, (ignore & IGNORE) || !p1); +#ifdef EDEBUG + etraci("exp1 p2", p2, vp); +#endif + return (p1 && p2); + } + return (p1); +} + +static int +exp2(vp, ignore) + register Char ***vp; + bool ignore; +{ + register int p1 = exp2a(vp, ignore); + +#ifdef EDEBUG + etraci("exp3 p1", p1, vp); +#endif + if (**vp && eq(**vp, STRor)) { + register int p2; + + (*vp)++; + p2 = exp2(vp, ignore); +#ifdef EDEBUG + etraci("exp3 p2", p2, vp); +#endif + return (p1 | p2); + } + return (p1); +} + +static int +exp2a(vp, ignore) + register Char ***vp; + bool ignore; +{ + register int p1 = exp2b(vp, ignore); + +#ifdef EDEBUG + etraci("exp2a p1", p1, vp); +#endif + if (**vp && eq(**vp, STRcaret)) { + register int p2; + + (*vp)++; + p2 = exp2a(vp, ignore); +#ifdef EDEBUG + etraci("exp2a p2", p2, vp); +#endif + return (p1 ^ p2); + } + return (p1); +} + +static int +exp2b(vp, ignore) + register Char ***vp; + bool ignore; +{ + register int p1 = exp2c(vp, ignore); + +#ifdef EDEBUG + etraci("exp2b p1", p1, vp); +#endif + if (**vp && eq(**vp, STRand)) { + register int p2; + + (*vp)++; + p2 = exp2b(vp, ignore); +#ifdef EDEBUG + etraci("exp2b p2", p2, vp); +#endif + return (p1 & p2); + } + return (p1); +} + +static int +exp2c(vp, ignore) + register Char ***vp; + bool ignore; +{ + register Char *p1 = exp3(vp, ignore); + register Char *p2; + register int i; + +#ifdef EDEBUG + etracc("exp2c p1", p1, vp); +#endif + if ((i = isa(**vp, EQOP)) != 0) { + (*vp)++; + if (i == EQMATCH || i == NOTEQMATCH) + ignore |= NOGLOB; + p2 = exp3(vp, ignore); +#ifdef EDEBUG + etracc("exp2c p2", p2, vp); +#endif + if (!(ignore & IGNORE)) + switch (i) { + + case EQEQ: + i = eq(p1, p2); + break; + + case NOTEQ: + i = !eq(p1, p2); + break; + + case EQMATCH: + i = Gmatch(p1, p2); + break; + + case NOTEQMATCH: + i = !Gmatch(p1, p2); + break; + } + xfree((ptr_t) p1); + xfree((ptr_t) p2); + return (i); + } + i = egetn(p1); + xfree((ptr_t) p1); + return (i); +} + +static Char * +exp3(vp, ignore) + register Char ***vp; + bool ignore; +{ + register Char *p1, *p2; + register int i; + + p1 = exp3a(vp, ignore); +#ifdef EDEBUG + etracc("exp3 p1", p1, vp); +#endif + if ((i = isa(**vp, RELOP)) != 0) { + (*vp)++; + if (**vp && eq(**vp, STRequal)) + i |= 1, (*vp)++; + p2 = exp3(vp, ignore); +#ifdef EDEBUG + etracc("exp3 p2", p2, vp); +#endif + if (!(ignore & IGNORE)) + switch (i) { + + case GTR: + i = egetn(p1) > egetn(p2); + break; + + case GTR | 1: + i = egetn(p1) >= egetn(p2); + break; + + case LSS: + i = egetn(p1) < egetn(p2); + break; + + case LSS | 1: + i = egetn(p1) <= egetn(p2); + break; + } + xfree((ptr_t) p1); + xfree((ptr_t) p2); + return (putn(i)); + } + return (p1); +} + +static Char * +exp3a(vp, ignore) + register Char ***vp; + bool ignore; +{ + register Char *p1, *p2, *op; + register int i; + + p1 = exp4(vp, ignore); +#ifdef EDEBUG + etracc("exp3a p1", p1, vp); +#endif + op = **vp; + if (op && any("<>", op[0]) && op[0] == op[1]) { + (*vp)++; + p2 = exp3a(vp, ignore); +#ifdef EDEBUG + etracc("exp3a p2", p2, vp); +#endif + if (op[0] == '<') + i = egetn(p1) << egetn(p2); + else + i = egetn(p1) >> egetn(p2); + xfree((ptr_t) p1); + xfree((ptr_t) p2); + return (putn(i)); + } + return (p1); +} + +static Char * +exp4(vp, ignore) + register Char ***vp; + bool ignore; +{ + register Char *p1, *p2; + register int i = 0; + + p1 = exp5(vp, ignore); +#ifdef EDEBUG + etracc("exp4 p1", p1, vp); +#endif + if (isa(**vp, ADDOP)) { + register Char *op = *(*vp)++; + + p2 = exp4(vp, ignore); +#ifdef EDEBUG + etracc("exp4 p2", p2, vp); +#endif + if (!(ignore & IGNORE)) + switch (op[0]) { + + case '+': + i = egetn(p1) + egetn(p2); + break; + + case '-': + i = egetn(p1) - egetn(p2); + break; + } + xfree((ptr_t) p1); + xfree((ptr_t) p2); + return (putn(i)); + } + return (p1); +} + +static Char * +exp5(vp, ignore) + register Char ***vp; + bool ignore; +{ + register Char *p1, *p2; + register int i = 0; + + p1 = exp6(vp, ignore); +#ifdef EDEBUG + etracc("exp5 p1", p1, vp); +#endif + if (isa(**vp, MULOP)) { + register Char *op = *(*vp)++; + + p2 = exp5(vp, ignore); +#ifdef EDEBUG + etracc("exp5 p2", p2, vp); +#endif + if (!(ignore & IGNORE)) + switch (op[0]) { + + case '*': + i = egetn(p1) * egetn(p2); + break; + + case '/': + i = egetn(p2); + if (i == 0) + stderror(ERR_DIV0); + i = egetn(p1) / i; + break; + + case '%': + i = egetn(p2); + if (i == 0) + stderror(ERR_MOD0); + i = egetn(p1) % i; + break; + } + xfree((ptr_t) p1); + xfree((ptr_t) p2); + return (putn(i)); + } + return (p1); +} + +static Char * +exp6(vp, ignore) + register Char ***vp; + bool ignore; +{ + int ccode, i = 0; + register Char *cp, *dp, *ep; + + if (**vp == 0) + stderror(ERR_NAME | ERR_EXPRESSION); + if (eq(**vp, STRbang)) { + (*vp)++; + cp = exp6(vp, ignore); +#ifdef EDEBUG + etracc("exp6 ! cp", cp, vp); +#endif + i = egetn(cp); + xfree((ptr_t) cp); + return (putn(!i)); + } + if (eq(**vp, STRtilde)) { + (*vp)++; + cp = exp6(vp, ignore); +#ifdef EDEBUG + etracc("exp6 ~ cp", cp, vp); +#endif + i = egetn(cp); + xfree((ptr_t) cp); + return (putn(~i)); + } + if (eq(**vp, STRLparen)) { + (*vp)++; + ccode = exp0(vp, ignore); +#ifdef EDEBUG + etraci("exp6 () ccode", ccode, vp); +#endif + if (*vp == 0 || **vp == 0 || ***vp != ')') + stderror(ERR_NAME | ERR_EXPRESSION); + (*vp)++; + return (putn(ccode)); + } + if (eq(**vp, STRLbrace)) { + register Char **v; + struct command faket; + Char *fakecom[2]; + + faket.t_dtyp = NODE_COMMAND; + faket.t_dflg = 0; + faket.t_dcar = faket.t_dcdr = faket.t_dspr = NULL; + faket.t_dcom = fakecom; + fakecom[0] = STRfakecom; + fakecom[1] = NULL; + (*vp)++; + v = *vp; + for (;;) { + if (!**vp) + stderror(ERR_NAME | ERR_MISSING, '}'); + if (eq(*(*vp)++, STRRbrace)) + break; + } + if (ignore & IGNORE) + return (Strsave(STRNULL)); + psavejob(); + if (pfork(&faket, -1) == 0) { + *--(*vp) = 0; + evalav(v); + exitstat(); + } + pwait(); + prestjob(); +#ifdef EDEBUG + etraci("exp6 {} status", egetn(value(STRstatus)), vp); +#endif + return (putn(egetn(value(STRstatus)) == 0)); + } + if (isa(**vp, ANYOP)) + return (Strsave(STRNULL)); + cp = *(*vp)++; + if (*cp == '-' && any("erwxfdzopls", cp[1])) { + struct stat stb; + + if (cp[2] != '\0') + stderror(ERR_NAME | ERR_FILEINQ); + /* + * Detect missing file names by checking for operator in the file name + * position. However, if an operator name appears there, we must make + * sure that there's no file by that name (e.g., "/") before announcing + * an error. Even this check isn't quite right, since it doesn't take + * globbing into account. + */ + if (isa(**vp, ANYOP) && stat(short2str(**vp), &stb)) + stderror(ERR_NAME | ERR_FILENAME); + + dp = *(*vp)++; + if (ignore & IGNORE) + return (Strsave(STRNULL)); + ep = globone(dp, G_ERROR); + switch (cp[1]) { + + case 'r': + i = !access(short2str(ep), R_OK); + break; + + case 'w': + i = !access(short2str(ep), W_OK); + break; + + case 'x': + i = !access(short2str(ep), X_OK); + break; + + default: + if ( +#ifdef S_IFLNK + cp[1] == 'l' ? lstat(short2str(ep), &stb) : +#endif + stat(short2str(ep), &stb)) { + xfree((ptr_t) ep); + return (Strsave(STR0)); + } + switch (cp[1]) { + + case 'f': + i = S_ISREG(stb.st_mode); + break; + + case 'd': + i = S_ISDIR(stb.st_mode); + break; + + case 'p': +#ifdef S_ISFIFO + i = S_ISFIFO(stb.st_mode); +#else + i = 0; +#endif + break; + + case 'l': +#ifdef S_ISLNK + i = S_ISLNK(stb.st_mode); +#else + i = 0; +#endif + break; + + case 's': +#ifdef S_ISSOCK + i = S_ISSOCK(stb.st_mode); +#else + i = 0; +#endif + break; + + case 'z': + i = stb.st_size == 0; + break; + + case 'e': + i = 1; + break; + + case 'o': + i = stb.st_uid == uid; + break; + } + } +#ifdef EDEBUG + etraci("exp6 -? i", i, vp); +#endif + xfree((ptr_t) ep); + return (putn(i)); + } +#ifdef EDEBUG + etracc("exp6 default", cp, vp); +#endif + return (ignore & NOGLOB ? Strsave(cp) : globone(cp, G_ERROR)); +} + +static void +evalav(v) + register Char **v; +{ + struct wordent paraml1; + register struct wordent *hp = ¶ml1; + struct command *t; + register struct wordent *wdp = hp; + + set(STRstatus, Strsave(STR0)); + hp->prev = hp->next = hp; + hp->word = STRNULL; + while (*v) { + register struct wordent *new = + (struct wordent *) xcalloc(1, sizeof *wdp); + + new->prev = wdp; + new->next = hp; + wdp->next = new; + wdp = new; + wdp->word = Strsave(*v++); + } + hp->prev = wdp; + alias(¶ml1); + t = syntax(paraml1.next, ¶ml1, 0); + if (seterr) + stderror(ERR_OLD); + execute(t, -1, NULL, NULL); + freelex(¶ml1), freesyn(t); +} + +static int +isa(cp, what) + register Char *cp; + register int what; +{ + if (cp == 0) + return ((what & RESTOP) != 0); + if (cp[1] == 0) { + if (what & ADDOP && (*cp == '+' || *cp == '-')) + return (1); + if (what & MULOP && (*cp == '*' || *cp == '/' || *cp == '%')) + return (1); + if (what & RESTOP && (*cp == '(' || *cp == ')' || *cp == '!' || + *cp == '~' || *cp == '^' || *cp == '"')) + return (1); + } + else if (cp[2] == 0) { + if (what & RESTOP) { + if (cp[0] == '|' && cp[1] == '&') + return (1); + if (cp[0] == '<' && cp[1] == '<') + return (1); + if (cp[0] == '>' && cp[1] == '>') + return (1); + } + if (what & EQOP) { + if (cp[0] == '=') { + if (cp[1] == '=') + return (EQEQ); + if (cp[1] == '~') + return (EQMATCH); + } + else if (cp[0] == '!') { + if (cp[1] == '=') + return (NOTEQ); + if (cp[1] == '~') + return (NOTEQMATCH); + } + } + } + if (what & RELOP) { + if (*cp == '<') + return (LSS); + if (*cp == '>') + return (GTR); + } + return (0); +} + +static int +egetn(cp) + register Char *cp; +{ + if (*cp && *cp != '-' && !Isdigit(*cp)) + stderror(ERR_NAME | ERR_EXPRESSION); + return (getn(cp)); +} + +/* Phew! */ + +#ifdef EDEBUG +static void +etraci(str, i, vp) + char *str; + int i; + Char ***vp; +{ + (void) fprintf(csherr, "%s=%d\t", str, i); + blkpr(csherr, *vp); + (void) fprintf(csherr, "\n"); +} +static void +etracc(str, cp, vp) + char *str; + Char *cp; + Char ***vp; +{ + (void) fprintf(csherr, "%s=%s\t", str, vis_str(cp)); + blkpr(csherr, *vp); + (void) fprintf(csherr, "\n"); +} +#endif diff --git a/bin/csh/extern.h b/bin/csh/extern.h new file mode 100644 index 000000000000..db37b8adaa5e --- /dev/null +++ b/bin/csh/extern.h @@ -0,0 +1,338 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * 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. + * + * @(#)extern.h 8.1 (Berkeley) 5/31/93 + */ + +#include <sys/cdefs.h> + +/* + * csh.c + */ +int gethdir __P((Char *)); +void dosource __P((Char **, struct command *)); +void exitstat __P((void)); +void goodbye __P((void)); +void importpath __P((Char *)); +void initdesc __P((void)); +void pintr __P((int)); +void pintr1 __P((bool)); +void printprompt __P((void)); +void process __P((bool)); +void rechist __P((void)); +void untty __P((void)); +int vis_fputc __P((int, FILE *)); + +#ifdef PROF +void done __P((int)); +#else +void xexit __P((int)); +#endif + +/* + * dir.c + */ +void dinit __P((Char *)); +void dodirs __P((Char **, struct command *)); +Char *dcanon __P((Char *, Char *)); +void dtildepr __P((Char *, Char *)); +void dtilde __P((void)); +void dochngd __P((Char **, struct command *)); +Char *dnormalize __P((Char *)); +void dopushd __P((Char **, struct command *)); +void dopopd __P((Char **, struct command *)); +struct directory; +void dfree __P((struct directory *)); + +/* + * dol.c + */ +void Dfix __P((struct command *)); +Char *Dfix1 __P((Char *)); +void heredoc __P((Char *)); + +/* + * err.c + */ +void seterror __P((int, ...)); +void stderror __P((int, ...)); + +/* + * exec.c + */ +void doexec __P((Char **, struct command *)); +void dohash __P((Char **, struct command *)); +void dounhash __P((Char **, struct command *)); +void dowhich __P((Char **, struct command *)); +void execash __P((Char **, struct command *)); +void hashstat __P((Char **, struct command *)); +void xechoit __P((Char **)); + +/* + * exp.c + */ +int expr __P((Char ***)); +int exp0 __P((Char ***, bool)); + +/* + * file.c + */ +#ifdef FILEC +int tenex __P((Char *, int)); +#endif + +/* + * func.c + */ +void Setenv __P((Char *, Char *)); +void doalias __P((Char **, struct command *)); +void dobreak __P((Char **, struct command *)); +void docontin __P((Char **, struct command *)); +void doecho __P((Char **, struct command *)); +void doelse __P((Char **, struct command *)); +void doend __P((Char **, struct command *)); +void doeval __P((Char **, struct command *)); +void doexit __P((Char **, struct command *)); +void doforeach __P((Char **, struct command *)); +void doglob __P((Char **, struct command *)); +void dogoto __P((Char **, struct command *)); +void doif __P((Char **, struct command *)); +void dolimit __P((Char **, struct command *)); +void dologin __P((Char **, struct command *)); +void dologout __P((Char **, struct command *)); +void donohup __P((Char **, struct command *)); +void doonintr __P((Char **, struct command *)); +void doprintf __P((Char **, struct command *)); +void dorepeat __P((Char **, struct command *)); +void dosetenv __P((Char **, struct command *)); +void dosuspend __P((Char **, struct command *)); +void doswbrk __P((Char **, struct command *)); +void doswitch __P((Char **, struct command *)); +void doumask __P((Char **, struct command *)); +void dounlimit __P((Char **, struct command *)); +void dounsetenv __P((Char **, struct command *)); +void dowhile __P((Char **, struct command *)); +void dozip __P((Char **, struct command *)); +void func __P((struct command *, struct biltins *)); +struct biltins * + isbfunc __P((struct command *)); +void prvars __P((void)); +void gotolab __P((Char *)); +int srchx __P((Char *)); +void unalias __P((Char **, struct command *)); +void wfree __P((void)); + +/* + * glob.c + */ +Char **dobackp __P((Char *, bool)); +void Gcat __P((Char *, Char *)); +Char *globone __P((Char *, int)); +int Gmatch __P((Char *, Char *)); +void ginit __P((void)); +Char **globall __P((Char **)); +void rscan __P((Char **, void (*)())); +void tglob __P((Char **)); +void trim __P((Char **)); +#ifdef FILEC +int sortscmp __P((const ptr_t, const ptr_t)); +#endif /* FILEC */ + +/* + * hist.c + */ +void dohist __P((Char **, struct command *)); +struct Hist * + enthist __P((int, struct wordent *, bool)); +void savehist __P((struct wordent *)); + +/* + * lex.c + */ +void addla __P((Char *)); +void bseek __P((struct Ain *)); +void btell __P((struct Ain *)); +void btoeof __P((void)); +void copylex __P((struct wordent *, struct wordent *)); +Char *domod __P((Char *, int)); +void freelex __P((struct wordent *)); +int lex __P((struct wordent *)); +void prlex __P((FILE *, struct wordent *)); +int readc __P((bool)); +void settell __P((void)); +void unreadc __P((int)); + +/* + * misc.c + */ +int any __P((char *, int)); +Char **blkcat __P((Char **, Char **)); +Char **blkcpy __P((Char **, Char **)); +Char **blkend __P((Char **)); +void blkfree __P((Char **)); +int blklen __P((Char **)); +void blkpr __P((FILE *, Char **)); +Char **blkspl __P((Char **, Char **)); +void closem __P((void)); +Char **copyblk __P((Char **)); +int dcopy __P((int, int)); +int dmove __P((int, int)); +void donefds __P((void)); +Char lastchr __P((Char *)); +void lshift __P((Char **, int)); +int number __P((Char *)); +int prefix __P((Char *, Char *)); +Char **saveblk __P((Char **)); +void setzero __P((char *, int)); +Char *strip __P((Char *)); +char *strsave __P((char *)); +char *strspl __P((char *, char *)); +void udvar __P((Char *)); + +#ifndef SHORT_STRINGS +# ifdef NOTUSED +char *strstr __P((const char *, const char *)); +# endif /* NOTUSED */ +char *strend __P((char *)); +#endif + +/* + * parse.c + */ +void alias __P((struct wordent *)); +void freesyn __P((struct command *)); +struct command * + syntax __P((struct wordent *, struct wordent *, int)); + + +/* + * proc.c + */ +void dobg __P((Char **, struct command *)); +void dobg1 __P((Char **, struct command *)); +void dofg __P((Char **, struct command *)); +void dofg1 __P((Char **, struct command *)); +void dojobs __P((Char **, struct command *)); +void dokill __P((Char **, struct command *)); +void donotify __P((Char **, struct command *)); +void dostop __P((Char **, struct command *)); +void dowait __P((Char **, struct command *)); +void palloc __P((int, struct command *)); +void panystop __P((bool)); +void pchild __P((int)); +void pendjob __P((void)); +struct process * + pfind __P((Char *)); +int pfork __P((struct command *, int)); +void pgetty __P((int, int)); +void pjwait __P((struct process *)); +void pnote __P((void)); +void prestjob __P((void)); +void psavejob __P((void)); +void pstart __P((struct process *, int)); +void pwait __P((void)); + +/* + * sem.c + */ +void execute __P((struct command *, int, int *, int *)); +void mypipe __P((int *)); + +/* + * set.c + */ +struct varent + *adrof1 __P((Char *, struct varent *)); +void doset __P((Char **, struct command *)); +void dolet __P((Char **, struct command *)); +Char *putn __P((int)); +int getn __P((Char *)); +Char *value1 __P((Char *, struct varent *)); +void set __P((Char *, Char *)); +void set1 __P((Char *, Char **, struct varent *)); +void setq __P((Char *, Char **, struct varent *)); +void unset __P((Char **, struct command *)); +void unset1 __P((Char *[], struct varent *)); +void unsetv __P((Char *)); +void setNS __P((Char *)); +void shift __P((Char **, struct command *)); +void plist __P((struct varent *)); + +/* + * time.c + */ +void donice __P((Char **, struct command *)); +void dotime __P((Char **, struct command *)); +void prusage __P((struct rusage *, struct rusage *, + struct timeval *, struct timeval *)); +void ruadd __P((struct rusage *, struct rusage *)); +void settimes __P((void)); +void tvadd __P((struct timeval *, struct timeval *)); +void tvsub __P((struct timeval *, struct timeval *, struct timeval *)); +void pcsecs __P((long)); +void psecs __P((long)); + +/* + * alloc.c + */ +void Free __P((ptr_t)); +ptr_t Malloc __P((size_t)); +ptr_t Realloc __P((ptr_t, size_t)); +ptr_t Calloc __P((size_t, size_t)); +void showall __P((Char **, struct command *)); + +/* + * str.c: + */ +#ifdef SHORT_STRINGS +Char *s_strchr __P((Char *, int)); +Char *s_strrchr __P((Char *, int)); +Char *s_strcat __P((Char *, Char *)); +#ifdef NOTUSED +Char *s_strncat __P((Char *, Char *, size_t)); +#endif +Char *s_strcpy __P((Char *, Char *)); +Char *s_strncpy __P((Char *, Char *, size_t)); +Char *s_strspl __P((Char *, Char *)); +size_t s_strlen __P((Char *)); +int s_strcmp __P((Char *, Char *)); +int s_strncmp __P((Char *, Char *, size_t)); +Char *s_strsave __P((Char *)); +Char *s_strend __P((Char *)); +Char *s_strstr __P((Char *, Char *)); +Char *str2short __P((char *)); +Char **blk2short __P((char **)); +char *short2str __P((Char *)); +char **short2blk __P((Char **)); +#endif +char *short2qstr __P((Char *)); +char *vis_str __P((Char *)); diff --git a/bin/csh/file.c b/bin/csh/file.c new file mode 100644 index 000000000000..523844d2b3a0 --- /dev/null +++ b/bin/csh/file.c @@ -0,0 +1,684 @@ +/*- + * Copyright (c) 1980, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * 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[] = "@(#)file.c 8.2 (Berkeley) 3/19/94"; +#endif /* not lint */ + +#ifdef FILEC + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <termios.h> +#include <dirent.h> +#include <pwd.h> +#include <stdlib.h> +#include <unistd.h> +#ifndef SHORT_STRINGS +#include <string.h> +#endif /* SHORT_STRINGS */ +#if __STDC__ +# include <stdarg.h> +#else +# include <varargs.h> +#endif + +#include "csh.h" +#include "extern.h" + +/* + * Tenex style file name recognition, .. and more. + * History: + * Author: Ken Greer, Sept. 1975, CMU. + * Finally got around to adding to the Cshell., Ken Greer, Dec. 1981. + */ + +#define ON 1 +#define OFF 0 +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#define ESC '\033' + +typedef enum { + LIST, RECOGNIZE +} COMMAND; + +static void setup_tty __P((int)); +static void back_to_col_1 __P((void)); +static void pushback __P((Char *)); +static void catn __P((Char *, Char *, int)); +static void copyn __P((Char *, Char *, int)); +static Char filetype __P((Char *, Char *)); +static void print_by_column __P((Char *, Char *[], int)); +static Char *tilde __P((Char *, Char *)); +static void retype __P((void)); +static void beep __P((void)); +static void print_recognized_stuff __P((Char *)); +static void extract_dir_and_name __P((Char *, Char *, Char *)); +static Char *getentry __P((DIR *, int)); +static void free_items __P((Char **)); +static int tsearch __P((Char *, COMMAND, int)); +static int recognize __P((Char *, Char *, int, int)); +static int is_prefix __P((Char *, Char *)); +static int is_suffix __P((Char *, Char *)); +static int ignored __P((Char *)); + +/* + * Put this here so the binary can be patched with adb to enable file + * completion by default. Filec controls completion, nobeep controls + * ringing the terminal bell on incomplete expansions. + */ +bool filec = 0; + +static void +setup_tty(on) + int on; +{ + static struct termios tchars; + + (void) tcgetattr(SHIN, &tchars); + + if (on) { + tchars.c_cc[VEOL] = ESC; + if (tchars.c_lflag & ICANON) + on = TCSANOW; + else { + on = TCSAFLUSH; + tchars.c_lflag |= ICANON; + } + } + else { + tchars.c_cc[VEOL] = _POSIX_VDISABLE; + on = TCSANOW; + } + + (void) tcsetattr(SHIN, TCSANOW, &tchars); +} + +/* + * Move back to beginning of current line + */ +static void +back_to_col_1() +{ + struct termios tty, tty_normal; + int omask; + + omask = sigblock(sigmask(SIGINT)); + (void) tcgetattr(SHOUT, &tty); + tty_normal = tty; + tty.c_iflag &= ~INLCR; + tty.c_oflag &= ~ONLCR; + (void) tcsetattr(SHOUT, TCSANOW, &tty); + (void) write(SHOUT, "\r", 1); + (void) tcsetattr(SHOUT, TCSANOW, &tty_normal); + (void) sigsetmask(omask); +} + +/* + * Push string contents back into tty queue + */ +static void +pushback(string) + Char *string; +{ + register Char *p; + struct termios tty, tty_normal; + int omask; + char c; + + omask = sigblock(sigmask(SIGINT)); + (void) tcgetattr(SHOUT, &tty); + tty_normal = tty; + tty.c_lflag &= ~(ECHOKE | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOCTL); + (void) tcsetattr(SHOUT, TCSANOW, &tty); + + for (p = string; (c = *p) != '\0'; p++) + (void) ioctl(SHOUT, TIOCSTI, (ioctl_t) & c); + (void) tcsetattr(SHOUT, TCSANOW, &tty_normal); + (void) sigsetmask(omask); +} + +/* + * Concatenate src onto tail of des. + * Des is a string whose maximum length is count. + * Always null terminate. + */ +static void +catn(des, src, count) + register Char *des, *src; + register int count; +{ + while (--count >= 0 && *des) + des++; + while (--count >= 0) + if ((*des++ = *src++) == 0) + return; + *des = '\0'; +} + +/* + * Like strncpy but always leave room for trailing \0 + * and always null terminate. + */ +static void +copyn(des, src, count) + register Char *des, *src; + register int count; +{ + while (--count >= 0) + if ((*des++ = *src++) == 0) + return; + *des = '\0'; +} + +static Char +filetype(dir, file) + Char *dir, *file; +{ + Char path[MAXPATHLEN]; + struct stat statb; + + catn(Strcpy(path, dir), file, sizeof(path) / sizeof(Char)); + if (lstat(short2str(path), &statb) == 0) { + switch (statb.st_mode & S_IFMT) { + case S_IFDIR: + return ('/'); + + case S_IFLNK: + if (stat(short2str(path), &statb) == 0 && /* follow it out */ + S_ISDIR(statb.st_mode)) + return ('>'); + else + return ('@'); + + case S_IFSOCK: + return ('='); + + default: + if (statb.st_mode & 0111) + return ('*'); + } + } + return (' '); +} + +static struct winsize win; + +/* + * Print sorted down columns + */ +static void +print_by_column(dir, items, count) + Char *dir, *items[]; + int count; +{ + register int i, rows, r, c, maxwidth = 0, columns; + + if (ioctl(SHOUT, TIOCGWINSZ, (ioctl_t) & win) < 0 || win.ws_col == 0) + win.ws_col = 80; + for (i = 0; i < count; i++) + maxwidth = maxwidth > (r = Strlen(items[i])) ? maxwidth : r; + maxwidth += 2; /* for the file tag and space */ + columns = win.ws_col / maxwidth; + if (columns == 0) + columns = 1; + rows = (count + (columns - 1)) / columns; + for (r = 0; r < rows; r++) { + for (c = 0; c < columns; c++) { + i = c * rows + r; + if (i < count) { + register int w; + + (void) fprintf(cshout, "%s", vis_str(items[i])); + (void) fputc(dir ? filetype(dir, items[i]) : ' ', cshout); + if (c < columns - 1) { /* last column? */ + w = Strlen(items[i]) + 1; + for (; w < maxwidth; w++) + (void) fputc(' ', cshout); + } + } + } + (void) fputc('\r', cshout); + (void) fputc('\n', cshout); + } +} + +/* + * Expand file name with possible tilde usage + * ~person/mumble + * expands to + * home_directory_of_person/mumble + */ +static Char * +tilde(new, old) + Char *new, *old; +{ + register Char *o, *p; + register struct passwd *pw; + static Char person[40]; + + if (old[0] != '~') + return (Strcpy(new, old)); + + for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++) + continue; + *p = '\0'; + if (person[0] == '\0') + (void) Strcpy(new, value(STRhome)); + else { + pw = getpwnam(short2str(person)); + if (pw == NULL) + return (NULL); + (void) Strcpy(new, str2short(pw->pw_dir)); + } + (void) Strcat(new, o); + return (new); +} + +/* + * Cause pending line to be printed + */ +static void +retype() +{ + struct termios tty; + + (void) tcgetattr(SHOUT, &tty); + tty.c_lflag |= PENDIN; + (void) tcsetattr(SHOUT, TCSANOW, &tty); +} + +static void +beep() +{ + if (adrof(STRnobeep) == 0) + (void) write(SHOUT, "\007", 1); +} + +/* + * Erase that silly ^[ and + * print the recognized part of the string + */ +static void +print_recognized_stuff(recognized_part) + Char *recognized_part; +{ + /* An optimized erasing of that silly ^[ */ + (void) fputc('\b', cshout); + (void) fputc('\b', cshout); + switch (Strlen(recognized_part)) { + + case 0: /* erase two Characters: ^[ */ + (void) fputc(' ', cshout); + (void) fputc(' ', cshout); + (void) fputc('\b', cshout); + (void) fputc('\b', cshout); + break; + + case 1: /* overstrike the ^, erase the [ */ + (void) fprintf(cshout, "%s", vis_str(recognized_part)); + (void) fputc(' ', cshout); + (void) fputc('\b', cshout); + break; + + default: /* overstrike both Characters ^[ */ + (void) fprintf(cshout, "%s", vis_str(recognized_part)); + break; + } + (void) fflush(cshout); +} + +/* + * Parse full path in file into 2 parts: directory and file names + * Should leave final slash (/) at end of dir. + */ +static void +extract_dir_and_name(path, dir, name) + Char *path, *dir, *name; +{ + register Char *p; + + p = Strrchr(path, '/'); + if (p == NULL) { + copyn(name, path, MAXNAMLEN); + dir[0] = '\0'; + } + else { + copyn(name, ++p, MAXNAMLEN); + copyn(dir, path, p - path); + } +} + +static Char * +getentry(dir_fd, looking_for_lognames) + DIR *dir_fd; + int looking_for_lognames; +{ + register struct passwd *pw; + register struct dirent *dirp; + + if (looking_for_lognames) { + if ((pw = getpwent()) == NULL) + return (NULL); + return (str2short(pw->pw_name)); + } + if ((dirp = readdir(dir_fd)) != NULL) + return (str2short(dirp->d_name)); + return (NULL); +} + +static void +free_items(items) + register Char **items; +{ + register int i; + + for (i = 0; items[i]; i++) + xfree((ptr_t) items[i]); + xfree((ptr_t) items); +} + +#define FREE_ITEMS(items) { \ + int omask;\ +\ + omask = sigblock(sigmask(SIGINT));\ + free_items(items);\ + items = NULL;\ + (void) sigsetmask(omask);\ +} + +/* + * Perform a RECOGNIZE or LIST command on string "word". + */ +static int +tsearch(word, command, max_word_length) + Char *word; + COMMAND command; + int max_word_length; +{ + static Char **items = NULL; + register DIR *dir_fd; + register numitems = 0, ignoring = TRUE, nignored = 0; + register name_length, looking_for_lognames; + Char tilded_dir[MAXPATHLEN + 1], dir[MAXPATHLEN + 1]; + Char name[MAXNAMLEN + 1], extended_name[MAXNAMLEN + 1]; + Char *entry; + +#define MAXITEMS 1024 + + if (items != NULL) + FREE_ITEMS(items); + + looking_for_lognames = (*word == '~') && (Strchr(word, '/') == NULL); + if (looking_for_lognames) { + (void) setpwent(); + copyn(name, &word[1], MAXNAMLEN); /* name sans ~ */ + dir_fd = NULL; + } + else { + extract_dir_and_name(word, dir, name); + if (tilde(tilded_dir, dir) == 0) + return (0); + dir_fd = opendir(*tilded_dir ? short2str(tilded_dir) : "."); + if (dir_fd == NULL) + return (0); + } + +again: /* search for matches */ + name_length = Strlen(name); + for (numitems = 0; (entry = getentry(dir_fd, looking_for_lognames)) != NULL;) { + if (!is_prefix(name, entry)) + continue; + /* Don't match . files on null prefix match */ + if (name_length == 0 && entry[0] == '.' && + !looking_for_lognames) + continue; + if (command == LIST) { + if (numitems >= MAXITEMS) { + (void) fprintf(csherr, "\nYikes!! Too many %s!!\n", + looking_for_lognames ? + "names in password file" : "files"); + break; + } + if (items == NULL) + items = (Char **) xcalloc(sizeof(items[0]), MAXITEMS); + items[numitems] = (Char *) xmalloc((size_t) (Strlen(entry) + 1) * + sizeof(Char)); + copyn(items[numitems], entry, MAXNAMLEN); + numitems++; + } + else { /* RECOGNIZE command */ + if (ignoring && ignored(entry)) + nignored++; + else if (recognize(extended_name, + entry, name_length, ++numitems)) + break; + } + } + if (ignoring && numitems == 0 && nignored > 0) { + ignoring = FALSE; + nignored = 0; + if (looking_for_lognames) + (void) setpwent(); + else + rewinddir(dir_fd); + goto again; + } + + if (looking_for_lognames) + (void) endpwent(); + else + (void) closedir(dir_fd); + if (numitems == 0) + return (0); + if (command == RECOGNIZE) { + if (looking_for_lognames) + copyn(word, STRtilde, 1); + else + /* put back dir part */ + copyn(word, dir, max_word_length); + /* add extended name */ + catn(word, extended_name, max_word_length); + return (numitems); + } + else { /* LIST */ + qsort((ptr_t) items, numitems, sizeof(items[0]), + (int (*) __P((const void *, const void *))) sortscmp); + print_by_column(looking_for_lognames ? NULL : tilded_dir, + items, numitems); + if (items != NULL) + FREE_ITEMS(items); + } + return (0); +} + +/* + * Object: extend what user typed up to an ambiguity. + * Algorithm: + * On first match, copy full entry (assume it'll be the only match) + * On subsequent matches, shorten extended_name to the first + * Character mismatch between extended_name and entry. + * If we shorten it back to the prefix length, stop searching. + */ +static int +recognize(extended_name, entry, name_length, numitems) + Char *extended_name, *entry; + int name_length, numitems; +{ + if (numitems == 1) /* 1st match */ + copyn(extended_name, entry, MAXNAMLEN); + else { /* 2nd & subsequent matches */ + register Char *x, *ent; + register int len = 0; + + x = extended_name; + for (ent = entry; *x && *x == *ent++; x++, len++) + continue; + *x = '\0'; /* Shorten at 1st Char diff */ + if (len == name_length) /* Ambiguous to prefix? */ + return (-1); /* So stop now and save time */ + } + return (0); +} + +/* + * Return true if check matches initial Chars in template. + * This differs from PWB imatch in that if check is null + * it matches anything. + */ +static int +is_prefix(check, template) + register Char *check, *template; +{ + do + if (*check == 0) + return (TRUE); + while (*check++ == *template++); + return (FALSE); +} + +/* + * Return true if the Chars in template appear at the + * end of check, I.e., are it's suffix. + */ +static int +is_suffix(check, template) + Char *check, *template; +{ + register Char *c, *t; + + for (c = check; *c++;) + continue; + for (t = template; *t++;) + continue; + for (;;) { + if (t == template) + return 1; + if (c == check || *--t != *--c) + return 0; + } +} + +int +tenex(inputline, inputline_size) + Char *inputline; + int inputline_size; +{ + register int numitems, num_read; + char tinputline[BUFSIZ]; + + + setup_tty(ON); + + while ((num_read = read(SHIN, tinputline, BUFSIZ)) > 0) { + int i; + static Char delims[] = {' ', '\'', '"', '\t', ';', '&', '<', + '>', '(', ')', '|', '^', '%', '\0'}; + register Char *str_end, *word_start, last_Char, should_retype; + register int space_left; + COMMAND command; + + for (i = 0; i < num_read; i++) + inputline[i] = (unsigned char) tinputline[i]; + last_Char = inputline[num_read - 1] & ASCII; + + if (last_Char == '\n' || num_read == inputline_size) + break; + command = (last_Char == ESC) ? RECOGNIZE : LIST; + if (command == LIST) + (void) fputc('\n', cshout); + str_end = &inputline[num_read]; + if (last_Char == ESC) + --str_end; /* wipeout trailing cmd Char */ + *str_end = '\0'; + /* + * Find LAST occurence of a delimiter in the inputline. The word start + * is one Character past it. + */ + for (word_start = str_end; word_start > inputline; --word_start) + if (Strchr(delims, word_start[-1])) + break; + space_left = inputline_size - (word_start - inputline) - 1; + numitems = tsearch(word_start, command, space_left); + + if (command == RECOGNIZE) { + /* print from str_end on */ + print_recognized_stuff(str_end); + if (numitems != 1) /* Beep = No match/ambiguous */ + beep(); + } + + /* + * Tabs in the input line cause trouble after a pushback. tty driver + * won't backspace over them because column positions are now + * incorrect. This is solved by retyping over current line. + */ + should_retype = FALSE; + if (Strchr(inputline, '\t')) { /* tab Char in input line? */ + back_to_col_1(); + should_retype = TRUE; + } + if (command == LIST) /* Always retype after a LIST */ + should_retype = TRUE; + if (should_retype) + printprompt(); + pushback(inputline); + if (should_retype) + retype(); + } + setup_tty(OFF); + return (num_read); +} + +static int +ignored(entry) + register Char *entry; +{ + struct varent *vp; + register Char **cp; + + if ((vp = adrof(STRfignore)) == NULL || (cp = vp->vec) == NULL) + return (FALSE); + for (; *cp != NULL; cp++) + if (is_suffix(entry, *cp)) + return (TRUE); + return (FALSE); +} +#endif /* FILEC */ diff --git a/bin/csh/func.c b/bin/csh/func.c new file mode 100644 index 000000000000..82664c67565e --- /dev/null +++ b/bin/csh/func.c @@ -0,0 +1,1498 @@ +/*- + * Copyright (c) 1980, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * 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[] = "@(#)func.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <signal.h> +#include <locale.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#if __STDC__ +# include <stdarg.h> +#else +# include <varargs.h> +#endif + +#include "csh.h" +#include "extern.h" +#include "pathnames.h" + +extern char **environ; + +static int zlast = -1; +static void islogin __P((void)); +static void reexecute __P((struct command *)); +static void preread __P((void)); +static void doagain __P((void)); +static void search __P((int, int, Char *)); +static int getword __P((Char *)); +static int keyword __P((Char *)); +static void toend __P((void)); +static void xecho __P((int, Char **)); +static void Unsetenv __P((Char *)); + +struct biltins * +isbfunc(t) + struct command *t; +{ + register Char *cp = t->t_dcom[0]; + register struct biltins *bp, *bp1, *bp2; + static struct biltins label = {"", dozip, 0, 0}; + static struct biltins foregnd = {"%job", dofg1, 0, 0}; + static struct biltins backgnd = {"%job &", dobg1, 0, 0}; + + if (lastchr(cp) == ':') { + label.bname = short2str(cp); + return (&label); + } + if (*cp == '%') { + if (t->t_dflg & F_AMPERSAND) { + t->t_dflg &= ~F_AMPERSAND; + backgnd.bname = short2str(cp); + return (&backgnd); + } + foregnd.bname = short2str(cp); + return (&foregnd); + } + /* + * Binary search Bp1 is the beginning of the current search range. Bp2 is + * one past the end. + */ + for (bp1 = bfunc, bp2 = bfunc + nbfunc; bp1 < bp2;) { + register i; + + bp = bp1 + ((bp2 - bp1) >> 1); + if ((i = *cp - *bp->bname) == 0 && + (i = Strcmp(cp, str2short(bp->bname))) == 0) + return bp; + if (i < 0) + bp2 = bp; + else + bp1 = bp + 1; + } + return (0); +} + +void +func(t, bp) + register struct command *t; + register struct biltins *bp; +{ + int i; + + xechoit(t->t_dcom); + setname(bp->bname); + i = blklen(t->t_dcom) - 1; + if (i < bp->minargs) + stderror(ERR_NAME | ERR_TOOFEW); + if (i > bp->maxargs) + stderror(ERR_NAME | ERR_TOOMANY); + (*bp->bfunct) (t->t_dcom, t); +} + +void +/*ARGSUSED*/ +doonintr(v, t) + Char **v; + struct command *t; +{ + register Char *cp; + register Char *vv = v[1]; + + if (parintr == SIG_IGN) + return; + if (setintr && intty) + stderror(ERR_NAME | ERR_TERMINAL); + cp = gointr; + gointr = 0; + xfree((ptr_t) cp); + if (vv == 0) { + if (setintr) + (void) sigblock(sigmask(SIGINT)); + else + (void) signal(SIGINT, SIG_DFL); + gointr = 0; + } + else if (eq((vv = strip(vv)), STRminus)) { + (void) signal(SIGINT, SIG_IGN); + gointr = Strsave(STRminus); + } + else { + gointr = Strsave(vv); + (void) signal(SIGINT, pintr); + } +} + +void +/*ARGSUSED*/ +donohup(v, t) + Char **v; + struct command *t; +{ + if (intty) + stderror(ERR_NAME | ERR_TERMINAL); + if (setintr == 0) { + (void) signal(SIGHUP, SIG_IGN); + } +} + +void +/*ARGSUSED*/ +dozip(v, t) + Char **v; + struct command *t; +{ + ; +} + +void +prvars() +{ + plist(&shvhed); +} + +void +/*ARGSUSED*/ +doalias(v, t) + Char **v; + struct command *t; +{ + register struct varent *vp; + register Char *p; + + v++; + p = *v++; + if (p == 0) + plist(&aliases); + else if (*v == 0) { + vp = adrof1(strip(p), &aliases); + if (vp) { + blkpr(cshout, vp->vec); + fputc('\n', cshout); + } + } + else { + if (eq(p, STRalias) || eq(p, STRunalias)) { + setname(vis_str(p)); + stderror(ERR_NAME | ERR_DANGER); + } + set1(strip(p), saveblk(v), &aliases); + } +} + +void +/*ARGSUSED*/ +unalias(v, t) + Char **v; + struct command *t; +{ + unset1(v, &aliases); +} + +void +/*ARGSUSED*/ +dologout(v, t) + Char **v; + struct command *t; +{ + islogin(); + goodbye(); +} + +void +/*ARGSUSED*/ +dologin(v, t) + Char **v; + struct command *t; +{ + islogin(); + rechist(); + (void) signal(SIGTERM, parterm); + (void) execl(_PATH_LOGIN, "login", short2str(v[1]), NULL); + untty(); + xexit(1); +} + +static void +islogin() +{ + if (chkstop == 0 && setintr) + panystop(0); + if (loginsh) + return; + stderror(ERR_NOTLOGIN); +} + +void +doif(v, kp) + Char **v; + struct command *kp; +{ + register int i; + register Char **vv; + + v++; + i = expr(&v); + vv = v; + if (*vv == NULL) + stderror(ERR_NAME | ERR_EMPTYIF); + if (eq(*vv, STRthen)) { + if (*++vv) + stderror(ERR_NAME | ERR_IMPRTHEN); + setname(vis_str(STRthen)); + /* + * If expression was zero, then scan to else, otherwise just fall into + * following code. + */ + if (!i) + search(T_IF, 0, NULL); + return; + } + /* + * Simple command attached to this if. Left shift the node in this tree, + * munging it so we can reexecute it. + */ + if (i) { + lshift(kp->t_dcom, vv - kp->t_dcom); + reexecute(kp); + donefds(); + } +} + +/* + * Reexecute a command, being careful not + * to redo i/o redirection, which is already set up. + */ +static void +reexecute(kp) + register struct command *kp; +{ + kp->t_dflg &= F_SAVE; + kp->t_dflg |= F_REPEAT; + /* + * If tty is still ours to arbitrate, arbitrate it; otherwise dont even set + * pgrp's as the jobs would then have no way to get the tty (we can't give + * it to them, and our parent wouldn't know their pgrp, etc. + */ + execute(kp, (tpgrp > 0 ? tpgrp : -1), NULL, NULL); +} + +void +/*ARGSUSED*/ +doelse(v, t) + Char **v; + struct command *t; +{ + search(T_ELSE, 0, NULL); +} + +void +/*ARGSUSED*/ +dogoto(v, t) + Char **v; + struct command *t; +{ + Char *lp; + + gotolab(lp = globone(v[1], G_ERROR)); + xfree((ptr_t) lp); +} + +void +gotolab(lab) + Char *lab; +{ + register struct whyle *wp; + /* + * While we still can, locate any unknown ends of existing loops. This + * obscure code is the WORST result of the fact that we don't really parse. + */ + zlast = T_GOTO; + for (wp = whyles; wp; wp = wp->w_next) + if (wp->w_end.type == F_SEEK && wp->w_end.f_seek == 0) { + search(T_BREAK, 0, NULL); + btell(&wp->w_end); + } + else + bseek(&wp->w_end); + search(T_GOTO, 0, lab); + /* + * Eliminate loops which were exited. + */ + wfree(); +} + +void +/*ARGSUSED*/ +doswitch(v, t) + Char **v; + struct command *t; +{ + register Char *cp, *lp; + + v++; + if (!*v || *(*v++) != '(') + stderror(ERR_SYNTAX); + cp = **v == ')' ? STRNULL : *v++; + if (*(*v++) != ')') + v--; + if (*v) + stderror(ERR_SYNTAX); + search(T_SWITCH, 0, lp = globone(cp, G_ERROR)); + xfree((ptr_t) lp); +} + +void +/*ARGSUSED*/ +dobreak(v, t) + Char **v; + struct command *t; +{ + if (whyles) + toend(); + else + stderror(ERR_NAME | ERR_NOTWHILE); +} + +void +/*ARGSUSED*/ +doexit(v, t) + Char **v; + struct command *t; +{ + if (chkstop == 0 && (intty || intact) && evalvec == 0) + panystop(0); + /* + * Don't DEMAND parentheses here either. + */ + v++; + if (*v) { + set(STRstatus, putn(expr(&v))); + if (*v) + stderror(ERR_NAME | ERR_EXPRESSION); + } + btoeof(); + if (intty) + (void) close(SHIN); +} + +void +/*ARGSUSED*/ +doforeach(v, t) + Char **v; + struct command *t; +{ + register Char *cp, *sp; + register struct whyle *nwp; + + v++; + sp = cp = strip(*v); + if (!letter(*sp)) + stderror(ERR_NAME | ERR_VARBEGIN); + while (*cp && alnum(*cp)) + cp++; + if (*cp) + stderror(ERR_NAME | ERR_VARALNUM); + if ((cp - sp) > MAXVARLEN) + stderror(ERR_NAME | ERR_VARTOOLONG); + cp = *v++; + if (v[0][0] != '(' || v[blklen(v) - 1][0] != ')') + stderror(ERR_NAME | ERR_NOPAREN); + v++; + gflag = 0, tglob(v); + v = globall(v); + if (v == 0) + stderror(ERR_NAME | ERR_NOMATCH); + nwp = (struct whyle *) xcalloc(1, sizeof *nwp); + nwp->w_fe = nwp->w_fe0 = v; + gargv = 0; + btell(&nwp->w_start); + nwp->w_fename = Strsave(cp); + nwp->w_next = whyles; + nwp->w_end.type = F_SEEK; + whyles = nwp; + /* + * Pre-read the loop so as to be more comprehensible to a terminal user. + */ + zlast = T_FOREACH; + if (intty) + preread(); + doagain(); +} + +void +/*ARGSUSED*/ +dowhile(v, t) + Char **v; + struct command *t; +{ + register int status; + register bool again = whyles != 0 && SEEKEQ(&whyles->w_start, &lineloc) && + whyles->w_fename == 0; + + v++; + /* + * Implement prereading here also, taking care not to evaluate the + * expression before the loop has been read up from a terminal. + */ + if (intty && !again) + status = !exp0(&v, 1); + else + status = !expr(&v); + if (*v) + stderror(ERR_NAME | ERR_EXPRESSION); + if (!again) { + register struct whyle *nwp = + (struct whyle *) xcalloc(1, sizeof(*nwp)); + + nwp->w_start = lineloc; + nwp->w_end.type = F_SEEK; + nwp->w_end.f_seek = 0; + nwp->w_next = whyles; + whyles = nwp; + zlast = T_WHILE; + if (intty) { + /* + * The tty preread + */ + preread(); + doagain(); + return; + } + } + if (status) + /* We ain't gonna loop no more, no more! */ + toend(); +} + +static void +preread() +{ + whyles->w_end.type = I_SEEK; + if (setintr) + (void) sigsetmask(sigblock((sigset_t) 0) & ~sigmask(SIGINT)); + + search(T_BREAK, 0, NULL); /* read the expression in */ + if (setintr) + (void) sigblock(sigmask(SIGINT)); + btell(&whyles->w_end); +} + +void +/*ARGSUSED*/ +doend(v, t) + Char **v; + struct command *t; +{ + if (!whyles) + stderror(ERR_NAME | ERR_NOTWHILE); + btell(&whyles->w_end); + doagain(); +} + +void +/*ARGSUSED*/ +docontin(v, t) + Char **v; + struct command *t; +{ + if (!whyles) + stderror(ERR_NAME | ERR_NOTWHILE); + doagain(); +} + +static void +doagain() +{ + /* Repeating a while is simple */ + if (whyles->w_fename == 0) { + bseek(&whyles->w_start); + return; + } + /* + * The foreach variable list actually has a spurious word ")" at the end of + * the w_fe list. Thus we are at the of the list if one word beyond this + * is 0. + */ + if (!whyles->w_fe[1]) { + dobreak(NULL, NULL); + return; + } + set(whyles->w_fename, Strsave(*whyles->w_fe++)); + bseek(&whyles->w_start); +} + +void +dorepeat(v, kp) + Char **v; + struct command *kp; +{ + register int i; + register sigset_t omask = 0; + + i = getn(v[1]); + if (setintr) + omask = sigblock(sigmask(SIGINT)) & ~sigmask(SIGINT); + lshift(v, 2); + while (i > 0) { + if (setintr) + (void) sigsetmask(omask); + reexecute(kp); + --i; + } + donefds(); + if (setintr) + (void) sigsetmask(omask); +} + +void +/*ARGSUSED*/ +doswbrk(v, t) + Char **v; + struct command *t; +{ + search(T_BRKSW, 0, NULL); +} + +int +srchx(cp) + register Char *cp; +{ + register struct srch *sp, *sp1, *sp2; + register i; + + /* + * Binary search Sp1 is the beginning of the current search range. Sp2 is + * one past the end. + */ + for (sp1 = srchn, sp2 = srchn + nsrchn; sp1 < sp2;) { + sp = sp1 + ((sp2 - sp1) >> 1); + if ((i = *cp - *sp->s_name) == 0 && + (i = Strcmp(cp, str2short(sp->s_name))) == 0) + return sp->s_value; + if (i < 0) + sp2 = sp; + else + sp1 = sp + 1; + } + return (-1); +} + +static Char Stype; +static Char *Sgoal; + +/*VARARGS2*/ +static void +search(type, level, goal) + int type; + register int level; + Char *goal; +{ + Char wordbuf[BUFSIZ]; + register Char *aword = wordbuf; + register Char *cp; + + Stype = type; + Sgoal = goal; + if (type == T_GOTO) { + struct Ain a; + a.type = F_SEEK; + a.f_seek = 0; + bseek(&a); + } + do { + if (intty && fseekp == feobp && aret == F_SEEK) + (void) fprintf(cshout, "? "), (void) fflush(cshout); + aword[0] = 0; + (void) getword(aword); + switch (srchx(aword)) { + + case T_ELSE: + if (level == 0 && type == T_IF) + return; + break; + + case T_IF: + while (getword(aword)) + continue; + if ((type == T_IF || type == T_ELSE) && + eq(aword, STRthen)) + level++; + break; + + case T_ENDIF: + if (type == T_IF || type == T_ELSE) + level--; + break; + + case T_FOREACH: + case T_WHILE: + if (type == T_BREAK) + level++; + break; + + case T_END: + if (type == T_BREAK) + level--; + break; + + case T_SWITCH: + if (type == T_SWITCH || type == T_BRKSW) + level++; + break; + + case T_ENDSW: + if (type == T_SWITCH || type == T_BRKSW) + level--; + break; + + case T_LABEL: + if (type == T_GOTO && getword(aword) && eq(aword, goal)) + level = -1; + break; + + default: + if (type != T_GOTO && (type != T_SWITCH || level != 0)) + break; + if (lastchr(aword) != ':') + break; + aword[Strlen(aword) - 1] = 0; + if ((type == T_GOTO && eq(aword, goal)) || + (type == T_SWITCH && eq(aword, STRdefault))) + level = -1; + break; + + case T_CASE: + if (type != T_SWITCH || level != 0) + break; + (void) getword(aword); + if (lastchr(aword) == ':') + aword[Strlen(aword) - 1] = 0; + cp = strip(Dfix1(aword)); + if (Gmatch(goal, cp)) + level = -1; + xfree((ptr_t) cp); + break; + + case T_DEFAULT: + if (type == T_SWITCH && level == 0) + level = -1; + break; + } + (void) getword(NULL); + } while (level >= 0); +} + +static int +getword(wp) + register Char *wp; +{ + register int found = 0; + register int c, d; + int kwd = 0; + Char *owp = wp; + + c = readc(1); + d = 0; + do { + while (c == ' ' || c == '\t') + c = readc(1); + if (c == '#') + do + c = readc(1); + while (c >= 0 && c != '\n'); + if (c < 0) + goto past; + if (c == '\n') { + if (wp) + break; + return (0); + } + unreadc(c); + found = 1; + do { + c = readc(1); + if (c == '\\' && (c = readc(1)) == '\n') + c = ' '; + if (c == '\'' || c == '"') + if (d == 0) + d = c; + else if (d == c) + d = 0; + if (c < 0) + goto past; + if (wp) { + *wp++ = c; + *wp = 0; /* end the string b4 test */ + } + } while ((d || (!(kwd = keyword(owp)) && c != ' ' + && c != '\t')) && c != '\n'); + } while (wp == 0); + + /* + * if we have read a keyword ( "if", "switch" or "while" ) then we do not + * need to unreadc the look-ahead char + */ + if (!kwd) { + unreadc(c); + if (found) + *--wp = 0; + } + + return (found); + +past: + switch (Stype) { + + case T_IF: + stderror(ERR_NAME | ERR_NOTFOUND, "then/endif"); + + case T_ELSE: + stderror(ERR_NAME | ERR_NOTFOUND, "endif"); + + case T_BRKSW: + case T_SWITCH: + stderror(ERR_NAME | ERR_NOTFOUND, "endsw"); + + case T_BREAK: + stderror(ERR_NAME | ERR_NOTFOUND, "end"); + + case T_GOTO: + setname(vis_str(Sgoal)); + stderror(ERR_NAME | ERR_NOTFOUND, "label"); + } + /* NOTREACHED */ + return (0); +} + +/* + * keyword(wp) determines if wp is one of the built-n functions if, + * switch or while. It seems that when an if statement looks like + * "if(" then getword above sucks in the '(' and so the search routine + * never finds what it is scanning for. Rather than rewrite doword, I hack + * in a test to see if the string forms a keyword. Then doword stops + * and returns the word "if" -strike + */ + +static int +keyword(wp) + Char *wp; +{ + static Char STRif[] = {'i', 'f', '\0'}; + static Char STRwhile[] = {'w', 'h', 'i', 'l', 'e', '\0'}; + static Char STRswitch[] = {'s', 'w', 'i', 't', 'c', 'h', '\0'}; + + if (!wp) + return (0); + + if ((Strcmp(wp, STRif) == 0) || (Strcmp(wp, STRwhile) == 0) + || (Strcmp(wp, STRswitch) == 0)) + return (1); + + return (0); +} + +static void +toend() +{ + if (whyles->w_end.type == F_SEEK && whyles->w_end.f_seek == 0) { + search(T_BREAK, 0, NULL); + btell(&whyles->w_end); + whyles->w_end.f_seek--; + } + else + bseek(&whyles->w_end); + wfree(); +} + +void +wfree() +{ + struct Ain o; + struct whyle *nwp; + + btell(&o); + + for (; whyles; whyles = nwp) { + register struct whyle *wp = whyles; + nwp = wp->w_next; + + /* + * We free loops that have different seek types. + */ + if (wp->w_end.type != I_SEEK && wp->w_start.type == wp->w_end.type && + wp->w_start.type == o.type) { + if (wp->w_end.type == F_SEEK) { + if (o.f_seek >= wp->w_start.f_seek && + (wp->w_end.f_seek == 0 || o.f_seek < wp->w_end.f_seek)) + break; + } + else { + if (o.a_seek >= wp->w_start.a_seek && + (wp->w_end.a_seek == 0 || o.a_seek < wp->w_end.a_seek)) + break; + } + } + + if (wp->w_fe0) + blkfree(wp->w_fe0); + if (wp->w_fename) + xfree((ptr_t) wp->w_fename); + xfree((ptr_t) wp); + } +} + +void +/*ARGSUSED*/ +doecho(v, t) + Char **v; + struct command *t; +{ + xecho(' ', v); +} + +void +/*ARGSUSED*/ +doglob(v, t) + Char **v; + struct command *t; +{ + xecho(0, v); + (void) fflush(cshout); +} + +static void +xecho(sep, v) + int sep; + register Char **v; +{ + register Char *cp; + int nonl = 0; + + if (setintr) + (void) sigsetmask(sigblock((sigset_t) 0) & ~sigmask(SIGINT)); + v++; + if (*v == 0) + return; + gflag = 0, tglob(v); + if (gflag) { + v = globall(v); + if (v == 0) + stderror(ERR_NAME | ERR_NOMATCH); + } + else { + v = gargv = saveblk(v); + trim(v); + } + if (sep == ' ' && *v && eq(*v, STRmn)) + nonl++, v++; + while ((cp = *v++) != NULL) { + register int c; + + while ((c = *cp++) != '\0') + (void) vis_fputc(c | QUOTE, cshout); + + if (*v) + (void) vis_fputc(sep | QUOTE, cshout); + } + if (sep && nonl == 0) + (void) fputc('\n', cshout); + else + (void) fflush(cshout); + if (setintr) + (void) sigblock(sigmask(SIGINT)); + if (gargv) + blkfree(gargv), gargv = 0; +} + +void +/*ARGSUSED*/ +dosetenv(v, t) + Char **v; + struct command *t; +{ + Char *vp, *lp; + + v++; + if ((vp = *v++) == 0) { + register Char **ep; + + if (setintr) + (void) sigsetmask(sigblock((sigset_t) 0) & ~sigmask(SIGINT)); + for (ep = STR_environ; *ep; ep++) + (void) fprintf(cshout, "%s\n", vis_str(*ep)); + return; + } + if ((lp = *v++) == 0) + lp = STRNULL; + Setenv(vp, lp = globone(lp, G_APPEND)); + if (eq(vp, STRPATH)) { + importpath(lp); + dohash(NULL, NULL); + } + else if (eq(vp, STRLANG) || eq(vp, STRLC_CTYPE)) { +#ifdef NLS + int k; + + (void) setlocale(LC_ALL, ""); + for (k = 0200; k <= 0377 && !Isprint(k); k++) + continue; + AsciiOnly = k > 0377; +#else + AsciiOnly = 0; +#endif /* NLS */ + } + xfree((ptr_t) lp); +} + +void +/*ARGSUSED*/ +dounsetenv(v, t) + Char **v; + struct command *t; +{ + Char **ep, *p, *n; + int i, maxi; + static Char *name = NULL; + + if (name) + xfree((ptr_t) name); + /* + * Find the longest environment variable + */ + for (maxi = 0, ep = STR_environ; *ep; ep++) { + for (i = 0, p = *ep; *p && *p != '='; p++, i++) + continue; + if (i > maxi) + maxi = i; + } + + name = (Char *) xmalloc((size_t) (maxi + 1) * sizeof(Char)); + + while (++v && *v) + for (maxi = 1; maxi;) + for (maxi = 0, ep = STR_environ; *ep; ep++) { + for (n = name, p = *ep; *p && *p != '='; *n++ = *p++) + continue; + *n = '\0'; + if (!Gmatch(name, *v)) + continue; + maxi = 1; + if (eq(name, STRLANG) || eq(name, STRLC_CTYPE)) { +#ifdef NLS + int k; + + (void) setlocale(LC_ALL, ""); + for (k = 0200; k <= 0377 && !Isprint(k); k++) + continue; + AsciiOnly = k > 0377; +#else + AsciiOnly = getenv("LANG") == NULL && + getenv("LC_CTYPE") == NULL; +#endif /* NLS */ + } + /* + * Delete name, and start again cause the environment changes + */ + Unsetenv(name); + break; + } + xfree((ptr_t) name); + name = NULL; +} + +void +Setenv(name, val) + Char *name, *val; +{ + register Char **ep = STR_environ; + register Char *cp, *dp; + Char *blk[2]; + Char **oep = ep; + + + for (; *ep; ep++) { + for (cp = name, dp = *ep; *cp && *cp == *dp; cp++, dp++) + continue; + if (*cp != 0 || *dp != '=') + continue; + cp = Strspl(STRequal, val); + xfree((ptr_t) * ep); + *ep = strip(Strspl(name, cp)); + xfree((ptr_t) cp); + blkfree((Char **) environ); + environ = short2blk(STR_environ); + return; + } + cp = Strspl(name, STRequal); + blk[0] = strip(Strspl(cp, val)); + xfree((ptr_t) cp); + blk[1] = 0; + STR_environ = blkspl(STR_environ, blk); + blkfree((Char **) environ); + environ = short2blk(STR_environ); + xfree((ptr_t) oep); +} + +static void +Unsetenv(name) + Char *name; +{ + register Char **ep = STR_environ; + register Char *cp, *dp; + Char **oep = ep; + + for (; *ep; ep++) { + for (cp = name, dp = *ep; *cp && *cp == *dp; cp++, dp++) + continue; + if (*cp != 0 || *dp != '=') + continue; + cp = *ep; + *ep = 0; + STR_environ = blkspl(STR_environ, ep + 1); + environ = short2blk(STR_environ); + *ep = cp; + xfree((ptr_t) cp); + xfree((ptr_t) oep); + return; + } +} + +void +/*ARGSUSED*/ +doumask(v, t) + Char **v; + struct command *t; +{ + register Char *cp = v[1]; + register int i; + + if (cp == 0) { + i = umask(0); + (void) umask(i); + (void) fprintf(cshout, "%o\n", i); + return; + } + i = 0; + while (Isdigit(*cp) && *cp != '8' && *cp != '9') + i = i * 8 + *cp++ - '0'; + if (*cp || i < 0 || i > 0777) + stderror(ERR_NAME | ERR_MASK); + (void) umask(i); +} + +typedef quad_t RLIM_TYPE; + +static struct limits { + int limconst; + char *limname; + int limdiv; + char *limscale; +} limits[] = { + { RLIMIT_CPU, "cputime", 1, "seconds" }, + { RLIMIT_FSIZE, "filesize", 1024, "kbytes" }, + { RLIMIT_DATA, "datasize", 1024, "kbytes" }, + { RLIMIT_STACK, "stacksize", 1024, "kbytes" }, + { RLIMIT_CORE, "coredumpsize", 1024, "kbytes" }, + { RLIMIT_RSS, "memoryuse", 1024, "kbytes" }, + { RLIMIT_MEMLOCK, "memorylocked", 1024, "kbytes" }, + { RLIMIT_NPROC, "maxproc", 1, "" }, + { RLIMIT_NOFILE, "openfiles", 1, "" }, + { -1, NULL, 0, NULL } +}; + +static struct limits *findlim(); +static RLIM_TYPE getval(); +static void limtail(); +static void plim(); +static int setlim(); + +static struct limits * +findlim(cp) + Char *cp; +{ + register struct limits *lp, *res; + + res = (struct limits *) NULL; + for (lp = limits; lp->limconst >= 0; lp++) + if (prefix(cp, str2short(lp->limname))) { + if (res) + stderror(ERR_NAME | ERR_AMBIG); + res = lp; + } + if (res) + return (res); + stderror(ERR_NAME | ERR_LIMIT); + /* NOTREACHED */ + return (0); +} + +void +/*ARGSUSED*/ +dolimit(v, t) + Char **v; + struct command *t; +{ + register struct limits *lp; + register RLIM_TYPE limit; + char hard = 0; + + v++; + if (*v && eq(*v, STRmh)) { + hard = 1; + v++; + } + if (*v == 0) { + for (lp = limits; lp->limconst >= 0; lp++) + plim(lp, hard); + return; + } + lp = findlim(v[0]); + if (v[1] == 0) { + plim(lp, hard); + return; + } + limit = getval(lp, v + 1); + if (setlim(lp, hard, limit) < 0) + stderror(ERR_SILENT); +} + +static RLIM_TYPE +getval(lp, v) + register struct limits *lp; + Char **v; +{ + register float f; + double atof(); + Char *cp = *v++; + + f = atof(short2str(cp)); + + while (Isdigit(*cp) || *cp == '.' || *cp == 'e' || *cp == 'E') + cp++; + if (*cp == 0) { + if (*v == 0) + return ((RLIM_TYPE) ((f + 0.5) * lp->limdiv)); + cp = *v; + } + switch (*cp) { + case ':': + if (lp->limconst != RLIMIT_CPU) + goto badscal; + return ((RLIM_TYPE) (f * 60.0 + atof(short2str(cp + 1)))); + case 'h': + if (lp->limconst != RLIMIT_CPU) + goto badscal; + limtail(cp, "hours"); + f *= 3600.0; + break; + case 'm': + if (lp->limconst == RLIMIT_CPU) { + limtail(cp, "minutes"); + f *= 60.0; + break; + } + *cp = 'm'; + limtail(cp, "megabytes"); + f *= 1024.0 * 1024.0; + break; + case 's': + if (lp->limconst != RLIMIT_CPU) + goto badscal; + limtail(cp, "seconds"); + break; + case 'M': + if (lp->limconst == RLIMIT_CPU) + goto badscal; + *cp = 'm'; + limtail(cp, "megabytes"); + f *= 1024.0 * 1024.0; + break; + case 'k': + if (lp->limconst == RLIMIT_CPU) + goto badscal; + limtail(cp, "kbytes"); + f *= 1024.0; + break; + case 'u': + limtail(cp, "unlimited"); + return (RLIM_INFINITY); + default: +badscal: + stderror(ERR_NAME | ERR_SCALEF); + } + f += 0.5; + if (f > (float) RLIM_INFINITY) + return RLIM_INFINITY; + else + return ((RLIM_TYPE) f); +} + +static void +limtail(cp, str) + Char *cp; + char *str; +{ + while (*cp && *cp == *str) + cp++, str++; + if (*cp) + stderror(ERR_BADSCALE, str); +} + + +/*ARGSUSED*/ +static void +plim(lp, hard) + register struct limits *lp; + Char hard; +{ + struct rlimit rlim; + RLIM_TYPE limit; + + (void) fprintf(cshout, "%s \t", lp->limname); + + (void) getrlimit(lp->limconst, &rlim); + limit = hard ? rlim.rlim_max : rlim.rlim_cur; + + if (limit == RLIM_INFINITY) + (void) fprintf(cshout, "unlimited"); + else if (lp->limconst == RLIMIT_CPU) + psecs((long) limit); + else + (void) fprintf(cshout, "%ld %s", (long) (limit / lp->limdiv), + lp->limscale); + (void) fputc('\n', cshout); +} + +void +/*ARGSUSED*/ +dounlimit(v, t) + Char **v; + struct command *t; +{ + register struct limits *lp; + int lerr = 0; + Char hard = 0; + + v++; + if (*v && eq(*v, STRmh)) { + hard = 1; + v++; + } + if (*v == 0) { + for (lp = limits; lp->limconst >= 0; lp++) + if (setlim(lp, hard, (RLIM_TYPE) RLIM_INFINITY) < 0) + lerr++; + if (lerr) + stderror(ERR_SILENT); + return; + } + while (*v) { + lp = findlim(*v++); + if (setlim(lp, hard, (RLIM_TYPE) RLIM_INFINITY) < 0) + stderror(ERR_SILENT); + } +} + +static int +setlim(lp, hard, limit) + register struct limits *lp; + Char hard; + RLIM_TYPE limit; +{ + struct rlimit rlim; + + (void) getrlimit(lp->limconst, &rlim); + + if (hard) + rlim.rlim_max = limit; + else if (limit == RLIM_INFINITY && geteuid() != 0) + rlim.rlim_cur = rlim.rlim_max; + else + rlim.rlim_cur = limit; + + if (setrlimit(lp->limconst, &rlim) < 0) { + (void) fprintf(csherr, "%s: %s: Can't %s%s limit\n", bname, lp->limname, + limit == RLIM_INFINITY ? "remove" : "set", + hard ? " hard" : ""); + return (-1); + } + return (0); +} + +void +/*ARGSUSED*/ +dosuspend(v, t) + Char **v; + struct command *t; +{ + int ctpgrp; + + void (*old) (); + + if (loginsh) + stderror(ERR_SUSPLOG); + untty(); + + old = signal(SIGTSTP, SIG_DFL); + (void) kill(0, SIGTSTP); + /* the shell stops here */ + (void) signal(SIGTSTP, old); + + if (tpgrp != -1) { + ctpgrp = tcgetpgrp(FSHTTY); + while (ctpgrp != opgrp) { + old = signal(SIGTTIN, SIG_DFL); + (void) kill(0, SIGTTIN); + (void) signal(SIGTTIN, old); + } + (void) setpgid(0, shpgrp); + (void) tcsetpgrp(FSHTTY, shpgrp); + } +} + +/* This is the dreaded EVAL built-in. + * If you don't fiddle with file descriptors, and reset didfds, + * this command will either ignore redirection inside or outside + * its aguments, e.g. eval "date >x" vs. eval "date" >x + * The stuff here seems to work, but I did it by trial and error rather + * than really knowing what was going on. If tpgrp is zero, we are + * probably a background eval, e.g. "eval date &", and we want to + * make sure that any processes we start stay in our pgrp. + * This is also the case for "time eval date" -- stay in same pgrp. + * Otherwise, under stty tostop, processes will stop in the wrong + * pgrp, with no way for the shell to get them going again. -IAN! + */ +static Char **gv = NULL; +void +/*ARGSUSED*/ +doeval(v, t) + Char **v; + struct command *t; +{ + Char **oevalvec; + Char *oevalp; + int odidfds; + jmp_buf osetexit; + int my_reenter; + Char **savegv = gv; + int saveIN; + int saveOUT; + int saveERR; + int oSHIN; + int oSHOUT; + int oSHERR; + + UNREGISTER(v); + + oevalvec = evalvec; + oevalp = evalp; + odidfds = didfds; + oSHIN = SHIN; + oSHOUT = SHOUT; + oSHERR = SHERR; + + v++; + if (*v == 0) + return; + gflag = 0, tglob(v); + if (gflag) { + gv = v = globall(v); + gargv = 0; + if (v == 0) + stderror(ERR_NOMATCH); + v = copyblk(v); + } + else { + gv = NULL; + v = copyblk(v); + trim(v); + } + + saveIN = dcopy(SHIN, -1); + saveOUT = dcopy(SHOUT, -1); + saveERR = dcopy(SHERR, -1); + + getexit(osetexit); + + if ((my_reenter = setexit()) == 0) { + evalvec = v; + evalp = 0; + SHIN = dcopy(0, -1); + SHOUT = dcopy(1, -1); + SHERR = dcopy(2, -1); + didfds = 0; + process(0); + } + + evalvec = oevalvec; + evalp = oevalp; + doneinp = 0; + didfds = odidfds; + (void) close(SHIN); + (void) close(SHOUT); + (void) close(SHERR); + SHIN = dmove(saveIN, oSHIN); + SHOUT = dmove(saveOUT, oSHOUT); + SHERR = dmove(saveERR, oSHERR); + if (gv) + blkfree(gv), gv = NULL; + resexit(osetexit); + gv = savegv; + if (my_reenter) + stderror(ERR_SILENT); +} + +void +/*ARGSUSED*/ +doprintf(v, t) + Char **v; + struct command *t; +{ + char **c; + extern int progprintf __P((int, char **)); + int ret; + + ret = progprintf(blklen(v), c = short2blk(v)); + (void) fflush(cshout); + (void) fflush(csherr); + + blkfree((Char **) c); + if (ret) + stderror(ERR_SILENT); +} diff --git a/bin/csh/glob.c b/bin/csh/glob.c new file mode 100644 index 000000000000..bdbebad0c3bb --- /dev/null +++ b/bin/csh/glob.c @@ -0,0 +1,939 @@ +/*- + * Copyright (c) 1980, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * 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[] = "@(#)glob.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <glob.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#if __STDC__ +# include <stdarg.h> +#else +# include <varargs.h> +#endif + +#include "csh.h" +#include "extern.h" + +static int noglob; +static int pargsiz, gargsiz; + +/* + * Values for gflag + */ +#define G_NONE 0 /* No globbing needed */ +#define G_GLOB 1 /* string contains *?[] characters */ +#define G_CSH 2 /* string contains ~`{ characters */ + +#define GLOBSPACE 100 /* Alloc increment */ + +#define LBRC '{' +#define RBRC '}' +#define LBRK '[' +#define RBRK ']' +#define EOS '\0' + +Char **gargv = NULL; +long gargc = 0; +Char **pargv = NULL; +long pargc = 0; + +/* + * globbing is now done in two stages. In the first pass we expand + * csh globbing idioms ~`{ and then we proceed doing the normal + * globbing if needed ?*[ + * + * Csh type globbing is handled in globexpand() and the rest is + * handled in glob() which is part of the 4.4BSD libc. + * + */ +static Char *globtilde __P((Char **, Char *)); +static Char **libglob __P((Char **)); +static Char **globexpand __P((Char **)); +static int globbrace __P((Char *, Char *, Char ***)); +static void expbrace __P((Char ***, Char ***, int)); +static int pmatch __P((Char *, Char *)); +static void pword __P((void)); +static void psave __P((int)); +static void backeval __P((Char *, bool)); + + +static Char * +globtilde(nv, s) + Char **nv, *s; +{ + Char gbuf[MAXPATHLEN], *gstart, *b, *u, *e; + + gstart = gbuf; + *gstart++ = *s++; + u = s; + for (b = gstart, e = &gbuf[MAXPATHLEN - 1]; + *s && *s != '/' && *s != ':' && b < e; + *b++ = *s++) + continue; + *b = EOS; + if (gethdir(gstart)) { + blkfree(nv); + if (*gstart) + stderror(ERR_UNKUSER, vis_str(gstart)); + else + stderror(ERR_NOHOME); + } + b = &gstart[Strlen(gstart)]; + while (*s) + *b++ = *s++; + *b = EOS; + --u; + xfree((ptr_t) u); + return (Strsave(gstart)); +} + +static int +globbrace(s, p, bl) + Char *s, *p, ***bl; +{ + int i, len; + Char *pm, *pe, *lm, *pl; + Char **nv, **vl; + Char gbuf[MAXPATHLEN]; + int size = GLOBSPACE; + + nv = vl = (Char **) xmalloc((size_t) sizeof(Char *) * size); + *vl = NULL; + + len = 0; + /* copy part up to the brace */ + for (lm = gbuf, p = s; *p != LBRC; *lm++ = *p++) + continue; + + /* check for balanced braces */ + for (i = 0, pe = ++p; *pe; pe++) + if (*pe == LBRK) { + /* Ignore everything between [] */ + for (++pe; *pe != RBRK && *pe != EOS; pe++) + continue; + if (*pe == EOS) { + blkfree(nv); + return (-RBRK); + } + } + else if (*pe == LBRC) + i++; + else if (*pe == RBRC) { + if (i == 0) + break; + i--; + } + + if (i != 0 || *pe == '\0') { + blkfree(nv); + return (-RBRC); + } + + for (i = 0, pl = pm = p; pm <= pe; pm++) + switch (*pm) { + case LBRK: + for (++pm; *pm != RBRK && *pm != EOS; pm++) + continue; + if (*pm == EOS) { + *vl = NULL; + blkfree(nv); + return (-RBRK); + } + break; + case LBRC: + i++; + break; + case RBRC: + if (i) { + i--; + break; + } + /* FALLTHROUGH */ + case ',': + if (i && *pm == ',') + break; + else { + Char savec = *pm; + + *pm = EOS; + (void) Strcpy(lm, pl); + (void) Strcat(gbuf, pe + 1); + *pm = savec; + *vl++ = Strsave(gbuf); + len++; + pl = pm + 1; + if (vl == &nv[size]) { + size += GLOBSPACE; + nv = (Char **) xrealloc((ptr_t) nv, (size_t) + size * sizeof(Char *)); + vl = &nv[size - GLOBSPACE]; + } + } + break; + default: + break; + } + *vl = NULL; + *bl = nv; + return (len); +} + + +static void +expbrace(nvp, elp, size) + Char ***nvp, ***elp; + int size; +{ + Char **vl, **el, **nv, *s; + + vl = nv = *nvp; + if (elp != NULL) + el = *elp; + else + for (el = vl; *el; el++) + continue; + + for (s = *vl; s; s = *++vl) { + Char *b; + Char **vp, **bp; + + /* leave {} untouched for find */ + if (s[0] == '{' && (s[1] == '\0' || (s[1] == '}' && s[2] == '\0'))) + continue; + if ((b = Strchr(s, '{')) != NULL) { + Char **bl; + int len; + + if ((len = globbrace(s, b, &bl)) < 0) { + xfree((ptr_t) nv); + stderror(ERR_MISSING, -len); + } + xfree((ptr_t) s); + if (len == 1) { + *vl-- = *bl; + xfree((ptr_t) bl); + continue; + } + len = blklen(bl); + if (&el[len] >= &nv[size]) { + int l, e; + + l = &el[len] - &nv[size]; + size += GLOBSPACE > l ? GLOBSPACE : l; + l = vl - nv; + e = el - nv; + nv = (Char **) xrealloc((ptr_t) nv, (size_t) + size * sizeof(Char *)); + vl = nv + l; + el = nv + e; + } + vp = vl--; + *vp = *bl; + len--; + for (bp = el; bp != vp; bp--) + bp[len] = *bp; + el += len; + vp++; + for (bp = bl + 1; *bp; *vp++ = *bp++) + continue; + xfree((ptr_t) bl); + } + + } + if (elp != NULL) + *elp = el; + *nvp = nv; +} + +static Char ** +globexpand(v) + Char **v; +{ + Char *s; + Char **nv, **vl, **el; + int size = GLOBSPACE; + + + nv = vl = (Char **) xmalloc((size_t) sizeof(Char *) * size); + *vl = NULL; + + /* + * Step 1: expand backquotes. + */ + while ((s = *v++) != NULL) { + if (Strchr(s, '`')) { + int i; + + (void) dobackp(s, 0); + for (i = 0; i < pargc; i++) { + *vl++ = pargv[i]; + if (vl == &nv[size]) { + size += GLOBSPACE; + nv = (Char **) xrealloc((ptr_t) nv, + (size_t) size * sizeof(Char *)); + vl = &nv[size - GLOBSPACE]; + } + } + xfree((ptr_t) pargv); + pargv = NULL; + } + else { + *vl++ = Strsave(s); + if (vl == &nv[size]) { + size += GLOBSPACE; + nv = (Char **) xrealloc((ptr_t) nv, (size_t) + size * sizeof(Char *)); + vl = &nv[size - GLOBSPACE]; + } + } + } + *vl = NULL; + + if (noglob) + return (nv); + + /* + * Step 2: expand braces + */ + el = vl; + expbrace(&nv, &el, size); + + /* + * Step 3: expand ~ + */ + vl = nv; + for (s = *vl; s; s = *++vl) + if (*s == '~') + *vl = globtilde(nv, s); + vl = nv; + return (vl); +} + +static Char * +handleone(str, vl, action) + Char *str, **vl; + int action; +{ + + Char *cp, **vlp = vl; + + switch (ac |