diff options
author | rgrimes <rgrimes@FreeBSD.org> | 1994-05-26 06:18:55 +0000 |
---|---|---|
committer | rgrimes <rgrimes@FreeBSD.org> | 1994-05-26 06:18:55 +0000 |
commit | f185bf7f09d364269a9c6dcee37a105241cf240f (patch) | |
tree | a0ed2782ab2ff5ad5db9841d4dc4cd101011351e | |
parent | c5df1ac6c773cd8f0c6fafa23187dfcfe122bee2 (diff) | |
parent | e3cfc8ce61f788739c66445d903f8beacb40c93d (diff) | |
download | FreeBSD-src-f185bf7f09d364269a9c6dcee37a105241cf240f.zip FreeBSD-src-f185bf7f09d364269a9c6dcee37a105241cf240f.tar.gz |
This commit was generated by cvs2svn to compensate for changes in r1556,
which included commits to RCS files with non-trunk default branches.
245 files changed, 75315 insertions, 0 deletions
diff --git a/bin/Makefile b/bin/Makefile new file mode 100644 index 0000000..3ae43c4 --- /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 0000000..b569b70 --- /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 0000000..c2d7cb5 --- /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 0000000..102b22e --- /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 0000000..30e5bfc --- /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 0000000..f28bcfe --- /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 0000000..ce92124 --- /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 0000000..f6251a3 --- /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 0000000..ad07b68 --- /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 0000000..f04d37b --- /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 0000000..b58bb99 --- /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 0000000..6c7c6e2 --- /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 0000000..5a5edcb --- /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 0000000..e29f549 --- /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 0000000..e73be63 --- /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 0000000..9b8e394 --- /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 0000000..610105c --- /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 0000000..cf8af73 --- /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 0000000..ee862df --- /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 0000000..1026faf --- /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 0000000..0641769 --- /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 0000000..9d274a1 --- /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 0000000..6fc8cf5 --- /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 0000000..7da611f --- /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 0000000..a791de4 --- /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 0000000..25a02e0 --- /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 0000000..58a9ddb --- /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 0000000..8f70d32 --- /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 0000000..367b51e --- /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 0000000..04efd3f --- /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 0000000..dcd155b --- /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 0000000..fcd4c65 --- /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 0000000..745adc8 --- /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 0000000..c9799cb --- /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 0000000..59b6964 --- /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 0000000..db37b8a --- /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 0000000..523844d --- /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 0000000..82664c6 --- /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 0000000..bdbebad --- /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 (action) { + case G_ERROR: + setname(vis_str(str)); + blkfree(vl); + stderror(ERR_NAME | ERR_AMBIG); + break; + case G_APPEND: + trim(vlp); + str = Strsave(*vlp++); + do { + cp = Strspl(str, STRspace); + xfree((ptr_t) str); + str = Strspl(cp, *vlp); + xfree((ptr_t) cp); + } + while (*++vlp); + blkfree(vl); + break; + case G_IGNORE: + str = Strsave(strip(*vlp)); + blkfree(vl); + break; + default: + break; + } + return (str); +} + +static Char ** +libglob(vl) + Char **vl; +{ + int gflgs = GLOB_QUOTE | GLOB_NOMAGIC; + glob_t globv; + char *ptr; + int nonomatch = adrof(STRnonomatch) != 0, magic = 0, match = 0; + + if (!vl || !vl[0]) + return (vl); + + globv.gl_offs = 0; + globv.gl_pathv = 0; + globv.gl_pathc = 0; + + if (nonomatch) + gflgs |= GLOB_NOCHECK; + + do { + ptr = short2qstr(*vl); + switch (glob(ptr, gflgs, 0, &globv)) { + case GLOB_ABEND: + setname(vis_str(*vl)); + stderror(ERR_NAME | ERR_GLOB); + /* NOTREACHED */ + case GLOB_NOSPACE: + stderror(ERR_NOMEM); + /* NOTREACHED */ + default: + break; + } + if (globv.gl_flags & GLOB_MAGCHAR) { + match |= (globv.gl_matchc != 0); + magic = 1; + } + gflgs |= GLOB_APPEND; + } + while (*++vl); + vl = (globv.gl_pathc == 0 || (magic && !match && !nonomatch)) ? + NULL : blk2short(globv.gl_pathv); + globfree(&globv); + return (vl); +} + +Char * +globone(str, action) + Char *str; + int action; +{ + Char *v[2], **vl, **vo; + int gflg; + + noglob = adrof(STRnoglob) != 0; + gflag = 0; + v[0] = str; + v[1] = 0; + tglob(v); + gflg = gflag; + if (gflg == G_NONE) + return (strip(Strsave(str))); + + if (gflg & G_CSH) { + /* + * Expand back-quote, tilde and brace + */ + vo = globexpand(v); + if (noglob || (gflg & G_GLOB) == 0) { + if (vo[0] == NULL) { + xfree((ptr_t) vo); + return (Strsave(STRNULL)); + } + if (vo[1] != NULL) + return (handleone(str, vo, action)); + else { + str = strip(vo[0]); + xfree((ptr_t) vo); + return (str); + } + } + } + else if (noglob || (gflg & G_GLOB) == 0) + return (strip(Strsave(str))); + else + vo = v; + + vl = libglob(vo); + if ((gflg & G_CSH) && vl != vo) + blkfree(vo); + if (vl == NULL) { + setname(vis_str(str)); + stderror(ERR_NAME | ERR_NOMATCH); + } + if (vl[0] == NULL) { + xfree((ptr_t) vl); + return (Strsave(STRNULL)); + } + if (vl[1] != NULL) + return (handleone(str, vl, action)); + else { + str = strip(*vl); + xfree((ptr_t) vl); + return (str); + } +} + +Char ** +globall(v) + Char **v; +{ + Char **vl, **vo; + int gflg = gflag; + + if (!v || !v[0]) { + gargv = saveblk(v); + gargc = blklen(gargv); + return (gargv); + } + + noglob = adrof(STRnoglob) != 0; + + if (gflg & G_CSH) + /* + * Expand back-quote, tilde and brace + */ + vl = vo = globexpand(v); + else + vl = vo = saveblk(v); + + if (!noglob && (gflg & G_GLOB)) { + vl = libglob(vo); + if ((gflg & G_CSH) && vl != vo) + blkfree(vo); + } + else + trim(vl); + + gargc = vl ? blklen(vl) : 0; + return (gargv = vl); +} + +void +ginit() +{ + gargsiz = GLOBSPACE; + gargv = (Char **) xmalloc((size_t) sizeof(Char *) * gargsiz); + gargv[0] = 0; + gargc = 0; +} + +void +rscan(t, f) + register Char **t; + void (*f) (); +{ + register Char *p; + + while ((p = *t++) != NULL) + while (*p) + (*f) (*p++); +} + +void +trim(t) + register Char **t; +{ + register Char *p; + + while ((p = *t++) != NULL) + while (*p) + *p++ &= TRIM; +} + +void +tglob(t) + register Char **t; +{ + register Char *p, c; + + while ((p = *t++) != NULL) { + if (*p == '~' || *p == '=') + gflag |= G_CSH; + else if (*p == '{' && + (p[1] == '\0' || (p[1] == '}' && p[2] == '\0'))) + continue; + while ((c = *p++) != '\0') { + /* + * eat everything inside the matching backquotes + */ + if (c == '`') { + gflag |= G_CSH; + while (*p && *p != '`') + if (*p++ == '\\') { + if (*p) /* Quoted chars */ + p++; + else + break; + } + if (*p) /* The matching ` */ + p++; + else + break; + } + else if (c == '{') + gflag |= G_CSH; + else if (isglob(c)) + gflag |= G_GLOB; + } + } +} + +/* + * Command substitute cp. If literal, then this is a substitution from a + * << redirection, and so we should not crunch blanks and tabs, separating + * words only at newlines. + */ +Char ** +dobackp(cp, literal) + Char *cp; + bool literal; +{ + register Char *lp, *rp; + Char *ep, word[MAXPATHLEN]; + + if (pargv) { +#ifdef notdef + abort(); +#endif + blkfree(pargv); + } + pargsiz = GLOBSPACE; + pargv = (Char **) xmalloc((size_t) sizeof(Char *) * pargsiz); + pargv[0] = NULL; + pargcp = pargs = word; + pargc = 0; + pnleft = MAXPATHLEN - 4; + for (;;) { + for (lp = cp; *lp != '`'; lp++) { + if (*lp == 0) { + if (pargcp != pargs) + pword(); + return (pargv); + } + psave(*lp); + } + lp++; + for (rp = lp; *rp && *rp != '`'; rp++) + if (*rp == '\\') { + rp++; + if (!*rp) + goto oops; + } + if (!*rp) + oops: stderror(ERR_UNMATCHED, '`'); + ep = Strsave(lp); + ep[rp - lp] = 0; + backeval(ep, literal); + cp = rp + 1; + } +} + +static void +backeval(cp, literal) + Char *cp; + bool literal; +{ + register int icnt, c; + register Char *ip; + struct command faket; + bool hadnl; + int pvec[2], quoted; + Char *fakecom[2], ibuf[BUFSIZ]; + char tibuf[BUFSIZ]; + + hadnl = 0; + icnt = 0; + quoted = (literal || (cp[0] & QUOTE)) ? QUOTE : 0; + faket.t_dtyp = NODE_COMMAND; + faket.t_dflg = 0; + faket.t_dlef = 0; + faket.t_drit = 0; + faket.t_dspr = 0; + faket.t_dcom = fakecom; + fakecom[0] = STRfakecom1; + fakecom[1] = 0; + + /* + * We do the psave job to temporarily change the current job so that the + * following fork is considered a separate job. This is so that when + * backquotes are used in a builtin function that calls glob the "current + * job" is not corrupted. We only need one level of pushed jobs as long as + * we are sure to fork here. + */ + psavejob(); + + /* + * It would be nicer if we could integrate this redirection more with the + * routines in sh.sem.c by doing a fake execute on a builtin function that + * was piped out. + */ + mypipe(pvec); + if (pfork(&faket, -1) == 0) { + struct wordent paraml; + struct command *t; + + (void) close(pvec[0]); + (void) dmove(pvec[1], 1); + (void) dmove(SHERR, 2); + initdesc(); + /* + * Bugfix for nested backquotes by Michael Greim <greim@sbsvax.UUCP>, + * posted to comp.bugs.4bsd 12 Sep. 1989. + */ + if (pargv) /* mg, 21.dec.88 */ + blkfree(pargv), pargv = 0, pargsiz = 0; + /* mg, 21.dec.88 */ + arginp = cp; + while (*cp) + *cp++ &= TRIM; + + /* + * In the child ``forget'' everything about current aliases or + * eval vectors. + */ + alvec = NULL; + evalvec = NULL; + alvecp = NULL; + evalp = NULL; + (void) lex(¶ml); + if (seterr) + stderror(ERR_OLD); + alias(¶ml); + t = syntax(paraml.next, ¶ml, 0); + if (seterr) + stderror(ERR_OLD); + if (t) + t->t_dflg |= F_NOFORK; + (void) signal(SIGTSTP, SIG_IGN); + (void) signal(SIGTTIN, SIG_IGN); + (void) signal(SIGTTOU, SIG_IGN); + execute(t, -1, NULL, NULL); + exitstat(); + } + xfree((ptr_t) cp); + (void) close(pvec[1]); + c = 0; + ip = NULL; + do { + int cnt = 0; + + for (;;) { + if (icnt == 0) { + int i; + + ip = ibuf; + do + icnt = read(pvec[0], tibuf, BUFSIZ); + while (icnt == -1 && errno == EINTR); + if (icnt <= 0) { + c = -1; + break; + } + for (i = 0; i < icnt; i++) + ip[i] = (unsigned char) tibuf[i]; + } + if (hadnl) + break; + --icnt; + c = (*ip++ & TRIM); + if (c == 0) + break; + if (c == '\n') { + /* + * Continue around the loop one more time, so that we can eat + * the last newline without terminating this word. + */ + hadnl = 1; + continue; + } + if (!quoted && (c == ' ' || c == '\t')) + break; + cnt++; + psave(c | quoted); + } + /* + * Unless at end-of-file, we will form a new word here if there were + * characters in the word, or in any case when we take text literally. + * If we didn't make empty words here when literal was set then we + * would lose blank lines. + */ + if (c != -1 && (cnt || literal)) + pword(); + hadnl = 0; + } while (c >= 0); + (void) close(pvec[0]); + pwait(); + prestjob(); +} + +static void +psave(c) + int c; +{ + if (--pnleft <= 0) + stderror(ERR_WTOOLONG); + *pargcp++ = c; +} + +static void +pword() +{ + psave(0); + if (pargc == pargsiz - 1) { + pargsiz += GLOBSPACE; + pargv = (Char **) xrealloc((ptr_t) pargv, + (size_t) pargsiz * sizeof(Char *)); + } + pargv[pargc++] = Strsave(pargs); + pargv[pargc] = NULL; + pargcp = pargs; + pnleft = MAXPATHLEN - 4; +} + +int +Gmatch(string, pattern) + Char *string, *pattern; +{ + Char **blk, **p; + int gpol = 1, gres = 0; + + if (*pattern == '^') { + gpol = 0; + pattern++; + } + + blk = (Char **) xmalloc(GLOBSPACE * sizeof(Char *)); + blk[0] = Strsave(pattern); + blk[1] = NULL; + + expbrace(&blk, NULL, GLOBSPACE); + + for (p = blk; *p; p++) + gres |= pmatch(string, *p); + + blkfree(blk); + return(gres == gpol); +} + +static int +pmatch(string, pattern) + register Char *string, *pattern; +{ + register Char stringc, patternc; + int match, negate_range; + Char rangec; + + for (;; ++string) { + stringc = *string & TRIM; + patternc = *pattern++; + switch (patternc) { + case 0: + return (stringc == 0); + case '?': + if (stringc == 0) + return (0); + break; + case '*': + if (!*pattern) + return (1); + while (*string) + if (Gmatch(string++, pattern)) + return (1); + return (0); + case '[': + match = 0; + if ((negate_range = (*pattern == '^')) != 0) + pattern++; + while ((rangec = *pattern++) != '\0') { + if (rangec == ']') + break; + if (match) + continue; + if (rangec == '-' && *(pattern-2) != '[' && *pattern != ']') { + match = (stringc <= (*pattern & TRIM) && + (*(pattern-2) & TRIM) <= stringc); + pattern++; + } + else + match = (stringc == (rangec & TRIM)); + } + if (rangec == 0) + stderror(ERR_NAME | ERR_MISSING, ']'); + if (match == negate_range) + return (0); + break; + default: + if ((patternc & TRIM) != stringc) + return (0); + break; + + } + } +} + +void +Gcat(s1, s2) + Char *s1, *s2; +{ + register Char *p, *q; + int n; + + for (p = s1; *p++;) + continue; + for (q = s2; *q++;) + continue; + n = (p - s1) + (q - s2) - 1; + if (++gargc >= gargsiz) { + gargsiz += GLOBSPACE; + gargv = (Char **) xrealloc((ptr_t) gargv, + (size_t) gargsiz * sizeof(Char *)); + } + gargv[gargc] = 0; + p = gargv[gargc - 1] = (Char *) xmalloc((size_t) n * sizeof(Char)); + for (q = s1; (*p++ = *q++) != '\0';) + continue; + for (p--, q = s2; (*p++ = *q++) != '\0';) + continue; +} + +#ifdef FILEC +int +sortscmp(a, b) + register const ptr_t a, b; +{ +#if defined(NLS) && !defined(NOSTRCOLL) + char buf[2048]; +#endif + + if (!a) /* check for NULL */ + return (b ? 1 : 0); + if (!b) + return (-1); + + if (!*(Char **)a) /* check for NULL */ + return (*(Char **)b ? 1 : 0); + if (!*(Char **)b) + return (-1); + +#if defined(NLS) && !defined(NOSTRCOLL) + (void) strcpy(buf, short2str(*(Char **)a)); + return ((int) strcoll(buf, short2str(*(Char **)b))); +#else + return ((int) Strcmp(*(Char **)a, *(Char **)b)); +#endif +} +#endif /* FILEC */ diff --git a/bin/csh/hist.c b/bin/csh/hist.c new file mode 100644 index 0000000..d55a416 --- /dev/null +++ b/bin/csh/hist.c @@ -0,0 +1,184 @@ +/*- + * 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[] = "@(#)hist.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <stdlib.h> +#if __STDC__ +# include <stdarg.h> +#else +# include <varargs.h> +#endif + +#include "csh.h" +#include "extern.h" + +static void hfree __P((struct Hist *)); +static void dohist1 __P((struct Hist *, int *, int, int)); +static void phist __P((struct Hist *, int)); + +void +savehist(sp) + struct wordent *sp; +{ + register struct Hist *hp, *np; + register int histlen = 0; + Char *cp; + + /* throw away null lines */ + if (sp->next->word[0] == '\n') + return; + cp = value(STRhistory); + if (*cp) { + register Char *p = cp; + + while (*p) { + if (!Isdigit(*p)) { + histlen = 0; + break; + } + histlen = histlen * 10 + *p++ - '0'; + } + } + for (hp = &Histlist; (np = hp->Hnext) != NULL;) + if (eventno - np->Href >= histlen || histlen == 0) + hp->Hnext = np->Hnext, hfree(np); + else + hp = np; + (void) enthist(++eventno, sp, 1); +} + +struct Hist * +enthist(event, lp, docopy) + int event; + register struct wordent *lp; + bool docopy; +{ + register struct Hist *np; + + np = (struct Hist *) xmalloc((size_t) sizeof(*np)); + np->Hnum = np->Href = event; + if (docopy) { + copylex(&np->Hlex, lp); + } + else { + np->Hlex.next = lp->next; + lp->next->prev = &np->Hlex; + np->Hlex.prev = lp->prev; + lp->prev->next = &np->Hlex; + } + np->Hnext = Histlist.Hnext; + Histlist.Hnext = np; + return (np); +} + +static void +hfree(hp) + register struct Hist *hp; +{ + + freelex(&hp->Hlex); + xfree((ptr_t) hp); +} + +void +/*ARGSUSED*/ +dohist(v, t) + Char **v; + struct command *t; +{ + int n, rflg = 0, hflg = 0; + + if (getn(value(STRhistory)) == 0) + return; + if (setintr) + (void) sigsetmask(sigblock((sigset_t) 0) & ~sigmask(SIGINT)); + while (*++v && **v == '-') { + Char *vp = *v; + + while (*++vp) + switch (*vp) { + case 'h': + hflg++; + break; + case 'r': + rflg++; + break; + case '-': /* ignore multiple '-'s */ + break; + default: + stderror(ERR_HISTUS); + break; + } + } + if (*v) + n = getn(*v); + else { + n = getn(value(STRhistory)); + } + dohist1(Histlist.Hnext, &n, rflg, hflg); +} + +static void +dohist1(hp, np, rflg, hflg) + struct Hist *hp; + int *np, rflg, hflg; +{ + bool print = (*np) > 0; + + for (; hp != 0; hp = hp->Hnext) { + (*np)--; + hp->Href++; + if (rflg == 0) { + dohist1(hp->Hnext, np, rflg, hflg); + if (print) + phist(hp, hflg); + return; + } + if (*np >= 0) + phist(hp, hflg); + } +} + +static void +phist(hp, hflg) + register struct Hist *hp; + int hflg; +{ + if (hflg == 0) + (void) fprintf(cshout, "%6d\t", hp->Hnum); + prlex(cshout, &hp->Hlex); +} diff --git a/bin/csh/init.c b/bin/csh/init.c new file mode 100644 index 0000000..906eea3 --- /dev/null +++ b/bin/csh/init.c @@ -0,0 +1,135 @@ +/*- + * 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 +/*###9 [cc] warning: `sccsid' defined but not used%%%*/ +static char sccsid[] = "@(#)init.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#if __STDC__ +# include <stdarg.h> +#else +# include <varargs.h> +#endif + +#include "csh.h" +#include "extern.h" + +#define INF 1000 + +struct biltins bfunc[] = +{ + { "@", dolet, 0, INF }, + { "alias", doalias, 0, INF }, + { "alloc", showall, 0, 1 }, + { "bg", dobg, 0, INF }, + { "break", dobreak, 0, 0 }, + { "breaksw", doswbrk, 0, 0 }, + { "case", dozip, 0, 1 }, + { "cd", dochngd, 0, INF }, + { "chdir", dochngd, 0, INF }, + { "continue", docontin, 0, 0 }, + { "default", dozip, 0, 0 }, + { "dirs", dodirs, 0, INF }, + { "echo", doecho, 0, INF }, + { "else", doelse, 0, INF }, + { "end", doend, 0, 0 }, + { "endif", dozip, 0, 0 }, + { "endsw", dozip, 0, 0 }, + { "eval", doeval, 0, INF }, + { "exec", execash, 1, INF }, + { "exit", doexit, 0, INF }, + { "fg", dofg, 0, INF }, + { "foreach", doforeach, 3, INF }, + { "glob", doglob, 0, INF }, + { "goto", dogoto, 1, 1 }, + { "hashstat", hashstat, 0, 0 }, + { "history", dohist, 0, 2 }, + { "if", doif, 1, INF }, + { "jobs", dojobs, 0, 1 }, + { "kill", dokill, 1, INF }, + { "limit", dolimit, 0, 3 }, + { "linedit", doecho, 0, INF }, + { "login", dologin, 0, 1 }, + { "logout", dologout, 0, 0 }, + { "nice", donice, 0, INF }, + { "nohup", donohup, 0, INF }, + { "notify", donotify, 0, INF }, + { "onintr", doonintr, 0, 2 }, + { "popd", dopopd, 0, INF }, + { "printf", doprintf, 1, INF }, + { "pushd", dopushd, 0, INF }, + { "rehash", dohash, 0, 0 }, + { "repeat", dorepeat, 2, INF }, + { "set", doset, 0, INF }, + { "setenv", dosetenv, 0, 2 }, + { "shift", shift, 0, 1 }, + { "source", dosource, 1, 2 }, + { "stop", dostop, 1, INF }, + { "suspend", dosuspend, 0, 0 }, + { "switch", doswitch, 1, INF }, + { "time", dotime, 0, INF }, + { "umask", doumask, 0, 1 }, + { "unalias", unalias, 1, INF }, + { "unhash", dounhash, 0, 0 }, + { "unlimit", dounlimit, 0, INF }, + { "unset", unset, 1, INF }, + { "unsetenv", dounsetenv, 1, INF }, + { "wait", dowait, 0, 0 }, + { "which", dowhich, 1, INF }, + { "while", dowhile, 1, INF } +}; +int nbfunc = sizeof bfunc / sizeof *bfunc; + +struct srch srchn[] = +{ + { "@", T_LET }, + { "break", T_BREAK }, + { "breaksw", T_BRKSW }, + { "case", T_CASE }, + { "default", T_DEFAULT }, + { "else", T_ELSE }, + { "end", T_END }, + { "endif", T_ENDIF }, + { "endsw", T_ENDSW }, + { "exit", T_EXIT }, + { "foreach", T_FOREACH }, + { "goto", T_GOTO }, + { "if", T_IF }, + { "label", T_LABEL }, + { "set", T_SET }, + { "switch", T_SWITCH }, + { "while", T_WHILE } +}; +int nsrchn = sizeof srchn / sizeof *srchn; + diff --git a/bin/csh/lex.c b/bin/csh/lex.c new file mode 100644 index 0000000..21f50d3 --- /dev/null +++ b/bin/csh/lex.c @@ -0,0 +1,1639 @@ +/*- + * 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[] = "@(#)lex.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <termios.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 lexical routines read input and form lists of words. + * There is some involved processing here, because of the complications + * of input buffering, and especially because of history substitution. + */ + +static Char *word __P((void)); +static int getC1 __P((int)); +static void getdol __P((void)); +static void getexcl __P((int)); +static struct Hist + *findev __P((Char *, bool)); +static void setexclp __P((Char *)); +static int bgetc __P((void)); +static void bfree __P((void)); +static struct wordent + *gethent __P((int)); +static int matchs __P((Char *, Char *)); +static int getsel __P((int *, int *, int)); +static struct wordent + *getsub __P((struct wordent *)); +static Char *subword __P((Char *, int, bool *)); +static struct wordent + *dosub __P((int, struct wordent *, bool)); + +/* + * Peekc is a peek character for getC, peekread for readc. + * There is a subtlety here in many places... history routines + * will read ahead and then insert stuff into the input stream. + * If they push back a character then they must push it behind + * the text substituted by the history substitution. On the other + * hand in several places we need 2 peek characters. To make this + * all work, the history routines read with getC, and make use both + * of ungetC and unreadc. The key observation is that the state + * of getC at the call of a history reference is such that calls + * to getC from the history routines will always yield calls of + * readc, unless this peeking is involved. That is to say that during + * getexcl the variables lap, exclp, and exclnxt are all zero. + * + * Getdol invokes history substitution, hence the extra peek, peekd, + * which it can ungetD to be before history substitutions. + */ +static Char peekc = 0, peekd = 0; +static Char peekread = 0; + +/* (Tail of) current word from ! subst */ +static Char *exclp = NULL; + +/* The rest of the ! subst words */ +static struct wordent *exclnxt = NULL; + +/* Count of remaining words in ! subst */ +static int exclc = 0; + +/* "Globp" for alias resubstitution */ +Char *alvecp = NULL; +int aret = F_SEEK; + +/* + * 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. + */ +static Char labuf[BUFSIZ]; + +/* + * Lex returns to its caller not only a wordlist (as a "var" parameter) + * but also whether a history substitution occurred. This is used in + * the main (process) routine to determine whether to echo, and also + * when called by the alias routine to determine whether to keep the + * argument list. + */ +static bool hadhist = 0; + +/* + * Avoid alias expansion recursion via \!# + */ +int hleft; + +static Char getCtmp; + +#define getC(f) ((getCtmp = peekc) ? (peekc = 0, getCtmp) : getC1(f)) +#define ungetC(c) peekc = c +#define ungetD(c) peekd = c + +int +lex(hp) + register struct wordent *hp; +{ + register struct wordent *wdp; + int c; + + btell(&lineloc); + hp->next = hp->prev = hp; + hp->word = STRNULL; + hadhist = 0; + do + c = readc(0); + while (c == ' ' || c == '\t'); + if (c == HISTSUB && intty) + /* ^lef^rit from tty is short !:s^lef^rit */ + getexcl(c); + else + unreadc(c); + wdp = hp; + /* + * The following loop is written so that the links needed by freelex will + * be ready and rarin to go even if it is interrupted. + */ + do { + register struct wordent *new; + + new = (struct wordent *) xmalloc((size_t) sizeof(*wdp)); + new->word = 0; + new->prev = wdp; + new->next = hp; + wdp->next = new; + wdp = new; + wdp->word = word(); + } while (wdp->word[0] != '\n'); + hp->prev = wdp; + return (hadhist); +} + +void +prlex(fp, sp0) + FILE *fp; + struct wordent *sp0; +{ + register struct wordent *sp = sp0->next; + + for (;;) { + (void) fprintf(fp, "%s", vis_str(sp->word)); + sp = sp->next; + if (sp == sp0) + break; + if (sp->word[0] != '\n') + (void) fputc(' ', fp); + } +} + +void +copylex(hp, fp) + register struct wordent *hp; + register struct wordent *fp; +{ + register struct wordent *wdp; + + wdp = hp; + fp = fp->next; + do { + register struct wordent *new; + + new = (struct wordent *) xmalloc((size_t) sizeof(*wdp)); + new->prev = wdp; + new->next = hp; + wdp->next = new; + wdp = new; + wdp->word = Strsave(fp->word); + fp = fp->next; + } while (wdp->word[0] != '\n'); + hp->prev = wdp; +} + +void +freelex(vp) + register struct wordent *vp; +{ + register struct wordent *fp; + + while (vp->next != vp) { + fp = vp->next; + vp->next = fp->next; + xfree((ptr_t) fp->word); + xfree((ptr_t) fp); + } + vp->prev = vp; +} + +static Char * +word() +{ + register Char c, c1; + register Char *wp; + Char wbuf[BUFSIZ]; + register bool dolflg; + register int i; + + wp = wbuf; + i = BUFSIZ - 4; +loop: + while ((c = getC(DOALL)) == ' ' || c == '\t') + continue; + if (cmap(c, _META | _ESC)) + switch (c) { + case '&': + case '|': + case '<': + case '>': + *wp++ = c; + c1 = getC(DOALL); + if (c1 == c) + *wp++ = c1; + else + ungetC(c1); + goto ret; + + case '#': + if (intty) + break; + c = 0; + do { + c1 = c; + c = getC(0); + } while (c != '\n'); + if (c1 == '\\') + goto loop; + /* fall into ... */ + + case ';': + case '(': + case ')': + case '\n': + *wp++ = c; + goto ret; + + case '\\': + c = getC(0); + if (c == '\n') { + if (onelflg == 1) + onelflg = 2; + goto loop; + } + if (c != HIST) + *wp++ = '\\', --i; + c |= QUOTE; + } + c1 = 0; + dolflg = DOALL; + for (;;) { + if (c1) { + if (c == c1) { + c1 = 0; + dolflg = DOALL; + } + else if (c == '\\') { + c = getC(0); + if (c == HIST) + c |= QUOTE; + else { + if (c == '\n') + /* + * if (c1 == '`') c = ' '; else + */ + c |= QUOTE; + ungetC(c); + c = '\\'; + } + } + else if (c == '\n') { + seterror(ERR_UNMATCHED, c1); + ungetC(c); + break; + } + } + else if (cmap(c, _META | _QF | _QB | _ESC)) { + if (c == '\\') { + c = getC(0); + if (c == '\n') { + if (onelflg == 1) + onelflg = 2; + break; + } + if (c != HIST) + *wp++ = '\\', --i; + c |= QUOTE; + } + else if (cmap(c, _QF | _QB)) { /* '"` */ + c1 = c; + dolflg = c == '"' ? DOALL : DOEXCL; + } + else if (c != '#' || !intty) { + ungetC(c); + break; + } + } + if (--i > 0) { + *wp++ = c; + c = getC(dolflg); + } + else { + seterror(ERR_WTOOLONG); + wp = &wbuf[1]; + break; + } + } +ret: + *wp = 0; + return (Strsave(wbuf)); +} + +static int +getC1(flag) + register int flag; +{ + register Char c; + + while (1) { + if ((c = peekc) != '\0') { + peekc = 0; + return (c); + } + if (lap) { + if ((c = *lap++) == 0) + lap = 0; + else { + if (cmap(c, _META | _QF | _QB)) + c |= QUOTE; + return (c); + } + } + if ((c = peekd) != '\0') { + peekd = 0; + return (c); + } + if (exclp) { + if ((c = *exclp++) != '\0') + return (c); + if (exclnxt && --exclc >= 0) { + exclnxt = exclnxt->next; + setexclp(exclnxt->word); + return (' '); + } + exclp = 0; + exclnxt = 0; + } + if (exclnxt) { + exclnxt = exclnxt->next; + if (--exclc < 0) + exclnxt = 0; + else + setexclp(exclnxt->word); + continue; + } + c = readc(0); + if (c == '$' && (flag & DODOL)) { + getdol(); + continue; + } + if (c == HIST && (flag & DOEXCL)) { + getexcl(0); + continue; + } + break; + } + return (c); +} + +static void +getdol() +{ + register Char *np, *ep; + Char name[4 * MAXVARLEN + 1]; + register int c; + int sc; + bool special = 0, toolong; + + np = name, *np++ = '$'; + c = sc = getC(DOEXCL); + if (any("\t \n", c)) { + ungetD(c); + ungetC('$' | QUOTE); + return; + } + if (c == '{') + *np++ = c, c = getC(DOEXCL); + if (c == '#' || c == '?') + special++, *np++ = c, c = getC(DOEXCL); + *np++ = c; + switch (c) { + + case '<': + case '$': + case '!': + if (special) + seterror(ERR_SPDOLLT); + *np = 0; + addla(name); + return; + + case '\n': + ungetD(c); + np--; + seterror(ERR_NEWLINE); + *np = 0; + addla(name); + return; + + case '*': + if (special) + seterror(ERR_SPSTAR); + *np = 0; + addla(name); + return; + + default: + toolong = 0; + if (Isdigit(c)) { +#ifdef notdef + /* let $?0 pass for now */ + if (special) { + seterror(ERR_DIGIT); + *np = 0; + addla(name); + return; + } +#endif + /* we know that np < &name[4] */ + ep = &np[MAXVARLEN]; + while ((c = getC(DOEXCL)) != '\0'){ + if (!Isdigit(c)) + break; + if (np < ep) + *np++ = c; + else + toolong = 1; + } + } + else if (letter(c)) { + /* we know that np < &name[4] */ + ep = &np[MAXVARLEN]; + toolong = 0; + while ((c = getC(DOEXCL)) != '\0') { + /* Bugfix for ${v123x} from Chris Torek, DAS DEC-90. */ + if (!letter(c) && !Isdigit(c)) + break; + if (np < ep) + *np++ = c; + else + toolong = 1; + } + } + else { + *np = 0; + seterror(ERR_VARILL); + addla(name); + return; + } + if (toolong) { + seterror(ERR_VARTOOLONG); + *np = 0; + addla(name); + return; + } + break; + } + if (c == '[') { + *np++ = c; + /* + * Name up to here is a max of MAXVARLEN + 8. + */ + ep = &np[2 * MAXVARLEN + 8]; + do { + /* + * Michael Greim: Allow $ expansion to take place in selector + * expressions. (limits the number of characters returned) + */ + c = getC(DOEXCL | DODOL); + if (c == '\n') { + ungetD(c); + np--; + seterror(ERR_NLINDEX); + *np = 0; + addla(name); + return; + } + if (np < ep) + *np++ = c; + } while (c != ']'); + *np = '\0'; + if (np >= ep) { + seterror(ERR_SELOVFL); + addla(name); + return; + } + c = getC(DOEXCL); + } + /* + * Name up to here is a max of 2 * MAXVARLEN + 8. + */ + if (c == ':') { + /* + * if the :g modifier is followed by a newline, then error right away! + * -strike + */ + + int gmodflag = 0, amodflag = 0; + + do { + *np++ = c, c = getC(DOEXCL); + if (c == 'g' || c == 'a') { + if (c == 'g') + gmodflag++; + else + amodflag++; + *np++ = c; c = getC(DOEXCL); + } + if ((c == 'g' && !gmodflag) || (c == 'a' && !amodflag)) { + if (c == 'g') + gmodflag++; + else + amodflag++; + *np++ = c; c = getC(DOEXCL); + } + *np++ = c; + /* scan s// [eichin:19910926.0512EST] */ + if (c == 's') { + int delimcnt = 2; + int delim = getC(0); + *np++ = delim; + + if (!delim || letter(delim) + || Isdigit(delim) || any(" \t\n", delim)) { + seterror(ERR_BADSUBST); + break; + } + while ((c = getC(0)) != (-1)) { + *np++ = c; + if(c == delim) delimcnt--; + if(!delimcnt) break; + } + if(delimcnt) { + seterror(ERR_BADSUBST); + break; + } + c = 's'; + } + if (!any("htrqxes", c)) { + if ((amodflag || gmodflag) && c == '\n') + stderror(ERR_VARSYN); /* strike */ + seterror(ERR_VARMOD, c); + *np = 0; + addla(name); + return; + } + } + while ((c = getC(DOEXCL)) == ':'); + ungetD(c); + } + else + ungetD(c); + if (sc == '{') { + c = getC(DOEXCL); + if (c != '}') { + ungetD(c); + seterror(ERR_MISSING, '}'); + *np = 0; + addla(name); + return; + } + *np++ = c; + } + *np = 0; + addla(name); + return; +} + +void +addla(cp) + Char *cp; +{ + Char buf[BUFSIZ]; + + if (Strlen(cp) + (lap ? Strlen(lap) : 0) >= + (sizeof(labuf) - 4) / sizeof(Char)) { + seterror(ERR_EXPOVFL); + return; + } + if (lap) + (void) Strcpy(buf, lap); + (void) Strcpy(labuf, cp); + if (lap) + (void) Strcat(labuf, buf); + lap = labuf; +} + +static Char lhsb[32]; +static Char slhs[32]; +static Char rhsb[64]; +static int quesarg; + +static void +getexcl(sc) + int sc; +{ + register struct wordent *hp, *ip; + int left, right, dol; + register int c; + + if (sc == 0) { + sc = getC(0); + if (sc != '{') { + ungetC(sc); + sc = 0; + } + } + quesarg = -1; + lastev = eventno; + hp = gethent(sc); + if (hp == 0) + return; + hadhist = 1; + dol = 0; + if (hp == alhistp) + for (ip = hp->next->next; ip != alhistt; ip = ip->next) + dol++; + else + for (ip = hp->next->next; ip != hp->prev; ip = ip->next) + dol++; + left = 0, right = dol; + if (sc == HISTSUB) { + ungetC('s'), unreadc(HISTSUB), c = ':'; + goto subst; + } + c = getC(0); + if (!any(":^$*-%", c)) + goto subst; + left = right = -1; + if (c == ':') { + c = getC(0); + unreadc(c); + if (letter(c) || c == '&') { + c = ':'; + left = 0, right = dol; + goto subst; + } + } + else + ungetC(c); + if (!getsel(&left, &right, dol)) + return; + c = getC(0); + if (c == '*') + ungetC(c), c = '-'; + if (c == '-') { + if (!getsel(&left, &right, dol)) + return; + c = getC(0); + } +subst: + exclc = right - left + 1; + while (--left >= 0) + hp = hp->next; + if (sc == HISTSUB || c == ':') { + do { + hp = getsub(hp); + c = getC(0); + } while (c == ':'); + } + unreadc(c); + if (sc == '{') { + c = getC(0); + if (c != '}') + seterror(ERR_BADBANG); + } + exclnxt = hp; +} + +static struct wordent * +getsub(en) + struct wordent *en; +{ + register Char *cp; + int delim; + register int c; + int sc; + bool global; + Char orhsb[sizeof(rhsb) / sizeof(Char)]; + + do { + exclnxt = 0; + global = 0; + sc = c = getC(0); + if (c == 'g' || c == 'a') { + global |= (c == 'g') ? 1 : 2; + sc = c = getC(0); + } + if (((c =='g') && !(global & 1)) || ((c == 'a') && !(global & 2))) { + global |= (c == 'g') ? 1 : 2; + sc = c = getC(0); + } + + switch (c) { + case 'p': + justpr++; + return (en); + + case 'x': + case 'q': + global |= 1; + + /* fall into ... */ + + case 'h': + case 'r': + case 't': + case 'e': + break; + + case '&': + if (slhs[0] == 0) { + seterror(ERR_NOSUBST); + return (en); + } + (void) Strcpy(lhsb, slhs); + break; + +#ifdef notdef + case '~': + if (lhsb[0] == 0) + goto badlhs; + break; +#endif + + case 's': + delim = getC(0); + if (letter(delim) || Isdigit(delim) || any(" \t\n", delim)) { + unreadc(delim); + lhsb[0] = 0; + seterror(ERR_BADSUBST); + return (en); + } + cp = lhsb; + for (;;) { + c = getC(0); + if (c == '\n') { + unreadc(c); + break; + } + if (c == delim) + break; + if (cp > &lhsb[sizeof(lhsb) / sizeof(Char) - 2]) { + lhsb[0] = 0; + seterror(ERR_BADSUBST); + return (en); + } + if (c == '\\') { + c = getC(0); + if (c != delim && c != '\\') + *cp++ = '\\'; + } + *cp++ = c; + } + if (cp != lhsb) + *cp++ = 0; + else if (lhsb[0] == 0) { + seterror(ERR_LHS); + return (en); + } + cp = rhsb; + (void) Strcpy(orhsb, cp); + for (;;) { + c = getC(0); + if (c == '\n') { + unreadc(c); + break; + } + if (c == delim) + break; +#ifdef notdef + if (c == '~') { + if (&cp[Strlen(orhsb)] > &rhsb[sizeof(rhsb) / + sizeof(Char) - 2]) + goto toorhs; + (void) Strcpy(cp, orhsb); + cp = Strend(cp); + continue; + } +#endif + if (cp > &rhsb[sizeof(rhsb) / sizeof(Char) - 2]) { + seterror(ERR_RHSLONG); + return (en); + } + if (c == '\\') { + c = getC(0); + if (c != delim /* && c != '~' */ ) + *cp++ = '\\'; + } + *cp++ = c; + } + *cp++ = 0; + break; + + default: + if (c == '\n') + unreadc(c); + seterror(ERR_BADBANGMOD, c); + return (en); + } + (void) Strcpy(slhs, lhsb); + if (exclc) + en = dosub(sc, en, global); + } + while ((c = getC(0)) == ':'); + unreadc(c); + return (en); +} + +static struct wordent * +dosub(sc, en, global) + int sc; + struct wordent *en; + bool global; +{ + struct wordent lexi; + bool didsub = 0, didone = 0; + struct wordent *hp = &lexi; + register struct wordent *wdp; + register int i = exclc; + + wdp = hp; + while (--i >= 0) { + register struct wordent *new = + (struct wordent *) xcalloc(1, sizeof *wdp); + + new->word = 0; + new->prev = wdp; + new->next = hp; + wdp->next = new; + wdp = new; + en = en->next; + if (en->word) { + Char *tword, *otword; + + if ((global & 1) || didsub == 0) { + tword = subword(en->word, sc, &didone); + if (didone) + didsub = 1; + if (global & 2) { + while (didone && tword != STRNULL) { + otword = tword; + tword = subword(otword, sc, &didone); + if (Strcmp(tword, otword) == 0) { + xfree((ptr_t) otword); + break; + } + else + xfree((ptr_t) otword); + } + } + } + else + tword = Strsave(en->word); + wdp->word = tword; + } + } + if (didsub == 0) + seterror(ERR_MODFAIL); + hp->prev = wdp; + return (&enthist(-1000, &lexi, 0)->Hlex); +} + +static Char * +subword(cp, type, adid) + Char *cp; + int type; + bool *adid; +{ + Char wbuf[BUFSIZ]; + register Char *wp, *mp, *np; + register int i; + + *adid = 0; + switch (type) { + + case 'r': + case 'e': + case 'h': + case 't': + case 'q': + case 'x': + wp = domod(cp, type); + if (wp == 0) + return (Strsave(cp)); + *adid = 1; + return (wp); + + default: + wp = wbuf; + i = BUFSIZ - 4; + for (mp = cp; *mp; mp++) + if (matchs(mp, lhsb)) { + for (np = cp; np < mp;) + *wp++ = *np++, --i; + for (np = rhsb; *np; np++) + switch (*np) { + + case '\\': + if (np[1] == '&') + np++; + /* fall into ... */ + + default: + if (--i < 0) { + seterror(ERR_SUBOVFL); + return (STRNULL); + } + *wp++ = *np; + continue; + + case '&': + i -= Strlen(lhsb); + if (i < 0) { + seterror(ERR_SUBOVFL); + return (STRNULL); + } + *wp = 0; + (void) Strcat(wp, lhsb); + wp = Strend(wp); + continue; + } + mp += Strlen(lhsb); + i -= Strlen(mp); + if (i < 0) { + seterror(ERR_SUBOVFL); + return (STRNULL); + } + *wp = 0; + (void) Strcat(wp, mp); + *adid = 1; + return (Strsave(wbuf)); + } + return (Strsave(cp)); + } +} + +Char * +domod(cp, type) + Char *cp; + int type; +{ + register Char *wp, *xp; + register int c; + + switch (type) { + + case 'x': + case 'q': + wp = Strsave(cp); + for (xp = wp; (c = *xp) != '\0'; xp++) + if ((c != ' ' && c != '\t') || type == 'q') + *xp |= QUOTE; + return (wp); + + case 'h': + case 't': + if (!any(short2str(cp), '/')) + return (type == 't' ? Strsave(cp) : 0); + wp = Strend(cp); + while (*--wp != '/') + continue; + if (type == 'h') + xp = Strsave(cp), xp[wp - cp] = 0; + else + xp = Strsave(wp + 1); + return (xp); + + case 'e': + case 'r': + wp = Strend(cp); + for (wp--; wp >= cp && *wp != '/'; wp--) + if (*wp == '.') { + if (type == 'e') + xp = Strsave(wp + 1); + else + xp = Strsave(cp), xp[wp - cp] = 0; + return (xp); + } + return (Strsave(type == 'e' ? STRNULL : cp)); + default: + break; + } + return (0); +} + +static int +matchs(str, pat) + register Char *str, *pat; +{ + while (*str && *pat && *str == *pat) + str++, pat++; + return (*pat == 0); +} + +static int +getsel(al, ar, dol) + register int *al, *ar; + int dol; +{ + register int c = getC(0); + register int i; + bool first = *al < 0; + + switch (c) { + + case '%': + if (quesarg == -1) { + seterror(ERR_BADBANGARG); + return (0); + } + if (*al < 0) + *al = quesarg; + *ar = quesarg; + break; + + case '-': + if (*al < 0) { + *al = 0; + *ar = dol - 1; + unreadc(c); + } + return (1); + + case '^': + if (*al < 0) + *al = 1; + *ar = 1; + break; + + case '$': + if (*al < 0) + *al = dol; + *ar = dol; + break; + + case '*': + if (*al < 0) + *al = 1; + *ar = dol; + if (*ar < *al) { + *ar = 0; + *al = 1; + return (1); + } + break; + + default: + if (Isdigit(c)) { + i = 0; + while (Isdigit(c)) { + i = i * 10 + c - '0'; + c = getC(0); + } + if (i < 0) + i = dol + 1; + if (*al < 0) + *al = i; + *ar = i; + } + else if (*al < 0) + *al = 0, *ar = dol; + else + *ar = dol - 1; + unreadc(c); + break; + } + if (first) { + c = getC(0); + unreadc(c); + if (any("-$*", c)) + return (1); + } + if (*al > *ar || *ar > dol) { + seterror(ERR_BADBANGARG); + return (0); + } + return (1); + +} + +static struct wordent * +gethent(sc) + int sc; +{ + register struct Hist *hp; + register Char *np; + register int c; + int event; + bool back = 0; + + c = sc == HISTSUB ? HIST : getC(0); + if (c == HIST) { + if (alhistp) + return (alhistp); + event = eventno; + } + else + switch (c) { + + case ':': + case '^': + case '$': + case '*': + case '%': + ungetC(c); + if (lastev == eventno && alhistp) + return (alhistp); + event = lastev; + break; + + case '#': /* !# is command being typed in (mrh) */ + if (--hleft == 0) { + seterror(ERR_HISTLOOP); + return (0); + } + else + return (¶ml); + /* NOTREACHED */ + + case '-': + back = 1; + c = getC(0); + /* FALLSTHROUGH */ + + default: + if (any("(=~", c)) { + unreadc(c); + ungetC(HIST); + return (0); + } + np = lhsb; + event = 0; + while (!cmap(c, _ESC | _META | _QF | _QB) && !any("${}:", c)) { + if (event != -1 && Isdigit(c)) + event = event * 10 + c - '0'; + else + event = -1; + if (np < &lhsb[sizeof(lhsb) / sizeof(Char) - 2]) + *np++ = c; + c = getC(0); + } + unreadc(c); + if (np == lhsb) { + ungetC(HIST); + return (0); + } + *np++ = 0; + if (event != -1) { + /* + * History had only digits + */ + if (back) + event = eventno + (alhistp == 0) - (event ? event : 0); + break; + } + hp = findev(lhsb, 0); + if (hp) + lastev = hp->Hnum; + return (&hp->Hlex); + + case '?': + np = lhsb; + for (;;) { + c = getC(0); + if (c == '\n') { + unreadc(c); + break; + } + if (c == '?') + break; + if (np < &lhsb[sizeof(lhsb) / sizeof(Char) - 2]) + *np++ = c; + } + if (np == lhsb) { + if (lhsb[0] == 0) { + seterror(ERR_NOSEARCH); + return (0); + } + } + else + *np++ = 0; + hp = findev(lhsb, 1); + if (hp) + lastev = hp->Hnum; + return (&hp->Hlex); + } + + for (hp = Histlist.Hnext; hp; hp = hp->Hnext) + if (hp->Hnum == event) { + hp->Href = eventno; + lastev = hp->Hnum; + return (&hp->Hlex); + } + np = putn(event); + seterror(ERR_NOEVENT, vis_str(np)); + return (0); +} + +static struct Hist * +findev(cp, anyarg) + Char *cp; + bool anyarg; +{ + register struct Hist *hp; + + for (hp = Histlist.Hnext; hp; hp = hp->Hnext) { + Char *dp; + register Char *p, *q; + register struct wordent *lp = hp->Hlex.next; + int argno = 0; + + /* + * The entries added by alias substitution don't have a newline but do + * have a negative event number. Savehist() trims off these entries, + * but it happens before alias expansion, too early to delete those + * from the previous command. + */ + if (hp->Hnum < 0) + continue; + if (lp->word[0] == '\n') + continue; + if (!anyarg) { + p = cp; + q = lp->word; + do + if (!*p) + return (hp); + while (*p++ == *q++); + continue; + } + do { + for (dp = lp->word; *dp; dp++) { + p = cp; + q = dp; + do + if (!*p) { + quesarg = argno; + return (hp); + } + while (*p++ == *q++); + } + lp = lp->next; + argno++; + } while (lp->word[0] != '\n'); + } + seterror(ERR_NOEVENT, vis_str(cp)); + return (0); +} + + +static void +setexclp(cp) + register Char *cp; +{ + if (cp && cp[0] == '\n') + return; + exclp = cp; +} + +void +unreadc(c) + int c; +{ + peekread = c; +} + +int +readc(wanteof) + bool wanteof; +{ + register int c; + static sincereal; + + aret = F_SEEK; + if ((c = peekread) != '\0') { + peekread = 0; + return (c); + } +top: + aret = F_SEEK; + if (alvecp) { + aret = A_SEEK; + if ((c = *alvecp++) != '\0') + return (c); + if (alvec && *alvec) { + alvecp = *alvec++; + return (' '); + } + else { + aret = F_SEEK; + alvecp = NULL; + return('\n'); + } + } + if (alvec) { + if ((alvecp = *alvec) != '\0') { + alvec++; + goto top; + } + /* Infinite source! */ + return ('\n'); + } + if (evalp) { + aret = E_SEEK; + if ((c = *evalp++) != '\0') + return (c); + if (evalvec && *evalvec) { + evalp = *evalvec++; + return (' '); + } + aret = F_SEEK; + evalp = 0; + } + if (evalvec) { + if (evalvec == (Char **) 1) { + doneinp = 1; + reset(); + } + if ((evalp = *evalvec) != '\0') { + evalvec++; + goto top; + } + evalvec = (Char **) 1; + return ('\n'); + } + do { + if (arginp == (Char *) 1 || onelflg == 1) { + if (wanteof) + return (-1); + exitstat(); + } + if (arginp) { + if ((c = *arginp++) == 0) { + arginp = (Char *) 1; + return ('\n'); + } + return (c); + } +reread: + c = bgetc(); + if (c < 0) { + struct termios tty; + if (wanteof) + return (-1); + /* was isatty but raw with ignoreeof yields problems */ + if (tcgetattr(SHIN, &tty) == 0 && (tty.c_lflag & ICANON)) + { + /* was 'short' for FILEC */ + int ctpgrp; + + if (++sincereal > 25) + goto oops; + if (tpgrp != -1 && + (ctpgrp = tcgetpgrp(FSHTTY)) != -1 && + tpgrp != ctpgrp) { + (void) tcsetpgrp(FSHTTY, tpgrp); + (void) killpg((pid_t) ctpgrp, SIGHUP); + (void) fprintf(csherr, "Reset tty pgrp from %d to %d\n", + ctpgrp, tpgrp); + goto reread; + } + if (adrof(STRignoreeof)) { + if (loginsh) + (void) fprintf(csherr,"\nUse \"logout\" to logout.\n"); + else + (void) fprintf(csherr,"\nUse \"exit\" to leave csh.\n"); + reset(); + } + if (chkstop == 0) + panystop(1); + } + oops: + doneinp = 1; + reset(); + } + sincereal = 0; + if (c == '\n' && onelflg) + onelflg--; + } while (c == 0); + return (c); +} + +static int +bgetc() +{ + register int buf, off, c; + +#ifdef FILEC + register int numleft = 0, roomleft; + Char ttyline[BUFSIZ]; +#endif + char tbuf[BUFSIZ + 1]; + + if (cantell) { + if (fseekp < fbobp || fseekp > feobp) { + fbobp = feobp = fseekp; + (void) lseek(SHIN, fseekp, L_SET); + } + if (fseekp == feobp) { + int i; + + fbobp = feobp; + do + c = read(SHIN, tbuf, BUFSIZ); + while (c < 0 && errno == EINTR); + if (c <= 0) + return (-1); + for (i = 0; i < c; i++) + fbuf[0][i] = (unsigned char) tbuf[i]; + feobp += c; + } + c = fbuf[0][fseekp - fbobp]; + fseekp++; + return (c); + } + +again: + buf = (int) fseekp / BUFSIZ; + if (buf >= fblocks) { + register Char **nfbuf = + (Char **) xcalloc((size_t) (fblocks + 2), + sizeof(Char **)); + + if (fbuf) { + (void) blkcpy(nfbuf, fbuf); + xfree((ptr_t) fbuf); + } + fbuf = nfbuf; + fbuf[fblocks] = (Char *) xcalloc(BUFSIZ, sizeof(Char)); + fblocks++; + if (!intty) + goto again; + } + if (fseekp >= feobp) { + buf = (int) feobp / BUFSIZ; + off = (int) feobp % BUFSIZ; + roomleft = BUFSIZ - off; + +#ifdef FILEC + roomleft = BUFSIZ - off; + for (;;) { + if (filec && intty) { + c = numleft ? numleft : tenex(ttyline, BUFSIZ); + if (c > roomleft) { + /* start with fresh buffer */ + feobp = fseekp = fblocks * BUFSIZ; + numleft = c; + goto again; + } + if (c > 0) + bcopy(ttyline, fbuf[buf] + off, c * sizeof(Char)); + numleft = 0; + } + else { +#endif + c = read(SHIN, tbuf, roomleft); + if (c > 0) { + int i; + Char *ptr = fbuf[buf] + off; + + for (i = 0; i < c; i++) + ptr[i] = (unsigned char) tbuf[i]; + } +#ifdef FILEC + } +#endif + if (c >= 0) + break; + if (errno == EWOULDBLOCK) { + int off = 0; + + (void) ioctl(SHIN, FIONBIO, (ioctl_t) & off); + } + else if (errno != EINTR) + break; + } + if (c <= 0) + return (-1); + feobp += c; +#ifndef FILEC + goto again; +#else + if (filec && !intty) + goto again; +#endif + } + c = fbuf[buf][(int) fseekp % BUFSIZ]; + fseekp++; + return (c); +} + +static void +bfree() +{ + register int sb, i; + + if (cantell) + return; + if (whyles) + return; + sb = (int) (fseekp - 1) / BUFSIZ; + if (sb > 0) { + for (i = 0; i < sb; i++) + xfree((ptr_t) fbuf[i]); + (void) blkcpy(fbuf, &fbuf[sb]); + fseekp -= BUFSIZ * sb; + feobp -= BUFSIZ * sb; + fblocks -= sb; + } +} + +void +bseek(l) + struct Ain *l; +{ + switch (aret = l->type) { + case E_SEEK: + evalvec = l->a_seek; + evalp = l->c_seek; + return; + case A_SEEK: + alvec = l->a_seek; + alvecp = l->c_seek; + return; + case F_SEEK: + fseekp = l->f_seek; + return; + default: + (void) fprintf(csherr, "Bad seek type %d\n", aret); + abort(); + } +} + +void +btell(l) + struct Ain *l; +{ + switch (l->type = aret) { + case E_SEEK: + l->a_seek = evalvec; + l->c_seek = evalp; + return; + case A_SEEK: + l->a_seek = alvec; + l->c_seek = alvecp; + return; + case F_SEEK: + l->f_seek = fseekp; + l->a_seek = NULL; + return; + default: + (void) fprintf(csherr, "Bad seek type %d\n", aret); + abort(); + } +} + +void +btoeof() +{ + (void) lseek(SHIN, (off_t) 0, L_XTND); + aret = F_SEEK; + fseekp = feobp; + alvec = NULL; + alvecp = NULL; + evalvec = NULL; + evalp = NULL; + wfree(); + bfree(); +} + +void +settell() +{ + cantell = 0; + if (arginp || onelflg || intty) + return; + if (lseek(SHIN, (off_t) 0, L_INCR) < 0 || errno == ESPIPE) + return; + fbuf = (Char **) xcalloc(2, sizeof(Char **)); + fblocks = 1; + fbuf[0] = (Char *) xcalloc(BUFSIZ, sizeof(Char)); + fseekp = fbobp = feobp = lseek(SHIN, (off_t) 0, L_INCR); + cantell = 1; +} diff --git a/bin/csh/misc.c b/bin/csh/misc.c new file mode 100644 index 0000000..bd84253 --- /dev/null +++ b/bin/csh/misc.c @@ -0,0 +1,420 @@ +/*- + * 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[] = "@(#)misc.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <stdlib.h> +#include <unistd.h> +#if __STDC__ +# include <stdarg.h> +#else +# include <varargs.h> +#endif + +#include "csh.h" +#include "extern.h" + +static int renum __P((int, int)); + +int +any(s, c) + register char *s; + register int c; +{ + if (!s) + return (0); /* Check for nil pointer */ + while (*s) + if (*s++ == c) + return (1); + return (0); +} + +void +setzero(cp, i) + char *cp; + int i; +{ + if (i != 0) + do + *cp++ = 0; + while (--i); +} + +char * +strsave(s) + register char *s; +{ + char *n; + register char *p; + + if (s == NULL) + s = ""; + for (p = s; *p++;) + continue; + n = p = (char *) xmalloc((size_t) ((p - s) * sizeof(char))); + while ((*p++ = *s++) != '\0') + continue; + return (n); +} + +Char ** +blkend(up) + register Char **up; +{ + + while (*up) + up++; + return (up); +} + + +void +blkpr(fp, av) + FILE *fp; + register Char **av; +{ + + for (; *av; av++) { + (void) fprintf(fp, "%s", vis_str(*av)); + if (av[1]) + (void) fprintf(fp, " "); + } +} + +int +blklen(av) + register Char **av; +{ + register int i = 0; + + while (*av++) + i++; + return (i); +} + +Char ** +blkcpy(oav, bv) + Char **oav; + register Char **bv; +{ + register Char **av = oav; + + while ((*av++ = *bv++) != NULL) + continue; + return (oav); +} + +Char ** +blkcat(up, vp) + Char **up, **vp; +{ + + (void) blkcpy(blkend(up), vp); + return (up); +} + +void +blkfree(av0) + Char **av0; +{ + register Char **av = av0; + + if (!av0) + return; + for (; *av; av++) + xfree((ptr_t) * av); + xfree((ptr_t) av0); +} + +Char ** +saveblk(v) + register Char **v; +{ + register Char **newv = + (Char **) xcalloc((size_t) (blklen(v) + 1), sizeof(Char **)); + Char **onewv = newv; + + while (*v) + *newv++ = Strsave(*v++); + return (onewv); +} + +#ifdef NOTUSED +char * +strstr(s, t) + register char *s, *t; +{ + do { + register char *ss = s; + register char *tt = t; + + do + if (*tt == '\0') + return (s); + while (*ss++ == *tt++); + } while (*s++ != '\0'); + return (NULL); +} + +#endif /* NOTUSED */ + +#ifndef SHORT_STRINGS +char * +strspl(cp, dp) + char *cp, *dp; +{ + char *ep; + register char *p, *q; + + if (!cp) + cp = ""; + if (!dp) + dp = ""; + for (p = cp; *p++;) + continue; + for (q = dp; *q++;) + continue; + ep = (char *) xmalloc((size_t) (((p - cp) + (q - dp) - 1) * sizeof(char))); + for (p = ep, q = cp; *p++ = *q++;) + continue; + for (p--, q = dp; *p++ = *q++;) + continue; + return (ep); +} + +#endif + +Char ** +blkspl(up, vp) + register Char **up, **vp; +{ + register Char **wp = + (Char **) xcalloc((size_t) (blklen(up) + blklen(vp) + 1), + sizeof(Char **)); + + (void) blkcpy(wp, up); + return (blkcat(wp, vp)); +} + +Char +lastchr(cp) + register Char *cp; +{ + + if (!cp) + return (0); + if (!*cp) + return (0); + while (cp[1]) + cp++; + return (*cp); +} + +/* + * This routine is called after an error to close up + * any units which may have been left open accidentally. + */ +void +closem() +{ + register int f; + + for (f = 0; f < NOFILE; f++) + if (f != SHIN && f != SHOUT && f != SHERR && f != OLDSTD && + f != FSHTTY) + (void) close(f); +} + +void +donefds() +{ + (void) close(0); + (void) close(1); + (void) close(2); + + didfds = 0; +} + +/* + * Move descriptor i to j. + * If j is -1 then we just want to get i to a safe place, + * i.e. to a unit > 2. This also happens in dcopy. + */ +int +dmove(i, j) + register int i, j; +{ + + if (i == j || i < 0) + return (i); + if (j >= 0) { + (void) dup2(i, j); + if (j != i) + (void) close(i); + return (j); + } + j = dcopy(i, j); + if (j != i) + (void) close(i); + return (j); +} + +int +dcopy(i, j) + register int i, j; +{ + + if (i == j || i < 0 || (j < 0 && i > 2)) + return (i); + if (j >= 0) { + (void) dup2(i, j); + return (j); + } + (void) close(j); + return (renum(i, j)); +} + +static int +renum(i, j) + register int i, j; +{ + register int k = dup(i); + + if (k < 0) + return (-1); + if (j == -1 && k > 2) + return (k); + if (k != j) { + j = renum(k, j); + (void) close(k); + return (j); + } + return (k); +} + +/* + * Left shift a command argument list, discarding + * the first c arguments. Used in "shift" commands + * as well as by commands like "repeat". + */ +void +lshift(v, c) + register Char **v; + register int c; +{ + register Char **u; + + for (u = v; *u && --c >= 0; u++) + xfree((ptr_t) *u); + (void) blkcpy(v, u); +} + +int +number(cp) + Char *cp; +{ + if (!cp) + return(0); + if (*cp == '-') { + cp++; + if (!Isdigit(*cp)) + return (0); + cp++; + } + while (*cp && Isdigit(*cp)) + cp++; + return (*cp == 0); +} + +Char ** +copyblk(v) + register Char **v; +{ + Char **nv = (Char **) xcalloc((size_t) (blklen(v) + 1), sizeof(Char **)); + + return (blkcpy(nv, v)); +} + +#ifndef SHORT_STRINGS +char * +strend(cp) + register char *cp; +{ + if (!cp) + return (cp); + while (*cp) + cp++; + return (cp); +} + +#endif /* SHORT_STRINGS */ + +Char * +strip(cp) + Char *cp; +{ + register Char *dp = cp; + + if (!cp) + return (cp); + while ((*dp++ &= TRIM) != '\0') + continue; + return (cp); +} + +void +udvar(name) + Char *name; +{ + + setname(vis_str(name)); + stderror(ERR_NAME | ERR_UNDVAR); +} + +int +prefix(sub, str) + register Char *sub, *str; +{ + + for (;;) { + if (*sub == 0) + return (1); + if (*str == 0) + return (0); + if (*sub++ != *str++) + return (0); + } +} diff --git a/bin/csh/parse.c b/bin/csh/parse.c new file mode 100644 index 0000000..18b9fc0 --- /dev/null +++ b/bin/csh/parse.c @@ -0,0 +1,698 @@ +/*- + * 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[] = "@(#)parse.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <stdlib.h> +#include <string.h> +#if __STDC__ +# include <stdarg.h> +#else +# include <varargs.h> +#endif + +#include "csh.h" +#include "extern.h" + +static void asyntax __P((struct wordent *, struct wordent *)); +static void asyn0 __P((struct wordent *, struct wordent *)); +static void asyn3 __P((struct wordent *, struct wordent *)); +static struct wordent + *freenod __P((struct wordent *, struct wordent *)); +static struct command + *syn0 __P((struct wordent *, struct wordent *, int)); +static struct command + *syn1 __P((struct wordent *, struct wordent *, int)); +static struct command + *syn1a __P((struct wordent *, struct wordent *, int)); +static struct command + *syn1b __P((struct wordent *, struct wordent *, int)); +static struct command + *syn2 __P((struct wordent *, struct wordent *, int)); +static struct command + *syn3 __P((struct wordent *, struct wordent *, int)); + +#define ALEFT 21 /* max of 20 alias expansions */ +#define HLEFT 11 /* max of 10 history expansions */ +/* + * Perform aliasing on the word list lex + * Do a (very rudimentary) parse to separate into commands. + * If word 0 of a command has an alias, do it. + * Repeat a maximum of 20 times. + */ +static int aleft; +extern int hleft; +void +alias(lex) + register struct wordent *lex; +{ + jmp_buf osetexit; + + aleft = ALEFT; + hleft = HLEFT; + getexit(osetexit); + (void) setexit(); + if (haderr) { + resexit(osetexit); + reset(); + } + if (--aleft == 0) + stderror(ERR_ALIASLOOP); + asyntax(lex->next, lex); + resexit(osetexit); +} + +static void +asyntax(p1, p2) + register struct wordent *p1, *p2; +{ + while (p1 != p2) + if (any(";&\n", p1->word[0])) + p1 = p1->next; + else { + asyn0(p1, p2); + return; + } +} + +static void +asyn0(p1, p2) + struct wordent *p1; + register struct wordent *p2; +{ + register struct wordent *p; + register int l = 0; + + for (p = p1; p != p2; p = p->next) + switch (p->word[0]) { + + case '(': + l++; + continue; + + case ')': + l--; + if (l < 0) + stderror(ERR_TOOMANYRP); + continue; + + case '>': + if (p->next != p2 && eq(p->next->word, STRand)) + p = p->next; + continue; + + case '&': + case '|': + case ';': + case '\n': + if (l != 0) + continue; + asyn3(p1, p); + asyntax(p->next, p2); + return; + } + if (l == 0) + asyn3(p1, p2); +} + +static void +asyn3(p1, p2) + struct wordent *p1; + register struct wordent *p2; +{ + register struct varent *ap; + struct wordent alout; + register bool redid; + + if (p1 == p2) + return; + if (p1->word[0] == '(') { + for (p2 = p2->prev; p2->word[0] != ')'; p2 = p2->prev) + if (p2 == p1) + return; + if (p2 == p1->next) + return; + asyn0(p1->next, p2); + return; + } + ap = adrof1(p1->word, &aliases); + if (ap == 0) + return; + alhistp = p1->prev; + alhistt = p2; + alvec = ap->vec; + redid = lex(&alout); + alhistp = alhistt = 0; + alvec = 0; + if (seterr) { + freelex(&alout); + stderror(ERR_OLD); + } + if (p1->word[0] && eq(p1->word, alout.next->word)) { + Char *cp = alout.next->word; + + alout.next->word = Strspl(STRQNULL, cp); + xfree((ptr_t) cp); + } + p1 = freenod(p1, redid ? p2 : p1->next); + if (alout.next != &alout) { + p1->next->prev = alout.prev->prev; + alout.prev->prev->next = p1->next; + alout.next->prev = p1; + p1->next = alout.next; + xfree((ptr_t) alout.prev->word); + xfree((ptr_t) (alout.prev)); + } + reset(); /* throw! */ +} + +static struct wordent * +freenod(p1, p2) + register struct wordent *p1, *p2; +{ + register struct wordent *retp = p1->prev; + + while (p1 != p2) { + xfree((ptr_t) p1->word); + p1 = p1->next; + xfree((ptr_t) (p1->prev)); + } + retp->next = p2; + p2->prev = retp; + return (retp); +} + +#define PHERE 1 +#define PIN 2 +#define POUT 4 +#define PERR 8 + +/* + * syntax + * empty + * syn0 + */ +struct command * +syntax(p1, p2, flags) + register struct wordent *p1, *p2; + int flags; +{ + + while (p1 != p2) + if (any(";&\n", p1->word[0])) + p1 = p1->next; + else + return (syn0(p1, p2, flags)); + return (0); +} + +/* + * syn0 + * syn1 + * syn1 & syntax + */ +static struct command * +syn0(p1, p2, flags) + struct wordent *p1, *p2; + int flags; +{ + register struct wordent *p; + register struct command *t, *t1; + int l; + + l = 0; + for (p = p1; p != p2; p = p->next) + switch (p->word[0]) { + + case '(': + l++; + continue; + + case ')': + l--; + if (l < 0) + seterror(ERR_TOOMANYRP); + continue; + + case '|': + if (p->word[1] == '|') + continue; + /* fall into ... */ + + case '>': + if (p->next != p2 && eq(p->next->word, STRand)) + p = p->next; + continue; + + case '&': + if (l != 0) + break; + if (p->word[1] == '&') + continue; + t1 = syn1(p1, p, flags); + if (t1->t_dtyp == NODE_LIST || + t1->t_dtyp == NODE_AND || + t1->t_dtyp == NODE_OR) { + t = (struct command *) xcalloc(1, sizeof(*t)); + t->t_dtyp = NODE_PAREN; + t->t_dflg = F_AMPERSAND | F_NOINTERRUPT; + t->t_dspr = t1; + t1 = t; + } + else + t1->t_dflg |= F_AMPERSAND | F_NOINTERRUPT; + t = (struct command *) xcalloc(1, sizeof(*t)); + t->t_dtyp = NODE_LIST; + t->t_dflg = 0; + t->t_dcar = t1; + t->t_dcdr = syntax(p, p2, flags); + return (t); + } + if (l == 0) + return (syn1(p1, p2, flags)); + seterror(ERR_TOOMANYLP); + return (0); +} + +/* + * syn1 + * syn1a + * syn1a ; syntax + */ +static struct command * +syn1(p1, p2, flags) + struct wordent *p1, *p2; + int flags; +{ + register struct wordent *p; + register struct command *t; + int l; + + l = 0; + for (p = p1; p != p2; p = p->next) + switch (p->word[0]) { + + case '(': + l++; + continue; + + case ')': + l--; + continue; + + case ';': + case '\n': + if (l != 0) + break; + t = (struct command *) xcalloc(1, sizeof(*t)); + t->t_dtyp = NODE_LIST; + t->t_dcar = syn1a(p1, p, flags); + t->t_dcdr = syntax(p->next, p2, flags); + if (t->t_dcdr == 0) + t->t_dcdr = t->t_dcar, t->t_dcar = 0; + return (t); + } + return (syn1a(p1, p2, flags)); +} + +/* + * syn1a + * syn1b + * syn1b || syn1a + */ +static struct command * +syn1a(p1, p2, flags) + struct wordent *p1, *p2; + int flags; +{ + register struct wordent *p; + register struct command *t; + register int l = 0; + + for (p = p1; p != p2; p = p->next) + switch (p->word[0]) { + + case '(': + l++; + continue; + + case ')': + l--; + continue; + + case '|': + if (p->word[1] != '|') + continue; + if (l == 0) { + t = (struct command *) xcalloc(1, sizeof(*t)); + t->t_dtyp = NODE_OR; + t->t_dcar = syn1b(p1, p, flags); + t->t_dcdr = syn1a(p->next, p2, flags); + t->t_dflg = 0; + return (t); + } + continue; + } + return (syn1b(p1, p2, flags)); +} + +/* + * syn1b + * syn2 + * syn2 && syn1b + */ +static struct command * +syn1b(p1, p2, flags) + struct wordent *p1, *p2; + int flags; +{ + register struct wordent *p; + register struct command *t; + register int l = 0; + + for (p = p1; p != p2; p = p->next) + switch (p->word[0]) { + + case '(': + l++; + continue; + + case ')': + l--; + continue; + + case '&': + if (p->word[1] == '&' && l == 0) { + t = (struct command *) xcalloc(1, sizeof(*t)); + t->t_dtyp = NODE_AND; + t->t_dcar = syn2(p1, p, flags); + t->t_dcdr = syn1b(p->next, p2, flags); + t->t_dflg = 0; + return (t); + } + continue; + } + return (syn2(p1, p2, flags)); +} + +/* + * syn2 + * syn3 + * syn3 | syn2 + * syn3 |& syn2 + */ +static struct command * +syn2(p1, p2, flags) + struct wordent *p1, *p2; + int flags; +{ + register struct wordent *p, *pn; + register struct command *t; + register int l = 0; + int f; + + for (p = p1; p != p2; p = p->next) + switch (p->word[0]) { + + case '(': + l++; + continue; + + case ')': + l--; + continue; + + case '|': + if (l != 0) + continue; + t = (struct command *) xcalloc(1, sizeof(*t)); + f = flags | POUT; + pn = p->next; + if (pn != p2 && pn->word[0] == '&') { + f |= PERR; + t->t_dflg |= F_STDERR; + } + t->t_dtyp = NODE_PIPE; + t->t_dcar = syn3(p1, p, f); + if (pn != p2 && pn->word[0] == '&') + p = pn; + t->t_dcdr = syn2(p->next, p2, flags | PIN); + return (t); + } + return (syn3(p1, p2, flags)); +} + +static char RELPAR[] = {'<', '>', '(', ')', '\0'}; + +/* + * syn3 + * ( syn0 ) [ < in ] [ > out ] + * word word* [ < in ] [ > out ] + * KEYWORD ( word* ) word* [ < in ] [ > out ] + * + * KEYWORD = (@ exit foreach if set switch test while) + */ +static struct command * +syn3(p1, p2, flags) + struct wordent *p1, *p2; + int flags; +{ + register struct wordent *p; + struct wordent *lp, *rp; + register struct command *t; + register int l; + Char **av; + int n, c; + bool specp = 0; + + if (p1 != p2) { + p = p1; +again: + switch (srchx(p->word)) { + + case T_ELSE: + p = p->next; + if (p != p2) + goto again; + break; + + case T_EXIT: + case T_FOREACH: + case T_IF: + case T_LET: + case T_SET: + case T_SWITCH: + case T_WHILE: + specp = 1; + break; + } + } + n = 0; + l = 0; + for (p = p1; p != p2; p = p->next) + switch (p->word[0]) { + + case '(': + if (specp) + n++; + l++; + continue; + + case ')': + if (specp) + n++; + l--; + continue; + + case '>': + case '<': + if (l != 0) { + if (specp) + n++; + continue; + } + if (p->next == p2) + continue; + if (any(RELPAR, p->next->word[0])) + continue; + n--; + continue; + + default: + if (!specp && l != 0) + continue; + n++; + continue; + } + if (n < 0) + n = 0; + t = (struct command *) xcalloc(1, sizeof(*t)); + av = (Char **) xcalloc((size_t) (n + 1), sizeof(Char **)); + t->t_dcom = av; + n = 0; + if (p2->word[0] == ')') + t->t_dflg = F_NOFORK; + lp = 0; + rp = 0; + l = 0; + for (p = p1; p != p2; p = p->next) { + c = p->word[0]; + switch (c) { + + case '(': + if (l == 0) { + if (lp != 0 && !specp) + seterror(ERR_BADPLP); + lp = p->next; + } + l++; + goto savep; + + case ')': + l--; + if (l == 0) + rp = p; + goto savep; + + case '>': + if (l != 0) + goto savep; + if (p->word[1] == '>') + t->t_dflg |= F_APPEND; + if (p->next != p2 && eq(p->next->word, STRand)) { + t->t_dflg |= F_STDERR, p = p->next; + if (flags & (POUT | PERR)) { + seterror(ERR_OUTRED); + continue; + } + } + if (p->next != p2 && eq(p->next->word, STRbang)) + t->t_dflg |= F_OVERWRITE, p = p->next; + if (p->next == p2) { + seterror(ERR_MISRED); + continue; + } + p = p->next; + if (any(RELPAR, p->word[0])) { + seterror(ERR_MISRED); + continue; + } + if ((flags & POUT) && ((flags & PERR) == 0 || t->t_drit)) + seterror(ERR_OUTRED); + else + t->t_drit = Strsave(p->word); + continue; + + case '<': + if (l != 0) + goto savep; + if (p->word[1] == '<') + t->t_dflg |= F_READ; + if (p->next == p2) { + seterror(ERR_MISRED); + continue; + } + p = p->next; + if (any(RELPAR, p->word[0])) { + seterror(ERR_MISRED); + continue; + } + if ((flags & PHERE) && (t->t_dflg & F_READ)) + seterror(ERR_REDPAR); + else if ((flags & PIN) || t->t_dlef) + seterror(ERR_INRED); + else + t->t_dlef = Strsave(p->word); + continue; + + savep: + if (!specp) + continue; + default: + if (l != 0 && !specp) + continue; + if (seterr == 0) + av[n] = Strsave(p->word); + n++; + continue; + } + } + if (lp != 0 && !specp) { + if (n != 0) + seterror(ERR_BADPLPS); + t->t_dtyp = NODE_PAREN; + t->t_dspr = syn0(lp, rp, PHERE); + } + else { + if (n == 0) + seterror(ERR_NULLCOM); + t->t_dtyp = NODE_COMMAND; + } + return (t); +} + +void +freesyn(t) + register struct command *t; +{ + register Char **v; + + if (t == 0) + return; + switch (t->t_dtyp) { + + case NODE_COMMAND: + for (v = t->t_dcom; *v; v++) + xfree((ptr_t) * v); + xfree((ptr_t) (t->t_dcom)); + xfree((ptr_t) t->t_dlef); + xfree((ptr_t) t->t_drit); + break; + case NODE_PAREN: + freesyn(t->t_dspr); + xfree((ptr_t) t->t_dlef); + xfree((ptr_t) t->t_drit); + break; + + case NODE_AND: + case NODE_OR: + case NODE_PIPE: + case NODE_LIST: + freesyn(t->t_dcar), freesyn(t->t_dcdr); + break; + } + xfree((ptr_t) t); +} diff --git a/bin/csh/pathnames.h b/bin/csh/pathnames.h new file mode 100644 index 0000000..391f40d --- /dev/null +++ b/bin/csh/pathnames.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 1988, 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. + * + * @(#)pathnames.h 8.1 (Berkeley) 5/31/93 + */ + +#define _PATH_BIN "/bin" +#define _PATH_DOTCSHRC "/etc/csh.cshrc" +#define _PATH_DOTLOGIN "/etc/csh.login" +#define _PATH_DOTLOGOUT "/etc/csh.logout" +#define _PATH_LOGIN "/usr/bin/login" +#define _PATH_USRBIN "/usr/bin" diff --git a/bin/csh/proc.c b/bin/csh/proc.c new file mode 100644 index 0000000..8a85fdc --- /dev/null +++ b/bin/csh/proc.c @@ -0,0 +1,1356 @@ +/*- + * 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[] = "@(#)proc.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/wait.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#if __STDC__ +# include <stdarg.h> +#else +# include <varargs.h> +#endif + +#include "csh.h" +#include "dir.h" +#include "proc.h" +#include "extern.h" + +#define BIGINDEX 9 /* largest desirable job index */ + +static struct rusage zru; + +static void pflushall __P((void)); +static void pflush __P((struct process *)); +static void pclrcurr __P((struct process *)); +static void padd __P((struct command *)); +static int pprint __P((struct process *, int)); +static void ptprint __P((struct process *)); +static void pads __P((Char *)); +static void pkill __P((Char **v, int)); +static struct process + *pgetcurr __P((struct process *)); +static void okpcntl __P((void)); + +/* + * pchild - called at interrupt level by the SIGCHLD signal + * indicating that at least one child has terminated or stopped + * thus at least one wait system call will definitely return a + * childs status. Top level routines (like pwait) must be sure + * to mask interrupts when playing with the proclist data structures! + */ +/* ARGUSED */ +void +pchild(notused) + int notused; +{ + register struct process *pp; + register struct process *fp; + register int pid; + extern int insource; + union wait w; + int jobflags; + struct rusage ru; + +loop: + errno = 0; /* reset, just in case */ + pid = wait3(&w.w_status, + (setintr && (intty || insource) ? WNOHANG | WUNTRACED : WNOHANG), &ru); + + if (pid <= 0) { + if (errno == EINTR) { + errno = 0; + goto loop; + } + pnoprocesses = pid == -1; + return; + } + for (pp = proclist.p_next; pp != NULL; pp = pp->p_next) + if (pid == pp->p_pid) + goto found; + goto loop; +found: + if (pid == atoi(short2str(value(STRchild)))) + unsetv(STRchild); + pp->p_flags &= ~(PRUNNING | PSTOPPED | PREPORTED); + if (WIFSTOPPED(w)) { + pp->p_flags |= PSTOPPED; + pp->p_reason = w.w_stopsig; + } + else { + if (pp->p_flags & (PTIME | PPTIME) || adrof(STRtime)) + (void) gettimeofday(&pp->p_etime, NULL); + + pp->p_rusage = ru; + if (WIFSIGNALED(w)) { + if (w.w_termsig == SIGINT) + pp->p_flags |= PINTERRUPTED; + else + pp->p_flags |= PSIGNALED; + if (w.w_coredump) + pp->p_flags |= PDUMPED; + pp->p_reason = w.w_termsig; + } + else { + pp->p_reason = w.w_retcode; + if (pp->p_reason != 0) + pp->p_flags |= PAEXITED; + else + pp->p_flags |= PNEXITED; + } + } + jobflags = 0; + fp = pp; + do { + if ((fp->p_flags & (PPTIME | PRUNNING | PSTOPPED)) == 0 && + !child && adrof(STRtime) && + fp->p_rusage.ru_utime.tv_sec + fp->p_rusage.ru_stime.tv_sec + >= atoi(short2str(value(STRtime)))) + fp->p_flags |= PTIME; + jobflags |= fp->p_flags; + } while ((fp = fp->p_friends) != pp); + pp->p_flags &= ~PFOREGND; + if (pp == pp->p_friends && (pp->p_flags & PPTIME)) { + pp->p_flags &= ~PPTIME; + pp->p_flags |= PTIME; + } + if ((jobflags & (PRUNNING | PREPORTED)) == 0) { + fp = pp; + do { + if (fp->p_flags & PSTOPPED) + fp->p_flags |= PREPORTED; + } while ((fp = fp->p_friends) != pp); + while (fp->p_pid != fp->p_jobid) + fp = fp->p_friends; + if (jobflags & PSTOPPED) { + if (pcurrent && pcurrent != fp) + pprevious = pcurrent; + pcurrent = fp; + } + else + pclrcurr(fp); + if (jobflags & PFOREGND) { + if (jobflags & (PSIGNALED | PSTOPPED | PPTIME) || +#ifdef IIASA + jobflags & PAEXITED || +#endif + !eq(dcwd->di_name, fp->p_cwd->di_name)) { + ; /* print in pjwait */ + } + /* PWP: print a newline after ^C */ + else if (jobflags & PINTERRUPTED) { + (void) vis_fputc('\r' | QUOTE, cshout); + (void) fputc('\n', cshout); + } + } + else { + if (jobflags & PNOTIFY || adrof(STRnotify)) { + (void) vis_fputc('\r' | QUOTE, cshout); + (void) fputc('\n', cshout); + (void) pprint(pp, NUMBER | NAME | REASON); + if ((jobflags & PSTOPPED) == 0) + pflush(pp); + } + else { + fp->p_flags |= PNEEDNOTE; + neednote++; + } + } + } + goto loop; +} + +void +pnote() +{ + register struct process *pp; + int flags; + sigset_t omask; + + neednote = 0; + for (pp = proclist.p_next; pp != NULL; pp = pp->p_next) { + if (pp->p_flags & PNEEDNOTE) { + omask = sigblock(sigmask(SIGCHLD)); + pp->p_flags &= ~PNEEDNOTE; + flags = pprint(pp, NUMBER | NAME | REASON); + if ((flags & (PRUNNING | PSTOPPED)) == 0) + pflush(pp); + (void) sigsetmask(omask); + } + } +} + +/* + * pwait - wait for current job to terminate, maintaining integrity + * of current and previous job indicators. + */ +void +pwait() +{ + register struct process *fp, *pp; + sigset_t omask; + + /* + * Here's where dead procs get flushed. + */ + omask = sigblock(sigmask(SIGCHLD)); + for (pp = (fp = &proclist)->p_next; pp != NULL; pp = (fp = pp)->p_next) + if (pp->p_pid == 0) { + fp->p_next = pp->p_next; + xfree((ptr_t) pp->p_command); + if (pp->p_cwd && --pp->p_cwd->di_count == 0) + if (pp->p_cwd->di_next == 0) + dfree(pp->p_cwd); + xfree((ptr_t) pp); + pp = fp; + } + (void) sigsetmask(omask); + pjwait(pcurrjob); +} + + +/* + * pjwait - wait for a job to finish or become stopped + * It is assumed to be in the foreground state (PFOREGND) + */ +void +pjwait(pp) + register struct process *pp; +{ + register struct process *fp; + int jobflags, reason; + sigset_t omask; + + while (pp->p_pid != pp->p_jobid) + pp = pp->p_friends; + fp = pp; + + do { + if ((fp->p_flags & (PFOREGND | PRUNNING)) == PRUNNING) + (void) fprintf(csherr, "BUG: waiting for background job!\n"); + } while ((fp = fp->p_friends) != pp); + /* + * Now keep pausing as long as we are not interrupted (SIGINT), and the + * target process, or any of its friends, are running + */ + fp = pp; + omask = sigblock(sigmask(SIGCHLD)); + for (;;) { + (void) sigblock(sigmask(SIGCHLD)); + jobflags = 0; + do + jobflags |= fp->p_flags; + while ((fp = (fp->p_friends)) != pp); + if ((jobflags & PRUNNING) == 0) + break; +#ifdef JOBDEBUG + (void) fprintf(csherr, "starting to sigpause for SIGCHLD on %d\n", + fp->p_pid); +#endif /* JOBDEBUG */ + (void) sigpause(omask & ~sigmask(SIGCHLD)); + } + (void) sigsetmask(omask); + if (tpgrp > 0) /* get tty back */ + (void) tcsetpgrp(FSHTTY, tpgrp); + if ((jobflags & (PSIGNALED | PSTOPPED | PTIME)) || + !eq(dcwd->di_name, fp->p_cwd->di_name)) { + if (jobflags & PSTOPPED) { + (void) fputc('\n', cshout); + if (adrof(STRlistjobs)) { + Char *jobcommand[3]; + + jobcommand[0] = STRjobs; + if (eq(value(STRlistjobs), STRlong)) + jobcommand[1] = STRml; + else + jobcommand[1] = NULL; + jobcommand[2] = NULL; + + dojobs(jobcommand, NULL); + (void) pprint(pp, SHELLDIR); + } + else + (void) pprint(pp, AREASON | SHELLDIR); + } + else + (void) pprint(pp, AREASON | SHELLDIR); + } + if ((jobflags & (PINTERRUPTED | PSTOPPED)) && setintr && + (!gointr || !eq(gointr, STRminus))) { + if ((jobflags & PSTOPPED) == 0) + pflush(pp); + pintr1(0); + /* NOTREACHED */ + } + reason = 0; + fp = pp; + do { + if (fp->p_reason) + reason = fp->p_flags & (PSIGNALED | PINTERRUPTED) ? + fp->p_reason | META : fp->p_reason; + } while ((fp = fp->p_friends) != pp); + if ((reason != 0) && (adrof(STRprintexitvalue))) { + (void) fprintf(cshout, "Exit %d\n", reason); + } + set(STRstatus, putn(reason)); + if (reason && exiterr) + exitstat(); + pflush(pp); +} + +/* + * dowait - wait for all processes to finish + */ +void +/*ARGSUSED*/ +dowait(v, t) + Char **v; + struct command *t; +{ + register struct process *pp; + sigset_t omask; + + pjobs++; + omask = sigblock(sigmask(SIGCHLD)); +loop: + for (pp = proclist.p_next; pp; pp = pp->p_next) + if (pp->p_pid && /* pp->p_pid == pp->p_jobid && */ + pp->p_flags & PRUNNING) { + (void) sigpause((sigset_t) 0); + goto loop; + } + (void) sigsetmask(omask); + pjobs = 0; +} + +/* + * pflushall - flush all jobs from list (e.g. at fork()) + */ +static void +pflushall() +{ + register struct process *pp; + + for (pp = proclist.p_next; pp != NULL; pp = pp->p_next) + if (pp->p_pid) + pflush(pp); +} + +/* + * pflush - flag all process structures in the same job as the + * the argument process for deletion. The actual free of the + * space is not done here since pflush is called at interrupt level. + */ +static void +pflush(pp) + register struct process *pp; +{ + register struct process *np; + register int idx; + + if (pp->p_pid == 0) { + (void) fprintf(csherr, "BUG: process flushed twice"); + return; + } + while (pp->p_pid != pp->p_jobid) + pp = pp->p_friends; + pclrcurr(pp); + if (pp == pcurrjob) + pcurrjob = 0; + idx = pp->p_index; + np = pp; + do { + np->p_index = np->p_pid = 0; + np->p_flags &= ~PNEEDNOTE; + } while ((np = np->p_friends) != pp); + if (idx == pmaxindex) { + for (np = proclist.p_next, idx = 0; np; np = np->p_next) + if (np->p_index > idx) + idx = np->p_index; + pmaxindex = idx; + } +} + +/* + * pclrcurr - make sure the given job is not the current or previous job; + * pp MUST be the job leader + */ +static void +pclrcurr(pp) + register struct process *pp; +{ + + if (pp == pcurrent) + if (pprevious != NULL) { + pcurrent = pprevious; + pprevious = pgetcurr(pp); + } + else { + pcurrent = pgetcurr(pp); + pprevious = pgetcurr(pp); + } + else if (pp == pprevious) + pprevious = pgetcurr(pp); +} + +/* +4 here is 1 for '\0', 1 ea for << >& >> */ +static Char command[PMAXLEN + 4]; +static int cmdlen; +static Char *cmdp; + +/* + * palloc - allocate a process structure and fill it up. + * an important assumption is made that the process is running. + */ +void +palloc(pid, t) + int pid; + register struct command *t; +{ + register struct process *pp; + int i; + + pp = (struct process *) xcalloc(1, (size_t) sizeof(struct process)); + pp->p_pid = pid; + pp->p_flags = t->t_dflg & F_AMPERSAND ? PRUNNING : PRUNNING | PFOREGND; + if (t->t_dflg & F_TIME) + pp->p_flags |= PPTIME; + cmdp = command; + cmdlen = 0; + padd(t); + *cmdp++ = 0; + if (t->t_dflg & F_PIPEOUT) { + pp->p_flags |= PPOU; + if (t->t_dflg & F_STDERR) + pp->p_flags |= PERR; + } + pp->p_command = Strsave(command); + if (pcurrjob) { + struct process *fp; + + /* careful here with interrupt level */ + pp->p_cwd = 0; + pp->p_index = pcurrjob->p_index; + pp->p_friends = pcurrjob; + pp->p_jobid = pcurrjob->p_pid; + for (fp = pcurrjob; fp->p_friends != pcurrjob; fp = fp->p_friends) + continue; + fp->p_friends = pp; + } + else { + pcurrjob = pp; + pp->p_jobid = pid; + pp->p_friends = pp; + pp->p_cwd = dcwd; + dcwd->di_count++; + if (pmaxindex < BIGINDEX) + pp->p_index = ++pmaxindex; + else { + struct process *np; + + for (i = 1;; i++) { + for (np = proclist.p_next; np; np = np->p_next) + if (np->p_index == i) + goto tryagain; + pp->p_index = i; + if (i > pmaxindex) + pmaxindex = i; + break; + tryagain:; + } + } + if (pcurrent == NULL) + pcurrent = pp; + else if (pprevious == NULL) + pprevious = pp; + } + pp->p_next = proclist.p_next; + proclist.p_next = pp; + (void) gettimeofday(&pp->p_btime, NULL); +} + +static void +padd(t) + register struct command *t; +{ + Char **argp; + + if (t == 0) + return; + switch (t->t_dtyp) { + + case NODE_PAREN: + pads(STRLparensp); + padd(t->t_dspr); + pads(STRspRparen); + break; + + case NODE_COMMAND: + for (argp = t->t_dcom; *argp; argp++) { + pads(*argp); + if (argp[1]) + pads(STRspace); + } + break; + + case NODE_OR: + case NODE_AND: + case NODE_PIPE: + case NODE_LIST: + padd(t->t_dcar); + switch (t->t_dtyp) { + case NODE_OR: + pads(STRspor2sp); + break; + case NODE_AND: + pads(STRspand2sp); + break; + case NODE_PIPE: + pads(STRsporsp); + break; + case NODE_LIST: + pads(STRsemisp); + break; + } + padd(t->t_dcdr); + return; + } + if ((t->t_dflg & F_PIPEIN) == 0 && t->t_dlef) { + pads((t->t_dflg & F_READ) ? STRspLarrow2sp : STRspLarrowsp); + pads(t->t_dlef); + } + if ((t->t_dflg & F_PIPEOUT) == 0 && t->t_drit) { + pads((t->t_dflg & F_APPEND) ? STRspRarrow2 : STRspRarrow); + if (t->t_dflg & F_STDERR) + pads(STRand); + pads(STRspace); + pads(t->t_drit); + } +} + +static void +pads(cp) + Char *cp; +{ + register int i; + + /* + * Avoid the Quoted Space alias hack! Reported by: + * sam@john-bigboote.ICS.UCI.EDU (Sam Horrocks) + */ + if (cp[0] == STRQNULL[0]) + cp++; + + i = Strlen(cp); + + if (cmdlen >= PMAXLEN) + return; + if (cmdlen + i >= PMAXLEN) { + (void) Strcpy(cmdp, STRsp3dots); + cmdlen = PMAXLEN; + cmdp += 4; + return; + } + (void) Strcpy(cmdp, cp); + cmdp += i; + cmdlen += i; +} + +/* + * psavejob - temporarily save the current job on a one level stack + * so another job can be created. Used for { } in exp6 + * and `` in globbing. + */ +void +psavejob() +{ + + pholdjob = pcurrjob; + pcurrjob = NULL; +} + +/* + * prestjob - opposite of psavejob. This may be missed if we are interrupted + * somewhere, but pendjob cleans up anyway. + */ +void +prestjob() +{ + + pcurrjob = pholdjob; + pholdjob = NULL; +} + +/* + * pendjob - indicate that a job (set of commands) has been completed + * or is about to begin. + */ +void +pendjob() +{ + register struct process *pp, *tp; + + if (pcurrjob && (pcurrjob->p_flags & (PFOREGND | PSTOPPED)) == 0) { + pp = pcurrjob; + while (pp->p_pid != pp->p_jobid) + pp = pp->p_friends; + (void) fprintf(cshout, "[%d]", pp->p_index); + tp = pp; + do { + (void) fprintf(cshout, " %d", pp->p_pid); + pp = pp->p_friends; + } while (pp != tp); + (void) fputc('\n', cshout); + } + pholdjob = pcurrjob = 0; +} + +/* + * pprint - print a job + */ +static int +pprint(pp, flag) + register struct process *pp; + bool flag; +{ + register status, reason; + struct process *tp; + int jobflags, pstatus; + bool hadnl = 1; /* did we just have a newline */ + char *format; + + (void) fpurge(cshout); + + while (pp->p_pid != pp->p_jobid) + pp = pp->p_friends; + if (pp == pp->p_friends && (pp->p_flags & PPTIME)) { + pp->p_flags &= ~PPTIME; + pp->p_flags |= PTIME; + } + tp = pp; + status = reason = -1; + jobflags = 0; + do { + jobflags |= pp->p_flags; + pstatus = pp->p_flags & PALLSTATES; + if (tp != pp && !hadnl && !(flag & FANCY) && + ((pstatus == status && pp->p_reason == reason) || + !(flag & REASON))) { + (void) fputc(' ', cshout); + hadnl = 0; + } + else { + if (tp != pp && !hadnl) { + (void) fputc('\n', cshout); + hadnl = 1; + } + if (flag & NUMBER) { + if (pp == tp) + (void) fprintf(cshout, "[%d]%s %c ", pp->p_index, + pp->p_index < 10 ? " " : "", + pp == pcurrent ? '+' : + (pp == pprevious ? '-' : ' ')); + else + (void) fprintf(cshout, " "); + hadnl = 0; + } + if (flag & FANCY) { + (void) fprintf(cshout, "%5d ", pp->p_pid); + hadnl = 0; + } + if (flag & (REASON | AREASON)) { + if (flag & NAME) + format = "%-23s"; + else + format = "%s"; + if (pstatus == status) + if (pp->p_reason == reason) { + (void) fprintf(cshout, format, ""); + hadnl = 0; + goto prcomd; + } + else + reason = pp->p_reason; + else { + status = pstatus; + reason = pp->p_reason; + } + switch (status) { + + case PRUNNING: + (void) fprintf(cshout, format, "Running "); + hadnl = 0; + break; + + case PINTERRUPTED: + case PSTOPPED: + case PSIGNALED: + /* + * tell what happened to the background job + * From: Michael Schroeder + * <mlschroe@immd4.informatik.uni-erlangen.de> + */ + if ((flag & REASON) + || ((flag & AREASON) + && reason != SIGINT + && (reason != SIGPIPE + || (pp->p_flags & PPOU) == 0))) { + (void) fprintf(cshout, format, + sys_siglist[(unsigned char) + pp->p_reason]); + hadnl = 0; + } + break; + + case PNEXITED: + case PAEXITED: + if (flag & REASON) { + if (pp->p_reason) + (void) fprintf(cshout, "Exit %-18d", pp->p_reason); + else + (void) fprintf(cshout, format, "Done"); + hadnl = 0; + } + break; + + default: + (void) fprintf(csherr, "BUG: status=%-9o", status); + } + } + } +prcomd: + if (flag & NAME) { + (void) fprintf(cshout, "%s", vis_str(pp->p_command)); + if (pp->p_flags & PPOU) + (void) fprintf(cshout, " |"); + if (pp->p_flags & PERR) + (void) fputc('&', cshout); + hadnl = 0; + } + if (flag & (REASON | AREASON) && pp->p_flags & PDUMPED) { + (void) fprintf(cshout, " (core dumped)"); + hadnl = 0; + } + if (tp == pp->p_friends) { + if (flag & AMPERSAND) { + (void) fprintf(cshout, " &"); + hadnl = 0; + } + if (flag & JOBDIR && + !eq(tp->p_cwd->di_name, dcwd->di_name)) { + (void) fprintf(cshout, " (wd: "); + dtildepr(value(STRhome), tp->p_cwd->di_name); + (void) fputc(')', cshout); + hadnl = 0; + } + } + if (pp->p_flags & PPTIME && !(status & (PSTOPPED | PRUNNING))) { + if (!hadnl) + (void) fprintf(cshout, "\n\t"); + prusage(&zru, &pp->p_rusage, &pp->p_etime, + &pp->p_btime); + hadnl = 1; + } + if (tp == pp->p_friends) { + if (!hadnl) { + (void) fputc('\n', cshout); + hadnl = 1; + } + if (flag & SHELLDIR && !eq(tp->p_cwd->di_name, dcwd->di_name)) { + (void) fprintf(cshout, "(wd now: "); + dtildepr(value(STRhome), dcwd->di_name); + (void) fprintf(cshout, ")\n"); + hadnl = 1; + } + } + } while ((pp = pp->p_friends) != tp); + if (jobflags & PTIME && (jobflags & (PSTOPPED | PRUNNING)) == 0) { + if (jobflags & NUMBER) + (void) fprintf(cshout, " "); + ptprint(tp); + hadnl = 1; + } + (void) fflush(cshout); + return (jobflags); +} + +static void +ptprint(tp) + register struct process *tp; +{ + struct timeval tetime, diff; + static struct timeval ztime; + struct rusage ru; + static struct rusage zru; + register struct process *pp = tp; + + ru = zru; + tetime = ztime; + do { + ruadd(&ru, &pp->p_rusage); + tvsub(&diff, &pp->p_etime, &pp->p_btime); + if (timercmp(&diff, &tetime, >)) + tetime = diff; + } while ((pp = pp->p_friends) != tp); + prusage(&zru, &ru, &tetime, &ztime); +} + +/* + * dojobs - print all jobs + */ +void +/*ARGSUSED*/ +dojobs(v, t) + Char **v; + struct command *t; +{ + register struct process *pp; + register int flag = NUMBER | NAME | REASON; + int i; + + if (chkstop) + chkstop = 2; + if (*++v) { + if (v[1] || !eq(*v, STRml)) + stderror(ERR_JOBS); + flag |= FANCY | JOBDIR; + } + for (i = 1; i <= pmaxindex; i++) + for (pp = proclist.p_next; pp; pp = pp->p_next) + if (pp->p_index == i && pp->p_pid == pp->p_jobid) { + pp->p_flags &= ~PNEEDNOTE; + if (!(pprint(pp, flag) & (PRUNNING | PSTOPPED))) + pflush(pp); + break; + } +} + +/* + * dofg - builtin - put the job into the foreground + */ +void +/*ARGSUSED*/ +dofg(v, t) + Char **v; + struct command *t; +{ + register struct process *pp; + + okpcntl(); + ++v; + do { + pp = pfind(*v); + pstart(pp, 1); + pjwait(pp); + } while (*v && *++v); +} + +/* + * %... - builtin - put the job into the foreground + */ +void +/*ARGSUSED*/ +dofg1(v, t) + Char **v; + struct command *t; +{ + register struct process *pp; + + okpcntl(); + pp = pfind(v[0]); + pstart(pp, 1); + pjwait(pp); +} + +/* + * dobg - builtin - put the job into the background + */ +void +/*ARGSUSED*/ +dobg(v, t) + Char **v; + struct command *t; +{ + register struct process *pp; + + okpcntl(); + ++v; + do { + pp = pfind(*v); + pstart(pp, 0); + } while (*v && *++v); +} + +/* + * %... & - builtin - put the job into the background + */ +void +/*ARGSUSED*/ +dobg1(v, t) + Char **v; + struct command *t; +{ + register struct process *pp; + + pp = pfind(v[0]); + pstart(pp, 0); +} + +/* + * dostop - builtin - stop the job + */ +void +/*ARGSUSED*/ +dostop(v, t) + Char **v; + struct command *t; +{ + pkill(++v, SIGSTOP); +} + +/* + * dokill - builtin - superset of kill (1) + */ +void +/*ARGSUSED*/ +dokill(v, t) + Char **v; + struct command *t; +{ + register int signum = SIGTERM; + register char *name; + + v++; + if (v[0] && v[0][0] == '-') { + if (v[0][1] == 'l') { + for (signum = 1; signum < NSIG; signum++) { + (void) fprintf(cshout, "%s ", sys_signame[signum]); + if (signum == NSIG / 2) + (void) fputc('\n', cshout); + } + (void) fputc('\n', cshout); + return; + } + if (Isdigit(v[0][1])) { + signum = atoi(short2str(v[0] + 1)); + if (signum < 0 || signum > NSIG) + stderror(ERR_NAME | ERR_BADSIG); + } + else { + name = short2str(&v[0][1]); + if (!strncasecmp(name, "sig", 3)) + name += 3; + + for (signum = 1; signum < NSIG; signum++) + if (!strcasecmp(sys_signame[signum], name)) + break; + + if (signum == NSIG) { + setname(vis_str(&v[0][1])); + stderror(ERR_NAME | ERR_UNKSIG); + } + } + v++; + } + pkill(v, signum); +} + +static void +pkill(v, signum) + Char **v; + int signum; +{ + register struct process *pp, *np; + register int jobflags = 0; + int pid, err1 = 0; + sigset_t omask; + Char *cp; + + omask = sigmask(SIGCHLD); + if (setintr) + omask |= sigmask(SIGINT); + omask = sigblock(omask) & ~omask; + gflag = 0, tglob(v); + if (gflag) { + v = globall(v); + if (v == 0) + stderror(ERR_NAME | ERR_NOMATCH); + } + else { + v = gargv = saveblk(v); + trim(v); + } + + while (v && (cp = *v)) { + if (*cp == '%') { + np = pp = pfind(cp); + do + jobflags |= np->p_flags; + while ((np = np->p_friends) != pp); + switch (signum) { + + case SIGSTOP: + case SIGTSTP: + case SIGTTIN: + case SIGTTOU: + if ((jobflags & PRUNNING) == 0) { + (void) fprintf(csherr, "%s: Already suspended\n", + vis_str(cp)); + err1++; + goto cont; + } + break; + /* + * suspend a process, kill -CONT %, then type jobs; the shell + * says it is suspended, but it is running; thanks jaap.. + */ + case SIGCONT: + pstart(pp, 0); + goto cont; + } + if (killpg((pid_t) pp->p_jobid, signum) < 0) { + (void) fprintf(csherr, "%s: %s\n", vis_str(cp), + strerror(errno)); + err1++; + } + if (signum == SIGTERM || signum == SIGHUP) + (void) killpg((pid_t) pp->p_jobid, SIGCONT); + } + else if (!(Isdigit(*cp) || *cp == '-')) + stderror(ERR_NAME | ERR_JOBARGS); + else { + pid = atoi(short2str(cp)); + if (kill((pid_t) pid, signum) < 0) { + (void) fprintf(csherr, "%d: %s\n", pid, strerror(errno)); + err1++; + goto cont; + } + if (signum == SIGTERM || signum == SIGHUP) + (void) kill((pid_t) pid, SIGCONT); + } +cont: + v++; + } + if (gargv) + blkfree(gargv), gargv = 0; + (void) sigsetmask(omask); + if (err1) + stderror(ERR_SILENT); +} + +/* + * pstart - start the job in foreground/background + */ +void +pstart(pp, foregnd) + register struct process *pp; + int foregnd; +{ + register struct process *np; + sigset_t omask; + long jobflags = 0; + + omask = sigblock(sigmask(SIGCHLD)); + np = pp; + do { + jobflags |= np->p_flags; + if (np->p_flags & (PRUNNING | PSTOPPED)) { + np->p_flags |= PRUNNING; + np->p_flags &= ~PSTOPPED; + if (foregnd) + np->p_flags |= PFOREGND; + else + np->p_flags &= ~PFOREGND; + } + } while ((np = np->p_friends) != pp); + if (!foregnd) + pclrcurr(pp); + (void) pprint(pp, foregnd ? NAME | JOBDIR : NUMBER | NAME | AMPERSAND); + if (foregnd) + (void) tcsetpgrp(FSHTTY, pp->p_jobid); + if (jobflags & PSTOPPED) + (void) killpg((pid_t) pp->p_jobid, SIGCONT); + (void) sigsetmask(omask); +} + +void +panystop(neednl) + bool neednl; +{ + register struct process *pp; + + chkstop = 2; + for (pp = proclist.p_next; pp; pp = pp->p_next) + if (pp->p_flags & PSTOPPED) + stderror(ERR_STOPPED, neednl ? "\n" : ""); +} + +struct process * +pfind(cp) + Char *cp; +{ + register struct process *pp, *np; + + if (cp == 0 || cp[1] == 0 || eq(cp, STRcent2) || eq(cp, STRcentplus)) { + if (pcurrent == NULL) + stderror(ERR_NAME | ERR_JOBCUR); + return (pcurrent); + } + if (eq(cp, STRcentminus) || eq(cp, STRcenthash)) { + if (pprevious == NULL) + stderror(ERR_NAME | ERR_JOBPREV); + return (pprevious); + } + if (Isdigit(cp[1])) { + int idx = atoi(short2str(cp + 1)); + + for (pp = proclist.p_next; pp; pp = pp->p_next) + if (pp->p_index == idx && pp->p_pid == pp->p_jobid) + return (pp); + stderror(ERR_NAME | ERR_NOSUCHJOB); + } + np = NULL; + for (pp = proclist.p_next; pp; pp = pp->p_next) + if (pp->p_pid == pp->p_jobid) { + if (cp[1] == '?') { + register Char *dp; + + for (dp = pp->p_command; *dp; dp++) { + if (*dp != cp[2]) + continue; + if (prefix(cp + 2, dp)) + goto match; + } + } + else if (prefix(cp + 1, pp->p_command)) { + match: + if (np) + stderror(ERR_NAME | ERR_AMBIG); + np = pp; + } + } + if (np) + return (np); + stderror(ERR_NAME | cp[1] == '?' ? ERR_JOBPAT : ERR_NOSUCHJOB); + /* NOTREACHED */ + return (0); +} + + +/* + * pgetcurr - find most recent job that is not pp, preferably stopped + */ +static struct process * +pgetcurr(pp) + register struct process *pp; +{ + register struct process *np; + register struct process *xp = NULL; + + for (np = proclist.p_next; np; np = np->p_next) + if (np != pcurrent && np != pp && np->p_pid && + np->p_pid == np->p_jobid) { + if (np->p_flags & PSTOPPED) + return (np); + if (xp == NULL) + xp = np; + } + return (xp); +} + +/* + * donotify - flag the job so as to report termination asynchronously + */ +void +/*ARGSUSED*/ +donotify(v, t) + Char **v; + struct command *t; +{ + register struct process *pp; + + pp = pfind(*++v); + pp->p_flags |= PNOTIFY; +} + +/* + * Do the fork and whatever should be done in the child side that + * should not be done if we are not forking at all (like for simple builtin's) + * Also do everything that needs any signals fiddled with in the parent side + * + * Wanttty tells whether process and/or tty pgrps are to be manipulated: + * -1: leave tty alone; inherit pgrp from parent + * 0: already have tty; manipulate process pgrps only + * 1: want to claim tty; manipulate process and tty pgrps + * It is usually just the value of tpgrp. + */ + +int +pfork(t, wanttty) + struct command *t; /* command we are forking for */ + int wanttty; +{ + register int pid; + bool ignint = 0; + int pgrp; + sigset_t omask; + + /* + * A child will be uninterruptible only under very special conditions. + * Remember that the semantics of '&' is implemented by disconnecting the + * process from the tty so signals do not need to ignored just for '&'. + * Thus signals are set to default action for children unless: we have had + * an "onintr -" (then specifically ignored) we are not playing with + * signals (inherit action) + */ + if (setintr) + ignint = (tpgrp == -1 && (t->t_dflg & F_NOINTERRUPT)) + || (gointr && eq(gointr, STRminus)); + /* + * Check for maximum nesting of 16 processes to avoid Forking loops + */ + if (child == 16) + stderror(ERR_NESTING, 16); + /* + * Hold SIGCHLD until we have the process installed in our table. + */ + omask = sigblock(sigmask(SIGCHLD)); + while ((pid = fork()) < 0) + if (setintr == 0) + (void) sleep(FORKSLEEP); + else { + (void) sigsetmask(omask); + stderror(ERR_NOPROC); + } + if (pid == 0) { + settimes(); + pgrp = pcurrjob ? pcurrjob->p_jobid : getpid(); + pflushall(); + pcurrjob = NULL; + child++; + if (setintr) { + setintr = 0; /* until I think otherwise */ + /* + * Children just get blown away on SIGINT, SIGQUIT unless "onintr + * -" seen. + */ + (void) signal(SIGINT, ignint ? SIG_IGN : SIG_DFL); + (void) signal(SIGQUIT, ignint ? SIG_IGN : SIG_DFL); + if (wanttty >= 0) { + /* make stoppable */ + (void) signal(SIGTSTP, SIG_DFL); + (void) signal(SIGTTIN, SIG_DFL); + (void) signal(SIGTTOU, SIG_DFL); + } + (void) signal(SIGTERM, parterm); + } + else if (tpgrp == -1 && (t->t_dflg & F_NOINTERRUPT)) { + (void) signal(SIGINT, SIG_IGN); + (void) signal(SIGQUIT, SIG_IGN); + } + pgetty(wanttty, pgrp); + /* + * Nohup and nice apply only to NODE_COMMAND's but it would be nice + * (?!?) if you could say "nohup (foo;bar)" Then the parser would have + * to know about nice/nohup/time + */ + if (t->t_dflg & F_NOHUP) + (void) signal(SIGHUP, SIG_IGN); + if (t->t_dflg & F_NICE) + (void) setpriority(PRIO_PROCESS, 0, t->t_nice); + } + else { + if (wanttty >= 0) + (void) setpgid(pid, pcurrjob ? pcurrjob->p_jobid : pid); + palloc(pid, t); + (void) sigsetmask(omask); + } + + return (pid); +} + +static void +okpcntl() +{ + if (tpgrp == -1) + stderror(ERR_JOBCONTROL); + if (tpgrp == 0) + stderror(ERR_JOBCTRLSUB); +} + +/* + * if we don't have vfork(), things can still go in the wrong order + * resulting in the famous 'Stopped (tty output)'. But some systems + * don't permit the setpgid() call, (these are more recent secure + * systems such as ibm's aix). Then we'd rather print an error message + * than hang the shell! + * I am open to suggestions how to fix that. + */ +void +pgetty(wanttty, pgrp) + int wanttty, pgrp; +{ + sigset_t omask = 0; + + /* + * christos: I am blocking the tty signals till I've set things + * correctly.... + */ + if (wanttty > 0) + omask = sigblock(sigmask(SIGTSTP)|sigmask(SIGTTIN)|sigmask(SIGTTOU)); + /* + * From: Michael Schroeder <mlschroe@immd4.informatik.uni-erlangen.de> + * Don't check for tpgrp >= 0 so even non-interactive shells give + * background jobs process groups Same for the comparison in the other part + * of the #ifdef + */ + if (wanttty >= 0) + if (setpgid(0, pgrp) == -1) { + (void) fprintf(csherr, "csh: setpgid error.\n"); + xexit(0); + } + + if (wanttty > 0) { + (void) tcsetpgrp(FSHTTY, pgrp); + (void) sigsetmask(omask); + } + + if (tpgrp > 0) + tpgrp = 0; /* gave tty away */ +} diff --git a/bin/csh/proc.h b/bin/csh/proc.h new file mode 100644 index 0000000..fcbf96a --- /dev/null +++ b/bin/csh/proc.h @@ -0,0 +1,101 @@ +/*- + * 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. + * + * @(#)proc.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * Structure for each process the shell knows about: + * allocated and filled by pcreate. + * flushed by pflush; freeing always happens at top level + * so the interrupt level has less to worry about. + * processes are related to "friends" when in a pipeline; + * p_friends links makes a circular list of such jobs + */ +struct process { + struct process *p_next; /* next in global "proclist" */ + struct process *p_friends; /* next in job list (or self) */ + struct directory *p_cwd; /* cwd of the job (only in head) */ + short unsigned p_flags; /* various job status flags */ + char p_reason; /* reason for entering this state */ + int p_index; /* shorthand job index */ + int p_pid; + int p_jobid; /* pid of job leader */ + /* if a job is stopped/background p_jobid gives its pgrp */ + struct timeval p_btime; /* begin time */ + struct timeval p_etime; /* end time */ + struct rusage p_rusage; + Char *p_command; /* first PMAXLEN chars of command */ +}; + +/* flag values for p_flags */ +#define PRUNNING (1<<0) /* running */ +#define PSTOPPED (1<<1) /* stopped */ +#define PNEXITED (1<<2) /* normally exited */ +#define PAEXITED (1<<3) /* abnormally exited */ +#define PSIGNALED (1<<4) /* terminated by a signal != SIGINT */ + +#define PALLSTATES (PRUNNING|PSTOPPED|PNEXITED|PAEXITED|PSIGNALED|PINTERRUPTED) +#define PNOTIFY (1<<5) /* notify async when done */ +#define PTIME (1<<6) /* job times should be printed */ +#define PAWAITED (1<<7) /* top level is waiting for it */ +#define PFOREGND (1<<8) /* started in shells pgrp */ +#define PDUMPED (1<<9) /* process dumped core */ +#define PERR (1<<10) /* diagnostic output also piped out */ +#define PPOU (1<<11) /* piped output */ +#define PREPORTED (1<<12) /* status has been reported */ +#define PINTERRUPTED (1<<13) /* job stopped via interrupt signal */ +#define PPTIME (1<<14) /* time individual process */ +#define PNEEDNOTE (1<<15) /* notify as soon as practical */ + +#define PMAXLEN 80 + +/* defines for arguments to pprint */ +#define NUMBER 01 +#define NAME 02 +#define REASON 04 +#define AMPERSAND 010 +#define FANCY 020 +#define SHELLDIR 040 /* print shell's dir if not the same */ +#define JOBDIR 0100 /* print job's dir if not the same */ +#define AREASON 0200 + +struct process proclist; /* list head of all processes */ +bool pnoprocesses; /* pchild found nothing to wait for */ + +struct process *pholdjob; /* one level stack of current jobs */ + +struct process *pcurrjob; /* current job */ +struct process *pcurrent; /* current job in table */ +struct process *pprevious; /* previous job in table */ + +int pmaxindex; /* current maximum job index */ diff --git a/bin/csh/sem.c b/bin/csh/sem.c new file mode 100644 index 0000000..584d3fb --- /dev/null +++ b/bin/csh/sem.c @@ -0,0 +1,645 @@ +/*- + * 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[] = "@(#)sem.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#if __STDC__ +# include <stdarg.h> +#else +# include <varargs.h> +#endif + +#include "csh.h" +#include "proc.h" +#include "extern.h" + +static void vffree __P((int)); +static Char *splicepipe __P((struct command *t, Char *)); +static void doio __P((struct command *t, int *, int *)); +static void chkclob __P((char *)); + +void +execute(t, wanttty, pipein, pipeout) + register struct command *t; + int wanttty, *pipein, *pipeout; +{ + bool forked = 0; + struct biltins *bifunc; + int pid = 0; + int pv[2]; + + static sigset_t csigmask; + + static sigset_t ocsigmask; + static int onosigchld = 0; + static int nosigchld = 0; + + UNREGISTER(forked); + UNREGISTER(bifunc); + UNREGISTER(wanttty); + + if (t == 0) + return; + + if (t->t_dflg & F_AMPERSAND) + wanttty = 0; + switch (t->t_dtyp) { + + case NODE_COMMAND: + if ((t->t_dcom[0][0] & (QUOTE | TRIM)) == QUOTE) + (void) Strcpy(t->t_dcom[0], t->t_dcom[0] + 1); + if ((t->t_dflg & F_REPEAT) == 0) + Dfix(t); /* $ " ' \ */ + if (t->t_dcom[0] == 0) + return; + /* fall into... */ + + case NODE_PAREN: + if (t->t_dflg & F_PIPEOUT) + mypipe(pipeout); + /* + * Must do << early so parent will know where input pointer should be. + * If noexec then this is all we do. + */ + if (t->t_dflg & F_READ) { + (void) close(0); + heredoc(t->t_dlef); + if (noexec) + (void) close(0); + } + + set(STRstatus, Strsave(STR0)); + + /* + * This mess is the necessary kludge to handle the prefix builtins: + * nice, nohup, time. These commands can also be used by themselves, + * and this is not handled here. This will also work when loops are + * parsed. + */ + while (t->t_dtyp == NODE_COMMAND) + if (eq(t->t_dcom[0], STRnice)) + if (t->t_dcom[1]) + if (strchr("+-", t->t_dcom[1][0])) + if (t->t_dcom[2]) { + setname("nice"); + t->t_nice = + getn(t->t_dcom[1]); + lshift(t->t_dcom, 2); + t->t_dflg |= F_NICE; + } + else + break; + else { + t->t_nice = 4; + lshift(t->t_dcom, 1); + t->t_dflg |= F_NICE; + } + else + break; + else if (eq(t->t_dcom[0], STRnohup)) + if (t->t_dcom[1]) { + t->t_dflg |= F_NOHUP; + lshift(t->t_dcom, 1); + } + else + break; + else if (eq(t->t_dcom[0], STRtime)) + if (t->t_dcom[1]) { + t->t_dflg |= F_TIME; + lshift(t->t_dcom, 1); + } + else + break; + else + break; + + /* is it a command */ + if (t->t_dtyp == NODE_COMMAND) { + /* + * Check if we have a builtin function and remember which one. + */ + bifunc = isbfunc(t); + if (noexec) { + /* + * Continue for builtins that are part of the scripting language + */ + if (bifunc->bfunct != dobreak && bifunc->bfunct != docontin && + bifunc->bfunct != doelse && bifunc->bfunct != doend && + bifunc->bfunct != doforeach && bifunc->bfunct != dogoto && + bifunc->bfunct != doif && bifunc->bfunct != dorepeat && + bifunc->bfunct != doswbrk && bifunc->bfunct != doswitch && + bifunc->bfunct != dowhile && bifunc->bfunct != dozip) + break; + } + } + else { /* not a command */ + bifunc = NULL; + if (noexec) + break; + } + + /* + * We fork only if we are timed, or are not the end of a parenthesized + * list and not a simple builtin function. Simple meaning one that is + * not pipedout, niced, nohupped, or &'d. It would be nice(?) to not + * fork in some of these cases. + */ + /* + * Prevent forking cd, pushd, popd, chdir cause this will cause the + * shell not to change dir! + */ + if (bifunc && (bifunc->bfunct == dochngd || + bifunc->bfunct == dopushd || + bifunc->bfunct == dopopd)) + t->t_dflg &= ~(F_NICE); + if (((t->t_dflg & F_TIME) || ((t->t_dflg & F_NOFORK) == 0 && + (!bifunc || t->t_dflg & + (F_PIPEOUT | F_AMPERSAND | F_NICE | F_NOHUP)))) || + /* + * We have to fork for eval too. + */ + (bifunc && (t->t_dflg & (F_PIPEIN | F_PIPEOUT)) != 0 && + bifunc->bfunct == doeval)) + if (t->t_dtyp == NODE_PAREN || + t->t_dflg & (F_REPEAT | F_AMPERSAND) || bifunc) { + forked++; + /* + * We need to block SIGCHLD here, so that if the process does + * not die before we can set the process group + */ + if (wanttty >= 0 && !nosigchld) { + csigmask = sigblock(sigmask(SIGCHLD)); + nosigchld = 1; + } + + pid = pfork(t, wanttty); + if (pid == 0 && nosigchld) { + (void) sigsetmask(csigmask); + nosigchld = 0; + } + else if (pid != 0 && (t->t_dflg & F_AMPERSAND)) + backpid = pid; + + } + else { + int ochild, osetintr, ohaderr, odidfds; + int oSHIN, oSHOUT, oSHERR, oOLDSTD, otpgrp; + sigset_t omask; + + /* + * Prepare for the vfork by saving everything that the child + * corrupts before it exec's. Note that in some signal + * implementations which keep the signal info in user space + * (e.g. Sun's) it will also be necessary to save and restore + * the current sigvec's for the signals the child touches + * before it exec's. + */ + if (wanttty >= 0 && !nosigchld && !noexec) { + csigmask = sigblock(sigmask(SIGCHLD)); + nosigchld = 1; + } + omask = sigblock(sigmask(SIGCHLD) | sigmask(SIGINT)); + ochild = child; + osetintr = setintr; + ohaderr = haderr; + odidfds = didfds; + oSHIN = SHIN; + oSHOUT = SHOUT; + oSHERR = SHERR; + oOLDSTD = OLDSTD; + otpgrp = tpgrp; + ocsigmask = csigmask; + onosigchld = nosigchld; + Vsav = Vdp = 0; + Vexpath = 0; + Vt = 0; + pid = vfork(); + + if (pid < 0) { + (void) sigsetmask(omask); + stderror(ERR_NOPROC); + } + forked++; + if (pid) { /* parent */ + child = ochild; + setintr = osetintr; + haderr = ohaderr; + didfds = odidfds; + SHIN = oSHIN; + SHOUT = oSHOUT; + SHERR = oSHERR; + OLDSTD = oOLDSTD; + tpgrp = otpgrp; + csigmask = ocsigmask; + nosigchld = onosigchld; + + xfree((ptr_t) Vsav); + Vsav = 0; + xfree((ptr_t) Vdp); + Vdp = 0; + xfree((ptr_t) Vexpath); + Vexpath = 0; + blkfree((Char **) Vt); + Vt = 0; + /* this is from pfork() */ + palloc(pid, t); + (void) sigsetmask(omask); + } + else { /* child */ + /* this is from pfork() */ + int pgrp; + bool ignint = 0; + + if (nosigchld) { + (void) sigsetmask(csigmask); + nosigchld = 0; + } + + if (setintr) + ignint = + (tpgrp == -1 && + (t->t_dflg & F_NOINTERRUPT)) + || (gointr && eq(gointr, STRminus)); + pgrp = pcurrjob ? pcurrjob->p_jobid : getpid(); + child++; + if (setintr) { + setintr = 0; + if (ignint) { + (void) signal(SIGINT, SIG_IGN); + (void) signal(SIGQUIT, SIG_IGN); + } + else { + (void) signal(SIGINT, vffree); + (void) signal(SIGQUIT, SIG_DFL); + } + + if (wanttty >= 0) { + (void) signal(SIGTSTP, SIG_DFL); + (void) signal(SIGTTIN, SIG_DFL); + (void) signal(SIGTTOU, SIG_DFL); + } + + (void) signal(SIGTERM, parterm); + } + else if (tpgrp == -1 && + (t->t_dflg & F_NOINTERRUPT)) { + (void) signal(SIGINT, SIG_IGN); + (void) signal(SIGQUIT, SIG_IGN); + } + + pgetty(wanttty, pgrp); + if (t->t_dflg & F_NOHUP) + (void) signal(SIGHUP, SIG_IGN); + if (t->t_dflg & F_NICE) + (void) setpriority(PRIO_PROCESS, 0, t->t_nice); + } + + } + if (pid != 0) { + /* + * It would be better if we could wait for the whole job when we + * knew the last process had been started. Pwait, in fact, does + * wait for the whole job anyway, but this test doesn't really + * express our intentions. + */ + if (didfds == 0 && t->t_dflg & F_PIPEIN) { + (void) close(pipein[0]); + (void) close(pipein[1]); + } + if ((t->t_dflg & F_PIPEOUT) == 0) { + if (nosigchld) { + (void) sigsetmask(csigmask); + nosigchld = 0; + } + if ((t->t_dflg & F_AMPERSAND) == 0) + pwait(); + } + break; + } + doio(t, pipein, pipeout); + if (t->t_dflg & F_PIPEOUT) { + (void) close(pipeout[0]); + (void) close(pipeout[1]); + } + /* + * Perform a builtin function. If we are not forked, arrange for + * possible stopping + */ + if (bifunc) { + func(t, bifunc); + if (forked) + exitstat(); + break; + } + if (t->t_dtyp != NODE_PAREN) { + doexec(NULL, t); + /* NOTREACHED */ + } + /* + * For () commands must put new 0,1,2 in FSH* and recurse + */ + OLDSTD = dcopy(0, FOLDSTD); + SHOUT = dcopy(1, FSHOUT); + SHERR = dcopy(2, FSHERR); + (void) close(SHIN); + SHIN = -1; + didfds = 0; + wanttty = -1; + t->t_dspr->t_dflg |= t->t_dflg & F_NOINTERRUPT; + execute(t->t_dspr, wanttty, NULL, NULL); + exitstat(); + + case NODE_PIPE: + t->t_dcar->t_dflg |= F_PIPEOUT | + (t->t_dflg & (F_PIPEIN | F_AMPERSAND | F_STDERR | F_NOINTERRUPT)); + execute(t->t_dcar, wanttty, pipein, pv); + t->t_dcdr->t_dflg |= F_PIPEIN | (t->t_dflg & + (F_PIPEOUT | F_AMPERSAND | F_NOFORK | F_NOINTERRUPT)); + if (wanttty > 0) + wanttty = 0; /* got tty already */ + execute(t->t_dcdr, wanttty, pv, pipeout); + break; + + case NODE_LIST: + if (t->t_dcar) { + t->t_dcar->t_dflg |= t->t_dflg & F_NOINTERRUPT; + execute(t->t_dcar, wanttty, NULL, NULL); + /* + * In strange case of A&B make a new job after A + */ + if (t->t_dcar->t_dflg & F_AMPERSAND && t->t_dcdr && + (t->t_dcdr->t_dflg & F_AMPERSAND) == 0) + pendjob(); + } + if (t->t_dcdr) { + t->t_dcdr->t_dflg |= t->t_dflg & + (F_NOFORK | F_NOINTERRUPT); + execute(t->t_dcdr, wanttty, NULL, NULL); + } + break; + + case NODE_OR: + case NODE_AND: + if (t->t_dcar) { + t->t_dcar->t_dflg |= t->t_dflg & F_NOINTERRUPT; + execute(t->t_dcar, wanttty, NULL, NULL); + if ((getn(value(STRstatus)) == 0) != + (t->t_dtyp == NODE_AND)) + return; + } + if (t->t_dcdr) { + t->t_dcdr->t_dflg |= t->t_dflg & + (F_NOFORK | F_NOINTERRUPT); + execute(t->t_dcdr, wanttty, NULL, NULL); + } + break; + } + /* + * Fall through for all breaks from switch + * + * If there will be no more executions of this command, flush all file + * descriptors. Places that turn on the F_REPEAT bit are responsible for + * doing donefds after the last re-execution + */ + if (didfds && !(t->t_dflg & F_REPEAT)) + donefds(); +} + +static void +vffree(i) +int i; +{ + register Char **v; + + if ((v = gargv) != NULL) { + gargv = 0; + xfree((ptr_t) v); + } + if ((v = pargv) != NULL) { + pargv = 0; + xfree((ptr_t) v); + } + _exit(i); +} + +/* + * Expand and glob the words after an i/o redirection. + * If more than one word is generated, then update the command vector. + * + * This is done differently in all the shells: + * 1. in the bourne shell and ksh globbing is not performed + * 2. Bash/csh say ambiguous + * 3. zsh does i/o to/from all the files + * 4. itcsh concatenates the words. + * + * I don't know what is best to do. I think that Ambiguous is better + * than restructuring the command vector, because the user can get + * unexpected results. In any case, the command vector restructuring + * code is present and the user can choose it by setting noambiguous + */ +static Char * +splicepipe(t, cp) + register struct command *t; + Char *cp; /* word after < or > */ +{ + Char *blk[2]; + + if (adrof(STRnoambiguous)) { + Char **pv; + + blk[0] = Dfix1(cp); /* expand $ */ + blk[1] = NULL; + + gflag = 0, tglob(blk); + if (gflag) { + pv = globall(blk); + if (pv == NULL) { + setname(vis_str(blk[0])); + xfree((ptr_t) blk[0]); + stderror(ERR_NAME | ERR_NOMATCH); + } + gargv = NULL; + if (pv[1] != NULL) { /* we need to fix the command vector */ + Char **av = blkspl(t->t_dcom, &pv[1]); + xfree((ptr_t) t->t_dcom); + t->t_dcom = av; + } + xfree((ptr_t) blk[0]); + blk[0] = pv[0]; + xfree((ptr_t) pv); + } + } + else { + blk[0] = globone(blk[1] = Dfix1(cp), G_ERROR); + xfree((ptr_t) blk[1]); + } + return(blk[0]); +} + +/* + * Perform io redirection. + * We may or maynot be forked here. + */ +static void +doio(t, pipein, pipeout) + register struct command *t; + int *pipein, *pipeout; +{ + register int fd; + register Char *cp; + register int flags = t->t_dflg; + + if (didfds || (flags & F_REPEAT)) + return; + if ((flags & F_READ) == 0) {/* F_READ already done */ + if (t->t_dlef) { + char tmp[MAXPATHLEN+1]; + + /* + * so < /dev/std{in,out,err} work + */ + (void) dcopy(SHIN, 0); + (void) dcopy(SHOUT, 1); + (void) dcopy(SHERR, 2); + cp = splicepipe(t, t->t_dlef); + (void) strncpy(tmp, short2str(cp), MAXPATHLEN); + tmp[MAXPATHLEN] = '\0'; + xfree((ptr_t) cp); + if ((fd = open(tmp, O_RDONLY)) < 0) + stderror(ERR_SYSTEM, tmp, strerror(errno)); + (void) dmove(fd, 0); + } + else if (flags & F_PIPEIN) { + (void) close(0); + (void) dup(pipein[0]); + (void) close(pipein[0]); + (void) close(pipein[1]); + } + else if ((flags & F_NOINTERRUPT) && tpgrp == -1) { + (void) close(0); + (void) open(_PATH_DEVNULL, O_RDONLY); + } + else { + (void) close(0); + (void) dup(OLDSTD); + (void) ioctl(0, FIONCLEX, NULL); + } + } + if (t->t_drit) { + char tmp[MAXPATHLEN+1]; + + cp = splicepipe(t, t->t_drit); + (void) strncpy(tmp, short2str(cp), MAXPATHLEN); + tmp[MAXPATHLEN] = '\0'; + xfree((ptr_t) cp); + /* + * so > /dev/std{out,err} work + */ + (void) dcopy(SHOUT, 1); + (void) dcopy(SHERR, 2); + if ((flags & F_APPEND) && +#ifdef O_APPEND + (fd = open(tmp, O_WRONLY | O_APPEND)) >= 0); +#else + (fd = open(tmp, O_WRONLY)) >= 0) + (void) lseek(1, (off_t) 0, L_XTND); +#endif + else { + if (!(flags & F_OVERWRITE) && adrof(STRnoclobber)) { + if (flags & F_APPEND) + stderror(ERR_SYSTEM, tmp, strerror(errno)); + chkclob(tmp); + } + if ((fd = creat(tmp, 0666)) < 0) + stderror(ERR_SYSTEM, tmp, strerror(errno)); + } + (void) dmove(fd, 1); + } + else if (flags & F_PIPEOUT) { + (void) close(1); + (void) dup(pipeout[1]); + } + else { + (void) close(1); + (void) dup(SHOUT); + (void) ioctl(1, FIONCLEX, NULL); + } + + (void) close(2); + if (flags & F_STDERR) { + (void) dup(1); + } + else { + (void) dup(SHERR); + (void) ioctl(2, FIONCLEX, NULL); + } + didfds = 1; +} + +void +mypipe(pv) + register int *pv; +{ + + if (pipe(pv) < 0) + goto oops; + pv[0] = dmove(pv[0], -1); + pv[1] = dmove(pv[1], -1); + if (pv[0] >= 0 && pv[1] >= 0) + return; +oops: + stderror(ERR_PIPE); +} + +static void +chkclob(cp) + register char *cp; +{ + struct stat stb; + + if (stat(cp, &stb) < 0) + return; + if (S_ISCHR(stb.st_mode)) + return; + stderror(ERR_EXISTS, cp); +} diff --git a/bin/csh/set.c b/bin/csh/set.c new file mode 100644 index 0000000..b7b7ff1 --- /dev/null +++ b/bin/csh/set.c @@ -0,0 +1,844 @@ +/*- + * 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[] = "@(#)set.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <stdlib.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" + +static Char *getinx __P((Char *, int *)); +static void asx __P((Char *, int, Char *)); +static struct varent + *getvx __P((Char *, int)); +static Char *xset __P((Char *, Char ***)); +static Char *operate __P((int, Char *, Char *)); +static void putn1 __P((int)); +static struct varent + *madrof __P((Char *, struct varent *)); +static void unsetv1 __P((struct varent *)); +static void exportpath __P((Char **)); +static void balance __P((struct varent *, int, int)); + + +/* + * C Shell + */ + +void +/*ARGSUSED*/ +doset(v, t) + Char **v; + struct command *t; +{ + register Char *p; + Char *vp, op; + Char **vecp; + bool hadsub; + int subscr; + + v++; + p = *v++; + if (p == 0) { + prvars(); + return; + } + do { + hadsub = 0; + vp = p; + if (letter(*p)) + for (; alnum(*p); p++) + continue; + if (vp == p || !letter(*vp)) + stderror(ERR_NAME | ERR_VARBEGIN); + if ((p - vp) > MAXVARLEN) { + stderror(ERR_NAME | ERR_VARTOOLONG); + return; + } + if (*p == '[') { + hadsub++; + p = getinx(p, &subscr); + } + if ((op = *p) != '\0') { + *p++ = 0; + if (*p == 0 && *v && **v == '(') + p = *v++; + } + else if (*v && eq(*v, STRequal)) { + op = '=', v++; + if (*v) + p = *v++; + } + if (op && op != '=') + stderror(ERR_NAME | ERR_SYNTAX); + if (eq(p, STRLparen)) { + register Char **e = v; + + if (hadsub) + stderror(ERR_NAME | ERR_SYNTAX); + for (;;) { + if (!*e) + stderror(ERR_NAME | ERR_MISSING, ')'); + if (**e == ')') + break; + e++; + } + p = *e; + *e = 0; + vecp = saveblk(v); + set1(vp, vecp, &shvhed); + *e = p; + v = e + 1; + } + else if (hadsub) + asx(vp, subscr, Strsave(p)); + else + set(vp, Strsave(p)); + if (eq(vp, STRpath)) { + exportpath(adrof(STRpath)->vec); + dohash(NULL, NULL); + } + else if (eq(vp, STRhistchars)) { + register Char *pn = value(STRhistchars); + + HIST = *pn++; + HISTSUB = *pn; + } + else if (eq(vp, STRuser)) { + Setenv(STRUSER, value(vp)); + Setenv(STRLOGNAME, value(vp)); + } + else if (eq(vp, STRwordchars)) { + word_chars = value(vp); + } + else if (eq(vp, STRterm)) + Setenv(STRTERM, value(vp)); + else if (eq(vp, STRhome)) { + register Char *cp; + + cp = Strsave(value(vp)); /* get the old value back */ + + /* + * convert to cononical pathname (possibly resolving symlinks) + */ + cp = dcanon(cp, cp); + + set(vp, Strsave(cp)); /* have to save the new val */ + + /* and now mirror home with HOME */ + Setenv(STRHOME, cp); + /* fix directory stack for new tilde home */ + dtilde(); + xfree((ptr_t) cp); + } +#ifdef FILEC + else if (eq(vp, STRfilec)) + filec = 1; +#endif + } while ((p = *v++) != NULL); +} + +static Char * +getinx(cp, ip) + register Char *cp; + register int *ip; +{ + + *ip = 0; + *cp++ = 0; + while (*cp && Isdigit(*cp)) + *ip = *ip * 10 + *cp++ - '0'; + if (*cp++ != ']') + stderror(ERR_NAME | ERR_SUBSCRIPT); + return (cp); +} + +static void +asx(vp, subscr, p) + Char *vp; + int subscr; + Char *p; +{ + register struct varent *v = getvx(vp, subscr); + + xfree((ptr_t) v->vec[subscr - 1]); + v->vec[subscr - 1] = globone(p, G_APPEND); +} + +static struct varent * +getvx(vp, subscr) + Char *vp; + int subscr; +{ + register struct varent *v = adrof(vp); + + if (v == 0) + udvar(vp); + if (subscr < 1 || subscr > blklen(v->vec)) + stderror(ERR_NAME | ERR_RANGE); + return (v); +} + +void +/*ARGSUSED*/ +dolet(v, t) + Char **v; + struct command *t; +{ + register Char *p; + Char *vp, c, op; + bool hadsub; + int subscr; + + v++; + p = *v++; + if (p == 0) { + prvars(); + return; + } + do { + hadsub = 0; + vp = p; + if (letter(*p)) + for (; alnum(*p); p++) + continue; + if (vp == p || !letter(*vp)) + stderror(ERR_NAME | ERR_VARBEGIN); + if ((p - vp) > MAXVARLEN) + stderror(ERR_NAME | ERR_VARTOOLONG); + if (*p == '[') { + hadsub++; + p = getinx(p, &subscr); + } + if (*p == 0 && *v) + p = *v++; + if ((op = *p) != '\0') + *p++ = 0; + else + stderror(ERR_NAME | ERR_ASSIGN); + + if (*p == '\0' && *v == NULL) + stderror(ERR_NAME | ERR_ASSIGN); + + vp = Strsave(vp); + if (op == '=') { + c = '='; + p = xset(p, &v); + } + else { + c = *p++; + if (any("+-", c)) { + if (c != op || *p) + stderror(ERR_NAME | ERR_UNKNOWNOP); + p = Strsave(STR1); + } + else { + if (any("<>", op)) { + if (c != op) + stderror(ERR_NAME | ERR_UNKNOWNOP); + c = *p++; + stderror(ERR_NAME | ERR_SYNTAX); + } + if (c != '=') + stderror(ERR_NAME | ERR_UNKNOWNOP); + p = xset(p, &v); + } + } + if (op == '=') + if (hadsub) + asx(vp, subscr, p); + else + set(vp, p); + else if (hadsub) { + struct varent *gv = getvx(vp, subscr); + + asx(vp, subscr, operate(op, gv->vec[subscr - 1], p)); + } + else + set(vp, operate(op, value(vp), p)); + if (eq(vp, STRpath)) { + exportpath(adrof(STRpath)->vec); + dohash(NULL, NULL); + } + xfree((ptr_t) vp); + if (c != '=') + xfree((ptr_t) p); + } while ((p = *v++) != NULL); +} + +static Char * +xset(cp, vp) + Char *cp, ***vp; +{ + register Char *dp; + + if (*cp) { + dp = Strsave(cp); + --(*vp); + xfree((ptr_t) ** vp); + **vp = dp; + } + return (putn(expr(vp))); +} + +static Char * +operate(op, vp, p) + int op; + Char *vp, *p; +{ + Char opr[2]; + Char *vec[5]; + register Char **v = vec; + Char **vecp = v; + register int i; + + if (op != '=') { + if (*vp) + *v++ = vp; + opr[0] = op; + opr[1] = 0; + *v++ = opr; + if (op == '<' || op == '>') + *v++ = opr; + } + *v++ = p; + *v++ = 0; + i = expr(&vecp); + if (*vecp) + stderror(ERR_NAME | ERR_EXPRESSION); + return (putn(i)); +} + +static Char *putp; + +Char * +putn(n) + register int n; +{ + int num; + static Char number[15]; + + putp = number; + if (n < 0) { + n = -n; + *putp++ = '-'; + } + num = 2; /* confuse lint */ + if (sizeof(int) == num && ((unsigned int) n) == 0x8000) { + *putp++ = '3'; + n = 2768; +#ifdef pdp11 + } +#else + } + else { + num = 4; /* confuse lint */ + if (sizeof(int) == num && ((unsigned int) n) == 0x80000000) { + *putp++ = '2'; + n = 147483648; + } + } +#endif + putn1(n); + *putp = 0; + return (Strsave(number)); +} + +static void +putn1(n) + register int n; +{ + if (n > 9) + putn1(n / 10); + *putp++ = n % 10 + '0'; +} + +int +getn(cp) + register Char *cp; +{ + register int n; + int sign; + + sign = 0; + if (cp[0] == '+' && cp[1]) + cp++; + if (*cp == '-') { + sign++; + cp++; + if (!Isdigit(*cp)) + stderror(ERR_NAME | ERR_BADNUM); + } + n = 0; + while (Isdigit(*cp)) + n = n * 10 + *cp++ - '0'; + if (*cp) + stderror(ERR_NAME | ERR_BADNUM); + return (sign ? -n : n); +} + +Char * +value1(var, head) + Char *var; + struct varent *head; +{ + register struct varent *vp; + + vp = adrof1(var, head); + return (vp == 0 || vp->vec[0] == 0 ? STRNULL : vp->vec[0]); +} + +static struct varent * +madrof(pat, vp) + Char *pat; + register struct varent *vp; +{ + register struct varent *vp1; + + for (; vp; vp = vp->v_right) { + if (vp->v_left && (vp1 = madrof(pat, vp->v_left))) + return vp1; + if (Gmatch(vp->v_name, pat)) + return vp; + } + return vp; +} + +struct varent * +adrof1(name, v) + register Char *name; + register struct varent *v; +{ + register cmp; + + v = v->v_left; + while (v && ((cmp = *name - *v->v_name) || + (cmp = Strcmp(name, v->v_name)))) + if (cmp < 0) + v = v->v_left; + else + v = v->v_right; + return v; +} + +/* + * The caller is responsible for putting value in a safe place + */ +void +set(var, val) + Char *var, *val; +{ + register Char **vec = (Char **) xmalloc((size_t) (2 * sizeof(Char **))); + + vec[0] = val; + vec[1] = 0; + set1(var, vec, &shvhed); +} + +void +set1(var, vec, head) + Char *var, **vec; + struct varent *head; +{ + register Char **oldv = vec; + + gflag = 0; + tglob(oldv); + if (gflag) { + vec = globall(oldv); + if (vec == 0) { + blkfree(oldv); + stderror(ERR_NAME | ERR_NOMATCH); + return; + } + blkfree(oldv); + gargv = 0; + } + setq(var, vec, head); +} + + +void +setq(name, vec, p) + Char *name, **vec; + register struct varent *p; +{ + register struct varent *c; + register f; + + f = 0; /* tree hangs off the header's left link */ + while ((c = p->v_link[f]) != NULL) { + if ((f = *name - *c->v_name) == 0 && + (f = Strcmp(name, c->v_name)) == 0) { + blkfree(c->vec); + goto found; + } + p = c; + f = f > 0; + } + p->v_link[f] = c = (struct varent *) xmalloc((size_t) sizeof(struct varent)); + c->v_name = Strsave(name); + c->v_bal = 0; + c->v_left = c->v_right = 0; + c->v_parent = p; + balance(p, f, 0); +found: + trim(c->vec = vec); +} + +void +/*ARGSUSED*/ +unset(v, t) + Char **v; + struct command *t; +{ + unset1(v, &shvhed); +#ifdef FILEC + if (adrof(STRfilec) == 0) + filec = 0; +#endif + if (adrof(STRhistchars) == 0) { + HIST = '!'; + HISTSUB = '^'; + } + if (adrof(STRwordchars) == 0) + word_chars = STR_WORD_CHARS; +} + +void +unset1(v, head) + register Char *v[]; + struct varent *head; +{ + register struct varent *vp; + register int cnt; + + while (*++v) { + cnt = 0; + while ((vp = madrof(*v, head->v_left)) != NULL) + unsetv1(vp), cnt++; + if (cnt == 0) + setname(vis_str(*v)); + } +} + +void +unsetv(var) + Char *var; +{ + register struct varent *vp; + + if ((vp = adrof1(var, &shvhed)) == 0) + udvar(var); + unsetv1(vp); +} + +static void +unsetv1(p) + register struct varent *p; +{ + register struct varent *c, *pp; + register f; + + /* + * Free associated memory first to avoid complications. + */ + blkfree(p->vec); + xfree((ptr_t) p->v_name); + /* + * If p is missing one child, then we can move the other into where p is. + * Otherwise, we find the predecessor of p, which is guaranteed to have no + * right child, copy it into p, and move it's left child into it. + */ + if (p->v_right == 0) + c = p->v_left; + else if (p->v_left == 0) + c = p->v_right; + else { + for (c = p->v_left; c->v_right; c = c->v_right) + continue; + p->v_name = c->v_name; + p->vec = c->vec; + p = c; + c = p->v_left; + } + /* + * Move c into where p is. + */ + pp = p->v_parent; + f = pp->v_right == p; + if ((pp->v_link[f] = c) != NULL) + c->v_parent = pp; + /* + * Free the deleted node, and rebalance. + */ + xfree((ptr_t) p); + balance(pp, f, 1); +} + +void +setNS(cp) + Char *cp; +{ + set(cp, Strsave(STRNULL)); +} + +void +/*ARGSUSED*/ +shift(v, t) + Char **v; + struct command *t; +{ + register struct varent *argv; + register Char *name; + + v++; + name = *v; + if (name == 0) + name = STRargv; + else + (void) strip(name); + argv = adrof(name); + if (argv == 0) + udvar(name); + if (argv->vec[0] == 0) + stderror(ERR_NAME | ERR_NOMORE); + lshift(argv->vec, 1); +} + +static void +exportpath(val) + Char **val; +{ + Char exppath[BUFSIZ]; + + exppath[0] = 0; + if (val) + while (*val) { + if (Strlen(*val) + Strlen(exppath) + 2 > BUFSIZ) { + (void) fprintf(csherr, + "Warning: ridiculously long PATH truncated\n"); + break; + } + if ((**val != '/' || **val == '\0') && (euid == 0 || uid == 0)) + (void) fprintf(csherr, + "Warning: exported path contains relative components.\n"); + (void) Strcat(exppath, *val++); + if (*val == 0 || eq(*val, STRRparen)) + break; + (void) Strcat(exppath, STRcolon); + } + Setenv(STRPATH, exppath); +} + +#ifndef lint + /* + * Lint thinks these have null effect + */ + /* macros to do single rotations on node p */ +#define rright(p) (\ + t = (p)->v_left,\ + (t)->v_parent = (p)->v_parent,\ + ((p)->v_left = t->v_right) ? (t->v_right->v_parent = (p)) : 0,\ + (t->v_right = (p))->v_parent = t,\ + (p) = t) +#define rleft(p) (\ + t = (p)->v_right,\ + (t)->v_parent = (p)->v_parent,\ + ((p)->v_right = t->v_left) ? (t->v_left->v_parent = (p)) : 0,\ + (t->v_left = (p))->v_parent = t,\ + (p) = t) +#else +struct varent * +rleft(p) + struct varent *p; +{ + return (p); +} +struct varent * +rright(p) + struct varent *p; +{ + return (p); +} + +#endif /* ! lint */ + + +/* + * Rebalance a tree, starting at p and up. + * F == 0 means we've come from p's left child. + * D == 1 means we've just done a delete, otherwise an insert. + */ +static void +balance(p, f, d) + register struct varent *p; + register int f, d; +{ + register struct varent *pp; + +#ifndef lint + register struct varent *t; /* used by the rotate macros */ + +#endif + register ff; + + /* + * Ok, from here on, p is the node we're operating on; pp is it's parent; f + * is the branch of p from which we have come; ff is the branch of pp which + * is p. + */ + for (; (pp = p->v_parent) != NULL; p = pp, f = ff) { + ff = pp->v_right == p; + if (f ^ d) { /* right heavy */ + switch (p->v_bal) { + case -1: /* was left heavy */ + p->v_bal = 0; + break; + case 0: /* was balanced */ + p->v_bal = 1; + break; + case 1: /* was already right heavy */ + switch (p->v_right->v_bal) { + case 1: /* sigle rotate */ + pp->v_link[ff] = rleft(p); + p->v_left->v_bal = 0; + p->v_bal = 0; + break; + case 0: /* single rotate */ + pp->v_link[ff] = rleft(p); + p->v_left->v_bal = 1; + p->v_bal = -1; + break; + case -1: /* double rotate */ + (void) rright(p->v_right); + pp->v_link[ff] = rleft(p); + p->v_left->v_bal = + p->v_bal < 1 ? 0 : -1; + p->v_right->v_bal = + p->v_bal > -1 ? 0 : 1; + p->v_bal = 0; + break; + } + break; + } + } + else { /* left heavy */ + switch (p->v_bal) { + case 1: /* was right heavy */ + p->v_bal = 0; + break; + case 0: /* was balanced */ + p->v_bal = -1; + break; + case -1: /* was already left heavy */ + switch (p->v_left->v_bal) { + case -1: /* single rotate */ + pp->v_link[ff] = rright(p); + p->v_right->v_bal = 0; + p->v_bal = 0; + break; + case 0: /* signle rotate */ + pp->v_link[ff] = rright(p); + p->v_right->v_bal = -1; + p->v_bal = 1; + break; + case 1: /* double rotate */ + (void) rleft(p->v_left); + pp->v_link[ff] = rright(p); + p->v_left->v_bal = + p->v_bal < 1 ? 0 : -1; + p->v_right->v_bal = + p->v_bal > -1 ? 0 : 1; + p->v_bal = 0; + break; + } + break; + } + } + /* + * If from insert, then we terminate when p is balanced. If from + * delete, then we terminate when p is unbalanced. + */ + if ((p->v_bal == 0) ^ d) + break; + } +} + +void +plist(p) + register struct varent *p; +{ + register struct varent *c; + register len; + + if (setintr) + (void) sigsetmask(sigblock((sigset_t) 0) & ~sigmask(SIGINT)); + + for (;;) { + while (p->v_left) + p = p->v_left; +x: + if (p->v_parent == 0) /* is it the header? */ + return; + len = blklen(p->vec); + (void) fprintf(cshout, "%s\t", short2str(p->v_name)); + if (len != 1) + (void) fputc('(', cshout); + blkpr(cshout, p->vec); + if (len != 1) + (void) fputc(')', cshout); + (void) fputc('\n', cshout); + if (p->v_right) { + p = p->v_right; + continue; + } + do { + c = p; + p = p->v_parent; + } while (p->v_right == c); + goto x; + } +} diff --git a/bin/csh/str.c b/bin/csh/str.c new file mode 100644 index 0000000..15fe874 --- /dev/null +++ b/bin/csh/str.c @@ -0,0 +1,467 @@ +/*- + * 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[] = "@(#)str.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#define MALLOC_INCR 128 + +/* + * tc.str.c: Short string package + * This has been a lesson of how to write buggy code! + */ + +#include <sys/types.h> +#if __STDC__ +# include <stdarg.h> +#else +# include <varargs.h> +#endif +#include <vis.h> + +#include "csh.h" +#include "extern.h" + +#ifdef SHORT_STRINGS + +Char ** +blk2short(src) + register char **src; +{ + size_t n; + register Char **sdst, **dst; + + /* + * Count + */ + for (n = 0; src[n] != NULL; n++) + continue; + sdst = dst = (Char **) xmalloc((size_t) ((n + 1) * sizeof(Char *))); + + for (; *src != NULL; src++) + *dst++ = SAVE(*src); + *dst = NULL; + return (sdst); +} + +char ** +short2blk(src) + register Char **src; +{ + size_t n; + register char **sdst, **dst; + + /* + * Count + */ + for (n = 0; src[n] != NULL; n++) + continue; + sdst = dst = (char **) xmalloc((size_t) ((n + 1) * sizeof(char *))); + + for (; *src != NULL; src++) + *dst++ = strsave(short2str(*src)); + *dst = NULL; + return (sdst); +} + +Char * +str2short(src) + register char *src; +{ + static Char *sdst; + static size_t dstsize = 0; + register Char *dst, *edst; + + if (src == NULL) + return (NULL); + + if (sdst == (NULL)) { + dstsize = MALLOC_INCR; + sdst = (Char *) xmalloc((size_t) dstsize * sizeof(Char)); + } + + dst = sdst; + edst = &dst[dstsize]; + while (*src) { + *dst++ = (Char) ((unsigned char) *src++); + if (dst == edst) { + dstsize += MALLOC_INCR; + sdst = (Char *) xrealloc((ptr_t) sdst, + (size_t) dstsize * sizeof(Char)); + edst = &sdst[dstsize]; + dst = &edst[-MALLOC_INCR]; + } + } + *dst = 0; + return (sdst); +} + +char * +short2str(src) + register Char *src; +{ + static char *sdst = NULL; + static size_t dstsize = 0; + register char *dst, *edst; + + if (src == NULL) + return (NULL); + + if (sdst == NULL) { + dstsize = MALLOC_INCR; + sdst = (char *) xmalloc((size_t) dstsize * sizeof(char)); + } + dst = sdst; + edst = &dst[dstsize]; + while (*src) { + *dst++ = (char) *src++; + if (dst == edst) { + dstsize += MALLOC_INCR; + sdst = (char *) xrealloc((ptr_t) sdst, + (size_t) dstsize * sizeof(char)); + edst = &sdst[dstsize]; + dst = &edst[-MALLOC_INCR]; + } + } + *dst = 0; + return (sdst); +} + +Char * +s_strcpy(dst, src) + register Char *dst, *src; +{ + register Char *sdst; + + sdst = dst; + while ((*dst++ = *src++) != '\0') + continue; + return (sdst); +} + +Char * +s_strncpy(dst, src, n) + register Char *dst, *src; + register size_t n; +{ + register Char *sdst; + + if (n == 0) + return(dst); + + sdst = dst; + do + if ((*dst++ = *src++) == '\0') { + while (--n != 0) + *dst++ = '\0'; + return(sdst); + } + while (--n != 0); + return (sdst); +} + +Char * +s_strcat(dst, src) + register Char *dst, *src; +{ + register short *sdst; + + sdst = dst; + while (*dst++) + continue; + --dst; + while ((*dst++ = *src++) != '\0') + continue; + return (sdst); +} + +#ifdef NOTUSED +Char * +s_strncat(dst, src, n) + register Char *dst, *src; + register size_t n; +{ + register Char *sdst; + + if (n == 0) + return (dst); + + sdst = dst; + + while (*dst++) + continue; + --dst; + + do + if ((*dst++ = *src++) == '\0') + return(sdst); + while (--n != 0) + continue; + + *dst = '\0'; + return (sdst); +} + +#endif + +Char * +s_strchr(str, ch) + register Char *str; + int ch; +{ + do + if (*str == ch) + return (str); + while (*str++); + return (NULL); +} + +Char * +s_strrchr(str, ch) + register Char *str; + int ch; +{ + register Char *rstr; + + rstr = NULL; + do + if (*str == ch) + rstr = str; + while (*str++); + return (rstr); +} + +size_t +s_strlen(str) + register Char *str; +{ + register size_t n; + + for (n = 0; *str++; n++) + continue; + return (n); +} + +int +s_strcmp(str1, str2) + register Char *str1, *str2; +{ + for (; *str1 && *str1 == *str2; str1++, str2++) + continue; + /* + * The following case analysis is necessary so that characters which look + * negative collate low against normal characters but high against the + * end-of-string NUL. + */ + if (*str1 == '\0' && *str2 == '\0') + return (0); + else if (*str1 == '\0') + return (-1); + else if (*str2 == '\0') + return (1); + else + return (*str1 - *str2); +} + +int +s_strncmp(str1, str2, n) + register Char *str1, *str2; + register size_t n; +{ + if (n == 0) + return (0); + do { + if (*str1 != *str2) { + /* + * The following case analysis is necessary so that characters + * which look negative collate low against normal characters + * but high against the end-of-string NUL. + */ + if (*str1 == '\0') + return (-1); + else if (*str2 == '\0') + return (1); + else + return (*str1 - *str2); + break; + } + if (*str1 == '\0') + return(0); + str1++, str2++; + } while (--n != 0); + return(0); +} + +Char * +s_strsave(s) + register Char *s; +{ + Char *n; + register Char *p; + + if (s == 0) + s = STRNULL; + for (p = s; *p++;) + continue; + n = p = (Char *) xmalloc((size_t) ((p - s) * sizeof(Char))); + while ((*p++ = *s++) != '\0') + continue; + return (n); +} + +Char * +s_strspl(cp, dp) + Char *cp, *dp; +{ + Char *ep; + register Char *p, *q; + + if (!cp) + cp = STRNULL; + if (!dp) + dp = STRNULL; + for (p = cp; *p++;) + continue; + for (q = dp; *q++;) + continue; + ep = (Char *) xmalloc((size_t) + (((p - cp) + (q - dp) - 1) * sizeof(Char))); + for (p = ep, q = cp; (*p++ = *q++) != '\0';) + continue; + for (p--, q = dp; (*p++ = *q++) != '\0';) + continue; + return (ep); +} + +Char * +s_strend(cp) + register Char *cp; +{ + if (!cp) + return (cp); + while (*cp) + cp++; + return (cp); +} + +Char * +s_strstr(s, t) + register Char *s, *t; +{ + do { + register Char *ss = s; + register Char *tt = t; + + do + if (*tt == '\0') + return (s); + while (*ss++ == *tt++); + } while (*s++ != '\0'); + return (NULL); +} +#endif /* SHORT_STRINGS */ + +char * +short2qstr(src) + register Char *src; +{ + static char *sdst = NULL; + static size_t dstsize = 0; + register char *dst, *edst; + + if (src == NULL) + return (NULL); + + if (sdst == NULL) { + dstsize = MALLOC_INCR; + sdst = (char *) xmalloc((size_t) dstsize * sizeof(char)); + } + dst = sdst; + edst = &dst[dstsize]; + while (*src) { + if (*src & QUOTE) { + *dst++ = '\\'; + if (dst == edst) { + dstsize += MALLOC_INCR; + sdst = (char *) xrealloc((ptr_t) sdst, + (size_t) dstsize * sizeof(char)); + edst = &sdst[dstsize]; + dst = &edst[-MALLOC_INCR]; + } + } + *dst++ = (char) *src++; + if (dst == edst) { + dstsize += MALLOC_INCR; + sdst = (char *) xrealloc((ptr_t) sdst, + (size_t) dstsize * sizeof(char)); + edst = &sdst[dstsize]; + dst = &edst[-MALLOC_INCR]; + } + } + *dst = 0; + return (sdst); +} + +/* + * XXX: Should we worry about QUOTE'd chars? + */ +char * +vis_str(cp) + Char *cp; +{ + static char *sdst = NULL; + static size_t dstsize = 0; + size_t n; + Char *dp; + + if (cp == NULL) + return (NULL); + + for (dp = cp; *dp++;) + continue; + n = ((dp - cp) << 2) + 1; /* 4 times + NULL */ + if (dstsize < n) { + sdst = (char *) (dstsize ? + xrealloc(sdst, (size_t) n * sizeof(char)) : + xmalloc((size_t) n * sizeof(char))); + dstsize = n; + } + /* + * 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) strvis(sdst, short2str(cp), VIS_NOSLASH); + return (sdst); +} + diff --git a/bin/csh/time.c b/bin/csh/time.c new file mode 100644 index 0000000..32113ac --- /dev/null +++ b/bin/csh/time.c @@ -0,0 +1,316 @@ +/*- + * 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[] = "@(#)time.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include <sys/types.h> +#if __STDC__ +# include <stdarg.h> +#else +# include <varargs.h> +#endif + +#include "csh.h" +#include "extern.h" + +/* + * C Shell - routines handling process timing and niceing + */ +static void pdeltat __P((struct timeval *, struct timeval *)); + +void +settimes() +{ + struct rusage ruch; + + (void) gettimeofday(&time0, NULL); + (void) getrusage(RUSAGE_SELF, &ru0); + (void) getrusage(RUSAGE_CHILDREN, &ruch); + ruadd(&ru0, &ruch); +} + +/* + * dotime is only called if it is truly a builtin function and not a + * prefix to another command + */ +void +/*ARGSUSED*/ +dotime(v, t) + Char **v; + struct command *t; +{ + struct timeval timedol; + struct rusage ru1, ruch; + + (void) getrusage(RUSAGE_SELF, &ru1); + (void) getrusage(RUSAGE_CHILDREN, &ruch); + ruadd(&ru1, &ruch); + (void) gettimeofday(&timedol, NULL); + prusage(&ru0, &ru1, &timedol, &time0); +} + +/* + * donice is only called when it on the line by itself or with a +- value + */ +void +/*ARGSUSED*/ +donice(v, t) + Char **v; + struct command *t; +{ + register Char *cp; + int nval = 0; + + v++, cp = *v++; + if (cp == 0) + nval = 4; + else if (*v == 0 && any("+-", cp[0])) + nval = getn(cp); + (void) setpriority(PRIO_PROCESS, 0, nval); +} + +void +ruadd(ru, ru2) + register struct rusage *ru, *ru2; +{ + tvadd(&ru->ru_utime, &ru2->ru_utime); + tvadd(&ru->ru_stime, &ru2->ru_stime); + if (ru2->ru_maxrss > ru->ru_maxrss) + ru->ru_maxrss = ru2->ru_maxrss; + + ru->ru_ixrss += ru2->ru_ixrss; + ru->ru_idrss += ru2->ru_idrss; + ru->ru_isrss += ru2->ru_isrss; + ru->ru_minflt += ru2->ru_minflt; + ru->ru_majflt += ru2->ru_majflt; + ru->ru_nswap += ru2->ru_nswap; + ru->ru_inblock += ru2->ru_inblock; + ru->ru_oublock += ru2->ru_oublock; + ru->ru_msgsnd += ru2->ru_msgsnd; + ru->ru_msgrcv += ru2->ru_msgrcv; + ru->ru_nsignals += ru2->ru_nsignals; + ru->ru_nvcsw += ru2->ru_nvcsw; + ru->ru_nivcsw += ru2->ru_nivcsw; +} + +void +prusage(r0, r1, e, b) + register struct rusage *r0, *r1; + struct timeval *e, *b; +{ + register time_t t = + (r1->ru_utime.tv_sec - r0->ru_utime.tv_sec) * 100 + + (r1->ru_utime.tv_usec - r0->ru_utime.tv_usec) / 10000 + + (r1->ru_stime.tv_sec - r0->ru_stime.tv_sec) * 100 + + (r1->ru_stime.tv_usec - r0->ru_stime.tv_usec) / 10000; + register char *cp; + register long i; + register struct varent *vp = adrof(STRtime); + + int ms = + (e->tv_sec - b->tv_sec) * 100 + (e->tv_usec - b->tv_usec) / 10000; + + cp = "%Uu %Ss %E %P %X+%Dk %I+%Oio %Fpf+%Ww"; + + if (vp && vp->vec[0] && vp->vec[1]) + cp = short2str(vp->vec[1]); + + for (; *cp; cp++) + if (*cp != '%') + (void) fputc(*cp, cshout); + else if (cp[1]) + switch (*++cp) { + + case 'U': /* user CPU time used */ + pdeltat(&r1->ru_utime, &r0->ru_utime); + break; + + case 'S': /* system CPU time used */ + pdeltat(&r1->ru_stime, &r0->ru_stime); + break; + + case 'E': /* elapsed (wall-clock) time */ + pcsecs((long) ms); + break; + + case 'P': /* percent time spent running */ + /* check if it did not run at all */ + i = (ms == 0) ? 0 : (t * 1000 / ms); + /* nn.n% */ + (void) fprintf(cshout, "%ld.%01ld%%", i / 10, i % 10); + break; + + case 'W': /* number of swaps */ + i = r1->ru_nswap - r0->ru_nswap; + (void) fprintf(cshout, "%ld", i); + break; + + case 'X': /* (average) shared text size */ + (void) fprintf(cshout, "%ld", t == 0 ? 0L : + (r1->ru_ixrss - r0->ru_ixrss) / t); + break; + + case 'D': /* (average) unshared data size */ + (void) fprintf(cshout, "%ld", t == 0 ? 0L : + (r1->ru_idrss + r1->ru_isrss - + (r0->ru_idrss + r0->ru_isrss)) / t); + break; + + case 'K': /* (average) total data memory used */ + (void) fprintf(cshout, "%ld", t == 0 ? 0L : + ((r1->ru_ixrss + r1->ru_isrss + r1->ru_idrss) - + (r0->ru_ixrss + r0->ru_idrss + r0->ru_isrss)) / t); + break; + + case 'M': /* max. Resident Set Size */ + (void) fprintf(cshout, "%ld", r1->ru_maxrss / 2L); + break; + + case 'F': /* page faults */ + (void) fprintf(cshout, "%ld", r1->ru_majflt - r0->ru_majflt); + break; + + case 'R': /* page reclaims */ + (void) fprintf(cshout, "%ld", r1->ru_minflt - r0->ru_minflt); + break; + + case 'I': /* FS blocks in */ + (void) fprintf(cshout, "%ld", r1->ru_inblock - r0->ru_inblock); + break; + + case 'O': /* FS blocks out */ + (void) fprintf(cshout, "%ld", r1->ru_oublock - r0->ru_oublock); + break; + + case 'r': /* socket messages recieved */ + (void) fprintf(cshout, "%ld", r1->ru_msgrcv - r0->ru_msgrcv); + break; + + case 's': /* socket messages sent */ + (void) fprintf(cshout, "%ld", r1->ru_msgsnd - r0->ru_msgsnd); + break; + + case 'k': /* number of signals recieved */ + (void) fprintf(cshout, "%ld", r1->ru_nsignals-r0->ru_nsignals); + break; + + case 'w': /* num. voluntary context switches (waits) */ + (void) fprintf(cshout, "%ld", r1->ru_nvcsw - r0->ru_nvcsw); + break; + + case 'c': /* num. involuntary context switches */ + (void) fprintf(cshout, "%ld", r1->ru_nivcsw - r0->ru_nivcsw); + break; + } + (void) fputc('\n', cshout); +} + +static void +pdeltat(t1, t0) + struct timeval *t1, *t0; +{ + struct timeval td; + + tvsub(&td, t1, t0); + (void) fprintf(cshout, "%d.%01d", td.tv_sec, td.tv_usec / 100000); +} + +void +tvadd(tsum, t0) + struct timeval *tsum, *t0; +{ + + tsum->tv_sec += t0->tv_sec; + tsum->tv_usec += t0->tv_usec; + if (tsum->tv_usec > 1000000) + tsum->tv_sec++, tsum->tv_usec -= 1000000; +} + +void +tvsub(tdiff, t1, t0) + struct timeval *tdiff, *t1, *t0; +{ + + tdiff->tv_sec = t1->tv_sec - t0->tv_sec; + tdiff->tv_usec = t1->tv_usec - t0->tv_usec; + if (tdiff->tv_usec < 0) + tdiff->tv_sec--, tdiff->tv_usec += 1000000; +} + +#define P2DIG(i) (void) fprintf(cshout, "%d%d", (i) / 10, (i) % 10) + +void +psecs(l) + long l; +{ + register int i; + + i = l / 3600; + if (i) { + (void) fprintf(cshout, "%d:", i); + i = l % 3600; + P2DIG(i / 60); + goto minsec; + } + i = l; + (void) fprintf(cshout, "%d", i / 60); +minsec: + i %= 60; + (void) fputc(':', cshout); + P2DIG(i); +} + +void +pcsecs(l) /* PWP: print mm:ss.dd, l is in sec*100 */ + long l; +{ + register int i; + + i = l / 360000; + if (i) { + (void) fprintf(cshout, "%d:", i); + i = (l % 360000) / 100; + P2DIG(i / 60); + goto minsec; + } + i = l / 100; + (void) fprintf(cshout, "%d", i / 60); +minsec: + i %= 60; + (void) fputc(':', cshout); + P2DIG(i); + (void) fputc('.', cshout); + P2DIG((int) (l % 100)); +} diff --git a/bin/date/Makefile b/bin/date/Makefile new file mode 100644 index 0000000..7dfc15e --- /dev/null +++ b/bin/date/Makefile @@ -0,0 +1,8 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 + +PROG= date +SRCS= date.c netdate.c +DPADD= ${LIBUTIL} +LDADD= -lutil + +.include <bsd.prog.mk> diff --git a/bin/date/date.1 b/bin/date/date.1 new file mode 100644 index 0000000..0eb6c7b --- /dev/null +++ b/bin/date/date.1 @@ -0,0 +1,230 @@ +.\" Copyright (c) 1980, 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. +.\" +.\" @(#)date.1 8.2 (Berkeley) 11/17/93 +.\" +.Dd November 17, 1993 +.Dt DATE 1 +.Os +.Sh NAME +.Nm date +.Nd display or set date and time +.Sh SYNOPSIS +.Nm date +.Op Fl d Ar dst +.Op Fl r Ar seconds +.Op Fl t Ar minutes_west +.Op Fl nu +.Op Cm + Ns Ar format +.Op [yy[mm[dd[hh]]]]mm[\&.ss] +.Sh DESCRIPTION +.Nm Date +displays the current date and time when invoked without arguments. +Providing arguments will format the date and time in a user-defined +way or set the date. +Only the superuser may set the date. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl d +Set the kernel's value for daylight savings time. +If +.Ar dst +is non-zero, future calls +to +.Xr gettimeofday 2 +will return a non-zero +.Ql tz_dsttime . +.It Fl n +The utility +.Xr timed 8 +is used to synchronize the clocks on groups of machines. +By default, if +.Xr timed +is running, +.Nm date +will set the time on all of the machines in the local group. +The +.Fl n +option stops +.Nm date +from setting the time for other than the current machine. +.It Fl r +Print out the date and time in +.Ar seconds +from the Epoch. +.It Fl t +Set the kernel's value for minutes west of +.Tn GMT . +.Ar Minutes_west +specifies the number of minutes returned in +.Ql tz_minuteswest +by future calls to +.Xr gettimeofday 2 . +.It Fl u +Display or set the date in +.Tn UCT +(universal) time. +.El +.Pp +An operand with a leading plus (``+'') sign signals a user-defined format +string which specifies the format in which to display the date and time. +The format string may contain any of the conversion specifications described +in the +.Xr strftime 3 +manual page, as well as any arbitrary text. +The format string for the default display is: +.Bd -literal -offset indent +``%a %b %e %H:%M:%S %Z n''. +.Ed +.Pp +If an operand does not have a leading plus sign, it is interpreted as +a value for setting the system's notion of the current date and time. +The canonical representation for setting the date and time is: +.Pp +.Bl -tag -width Ds -compact -offset indent +.It Ar yy +Year in abbreviated form (.e.g 89 for 1989). +.It Ar mm +Numeric month. +A number from 1 to 12. +.It Ar dd +Day, a number from 1 to 31. +.It Ar hh +Hour, a number from 0 to 23. +.It Ar mm +Minutes, a number from 0 to 59. +.It Ar .ss +Seconds, a number from 0 to 61 (59 plus a a maximum of two leap seconds). +.El +.Pp +Everything but the minutes is optional. +.Pp +Time changes for Daylight Saving and Standard time and leap seconds +and years are handled automatically. +.Sh EXAMPLES +The command: +.Bd -literal -offset indent +date ``+DATE: %m/%d/%y%nTIME: %H:%M:0n'' +.Ed +.Pp +will display: +.Bd -literal -offset indent +DATE: 11/21/87 +TIME: 13:36:16 +.Ed +.Pp +The command: +.Bd -literal -offset indent +date 8506131627 +.Ed +.Pp +sets the date to +.Dq Li "June 13, 1985, 4:27 PM" . +.Pp +The command: +.Bd -literal -offset indent +date 1432 +.Ed +.Pp +sets the time to +.Li "2:32 PM" , +without modifying the date. +.Sh ENVIRONMENTAL VARIABLES +The following environment variables affect the execution of +.Nm date : +.Bl -tag -width TZ +.It Ev TZ +The timezone to use when displaying dates. +See +.Xr environ 7 +for more information. +.El +.Sh FILES +.Bl -tag -width /var/log/messages -compact +.It Pa /var/log/wtmp +A record of date resets and time changes. +.It Pa /var/log/messages +A record of the user setting the time. +.El +.Sh SEE ALSO +.Xr gettimeofday 2 , +.Xr strftime 3 , +.Xr utmp 5 , +.Xr timed 8 +.Rs +.%T "TSP: The Time Synchronization Protocol for UNIX 4.3BSD" +.%A R. Gusella +.%A S. Zatti +.Re +.Sh DIAGNOSTICS +Exit status is 0 on success, 1 if unable to set the date, and 2 +if able to set the local date, but unable to set it globally. +.Pp +Occasionally, when +.Xr timed +synchronizes the time on many hosts, the setting of a new time value may +require more than a few seconds. +On these occasions, +.Nm date +prints: +.Ql Network time being set . +The message +.Ql Communication error with timed +occurs when the communication +between +.Nm date +and +.Xr timed +fails. +.Sh BUGS +The system attempts to keep the date in a format closely compatible +with +.Tn VMS . +.Tn VMS , +however, uses local time (rather than +.Tn GMT ) +and does not understand daylight-savings time. +Thus, if you use both +.Tn UNIX +and +.Tn VMS , +.Tn VMS +will be running on +.Tn GMT . +.Sh STANDARDS +The +.Nm date +command is expected to be compatible with +.St -p1003.2 . diff --git a/bin/date/date.c b/bin/date/date.c new file mode 100644 index 0000000..3563bac --- /dev/null +++ b/bin/date/date.c @@ -0,0 +1,237 @@ +/* + * Copyright (c) 1985, 1987, 1988, 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) 1985, 1987, 1988, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)date.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> + +#include <ctype.h> +#include <err.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +#include "extern.h" + +time_t tval; +int retval, nflag; + +static void setthetime __P((char *)); +static void badformat __P((void)); +static void usage __P((void)); + +int logwtmp __P((char *, char *, char *)); + +int +main(argc, argv) + int argc; + char **argv; +{ + extern int optind; + extern char *optarg; + struct timezone tz; + int ch, rflag; + char *format, buf[1024]; + + tz.tz_dsttime = tz.tz_minuteswest = 0; + rflag = 0; + while ((ch = getopt(argc, argv, "d:nr:ut:")) != EOF) + switch((char)ch) { + case 'd': /* daylight savings time */ + tz.tz_dsttime = atoi(optarg) ? 1 : 0; + break; + case 'n': /* don't set network */ + nflag = 1; + break; + case 'r': /* user specified seconds */ + rflag = 1; + tval = atol(optarg); + break; + case 'u': /* do everything in GMT */ + (void)setenv("TZ", "GMT0", 1); + break; + case 't': /* minutes west of GMT */ + /* error check; don't allow "PST" */ + if (isdigit(*optarg)) { + tz.tz_minuteswest = atoi(optarg); + break; + } + /* FALLTHROUGH */ + default: + usage(); + } + argc -= optind; + argv += optind; + + /* + * If -d or -t, set the timezone or daylight savings time; this + * doesn't belong here, there kernel should not know about either. + */ + if ((tz.tz_minuteswest || tz.tz_dsttime) && + settimeofday(NULL, &tz)) + err(1, "settimeofday"); + + if (!rflag && time(&tval) == -1) + err(1, "time"); + + format = "%a %b %e %H:%M:%S %Z %Y\n"; + + /* allow the operands in any order */ + if (*argv && **argv == '+') { + format = *argv + 1; + ++argv; + } + + if (*argv) { + setthetime(*argv); + ++argv; + } + + if (*argv && **argv == '+') + format = *argv + 1; + + (void)strftime(buf, sizeof(buf), format, localtime(&tval)); + (void)printf("%s", buf); + exit(retval); +} + +#define ATOI2(ar) ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2; +void +setthetime(p) + register char *p; +{ + register struct tm *lt; + struct timeval tv; + char *dot, *t; + + for (t = p, dot = NULL; *t; ++t) { + if (isdigit(*t)) + continue; + if (*t == '.' && dot == NULL) { + dot = t; + continue; + } + badformat(); + } + + lt = localtime(&tval); + + if (dot != NULL) { /* .ss */ + *dot++ = '\0'; + if (strlen(dot) != 2) + badformat(); + lt->tm_sec = ATOI2(dot); + if (lt->tm_sec > 61) + badformat(); + } else + lt->tm_sec = 0; + + switch (strlen(p)) { + case 10: /* yy */ + lt->tm_year = ATOI2(p); + if (lt->tm_year < 69) /* hack for 2000 ;-} */ + lt->tm_year += 100; + /* FALLTHROUGH */ + case 8: /* mm */ + lt->tm_mon = ATOI2(p); + if (lt->tm_mon > 12) + badformat(); + --lt->tm_mon; /* time struct is 0 - 11 */ + /* FALLTHROUGH */ + case 6: /* dd */ + lt->tm_mday = ATOI2(p); + if (lt->tm_mday > 31) + badformat(); + /* FALLTHROUGH */ + case 4: /* hh */ + lt->tm_hour = ATOI2(p); + if (lt->tm_hour > 23) + badformat(); + /* FALLTHROUGH */ + case 2: /* mm */ + lt->tm_min = ATOI2(p); + if (lt->tm_min > 59) + badformat(); + break; + default: + badformat(); + } + + /* convert broken-down time to GMT clock time */ + if ((tval = mktime(lt)) == -1) + badformat(); + + /* set the time */ + if (nflag || netsettime(tval)) { + logwtmp("|", "date", ""); + tv.tv_sec = tval; + tv.tv_usec = 0; + if (settimeofday(&tv, NULL)) { + perror("date: settimeofday"); + exit(1); + } + logwtmp("{", "date", ""); + } + + if ((p = getlogin()) == NULL) + p = "???"; + syslog(LOG_AUTH | LOG_NOTICE, "date set by %s", p); +} + +static void +badformat() +{ + warnx("illegal time format"); + usage(); +} + +static void +usage() +{ + (void)fprintf(stderr, + "usage: date [-nu] [-d dst] [-r seconds] [-t west] [+format]\n"); + (void)fprintf(stderr, " [yy[mm[dd[hh]]]]mm[.ss]]\n"); + exit(1); +} diff --git a/bin/date/extern.h b/bin/date/extern.h new file mode 100644 index 0000000..ed0e7d5 --- /dev/null +++ b/bin/date/extern.h @@ -0,0 +1,36 @@ +/*- + * Copyright (c) 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 + */ + +int netsettime __P((time_t)); diff --git a/bin/date/netdate.c b/bin/date/netdate.c new file mode 100644 index 0000000..2dca0f7 --- /dev/null +++ b/bin/date/netdate.c @@ -0,0 +1,182 @@ +/*- + * Copyright (c) 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. + */ + +#ifndef lint +static char sccsid[] = "@(#)netdate.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <netdb.h> +#define TSPTYPES +#include <protocols/timed.h> + +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "extern.h" + +#define WAITACK 2 /* seconds */ +#define WAITDATEACK 5 /* seconds */ + +extern int retval; + +/* + * Set the date in the machines controlled by timedaemons by communicating the + * new date to the local timedaemon. If the timedaemon is in the master state, + * it performs the correction on all slaves. If it is in the slave state, it + * notifies the master that a correction is needed. + * Returns 0 on success. Returns > 0 on failure, setting retval to 2; + */ +int +netsettime(tval) + time_t tval; +{ + struct timeval tout; + struct servent *sp; + struct tsp msg; + struct sockaddr_in sin, dest, from; + fd_set ready; + long waittime; + int s, length, port, timed_ack, found, err; + char hostname[MAXHOSTNAMELEN]; + + if ((sp = getservbyname("timed", "udp")) == NULL) { + warnx("udp/timed: unknown service"); + return (retval = 2); + } + + dest.sin_port = sp->s_port; + dest.sin_family = AF_INET; + dest.sin_addr.s_addr = htonl((u_long)INADDR_ANY); + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) { + if (errno != EPROTONOSUPPORT) + warn("timed"); + return (retval = 2); + } + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + for (port = IPPORT_RESERVED - 1; port > IPPORT_RESERVED / 2; port--) { + sin.sin_port = htons((u_short)port); + if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0) + break; + if (errno == EADDRINUSE) + continue; + if (errno != EADDRNOTAVAIL) + warn("bind"); + goto bad; + } + if (port == IPPORT_RESERVED / 2) { + warnx("all ports in use"); + goto bad; + } + msg.tsp_type = TSP_SETDATE; + msg.tsp_vers = TSPVERSION; + if (gethostname(hostname, sizeof(hostname))) { + warn("gethostname"); + goto bad; + } + (void)strncpy(msg.tsp_name, hostname, sizeof(hostname)); + msg.tsp_seq = htons((u_short)0); + msg.tsp_time.tv_sec = htonl((u_long)tval); + msg.tsp_time.tv_usec = htonl((u_long)0); + length = sizeof(struct sockaddr_in); + if (connect(s, (struct sockaddr *)&dest, length) < 0) { + warn("connect"); + goto bad; + } + if (send(s, (char *)&msg, sizeof(struct tsp), 0) < 0) { + if (errno != ECONNREFUSED) + warn("send"); + goto bad; + } + + timed_ack = -1; + waittime = WAITACK; +loop: + tout.tv_sec = waittime; + tout.tv_usec = 0; + + FD_ZERO(&ready); + FD_SET(s, &ready); + found = select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0, &tout); + + length = sizeof(err); + if (!getsockopt(s, + SOL_SOCKET, SO_ERROR, (char *)&err, &length) && err) { + if (err != ECONNREFUSED) + warn("send (delayed error)"); + goto bad; + } + + if (found > 0 && FD_ISSET(s, &ready)) { + length = sizeof(struct sockaddr_in); + if (recvfrom(s, &msg, sizeof(struct tsp), 0, + (struct sockaddr *)&from, &length) < 0) { + if (errno != ECONNREFUSED) + warn("recvfrom"); + goto bad; + } + msg.tsp_seq = ntohs(msg.tsp_seq); + msg.tsp_time.tv_sec = ntohl(msg.tsp_time.tv_sec); + msg.tsp_time.tv_usec = ntohl(msg.tsp_time.tv_usec); + switch (msg.tsp_type) { + case TSP_ACK: + timed_ack = TSP_ACK; + waittime = WAITDATEACK; + goto loop; + case TSP_DATEACK: + (void)close(s); + return (0); + default: + warnx("wrong ack received from timed: %s", + tsptype[msg.tsp_type]); + timed_ack = -1; + break; + } + } + if (timed_ack == -1) + warnx("can't reach time daemon, time set locally"); + +bad: + (void)close(s); + return (retval = 2); +} diff --git a/bin/dd/Makefile b/bin/dd/Makefile new file mode 100644 index 0000000..fc4965b --- /dev/null +++ b/bin/dd/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 + +PROG= dd +SRCS= args.c conv.c conv_tab.c dd.c misc.c position.c + +.include <bsd.prog.mk> diff --git a/bin/dd/args.c b/bin/dd/args.c new file mode 100644 index 0000000..3c2f3a2 --- /dev/null +++ b/bin/dd/args.c @@ -0,0 +1,386 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * 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[] = "@(#)args.c 8.3 (Berkeley) 4/2/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <err.h> +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "dd.h" +#include "extern.h" + +static int c_arg __P((const void *, const void *)); +static int c_conv __P((const void *, const void *)); +static void f_bs __P((char *)); +static void f_cbs __P((char *)); +static void f_conv __P((char *)); +static void f_count __P((char *)); +static void f_files __P((char *)); +static void f_ibs __P((char *)); +static void f_if __P((char *)); +static void f_obs __P((char *)); +static void f_of __P((char *)); +static void f_seek __P((char *)); +static void f_skip __P((char *)); +static u_long get_bsz __P((char *)); + +static struct arg { + char *name; + void (*f) __P((char *)); + u_int set, noset; +} args[] = { + { "bs", f_bs, C_BS, C_BS|C_IBS|C_OBS|C_OSYNC }, + { "cbs", f_cbs, C_CBS, C_CBS }, + { "conv", f_conv, 0, 0 }, + { "count", f_count, C_COUNT, C_COUNT }, + { "files", f_files, C_FILES, C_FILES }, + { "ibs", f_ibs, C_IBS, C_BS|C_IBS }, + { "if", f_if, C_IF, C_IF }, + { "obs", f_obs, C_OBS, C_BS|C_OBS }, + { "of", f_of, C_OF, C_OF }, + { "seek", f_seek, C_SEEK, C_SEEK }, + { "skip", f_skip, C_SKIP, C_SKIP }, +}; + +static char *oper; + +/* + * args -- parse JCL syntax of dd. + */ +void +jcl(argv) + char **argv; +{ + struct arg *ap, tmp; + char *arg; + + in.dbsz = out.dbsz = 512; + + while (oper = *++argv) { + if ((arg = strchr(oper, '=')) == NULL) + errx(1, "unknown operand %s", oper); + *arg++ = '\0'; + if (!*arg) + errx(1, "no value specified for %s", oper); + tmp.name = oper; + if (!(ap = (struct arg *)bsearch(&tmp, args, + sizeof(args)/sizeof(struct arg), sizeof(struct arg), + c_arg))) + errx(1, "unknown operand %s", tmp.name); + if (ddflags & ap->noset) + errx(1, "%s: illegal argument combination or already set", + tmp.name); + ddflags |= ap->set; + ap->f(arg); + } + + /* Final sanity checks. */ + + if (ddflags & C_BS) { + /* + * Bs is turned off by any conversion -- we assume the user + * just wanted to set both the input and output block sizes + * and didn't want the bs semantics, so we don't warn. + */ + if (ddflags & (C_BLOCK|C_LCASE|C_SWAB|C_UCASE|C_UNBLOCK)) + ddflags &= ~C_BS; + + /* Bs supersedes ibs and obs. */ + if (ddflags & C_BS && ddflags & (C_IBS|C_OBS)) + warnx("bs supersedes ibs and obs"); + } + + /* + * Ascii/ebcdic and cbs implies block/unblock. + * Block/unblock requires cbs and vice-versa. + */ + if (ddflags & (C_BLOCK|C_UNBLOCK)) { + if (!(ddflags & C_CBS)) + errx(1, "record operations require cbs"); + if (cbsz == 0) + errx(1, "cbs cannot be zero"); + cfunc = ddflags & C_BLOCK ? block : unblock; + } else if (ddflags & C_CBS) { + if (ddflags & (C_ASCII|C_EBCDIC)) { + if (ddflags & C_ASCII) { + ddflags |= C_UNBLOCK; + cfunc = unblock; + } else { + ddflags |= C_BLOCK; + cfunc = block; + } + } else + errx(1, "cbs meaningless if not doing record operations"); + if (cbsz == 0) + errx(1, "cbs cannot be zero"); + } else + cfunc = def; + + if (in.dbsz == 0 || out.dbsz == 0) + errx(1, "buffer sizes cannot be zero"); + + /* + * Read, write and seek calls take ints as arguments. Seek sizes + * could be larger if we wanted to do it in stages or check only + * regular files, but it's probably not worth it. + */ + if (in.dbsz > INT_MAX || out.dbsz > INT_MAX) + errx(1, "buffer sizes cannot be greater than %d", INT_MAX); + if (in.offset > INT_MAX / in.dbsz || out.offset > INT_MAX / out.dbsz) + errx(1, "seek offsets cannot be larger than %d", INT_MAX); +} + +static int +c_arg(a, b) + const void *a, *b; +{ + + return (strcmp(((struct arg *)a)->name, ((struct arg *)b)->name)); +} + +static void +f_bs(arg) + char *arg; +{ + + in.dbsz = out.dbsz = (int)get_bsz(arg); +} + +static void +f_cbs(arg) + char *arg; +{ + + cbsz = (int)get_bsz(arg); +} + +static void +f_count(arg) + char *arg; +{ + + cpy_cnt = (u_int)get_bsz(arg); + if (!cpy_cnt) + terminate(0); +} + +static void +f_files(arg) + char *arg; +{ + + files_cnt = (int)get_bsz(arg); +} + +static void +f_ibs(arg) + char *arg; +{ + + if (!(ddflags & C_BS)) + in.dbsz = (int)get_bsz(arg); +} + +static void +f_if(arg) + char *arg; +{ + + in.name = arg; +} + +static void +f_obs(arg) + char *arg; +{ + + if (!(ddflags & C_BS)) + out.dbsz = (int)get_bsz(arg); +} + +static void +f_of(arg) + char *arg; +{ + + out.name = arg; +} + +static void +f_seek(arg) + char *arg; +{ + + out.offset = (u_int)get_bsz(arg); +} + +static void +f_skip(arg) + char *arg; +{ + + in.offset = (u_int)get_bsz(arg); +} + +static struct conv { + char *name; + u_int set, noset; + u_char *ctab; +} clist[] = { + { "ascii", C_ASCII, C_EBCDIC, e2a_POSIX }, + { "block", C_BLOCK, C_UNBLOCK, NULL }, + { "ebcdic", C_EBCDIC, C_ASCII, a2e_POSIX }, + { "ibm", C_EBCDIC, C_ASCII, a2ibm_POSIX }, + { "lcase", C_LCASE, C_UCASE, NULL }, + { "noerror", C_NOERROR, 0, NULL }, + { "notrunc", C_NOTRUNC, 0, NULL }, + { "oldascii", C_ASCII, C_EBCDIC, e2a_32V }, + { "oldebcdic", C_EBCDIC, C_ASCII, a2e_32V }, + { "oldibm", C_EBCDIC, C_ASCII, a2ibm_32V }, + { "osync", C_OSYNC, C_BS, NULL }, + { "swab", C_SWAB, 0, NULL }, + { "sync", C_SYNC, 0, NULL }, + { "ucase", C_UCASE, C_LCASE, NULL }, + { "unblock", C_UNBLOCK, C_BLOCK, NULL }, +}; + +static void +f_conv(arg) + char *arg; +{ + struct conv *cp, tmp; + + while (arg != NULL) { + tmp.name = strsep(&arg, ","); + if (!(cp = (struct conv *)bsearch(&tmp, clist, + sizeof(clist)/sizeof(struct conv), sizeof(struct conv), + c_conv))) + errx(1, "unknown conversion %s", tmp.name); + if (ddflags & cp->noset) + errx(1, "%s: illegal conversion combination", tmp.name); + ddflags |= cp->set; + if (cp->ctab) + ctab = cp->ctab; + } +} + +static int +c_conv(a, b) + const void *a, *b; +{ + + return (strcmp(((struct conv *)a)->name, ((struct conv *)b)->name)); +} + +/* + * Convert an expression of the following forms to an unsigned long. + * 1) A positive decimal number. + * 2) A positive decimal number followed by a b (mult by 512). + * 3) A positive decimal number followed by a k (mult by 1024). + * 4) A positive decimal number followed by a m (mult by 512). + * 5) A positive decimal number followed by a w (mult by sizeof int) + * 6) Two or more positive decimal numbers (with/without k,b or w). + * seperated by x (also * for backwards compatibility), specifying + * the product of the indicated values. + */ +static u_long +get_bsz(val) + char *val; +{ + u_long num, t; + char *expr; + + num = strtoul(val, &expr, 0); + if (num == ULONG_MAX) /* Overflow. */ + err(1, "%s", oper); + if (expr == val) /* No digits. */ + errx(1, "%s: illegal numeric value", oper); + + switch(*expr) { + case 'b': + t = num; + num *= 512; + if (t > num) + goto erange; + ++expr; + break; + case 'k': + t = num; + num *= 1024; + if (t > num) + goto erange; + ++expr; + break; + case 'm': + t = num; + num *= 1048576; + if (t > num) + goto erange; + ++expr; + break; + case 'w': + t = num; + num *= sizeof(int); + if (t > num) + goto erange; + ++expr; + break; + } + + switch(*expr) { + case '\0': + break; + case '*': /* Backward compatible. */ + case 'x': + t = num; + num *= get_bsz(expr + 1); + if (t > num) +erange: errx(1, "%s: %s", oper, strerror(ERANGE)); + break; + default: + errx(1, "%s: illegal numeric value", oper); + } + return (num); +} diff --git a/bin/dd/conv.c b/bin/dd/conv.c new file mode 100644 index 0000000..69fa8cf --- /dev/null +++ b/bin/dd/conv.c @@ -0,0 +1,260 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * 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[] = "@(#)conv.c 8.3 (Berkeley) 4/2/94"; +#endif /* not lint */ + +#include <sys/param.h> + +#include <err.h> +#include <string.h> + +#include "dd.h" +#include "extern.h" + +/* + * def -- + * Copy input to output. Input is buffered until reaches obs, and then + * output until less than obs remains. Only a single buffer is used. + * Worst case buffer calculation is (ibs + obs - 1). + */ +void +def() +{ + int cnt; + u_char *inp, *t; + + if (t = ctab) + for (inp = in.dbp - (cnt = in.dbrcnt); cnt--; ++inp) + *inp = t[*inp]; + + /* Make the output buffer look right. */ + out.dbp = in.dbp; + out.dbcnt = in.dbcnt; + + if (in.dbcnt >= out.dbsz) { + /* If the output buffer is full, write it. */ + dd_out(0); + + /* + * Ddout copies the leftover output to the beginning of + * the buffer and resets the output buffer. Reset the + * input buffer to match it. + */ + in.dbp = out.dbp; + in.dbcnt = out.dbcnt; + } +} + +void +def_close() +{ + /* Just update the count, everything is already in the buffer. */ + if (in.dbcnt) + out.dbcnt = in.dbcnt; +} + +/* + * Copy variable length newline terminated records with a max size cbsz + * bytes to output. Records less than cbs are padded with spaces. + * + * max in buffer: MAX(ibs, cbsz) + * max out buffer: obs + cbsz + */ +void +block() +{ + static int intrunc; + int ch, cnt, maxlen; + u_char *inp, *outp, *t; + + /* + * Record truncation can cross block boundaries. If currently in a + * truncation state, keep tossing characters until reach a newline. + * Start at the beginning of the buffer, as the input buffer is always + * left empty. + */ + if (intrunc) { + for (inp = in.db, cnt = in.dbrcnt; + cnt && *inp++ != '\n'; --cnt); + if (!cnt) { + in.dbcnt = 0; + in.dbp = in.db; + return; + } + intrunc = 0; + /* Adjust the input buffer numbers. */ + in.dbcnt = cnt - 1; + in.dbp = inp + cnt - 1; + } + + /* + * Copy records (max cbsz size chunks) into the output buffer. The + * translation is done as we copy into the output buffer. + */ + for (inp = in.dbp - in.dbcnt, outp = out.dbp; in.dbcnt;) { + maxlen = MIN(cbsz, in.dbcnt); + if (t = ctab) + for (cnt = 0; + cnt < maxlen && (ch = *inp++) != '\n'; ++cnt) + *outp++ = t[ch]; + else + for (cnt = 0; + cnt < maxlen && (ch = *inp++) != '\n'; ++cnt) + *outp++ = ch; + /* + * Check for short record without a newline. Reassemble the + * input block. + */ + if (ch != '\n' && in.dbcnt < cbsz) { + memmove(in.db, in.dbp - in.dbcnt, in.dbcnt); + break; + } + + /* Adjust the input buffer numbers. */ + in.dbcnt -= cnt; + if (ch == '\n') + --in.dbcnt; + + /* Pad short records with spaces. */ + if (cnt < cbsz) + (void)memset(outp, ctab ? ctab[' '] : ' ', cbsz - cnt); + else { + /* + * If the next character wouldn't have ended the + * block, it's a truncation. + */ + if (!in.dbcnt || *inp != '\n') + ++st.trunc; + + /* Toss characters to a newline. */ + for (; in.dbcnt && *inp++ != '\n'; --in.dbcnt); + if (!in.dbcnt) + intrunc = 1; + else + --in.dbcnt; + } + + /* Adjust output buffer numbers. */ + out.dbp += cbsz; + if ((out.dbcnt += cbsz) >= out.dbsz) + dd_out(0); + outp = out.dbp; + } + in.dbp = in.db + in.dbcnt; +} + +void +block_close() +{ + /* + * Copy any remaining data into the output buffer and pad to a record. + * Don't worry about truncation or translation, the input buffer is + * always empty when truncating, and no characters have been added for + * translation. The bottom line is that anything left in the input + * buffer is a truncated record. Anything left in the output buffer + * just wasn't big enough. + */ + if (in.dbcnt) { + ++st.trunc; + memmove(out.dbp, in.dbp - in.dbcnt, in.dbcnt); + (void)memset(out.dbp + in.dbcnt, + ctab ? ctab[' '] : ' ', cbsz - in.dbcnt); + out.dbcnt += cbsz; + } +} + +/* + * Convert fixed length (cbsz) records to variable length. Deletes any + * trailing blanks and appends a newline. + * + * max in buffer: MAX(ibs, cbsz) + cbsz + * max out buffer: obs + cbsz + */ +void +unblock() +{ + int cnt; + u_char *inp, *t; + + /* Translation and case conversion. */ + if (t = ctab) + for (cnt = in.dbrcnt, inp = in.dbp; cnt--;) + *--inp = t[*inp]; + /* + * Copy records (max cbsz size chunks) into the output buffer. The + * translation has to already be done or we might not recognize the + * spaces. + */ + for (inp = in.db; in.dbcnt >= cbsz; inp += cbsz, in.dbcnt -= cbsz) { + for (t = inp + cbsz - 1; t >= inp && *t == ' '; --t); + if (t >= inp) { + cnt = t - inp + 1; + memmove(out.dbp, inp, cnt); + out.dbp += cnt; + out.dbcnt += cnt; + } + ++out.dbcnt; + *out.dbp++ = '\n'; + if (out.dbcnt >= out.dbsz) + dd_out(0); + } + if (in.dbcnt) + memmove(in.db, in.dbp - in.dbcnt, in.dbcnt); + in.dbp = in.db + in.dbcnt; +} + +void +unblock_close() +{ + int cnt; + u_char *t; + + if (in.dbcnt) { + warnx("%s: short input record", in.name); + for (t = in.db + in.dbcnt - 1; t >= in.db && *t == ' '; --t); + if (t >= in.db) { + cnt = t - in.db + 1; + memmove(out.dbp, in.db, cnt); + out.dbp += cnt; + out.dbcnt += cnt; + } + ++out.dbcnt; + *out.dbp++ = '\n'; + } +} diff --git a/bin/dd/conv_tab.c b/bin/dd/conv_tab.c new file mode 100644 index 0000000..5d238d5 --- /dev/null +++ b/bin/dd/conv_tab.c @@ -0,0 +1,357 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * 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[] = "@(#)conv_tab.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include <sys/types.h> + +/* + * There are currently eight tables: + * + * lower-case -> upper-case conv=upper + * upper-case -> lower-case conv=lower + * + * ebcdic -> ascii 32V conv=oldascii + * ascii -> ebcdic 32V conv=oldebcdic + * ascii -> ibm ebcdic 32V conv=oldibm + * + * ebcdic -> ascii POSIX/S5 conv=ascii + * ascii -> ebcdic POSIX/S5 conv=ebcdic + * ascii -> ibm ebcdic POSIX/S5 conv=ibm + * + * Other tables are built from these if multiple conversions are being + * done. + * + * Tables used for conversions to/from IBM and EBCDIC to support an extension + * to POSIX P1003.2/D11. The tables referencing POSIX contain data extracted + * from tables 4-3 and 4-4 in P1003.2/Draft 11. The historic tables were + * constructed by running against a file with all possible byte values. + * + * More information can be obtained in "Correspondences of 8-Bit and Hollerith + * Codes for Computer Environments-A USASI Tutorial", Communications of the + * ACM, Volume 11, Number 11, November 1968, pp. 783-789. + */ + +/* Lower-case to upper-case */ +u_char l2u[] = { + 0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007, /* 0000 */ + 0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017, /* 0010 */ + 0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027, /* 0020 */ + 0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037, /* 0030 */ + 0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047, /* 0040 */ + 0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057, /* 0050 */ + 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067, /* 0060 */ + 0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077, /* 0070 */ + 0100, 0101, 0102, 0103, 0104, 0105, 0106, 0107, /* 0100 */ + 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117, /* 0110 */ + 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127, /* 0120 */ + 0130, 0131, 0132, 0133, 0134, 0135, 0136, 0137, /* 0130 */ + 0140, 0101, 0102, 0103, 0104, 0105, 0106, 0107, /* 0140 */ + 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117, /* 0150 */ + 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127, /* 0160 */ + 0130, 0131, 0132, 0173, 0174, 0175, 0176, 0177, /* 0170 */ + 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207, /* 0200 */ + 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217, /* 0210 */ + 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227, /* 0220 */ + 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237, /* 0230 */ + 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247, /* 0240 */ + 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257, /* 0250 */ + 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, /* 0260 */ + 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, /* 0270 */ + 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307, /* 0300 */ + 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317, /* 0310 */ + 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327, /* 0320 */ + 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, /* 0330 */ + 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347, /* 0340 */ + 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357, /* 0350 */ + 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, /* 0360 */ + 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */ +}; + +/* Upper-case to lower-case */ +u_char u2l[] = { + 0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007, /* 0000 */ + 0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017, /* 0010 */ + 0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027, /* 0020 */ + 0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037, /* 0030 */ + 0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047, /* 0040 */ + 0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057, /* 0050 */ + 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067, /* 0060 */ + 0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077, /* 0070 */ + 0100, 0141, 0142, 0143, 0144, 0145, 0146, 0147, /* 0100 */ + 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157, /* 0110 */ + 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167, /* 0120 */ + 0170, 0171, 0172, 0133, 0134, 0135, 0136, 0137, /* 0130 */ + 0140, 0141, 0142, 0143, 0144, 0145, 0146, 0147, /* 0140 */ + 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157, /* 0150 */ + 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167, /* 0160 */ + 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177, /* 0170 */ + 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207, /* 0200 */ + 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217, /* 0210 */ + 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227, /* 0220 */ + 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237, /* 0230 */ + 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247, /* 0240 */ + 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257, /* 0250 */ + 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, /* 0260 */ + 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, /* 0270 */ + 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307, /* 0300 */ + 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317, /* 0310 */ + 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327, /* 0320 */ + 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, /* 0330 */ + 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347, /* 0340 */ + 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357, /* 0350 */ + 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, /* 0360 */ + 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */ +}; + +/* EBCDIC to ASCII -- 32V compatible. */ +u_char e2a_32V[] = { + 0000, 0001, 0002, 0003, 0234, 0011, 0206, 0177, /* 0000 */ + 0227, 0215, 0216, 0013, 0014, 0015, 0016, 0017, /* 0010 */ + 0020, 0021, 0022, 0023, 0235, 0205, 0010, 0207, /* 0020 */ + 0030, 0031, 0222, 0217, 0034, 0035, 0036, 0037, /* 0030 */ + 0200, 0201, 0202, 0203, 0204, 0012, 0027, 0033, /* 0040 */ + 0210, 0211, 0212, 0213, 0214, 0005, 0006, 0007, /* 0050 */ + 0220, 0221, 0026, 0223, 0224, 0225, 0226, 0004, /* 0060 */ + 0230, 0231, 0232, 0233, 0024, 0025, 0236, 0032, /* 0070 */ + 0040, 0240, 0241, 0242, 0243, 0244, 0245, 0246, /* 0100 */ + 0247, 0250, 0133, 0056, 0074, 0050, 0053, 0041, /* 0110 */ + 0046, 0251, 0252, 0253, 0254, 0255, 0256, 0257, /* 0120 */ + 0260, 0261, 0135, 0044, 0052, 0051, 0073, 0136, /* 0130 */ + 0055, 0057, 0262, 0263, 0264, 0265, 0266, 0267, /* 0140 */ + 0270, 0271, 0174, 0054, 0045, 0137, 0076, 0077, /* 0150 */ + 0272, 0273, 0274, 0275, 0276, 0277, 0300, 0301, /* 0160 */ + 0302, 0140, 0072, 0043, 0100, 0047, 0075, 0042, /* 0170 */ + 0303, 0141, 0142, 0143, 0144, 0145, 0146, 0147, /* 0200 */ + 0150, 0151, 0304, 0305, 0306, 0307, 0310, 0311, /* 0210 */ + 0312, 0152, 0153, 0154, 0155, 0156, 0157, 0160, /* 0220 */ + 0161, 0162, 0313, 0314, 0315, 0316, 0317, 0320, /* 0230 */ + 0321, 0176, 0163, 0164, 0165, 0166, 0167, 0170, /* 0240 */ + 0171, 0172, 0322, 0323, 0324, 0325, 0326, 0327, /* 0250 */ + 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, /* 0260 */ + 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347, /* 0270 */ + 0173, 0101, 0102, 0103, 0104, 0105, 0106, 0107, /* 0300 */ + 0110, 0111, 0350, 0351, 0352, 0353, 0354, 0355, /* 0310 */ + 0175, 0112, 0113, 0114, 0115, 0116, 0117, 0120, /* 0320 */ + 0121, 0122, 0356, 0357, 0360, 0361, 0362, 0363, /* 0330 */ + 0134, 0237, 0123, 0124, 0125, 0126, 0127, 0130, /* 0340 */ + 0131, 0132, 0364, 0365, 0366, 0367, 0370, 0371, /* 0350 */ + 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067, /* 0360 */ + 0070, 0071, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */ +}; + +/* ASCII to EBCDIC -- 32V compatible. */ +u_char a2e_32V[] = { + 0000, 0001, 0002, 0003, 0067, 0055, 0056, 0057, /* 0000 */ + 0026, 0005, 0045, 0013, 0014, 0015, 0016, 0017, /* 0010 */ + 0020, 0021, 0022, 0023, 0074, 0075, 0062, 0046, /* 0020 */ + 0030, 0031, 0077, 0047, 0034, 0035, 0036, 0037, /* 0030 */ + 0100, 0117, 0177, 0173, 0133, 0154, 0120, 0175, /* 0040 */ + 0115, 0135, 0134, 0116, 0153, 0140, 0113, 0141, /* 0050 */ + 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, /* 0060 */ + 0370, 0371, 0172, 0136, 0114, 0176, 0156, 0157, /* 0070 */ + 0174, 0301, 0302, 0303, 0304, 0305, 0306, 0307, /* 0100 */ + 0310, 0311, 0321, 0322, 0323, 0324, 0325, 0326, /* 0110 */ + 0327, 0330, 0331, 0342, 0343, 0344, 0345, 0346, /* 0120 */ + 0347, 0350, 0351, 0112, 0340, 0132, 0137, 0155, /* 0130 */ + 0171, 0201, 0202, 0203, 0204, 0205, 0206, 0207, /* 0140 */ + 0210, 0211, 0221, 0222, 0223, 0224, 0225, 0226, /* 0150 */ + 0227, 0230, 0231, 0242, 0243, 0244, 0245, 0246, /* 0160 */ + 0247, 0250, 0251, 0300, 0152, 0320, 0241, 0007, /* 0170 */ + 0040, 0041, 0042, 0043, 0044, 0025, 0006, 0027, /* 0200 */ + 0050, 0051, 0052, 0053, 0054, 0011, 0012, 0033, /* 0210 */ + 0060, 0061, 0032, 0063, 0064, 0065, 0066, 0010, /* 0220 */ + 0070, 0071, 0072, 0073, 0004, 0024, 0076, 0341, /* 0230 */ + 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110, /* 0240 */ + 0111, 0121, 0122, 0123, 0124, 0125, 0126, 0127, /* 0250 */ + 0130, 0131, 0142, 0143, 0144, 0145, 0146, 0147, /* 0260 */ + 0150, 0151, 0160, 0161, 0162, 0163, 0164, 0165, /* 0270 */ + 0166, 0167, 0170, 0200, 0212, 0213, 0214, 0215, /* 0300 */ + 0216, 0217, 0220, 0232, 0233, 0234, 0235, 0236, /* 0310 */ + 0237, 0240, 0252, 0253, 0254, 0255, 0256, 0257, /* 0320 */ + 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, /* 0330 */ + 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, /* 0340 */ + 0312, 0313, 0314, 0315, 0316, 0317, 0332, 0333, /* 0350 */ + 0334, 0335, 0336, 0337, 0352, 0353, 0354, 0355, /* 0360 */ + 0356, 0357, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */ +}; + +/* ASCII to IBM EBCDIC -- 32V compatible. */ +u_char a2ibm_32V[] = { + 0000, 0001, 0002, 0003, 0067, 0055, 0056, 0057, /* 0000 */ + 0026, 0005, 0045, 0013, 0014, 0015, 0016, 0017, /* 0010 */ + 0020, 0021, 0022, 0023, 0074, 0075, 0062, 0046, /* 0020 */ + 0030, 0031, 0077, 0047, 0034, 0035, 0036, 0037, /* 0030 */ + 0100, 0132, 0177, 0173, 0133, 0154, 0120, 0175, /* 0040 */ + 0115, 0135, 0134, 0116, 0153, 0140, 0113, 0141, /* 0050 */ + 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, /* 0060 */ + 0370, 0371, 0172, 0136, 0114, 0176, 0156, 0157, /* 0070 */ + 0174, 0301, 0302, 0303, 0304, 0305, 0306, 0307, /* 0100 */ + 0310, 0311, 0321, 0322, 0323, 0324, 0325, 0326, /* 0110 */ + 0327, 0330, 0331, 0342, 0343, 0344, 0345, 0346, /* 0120 */ + 0347, 0350, 0351, 0255, 0340, 0275, 0137, 0155, /* 0130 */ + 0171, 0201, 0202, 0203, 0204, 0205, 0206, 0207, /* 0140 */ + 0210, 0211, 0221, 0222, 0223, 0224, 0225, 0226, /* 0150 */ + 0227, 0230, 0231, 0242, 0243, 0244, 0245, 0246, /* 0160 */ + 0247, 0250, 0251, 0300, 0117, 0320, 0241, 0007, /* 0170 */ + 0040, 0041, 0042, 0043, 0044, 0025, 0006, 0027, /* 0200 */ + 0050, 0051, 0052, 0053, 0054, 0011, 0012, 0033, /* 0210 */ + 0060, 0061, 0032, 0063, 0064, 0065, 0066, 0010, /* 0220 */ + 0070, 0071, 0072, 0073, 0004, 0024, 0076, 0341, /* 0230 */ + 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110, /* 0240 */ + 0111, 0121, 0122, 0123, 0124, 0125, 0126, 0127, /* 0250 */ + 0130, 0131, 0142, 0143, 0144, 0145, 0146, 0147, /* 0260 */ + 0150, 0151, 0160, 0161, 0162, 0163, 0164, 0165, /* 0270 */ + 0166, 0167, 0170, 0200, 0212, 0213, 0214, 0215, /* 0300 */ + 0216, 0217, 0220, 0232, 0233, 0234, 0235, 0236, /* 0310 */ + 0237, 0240, 0252, 0253, 0254, 0255, 0256, 0257, /* 0320 */ + 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, /* 0330 */ + 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, /* 0340 */ + 0312, 0313, 0314, 0315, 0316, 0317, 0332, 0333, /* 0350 */ + 0334, 0335, 0336, 0337, 0352, 0353, 0354, 0355, /* 0360 */ + 0356, 0357, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */ +}; + +/* EBCDIC to ASCII -- POSIX and System V compatible. */ +u_char e2a_POSIX[] = { + 0000, 0001, 0002, 0003, 0234, 0011, 0206, 0177, /* 0000 */ + 0227, 0215, 0216, 0013, 0014, 0015, 0016, 0017, /* 0010 */ + 0020, 0021, 0022, 0023, 0235, 0205, 0010, 0207, /* 0020 */ + 0030, 0031, 0222, 0217, 0034, 0035, 0036, 0037, /* 0030 */ + 0200, 0201, 0202, 0203, 0204, 0012, 0027, 0033, /* 0040 */ + 0210, 0211, 0212, 0213, 0214, 0005, 0006, 0007, /* 0050 */ + 0220, 0221, 0026, 0223, 0224, 0225, 0226, 0004, /* 0060 */ + 0230, 0231, 0232, 0233, 0024, 0025, 0236, 0032, /* 0070 */ + 0040, 0240, 0241, 0242, 0243, 0244, 0245, 0246, /* 0100 */ + 0247, 0250, 0325, 0056, 0074, 0050, 0053, 0174, /* 0110 */ + 0046, 0251, 0252, 0253, 0254, 0255, 0256, 0257, /* 0120 */ + 0260, 0261, 0041, 0044, 0052, 0051, 0073, 0176, /* 0130 */ + 0055, 0057, 0262, 0263, 0264, 0265, 0266, 0267, /* 0140 */ + 0270, 0271, 0313, 0054, 0045, 0137, 0076, 0077, /* 0150 */ + 0272, 0273, 0274, 0275, 0276, 0277, 0300, 0301, /* 0160 */ + 0302, 0140, 0072, 0043, 0100, 0047, 0075, 0042, /* 0170 */ + 0303, 0141, 0142, 0143, 0144, 0145, 0146, 0147, /* 0200 */ + 0150, 0151, 0304, 0305, 0306, 0307, 0310, 0311, /* 0210 */ + 0312, 0152, 0153, 0154, 0155, 0156, 0157, 0160, /* 0220 */ + 0161, 0162, 0136, 0314, 0315, 0316, 0317, 0320, /* 0230 */ + 0321, 0345, 0163, 0164, 0165, 0166, 0167, 0170, /* 0240 */ + 0171, 0172, 0322, 0323, 0324, 0133, 0326, 0327, /* 0250 */ + 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, /* 0260 */ + 0340, 0341, 0342, 0343, 0344, 0135, 0346, 0347, /* 0270 */ + 0173, 0101, 0102, 0103, 0104, 0105, 0106, 0107, /* 0300 */ + 0110, 0111, 0350, 0351, 0352, 0353, 0354, 0355, /* 0310 */ + 0175, 0112, 0113, 0114, 0115, 0116, 0117, 0120, /* 0320 */ + 0121, 0122, 0356, 0357, 0360, 0361, 0362, 0363, /* 0330 */ + 0134, 0237, 0123, 0124, 0125, 0126, 0127, 0130, /* 0340 */ + 0131, 0132, 0364, 0365, 0366, 0367, 0370, 0371, /* 0350 */ + 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067, /* 0360 */ + 0070, 0071, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */ +}; + +/* ASCII to EBCDIC -- POSIX and System V compatible. */ +u_char a2e_POSIX[] = { + 0000, 0001, 0002, 0003, 0067, 0055, 0056, 0057, /* 0000 */ + 0026, 0005, 0045, 0013, 0014, 0015, 0016, 0017, /* 0010 */ + 0020, 0021, 0022, 0023, 0074, 0075, 0062, 0046, /* 0020 */ + 0030, 0031, 0077, 0047, 0034, 0035, 0036, 0037, /* 0030 */ + 0100, 0132, 0177, 0173, 0133, 0154, 0120, 0175, /* 0040 */ + 0115, 0135, 0134, 0116, 0153, 0140, 0113, 0141, /* 0050 */ + 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, /* 0060 */ + 0370, 0371, 0172, 0136, 0114, 0176, 0156, 0157, /* 0070 */ + 0174, 0301, 0302, 0303, 0304, 0305, 0306, 0307, /* 0100 */ + 0310, 0311, 0321, 0322, 0323, 0324, 0325, 0326, /* 0110 */ + 0327, 0330, 0331, 0342, 0343, 0344, 0345, 0346, /* 0120 */ + 0347, 0350, 0351, 0255, 0340, 0275, 0232, 0155, /* 0130 */ + 0171, 0201, 0202, 0203, 0204, 0205, 0206, 0207, /* 0140 */ + 0210, 0211, 0221, 0222, 0223, 0224, 0225, 0226, /* 0150 */ + 0227, 0230, 0231, 0242, 0243, 0244, 0245, 0246, /* 0160 */ + 0247, 0250, 0251, 0300, 0117, 0320, 0137, 0007, /* 0170 */ + 0040, 0041, 0042, 0043, 0044, 0025, 0006, 0027, /* 0200 */ + 0050, 0051, 0052, 0053, 0054, 0011, 0012, 0033, /* 0210 */ + 0060, 0061, 0032, 0063, 0064, 0065, 0066, 0010, /* 0220 */ + 0070, 0071, 0072, 0073, 0004, 0024, 0076, 0341, /* 0230 */ + 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110, /* 0240 */ + 0111, 0121, 0122, 0123, 0124, 0125, 0126, 0127, /* 0250 */ + 0130, 0131, 0142, 0143, 0144, 0145, 0146, 0147, /* 0260 */ + 0150, 0151, 0160, 0161, 0162, 0163, 0164, 0165, /* 0270 */ + 0166, 0167, 0170, 0200, 0212, 0213, 0214, 0215, /* 0300 */ + 0216, 0217, 0220, 0152, 0233, 0234, 0235, 0236, /* 0310 */ + 0237, 0240, 0252, 0253, 0254, 0112, 0256, 0257, /* 0320 */ + 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, /* 0330 */ + 0270, 0271, 0272, 0273, 0274, 0241, 0276, 0277, /* 0340 */ + 0312, 0313, 0314, 0315, 0316, 0317, 0332, 0333, /* 0350 */ + 0334, 0335, 0336, 0337, 0352, 0353, 0354, 0355, /* 0360 */ + 0356, 0357, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */ +}; + +/* ASCII to IBM EBCDIC -- POSIX and System V compatible. */ +u_char a2ibm_POSIX[] = { + 0000, 0001, 0002, 0003, 0067, 0055, 0056, 0057, /* 0000 */ + 0026, 0005, 0045, 0013, 0014, 0015, 0016, 0017, /* 0010 */ + 0020, 0021, 0022, 0023, 0074, 0075, 0062, 0046, /* 0020 */ + 0030, 0031, 0077, 0047, 0034, 0035, 0036, 0037, /* 0030 */ + 0100, 0132, 0177, 0173, 0133, 0154, 0120, 0175, /* 0040 */ + 0115, 0135, 0134, 0116, 0153, 0140, 0113, 0141, /* 0050 */ + 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, /* 0060 */ + 0370, 0371, 0172, 0136, 0114, 0176, 0156, 0157, /* 0070 */ + 0174, 0301, 0302, 0303, 0304, 0305, 0306, 0307, /* 0100 */ + 0310, 0311, 0321, 0322, 0323, 0324, 0325, 0326, /* 0110 */ + 0327, 0330, 0331, 0342, 0343, 0344, 0345, 0346, /* 0120 */ + 0347, 0350, 0351, 0255, 0340, 0275, 0137, 0155, /* 0130 */ + 0171, 0201, 0202, 0203, 0204, 0205, 0206, 0207, /* 0140 */ + 0210, 0211, 0221, 0222, 0223, 0224, 0225, 0226, /* 0150 */ + 0227, 0230, 0231, 0242, 0243, 0244, 0245, 0246, /* 0160 */ + 0247, 0250, 0251, 0300, 0117, 0320, 0241, 0007, /* 0170 */ + 0040, 0041, 0042, 0043, 0044, 0025, 0006, 0027, /* 0200 */ + 0050, 0051, 0052, 0053, 0054, 0011, 0012, 0033, /* 0210 */ + 0060, 0061, 0032, 0063, 0064, 0065, 0066, 0010, /* 0220 */ + 0070, 0071, 0072, 0073, 0004, 0024, 0076, 0341, /* 0230 */ + 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110, /* 0240 */ + 0111, 0121, 0122, 0123, 0124, 0125, 0126, 0127, /* 0250 */ + 0130, 0131, 0142, 0143, 0144, 0145, 0146, 0147, /* 0260 */ + 0150, 0151, 0160, 0161, 0162, 0163, 0164, 0165, /* 0270 */ + 0166, 0167, 0170, 0200, 0212, 0213, 0214, 0215, /* 0300 */ + 0216, 0217, 0220, 0232, 0233, 0234, 0235, 0236, /* 0310 */ + 0237, 0240, 0252, 0253, 0254, 0255, 0256, 0257, /* 0320 */ + 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, /* 0330 */ + 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, /* 0340 */ + 0312, 0313, 0314, 0315, 0316, 0317, 0332, 0333, /* 0350 */ + 0334, 0335, 0336, 0337, 0352, 0353, 0354, 0355, /* 0360 */ + 0356, 0357, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */ +}; diff --git a/bin/dd/dd.1 b/bin/dd/dd.1 new file mode 100644 index 0000000..0b20781 --- /dev/null +++ b/bin/dd/dd.1 @@ -0,0 +1,348 @@ +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Keith Muller of the University of California, San Diego. +.\" +.\" 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. +.\" +.\" @(#)dd.1 8.2 (Berkeley) 1/13/94 +.\" +.Dd January 13, 1994 +.Dt DD 1 +.Os +.Sh NAME +.Nm dd +.Nd convert and copy a file +.Sh SYNOPSIS +.Nm dd +.Op operands ... +.Sh DESCRIPTION +The +.Nm +utility copies the standard input to the standard output. +Input data is read and written in 512-byte blocks. +If input reads are short, input from multiple reads are aggregated +to form the output block. +When finished, +.Nm dd +displays the number of complete and partial input and output blocks +and truncated input records to the standard error output. +.Pp +The following operands are available: +.Bl -tag -width of=file +.It Cm bs= Ns Ar n +Set both input and output block size, superseding the +.Cm ibs +and +.Cm obs +operands. +If no conversion values other than +.Cm noerror , +.Cm notrunc +or +.Cm sync +are specified, then each input block is copied to the output as a +single block without any aggregation of short blocks. +.It Cm cbs= Ns Ar n +Set the conversion record size to +.Va n +bytes. +The conversion record size is required by the record oriented conversion +values. +.It Cm count= Ns Ar n +Copy only +.Va n +input blocks. +.It Cm files= Ns Ar n +Copy +.Va n +input files before terminating. +This operand is only applicable when the input device is a tape. +.It Cm ibs= Ns Ar n +Set the input block size to +.Va n +bytes instead of the default 512. +.It Cm if= Ns Ar file +Read input from +.Ar file +instead of the standard input. +.It Cm obs= Ns Ar n +Set the output block size to +.Va n +bytes instead of the default 512. +.It Cm of= Ns Ar file +Write output to +.Ar file +instead of the standard output. +Any regular output file is truncated unless the +.Cm notrunc +conversion value is specified. +If an initial portion of the output file is skipped (see the +.Cm seek +operand) +the output file is truncated at that point. +.It Cm seek= Ns Ar n +Seek +.Va n +blocks from the beginning of the output before copying. +On non-tape devices, a +.Xr lseek 2 +operation is used. +Otherwise, existing blocks are read and the data discarded. +If the user does not have read permission for the tape, it is positioned +using the tape +.Xr ioctl 2 +function calls. +If the seek operation is past the end of file, space from the current +end of file to the specified offset is filled with blocks of +.Tn NUL +bytes. +.It Cm skip= Ns Ar n +Skip +.Va n +blocks from the beginning of the input before copying. +On input which supports seeks, a +.Xr lseek 2 +operation is used. +Otherwise, input data is read and discarded. +For pipes, the correct number of bytes is read. +For all other devices, the correct number of blocks is read without +distinguishing between a partial or complete block being read. +.It Xo +.Cm conv= +.Ns Cm value Ns Op \&, Cm value \&... +.Xc +Where +.Cm value +is one of the symbols from the following list. +.Bl -tag -width unblock +.It Cm ascii , oldascii +The same as the +.Cm unblock +value except that characters are translated from +.Tn ECBDIC +to +.Tn ASCII +before the +records are converted. +(These values imply +.Cm unblock +if the operand +.Cm cbs +is also specified.) +There are two conversion maps for +.Tn ASCII . +The value +.Cm ascii +specifies the recommended one which is compatible with System V. +The value +.Cm oldascii +specifies the one used in historic +.Tn AT&T +and pre-4.3BSD-reno systems. +.It Cm block +Treats the input as a sequence of newline or end-of-file terminated variable +length records independent of input and output block boundaries. +Any trailing newline character is discarded. +Each input record is converted to a fixed length output record where the +length is specified by the +.Cm cbs +operand. +Input records shorter than the conversion record size are padded with spaces. +Input records longer than the conversion record size are truncated. +The number of truncated input records, if any, are reported to the standard +error output at the completion of the copy. +.It Cm ebcdic , ibm , oldebcdic , oldibm +The same as the +.Cm block +value except that characters are translated from +.Tn ASCII +to +.Tn EBCDIC +after the +records are converted. +(These values imply +.Cm block +if the operand +.Cm cbs +is also specified.) +There are four conversion maps for +.Tn EBCDIC . +The value +.Cm ebcdic +specifies the recommended one which is compatible with +.At V . +The value +.Cm ibm +is a slightly different mapping, which is compatible with the +.At V +.Cm ibm +value. +The values +.Cm oldebcdic +and +.Cm oldibm +are maps used in historic +.Tn AT&T +and pre-4.3BSD-reno systems. +.It Cm lcase +Transform uppercase characters into lowercase characters. +.It Cm noerror +Do not stop processing on an input error. +When an input error occurs, a diagnostic message followed by the current +input and output block counts will be written to the standard error output +in the same format as the standard completion message. +If the +.Cm sync +conversion is also specified, any missing input data will be replaced +with +.Tn NUL +bytes (or with spaces if a block oriented conversion value was +specified) and processed as a normal input buffer. +If the +.Cm sync +conversion is not specified, the input block is omitted from the output. +On input files which are not tapes or pipes, the file offset +will be positioned past the block in which the error occurred using +.Xr lseek 2 . +.It Cm notrunc +Do not truncate the output file. +This will preserve any blocks in the output file not explicitly written +by +.Nm dd . +The +.Cm notrunc +value is not supported for tapes. +.It Cm osync +Pad the final output block to the full output block size. +If the input file is not a multiple of the output block size +after conversion, this conversion forces the final output block +to be the same size as preceding blocks for use on devices that require +regularly sized blocks to be written. +This option is incompatible with use of the +.Cm bs= Ns Ar n +block size specification. +.It Cm swab +Swap every pair of input bytes. +If an input buffer has an odd number of bytes, the last byte will be +ignored during swapping. +.It Cm sync +Pad every input block to the input buffer size. +Spaces are used for pad bytes if a block oriented conversion value is +specified, otherwise +.Tn NUL +bytes are used. +.It Cm ucase +Transform lowercase characters into uppercase characters. +.It Cm unblock +Treats the input as a sequence of fixed length records independent of input +and output block boundaries. +The length of the input records is specified by the +.Cm cbs +operand. +Any trailing space characters are discarded and a newline character is +appended. +.El +.El +.Pp +Where sizes are specified, a decimal number of bytes is expected. +If the number ends with a ``b'', ``k'', ``m'' or ``w'', the number +is multiplied by 512, 1024 (1K), 1048576 (1M) or the number of bytes +in an integer, respectively. +Two or more numbers may be separated by an ``x'' to indicate a product. +.Pp +When finished, +.Nm dd +displays the number of complete and partial input and output blocks, +truncated input records and odd-length byte-swapping blocks to the +standard error output. +A partial input block is one where less than the input block size +was read. +A partial output block is one where less than the output block size +was written. +Partial output blocks to tape devices are considered fatal errors. +Otherwise, the rest of the block will be written. +Partial output blocks to character devices will produce a warning message. +A truncated input block is one where a variable length record oriented +conversion value was specified and the input line was too long to +fit in the conversion record or was not newline terminated. +.Pp +Normally, data resulting from input or conversion or both are aggregated +into output blocks of the specified size. +After the end of input is reached, any remaining output is written as +a block. +This means that the final output block may be shorter than the output +block size. +.Pp +If +.Nm dd +receives a +.Dv SIGINFO +(see the ``status'' argument for +.Xr stty 1 ) +signal, the current input and output block counts will +be written to the standard error output +in the same format as the standard completion message. +If +.Nm dd +receives a +.Dv SIGINT +signal, the current input and output block counts will +be written to the standard error output +in the same format as the standard completion message and +.Nm dd +will exit. +.Pp +The +.Nm dd +utility exits 0 on success and >0 if an error occurred. +.Sh SEE ALSO +.Xr cp 1 , +.Xr mt 1 , +.Xr tr 1 +.Sh STANDARDS +The +.Nm dd +utility is expected to be a superset of the +.St -p1003.2 +standard. +The +.Cm files +operand and the +.Cm ascii , +.Cm ebcdic , +.Cm ibm , +.Cm oldascii , +.Cm oldebcdic +and +.Cm oldibm +values are extensions to the +.Tn POSIX +standard. diff --git a/bin/dd/dd.c b/bin/dd/dd.c new file mode 100644 index 0000000..05ac7a9 --- /dev/null +++ b/bin/dd/dd.c @@ -0,0 +1,395 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * 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) 1991, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)dd.c 8.5 (Berkeley) 4/2/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/mtio.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "dd.h" +#include "extern.h" + +static void dd_close __P((void)); +static void dd_in __P((void)); +static void getfdtype __P((IO *)); +static void setup __P((void)); + +IO in, out; /* input/output state */ +STAT st; /* statistics */ +void (*cfunc) __P((void)); /* conversion function */ +u_long cpy_cnt; /* # of blocks to copy */ +u_int ddflags; /* conversion options */ +u_int cbsz; /* conversion block size */ +u_int files_cnt = 1; /* # of files to copy */ +u_char *ctab; /* conversion table */ + +int +main(argc, argv) + int argc; + char *argv[]; +{ + jcl(argv); + setup(); + + (void)signal(SIGINFO, summaryx); + (void)signal(SIGINT, terminate); + + atexit(summary); + + while (files_cnt--) + dd_in(); + + dd_close(); + exit(0); +} + +static void +setup() +{ + u_int cnt; + + if (in.name == NULL) { + in.name = "stdin"; + in.fd = STDIN_FILENO; + } else { + in.fd = open(in.name, O_RDONLY, 0); + if (in.fd < 0) + err(1, "%s", in.name); + } + + getfdtype(&in); + + if (files_cnt > 1 && !(in.flags & ISTAPE)) + errx(1, "files is not supported for non-tape devices"); + + if (out.name == NULL) { + /* No way to check for read access here. */ + out.fd = STDOUT_FILENO; + out.name = "stdout"; + } else { +#define OFLAGS \ + (O_CREAT | (ddflags & (C_SEEK | C_NOTRUNC) ? 0 : O_TRUNC)) + out.fd = open(out.name, O_RDWR | OFLAGS, DEFFILEMODE); + /* + * May not have read access, so try again with write only. + * Without read we may have a problem if output also does + * not support seeks. + */ + if (out.fd < 0) { + out.fd = open(out.name, O_WRONLY | OFLAGS, DEFFILEMODE); + out.flags |= NOREAD; + } + if (out.fd < 0) + err(1, "%s", out.name); + } + + getfdtype(&out); + + /* + * Allocate space for the input and output buffers. If not doing + * record oriented I/O, only need a single buffer. + */ + if (!(ddflags & (C_BLOCK|C_UNBLOCK))) { + if ((in.db = malloc(out.dbsz + in.dbsz - 1)) == NULL) + err(1, NULL); + out.db = in.db; + } else if ((in.db = + malloc((u_int)(MAX(in.dbsz, cbsz) + cbsz))) == NULL || + (out.db = malloc((u_int)(out.dbsz + cbsz))) == NULL) + err(1, NULL); + in.dbp = in.db; + out.dbp = out.db; + + /* Position the input/output streams. */ + if (in.offset) + pos_in(); + if (out.offset) + pos_out(); + + /* + * Truncate the output file; ignore errors because it fails on some + * kinds of output files, tapes, for example. + */ + if (ddflags & (C_OF | C_SEEK | C_NOTRUNC) == (C_OF | C_SEEK)) + (void)ftruncate(out.fd, (off_t)out.offset * out.dbsz); + + /* + * If converting case at the same time as another conversion, build a + * table that does both at once. If just converting case, use the + * built-in tables. + */ + if (ddflags & (C_LCASE|C_UCASE)) + if (ddflags & C_ASCII) + if (ddflags & C_LCASE) { + for (cnt = 0; cnt < 0377; ++cnt) + if (isupper(ctab[cnt])) + ctab[cnt] = tolower(ctab[cnt]); + } else { + for (cnt = 0; cnt < 0377; ++cnt) + if (islower(ctab[cnt])) + ctab[cnt] = toupper(ctab[cnt]); + } + else if (ddflags & C_EBCDIC) + if (ddflags & C_LCASE) { + for (cnt = 0; cnt < 0377; ++cnt) + if (isupper(cnt)) + ctab[cnt] = ctab[tolower(cnt)]; + } else { + for (cnt = 0; cnt < 0377; ++cnt) + if (islower(cnt)) + ctab[cnt] = ctab[toupper(cnt)]; + } + else + ctab = ddflags & C_LCASE ? u2l : l2u; + (void)time(&st.start); /* Statistics timestamp. */ +} + +static void +getfdtype(io) + IO *io; +{ + struct mtget mt; + struct stat sb; + + if (fstat(io->fd, &sb)) + err(1, "%s", io->name); + if (S_ISCHR(sb.st_mode)) + io->flags |= ioctl(io->fd, MTIOCGET, &mt) ? ISCHR : ISTAPE; + else if (lseek(io->fd, (off_t)0, SEEK_CUR) == -1 && errno == ESPIPE) + io->flags |= ISPIPE; /* XXX fixed in 4.4BSD */ +} + +static void +dd_in() +{ + int flags, n; + + for (flags = ddflags;;) { + if (cpy_cnt && (st.in_full + st.in_part) >= cpy_cnt) + return; + + /* + * Zero the buffer first if trying to recover from errors so + * lose the minimum amount of data. If doing block operations + * use spaces. + */ + if ((flags & (C_NOERROR|C_SYNC)) == (C_NOERROR|C_SYNC)) + if (flags & (C_BLOCK|C_UNBLOCK)) + memset(in.dbp, ' ', in.dbsz); + else + memset(in.dbp, 0, in.dbsz); + + n = read(in.fd, in.dbp, in.dbsz); + if (n == 0) { + in.dbrcnt = 0; + return; + } + + /* Read error. */ + if (n < 0) { + /* + * If noerror not specified, die. POSIX requires that + * the warning message be followed by an I/O display. + */ + if (!(flags & C_NOERROR)) + err(1, "%s", in.name); + warn("%s", in.name); + summary(); + + /* + * If it's not a tape drive or a pipe, seek past the + * error. If your OS doesn't do the right thing for + * raw disks this section should be modified to re-read + * in sector size chunks. + */ + if (!(in.flags & (ISPIPE|ISTAPE)) && + lseek(in.fd, (off_t)in.dbsz, SEEK_CUR)) + warn("%s", in.name); + + /* If sync not specified, omit block and continue. */ + if (!(ddflags & C_SYNC)) + continue; + + /* Read errors count as full blocks. */ + in.dbcnt += in.dbrcnt = in.dbsz; + ++st.in_full; + + /* Handle full input blocks. */ + } else if (n == in.dbsz) { + in.dbcnt += in.dbrcnt = n; + ++st.in_full; + + /* Handle partial input blocks. */ + } else { + /* If sync, use the entire block. */ + if (ddflags & C_SYNC) + in.dbcnt += in.dbrcnt = in.dbsz; + else + in.dbcnt += in.dbrcnt = n; + ++st.in_part; + } + + /* + * POSIX states that if bs is set and no other conversions + * than noerror, notrunc or sync are specified, the block + * is output without buffering as it is read. + */ + if (ddflags & C_BS) { + out.dbcnt = in.dbcnt; + dd_out(1); + in.dbcnt = 0; + continue; + } + + if (ddflags & C_SWAB) { + if ((n = in.dbcnt) & 1) { + ++st.swab; + --n; + } + swab(in.dbp, in.dbp, n); + } + + in.dbp += in.dbrcnt; + (*cfunc)(); + } +} + +/* + * Cleanup any remaining I/O and flush output. If necesssary, output file + * is truncated. + */ +static void +dd_close() +{ + if (cfunc == def) + def_close(); + else if (cfunc == block) + block_close(); + else if (cfunc == unblock) + unblock_close(); + if (ddflags & C_OSYNC && out.dbcnt < out.dbsz) { + memset(out.dbp, 0, out.dbsz - out.dbcnt); + out.dbcnt = out.dbsz; + } + if (out.dbcnt) + dd_out(1); +} + +void +dd_out(force) + int force; +{ + static int warned; + int cnt, n, nw; + u_char *outp; + + /* + * Write one or more blocks out. The common case is writing a full + * output block in a single write; increment the full block stats. + * Otherwise, we're into partial block writes. If a partial write, + * and it's a character device, just warn. If a tape device, quit. + * + * The partial writes represent two cases. 1: Where the input block + * was less than expected so the output block was less than expected. + * 2: Where the input block was the right size but we were forced to + * write the block in multiple chunks. The original versions of dd(1) + * never wrote a block in more than a single write, so the latter case + * never happened. + * + * One special case is if we're forced to do the write -- in that case + * we play games with the buffer size, and it's usually a partial write. + */ + outp = out.db; + for (n = force ? out.dbcnt : out.dbsz;; n = out.dbsz) { + for (cnt = n;; cnt -= nw) { + nw = write(out.fd, outp, cnt); + if (nw <= 0) { + if (nw == 0) + errx(1, "%s: end of device", out.name); + if (errno != EINTR) + err(1, "%s", out.name); + nw = 0; + } + outp += nw; + st.bytes += nw; + if (nw == n) { + if (n != out.dbsz) + ++st.out_part; + else + ++st.out_full; + break; + } + ++st.out_part; + if (nw == cnt) + break; + if (out.flags & ISCHR && !warned) { + warned = 1; + warnx("%s: short write on character device", + out.name); + } + if (out.flags & ISTAPE) + errx(1, "%s: short write on tape device", out.name); + } + if ((out.dbcnt -= n) < out.dbsz) + break; + } + + /* Reassemble the output block. */ + if (out.dbcnt) + memmove(out.db, out.dbp - out.dbcnt, out.dbcnt); + out.dbp = out.db + out.dbcnt; +} diff --git a/bin/dd/dd.h b/bin/dd/dd.h new file mode 100644 index 0000000..3b790a7 --- /dev/null +++ b/bin/dd/dd.h @@ -0,0 +1,96 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * 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. + * + * @(#)dd.h 8.3 (Berkeley) 4/2/94 + */ + +/* Input/output stream state. */ +typedef struct { + u_char *db; /* buffer address */ + u_char *dbp; /* current buffer I/O address */ + u_long dbcnt; /* current buffer byte count */ + int dbrcnt; /* last read byte count */ + u_long dbsz; /* buffer size */ + +#define ISCHR 0x01 /* character device (warn on short) */ +#define ISPIPE 0x02 /* pipe (not truncatable) */ +#define ISTAPE 0x04 /* tape (not seekable) */ +#define NOREAD 0x08 /* not readable */ + u_int flags; + + char *name; /* name */ + int fd; /* file descriptor */ + u_long offset; /* # of blocks to skip */ + + u_long f_stats; /* # of full blocks processed */ + u_long p_stats; /* # of partial blocks processed */ + u_long s_stats; /* # of odd swab blocks */ + u_long t_stats; /* # of truncations */ +} IO; + +typedef struct { + u_long in_full; /* # of full input blocks */ + u_long in_part; /* # of partial input blocks */ + u_long out_full; /* # of full output blocks */ + u_long out_part; /* # of partial output blocks */ + u_long trunc; /* # of truncated records */ + u_long swab; /* # of odd-length swab blocks */ + u_long bytes; /* # of bytes written */ + time_t start; /* start time of dd */ +} STAT; + +/* Flags (in ddflags). */ +#define C_ASCII 0x00001 +#define C_BLOCK 0x00002 +#define C_BS 0x00004 +#define C_CBS 0x00008 +#define C_COUNT 0x00010 +#define C_EBCDIC 0x00020 +#define C_FILES 0x00040 +#define C_IBS 0x00080 +#define C_IF 0x00100 +#define C_LCASE 0x00200 +#define C_NOERROR 0x00400 +#define C_NOTRUNC 0x00800 +#define C_OBS 0x01000 +#define C_OF 0x02000 +#define C_SEEK 0x04000 +#define C_SKIP 0x08000 +#define C_SWAB 0x10000 +#define C_SYNC 0x20000 +#define C_UCASE 0x40000 +#define C_UNBLOCK 0x80000 +#define C_OSYNC 0x100000 diff --git a/bin/dd/extern.h b/bin/dd/extern.h new file mode 100644 index 0000000..e8fde63 --- /dev/null +++ b/bin/dd/extern.h @@ -0,0 +1,65 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * 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.3 (Berkeley) 4/2/94 + */ + +#include <sys/cdefs.h> + +void block __P((void)); +void block_close __P((void)); +void dd_out __P((int)); +void def __P((void)); +void def_close __P((void)); +void jcl __P((char **)); +void pos_in __P((void)); +void pos_out __P((void)); +void summary __P((void)); +void summaryx __P((int)); +void terminate __P((int)); +void unblock __P((void)); +void unblock_close __P((void)); + +extern IO in, out; +extern STAT st; +extern void (*cfunc)(); +extern u_long cpy_cnt; +extern u_int cbsz; +extern u_int ddflags; +extern u_int files_cnt; +extern u_char *ctab; +extern u_char a2e_32V[], a2e_POSIX[], a2ibm_32V[], a2ibm_POSIX[], e2a_32V[]; +extern u_char e2a_POSIX[], l2u[], u2l[]; diff --git a/bin/dd/misc.c b/bin/dd/misc.c new file mode 100644 index 0000000..db90352 --- /dev/null +++ b/bin/dd/misc.c @@ -0,0 +1,100 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * 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[] = "@(#)misc.c 8.3 (Berkeley) 4/2/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include "dd.h" +#include "extern.h" + +void +summary() +{ + time_t secs; + char buf[100]; + + (void)time(&secs); + if ((secs -= st.start) == 0) + secs = 1; + /* Use snprintf(3) so that we don't reenter stdio(3). */ + (void)snprintf(buf, sizeof(buf), + "%u+%u records in\n%u+%u records out\n", + st.in_full, st.in_part, st.out_full, st.out_part); + (void)write(STDERR_FILENO, buf, strlen(buf)); + if (st.swab) { + (void)snprintf(buf, sizeof(buf), "%u odd length swab %s\n", + st.swab, (st.swab == 1) ? "block" : "blocks"); + (void)write(STDERR_FILENO, buf, strlen(buf)); + } + if (st.trunc) { + (void)snprintf(buf, sizeof(buf), "%u truncated %s\n", + st.trunc, (st.trunc == 1) ? "block" : "blocks"); + (void)write(STDERR_FILENO, buf, strlen(buf)); + } + (void)snprintf(buf, sizeof(buf), + "%u bytes transferred in %u secs (%u bytes/sec)\n", + st.bytes, secs, st.bytes / secs); + (void)write(STDERR_FILENO, buf, strlen(buf)); +} + +/* ARGSUSED */ +void +summaryx(notused) + int notused; +{ + + summary(); +} + +/* ARGSUSED */ +void +terminate(notused) + int notused; +{ + + exit(0); +} diff --git a/bin/dd/position.c b/bin/dd/position.c new file mode 100644 index 0000000..9375b1e --- /dev/null +++ b/bin/dd/position.c @@ -0,0 +1,166 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * 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[] = "@(#)position.c 8.3 (Berkeley) 4/2/94"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/mtio.h> + +#include <err.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> + +#include "dd.h" +#include "extern.h" + +/* + * Position input/output data streams before starting the copy. Device type + * dependent. Seekable devices use lseek, and the rest position by reading. + * Seeking past the end of file can cause null blocks to be written to the + * output. + */ +void +pos_in() +{ + int bcnt, cnt, nr, warned; + + /* If not a character, pipe or tape device, try to seek on it. */ + if (!(in.flags & (ISCHR|ISPIPE|ISTAPE))) { + if (lseek(in.fd, (off_t)(in.offset * in.dbsz), SEEK_CUR) == -1) + err(1, "%s", in.name); + return; + } + + /* + * Read the data. If a pipe, read until satisfy the number of bytes + * being skipped. No differentiation for reading complete and partial + * blocks for other devices. + */ + for (bcnt = in.dbsz, cnt = in.offset, warned = 0; cnt;) { + if ((nr = read(in.fd, in.db, bcnt)) > 0) { + if (in.flags & ISPIPE) { + if (!(bcnt -= nr)) { + bcnt = in.dbsz; + --cnt; + } + } else + --cnt; + continue; + } + + if (nr == 0) { + if (files_cnt > 1) { + --files_cnt; + continue; + } + errx(1, "skip reached end of input"); + } + + /* + * Input error -- either EOF with no more files, or I/O error. + * If noerror not set die. POSIX requires that the warning + * message be followed by an I/O display. + */ + if (ddflags & C_NOERROR) { + if (!warned) { + warn("%s", in.name); + warned = 1; + summary(); + } + continue; + } + err(1, "%s", in.name); + } +} + +void +pos_out() +{ + struct mtop t_op; + int cnt, n; + + /* + * If not a tape, try seeking on the file. Seeking on a pipe is + * going to fail, but don't protect the user -- they shouldn't + * have specified the seek operand. + */ + if (!(out.flags & ISTAPE)) { + if (lseek(out.fd, + (off_t)out.offset * out.dbsz, SEEK_SET) == -1) + err(1, "%s", out.name); + return; + } + + /* If no read access, try using mtio. */ + if (out.flags & NOREAD) { + t_op.mt_op = MTFSR; + t_op.mt_count = out.offset; + + if (ioctl(out.fd, MTIOCTOP, &t_op) < 0) + err(1, "%s", out.name); + return; + } + + /* Read it. */ + for (cnt = 0; cnt < out.offset; ++cnt) { + if ((n = read(out.fd, out.db, out.dbsz)) > 0) + continue; + + if (n < 0) + err(1, "%s", out.name); + + /* + * If reach EOF, fill with NUL characters; first, back up over + * the EOF mark. Note, cnt has not yet been incremented, so + * the EOF read does not count as a seek'd block. + */ + t_op.mt_op = MTBSR; + t_op.mt_count = 1; + if (ioctl(out.fd, MTIOCTOP, &t_op) == -1) + err(1, "%s", out.name); + + while (cnt++ < out.offset) + if ((n = write(out.fd, out.db, out.dbsz)) != out.dbsz) + err(1, "%s", out.name); + break; + } +} diff --git a/bin/df/Makefile b/bin/df/Makefile new file mode 100644 index 0000000..e3f54d7 --- /dev/null +++ b/bin/df/Makefile @@ -0,0 +1,7 @@ +# @(#)Makefile 8.2 (Berkeley) 4/1/94 + +PROG= df +BINGRP= operator +BINMODE=2555 + +.include <bsd.prog.mk> diff --git a/bin/df/df.1 b/bin/df/df.1 new file mode 100644 index 0000000..bf461d6 --- /dev/null +++ b/bin/df/df.1 @@ -0,0 +1,116 @@ +.\" Copyright (c) 1989, 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. +.\" +.\" @(#)df.1 8.2 (Berkeley) 1/13/94 +.\" +.Dd January 13, 1994 +.Dt DF 1 +.Os BSD 4 +.Sh NAME +.Nm df +.Nd display free disk space +.Sh SYNOPSIS +.Nm df +.Op Fl in +.Op Fl t Ar type +.Op Ar file | Ar filesystem ... +.Sh DESCRIPTION +.Nm Df +displays statistics about the amount of free disk space on the specified +.Ar filesystem +or on the filesystem of which +.Ar file +is a part. +Values are displayed in 512-byte per block block counts. +If neither a file or a filesystem operand is specified, +statistics for all mounted filesystems are displayed +(subject to the +.Fl t +option below). +.Pp +The following options are available: +.Bl -tag -width Ds +.It Fl i +Include statistics on the number of free inodes. +.It Fl n +Print out the previously obtained statistics from the filesystems. +This option should be used if it is possible that one or more +filesystems are in a state such that they will not be able to provide +statistics without a long delay. +When this option is specified, +.Nm df +will not request new statistics from the filesystems, but will respond +with the possibly stale statistics that were previously obtained. +.It Fl t +Only print out statistics for filesystems of the specified types. +The recognized types are: +ufs, nfs, mfs, lfs, msdos, fdesc, portal, kernfs, procfs, afs and isofs. +along with the aggregates: +all (the default), +local (ufs, mfs, lfs, msdos, isofs), +and misc (fdesc, portal, kernfs, procfs). +The string ``no'' may be prepending to a type to get its complement +(e.g. ``nonfs'' to get non-NFS filesystems). The first +.Fl t +option overrides the default, additional such options will add to +(or subtract from) the current set of types; e.g. either +``df -t ufs -t lfs'' +or +``df -t local -t nomfs'' +will display statistics for UFS and LFS filesystems. +.El +.Sh ENVIRONMENTAL VARIABLES +.Bl -tag -width BLOCKSIZE +.It Ev BLOCKSIZE +If the environmental variable +.Ev BLOCKSIZE +is set, the block counts will be displayed in units of that size block. +.El +.Sh BUGS +The +.Fl n +and +.Fl t +flags are ignored if a file or filesystem is specified. +.Sh SEE ALSO +.Xr quota 1 , +.Xr statfs 2 , +.Xr fstatfs 2 , +.Xr getfsstat 2 , +.Xr getmntinfo 3 , +.Xr fstab 5 , +.Xr mount 8 , +.Xr quot 8 +.Sh HISTORY +A +.Nm df +command appeared in +.At v6 . diff --git a/bin/df/df.c b/bin/df/df.c new file mode 100644 index 0000000..eb6ee81 --- /dev/null +++ b/bin/df/df.c @@ -0,0 +1,420 @@ +/* + * Copyright (c) 1980, 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, 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) 1980, 1990, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)df.c 8.7 (Berkeley) 4/2/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/mount.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +/* XXX assumes MOUNT_MAXTYPE < 32 */ +#define MT(m) (1 << (m)) + +/* fixed values */ +#define MT_NONE (0) +#define MT_ALL (MT(MOUNT_MAXTYPE+1)-1) + +/* subject to change */ +#define MT_LOCAL \ + (MT(MOUNT_UFS)|MT(MOUNT_MFS)|MT(MOUNT_LFS)|MT(MOUNT_MSDOS)|MT(MOUNT_CD9660)) +#define MT_DEFAULT MT_ALL + +struct typetab { + char *str; + long types; +} typetab[] = { + "ufs", MT(MOUNT_UFS), + "local", MT_LOCAL, + "all", MT_ALL, + "nfs", MT(MOUNT_NFS), + "mfs", MT(MOUNT_MFS), + "lfs", MT(MOUNT_LFS), + "msdos", MT(MOUNT_MSDOS), + "fdesc", MT(MOUNT_FDESC), + "portal", MT(MOUNT_PORTAL), +#if 0 + /* return fsid of underlying FS */ + "lofs", MT(MOUNT_LOFS), + "null", MT(MOUNT_NULL), + "umap", MT(MOUNT_UMAP), +#endif + "kernfs", MT(MOUNT_KERNFS), + "procfs", MT(MOUNT_PROCFS), + "afs", MT(MOUNT_AFS), + "iso9660fs", MT(MOUNT_CD9660), + "cdfs", MT(MOUNT_CD9660), + "misc", MT(MOUNT_LOFS)|MT(MOUNT_FDESC)|MT(MOUNT_PORTAL)| + MT(MOUNT_KERNFS)|MT(MOUNT_PROCFS), + NULL, 0 +}; + +long addtype __P((long, char *)); +long regetmntinfo __P((struct statfs **, long, long)); +int bread __P((off_t, void *, int)); +char *getmntpt __P((char *)); +void prtstat __P((struct statfs *, int)); +void ufs_df __P((char *, int)); +void usage __P((void)); + +int iflag, nflag, tflag; +struct ufs_args mdev; + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct stat stbuf; + struct statfs statfsbuf, *mntbuf; + long fsmask, mntsize; + int ch, err, i, maxwidth, width; + char *mntpt; + + while ((ch = getopt(argc, argv, "int:")) != EOF) + switch (ch) { + case 'i': + iflag = 1; + break; + case 'n': + nflag = 1; + break; + case 't': + fsmask = addtype(fsmask, optarg); + tflag = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); + maxwidth = 0; + for (i = 0; i < mntsize; i++) { + width = strlen(mntbuf[i].f_mntfromname); + if (width > maxwidth) + maxwidth = width; + } + + if (!*argv) { + if (!tflag) + fsmask = MT_DEFAULT; + mntsize = regetmntinfo(&mntbuf, mntsize, fsmask); + if (fsmask != MT_ALL) { + maxwidth = 0; + for (i = 0; i < mntsize; i++) { + width = strlen(mntbuf[i].f_mntfromname); + if (width > maxwidth) + maxwidth = width; + } + } + for (i = 0; i < mntsize; i++) + prtstat(&mntbuf[i], maxwidth); + exit(0); + } + + for (; *argv; argv++) { + if (stat(*argv, &stbuf) < 0) { + err = errno; + if ((mntpt = getmntpt(*argv)) == 0) { + warn("%s", *argv); + continue; + } + } else if ((stbuf.st_mode & S_IFMT) == S_IFCHR) { + ufs_df(*argv, maxwidth); + continue; + } else if ((stbuf.st_mode & S_IFMT) == S_IFBLK) { + if ((mntpt = getmntpt(*argv)) == 0) { + mntpt = mktemp(strdup("/tmp/df.XXXXXX")); + mdev.fspec = *argv; + if (mkdir(mntpt, DEFFILEMODE) != 0) { + warn("%s", mntpt); + continue; + } + if (mount(MOUNT_UFS, mntpt, MNT_RDONLY, + &mdev) != 0) { + ufs_df(*argv, maxwidth); + (void)rmdir(mntpt); + continue; + } else if (statfs(mntpt, &statfsbuf)) { + statfsbuf.f_mntonname[0] = '\0'; + prtstat(&statfsbuf, maxwidth); + } else + warn("%s", *argv); + (void)unmount(mntpt, 0); + (void)rmdir(mntpt); + continue; + } + } else + mntpt = *argv; + /* + * Statfs does not take a `wait' flag, so we cannot + * implement nflag here. + */ + if (statfs(mntpt, &statfsbuf) < 0) { + warn("%s", mntpt); + continue; + } + if (argc == 1) + maxwidth = strlen(statfsbuf.f_mntfromname) + 1; + prtstat(&statfsbuf, maxwidth); + } + return (0); +} + +char * +getmntpt(name) + char *name; +{ + long mntsize, i; + struct statfs *mntbuf; + + mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); + for (i = 0; i < mntsize; i++) { + if (!strcmp(mntbuf[i].f_mntfromname, name)) + return (mntbuf[i].f_mntonname); + } + return (0); +} + +long +addtype(omask, str) + long omask; + char *str; +{ + struct typetab *tp; + + /* + * If it is one of our known types, add it to the current mask + */ + for (tp = typetab; tp->str; tp++) + if (strcmp(str, tp->str) == 0) + return (tp->types | (tflag ? omask : MT_NONE)); + /* + * See if it is the negation of one of the known values + */ + if (strlen(str) > 2 && str[0] == 'n' && str[1] == 'o') + for (tp = typetab; tp->str; tp++) + if (strcmp(str+2, tp->str) == 0) + return (~tp->types & (tflag ? omask : MT_ALL)); + errx(1, "unknown type `%s'", str); +} + +/* + * Make a pass over the filesystem info in ``mntbuf'' filtering out + * filesystem types not in ``fsmask'' and possibly re-stating to get + * current (not cached) info. Returns the new count of valid statfs bufs. + */ +long +regetmntinfo(mntbufp, mntsize, fsmask) + struct statfs **mntbufp; + long mntsize, fsmask; +{ + int i, j; + struct statfs *mntbuf; + + if (fsmask == MT_ALL) + return (nflag ? mntsize : getmntinfo(mntbufp, MNT_WAIT)); + + mntbuf = *mntbufp; + j = 0; + for (i = 0; i < mntsize; i++) { + if (fsmask & MT(mntbuf[i].f_type)) { + if (!nflag) + (void)statfs(mntbuf[i].f_mntonname,&mntbuf[j]); + else if (i != j) + mntbuf[j] = mntbuf[i]; + j++; + } + } + return (j); +} + +/* + * Convert statfs returned filesystem size into BLOCKSIZE units. + * Attempts to avoid overflow for large filesystems. + */ +#define fsbtoblk(num, fsbs, bs) \ + (((fsbs) != 0 && (fsbs) < (bs)) ? \ + (num) / ((bs) / (fsbs)) : (num) * ((fsbs) / (bs))) + +/* + * Print out status about a filesystem. + */ +void +prtstat(sfsp, maxwidth) + struct statfs *sfsp; + int maxwidth; +{ + static long blocksize; + static int headerlen, timesthrough; + static char *header; + long used, availblks, inodes; + + if (maxwidth < 11) + maxwidth = 11; + if (++timesthrough == 1) { + header = getbsize(&headerlen, &blocksize); + (void)printf("%-*.*s %s Used Avail Capacity", + maxwidth, maxwidth, "Filesystem", header); + if (iflag) + (void)printf(" iused ifree %%iused"); + (void)printf(" Mounted on\n"); + } + (void)printf("%-*.*s", maxwidth, maxwidth, sfsp->f_mntfromname); + used = sfsp->f_blocks - sfsp->f_bfree; + availblks = sfsp->f_bavail + used; + (void)printf(" %*ld %8ld %8ld", headerlen, + fsbtoblk(sfsp->f_blocks, sfsp->f_bsize, blocksize), + fsbtoblk(used, sfsp->f_bsize, blocksize), + fsbtoblk(sfsp->f_bavail, sfsp->f_bsize, blocksize)); + (void)printf(" %5.0f%%", + availblks == 0 ? 100.0 : (double)used / (double)availblks * 100.0); + if (iflag) { + inodes = sfsp->f_files; + used = inodes - sfsp->f_ffree; + (void)printf(" %7ld %7ld %5.0f%% ", used, sfsp->f_ffree, + inodes == 0 ? 100.0 : (double)used / (double)inodes * 100.0); + } else + (void)printf(" "); + (void)printf(" %s\n", sfsp->f_mntonname); +} + +/* + * This code constitutes the pre-system call Berkeley df code for extracting + * information from filesystem superblocks. + */ +#include <ufs/ffs/fs.h> +#include <errno.h> +#include <fstab.h> + +union { + struct fs iu_fs; + char dummy[SBSIZE]; +} sb; +#define sblock sb.iu_fs + +int rfd; + +void +ufs_df(file, maxwidth) + char *file; + int maxwidth; +{ + struct statfs statfsbuf; + struct statfs *sfsp; + char *mntpt; + static int synced; + + if (synced++ == 0) + sync(); + + if ((rfd = open(file, O_RDONLY)) < 0) { + warn("%s", file); + return; + } + if (bread((off_t)SBOFF, &sblock, SBSIZE) == 0) { + (void)close(rfd); + return; + } + sfsp = &statfsbuf; + sfsp->f_type = MOUNT_UFS; + sfsp->f_flags = 0; + sfsp->f_bsize = sblock.fs_fsize; + sfsp->f_iosize = sblock.fs_bsize; + sfsp->f_blocks = sblock.fs_dsize; + sfsp->f_bfree = sblock.fs_cstotal.cs_nbfree * sblock.fs_frag + + sblock.fs_cstotal.cs_nffree; + sfsp->f_bavail = (sblock.fs_dsize * (100 - sblock.fs_minfree) / 100) - + (sblock.fs_dsize - sfsp->f_bfree); + if (sfsp->f_bavail < 0) + sfsp->f_bavail = 0; + sfsp->f_files = sblock.fs_ncg * sblock.fs_ipg; + sfsp->f_ffree = sblock.fs_cstotal.cs_nifree; + sfsp->f_fsid.val[0] = 0; + sfsp->f_fsid.val[1] = 0; + if ((mntpt = getmntpt(file)) == 0) + mntpt = ""; + memmove(&sfsp->f_mntonname[0], mntpt, MNAMELEN); + memmove(&sfsp->f_mntfromname[0], file, MNAMELEN); + prtstat(sfsp, maxwidth); + (void)close(rfd); +} + +int +bread(off, buf, cnt) + off_t off; + void *buf; + int cnt; +{ + int nr; + + (void)lseek(rfd, off, SEEK_SET); + if ((nr = read(rfd, buf, cnt)) != cnt) { + /* Probably a dismounted disk if errno == EIO. */ + if (errno != EIO) + (void)fprintf(stderr, "\ndf: %qd: %s\n", + off, strerror(nr > 0 ? EIO : errno)); + return (0); + } + return (1); +} + +void +usage() +{ + (void)fprintf(stderr, "usage: df [-in] [file | file_system ...]\n"); + exit(1); +} diff --git a/bin/echo/Makefile b/bin/echo/Makefile new file mode 100644 index 0000000..05caa3b --- /dev/null +++ b/bin/echo/Makefile @@ -0,0 +1,5 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 + +PROG= echo + +.include <bsd.prog.mk> diff --git a/bin/echo/echo.1 b/bin/echo/echo.1 new file mode 100644 index 0000000..dc86b7c --- /dev/null +++ b/bin/echo/echo.1 @@ -0,0 +1,70 @@ +.\" Copyright (c) 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. +.\" +.\" @(#)echo.1 8.1 (Berkeley) 7/22/93 +.\" +.Dd July 22, 1993 +.Dt ECHO 1 +.Os +.Sh NAME +.Nm echo +.Nd write arguments to the standard output +.Sh SYNOPSIS +.Nm echo +.Op Fl n +.Op "string ..." +.Sh DESCRIPTION +The +.Nm echo +utility writes any specified operands, separated by single blank (`` '') +characters and followed by a newline (``\en'') character, to the standard +output. +.Pp +The following option is available: +.Bl -tag -width flag +.It Fl n +Do not print the trailing newline character. +.El +.Pp +The +.Nm echo +utility exits 0 on success, and >0 if an error occurs. +.Sh SEE ALSO +.Xr printf 1 +.Sh STANDARDS +The +.Nm echo +utility is expected to be +.St -p1003.2 +compatible. diff --git a/bin/echo/echo.c b/bin/echo/echo.c new file mode 100644 index 0000000..560e10a --- /dev/null +++ b/bin/echo/echo.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 1989, 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) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)echo.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int nflag; + + /* This utility may NOT do getopt(3) option parsing. */ + if (*++argv && !strcmp(*argv, "-n")) { + ++argv; + nflag = 1; + } + else + nflag = 0; + + while (*argv) { + (void)printf("%s", *argv); + if (*++argv) + putchar(' '); + } + if (!nflag) + putchar('\n'); + exit(0); +} diff --git a/bin/hostname/Makefile b/bin/hostname/Makefile new file mode 100644 index 0000000..d16477e --- /dev/null +++ b/bin/hostname/Makefile @@ -0,0 +1,5 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 + +PROG= hostname + +.include <bsd.prog.mk> diff --git a/bin/hostname/hostname.1 b/bin/hostname/hostname.1 new file mode 100644 index 0000000..98bcb7e --- /dev/null +++ b/bin/hostname/hostname.1 @@ -0,0 +1,65 @@ +.\" Copyright (c) 1983, 1988, 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. +.\" +.\" @(#)hostname.1 8.1 (Berkeley) 5/31/93 +.\" +.Dd May 31, 1993 +.Dt HOSTNAME 1 +.Os BSD 4.2 +.Sh NAME +.Nm hostname +.Nd set or print name of current host system +.Sh SYNOPSIS +.Nm hostname +.Op Fl s +.Op Ar nameofhost +.Sh DESCRIPTION +.Nm Hostname +prints the name of the current host. The super-user can +set the hostname by supplying an argument; this is usually done in the +network initialization script +.Pa /etc/netstart , +normally run at boot +time. +.Pp +Options: +.Bl -tag -width flag +.It Fl s +Trims off any domain information from the printed +name. +.El +.Sh SEE ALSO +.Xr gethostname 2 +.Sh HISTORY +The +.Nm hostname +command appeared in +.Bx 4.2 . diff --git a/bin/hostname/hostname.c b/bin/hostname/hostname.c new file mode 100644 index 0000000..afee46a --- /dev/null +++ b/bin/hostname/hostname.c @@ -0,0 +1,87 @@ +/* + * Copyright (c) 1988, 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) 1988, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)hostname.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include <sys/param.h> + +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +int +main(argc,argv) + int argc; + char *argv[]; +{ + extern int optind; + int ch, sflag; + char *p, hostname[MAXHOSTNAMELEN]; + + sflag = 0; + while ((ch = getopt(argc, argv, "s")) != EOF) + switch (ch) { + case 's': + sflag = 1; + break; + case '?': + default: + (void)fprintf(stderr, + "usage: hostname [-s] [hostname]\n"); + exit(1); + } + argc -= optind; + argv += optind; + + if (*argv) { + if (sethostname(*argv, strlen(*argv))) + err(1, "sethostname"); + } else { + if (gethostname(hostname, sizeof(hostname))) + err(1, "gethostname"); + if (sflag && (p = strchr(hostname, '.'))) + *p = '\0'; + (void)printf("%s\n", hostname); + } + exit(0); +} diff --git a/bin/kill/Makefile b/bin/kill/Makefile new file mode 100644 index 0000000..0cea87a --- /dev/null +++ b/bin/kill/Makefile @@ -0,0 +1,5 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 + +PROG= kill + +.include <bsd.prog.mk> diff --git a/bin/kill/kill.1 b/bin/kill/kill.1 new file mode 100644 index 0000000..bffef49 --- /dev/null +++ b/bin/kill/kill.1 @@ -0,0 +1,120 @@ +.\" Copyright (c) 1980, 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. +.\" +.\" @(#)kill.1 8.1 (Berkeley) 5/31/93 +.\" +.Dd May 31, 1993 +.Dt KILL 1 +.Os +.Sh NAME +.Nm kill +.Nd terminate or signal a process +.Sh SYNOPSIS +.Nm kill +.Op Fl signal_name +.Ar pid +\&... +.Nm kill +.Op Fl signal_number +.Ar pid +\&... +.Nm kill +.Op Fl l +.Sh DESCRIPTION +The kill utility sends the +.Dv TERM +signal to the processes specified +by the pid operand(s). +.Pp +Only the super-user may send signals to other users' processes. +.Pp +The options are as follows: +.Pp +.Bl -tag -width Ds +.It Fl l +List the signal names. +.It Fl signal_name +A symbolic signal name specifying the signal to be sent instead of the +default +.Dv TERM . +The +.Fl l +option displays the signal names. +.It Fl signal_number +A non-negative decimal integer, specifying the signal to be sent instead +of the default +.Dv TERM . +.El +.Pp +Some of the more commonly used signals: +.Bd -ragged -offset indent -compact +.Bl -column XXX TERM +.It -1 -1 (super-user broadcast to all processes, or user broadcast +to user's processes) +.It 0 0 (sh(1) only, signals all members of process group) +.It 2 INT (interrupt) +.It 3 QUIT (quit) +.It 6 ABRT (abort) +.It 9 KILL (non-catchable, non-ignorable kill) +.It 14 ALRM (alarm clock) +.It 15 TERM (software termination signal) +.El +.Ed +.Pp +.Nm Kill +is a built-in to +.Xr csh 1 ; +it allows job specifiers of the form ``%...'' as arguments +so process id's are not as often used as +.Nm kill +arguments. +See +.Xr csh 1 +for details. +.Sh SEE ALSO +.Xr csh 1 , +.Xr ps 1 , +.Xr kill 2 , +.Xr sigvec 2 +.Sh HISTORY +A +.Nm kill +command appeared in +.At v6 . +.Sh BUGS +A replacement for the command +.Dq Li kill 0 +for +.Xr csh 1 +users should be provided. diff --git a/bin/kill/kill.c b/bin/kill/kill.c new file mode 100644 index 0000000..00daeaf --- /dev/null +++ b/bin/kill/kill.c @@ -0,0 +1,144 @@ +/* + * Copyright (c) 1988, 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) 1988, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)kill.c 8.3 (Berkeley) 4/2/94"; +#endif /* not lint */ + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +void nosig __P((char *)); +void printsig __P((FILE *)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + const char *const *p; + int errors, numsig, pid; + char *ep; + + if (argc < 2) + usage(); + + if (!strcmp(*++argv, "-l")) { + printsig(stdout); + exit(0); + } + + numsig = SIGTERM; + if (**argv == '-') { + ++*argv; + if (isalpha(**argv)) { + if (!strncasecmp(*argv, "sig", 3)) + *argv += 3; + for (numsig = NSIG, p = sys_signame + 1; --numsig; ++p) + if (!strcasecmp(*p, *argv)) { + numsig = p - sys_signame; + break; + } + if (!numsig) + nosig(*argv); + } else if (isdigit(**argv)) { + numsig = strtol(*argv, &ep, 10); + if (!*argv || *ep) + errx(1, "illegal signal number: %s", *argv); + if (numsig <= 0 || numsig > NSIG) + nosig(*argv); + } else + nosig(*argv); + ++argv; + } + + if (!*argv) + usage(); + + for (errors = 0; *argv; ++argv) { + pid = strtol(*argv, &ep, 10); + if (!*argv || *ep) { + warnx("illegal process id: %s", *argv); + errors = 1; + } else if (kill(pid, numsig) == -1) { + warn("%s", *argv); + errors = 1; + } + } + exit(errors); +} + +void +nosig(name) + char *name; +{ + + warnx("unknown signal %s; valid signals:", name); + printsig(stderr); + exit(1); +} + +void +printsig(fp) + FILE *fp; +{ + const char *const *p; + int cnt; + + for (cnt = NSIG, p = sys_signame + 1; --cnt; ++p) { + (void)fprintf(fp, "%s ", *p); + if (cnt == NSIG / 2) + (void)fprintf(fp, "\n"); + } + (void)fprintf(fp, "\n"); +} + +void +usage() +{ + + (void)fprintf(stderr, "usage: kill [-l] [-sig] pid ...\n"); + exit(1); +} diff --git a/bin/ln/Makefile b/bin/ln/Makefile new file mode 100644 index 0000000..b1929c6 --- /dev/null +++ b/bin/ln/Makefile @@ -0,0 +1,7 @@ +# @(#)Makefile 8.2 (Berkeley) 5/31/93 + +PROG= ln +MAN1= ln.0 +MAN7= symlink.0 + +.include <bsd.prog.mk> diff --git a/bin/ln/ln.1 b/bin/ln/ln.1 new file mode 100644 index 0000000..fd85a6b --- /dev/null +++ b/bin/ln/ln.1 @@ -0,0 +1,131 @@ +.\" Copyright (c) 1980, 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. +.\" +.\" @(#)ln.1 8.2 (Berkeley) 12/30/93 +.\" +.Dd December 30, 1993 +.Dt LN 1 +.Os BSD 4 +.Sh NAME +.Nm ln +.Nd make links +.Sh SYNOPSIS +.Nm ln +.Op Fl fs +.Ar source_file +.Op target_file +.Nm ln +.Op Fl fs +.Ar source_file ... +.Op target_dir +.Sh DESCRIPTION +The +.Nm ln +utility creates a new directory entry (linked file) which has the +same modes as the original file. +It is useful for maintaining multiple copies of a file in many places +at once without using up storage for the +.Dq copies ; +instead, a link +.Dq points +to the original copy. +There are two types of links; hard links and symbolic links. +How a link +.Dq points +to a file is one of the differences between a hard or symbolic link. +.Pp +The options are as follows: +.Bl -tag -width flag +.It Fl f +Unlink any already existing file, permitting the link to occur. +.It Fl s +Create a symbolic link. +.El +.Pp +By default +.Nm ln +makes +.Em hard +links. +A hard link to a file is indistinguishable from the original directory entry; +any changes to a file are effective independent of the name used to reference +the file. +Hard links may not normally refer to directories and may not span file systems. +.Pp +A symbolic link contains the name of the file to +which it is linked. The referenced file is used when an +.Xr open 2 +operation is performed on the link. +A +.Xr stat 2 +on a symbolic link will return the linked-to file; an +.Xr lstat 2 +must be done to obtain information about the link. +The +.Xr readlink 2 +call may be used to read the contents of a symbolic link. +Symbolic links may span file systems and may refer to directories. +.Pp +Given one or two arguments, +.Nm ln +creates a link to an existing file +.Ar source_file . +If +.Ar target_file +is given, the link has that name; +.Ar target_file +may also be a directory in which to place the link; +otherwise it is placed in the current directory. +If only the directory is specified, the link will be made +to the last component of +.Ar source_file . +.Pp +Given more than two arguments, +.Nm ln +makes links in +.Ar target_dir +to all the named source files. +The links made will have the same name as the files being linked to. +.Sh SEE ALSO +.Xr link 2 , +.Xr lstat 2 , +.Xr readlink 2 , +.Xr stat 2 , +.Xr symlink 2 , +.Xr symlink 7 +.Sh HISTORY +A +.Nm ln +command appeared in +.At v6 . diff --git a/bin/ln/ln.c b/bin/ln/ln.c new file mode 100644 index 0000000..b39e710 --- /dev/null +++ b/bin/ln/ln.c @@ -0,0 +1,164 @@ +/* + * Copyright (c) 1987, 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) 1987, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)ln.c 8.2 (Berkeley) 3/31/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> + +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +int dirflag; /* Undocumented directory flag. */ +int fflag; /* Unlink existing files. */ +int sflag; /* Symbolic, not hard, link. */ + /* System link call. */ +int (*linkf) __P((const char *, const char *)); + +int linkit __P((char *, char *, int)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern int optind; + struct stat sb; + int ch, exitval; + char *sourcedir; + + while ((ch = getopt(argc, argv, "Ffs")) != EOF) + switch (ch) { + case 'F': + dirflag = 1; /* XXX: deliberately undocumented. */ + break; + case 'f': + fflag = 1; + break; + case 's': + sflag = 1; + break; + case '?': + default: + usage(); + } + + argv += optind; + argc -= optind; + + linkf = sflag ? symlink : link; + + switch(argc) { + case 0: + usage(); + case 1: /* ln target */ + exit(linkit(argv[0], ".", 1)); + case 2: /* ln target source */ + exit(linkit(argv[0], argv[1], 0)); + } + /* ln target1 target2 directory */ + sourcedir = argv[argc - 1]; + if (stat(sourcedir, &sb)) + err(1, "%s", sourcedir); + if (!S_ISDIR(sb.st_mode)) + usage(); + for (exitval = 0; *argv != sourcedir; ++argv) + exitval |= linkit(*argv, sourcedir, 1); + exit(exitval); +} + +int +linkit(target, source, isdir) + char *target, *source; + int isdir; +{ + struct stat sb; + int exists; + char *p, path[MAXPATHLEN]; + + if (!sflag) { + /* If target doesn't exist, quit now. */ + if (stat(target, &sb)) { + warn("%s", target); + return (1); + } + /* Only symbolic links to directories, unless -F option used. */ + if (!dirflag && (sb.st_mode & S_IFMT) == S_IFDIR) { + warnx("%s: is a directory", target); + return (1); + } + } + + /* If the source is a directory, append the target's name. */ + if (isdir || (exists = !stat(source, &sb)) && S_ISDIR(sb.st_mode)) { + if ((p = strrchr(target, '/')) == NULL) + p = target; + else + ++p; + (void)snprintf(path, sizeof(path), "%s/%s", source, p); + source = path; + exists = !stat(source, &sb); + } else + exists = !stat(source, &sb); + + /* + * If the file exists, and -f was specified, unlink it. + * Attempt the link. + */ + if (fflag && exists && unlink(source) || (*linkf)(target, source)) { + warn("%s", source); + return (1); + } + return (0); +} + +void +usage() +{ + (void)fprintf(stderr, + "usage:\tln [-fs] file1 file2\n\tln [-fs] file ... directory\n"); + exit(1); +} diff --git a/bin/ln/symlink.7 b/bin/ln/symlink.7 new file mode 100644 index 0000000..0f4887d --- /dev/null +++ b/bin/ln/symlink.7 @@ -0,0 +1,432 @@ +.\" Copyright (c) 1992, 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. +.\" +.\" @(#)symlink.7 8.3 (Berkeley) 3/31/94 +.\" +.Dd March 31, 1994 +.Dt SYMLINK 7 +.Os BSD 4 +.Sh NAME +.Nm symlink +.Nd symbolic link handling +.Sh SYMBOLIC LINK HANDLING +Symbolic links are files that act as pointers to other files. +To understand their behavior, you must first understand how hard links +work. +A hard link to a file is indistinguishable from the original file because +it is a reference to the object underlying the original file name. +Changes to a file are independent of the name used to reference the +file. +Hard links may not refer to directories and may not reference files +on different file systems. +A symbolic link contains the name of the file to which it is linked, +i.e. it is a pointer to another name, and not to an underlying object. +For this reason, symbolic links may reference directories and may span +file systems. +.Pp +Because a symbolic link and its referenced object coexist in the filesystem +name space, confusion can arise in distinguishing between the link itself +and the referenced object. +Historically, commands and system calls have adopted their own link +following conventions in a somewhat ad-hoc fashion. +Rules for more a uniform approach, as they are implemented in this system, +are outlined here. +It is important that local applications conform to these rules, too, +so that the user interface can be as consistent as possible. +.Pp +Symbolic links are handled either by operating on the link itself, +or by operating on the object referenced by the link. +In the latter case, +an application or system call is said to +.Dq follow +the link. +Symbolic links may reference other symbolic links, +in which case the links are dereferenced until an object that is +not a symbolic link is found, +a symbolic link which references a file which doesn't exist is found, +or a loop is detected. +(Loop detection is done by placing an upper limit on the number of +links that may be followed, and an error results if this limit is +exceeded.) +.Pp +There are three separate areas that need to be discussed. +They are as follows: +.sp +.Bl -enum -compact -offset indent +.It +Symbolic links used as file name arguments for system calls. +.It +Symbolic links specified as command line arguments to utilities that +are not traversing a file tree. +.It +Symbolic links encountered by utilities that are traversing a file tree +(either specified on the command line or encountered as part of the +file hierarchy walk). +.El +.Ss System calls. +The first area is symbolic links used as file name arguments for +system calls. +.Pp +Except as noted below, all system calls follow symbolic links. +For example, if there were a symbolic link +.Dq Li slink +which pointed to a file named +.Dq Li afile , +the system call +.Dq Li open("slink" ...) +would return a file descriptor to the file +.Dq afile . +.Pp +There are four system calls that do not follow links, and which operate +on the symbolic link itself. +They are: +.Xr lstat 2 , +.Xr readlink 2 , +.Xr rename 2 , +and +.Xr unlink 2 . +Because +.Xr remove 3 +is an alias for +.Xr unlink 2 , +it also does not follow symbolic links. +.Pp +Unlike other filesystem objects, symbolic links do not have an owner, +group, permissions, access and modification times, etc. +The only attributes returned from an +.Xr lstat 2 +that refer to the symbolic link itself are the file type (S_IFLNK), +size, blocks, and link count (always 1). +The other attributes are filled in from the directory that contains +the link. +For portability reasons, you should be aware that other implementations +(including historic implementations of 4BSD), implement symbolic links +such that they have the same attributes as any other file. +.Pp +The +.Bx 4.4 +system differs from historical 4BSD systems in that the system call +.Xr chown 2 +has been changed to follow symbolic links. +.Ss Commands not traversing a file tree. +The second area is symbolic links, specified as command line file +name arguments, to commands which are not traversing a file tree. +.Pp +Except as noted below, commands follow symbolic links named as command +line arguments. +For example, if there were a symbolic link +.Dq Li slink +which pointed to a file named +.Dq Li afile , +the command +.Dq Li cat slink +would display the contents of the file +.Dq Li afile . +.Pp +It is important to realize that this rule includes commands which may +optionally traverse file trees, e.g. the command +.Dq Li "chown file" +is included in this rule, while the command +.Dq Li "chown -R file" +is not. +(The latter is described in the third area, below.) +.Pp +If it is explicitly intended that the command operate on the symbolic +link instead of following the symbolic link, e.g., it is desired that +.Dq Li "file slink" +display the type of file that +.Dq Li slink +is, whether it is a symbolic link or not, the +.Fl h +option should be used. +In the above example, +.Dq Li "file slink" +would report the type of the file referenced by +.Dq Li slink , +while +.Dq Li "file -h slink" +would report that +.Dq Li slink +was a symbolic link. +.Pp +There are three exceptions to this rule. +The +.Xr mv 1 +and +.Xr rm 1 +commands do not follow symbolic links named as arguments, +but respectively attempt to rename and delete them. +(Note, if the symbolic link references a file via a relative path, +moving it to another directory may very well cause it to stop working, +since the path may no longer be correct.) +.Pp +The +.Xr ls 1 +command is also an exception to this rule. +For compatibility with historic systems (when +.Nm ls +is not doing a tree walk, i.e. the +.Fl R +option is not specified), +the +.Nm ls +command follows symbolic links named as arguments if the +.Fl L +option is specified, +or if the +.Fl F , +.Fl d +or +.Fl l +options are not specified. +(If the +.Fl L +option is specified, +.Nm ls +always follows symbolic links. +.Nm Ls +is the only command where the +.Fl L +option affects its behavior even though it is not doing a walk of +a file tree.) +.Pp +The +.Bx 4.4 +system differs from historical 4BSD systems in that the +.Nm chown , +.Nm chgrp +and +.Nm file +commands follow symbolic links specified on the command line. +.Ss Commands traversing a file tree. +The following commands either optionally or always traverse file trees: +.Xr chflags 1 , +.Xr chgrp 1 , +.Xr chmod 1 , +.Xr cp 1 , +.Xr du 1 , +.Xr find 1 , +.Xr ls 1 , +.Xr pax 1 , +.Xr rm 1 , +.Xr tar 1 +and +.Xr chown 8 . +.Pp +It is important to realize that the following rules apply equally to +symbolic links encountered during the file tree traversal and symbolic +links listed as command line arguments. +.Pp +The first rule applies to symbolic links that reference files that are +not of type directory. +Operations that apply to symbolic links are performed on the links +themselves, but otherwise the links are ignored. +.Pp +For example, the command +.Dq Li "chown -R user slink directory" +will ignore +.Dq Li slink , +because symbolic links in this system do not have owners. +Any symbolic links encountered during the tree traversal will also be +ignored. +The command +.Dq Li "rm -r slink directory" +will remove +.Dq Li slink , +as well as any symbolic links encountered in the tree traversal of +.Dq Li directory , +because symbolic links may be removed. +In no case will either +.Nm chown +or +.Nm rm +affect the file which +.Dq Li slink +references in any way. +.Pp +The second rule applies to symbolic links that reference files of type +directory. +Symbolic links which reference files of type directory are never +.Dq followed +by default. +This is often referred to as a +.Dq physical +walk, as opposed to a +.Dq logical +walk (where symbolic links referencing directories are followed). +.Pp +As consistently as possible, you can make commands doing a file tree +walk follow any symbolic links named on the command line, regardless +of the type of file they reference, by specifying the +.Fl H +(for +.Dq half\-logical ) +flag. +This flag is intended to make the command line name space look +like the logical name space. +(Note, for commands that do not always do file tree traversals, the +.Fl H +flag will be ignored if the +.Fl R +flag is not also specified.) +.Pp +For example, the command +.Dq Li "chown -HR user slink" +will traverse the file hierarchy rooted in the file pointed to by +.Dq Li slink . +Note, the +.Fl H +is not the same as the previously discussed +.Fl h +flag. +The +.Fl H +flag causes symbolic links specified on the command line to be +dereferenced both for the purposes of the action to be performed +and the tree walk, and it is as if the user had specified the +name of the file to which the symbolic link pointed. +.Pp +As consistently as possible, you can make commands doing a file tree +walk follow any symbolic links named on the command line, as well as +any symbolic links encountered during the traversal, regardless of +the type of file they reference, by specifying the +.Fl L +(for +.Dq logical ) +flag. +This flag is intended to make the entire name space look like +the logical name space. +(Note, for commands that do not always do file tree traversals, the +.Fl L +flag will be ignored if the +.Fl R +flag is not also specified.) +.Pp +For example, the command +.Dq Li "chown -LR user slink" +will change the owner of the file referenced by +.Dq Li slink . +If +.Dq Li slink +references a directory, +.Nm chown +will traverse the file hierarchy rooted in the directory that it +references. +In addition, if any symbolic links are encountered in any file tree that +.Nm chown +traverses, they will be treated in the same fashion as +.Dq Li slink . +.Pp +As consistently as possible, you can specify the default behavior by +specifying the +.Fl P +(for +.Dq physical ) +flag. +This flag is intended to make the entire name space look like the +physical name space. +.Pp +For commands that do not by default do file tree traversals, the +.Fl H , +.Fl L +and +.Fl P +flags are ignored if the +.Fl R +flag is not also specified. +In addition, you may specify the +.Fl H , +.Fl L +and +.Fl P +options more than once; the last one specified determines the +command's behavior. +This is intended to permit you to alias commands to behave one way +or the other, and then override that behavior on the command line. +.Pp +The +.Xr ls 1 +and +.Xr rm 1 +commands have exceptions to these rules. +The +.Nm rm +command operates on the symbolic link, and not the file it references, +and therefore never follows a symbolic link. +The +.Nm rm +command does not support the +.Fl H , +.Fl L +or +.Fl P +options. +.Pp +To maintain compatibility with historic systems, +the +.Nm ls +command never follows symbolic links unless the +.Fl L +flag is specified. +If the +.Fl L +flag is specified, +.Nm ls +follows all symbolic links, +regardless of their type, +whether specified on the command line or encountered in the tree walk. +The +.Nm ls +command does not support the +.Fl H +or +.Fl P +options. +.Sh SEE ALSO +.Xr chflags 1 , +.Xr chgrp 1 , +.Xr chmod 1 , +.Xr cp 1 , +.Xr du 1 , +.Xr find 1 , +.Xr ln 1 , +.Xr ls 1 , +.Xr mv 1 , +.Xr pax 1 , +.Xr rm 1 , +.Xr tar 1 , +.Xr lstat 2 , +.Xr readlink 2 , +.Xr rename 2 , +.Xr unlink 2 , +.Xr fts 3 , +.Xr remove 3 , +.Xr chown 8 diff --git a/bin/ls/Makefile b/bin/ls/Makefile new file mode 100644 index 0000000..dd9036b --- /dev/null +++ b/bin/ls/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 6/2/93 + +PROG= ls +SRCS= cmp.c stat_flags.c ls.c print.c util.c + +.include <bsd.prog.mk> diff --git a/bin/ls/cmp.c b/bin/ls/cmp.c new file mode 100644 index 0000000..bad4888 --- /dev/null +++ b/bin/ls/cmp.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Michael Fischbein. + * + * 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[] = "@(#)cmp.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <fts.h> +#include <string.h> + +#include "ls.h" +#include "extern.h" + +int +namecmp(a, b) + const FTSENT *a, *b; +{ + return (strcmp(a->fts_name, b->fts_name)); +} + +int +revnamecmp(a, b) + const FTSENT *a, *b; +{ + return (strcmp(b->fts_name, a->fts_name)); +} + +int +modcmp(a, b) + const FTSENT *a, *b; +{ + return (b->fts_statp->st_mtime - a->fts_statp->st_mtime); +} + +int +revmodcmp(a, b) + const FTSENT *a, *b; +{ + return (a->fts_statp->st_mtime - b->fts_statp->st_mtime); +} + +int +acccmp(a, b) + const FTSENT *a, *b; +{ + return (b->fts_statp->st_atime - a->fts_statp->st_atime); +} + +int +revacccmp(a, b) + const FTSENT *a, *b; +{ + return (a->fts_statp->st_atime - b->fts_statp->st_atime); +} + +int +statcmp(a, b) + const FTSENT *a, *b; +{ + return (b->fts_statp->st_ctime - a->fts_statp->st_ctime); +} + +int +revstatcmp(a, b) + const FTSENT *a, *b; +{ + return (a->fts_statp->st_ctime - b->fts_statp->st_ctime); +} diff --git a/bin/ls/extern.h b/bin/ls/extern.h new file mode 100644 index 0000000..f69e4b8 --- /dev/null +++ b/bin/ls/extern.h @@ -0,0 +1,50 @@ +/*- + * 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 + */ + +int acccmp __P((const FTSENT *, const FTSENT *)); +int revacccmp __P((const FTSENT *, const FTSENT *)); +int modcmp __P((const FTSENT *, const FTSENT *)); +int revmodcmp __P((const FTSENT *, const FTSENT *)); +int namecmp __P((const FTSENT *, const FTSENT *)); +int revnamecmp __P((const FTSENT *, const FTSENT *)); +int statcmp __P((const FTSENT *, const FTSENT *)); +int revstatcmp __P((const FTSENT *, const FTSENT *)); + +char *flags_to_string __P((u_long, char *)); +void prcopy __P((char *, char *, int)); +void printcol __P((DISPLAY *)); +void printlong __P((DISPLAY *)); +void printscol __P((DISPLAY *)); +void usage __P((void)); diff --git a/bin/ls/ls.1 b/bin/ls/ls.1 new file mode 100644 index 0000000..cb45555 --- /dev/null +++ b/bin/ls/ls.1 @@ -0,0 +1,333 @@ +.\" Copyright (c) 1980, 1990, 1991, 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. +.\" +.\" @(#)ls.1 8.6 (Berkeley) 4/18/94 +.\" +.Dd April 18, 1994 +.Dt LS 1 +.Os +.Sh NAME +.Nm ls +.Nd list directory contents +.Sh SYNOPSIS +.Nm ls +.Op Fl ACFLRTacdfiloqrstu1 +.Op Ar file ... +.Sh DESCRIPTION +For each operand that names a +.Ar file +of a type other than +directory, +.Nm ls +displays its name as well as any requested, +associated information. +For each operand that names a +.Ar file +of type directory, +.Nm ls +displays the names of files contained +within that directory, as well as any requested, associated +information. +.Pp +If no operands are given, the contents of the current +directory are displayed. +If more than one operand is given, +non-directory operands are displayed first; directory +and non-directory operands are sorted separately and in +lexicographical order. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl A +List all entries except for +.Ql \&. +and +.Ql \&.. . +Always set for the super-user. +.It Fl C +Force multi-column output; this is the default when output is to a terminal. +.It Fl F +Display a slash (/) immediately after each pathname +that is a directory, an asterisk (*) after each that is +executable, +and an at sign (@) after each symbolic link. +.\" and a vertical bar (|) after each that is a +.\" .Tn FIFO . +.It Fl L +If argument is a symbolic link, list the file or directory the link references +rather than the link itself. +.It Fl R +Recursively list subdirectories encountered. +.It Fl T +Display complete time information for the file, including +month, day, hour, minute, second, and year. +.It Fl a +Include directory entries whose names begin with a +dot (.). +.It Fl c +Use time when file status was last changed for sorting or printing. +.It Fl d +Directories are listed as plain files (not searched recursively) and +symbolic links in the argument list are not indirected through. +.It Fl f +Output is not sorted. +.It Fl i +For each file, print the file's file serial number (inode number). +.It Fl l +(The lowercase letter ``ell.'') List in long format. (See below.) +If the output is to a terminal, a total sum for all the file +sizes is output on a line before the long listing. +.It Fl o +Include the file flags in a long +.Pq Fl l +output +.It Fl q +Force printing of non-graphic characters in file names as +the character `?'; this is the default when output is to a terminal. +.It Fl r +Reverse the order of the sort to get reverse +lexicographical order or the oldest entries first. +.It Fl s +Display the number of file system blocks actually used by each file, in units +of 512 bytes, where partial units are rounded up to the next integer value. +If the output is to a terminal, a total sum for all the file +sizes is output on a line before the listing. +.It Fl t +Sort by time modified (most recently modified +first) before sorting the operands by lexicographical +order. +.It Fl u +Use time of last access, +instead of last modification +of the file for sorting +.Pq Fl t +or printing +.Pq Fl l . +.It Fl \&1 +(The numeric digit ``one.'') Force output to be +one entry per line. +This is the default when +output is not to a terminal. +.El +.Pp +The +.Fl 1 , +.Fl C , +and +.Fl l +options all override each other; the last one specified determines +the format used. +.Pp +The +.Fl c , +and +.Fl u +options override each other; the last one specified determines +the file time used. +.Pp +By default, +.Nm ls +lists one entry per line to standard +output; the exceptions are to terminals or when the +.Fl C +option is specified. +.Pp +File information is displayed with one or more +<blank>s separating the information associated with the +.Fl i , +.Fl s , +and +.Fl l +options. +.Ss The Long Format +If the +.Fl l +option is given, the following information +is displayed for each file: +file mode, +number of links, owner name, group name, +number of bytes in the file, abbreviated +month, day-of-month file was last modified, +hour file last modified, minute file last +modified, and the pathname. +In addition, for each directory whose contents are displayed, the total +number of 512-byte blocks used by the files in the directory is displayed +on a line by itself immediately before the information for the files in the +directory. +.Pp +If the owner or group names are not a known user or group name +the numeric ID's are displayed. +.Pp +If the file is a character special or block special file, +the major and minor device numbers for the file are displayed +in the size field. If the file is a symbolic link the pathname of the +linked-to file is preceded by +.Dq \-> . +.Pp +The file mode printed under the -l option consists of the +entry type, owner permissions, and group permissions. +The entry type character describes the type of file, as +follows: +.Pp +.Bl -tag -width 4n -offset indent -compact +.It Sy b +Block special file. +.It Sy c +Character special file. +.It Sy d +Directory. +.It Sy l +Symbolic link. +.It Sy s +Socket link. +.\" .It Sy p +.\" .Tn FIFO . +.It Sy \- +Regular file. +.El +.Pp +The next three fields +are three characters each: +owner permissions, +group permissions, and +other permissions. +Each field has three character positions: +.Bl -enum -offset indent +.It +If +.Sy r , +the file is readable; if +.Sy \- , +it is not readable. +.It +If +.Sy w , +the file is writable; if +.Sy \- , +it is not writable. +.It +The first of the following that applies: +.Bl -tag -width 4n -offset indent +.It Sy S +If in the owner permissions, the file is not executable and +set-user-ID mode is set. +If in the group permissions, the file is not executable +and set-group-ID mode is set. +.It Sy s +If in the owner permissions, the file is executable +and set-user-ID mode is set. +If in the group permissions, the file is executable +and setgroup-ID mode is set. +.It Sy x +The file is executable or the directory is +searchable. +.It Sy \- +The file is neither readable, writeable, executable, +nor set-user-ID nor set-group-ID mode, nor sticky. (See below.) +.El +.Pp +These next two apply only to the third character in the last group +(other permissions). +.Bl -tag -width 4n -offset indent +.It Sy T +The sticky bit is set +(mode +.Li 1000 ) , +but not execute or search permission. (See +.Xr chmod 1 +or +.Xr sticky 8 . ) +.It Sy t +The sticky bit is set (mode +.Li 1000 ) , +and is searchable or executable. +(See +.Xr chmod 1 +or +.Xr sticky 8 . ) +.El +.El +.Pp +The +.Nm ls +utility exits 0 on success, and >0 if an error occurs. +.Sh ENVIRONMENTAL VARIABLES +The following environment variables affect the execution of +.Nm ls : +.Bl -tag -width BLOCKSIZE +.It Ev BLOCKSIZE +If the environmental variable +.Ev BLOCKSIZE +is set, the block counts +(see +.Fl s ) +will be displayed in units of that size block. +.It COLUMNS +If this variable contains a string representing a +decimal integer, it is used as the +column position width for displaying +multiple-text-column output. +The +.Nm ls +utility calculates how +many pathname text columns to display +based on the width provided. +(See +.Fl C . ) +.It Ev TZ +The timezone to use when displaying dates. +See +.Xr environ 7 +for more information. +.El +.Sh COMPATIBILITY +The group field is now automatically included in the long listing for +files in order to be compatible with the +.St -p1003.2 +specification. +.Sh SEE ALSO +.Xr chmod 1 , +.Xr symlink 7 , +.Xr sticky 8 +.Sh HISTORY +An +.Nm ls +command appeared in +.At v6 . +.Sh STANDARDS +The +.Nm ls +function is expected to be a superset of the +.St -p1003.2 +specification. diff --git a/bin/ls/ls.c b/bin/ls/ls.c new file mode 100644 index 0000000..1cea616 --- /dev/null +++ b/bin/ls/ls.c @@ -0,0 +1,506 @@ +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Michael Fischbein. + * + * 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[] = "@(#)ls.c 8.5 (Berkeley) 4/2/94"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> + +#include <dirent.h> +#include <err.h> +#include <errno.h> +#include <fts.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "ls.h" +#include "extern.h" + +static void display __P((FTSENT *, FTSENT *)); +static int mastercmp __P((const FTSENT **, const FTSENT **)); +static void traverse __P((int, char **, int)); + +static void (*printfcn) __P((DISPLAY *)); +static int (*sortfcn) __P((const FTSENT *, const FTSENT *)); + +long blocksize; /* block size units */ +int termwidth = 80; /* default terminal width */ + +/* flags */ +int f_accesstime; /* use time of last access */ +int f_column; /* columnated format */ +int f_flags; /* show flags associated with a file */ +int f_inode; /* print inode */ +int f_listdir; /* list actual directory, not contents */ +int f_listdot; /* list files beginning with . */ +int f_longform; /* long listing format */ +int f_newline; /* if precede with newline */ +int f_nonprint; /* show unprintables as ? */ +int f_nosort; /* don't sort output */ +int f_recursive; /* ls subdirectories also */ +int f_reversesort; /* reverse whatever sort is used */ +int f_sectime; /* print the real time for all files */ +int f_singlecol; /* use single column output */ +int f_size; /* list size in short listing */ +int f_statustime; /* use time of last mode change */ +int f_dirname; /* if precede with directory name */ +int f_timesort; /* sort by time vice name */ +int f_type; /* add type character for non-regular files */ + +int +main(argc, argv) + int argc; + char *argv[]; +{ + static char dot[] = ".", *dotav[] = { dot, NULL }; + struct winsize win; + int ch, fts_options, notused; + char *p; + + /* Terminal defaults to -Cq, non-terminal defaults to -1. */ + if (isatty(STDOUT_FILENO)) { + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == -1 || + !win.ws_col) { + if ((p = getenv("COLUMNS")) != NULL) + termwidth = atoi(p); + } + else + termwidth = win.ws_col; + f_column = f_nonprint = 1; + } else + f_singlecol = 1; + + /* Root is -A automatically. */ + if (!getuid()) + f_listdot = 1; + + fts_options = FTS_PHYSICAL; + while ((ch = getopt(argc, argv, "1ACFLRTacdfgiloqrstu")) != EOF) { + switch (ch) { + /* + * The -1, -C and -l options all override each other so shell + * aliasing works right. + */ + case '1': + f_singlecol = 1; + f_column = f_longform = 0; + break; + case 'C': + f_column = 1; + f_longform = f_singlecol = 0; + break; + case 'l': + f_longform = 1; + f_column = f_singlecol = 0; + break; + /* The -c and -u options override each other. */ + case 'c': + f_statustime = 1; + f_accesstime = 0; + break; + case 'u': + f_accesstime = 1; + f_statustime = 0; + break; + case 'F': + f_type = 1; + break; + case 'L': + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + break; + case 'R': + f_recursive = 1; + break; + case 'a': + fts_options |= FTS_SEEDOT; + /* FALLTHROUGH */ + case 'A': + f_listdot = 1; + break; + /* The -d option turns off the -R option. */ + case 'd': + f_listdir = 1; + f_recursive = 0; + break; + case 'f': + f_nosort = 1; + break; + case 'g': /* Compatibility with 4.3BSD. */ + break; + case 'i': + f_inode = 1; + break; + case 'o': + f_flags = 1; + break; + case 'q': + f_nonprint = 1; + break; + case 'r': + f_reversesort = 1; + break; + case 's': + f_size = 1; + break; + case 'T': + f_sectime = 1; + break; + case 't': + f_timesort = 1; + break; + default: + case '?': + usage(); + } + } + argc -= optind; + argv += optind; + + /* + * If not -F, -i, -l, -s or -t options, don't require stat + * information. + */ + if (!f_inode && !f_longform && !f_size && !f_timesort && !f_type) + fts_options |= FTS_NOSTAT; + + /* + * If not -F, -d or -l options, follow any symbolic links listed on + * the command line. + */ + if (!f_longform && !f_listdir && !f_type) + fts_options |= FTS_COMFOLLOW; + + /* If -l or -s, figure out block size. */ + if (f_longform || f_size) { + (void)getbsize(¬used, &blocksize); + blocksize /= 512; + } + + /* Select a sort function. */ + if (f_reversesort) { + if (!f_timesort) + sortfcn = revnamecmp; + else if (f_accesstime) + sortfcn = revacccmp; + else if (f_statustime) + sortfcn = revstatcmp; + else /* Use modification time. */ + sortfcn = revmodcmp; + } else { + if (!f_timesort) + sortfcn = namecmp; + else if (f_accesstime) + sortfcn = acccmp; + else if (f_statustime) + sortfcn = statcmp; + else /* Use modification time. */ + sortfcn = modcmp; + } + + /* Select a print function. */ + if (f_singlecol) + printfcn = printscol; + else if (f_longform) + printfcn = printlong; + else + printfcn = printcol; + + if (argc) + traverse(argc, argv, fts_options); + else + traverse(1, dotav, fts_options); + exit(0); +} + +static int output; /* If anything output. */ + +/* + * Traverse() walks the logical directory structure specified by the argv list + * in the order specified by the mastercmp() comparison function. During the + * traversal it passes linked lists of structures to display() which represent + * a superset (may be exact set) of the files to be displayed. + */ +static void +traverse(argc, argv, options) + int argc, options; + char *argv[]; +{ + FTS *ftsp; + FTSENT *p, *chp; + int ch_options; + + if ((ftsp = + fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL) + err(1, NULL); + + display(NULL, fts_children(ftsp, 0)); + if (f_listdir) + return; + + /* + * If not recursing down this tree and don't need stat info, just get + * the names. + */ + ch_options = !f_recursive && options & FTS_NOSTAT ? FTS_NAMEONLY : 0; + + while ((p = fts_read(ftsp)) != NULL) + switch (p->fts_info) { + case FTS_DC: + warnx("%s: directory causes a cycle", p->fts_name); + break; + case FTS_DNR: + case FTS_ERR: + warnx("%s: %s", p->fts_name, strerror(p->fts_errno)); + break; + case FTS_D: + if (p->fts_level != FTS_ROOTLEVEL && + p->fts_name[0] == '.' && !f_listdot) + break; + + /* + * If already output something, put out a newline as + * a separator. If multiple arguments, precede each + * directory with its name. + */ + if (output) + (void)printf("\n%s:\n", p->fts_path); + else if (argc > 1) { + (void)printf("%s:\n", p->fts_path); + output = 1; + } + + chp = fts_children(ftsp, ch_options); + display(p, chp); + + if (!f_recursive && chp != NULL) + (void)fts_set(ftsp, p, FTS_SKIP); + break; + } + if (errno) + err(1, "fts_read"); +} + +/* + * Display() takes a linked list of FTSENT structures and passes the list + * along with any other necessary information to the print function. P + * points to the parent directory of the display list. + */ +static void +display(p, list) + FTSENT *p, *list; +{ + struct stat *sp; + DISPLAY d; + FTSENT *cur; + NAMES *np; + u_quad_t maxsize; + u_long btotal, maxblock, maxinode, maxlen, maxnlink; + int bcfile, flen, glen, ulen, maxflags, maxgroup, maxuser; + int entries, needstats; + char *user, *group, *flags, buf[20]; /* 32 bits == 10 digits */ + + /* + * If list is NULL there are two possibilities: that the parent + * directory p has no children, or that fts_children() returned an + * error. We ignore the error case since it will be replicated + * on the next call to fts_read() on the post-order visit to the + * directory p, and will be signalled in traverse(). + */ + if (list == NULL) + return; + + needstats = f_inode || f_longform || f_size; + flen = 0; + btotal = maxblock = maxinode = maxlen = maxnlink = 0; + bcfile = 0; + maxuser = maxgroup = maxflags = 0; + maxsize = 0; + for (cur = list, entries = 0; cur; cur = cur->fts_link) { + if (cur->fts_info == FTS_ERR || cur->fts_info == FTS_NS) { + warnx("%s: %s", + cur->fts_name, strerror(cur->fts_errno)); + cur->fts_number = NO_PRINT; + continue; + } + + /* + * P is NULL if list is the argv list, to which different rules + * apply. + */ + if (p == NULL) { + /* Directories will be displayed later. */ + if (cur->fts_info == FTS_D && !f_listdir) { + cur->fts_number = NO_PRINT; + continue; + } + } else { + /* Only display dot file if -a/-A set. */ + if (cur->fts_name[0] == '.' && !f_listdot) { + cur->fts_number = NO_PRINT; + continue; + } + } + if (f_nonprint) + prcopy(cur->fts_name, cur->fts_name, cur->fts_namelen); + if (cur->fts_namelen > maxlen) + maxlen = cur->fts_namelen; + if (needstats) { + sp = cur->fts_statp; + if (sp->st_blocks > maxblock) + maxblock = sp->st_blocks; + if (sp->st_ino > maxinode) + maxinode = sp->st_ino; + if (sp->st_nlink > maxnlink) + maxnlink = sp->st_nlink; + if (sp->st_size > maxsize) + maxsize = sp->st_size; + + btotal += sp->st_blocks; + if (f_longform) { + user = user_from_uid(sp->st_uid, 0); + if ((ulen = strlen(user)) > maxuser) + maxuser = ulen; + group = group_from_gid(sp->st_gid, 0); + if ((glen = strlen(group)) > maxgroup) + maxgroup = glen; + if (f_flags) { + flags = + flags_to_string(sp->st_flags, "-"); + if ((flen = strlen(flags)) > maxflags) + maxflags = flen; + } else + flen = 0; + + if ((np = malloc(sizeof(NAMES) + + ulen + glen + flen + 3)) == NULL) + err(1, NULL); + + np->user = &np->data[0]; + (void)strcpy(np->user, user); + np->group = &np->data[ulen + 1]; + (void)strcpy(np->group, group); + + if (S_ISCHR(sp->st_mode) || + S_ISBLK(sp->st_mode)) + bcfile = 1; + + if (f_flags) { + np->flags = &np->data[ulen + glen + 2]; + (void)strcpy(np->flags, flags); + } + cur->fts_pointer = np; + } + } + ++entries; + } + + if (!entries) + return; + + d.list = list; + d.entries = entries; + d.maxlen = maxlen; + if (needstats) { + d.bcfile = bcfile; + d.btotal = btotal; + (void)snprintf(buf, sizeof(buf), "%lu", maxblock); + d.s_block = strlen(buf); + d.s_flags = maxflags; + d.s_group = maxgroup; + (void)snprintf(buf, sizeof(buf), "%lu", maxinode); + d.s_inode = strlen(buf); + (void)snprintf(buf, sizeof(buf), "%lu", maxnlink); + d.s_nlink = strlen(buf); + (void)snprintf(buf, sizeof(buf), "%qu", maxsize); + d.s_size = strlen(buf); + d.s_user = maxuser; + } + + printfcn(&d); + output = 1; + + if (f_longform) + for (cur = list; cur; cur = cur->fts_link) + free(cur->fts_pointer); +} + +/* + * Ordering for mastercmp: + * If ordering the argv (fts_level = FTS_ROOTLEVEL) return non-directories + * as larger than directories. Within either group, use the sort function. + * All other levels use the sort function. Error entries remain unsorted. + */ +static int +mastercmp(a, b) + const FTSENT **a, **b; +{ + int a_info, b_info; + + a_info = (*a)->fts_info; + if (a_info == FTS_ERR) + return (0); + b_info = (*b)->fts_info; + if (b_info == FTS_ERR) + return (0); + + if (a_info == FTS_NS || b_info == FTS_NS) + return (namecmp(*a, *b)); + + if (a_info == b_info) + return (sortfcn(*a, *b)); + + if ((*a)->fts_level == FTS_ROOTLEVEL) + if (a_info == FTS_D) + return (1); + else if (b_info == FTS_D) + return (-1); + else + return (sortfcn(*a, *b)); + else + return (sortfcn(*a, *b)); +} diff --git a/bin/ls/ls.h b/bin/ls/ls.h new file mode 100644 index 0000000..ab61480 --- /dev/null +++ b/bin/ls/ls.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Michael Fischbein. + * + * 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. + * + * @(#)ls.h 8.1 (Berkeley) 5/31/93 + */ + +#define NO_PRINT 1 + +extern long blocksize; /* block size units */ + +extern int f_accesstime; /* use time of last access */ +extern int f_flags; /* show flags associated with a file */ +extern int f_inode; /* print inode */ +extern int f_longform; /* long listing format */ +extern int f_sectime; /* print the real time for all files */ +extern int f_size; /* list size in short listing */ +extern int f_statustime; /* use time of last mode change */ +extern int f_type; /* add type character for non-regular files */ + +typedef struct { + FTSENT *list; + u_long btotal; + int bcfile; + int entries; + int maxlen; + int s_block; + int s_flags; + int s_group; + int s_inode; + int s_nlink; + int s_size; + int s_user; +} DISPLAY; + +typedef struct { + char *user; + char *group; + char *flags; + char data[1]; +} NAMES; diff --git a/bin/ls/print.c b/bin/ls/print.c new file mode 100644 index 0000000..5c068e9 --- /dev/null +++ b/bin/ls/print.c @@ -0,0 +1,293 @@ +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Michael Fischbein. + * + * 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[] = "@(#)print.c 8.4 (Berkeley) 4/17/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> + +#include <err.h> +#include <errno.h> +#include <fts.h> +#include <grp.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <tzfile.h> +#include <unistd.h> +#include <utmp.h> + +#include "ls.h" +#include "extern.h" + +static int printaname __P((FTSENT *, u_long, u_long)); +static void printlink __P((FTSENT *)); +static void printtime __P((time_t)); +static int printtype __P((u_int)); + +#define IS_NOPRINT(p) ((p)->fts_number == NO_PRINT) + +void +printscol(dp) + DISPLAY *dp; +{ + FTSENT *p; + + for (p = dp->list; p; p = p->fts_link) { + if (IS_NOPRINT(p)) + continue; + (void)printaname(p, dp->s_inode, dp->s_block); + (void)putchar('\n'); + } +} + +void +printlong(dp) + DISPLAY *dp; +{ + struct stat *sp; + FTSENT *p; + NAMES *np; + char buf[20]; + + if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size)) + (void)printf("total %lu\n", howmany(dp->btotal, blocksize)); + + for (p = dp->list; p; p = p->fts_link) { + if (IS_NOPRINT(p)) + continue; + sp = p->fts_statp; + if (f_inode) + (void)printf("%*lu ", dp->s_inode, sp->st_ino); + if (f_size) + (void)printf("%*qd ", + dp->s_block, howmany(sp->st_blocks, blocksize)); + (void)strmode(sp->st_mode, buf); + np = p->fts_pointer; + (void)printf("%s %*u %-*s %-*s ", buf, dp->s_nlink, + sp->st_nlink, dp->s_user, np->user, dp->s_group, + np->group); + if (f_flags) + (void)printf("%-*s ", dp->s_flags, np->flags); + if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode)) + (void)printf("%3d, %3d ", + major(sp->st_rdev), minor(sp->st_rdev)); + else if (dp->bcfile) + (void)printf("%*s%*qd ", + 8 - dp->s_size, "", dp->s_size, sp->st_size); + else + (void)printf("%*qd ", dp->s_size, sp->st_size); + if (f_accesstime) + printtime(sp->st_atime); + else if (f_statustime) + printtime(sp->st_ctime); + else + printtime(sp->st_mtime); + (void)printf("%s", p->fts_name); + if (f_type) + (void)printtype(sp->st_mode); + if (S_ISLNK(sp->st_mode)) + printlink(p); + (void)putchar('\n'); + } +} + +#define TAB 8 + +void +printcol(dp) + DISPLAY *dp; +{ + extern int termwidth; + static FTSENT **array; + static int lastentries = -1; + FTSENT *p; + int base, chcnt, cnt, col, colwidth, num; + int endcol, numcols, numrows, row; + + /* + * Have to do random access in the linked list -- build a table + * of pointers. + */ + if (dp->entries > lastentries) { + lastentries = dp->entries; + if ((array = + realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) { + warn(NULL); + printscol(dp); + } + } + for (p = dp->list, num = 0; p; p = p->fts_link) + if (p->fts_number != NO_PRINT) + array[num++] = p; + + colwidth = dp->maxlen; + if (f_inode) + colwidth += dp->s_inode + 1; + if (f_size) + colwidth += dp->s_block + 1; + if (f_type) + colwidth += 1; + + colwidth = (colwidth + TAB) & ~(TAB - 1); + if (termwidth < 2 * colwidth) { + printscol(dp); + return; + } + + numcols = termwidth / colwidth; + numrows = num / numcols; + if (num % numcols) + ++numrows; + + if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size)) + (void)printf("total %lu\n", howmany(dp->btotal, blocksize)); + for (row = 0; row < numrows; ++row) { + endcol = colwidth; + for (base = row, chcnt = col = 0; col < numcols; ++col) { + chcnt += printaname(array[base], dp->s_inode, + dp->s_block); + if ((base += numrows) >= num) + break; + while ((cnt = (chcnt + TAB & ~(TAB - 1))) <= endcol) { + (void)putchar('\t'); + chcnt = cnt; + } + endcol += colwidth; + } + (void)putchar('\n'); + } +} + +/* + * print [inode] [size] name + * return # of characters printed, no trailing characters. + */ +static int +printaname(p, inodefield, sizefield) + FTSENT *p; + u_long sizefield, inodefield; +{ + struct stat *sp; + int chcnt; + + sp = p->fts_statp; + chcnt = 0; + if (f_inode) + chcnt += printf("%*lu ", (int)inodefield, sp->st_ino); + if (f_size) + chcnt += printf("%*qd ", + (int)sizefield, howmany(sp->st_blocks, blocksize)); + chcnt += printf("%s", p->fts_name); + if (f_type) + chcnt += printtype(sp->st_mode); + return (chcnt); +} + +static void +printtime(ftime) + time_t ftime; +{ + int i; + char *longstring; + + longstring = ctime(&ftime); + for (i = 4; i < 11; ++i) + (void)putchar(longstring[i]); + +#define SIXMONTHS ((DAYSPERNYEAR / 2) * SECSPERDAY) + if (f_sectime) + for (i = 11; i < 24; i++) + (void)putchar(longstring[i]); + else if (ftime + SIXMONTHS > time(NULL)) + for (i = 11; i < 16; ++i) + (void)putchar(longstring[i]); + else { + (void)putchar(' '); + for (i = 20; i < 24; ++i) + (void)putchar(longstring[i]); + } + (void)putchar(' '); +} + +static int +printtype(mode) + u_int mode; +{ + switch (mode & S_IFMT) { + case S_IFDIR: + (void)putchar('/'); + return (1); + case S_IFIFO: + (void)putchar('|'); + return (1); + case S_IFLNK: + (void)putchar('@'); + return (1); + case S_IFSOCK: + (void)putchar('='); + return (1); + } + if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { + (void)putchar('*'); + return (1); + } + return (0); +} + +static void +printlink(p) + FTSENT *p; +{ + int lnklen; + char name[MAXPATHLEN + 1], path[MAXPATHLEN + 1]; + + if (p->fts_level == FTS_ROOTLEVEL) + (void)snprintf(name, sizeof(name), "%s", p->fts_name); + else + (void)snprintf(name, sizeof(name), + "%s/%s", p->fts_parent->fts_accpath, p->fts_name); + if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) { + (void)fprintf(stderr, "\nls: %s: %s\n", name, strerror(errno)); + return; + } + path[lnklen] = '\0'; + (void)printf(" -> %s", path); +} diff --git a/bin/ls/stat_flags.c b/bin/ls/stat_flags.c new file mode 100644 index 0000000..5bc4b85 --- /dev/null +++ b/bin/ls/stat_flags.c @@ -0,0 +1,148 @@ +/*- + * Copyright (c) 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[] = "@(#)stat_flags.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <stddef.h> +#include <string.h> + +#define SAPPEND(s) { \ + if (prefix != NULL) \ + (void)strcat(string, prefix); \ + (void)strcat(string, s); \ + prefix = ","; \ +} + +/* + * flags_to_string -- + * Convert stat flags to a comma-separated string. If no flags + * are set, return the default string. + */ +char * +flags_to_string(flags, def) + u_long flags; + char *def; +{ + static char string[128]; + char *prefix; + + string[0] = '\0'; + prefix = NULL; + if (flags & UF_APPEND) + SAPPEND("uappnd"); + if (flags & UF_IMMUTABLE) + SAPPEND("uchg"); + if (flags & UF_NODUMP) + SAPPEND("nodump"); + if (flags & SF_APPEND) + SAPPEND("sappnd"); + if (flags & SF_ARCHIVED) + SAPPEND("arch"); + if (flags & SF_IMMUTABLE) + SAPPEND("schg"); + return (prefix == NULL && def != NULL ? def : string); +} + +#define TEST(a, b, f) { \ + if (!memcmp(a, b, sizeof(b))) { \ + if (clear) { \ + if (clrp) \ + *clrp |= (f); \ + } else if (setp) \ + *setp |= (f); \ + break; \ + } \ +} + +/* + * string_to_flags -- + * Take string of arguments and return stat flags. Return 0 on + * success, 1 on failure. On failure, stringp is set to point + * to the offending token. + */ +int +string_to_flags(stringp, setp, clrp) + char **stringp; + u_long *setp, *clrp; +{ + int clear; + char *string, *p; + + clear = 0; + if (setp) + *setp = 0; + if (clrp) + *clrp = 0; + string = *stringp; + while ((p = strsep(&string, "\t ,")) != NULL) { + *stringp = p; + if (*p == '\0') + continue; + if (p[0] == 'n' && p[1] == 'o') { + clear = 1; + p += 2; + } + switch (p[0]) { + case 'a': + TEST(p, "arch", SF_ARCHIVED); + TEST(p, "archived", SF_ARCHIVED); + return (1); + case 'd': + clear = !clear; + TEST(p, "dump", UF_NODUMP); + return (1); + case 's': + TEST(p, "sappnd", SF_APPEND); + TEST(p, "sappend", SF_APPEND); + TEST(p, "schg", SF_IMMUTABLE); + TEST(p, "schange", SF_IMMUTABLE); + TEST(p, "simmutable", SF_IMMUTABLE); + return (1); + case 'u': + TEST(p, "uappnd", UF_APPEND); + TEST(p, "uappend", UF_APPEND); + TEST(p, "uchg", UF_IMMUTABLE); + TEST(p, "uchange", UF_IMMUTABLE); + TEST(p, "uimmutable", UF_IMMUTABLE); + /* FALLTHROUGH */ + default: + return (1); + } + } + return (0); +} diff --git a/bin/ls/util.c b/bin/ls/util.c new file mode 100644 index 0000000..4d1173d --- /dev/null +++ b/bin/ls/util.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Michael Fischbein. + * + * 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[] = "@(#)util.c 8.3 (Berkeley) 4/2/94"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <fts.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "ls.h" +#include "extern.h" + +void +prcopy(src, dest, len) + char *src, *dest; + int len; +{ + int ch; + + while (len--) { + ch = *src++; + *dest++ = isprint(ch) ? ch : '?'; + } +} + +void +usage() +{ + (void)fprintf(stderr, "usage: ls [-1ACFLRTacdfiklqrstu] [file ...]\n"); + exit(1); +} diff --git a/bin/mkdir/Makefile b/bin/mkdir/Makefile new file mode 100644 index 0000000..88521b1 --- /dev/null +++ b/bin/mkdir/Makefile @@ -0,0 +1,5 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 + +PROG= mkdir + +.include <bsd.prog.mk> diff --git a/bin/mkdir/mkdir.1 b/bin/mkdir/mkdir.1 new file mode 100644 index 0000000..f38a5dc --- /dev/null +++ b/bin/mkdir/mkdir.1 @@ -0,0 +1,92 @@ +.\" 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. +.\" +.\" @(#)mkdir.1 8.2 (Berkeley) 1/25/94 +.\" +.Dd January 25, 1994 +.Dt MKDIR 1 +.Os +.Sh NAME +.Nm mkdir +.Nd make directories +.Sh SYNOPSIS +.Nm mkdir +.Op Fl p +.Op Fl m Ar mode +.Ar directory_name ... +.Sh DESCRIPTION +.Nm Mkdir +creates the directories named as operands, in the order specified, +using mode +.Li rwxrwxrwx (\&0777) +as modified by the current +.Xr umask 2 . +.Pp +The options are as follows: +.Pp +.Bl -tag -width indent +.It Fl m +Set the file permission bits of the final created directory to +the specified mode. +The mode argument can be in any of the formats specified to the +.Xr chmod 1 +command. +If a symbolic mode is specified, the operation characters +.Dq + +and +.Dq - +are interpreted relative to an initial mode of +.Dq a=rwx . +.It Fl p +Create intermediate directories as required. +If this option is not specified, the full path prefix of each +operand must already exist. +Intermediate directories are created with permission bits of +.Li rwxrwxrwx (\&0777) +as modified by the current umask, plus write and search +permission for the owner. +.El +.Pp +The user must have write permission in the parent directory. +.Pp +.Nm Mkdir +exits 0 if successful, and >0 if an error occurred. +.Sh SEE ALSO +.Xr rmdir 1 +.Sh STANDARDS +The +.Nm mkdir +utility is expected to be +.St -p1003.2 +compatible. diff --git a/bin/mkdir/mkdir.c b/bin/mkdir/mkdir.c new file mode 100644 index 0000000..32d45d5 --- /dev/null +++ b/bin/mkdir/mkdir.c @@ -0,0 +1,169 @@ +/* + * Copyright (c) 1983, 1992, 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) 1983, 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mkdir.c 8.2 (Berkeley) 1/25/94"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +int build __P((char *)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch, exitval, oct, omode, pflag; + mode_t *set; + char *ep, *mode; + + pflag = 0; + mode = NULL; + while ((ch = getopt(argc, argv, "m:p")) != EOF) + switch(ch) { + case 'p': + pflag = 1; + break; + case 'm': + mode = optarg; + break; + case '?': + default: + usage(); + } + + argc -= optind; + argv += optind; + if (argv[0] == NULL) + usage(); + + if (mode == NULL) { + omode = S_IRWXU | S_IRWXG | S_IRWXO; + oct = 1; + } else if (*mode >= '0' && *mode <= '7') { + omode = (int)strtol(mode, &ep, 8); + if (omode < 0 || *ep) + errx(1, "invalid file mode: %s", mode); + oct = 1; + } else { + if ((set = setmode(mode)) == NULL) + errx(1, "invalid file mode: %s", mode); + oct = 0; + } + + for (exitval = 0; *argv != NULL; ++argv) { + if (pflag && build(*argv)) { + exitval = 1; + continue; + } + if (mkdir(*argv, oct ? + omode : getmode(set, S_IRWXU | S_IRWXG | S_IRWXO)) < 0) { + warn("%s", *argv); + exitval = 1; + } + } + exit(exitval); +} + +int +build(path) + char *path; +{ + struct stat sb; + mode_t numask, oumask; + int first; + char *p; + + p = path; + if (p[0] == '/') /* Skip leading '/'. */ + ++p; + for (first = 1;; ++p) { + if (p[0] == '\0' || p[0] == '/' && p[1] == '\0') + break; + if (p[0] != '/') + continue; + *p = '\0'; + if (first) { + /* + * POSIX 1003.2: + * For each dir operand that does not name an existing + * directory, effects equivalent to those cased by the + * following command shall occcur: + * + * mkdir -p -m $(umask -S),u+wx $(dirname dir) && + * mkdir [-m mode] dir + * + * We change the user's umask and then restore it, + * instead of doing chmod's. + */ + oumask = umask(0); + numask = oumask & ~(S_IWUSR | S_IXUSR); + (void)umask(numask); + first = 0; + } + if (stat(path, &sb)) { + if (errno != ENOENT || + mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO) < 0) { + warn("%s", path); + return (1); + } + } + *p = '/'; + } + if (!first) + (void)umask(oumask); + return (0); +} + +void +usage() +{ + (void)fprintf(stderr, "usage: mkdir [-p] [-m mode] directory ...\n"); + exit (1); +} diff --git a/bin/mv/Makefile b/bin/mv/Makefile new file mode 100644 index 0000000..98563dd --- /dev/null +++ b/bin/mv/Makefile @@ -0,0 +1,8 @@ +# @(#)Makefile 8.2 (Berkeley) 4/2/94 + +PROG= mv + +DPADD= ${LIBUTIL} +LDADD= -lutil + +.include <bsd.prog.mk> diff --git a/bin/mv/mv.1 b/bin/mv/mv.1 new file mode 100644 index 0000000..48ab6a5 --- /dev/null +++ b/bin/mv/mv.1 @@ -0,0 +1,129 @@ +.\" 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. +.\" +.\" @(#)mv.1 8.1 (Berkeley) 5/31/93 +.\" +.Dd May 31, 1993 +.Dt MV 1 +.Os +.Sh NAME +.Nm mv +.Nd move files +.Sh SYNOPSIS +.Nm mv +.Op Fl f | Fl i +.Ar source target +.Nm mv +.Op Fl f | Fl i +.Ar source ... source directory +.Sh DESCRIPTION +.Pp +In its first form, the +.Nm mv +utility renames the file named by the +.Ar source +operand to the destination path named by the +.Ar target +operand. +This form is assumed when the last operand does not name an already +existing directory. +.Pp +In its second form, +.Nm mv +moves each file named by a +.Ar source +operand to a destination file in the existing directory named by the +.Ar directory +operand. +The destination path for each operand is the pathname produced by the +concatenation of the last operand, a slash, and the final pathname +component of the named file. +.Pp +The following options are available: +.Bl -tag -width flag +.It Fl f +Do not prompt for confirmation before overwriting the destination +path. +(The +.Fl i +option is ignored if the +.Fl f +option is specified.) +.It Fl i +Causes +.Nm mv +to write a prompt to standard error before moving a file that would +overwrite an existing file. +If the response from the standard input begins with the character ``y'', +the move is attempted. +.El +.Pp +It is an error for either the +.Ar source +operand or the destination path to specify a directory unless both do. +.Pp +If the destination path does not have a mode which permits writing, +.Nm mv +prompts the user for confirmation as specified for the +.Fl i +option. +.Pp +As the +.Xr rename 2 +call does not work across file systems, +.Nm mv +uses +.Xr cp 1 +and +.Xr rm 1 +to accomplish the move. +The effect is equivalent to: +.Bd -literal -offset indent +rm -f destination_path && \e +\tcp -pr source_file destination && \e +\trm -rf source_file +.Ed +.Pp +The +.Nm mv +utility exits 0 on success, and >0 if an error occurs. +.Sh SEE ALSO +.Xr cp 1 , +.Xr symlink 7 +.Sh STANDARDS +The +.Nm mv +utility is expected to be +.St -p1003.2 +compatible. diff --git a/bin/mv/mv.c b/bin/mv/mv.c new file mode 100644 index 0000000..01fbc90 --- /dev/null +++ b/bin/mv/mv.c @@ -0,0 +1,300 @@ +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ken Smith of The State University of New York at Buffalo. + * + * 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[] = "@(#)mv.c 8.2 (Berkeley) 4/2/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/wait.h> +#include <sys/stat.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "pathnames.h" + +int fflg, iflg; + +int copy __P((char *, char *)); +int do_move __P((char *, char *)); +int fastcopy __P((char *, char *, struct stat *)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + register int baselen, len, rval; + register char *p, *endp; + struct stat sb; + int ch; + char path[MAXPATHLEN + 1]; + + while ((ch = getopt(argc, argv, "-if")) != EOF) + switch (ch) { + case 'i': + iflg = 1; + break; + case 'f': + fflg = 1; + break; + case '-': /* Undocumented; for compatibility. */ + goto endarg; + case '?': + default: + usage(); + } +endarg: argc -= optind; + argv += optind; + + if (argc < 2) + usage(); + + /* + * If the stat on the target fails or the target isn't a directory, + * try the move. More than 2 arguments is an error in this case. + */ + if (stat(argv[argc - 1], &sb) || !S_ISDIR(sb.st_mode)) { + if (argc > 2) + usage(); + exit(do_move(argv[0], argv[1])); + } + + /* It's a directory, move each file into it. */ + (void)strcpy(path, argv[argc - 1]); + baselen = strlen(path); + endp = &path[baselen]; + *endp++ = '/'; + ++baselen; + for (rval = 0; --argc; ++argv) { + if ((p = strrchr(*argv, '/')) == NULL) + p = *argv; + else + ++p; + if ((baselen + (len = strlen(p))) >= MAXPATHLEN) { + warnx("%s: destination pathname too long", *argv); + rval = 1; + } else { + memmove(endp, p, len + 1); + if (do_move(*argv, path)) + rval = 1; + } + } + exit(rval); +} + +int +do_move(from, to) + char *from, *to; +{ + struct stat sb; + int ask, ch; + char modep[15]; + + /* + * Check access. If interactive and file exists, ask user if it + * should be replaced. Otherwise if file exists but isn't writable + * make sure the user wants to clobber it. + */ + if (!fflg && !access(to, F_OK)) { + ask = 0; + if (iflg) { + (void)fprintf(stderr, "overwrite %s? ", to); + ask = 1; + } else if (access(to, W_OK) && !stat(to, &sb)) { + strmode(sb.st_mode, modep); + (void)fprintf(stderr, "override %s%s%s/%s for %s? ", + modep + 1, modep[9] == ' ' ? "" : " ", + user_from_uid(sb.st_uid, 0), + group_from_gid(sb.st_gid, 0), to); + ask = 1; + } + if (ask) { + if ((ch = getchar()) != EOF && ch != '\n') + while (getchar() != '\n'); + if (ch != 'y') + return (0); + } + } + if (!rename(from, to)) + return (0); + + if (errno != EXDEV) { + warn("rename %s to %s", from, to); + return (1); + } + + /* + * If rename fails because we're trying to cross devices, and + * it's a regular file, do the copy internally; otherwise, use + * cp and rm. + */ + if (stat(from, &sb)) { + warn("%s", from); + return (1); + } + return (S_ISREG(sb.st_mode) ? + fastcopy(from, to, &sb) : copy(from, to)); +} + +int +fastcopy(from, to, sbp) + char *from, *to; + struct stat *sbp; +{ + struct timeval tval[2]; + static u_int blen; + static char *bp; + register int nread, from_fd, to_fd; + + if ((from_fd = open(from, O_RDONLY, 0)) < 0) { + warn("%s", from); + return (1); + } + if ((to_fd = + open(to, O_CREAT | O_TRUNC | O_WRONLY, sbp->st_mode)) < 0) { + warn("%s", to); + (void)close(from_fd); + return (1); + } + if (!blen && !(bp = malloc(blen = sbp->st_blksize))) { + warn(NULL); + return (1); + } + while ((nread = read(from_fd, bp, blen)) > 0) + if (write(to_fd, bp, nread) != nread) { + warn("%s", to); + goto err; + } + if (nread < 0) { + warn("%s", from); +err: if (unlink(to)) + warn("%s: remove", to); + (void)close(from_fd); + (void)close(to_fd); + return (1); + } + (void)close(from_fd); + + if (fchown(to_fd, sbp->st_uid, sbp->st_gid)) + warn("%s: set owner/group", to); + if (fchmod(to_fd, sbp->st_mode)) + warn("%s: set mode", to); + + tval[0].tv_sec = sbp->st_atime; + tval[1].tv_sec = sbp->st_mtime; + tval[0].tv_usec = tval[1].tv_usec = 0; + if (utimes(to, tval)) + warn("%s: set times", to); + + if (close(to_fd)) { + warn("%s", to); + return (1); + } + + if (unlink(from)) { + warn("%s: remove", from); + return (1); + } + return (0); +} + +int +copy(from, to) + char *from, *to; +{ + int pid, status; + + if ((pid = vfork()) == 0) { + execl(_PATH_CP, "mv", "-PRp", from, to, NULL); + warn("%s", _PATH_CP); + _exit(1); + } + if (waitpid(pid, &status, 0) == -1) { + warn("%s: waitpid", _PATH_CP); + return (1); + } + if (!WIFEXITED(status)) { + warn("%s: did not terminate normally", _PATH_CP); + return (1); + } + if (WEXITSTATUS(status)) { + warn("%s: terminated with %d (non-zero) status", + _PATH_CP, WEXITSTATUS(status)); + return (1); + } + if (!(pid = vfork())) { + execl(_PATH_RM, "mv", "-rf", from, NULL); + warn("%s", _PATH_RM); + _exit(1); + } + if (waitpid(pid, &status, 0) == -1) { + warn("%s: waitpid", _PATH_RM); + return (1); + } + if (!WIFEXITED(status)) { + warn("%s: did not terminate normally", _PATH_RM); + return (1); + } + if (WEXITSTATUS(status)) { + warn("%s: terminated with %d (non-zero) status", + _PATH_RM, WEXITSTATUS(status)); + return (1); + } + return (0); +} + +void +usage() +{ + (void)fprintf(stderr, +"usage: mv [-if] src target;\n or: mv [-if] src1 ... srcN directory\n"); + exit(1); +} diff --git a/bin/mv/pathnames.h b/bin/mv/pathnames.h new file mode 100644 index 0000000..23fae70 --- /dev/null +++ b/bin/mv/pathnames.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 1989, 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. + * + * @(#)pathnames.h 8.1 (Berkeley) 5/31/93 + */ + +#define _PATH_RM "/bin/rm" +#define _PATH_CP "/bin/cp" diff --git a/bin/pax/Makefile b/bin/pax/Makefile new file mode 100644 index 0000000..578f65a --- /dev/null +++ b/bin/pax/Makefile @@ -0,0 +1,32 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 + +# To install on versions prior to BSD 4.4 the following may have to be +# defined with CFLAGS += +# +# -DNET2_STAT Use NET2 or older stat structure. The version of the +# stat structure is easily determined by looking at the +# basic type of an off_t (often defined in the file: +# /usr/include/sys/types.h). If off_t is a long (and is +# NOT A quad) then you must define NET2_STAT. +# This define is important, as if you do have a quad_t +# off_t and define NET2_STAT, pax will compile but will +# NOT RUN PROPERLY. +# +# -DNET2_FTS Use the older NET2 fts. To identify the version, +# examine the file: /usr/include/fts.h. If FTS_COMFOLLOW +# is not defined then you must define NET2_FTS. +# Pax may not compile if this not (un)defined properly. +# +# -DNET2_REGEX Use the older regexp.h not regex.h. The regex version +# is determined by looking at the value returned by +# regexec() (man 3 regexec). If regexec return a 1 for +# success (and NOT a 0 for success) you have the older +# regex routines and must define NET2_REGEX. +# Pax may not compile if this not (un)defined properly. + +PROG= pax +SRCS= ar_io.c ar_subs.c buf_subs.c cache.c cpio.c file_subs.c ftree.c\ + gen_subs.c options.c pat_rep.c pax.c sel_subs.c tables.c tar.c\ + tty_subs.c + +.include <bsd.prog.mk> diff --git a/bin/pax/ar_io.c b/bin/pax/ar_io.c new file mode 100644 index 0000000..07eb77f --- /dev/null +++ b/bin/pax/ar_io.c @@ -0,0 +1,1288 @@ +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * 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[] = "@(#)ar_io.c 8.2 (Berkeley) 4/18/94"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/mtio.h> +#include <sys/param.h> +#include <signal.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include "pax.h" +#include "extern.h" + +/* + * Routines which deal directly with the archive I/O device/file. + */ + +#define DMOD 0666 /* default mode of created archives */ +#define EXT_MODE O_RDONLY /* open mode for list/extract */ +#define AR_MODE (O_WRONLY | O_CREAT | O_TRUNC) /* mode for archive */ +#define APP_MODE O_RDWR /* mode for append */ +#define STDO "<STDOUT>" /* psuedo name for stdout */ +#define STDN "<STDIN>" /* psuedo name for stdin */ +static int arfd = -1; /* archive file descriptor */ +static int artyp = ISREG; /* archive type: file/FIFO/tape */ +static int arvol = 1; /* archive volume number */ +static int lstrval = -1; /* return value from last i/o */ +static int io_ok; /* i/o worked on volume after resync */ +static int did_io; /* did i/o ever occur on volume? */ +static int done; /* set via tty termination */ +static struct stat arsb; /* stat of archive device at open */ +static int invld_rec; /* tape has out of spec record size */ +static int wr_trail = 1; /* trailer was rewritten in append */ +static int can_unlnk = 0; /* do we unlink null archives? */ +char *arcname; /* printable name of archive */ + +static int get_phys __P((void)); +extern sigset_t s_mask; + +/* + * ar_open() + * Opens the next archive volume. Determines the type of the device and + * sets up block sizes as required by the archive device and the format. + * Note: we may be called with name == NULL on the first open only. + * Return: + * -1 on failure, 0 otherwise + */ + +#if __STDC__ +int +ar_open(char *name) +#else +int +ar_open(name) + char *name; +#endif +{ + struct mtget mb; + + if (arfd != -1) + (void)close(arfd); + arfd = -1; + can_unlnk = did_io = io_ok = invld_rec = 0; + artyp = ISREG; + flcnt = 0; + + /* + * open based on overall operation mode + */ + switch (act) { + case LIST: + case EXTRACT: + if (name == NULL) { + arfd = STDIN_FILENO; + arcname = STDN; + } else if ((arfd = open(name, EXT_MODE, DMOD)) < 0) + syswarn(0, errno, "Failed open to read on %s", name); + break; + case ARCHIVE: + if (name == NULL) { + arfd = STDOUT_FILENO; + arcname = STDO; + } else if ((arfd = open(name, AR_MODE, DMOD)) < 0) + syswarn(0, errno, "Failed open to write on %s", name); + else + can_unlnk = 1; + break; + case APPND: + if (name == NULL) { + arfd = STDOUT_FILENO; + arcname = STDO; + } else if ((arfd = open(name, APP_MODE, DMOD)) < 0) + syswarn(0, errno, "Failed open to read/write on %s", + name); + break; + case COPY: + /* + * arfd not used in COPY mode + */ + arcname = "<NONE>"; + lstrval = 1; + return(0); + } + if (arfd < 0) + return(-1); + + /* + * set up is based on device type + */ + if (fstat(arfd, &arsb) < 0) { + syswarn(0, errno, "Failed stat on %s", arcname); + (void)close(arfd); + arfd = -1; + can_unlnk = 0; + return(-1); + } + if (S_ISDIR(arsb.st_mode)) { + warn(0, "Cannot write an archive on top of a directory %s", + arcname); + (void)close(arfd); + arfd = -1; + can_unlnk = 0; + return(-1); + } + + if (S_ISCHR(arsb.st_mode)) + artyp = ioctl(arfd, MTIOCGET, &mb) ? ISCHR : ISTAPE; + else if (S_ISBLK(arsb.st_mode)) + artyp = ISBLK; + else if ((lseek(arfd, (off_t)0L, SEEK_CUR) == -1) && (errno == ESPIPE)) + artyp = ISPIPE; + else + artyp = ISREG; + + /* + * make sure we beyond any doubt that we only can unlink regular files + * we created + */ + if (artyp != ISREG) + can_unlnk = 0; + /* + * if we are writing, we are done + */ + if (act == ARCHIVE) { + blksz = rdblksz = wrblksz; + lstrval = 1; + return(0); + } + + /* + * set default blksz on read. APPNDs writes rdblksz on the last volume + * On all new archive volumes, we shift to wrblksz (if the user + * specified one, otherwize we will continue to use rdblksz). We + * must to set blocksize based on what kind of device the archive is + * stored. + */ + switch(artyp) { + case ISTAPE: + /* + * Tape drives come in at least two flavors. Those that support + * variable sized records and those that have fixed sized + * records. They must be treated differently. For tape drives + * that support variable sized records, we must make large + * reads to make sure we get the entire record, otherwise we + * will just get the first part of the record (up to size we + * asked). Tapes with fixed sized records may or may not return + * multiple records in a single read. We really do not care + * what the physical record size is UNLESS we are going to + * append. (We will need the physical block size to rewrite + * the trailer). Only when we are appending do we go to the + * effort to figure out the true PHYSICAL record size. + */ + blksz = rdblksz = MAXBLK; + break; + case ISPIPE: + case ISBLK: + case ISCHR: + /* + * Blocksize is not a major issue with these devices (but must + * be kept a multiple of 512). If the user specified a write + * block size, we use that to read. Under append, we must + * always keep blksz == rdblksz. Otherwise we go ahead and use + * the device optimal blocksize as (and if) returned by stat + * and if it is within pax specs. + */ + if ((act == APPND) && wrblksz) { + blksz = rdblksz = wrblksz; + break; + } + + if ((arsb.st_blksize > 0) && (arsb.st_blksize < MAXBLK) && + ((arsb.st_blksize % BLKMULT) == 0)) + rdblksz = arsb.st_blksize; + else + rdblksz = DEVBLK; + /* + * For performance go for large reads when we can without harm + */ + if ((act == APPND) || (artyp == ISCHR)) + blksz = rdblksz; + else + blksz = MAXBLK; + break; + case ISREG: + /* + * if the user specified wrblksz works, use it. Under appends + * we must always keep blksz == rdblksz + */ + if ((act == APPND) && wrblksz && ((arsb.st_size%wrblksz)==0)){ + blksz = rdblksz = wrblksz; + break; + } + /* + * See if we can find the blocking factor from the file size + */ + for (rdblksz = MAXBLK; rdblksz > 0; rdblksz -= BLKMULT) + if ((arsb.st_size % rdblksz) == 0) + break; + /* + * When we cannont find a match, we may have a flawed archive. + */ + if (rdblksz <= 0) + rdblksz = FILEBLK; + /* + * for performance go for large reads when we can + */ + if (act == APPND) + blksz = rdblksz; + else + blksz = MAXBLK; + break; + default: + /* + * should never happen, worse case, slow... + */ + blksz = rdblksz = BLKMULT; + break; + } + lstrval = 1; + return(0); +} + +/* + * ar_close() + * closes archive device, increments volume number, and prints i/o summary + */ +#if __STDC__ +void +ar_close(void) +#else +void +ar_close() +#endif +{ + FILE *outf; + + if (arfd < 0) { + did_io = io_ok = flcnt = 0; + return; + } + + if (act == LIST) + outf = stdout; + else + outf = stderr; + + /* + * Close archive file. This may take a LONG while on tapes (we may be + * forced to wait for the rewind to complete) so tell the user what is + * going on (this avoids the user hitting control-c thinking pax is + * broken). + */ + if (vflag && (artyp == ISTAPE)) { + if (vfpart) + (void)putc('\n', outf); + (void)fprintf(outf, + "%s: Waiting for tape drive close to complete...", + argv0); + (void)fflush(outf); + } + + /* + * if nothing was written to the archive (and we created it), we remove + * it + */ + if (can_unlnk && (fstat(arfd, &arsb) == 0) && (S_ISREG(arsb.st_mode)) && + (arsb.st_size == 0)) { + (void)unlink(arcname); + can_unlnk = 0; + } + + (void)close(arfd); + + if (vflag && (artyp == ISTAPE)) { + (void)fputs("done.\n", outf); + vfpart = 0; + (void)fflush(outf); + } + arfd = -1; + + if (!io_ok && !did_io) { + flcnt = 0; + return; + } + did_io = io_ok = 0; + + /* + * The volume number is only increased when the last device has data + * and we have already determined the archive format. + */ + if (frmt != NULL) + ++arvol; + + if (!vflag) { + flcnt = 0; + return; + } + + /* + * Print out a summary of I/O for this archive volume. + */ + if (vfpart) { + (void)putc('\n', outf); + vfpart = 0; + } + + /* + * If we have not determined the format yet, we just say how many bytes + * we have skipped over looking for a header to id. there is no way we + * could have written anything yet. + */ + if (frmt == NULL) { +# ifdef NET2_STAT + (void)fprintf(outf, "%s: unknown format, %lu bytes skipped.\n", +# else + (void)fprintf(outf, "%s: unknown format, %qu bytes skipped.\n", +# endif + argv0, rdcnt); + (void)fflush(outf); + flcnt = 0; + return; + } + + (void)fprintf(outf, +# ifdef NET2_STAT + "%s: %s vol %d, %lu files, %lu bytes read, %lu bytes written.\n", +# else + "%s: %s vol %d, %lu files, %qu bytes read, %qu bytes written.\n", +# endif + argv0, frmt->name, arvol-1, flcnt, rdcnt, wrcnt); + (void)fflush(outf); + flcnt = 0; +} + +/* + * ar_drain() + * drain any archive format independent padding from an archive read + * from a socket or a pipe. This is to prevent the process on the + * other side of the pipe from getting a SIGPIPE (pax will stop + * reading an archive once a format dependent trailer is detected). + */ +#if __STDC__ +void +ar_drain(void) +#else +void +ar_drain() +#endif +{ + register int res; + char drbuf[MAXBLK]; + + /* + * we only drain from a pipe/socket. Other devices can be closed + * without reading up to end of file. We sure hope that pipe is closed + * on the other side so we will get an EOF. + */ + if ((artyp != ISPIPE) || (lstrval <= 0)) + return; + + /* + * keep reading until pipe is drained + */ + while ((res = read(arfd, drbuf, sizeof(drbuf))) > 0) + ; + lstrval = res; +} + +/* + * ar_set_wr() + * Set up device right before switching from read to write in an append. + * device dependent code (if required) to do this should be added here. + * For all archive devices we are already positioned at the place we want + * to start writing when this routine is called. + * Return: + * 0 if all ready to write, -1 otherwise + */ + +#if __STDC__ +int +ar_set_wr(void) +#else +int +ar_set_wr() +#endif +{ + off_t cpos; + + /* + * we must make sure the trailer is rewritten on append, ar_next() + * will stop us if the archive containing the trailer was not written + */ + wr_trail = 0; + + /* + * Add any device dependent code as required here + */ + if (artyp != ISREG) + return(0); + /* + * Ok we have an archive in a regular file. If we were rewriting a + * file, we must get rid of all the stuff after the current offset + * (it was not written by pax). + */ + if (((cpos = lseek(arfd, (off_t)0L, SEEK_CUR)) < 0) || + (ftruncate(arfd, cpos) < 0)) { + syswarn(1, errno, "Unable to truncate archive file"); + return(-1); + } + return(0); +} + +/* + * ar_app_ok() + * check if the last volume in the archive allows appends. We cannot check + * this until we are ready to write since there is no spec that says all + * volumes in a single archive have to be of the same type... + * Return: + * 0 if we can append, -1 otherwise. + */ + +#if __STDC__ +int +ar_app_ok(void) +#else +int +ar_app_ok() +#endif +{ + if (artyp == ISPIPE) { + warn(1, "Cannot append to an archive obtained from a pipe."); + return(-1); + } + + if (!invld_rec) + return(0); + warn(1,"Cannot append, device record size %d does not support %s spec", + rdblksz, argv0); + return(-1); +} + +/* + * ar_read() + * read up to a specified number of bytes from the archive into the + * supplied buffer. When dealing with tapes we may not always be able to + * read what we want. + * Return: + * Number of bytes in buffer. 0 for end of file, -1 for a read error. + */ + +#if __STDC__ +int +ar_read(register char *buf, register int cnt) +#else +int +ar_read(buf, cnt) + register char *buf; + register int cnt; +#endif +{ + register int res = 0; + + /* + * if last i/o was in error, no more reads until reset or new volume + */ + if (lstrval <= 0) + return(lstrval); + + /* + * how we read must be based on device type + */ + switch (artyp) { + case ISTAPE: + if ((res = read(arfd, buf, cnt)) > 0) { + /* + * CAUTION: tape systems may not always return the same + * sized records so we leave blksz == MAXBLK. The + * physical record size that a tape drive supports is + * very hard to determine in a uniform and portable + * manner. + */ + io_ok = 1; + if (res != rdblksz) { + /* + * Record size changed. If this is happens on + * any record after the first, we probably have + * a tape drive which has a fixed record size + * we are getting multiple records in a single + * read). Watch out for record blocking that + * violates pax spec (must be a multiple of + * BLKMULT). + */ + rdblksz = res; + if (rdblksz % BLKMULT) + invld_rec = 1; + } + return(res); + } + break; + case ISREG: + case ISBLK: + case ISCHR: + case ISPIPE: + default: + /* + * Files are so easy to deal with. These other things cannot + * be trusted at all. So when we are dealing with character + * devices and pipes we just take what they have ready for us + * and return. Trying to do anything else with them runs the + * risk of failure. + */ + if ((res = read(arfd, buf, cnt)) > 0) { + io_ok = 1; + return(res); + } + break; + } + + /* + * We are in trouble at this point, something is broken... + */ + lstrval = res; + if (res < 0) + syswarn(1, errno, "Failed read on archive volume %d", arvol); + else + warn(0, "End of archive volume %d reached", arvol); + return(res); +} + +/* + * ar_write() + * Write a specified number of bytes in supplied buffer to the archive + * device so it appears as a single "block". Deals with errors and tries + * to recover when faced with short writes. + * Return: + * Number of bytes written. 0 indicates end of volume reached and with no + * flaws (as best that can be detected). A -1 indicates an unrecoverable + * error in the archive occured. + */ + +#if __STDC__ +int +ar_write(register char *buf, register int bsz) +#else +int +ar_write(buf, bsz) + register char *buf; + register int bsz; +#endif +{ + register int res; + off_t cpos; + + /* + * do not allow pax to create a "bad" archive. Once a write fails on + * an archive volume prevent further writes to it. + */ + if (lstrval <= 0) + return(lstrval); + + if ((res = write(arfd, buf, bsz)) == bsz) { + wr_trail = 1; + io_ok = 1; + return(bsz); + } + /* + * write broke, see what we can do with it. We try to send any partial + * writes that may violate pax spec to the next archive volume. + */ + if (res < 0) + lstrval = res; + else + lstrval = 0; + + switch (artyp) { + case ISREG: + if ((res > 0) && (res % BLKMULT)) { + /* + * try to fix up partial writes which are not BLKMULT + * in size by forcing the runt record to next archive + * volume + */ + if ((cpos = lseek(arfd, (off_t)0L, SEEK_CUR)) < 0) + break; + cpos -= (off_t)res; + if (ftruncate(arfd, cpos) < 0) + break; + res = lstrval = 0; + break; + } + if (res >= 0) + break; + /* + * if file is out of space, handle it like a return of 0 + */ + if ((errno == ENOSPC) || (errno == EFBIG) || (errno == EDQUOT)) + res = lstrval = 0; + break; + case ISTAPE: + case ISCHR: + case ISBLK: + if (res >= 0) + break; + if (errno == EACCES) { + warn(0, "Write failed, archive is write protected."); + res = lstrval = 0; + return(0); + } + /* + * see if we reached the end of media, if so force a change to + * the next volume + */ + if ((errno == ENOSPC) || (errno == EIO) || (errno == ENXIO)) + res = lstrval = 0; + break; + case ISPIPE: + default: + /* + * we cannot fix errors to these devices + */ + break; + } + + /* + * Better tell the user the bad news... + * if this is a block aligned archive format, we may have a bad archive + * if the format wants the header to start at a BLKMULT boundry. While + * we can deal with the mis-aligned data, it violates spec and other + * archive readers will likely fail. if the format is not block + * aligned, the user may be lucky (and the archive is ok). + */ + if (res >= 0) { + if (res > 0) + wr_trail = 1; + io_ok = 1; + } + + /* + * If we were trying to rewrite the trailer and it didn't work, we + * must quit right away. + */ + if (!wr_trail && (res <= 0)) { + warn(1,"Unable to append, trailer re-write failed. Quitting."); + return(res); + } + + if (res == 0) + warn(0, "End of archive volume %d reached", arvol); + else if (res < 0) + syswarn(1, errno, "Failed write to archive volume: %d", arvol); + else if (!frmt->blkalgn || ((res % frmt->blkalgn) == 0)) + warn(0,"WARNING: partial archive write. Archive MAY BE FLAWED"); + else + warn(1,"WARNING: partial archive write. Archive IS FLAWED"); + return(res); +} + +/* + * ar_rdsync() + * Try to move past a bad spot on a flawed archive as needed to continue + * I/O. Clears error flags to allow I/O to continue. + * Return: + * 0 when ok to try i/o again, -1 otherwise. + */ + +#if __STDC__ +int +ar_rdsync(void) +#else +int +ar_rdsync() +#endif +{ + long fsbz; + off_t cpos; + off_t mpos; + struct mtop mb; + + /* + * Fail resync attempts at user request (done) or this is going to be + * an update/append to a existing archive. if last i/o hit media end, + * we need to go to the next volume not try a resync + */ + if ((done > 0) || (lstrval == 0)) + return(-1); + + if ((act == APPND) || (act == ARCHIVE)) { + warn(1, "Cannot allow updates to an archive with flaws."); + return(-1); + } + if (io_ok) + did_io = 1; + + switch(artyp) { + case ISTAPE: + /* + * if the last i/o was a successful data transfer, we assume + * the fault is just a bad record on the tape that we are now + * past. If we did not get any data since the last resync try + * to move the tape foward one PHYSICAL record past any + * damaged tape section. Some tape drives are stubborn and need + * to be pushed. + */ + if (io_ok) { + io_ok = 0; + lstrval = 1; + break; + } + mb.mt_op = MTFSR; + mb.mt_count = 1; + if (ioctl(arfd, MTIOCTOP, &mb) < 0) + break; + lstrval = 1; + break; + case ISREG: + case ISCHR: + case ISBLK: + /* + * try to step over the bad part of the device. + */ + io_ok = 0; + if (((fsbz = arsb.st_blksize) <= 0) || (artyp != ISREG)) + fsbz = BLKMULT; + if ((cpos = lseek(arfd, (off_t)0L, SEEK_CUR)) < 0) + break; + mpos = fsbz - (cpos % (off_t)fsbz); + if (lseek(arfd, mpos, SEEK_CUR) < 0) + break; + lstrval = 1; + break; + case ISPIPE: + default: + /* + * cannot recover on these archive device types + */ + io_ok = 0; + break; + } + if (lstrval <= 0) { + warn(1, "Unable to recover from an archive read failure."); + return(-1); + } + warn(0, "Attempting to recover from an archive read failure."); + return(0); +} + +/* + * ar_fow() + * Move the I/O position within the archive foward the specified number of + * bytes as supported by the device. If we cannot move the requested + * number of bytes, return the actual number of bytes moved in skipped. + * Return: + * 0 if moved the requested distance, -1 on complete failure, 1 on + * partial move (the amount moved is in skipped) + */ + +#if __STDC__ +int +ar_fow(off_t sksz, off_t *skipped) +#else +int +ar_fow(sksz, skipped) + off_t sksz; + off_t *skipped; +#endif +{ + off_t cpos; + off_t mpos; + + *skipped = 0; + if (sksz <= 0) + return(0); + + /* + * we cannot move foward at EOF or error + */ + if (lstrval <= 0) + return(lstrval); + + /* + * Safer to read forward on devices where it is hard to find the end of + * the media without reading to it. With tapes we cannot be sure of the + * number of physical blocks to skip (we do not know physical block + * size at this point), so we must only read foward on tapes! + */ + if (artyp != ISREG) + return(0); + + /* + * figure out where we are in the archive + */ + if ((cpos = lseek(arfd, (off_t)0L, SEEK_CUR)) >= 0) { + /* + * we can be asked to move farther than there are bytes in this + * volume, if so, just go to file end and let normal buf_fill() + * deal with the end of file (it will go to next volume by + * itself) + */ + if ((mpos = cpos + sksz) > arsb.st_size) { + *skipped = arsb.st_size - cpos; + mpos = arsb.st_size; + } else + *skipped = sksz; + if (lseek(arfd, mpos, SEEK_SET) >= 0) + return(0); + } + syswarn(1, errno, "Foward positioning operation on archive failed"); + lstrval = -1; + return(-1); +} + +/* + * ar_rev() + * move the i/o position within the archive backwards the specified byte + * count as supported by the device. With tapes drives we RESET rdblksz to + * the PHYSICAL blocksize. + * NOTE: We should only be called to move backwards so we can rewrite the + * last records (the trailer) of an archive (APPEND). + * Return: + * 0 if moved the requested distance, -1 on complete failure + */ + +#if __STDC__ +int +ar_rev(off_t sksz) +#else +int +ar_rev(sksz) + off_t sksz; +#endif +{ + off_t cpos; + struct mtop mb; + register int phyblk; + + /* + * make sure we do not have try to reverse on a flawed archive + */ + if (lstrval < 0) + return(lstrval); + + switch(artyp) { + case ISPIPE: + if (sksz <= 0) + break; + /* + * cannot go backwards on these critters + */ + warn(1, "Reverse positioning on pipes is not supported."); + lstrval = -1; + return(-1); + case ISREG: + case ISBLK: + case ISCHR: + default: + if (sksz <= 0) + break; + + /* + * For things other than files, backwards movement has a very + * high probability of failure as we really do not know the + * true attributes of the device we are talking to (the device + * may not even have the ability to lseek() in any direction). + * First we figure out where we are in the archive. + */ + if ((cpos = lseek(arfd, (off_t)0L, SEEK_CUR)) < 0) { + syswarn(1, errno, + "Unable to obtain current archive byte offset"); + lstrval = -1; + return(-1); + } + + /* + * we may try to go backwards past the start when the archive + * is only a single record. If this hapens and we are on a + * multi volume archive, we need to go to the end of the + * previous volume and continue our movement backwards from + * there. + */ + if ((cpos -= sksz) < (off_t)0L) { + if (arvol > 1) { + /* + * this should never happen + */ + warn(1,"Reverse position on previous volume."); + lstrval = -1; + return(-1); + } + cpos = (off_t)0L; + } + if (lseek(arfd, cpos, SEEK_SET) < 0) { + syswarn(1, errno, "Unable to seek archive backwards"); + lstrval = -1; + return(-1); + } + break; + case ISTAPE: + /* + * Calculate and move the proper number of PHYSICAL tape + * blocks. If the sksz is not an even multiple of the physical + * tape size, we cannot do the move (this should never happen). + * (We also cannot handler trailers spread over two vols). + * get_phys() also makes sure we are in front of the filemark. + */ + if ((phyblk = get_phys()) <= 0) { + lstrval = -1; + return(-1); + } + + /* + * make sure future tape reads only go by physical tape block + * size (set rdblksz to the real size). + */ + rdblksz = phyblk; + + /* + * if no movement is required, just return (we must be after + * get_phys() so the physical blocksize is properly set) + */ + if (sksz <= 0) + break; + + /* + * ok we have to move. Make sure the tape drive can do it. + */ + if (sksz % phyblk) { + warn(1, + "Tape drive unable to backspace requested amount"); + lstrval = -1; + return(-1); + } + + /* + * move backwards the requested number of bytes + */ + mb.mt_op = MTBSR; + mb.mt_count = sksz/phyblk; + if (ioctl(arfd, MTIOCTOP, &mb) < 0) { + syswarn(1,errno, "Unable to backspace tape %d blocks.", + mb.mt_count); + lstrval = -1; + return(-1); + } + break; + } + lstrval = 1; + return(0); +} + +/* + * get_phys() + * Determine the physical block size on a tape drive. We need the physical + * block size so we know how many bytes we skip over when we move with + * mtio commands. We also make sure we are BEFORE THE TAPE FILEMARK when + * return. + * This is one really SLOW routine... + * Return: + * physical block size if ok (ok > 0), -1 otherwise + */ + +#if __STDC__ +static int +get_phys(void) +#else +static int +get_phys() +#endif +{ + register int padsz = 0; + register int res; + register int phyblk; + struct mtop mb; + char scbuf[MAXBLK]; + + /* + * move to the file mark, and then back up one record and read it. + * this should tell us the physical record size the tape is using. + */ + if (lstrval == 1) { + /* + * we know we are at file mark when we get back a 0 from + * read() + */ + while ((res = read(arfd, scbuf, sizeof(scbuf))) > 0) + padsz += res; + if (res < 0) { + syswarn(1, errno, "Unable to locate tape filemark."); + return(-1); + } + } + + /* + * move backwards over the file mark so we are at the end of the + * last record. + */ + mb.mt_op = MTBSF; + mb.mt_count = 1; + if (ioctl(arfd, MTIOCTOP, &mb) < 0) { + syswarn(1, errno, "Unable to backspace over tape filemark."); + return(-1); + } + + /* + * move backwards so we are in front of the last record and read it to + * get physical tape blocksize. + */ + mb.mt_op = MTBSR; + mb.mt_count = 1; + if (ioctl(arfd, MTIOCTOP, &mb) < 0) { + syswarn(1, errno, "Unable to backspace over last tape block."); + return(-1); + } + if ((phyblk = read(arfd, scbuf, sizeof(scbuf))) <= 0) { + syswarn(1, errno, "Cannot determine archive tape blocksize."); + return(-1); + } + + /* + * read foward to the file mark, then back up in front of the filemark + * (this is a bit paranoid, but should be safe to do). + */ + while ((res = read(arfd, scbuf, sizeof(scbuf))) > 0) + ; + if (res < 0) { + syswarn(1, errno, "Unable to locate tape filemark."); + return(-1); + } + mb.mt_op = MTBSF; + mb.mt_count = 1; + if (ioctl(arfd, MTIOCTOP, &mb) < 0) { + syswarn(1, errno, "Unable to backspace over tape filemark."); + return(-1); + } + + /* + * set lstrval so we know that the filemark has not been seen + */ + lstrval = 1; + + /* + * return if there was no padding + */ + if (padsz == 0) + return(phyblk); + + /* + * make sure we can move backwards over the padding. (this should + * never fail). + */ + if (padsz % phyblk) { + warn(1, "Tape drive unable to backspace requested amount"); + return(-1); + } + + /* + * move backwards over the padding so the head is where it was when + * we were first called (if required). + */ + mb.mt_op = MTBSR; + mb.mt_count = padsz/phyblk; + if (ioctl(arfd, MTIOCTOP, &mb) < 0) { + syswarn(1,errno,"Unable to backspace tape over %d pad blocks", + mb.mt_count); + return(-1); + } + return(phyblk); +} + +/* + * ar_next() + * prompts the user for the next volume in this archive. For some devices + * we may allow the media to be changed. Otherwise a new archive is + * prompted for. By pax spec, if there is no controlling tty or an eof is + * read on tty input, we must quit pax. + * Return: + * 0 when ready to continue, -1 when all done + */ + +#if __STDC__ +int +ar_next(void) +#else +int +ar_next() +#endif +{ + char buf[PAXPATHLEN+2]; + static int freeit = 0; + sigset_t o_mask; + + /* + * WE MUST CLOSE THE DEVICE. A lot of devices must see last close, (so + * things like writing EOF etc will be done) (Watch out ar_close() can + * also be called via a signal handler, so we must prevent a race. + */ + if (sigprocmask(SIG_BLOCK, &s_mask, &o_mask) < 0) + syswarn(0, errno, "Unable to set signal mask"); + ar_close(); + if (sigprocmask(SIG_SETMASK, &o_mask, (sigset_t *)NULL) < 0) + syswarn(0, errno, "Unable to restore signal mask"); + + if (done || !wr_trail) + return(-1); + + tty_prnt("\nATTENTION! %s archive volume change required.\n", argv0); + + /* + * if i/o is on stdin or stdout, we cannot reopen it (we do not know + * the name), the user will be forced to type it in. + */ + if (strcmp(arcname, STDO) && strcmp(arcname, STDN) && (artyp != ISREG) + && (artyp != ISPIPE)) { + if (artyp == ISTAPE) { + tty_prnt("%s ready for archive tape volume: %d\n", + arcname, arvol); + tty_prnt("Load the NEXT TAPE on the tape drive"); + } else { + tty_prnt("%s ready for archive volume: %d\n", + arcname, arvol); + tty_prnt("Load the NEXT STORAGE MEDIA (if required)"); + } + + if ((act == ARCHIVE) || (act == APPND)) + tty_prnt(" and make sure it is WRITE ENABLED.\n"); + else + tty_prnt("\n"); + + for(;;) { + tty_prnt("Type \"y\" to continue, \".\" to quit %s,", + argv0); + tty_prnt(" or \"s\" to switch to new device.\nIf you"); + tty_prnt(" cannot change storage media, type \"s\"\n"); + tty_prnt("Is the device ready and online? > "); + + if ((tty_read(buf,sizeof(buf))<0) || !strcmp(buf,".")){ + done = 1; + lstrval = -1; + tty_prnt("Quitting %s!\n", argv0); + vfpart = 0; + return(-1); + } + + if ((buf[0] == '\0') || (buf[1] != '\0')) { + tty_prnt("%s unknown command, try again\n",buf); + continue; + } + + switch (buf[0]) { + case 'y': + case 'Y': + /* + * we are to continue with the same device + */ + if (ar_open(arcname) >= 0) + return(0); + tty_prnt("Cannot re-open %s, try again\n", + arcname); + continue; + case 's': + case 'S': + /* + * user wants to open a different device + */ + tty_prnt("Switching to a different archive\n"); + break; + default: + tty_prnt("%s unknown command, try again\n",buf); + continue; + } + break; + } + } else + tty_prnt("Ready for archive volume: %d\n", arvol); + + /* + * have to go to a different archive + */ + for (;;) { + tty_prnt("Input archive name or \".\" to quit %s.\n", argv0); + tty_prnt("Archive name > "); + + if ((tty_read(buf, sizeof(buf)) < 0) || !strcmp(buf, ".")) { + done = 1; + lstrval = -1; + tty_prnt("Quitting %s!\n", argv0); + vfpart = 0; + return(-1); + } + if (buf[0] == '\0') { + tty_prnt("Empty file name, try again\n"); + continue; + } + if (!strcmp(buf, "..")) { + tty_prnt("Illegal file name: .. try again\n"); + continue; + } + if (strlen(buf) > PAXPATHLEN) { + tty_prnt("File name too long, try again\n"); + continue; + } + + /* + * try to open new archive + */ + if (ar_open(buf) >= 0) { + if (freeit) { + (void)free(arcname); + freeit = 0; + } + if ((arcname = strdup(buf)) == NULL) { + done = 1; + lstrval = -1; + warn(0, "Cannot save archive name."); + return(-1); + } + freeit = 1; + break; + } + tty_prnt("Cannot open %s, try again\n", buf); + continue; + } + return(0); +} diff --git a/bin/pax/ar_subs.c b/bin/pax/ar_subs.c new file mode 100644 index 0000000..5d6d3c6 --- /dev/null +++ b/bin/pax/ar_subs.c @@ -0,0 +1,1238 @@ +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * 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[] = "@(#)ar_subs.c 8.2 (Berkeley) 4/18/94"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <signal.h> +#include <string.h> +#include <stdio.h> +#include <ctype.h> +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include "pax.h" +#include "extern.h" + +static void wr_archive __P((register ARCHD *, int is_app)); +static int get_arc __P((void)); +static int next_head __P((register ARCHD *)); +extern sigset_t s_mask; + +/* + * Routines which control the overall operation modes of pax as specified by + * the user: list, append, read ... + */ + +static char hdbuf[BLKMULT]; /* space for archive header on read */ +u_long flcnt; /* number of files processed */ + +/* + * list() + * list the contents of an archive which match user supplied pattern(s) + * (no pattern matches all). + */ + +#if __STDC__ +void +list(void) +#else +void +list() +#endif +{ + register ARCHD *arcn; + register int res; + ARCHD archd; + time_t now; + + arcn = &archd; + /* + * figure out archive type; pass any format specific options to the + * archive option processing routine; call the format init routine. We + * also save current time for ls_list() so we do not make a system + * call for each file we need to print. If verbose (vflag) start up + * the name and group caches. + */ + if ((get_arc() < 0) || ((*frmt->options)() < 0) || + ((*frmt->st_rd)() < 0)) + return; + + if (vflag && ((uidtb_start() < 0) || (gidtb_start() < 0))) + return; + + now = time((time_t *)NULL); + + /* + * step through the archive until the format says it is done + */ + while (next_head(arcn) == 0) { + /* + * check for pattern, and user specified options match. + * When all patterns are matched we are done. + */ + if ((res = pat_match(arcn)) < 0) + break; + + if ((res == 0) && (sel_chk(arcn) == 0)) { + /* + * pattern resulted in a selected file + */ + if (pat_sel(arcn) < 0) + break; + + /* + * modify the name as requested by the user if name + * survives modification, do a listing of the file + */ + if ((res = mod_name(arcn)) < 0) + break; + if (res == 0) + ls_list(arcn, now); + } + + /* + * skip to next archive format header using values calculated + * by the format header read routine + */ + if (rd_skip(arcn->skip + arcn->pad) == 1) + break; + } + + /* + * all done, let format have a chance to cleanup, and make sure that + * the patterns supplied by the user were all matched + */ + (void)(*frmt->end_rd)(); + (void)sigprocmask(SIG_BLOCK, &s_mask, (sigset_t *)NULL); + ar_close(); + pat_chk(); +} + +/* + * extract() + * extract the member(s) of an archive as specified by user supplied + * pattern(s) (no patterns extracts all members) + */ + +#if __STDC__ +void +extract(void) +#else +void +extract() +#endif +{ + register ARCHD *arcn; + register int res; + off_t cnt; + ARCHD archd; + struct stat sb; + int fd; + + arcn = &archd; + /* + * figure out archive type; pass any format specific options to the + * archive option processing routine; call the format init routine; + * start up the directory modification time and access mode database + */ + if ((get_arc() < 0) || ((*frmt->options)() < 0) || + ((*frmt->st_rd)() < 0) || (dir_start() < 0)) + return; + + /* + * When we are doing interactive rename, we store the mapping of names + * so we can fix up hard links files later in the archive. + */ + if (iflag && (name_start() < 0)) + return; + + /* + * step through each entry on the archive until the format read routine + * says it is done + */ + while (next_head(arcn) == 0) { + + /* + * check for pattern, and user specified options match. When + * all the patterns are matched we are done + */ + if ((res = pat_match(arcn)) < 0) + break; + + if ((res > 0) || (sel_chk(arcn) != 0)) { + /* + * file is not selected. skip past any file data and + * padding and go back for the next archive member + */ + (void)rd_skip(arcn->skip + arcn->pad); + continue; + } + + /* + * with -u or -D only extract when the archive member is newer + * than the file with the same name in the file system (nos + * test of being the same type is required). + * NOTE: this test is done BEFORE name modifications as + * specified by pax. this operation can be confusing to the + * user who might expect the test to be done on an existing + * file AFTER the name mod. In honesty the pax spec is probably + * flawed in this respect. + */ + if ((uflag || Dflag) && ((lstat(arcn->name, &sb) == 0))) { + if (uflag && Dflag) { + if ((arcn->sb.st_mtime <= sb.st_mtime) && + (arcn->sb.st_ctime <= sb.st_ctime)) { + (void)rd_skip(arcn->skip + arcn->pad); + continue; + } + } else if (Dflag) { + if (arcn->sb.st_ctime <= sb.st_ctime) { + (void)rd_skip(arcn->skip + arcn->pad); + continue; + } + } else if (arcn->sb.st_mtime <= sb.st_mtime) { + (void)rd_skip(arcn->skip + arcn->pad); + continue; + } + } + + /* + * this archive member is now been selected. modify the name. + */ + if ((pat_sel(arcn) < 0) || ((res = mod_name(arcn)) < 0)) + break; + if (res > 0) { + /* + * a bad name mod, skip and purge name from link table + */ + purg_lnk(arcn); + (void)rd_skip(arcn->skip + arcn->pad); + continue; + } + + /* + * Non standard -Y and -Z flag. When the exisiting file is + * same age or newer skip + */ + if ((Yflag || Zflag) && ((lstat(arcn->name, &sb) == 0))) { + if (Yflag && Zflag) { + if ((arcn->sb.st_mtime <= sb.st_mtime) && + (arcn->sb.st_ctime <= sb.st_ctime)) { + (void)rd_skip(arcn->skip + arcn->pad); + continue; + } + } else if (Yflag) { + if (arcn->sb.st_ctime <= sb.st_ctime) { + (void)rd_skip(arcn->skip + arcn->pad); + continue; + } + } else if (arcn->sb.st_mtime <= sb.st_mtime) { + (void)rd_skip(arcn->skip + arcn->pad); + continue; + } + } + + if (vflag) { + (void)fputs(arcn->name, stderr); + vfpart = 1; + } + + /* + * all ok, extract this member based on type + */ + if ((arcn->type != PAX_REG) && (arcn->type != PAX_CTG)) { + /* + * process archive members that are not regular files. + * throw out padding and any data that might follow the + * header (as determined by the format). + */ + if ((arcn->type == PAX_HLK) || (arcn->type == PAX_HRG)) + res = lnk_creat(arcn); + else + res = node_creat(arcn); + + (void)rd_skip(arcn->skip + arcn->pad); + if (res < 0) + purg_lnk(arcn); + + if (vflag && vfpart) { + (void)putc('\n', stderr); + vfpart = 0; + } + continue; + } + /* + * we have a file with data here. If we can not create it, skip + * over the data and purge the name from hard link table + */ + if ((fd = file_creat(arcn)) < 0) { + (void)rd_skip(arcn->skip + arcn->pad); + purg_lnk(arcn); + continue; + } + /* + * extract the file from the archive and skip over padding and + * any unprocessed data + */ + res = (*frmt->rd_data)(arcn, fd, &cnt); + file_close(arcn, fd); + if (vflag && vfpart) { + (void)putc('\n', stderr); + vfpart = 0; + } + if (!res) + (void)rd_skip(cnt + arcn->pad); + } + + /* + * all done, restore directory modes and times as required; make sure + * all patterns supplied by the user were matched; block off signals + * to avoid chance for multiple entry into the cleanup code. + */ + (void)(*frmt->end_rd)(); + (void)sigprocmask(SIG_BLOCK, &s_mask, (sigset_t *)NULL); + ar_close(); + proc_dir(); + pat_chk(); +} + +/* + * wr_archive() + * Write an archive. used in both creating a new archive and appends on + * previously written archive. + */ + +#if __STDC__ +static void +wr_archive(register ARCHD *arcn, int is_app) +#else +static void +wr_archive(arcn, is_app) + register ARCHD *arcn; + int is_app; +#endif +{ + register int res; + register int hlk; + register int wr_one; + off_t cnt; + int (*wrf)(); + int fd = -1; + + /* + * if this format supports hard link storage, start up the database + * that detects them. + */ + if (((hlk = frmt->hlk) == 1) && (lnk_start() < 0)) + return; + + /* + * start up the file traversal code and format specific write + */ + if ((ftree_start() < 0) || ((*frmt->st_wr)() < 0)) + return; + wrf = frmt->wr; + + /* + * When we are doing interactive rename, we store the mapping of names + * so we can fix up hard links files later in the archive. + */ + if (iflag && (name_start() < 0)) + return; + + /* + * if this not append, and there are no files, we do no write a trailer + */ + wr_one = is_app; + + /* + * while there are files to archive, process them one at at time + */ + while (next_file(arcn) == 0) { + /* + * check if this file meets user specified options match. + */ + if (sel_chk(arcn) != 0) + continue; + fd = -1; + if (uflag) { + /* + * only archive if this file is newer than a file with + * the same name that is already stored on the archive + */ + if ((res = chk_ftime(arcn)) < 0) + break; + if (res > 0) + continue; + } + + /* + * this file is considered selected now. see if this is a hard + * link to a file already stored + */ + ftree_sel(arcn); + if (hlk && (chk_lnk(arcn) < 0)) + break; + + if ((arcn->type == PAX_REG) || (arcn->type == PAX_HRG) || + (arcn->type == PAX_CTG)) { + /* + * we will have to read this file. by opening it now we + * can avoid writing a header to the archive for a file + * we were later unable to read (we also purge it from + * the link table). + */ + if ((fd = open(arcn->org_name, O_RDONLY, 0)) < 0) { + syswarn(1,errno, "Unable to open %s to read", + arcn->org_name); + purg_lnk(arcn); + continue; + } + } + + /* + * Now modify the name as requested by the user + */ + if ((res = mod_name(arcn)) < 0) { + /* + * name modification says to skip this file, close the + * file and purge link table entry + */ + rdfile_close(arcn, &fd); + purg_lnk(arcn); + break; + } + + if ((res > 0) || (docrc && (set_crc(arcn, fd) < 0))) { + /* + * unable to obtain the crc we need, close the file, + * purge link table entry + */ + rdfile_close(arcn, &fd); + purg_lnk(arcn); + continue; + } + + if (vflag) { + (void)fputs(arcn->name, stderr); + vfpart = 1; + } + ++flcnt; + + /* + * looks safe to store the file, have the format specific + * routine write routine store the file header on the archive + */ + if ((res = (*wrf)(arcn)) < 0) { + rdfile_close(arcn, &fd); + break; + } + wr_one = 1; + if (res > 0) { + /* + * format write says no file data needs to be stored + * so we are done messing with this file + */ + if (vflag && vfpart) { + (void)putc('\n', stderr); + vfpart = 0; + } + rdfile_close(arcn, &fd); + continue; + } + + /* + * Add file data to the archive, quit on write error. if we + * cannot write the entire file contents to the archive we + * must pad the archive to replace the missing file data + * (otherwise during an extract the file header for the file + * which FOLLOWS this one will not be where we expect it to + * be). + */ + res = (*frmt->wr_data)(arcn, fd, &cnt); + rdfile_close(arcn, &fd); + if (vflag && vfpart) { + (void)putc('\n', stderr); + vfpart = 0; + } + if (res < 0) + break; + + /* + * pad as required, cnt is number of bytes not written + */ + if (((cnt > 0) && (wr_skip(cnt) < 0)) || + ((arcn->pad > 0) && (wr_skip(arcn->pad) < 0))) + break; + } + + /* + * tell format to write trailer; pad to block boundry; reset directory + * mode/access times, and check if all patterns supplied by the user + * were matched. block off signals to avoid chance for multiple entry + * into the cleanup code + */ + if (wr_one) { + (*frmt->end_wr)(); + wr_fin(); + } + (void)sigprocmask(SIG_BLOCK, &s_mask, (sigset_t *)NULL); + ar_close(); + if (tflag) + proc_dir(); + ftree_chk(); +} + +/* + * append() + * Add file to previously written archive. Archive format specified by the + * user must agree with archive. The archive is read first to collect + * modification times (if -u) and locate the archive trailer. The archive + * is positioned in front of the record with the trailer and wr_archive() + * is called to add the new members. + * PAX IMPLEMENTATION DETAIL NOTE: + * -u is implemented by adding the new members to the end of the archive. + * Care is taken so that these do not end up as links to the older + * version of the same file already stored in the archive. It is expected + * when extraction occurs these newer versions will over-write the older + * ones stored "earlier" in the archive (this may be a bad assumption as + * it depends on the implementation of the program doing the extraction). + * It is really difficult to splice in members without either re-writing + * the entire archive (from the point were the old version was), or having + * assistance of the format specification in terms of a special update + * header that invalidates a previous archive record. The posix spec left + * the method used to implement -u unspecified. This pax is able to + * over write existing files that it creates. + */ + +#if __STDC__ +void +append(void) +#else +void +append() +#endif +{ + register ARCHD *arcn; + register int res; + ARCHD archd; + FSUB *orgfrmt; + int udev; + off_t tlen; + + arcn = &archd; + orgfrmt = frmt; + + /* + * Do not allow an append operation if the actual archive is of a + * different format than the user specified foramt. + */ + if (get_arc() < 0) + return; + if ((orgfrmt != NULL) && (orgfrmt != frmt)) { + warn(1, "Cannot mix current archive format %s with %s", + frmt->name, orgfrmt->name); + return; + } + + /* + * pass the format any options and start up format + */ + if (((*frmt->options)() < 0) || ((*frmt->st_rd)() < 0)) + return; + + /* + * if we only are adding members that are newer, we need to save the + * mod times for all files we see. + */ + if (uflag && (ftime_start() < 0)) + return; + + /* + * some archive formats encode hard links by recording the device and + * file serial number (inode) but copy the file anyway (multiple times) + * to the archive. When we append, we run the risk that newly added + * files may have the same device and inode numbers as those recorded + * on the archive but during a previous run. If this happens, when the + * archive is extracted we get INCORRECT hard links. We avoid this by + * remapping the device numbers so that newly added files will never + * use the same device number as one found on the archive. remapping + * allows new members to safely have links among themselves. remapping + * also avoids problems with file inode (serial number) truncations + * when the inode number is larger than storage space in the archive + * header. See the remap routines for more details. + */ + if ((udev = frmt->udev) && (dev_start() < 0)) + return; + + /* + * reading the archive may take a long time. If verbose tell the user + */ + if (vflag) { + (void)fprintf(stderr, + "%s: Reading archive to position at the end...", argv0); + vfpart = 1; + } + + /* + * step through the archive until the format says it is done + */ + while (next_head(arcn) == 0) { + /* + * check if this file meets user specified options. + */ + if (sel_chk(arcn) != 0) { + if (rd_skip(arcn->skip + arcn->pad) == 1) + break; + continue; + } + + if (uflag) { + /* + * see if this is the newest version of this file has + * already been seen, if so skip. + */ + if ((res = chk_ftime(arcn)) < 0) + break; + if (res > 0) { + if (rd_skip(arcn->skip + arcn->pad) == 1) + break; + continue; + } + } + + /* + * Store this device number. Device numbers seen during the + * read phase of append will cause newly appended files with a + * device number seen in the old part of the archive to be + * remapped to an unused device number. + */ + if ((udev && (add_dev(arcn) < 0)) || + (rd_skip(arcn->skip + arcn->pad) == 1)) + break; + } + + /* + * done, finish up read and get the number of bytes to back up so we + * can add new members. The format might have used the hard link table, + * purge it. + */ + tlen = (*frmt->end_rd)(); + lnk_end(); + + /* + * try to postion for write, if this fails quit. if any error occurs, + * we will refuse to write + */ + if (appnd_start(tlen) < 0) + return; + + /* + * tell the user we are done reading. + */ + if (vflag && vfpart) { + (void)fputs("done.\n", stderr); + vfpart = 0; + } + + /* + * go to the writing phase to add the new members + */ + wr_archive(arcn, 1); +} + +/* + * archive() + * write a new archive + */ + +#if __STDC__ +void +archive(void) +#else +void +archive() +#endif +{ + ARCHD archd; + + /* + * if we only are adding members that are newer, we need to save the + * mod times for all files; set up for writing; pass the format any + * options write the archive + */ + if ((uflag && (ftime_start() < 0)) || (wr_start() < 0)) + return; + if ((*frmt->options)() < 0) + return; + + wr_archive(&archd, 0); +} + +/* + * copy() + * copy files from one part of the file system to another. this does not + * use any archive storage. The EFFECT OF THE COPY IS THE SAME as if an + * archive was written and then extracted in the destination directory + * (except the files are forced to be under the destination directory). + */ + +#if __STDC__ +void +copy(void) +#else +void +copy() +#endif +{ + register ARCHD *arcn; + register int res; + register int fddest; + register char *dest_pt; + register int dlen; + register int drem; + int fdsrc = -1; + struct stat sb; + ARCHD archd; + char dirbuf[PAXPATHLEN+1]; + + arcn = &archd; + /* + * set up the destination dir path and make sure it is a directory. We + * make sure we have a trailing / on the destination + */ + dlen = l_strncpy(dirbuf, dirptr, PAXPATHLEN); + dest_pt = dirbuf + dlen; + if (*(dest_pt-1) != '/') { + *dest_pt++ = '/'; + ++dlen; + } + *dest_pt = '\0'; + drem = PAXPATHLEN - dlen; + + if (stat(dirptr, &sb) < 0) { + syswarn(1, errno, "Cannot access destination directory %s", + dirptr); + return; + } + if (!S_ISDIR(sb.st_mode)) { + warn(1, "Destination is not a directory %s", dirptr); + return; + } + + /* + * start up the hard link table; file traversal routines and the + * modification time and access mode database + */ + if ((lnk_start() < 0) || (ftree_start() < 0) || (dir_start() < 0)) + return; + + /* + * When we are doing interactive rename, we store the mapping of names + * so we can fix up hard links files later in the archive. + */ + if (iflag && (name_start() < 0)) + return; + + /* + * set up to cp file trees + */ + cp_start(); + + /* + * while there are files to archive, process them + */ + while (next_file(arcn) == 0) { + fdsrc = -1; + + /* + * check if this file meets user specified options + */ + if (sel_chk(arcn) != 0) + continue; + + /* + * if there is already a file in the destination directory with + * the same name and it is newer, skip the one stored on the + * archive. + * NOTE: this test is done BEFORE name modifications as + * specified by pax. this can be confusing to the user who + * might expect the test to be done on an existing file AFTER + * the name mod. In honesty the pax spec is probably flawed in + * this respect + */ + if (uflag || Dflag) { + /* + * create the destination name + */ + if (*(arcn->name) == '/') + res = 1; + else + res = 0; + if ((arcn->nlen - res) > drem) { + warn(1, "Destination pathname too long %s", + arcn->name); + continue; + } + (void)strncpy(dest_pt, arcn->name + res, drem); + dirbuf[PAXPATHLEN] = '\0'; + + /* + * if existing file is same age or newer skip + */ + res = lstat(dirbuf, &sb); + *dest_pt = '\0'; + + if (res == 0) { + if (uflag && Dflag) { + if ((arcn->sb.st_mtime<=sb.st_mtime) && + (arcn->sb.st_ctime<=sb.st_ctime)) + continue; + } else if (Dflag) { + if (arcn->sb.st_ctime <= sb.st_ctime) + continue; + } else if (arcn->sb.st_mtime <= sb.st_mtime) + continue; + } + } + + /* + * this file is considered selected. See if this is a hard link + * to a previous file; modify the name as requested by the + * user; set the final destination. + */ + ftree_sel(arcn); + if ((chk_lnk(arcn) < 0) || ((res = mod_name(arcn)) < 0)) + break; + if ((res > 0) || (set_dest(arcn, dirbuf, dlen) < 0)) { + /* + * skip file, purge from link table + */ + purg_lnk(arcn); + continue; + } + + /* + * Non standard -Y and -Z flag. When the exisiting file is + * same age or newer skip + */ + if ((Yflag || Zflag) && ((lstat(arcn->name, &sb) == 0))) { + if (Yflag && Zflag) { + if ((arcn->sb.st_mtime <= sb.st_mtime) && + (arcn->sb.st_ctime <= sb.st_ctime)) + continue; + } else if (Yflag) { + if (arcn->sb.st_ctime <= sb.st_ctime) + continue; + } else if (arcn->sb.st_mtime <= sb.st_mtime) + continue; + } + + if (vflag) { + (void)fputs(arcn->name, stderr); + vfpart = 1; + } + ++flcnt; + + /* + * try to create a hard link to the src file if requested + * but make sure we are not trying to overwrite ourselves. + */ + if (lflag) + res = cross_lnk(arcn); + else + res = chk_same(arcn); + if (res <= 0) { + if (vflag && vfpart) { + (void)putc('\n', stderr); + vfpart = 0; + } + continue; + } + + /* + * have to create a new file + */ + if ((arcn->type != PAX_REG) && (arcn->type != PAX_CTG)) { + /* + * create a link or special file + */ + if ((arcn->type == PAX_HLK) || (arcn->type == PAX_HRG)) + res = lnk_creat(arcn); + else + res = node_creat(arcn); + if (res < 0) + purg_lnk(arcn); + if (vflag && vfpart) { + (void)putc('\n', stderr); + vfpart = 0; + } + continue; + } + + /* + * have to copy a regular file to the destination directory. + * first open source file and then create the destination file + */ + if ((fdsrc = open(arcn->org_name, O_RDONLY, 0)) < 0) { + syswarn(1, errno, "Unable to open %s to read", + arcn->org_name); + purg_lnk(arcn); + continue; + } + if ((fddest = file_creat(arcn)) < 0) { + rdfile_close(arcn, &fdsrc); + purg_lnk(arcn); + continue; + } + + /* + * copy source file data to the destination file + */ + cp_file(arcn, fdsrc, fddest); + file_close(arcn, fddest); + rdfile_close(arcn, &fdsrc); + + if (vflag && vfpart) { + (void)putc('\n', stderr); + vfpart = 0; + } + } + + /* + * restore directory modes and times as required; make sure all + * patterns were selected block off signals to avoid chance for + * multiple entry into the cleanup code. + */ + (void)sigprocmask(SIG_BLOCK, &s_mask, (sigset_t *)NULL); + ar_close(); + proc_dir(); + ftree_chk(); +} + +/* + * next_head() + * try to find a valid header in the archive. Uses format specific + * routines to extract the header and id the trailer. Trailers may be + * located within a valid header or in an invalid header (the location + * is format specific. The inhead field from the option table tells us + * where to look for the trailer). + * We keep reading (and resyncing) until we get enough contiguous data + * to check for a header. If we cannot find one, we shift by a byte + * add a new byte from the archive to the end of the buffer and try again. + * If we get a read error, we throw out what we have (as we must have + * contiguous data) and start over again. + * ASSUMED: headers fit within a BLKMULT header. + * Return: + * 0 if we got a header, -1 if we are unable to ever find another one + * (we reached the end of input, or we reached the limit on retries. see + * the specs for rd_wrbuf() for more details) + */ + +#if __STDC__ +static int +next_head(register ARCHD *arcn) +#else +static int +next_head(arcn) + register ARCHD *arcn; +#endif +{ + register int ret; + register char *hdend; + register int res; + register int shftsz; + register int hsz; + register int in_resync = 0; /* set when we are in resync mode */ + int cnt = 0; /* counter for trailer function */ + + /* + * set up initial conditions, we want a whole frmt->hsz block as we + * have no data yet. + */ + res = hsz = frmt->hsz; + hdend = hdbuf; + shftsz = hsz - 1; + for(;;) { + /* + * keep looping until we get a contiguous FULL buffer + * (frmt->hsz is the proper size) + */ + for (;;) { + if ((ret = rd_wrbuf(hdend, res)) == res) + break; + + /* + * some kind of archive read problem, try to resync the + * storage device, better give the user the bad news. + */ + if ((ret == 0) || (rd_sync() < 0)) { + warn(1,"Premature end of file on archive read"); + return(-1); + } + if (!in_resync) { + if (act == APPND) { + warn(1, + "Archive I/O error, cannot continue"); + return(-1); + } + warn(1,"Archive I/O error. Trying to recover."); + ++in_resync; + } + + /* + * oh well, throw it all out and start over + */ + res = hsz; + hdend = hdbuf; + } + + /* + * ok we have a contiguous buffer of the right size. Call the + * format read routine. If this was not a valid header and this + * format stores trailers outside of the header, call the + * format specific trailer routine to check for a trailer. We + * have to watch out that we do not mis-identify file data or + * block padding as a header or trailer. Format specific + * trailer functions must NOT check for the trailer while we + * are running in resync mode. Some trailer functions may tell + * us that this block cannot contain a valid header either, so + * we then throw out the entire block and start over. + */ + if ((*frmt->rd)(arcn, hdbuf) == 0) + break; + + if (!frmt->inhead) { + /* + * this format has trailers outside of valid headers + */ + if ((ret = (*frmt->trail)(hdbuf,in_resync,&cnt)) == 0){ + /* + * valid trailer found, drain input as required + */ + ar_drain(); + return(-1); + } + + if (ret == 1) { + /* + * we are in resync and we were told to throw + * the whole block out because none of the + * bytes in this block can be used to form a + * valid header + */ + res = hsz; + hdend = hdbuf; + continue; + } + } + + /* + * Brute force section. + * not a valid header. We may be able to find a header yet. So + * we shift over by one byte, and set up to read one byte at a + * time from the archive and place it at the end of the buffer. + * We will keep moving byte at a time until we find a header or + * get a read error and have to start over. + */ + if (!in_resync) { + if (act == APPND) { + warn(1,"Unable to append, archive header flaw"); + return(-1); + } + warn(1,"Invalid header, starting valid header search."); + ++in_resync; + } + bcopy(hdbuf+1, hdbuf, shftsz); + res = 1; + hdend = hdbuf + shftsz; + } + + /* + * ok got a valid header, check for trailer if format encodes it in the + * the header. NOTE: the parameters are different than trailer routines + * which encode trailers outside of the header! + */ + if (frmt->inhead && ((*frmt->trail)(arcn) == 0)) { + /* + * valid trailer found, drain input as required + */ + ar_drain(); + return(-1); + } + + ++flcnt; + return(0); +} + +/* + * get_arc() + * Figure out what format an archive is. Handles archive with flaws by + * brute force searches for a legal header in any supported format. The + * format id routines have to be careful to NOT mis-identify a format. + * ASSUMED: headers fit within a BLKMULT header. + * Return: + * 0 if archive found -1 otherwise + */ + +#if __STDC__ +static int +get_arc(void) +#else +static int +get_arc() +#endif +{ + register int i; + register int hdsz = 0; + register int res; + register int minhd = BLKMULT; + char *hdend; + int notice = 0; + + /* + * find the smallest header size in all archive formats and then set up + * to read the archive. + */ + for (i = 0; ford[i] >= 0; ++i) { + if (fsub[ford[i]].hsz < minhd) + minhd = fsub[ford[i]].hsz; + } + if (rd_start() < 0) + return(-1); + res = BLKMULT; + hdsz = 0; + hdend = hdbuf; + for(;;) { + for (;;) { + /* + * fill the buffer with at least the smallest header + */ + i = rd_wrbuf(hdend, res); + if (i > 0) + hdsz += i; + if (hdsz >= minhd) + break; + + /* + * if we cannot recover from a read error quit + */ + if ((i == 0) || (rd_sync() < 0)) + goto out; + + /* + * when we get an error none of the data we already + * have can be used to create a legal header (we just + * got an error in the middle), so we throw it all out + * and refill the buffer with fresh data. + */ + res = BLKMULT; + hdsz = 0; + hdend = hdbuf; + if (!notice) { + if (act == APPND) + return(-1); + warn(1,"Cannot identify format. Searching..."); + ++notice; + } + } + + /* + * we have at least the size of the smallest header in any + * archive format. Look to see if we have a match. The array + * ford[] is used to specify the header id order to reduce the + * chance of incorrectly id'ing a valid header (some formats + * may be subsets of each other and the order would then be + * important). + */ + for (i = 0; ford[i] >= 0; ++i) { + if ((*fsub[ford[i]].id)(hdbuf, hdsz) < 0) + continue; + frmt = &(fsub[ford[i]]); + /* + * yuck, to avoid slow special case code in the extract + * routines, just push this header back as if it was + * not seen. We have left extra space at start of the + * buffer for this purpose. This is a bit ugly, but + * adding all the special case code is far worse. + */ + pback(hdbuf, hdsz); + return(0); + } + + /* + * We have a flawed archive, no match. we start searching, but + * we never allow additions to flawed archives + */ + if (!notice) { + if (act == APPND) + return(-1); + warn(1, "Cannot identify format. Searching..."); + ++notice; + } + + /* + * brute force search for a header that we can id. + * we shift through byte at a time. this is slow, but we cannot + * determine the nature of the flaw in the archive in a + * portable manner + */ + if (--hdsz > 0) { + bcopy(hdbuf+1, hdbuf, hdsz); + res = BLKMULT - hdsz; + hdend = hdbuf + hdsz; + } else { + res = BLKMULT; + hdend = hdbuf; + hdsz = 0; + } + } + + out: + /* + * we cannot find a header, bow, apologize and quit + */ + warn(1, "Sorry, unable to determine archive format."); + return(-1); +} diff --git a/bin/pax/buf_subs.c b/bin/pax/buf_subs.c new file mode 100644 index 0000000..a4ec446 --- /dev/null +++ b/bin/pax/buf_subs.c @@ -0,0 +1,1083 @@ +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * 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[] = "@(#)buf_subs.c 8.2 (Berkeley) 4/18/94"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <stdio.h> +#include <ctype.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include "pax.h" +#include "extern.h" + +/* + * routines which implement archive and file buffering + */ + +#define MINFBSZ 512 /* default block size for hole detect */ +#define MAXFLT 10 /* default media read error limit */ + +/* + * Need to change bufmem to dynamic allocation when the upper + * limit on blocking size is removed (though that will violate pax spec) + * MAXBLK define and tests will also need to be updated. + */ +static char bufmem[MAXBLK+BLKMULT]; /* i/o buffer + pushback id space */ +static char *buf; /* normal start of i/o buffer */ +static char *bufend; /* end or last char in i/o buffer */ +static char *bufpt; /* read/write point in i/o buffer */ +int blksz = MAXBLK; /* block input/output size in bytes */ +int wrblksz; /* user spec output size in bytes */ +int maxflt = MAXFLT; /* MAX consecutive media errors */ +int rdblksz; /* first read blksize (tapes only) */ +off_t wrlimit; /* # of bytes written per archive vol */ +off_t wrcnt; /* # of bytes written on current vol */ +off_t rdcnt; /* # of bytes read on current vol */ + +/* + * wr_start() + * set up the buffering system to operate in a write mode + * Return: + * 0 if ok, -1 if the user specified write block size violates pax spec + */ + +#if __STDC__ +int +wr_start(void) +#else +int +wr_start() +#endif +{ + buf = &(bufmem[BLKMULT]); + /* + * Check to make sure the write block size meets pax specs. If the user + * does not specify a blocksize, we use the format default blocksize. + * We must be picky on writes, so we do not allow the user to create an + * archive that might be hard to read elsewhere. If all ok, we then + * open the first archive volume + */ + if (!wrblksz) + wrblksz = frmt->bsz; + if (wrblksz > MAXBLK) { + warn(1, "Write block size of %d too large, maximium is: %d", + wrblksz, MAXBLK); + return(-1); + } + if (wrblksz % BLKMULT) { + warn(1, "Write block size of %d is not a %d byte multiple", + wrblksz, BLKMULT); + return(-1); + } + + /* + * we only allow wrblksz to be used with all archive operations + */ + blksz = rdblksz = wrblksz; + if ((ar_open(arcname) < 0) && (ar_next() < 0)) + return(-1); + wrcnt = 0; + bufend = buf + wrblksz; + bufpt = buf; + return(0); +} + +/* + * rd_start() + * set up buffering system to read an archive + * Return: + * 0 if ok, -1 otherwise + */ + +#if __STDC__ +int +rd_start(void) +#else +int +rd_start() +#endif +{ + /* + * leave space for the header pushback (see get_arc()). If we are + * going to append and user specified a write block size, check it + * right away + */ + buf = &(bufmem[BLKMULT]); + if ((act == APPND) && wrblksz) { + if (wrblksz > MAXBLK) { + warn(1,"Write block size %d too large, maximium is: %d", + wrblksz, MAXBLK); + return(-1); + } + if (wrblksz % BLKMULT) { + warn(1, "Write block size %d is not a %d byte multiple", + wrblksz, BLKMULT); + return(-1); + } + } + + /* + * open the archive + */ + if ((ar_open(arcname) < 0) && (ar_next() < 0)) + return(-1); + bufend = buf + rdblksz; + bufpt = bufend; + rdcnt = 0; + return(0); +} + +/* + * cp_start() + * set up buffer system for copying within the file system + */ + +#if __STDC__ +void +cp_start(void) +#else +void +cp_start() +#endif +{ + buf = &(bufmem[BLKMULT]); + rdblksz = blksz = MAXBLK; +} + +/* + * appnd_start() + * Set up the buffering system to append new members to an archive that + * was just read. The last block(s) of an archive may contain a format + * specific trailer. To append a new member, this trailer has to be + * removed from the archive. The first byte of the trailer is replaced by + * the start of the header of the first file added to the archive. The + * format specific end read function tells us how many bytes to move + * backwards in the archive to be positioned BEFORE the trailer. Two + * different postions have to be adjusted, the O.S. file offset (e.g. the + * position of the tape head) and the write point within the data we have + * stored in the read (soon to become write) buffer. We may have to move + * back several records (the number depends on the size of the archive + * record and the size of the format trailer) to read up the record where + * the first byte of the trailer is recorded. Trailers may span (and + * overlap) record boundries. + * We first calculate which record has the first byte of the trailer. We + * move the OS file offset back to the start of this record and read it + * up. We set the buffer write pointer to be at this byte (the byte where + * the trailer starts). We then move the OS file pointer back to the + * start of this record so a flush of this buffer will replace the record + * in the archive. + * A major problem is rewriting this last record. For archives stored + * on disk files, this is trival. However, many devices are really picky + * about the conditions under which they will allow a write to occur. + * Often devices restrict the conditions where writes can be made writes, + * so it may not be feasable to append archives stored on all types of + * devices. + * Return: + * 0 for success, -1 for failure + */ + +#if __STDC__ +int +appnd_start(off_t skcnt) +#else +int +appnd_start(skcnt) + off_t skcnt; +#endif +{ + register int res; + off_t cnt; + + if (exit_val != 0) { + warn(0, "Cannot append to an archive that may have flaws."); + return(-1); + } + /* + * if the user did not specify a write blocksize, inherit the size used + * in the last archive volume read. (If a is set we still use rdblksz + * until next volume, cannot shift sizes within a single volume). + */ + if (!wrblksz) + wrblksz = blksz = rdblksz; + else + blksz = rdblksz; + + /* + * make sure that this volume allows appends + */ + if (ar_app_ok() < 0) + return(-1); + + /* + * Calculate bytes to move back and move in front of record where we + * need to start writing from. Remember we have to add in any padding + * that might be in the buffer after the trailer in the last block. We + * travel skcnt + padding ROUNDED UP to blksize. + */ + skcnt += bufend - bufpt; + if ((cnt = (skcnt/blksz) * blksz) < skcnt) + cnt += blksz; + if (ar_rev((off_t)cnt) < 0) + goto out; + + /* + * We may have gone too far if there is valid data in the block we are + * now in front of, read up the block and position the pointer after + * the valid data. + */ + if ((cnt -= skcnt) > 0) { + /* + * watch out for stupid tape drives. ar_rev() will set rdblksz + * to be real physical blocksize so we must loop until we get + * the old rdblksz (now in blksz). If ar_rev() fouls up the + * determination of the physical block size, we will fail. + */ + bufpt = buf; + bufend = buf + blksz; + while (bufpt < bufend) { + if ((res = ar_read(bufpt, rdblksz)) <= 0) + goto out; + bufpt += res; + } + if (ar_rev((off_t)(bufpt - buf)) < 0) + goto out; + bufpt = buf + cnt; + bufend = buf + blksz; + } else { + /* + * buffer is empty + */ + bufend = buf + blksz; + bufpt = buf; + } + rdblksz = blksz; + rdcnt -= skcnt; + wrcnt = 0; + + /* + * At this point we are ready to write. If the device requires special + * handling to write at a point were previously recorded data resides, + * that is handled in ar_set_wr(). From now on we operate under normal + * ARCHIVE mode (write) conditions + */ + if (ar_set_wr() < 0) + return(-1); + act = ARCHIVE; + return(0); + + out: + warn(1, "Unable to rewrite archive trailer, cannot append."); + return(-1); +} + +/* + * rd_sync() + * A read error occurred on this archive volume. Resync the buffer and + * try to reset the device (if possible) so we can continue to read. Keep + * trying to do this until we get a valid read, or we reach the limit on + * consecutive read faults (at which point we give up). The user can + * adjust the read error limit through a command line option. + * Returns: + * 0 on success, and -1 on failure + */ + +#if __STDC__ +int +rd_sync(void) +#else +int +rd_sync() +#endif +{ + register int errcnt = 0; + register int res; + + /* + * if the user says bail out on first fault, we are out of here... + */ + if (maxflt == 0) + return(-1); + if (act == APPND) { + warn(1, "Unable to append when there are archive read errors."); + return(-1); + } + + /* + * poke at device and try to get past media error + */ + if (ar_rdsync() < 0) { + if (ar_next() < 0) + return(-1); + else + rdcnt = 0; + } + + for (;;) { + if ((res = ar_read(buf, blksz)) > 0) { + /* + * All right! got some data, fill that buffer + */ + bufpt = buf; + bufend = buf + res; + rdcnt += res; + return(0); + } + + /* + * Oh well, yet another failed read... + * if error limit reached, ditch. o.w. poke device to move past + * bad media and try again. if media is badly damaged, we ask + * the poor (and upset user at this point) for the next archive + * volume. remember the goal on reads is to get the most we + * can extract out of the archive. + */ + if ((maxflt > 0) && (++errcnt > maxflt)) + warn(0,"Archive read error limit (%d) reached",maxflt); + else if (ar_rdsync() == 0) + continue; + if (ar_next() < 0) + break; + rdcnt = 0; + errcnt = 0; + } + return(-1); +} + +/* + * pback() + * push the data used during the archive id phase back into the I/O + * buffer. This is required as we cannot be sure that the header does NOT + * overlap a block boundry (as in the case we are trying to recover a + * flawed archived). This was not designed to be used for any other + * purpose. (What software engineering, HA!) + * WARNING: do not even THINK of pback greater than BLKMULT, unless the + * pback space is increased. + */ + +#if __STDC__ +void +pback(char *pt, int cnt) +#else +void +pback(pt, cnt) + char *pt; + int cnt; +#endif +{ + bufpt -= cnt; + bcopy(pt, bufpt, cnt); + return; +} + +/* + * rd_skip() + * skip foward in the archive during a archive read. Used to get quickly + * past file data and padding for files the user did NOT select. + * Return: + * 0 if ok, -1 failure, and 1 when EOF on the archive volume was detected. + */ + +#if __STDC__ +int +rd_skip(off_t skcnt) +#else +int +rd_skip(skcnt) + off_t skcnt; +#endif +{ + off_t res; + off_t cnt; + off_t skipped = 0; + + /* + * consume what data we have in the buffer. If we have to move foward + * whole records, we call the low level skip function to see if we can + * move within the archive without doing the expensive reads on data we + * do not want. + */ + if (skcnt == 0) + return(0); + res = MIN((bufend - bufpt), skcnt); + bufpt += res; + skcnt -= res; + + /* + * if skcnt is now 0, then no additional i/o is needed + */ + if (skcnt == 0) + return(0); + + /* + * We have to read more, calculate complete and partial record reads + * based on rdblksz. we skip over "cnt" complete records + */ + res = skcnt%rdblksz; + cnt = (skcnt/rdblksz) * rdblksz; + + /* + * if the skip fails, we will have to resync. ar_fow will tell us + * how much it can skip over. We will have to read the rest. + */ + if (ar_fow(cnt, &skipped) < 0) + return(-1); + res += cnt - skipped; + rdcnt += skipped; + + /* + * what is left we have to read (which may be the whole thing if + * ar_fow() told us the device can only read to skip records); + */ + while (res > 0L) { + cnt = bufend - bufpt; + /* + * if the read fails, we will have to resync + */ + if ((cnt <= 0) && ((cnt = buf_fill()) < 0)) + return(-1); + if (cnt == 0) + return(1); + cnt = MIN(cnt, res); + bufpt += cnt; + res -= cnt; + } + return(0); +} + +/* + * wr_fin() + * flush out any data (and pad if required) the last block. We always pad + * with zero (even though we do not have to). Padding with 0 makes it a + * lot easier to recover if the archive is damaged. zero paddding SHOULD + * BE a requirement.... + */ + +#if __STDC__ +void +wr_fin(void) +#else +void +wr_fin() +#endif +{ + if (bufpt > buf) { + bzero(bufpt, bufend - bufpt); + bufpt = bufend; + (void)buf_flush(blksz); + } +} + +/* + * wr_rdbuf() + * fill the write buffer from data passed to it in a buffer (usually used + * by format specific write routines to pass a file header). On failure we + * punt. We do not allow the user to continue to write flawed archives. + * We assume these headers are not very large (the memory copy we use is + * a bit expensive). + * Return: + * 0 if buffer was filled ok, -1 o.w. (buffer flush failure) + */ + +#if __STDC__ +int +wr_rdbuf(register char *out, register int outcnt) +#else +int +wr_rdbuf(out, outcnt) + register char *out; + register int outcnt; +#endif +{ + register int cnt; + + /* + * while there is data to copy copy into the write buffer. when the + * write buffer fills, flush it to the archive and continue + */ + while (outcnt > 0) { + cnt = bufend - bufpt; + if ((cnt <= 0) && ((cnt = buf_flush(blksz)) < 0)) + return(-1); + /* + * only move what we have space for + */ + cnt = MIN(cnt, outcnt); + bcopy(out, bufpt, cnt); + bufpt += cnt; + out += cnt; + outcnt -= cnt; + } + return(0); +} + +/* + * rd_wrbuf() + * copy from the read buffer into a supplied buffer a specified number of + * bytes. If the read buffer is empty fill it and continue to copy. + * usually used to obtain a file header for processing by a format + * specific read routine. + * Return + * number of bytes copied to the buffer, 0 indicates EOF on archive volume, + * -1 is a read error + */ + +#if __STDC__ +int +rd_wrbuf(register char *in, register int cpcnt) +#else +int +rd_wrbuf(in, cpcnt) + register char *in; + register int cpcnt; +#endif +{ + register int res; + register int cnt; + register int incnt = cpcnt; + + /* + * loop until we fill the buffer with the requested number of bytes + */ + while (incnt > 0) { + cnt = bufend - bufpt; + if ((cnt <= 0) && ((cnt = buf_fill()) <= 0)) { + /* + * read error, return what we got (or the error if + * no data was copied). The caller must know that an + * error occured and has the best knowledge what to + * do with it + */ + if ((res = cpcnt - incnt) > 0) + return(res); + return(cnt); + } + + /* + * calculate how much data to copy based on whats left and + * state of buffer + */ + cnt = MIN(cnt, incnt); + bcopy(bufpt, in, cnt); + bufpt += cnt; + incnt -= cnt; + in += cnt; + } + return(cpcnt); +} + +/* + * wr_skip() + * skip foward during a write. In other words add padding to the file. + * we add zero filled padding as it makes flawed archives much easier to + * recover from. the caller tells us how many bytes of padding to add + * This routine was not designed to add HUGE amount of padding, just small + * amounts (a few 512 byte blocks at most) + * Return: + * 0 if ok, -1 if there was a buf_flush failure + */ + +#if __STDC__ +int +wr_skip(off_t skcnt) +#else +int +wr_skip(skcnt) + off_t skcnt; +#endif +{ + register int cnt; + + /* + * loop while there is more padding to add + */ + while (skcnt > 0L) { + cnt = bufend - bufpt; + if ((cnt <= 0) && ((cnt = buf_flush(blksz)) < 0)) + return(-1); + cnt = MIN(cnt, skcnt); + bzero(bufpt, cnt); + bufpt += cnt; + skcnt -= cnt; + } + return(0); +} + +/* + * wr_rdfile() + * fill write buffer with the contents of a file. We are passed an open + * file descriptor to the file an the archive structure that describes the + * file we are storing. The variable "left" is modified to contain the + * number of bytes of the file we were NOT able to write to the archive. + * it is important that we always write EXACTLY the number of bytes that + * the format specific write routine told us to. The file can also get + * bigger, so reading to the end of file would create an improper archive, + * we just detect this case and warn the user. We never create a bad + * archive if we can avoid it. Of course trying to archive files that are + * active is asking for trouble. It we fail, we pass back how much we + * could NOT copy and let the caller deal with it. + * Return: + * 0 ok, -1 if archive write failure. a short read of the file returns a + * 0, but "left" is set to be greater than zero. + */ + +#if __STDC__ +int +wr_rdfile(ARCHD *arcn, int ifd, off_t *left) +#else +int +wr_rdfile(arcn, ifd, left) + ARCHD *arcn; + int ifd; + off_t *left; +#endif +{ + register int cnt; + register int res = 0; + register off_t size = arcn->sb.st_size; + struct stat sb; + + /* + * while there are more bytes to write + */ + while (size > 0L) { + cnt = bufend - bufpt; + if ((cnt <= 0) && ((cnt = buf_flush(blksz)) < 0)) { + *left = size; + return(-1); + } + cnt = MIN(cnt, size); + if ((res = read(ifd, bufpt, cnt)) <= 0) + break; + size -= res; + bufpt += res; + } + + /* + * better check the file did not change during this operation + * or the file read failed. + */ + if (res < 0) + syswarn(1, errno, "Read fault on %s", arcn->org_name); + else if (size != 0L) + warn(1, "File changed size during read %s", arcn->org_name); + else if (fstat(ifd, &sb) < 0) + syswarn(1, errno, "Failed stat on %s", arcn->org_name); + else if (arcn->sb.st_mtime != sb.st_mtime) + warn(1, "File %s was modified during copy to archive", + arcn->org_name); + *left = size; + return(0); +} + +/* + * rd_wrfile() + * extract the contents of a file from the archive. If we are unable to + * extract the entire file (due to failure to write the file) we return + * the numbers of bytes we did NOT process. This way the caller knows how + * many bytes to skip past to find the next archive header. If the failure + * was due to an archive read, we will catch that when we try to skip. If + * the format supplies a file data crc value, we calculate the actual crc + * so that it can be compared to the value stored in the header + * NOTE: + * We call a special function to write the file. This function attempts to + * restore file holes (blocks of zeros) into the file. When files are + * sparse this saves space, and is a LOT faster. For non sparse files + * the performance hit is small. As of this writing, no archive supports + * information on where the file holes are. + * Return: + * 0 ok, -1 if archive read failure. if we cannot write the entire file, + * we return a 0 but "left" is set to be the amount unwritten + */ + +#if __STDC__ +int +rd_wrfile(ARCHD *arcn, int ofd, off_t *left) +#else +int +rd_wrfile(arcn, ofd, left) + ARCHD *arcn; + int ofd; + off_t *left; +#endif +{ + register int cnt = 0; + register off_t size = arcn->sb.st_size; + register int res = 0; + register char *fnm = arcn->name; + int isem = 1; + int rem; + int sz = MINFBSZ; + struct stat sb; + u_long crc = 0L; + + /* + * pass the blocksize of the file being written to the write routine, + * if the size is zero, use the default MINFBSZ + */ + if (fstat(ofd, &sb) == 0) { + if (sb.st_blksize > 0) + sz = (int)sb.st_blksize; + } else + syswarn(0,errno,"Unable to obtain block size for file %s",fnm); + rem = sz; + *left = 0L; + + /* + * Copy the archive to the file the number of bytes specified. We have + * to assume that we want to recover file holes as none of the archive + * formats can record the location of file holes. + */ + while (size > 0L) { + cnt = bufend - bufpt; + /* + * if we get a read error, we do not want to skip, as we may + * miss a header, so we do not set left, but if we get a write + * error, we do want to skip over the unprocessed data. + */ + if ((cnt <= 0) && ((cnt = buf_fill()) <= 0)) + break; + cnt = MIN(cnt, size); + if ((res = file_write(ofd,bufpt,cnt,&rem,&isem,sz,fnm)) <= 0) { + *left = size; + break; + } + + if (docrc) { + /* + * update the actual crc value + */ + cnt = res; + while (--cnt >= 0) + crc += *bufpt++ & 0xff; + } else + bufpt += res; + size -= res; + } + + /* + * if the last block has a file hole (all zero), we must make sure this + * gets updated in the file. We force the last block of zeros to be + * written. just closing with the file offset moved foward may not put + * a hole at the end of the file. + */ + if (isem && (arcn->sb.st_size > 0L)) + file_flush(ofd, fnm, isem); + + /* + * if we failed from archive read, we do not want to skip + */ + if ((size > 0L) && (*left == 0L)) + return(-1); + + /* + * some formats record a crc on file data. If so, then we compare the + * calculated crc to the crc stored in the archive + */ + if (docrc && (size == 0L) && (arcn->crc != crc)) + warn(1,"Actual crc does not match expected crc %s",arcn->name); + return(0); +} + +/* + * cp_file() + * copy the contents of one file to another. used during -rw phase of pax + * just as in rd_wrfile() we use a special write function to write the + * destination file so we can properly copy files with holes. + */ + +#if __STDC__ +void +cp_file(ARCHD *arcn, int fd1, int fd2) +#else +void +cp_file(arcn, fd1, fd2) + ARCHD *arcn; + int fd1; + int fd2; +#endif +{ + register int cnt; + register off_t cpcnt = 0L; + register int res = 0; + register char *fnm = arcn->name; + register int no_hole = 0; + int isem = 1; + int rem; + int sz = MINFBSZ; + struct stat sb; + + /* + * check for holes in the source file. If none, we will use regular + * write instead of file write. + */ + if (((off_t)(arcn->sb.st_blocks * BLKMULT)) >= arcn->sb.st_size) + ++no_hole; + + /* + * pass the blocksize of the file being written to the write routine, + * if the size is zero, use the default MINFBSZ + */ + if (fstat(fd2, &sb) == 0) { + if (sb.st_blksize > 0) + sz = sb.st_blksize; + } else + syswarn(0,errno,"Unable to obtain block size for file %s",fnm); + rem = sz; + + /* + * read the source file and copy to destination file until EOF + */ + for(;;) { + if ((cnt = read(fd1, buf, blksz)) <= 0) + break; + if (no_hole) + res = write(fd2, buf, cnt); + else + res = file_write(fd2, buf, cnt, &rem, &isem, sz, fnm); + if (res != cnt) + break; + cpcnt += cnt; + } + + /* + * check to make sure the copy is valid. + */ + if (res < 0) + syswarn(1, errno, "Failed write during copy of %s to %s", + arcn->org_name, arcn->name); + else if (cpcnt != arcn->sb.st_size) + warn(1, "File %s changed size during copy to %s", + arcn->org_name, arcn->name); + else if (fstat(fd1, &sb) < 0) + syswarn(1, errno, "Failed stat of %s", arcn->org_name); + else if (arcn->sb.st_mtime != sb.st_mtime) + warn(1, "File %s was modified during copy to %s", + arcn->org_name, arcn->name); + + /* + * if the last block has a file hole (all zero), we must make sure this + * gets updated in the file. We force the last block of zeros to be + * written. just closing with the file offset moved foward may not put + * a hole at the end of the file. + */ + if (!no_hole && isem && (arcn->sb.st_size > 0L)) + file_flush(fd2, fnm, isem); + return; +} + +/* + * buf_fill() + * fill the read buffer with the next record (or what we can get) from + * the archive volume. + * Return: + * Number of bytes of data in the read buffer, -1 for read error, and + * 0 when finished (user specified termination in ar_next()). + */ + +#if __STDC__ +int +buf_fill(void) +#else +int +buf_fill() +#endif +{ + register int cnt; + static int fini = 0; + + if (fini) + return(0); + + for(;;) { + /* + * try to fill the buffer. on error the next archive volume is + * opened and we try again. + */ + if ((cnt = ar_read(buf, blksz)) > 0) { + bufpt = buf; + bufend = buf + cnt; + rdcnt += cnt; + return(cnt); + } + + /* + * errors require resync, EOF goes to next archive + */ + if (cnt < 0) + break; + if (ar_next() < 0) { + fini = 1; + return(0); + } + rdcnt = 0; + } + exit_val = 1; + return(-1); +} + +/* + * buf_flush() + * force the write buffer to the archive. We are passed the number of + * bytes in the buffer at the point of the flush. When we change archives + * the record size might change. (either larger or smaller). + * Return: + * 0 if all is ok, -1 when a write error occurs. + */ + +#if __STDC__ +int +buf_flush(register int bufcnt) +#else +int +buf_flush(bufcnt) + register int bufcnt; +#endif +{ + register int cnt; + register int push = 0; + register int totcnt = 0; + + /* + * if we have reached the user specified byte count for each archive + * volume, prompt for the next volume. (The non-standrad -R flag). + * NOTE: If the wrlimit is smaller than wrcnt, we will always write + * at least one record. We always round limit UP to next blocksize. + */ + if ((wrlimit > 0) && (wrcnt > wrlimit)) { + warn(0, "User specified archive volume byte limit reached."); + if (ar_next() < 0) { + wrcnt = 0; + exit_val = 1; + return(-1); + } + wrcnt = 0; + + /* + * The new archive volume might have changed the size of the + * write blocksize. if so we figure out if we need to write + * (one or more times), or if there is now free space left in + * the buffer (it is no longer full). bufcnt has the number of + * bytes in the buffer, (the blocksize, at the point we were + * CALLED). Push has the amount of "extra" data in the buffer + * if the block size has shrunk from a volume change. + */ + bufend = buf + blksz; + if (blksz > bufcnt) + return(0); + if (blksz < bufcnt) + push = bufcnt - blksz; + } + + /* + * We have enough data to write at least one archive block + */ + for (;;) { + /* + * write a block and check if it all went out ok + */ + cnt = ar_write(buf, blksz); + if (cnt == blksz) { + /* + * the write went ok + */ + wrcnt += cnt; + totcnt += cnt; + if (push > 0) { + /* we have extra data to push to the front. + * check for more than 1 block of push, and if + * so we loop back to write again + */ + bcopy(bufend, buf, push); + bufpt = buf + push; + if (push >= blksz) { + push -= blksz; + continue; + } + } else + bufpt = buf; + return(totcnt); + } else if (cnt > 0) { + /* + * Oh drat we got a partial write! + * if format doesnt care about alignment let it go, + * we warned the user in ar_write().... but this means + * the last record on this volume violates pax spec.... + */ + totcnt += cnt; + wrcnt += cnt; + bufpt = buf + cnt; + cnt = bufcnt - cnt; + bcopy(bufpt, buf, cnt); + bufpt = buf + cnt; + if (!frmt->blkalgn || ((cnt % frmt->blkalgn) == 0)) + return(totcnt); + break; + } + + /* + * All done, go to next archive + */ + wrcnt = 0; + if (ar_next() < 0) + break; + + /* + * The new archive volume might also have changed the block + * size. if so, figure out if we have too much or too little + * data for using the new block size + */ + bufend = buf + blksz; + if (blksz > bufcnt) + return(0); + if (blksz < bufcnt) + push = bufcnt - blksz; + } + + /* + * write failed, stop pax. we must not create a bad archive! + */ + exit_val = 1; + return(-1); +} diff --git a/bin/pax/cache.c b/bin/pax/cache.c new file mode 100644 index 0000000..2224e4d --- /dev/null +++ b/bin/pax/cache.c @@ -0,0 +1,479 @@ +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * 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[] = "@(#)cache.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <string.h> +#include <stdio.h> +#include <ctype.h> +#include <pwd.h> +#include <grp.h> +#include <unistd.h> +#include <stdlib.h> +#include "pax.h" +#include "cache.h" +#include "extern.h" + +/* + * routines that control user, group, uid and gid caches (for the archive + * member print routine). + * IMPORTANT: + * these routines cache BOTH hits and misses, a major performance improvement + */ + +static int pwopn = 0; /* is password file open */ +static int gropn = 0; /* is group file open */ +static UIDC **uidtb = NULL; /* uid to name cache */ +static GIDC **gidtb = NULL; /* gid to name cache */ +static UIDC **usrtb = NULL; /* user name to uid cache */ +static GIDC **grptb = NULL; /* group name to gid cache */ + +/* + * uidtb_start + * creates an an empty uidtb + * Return: + * 0 if ok, -1 otherwise + */ + +#if __STDC__ +int +uidtb_start(void) +#else +int +uidtb_start() +#endif +{ + static int fail = 0; + + if (uidtb != NULL) + return(0); + if (fail) + return(-1); + if ((uidtb = (UIDC **)calloc(UID_SZ, sizeof(UIDC *))) == NULL) { + ++fail; + warn(1, "Unable to allocate memory for user id cache table"); + return(-1); + } + return(0); +} + +/* + * gidtb_start + * creates an an empty gidtb + * Return: + * 0 if ok, -1 otherwise + */ + +#if __STDC__ +int +gidtb_start(void) +#else +int +gidtb_start() +#endif +{ + static int fail = 0; + + if (gidtb != NULL) + return(0); + if (fail) + return(-1); + if ((gidtb = (GIDC **)calloc(GID_SZ, sizeof(GIDC *))) == NULL) { + ++fail; + warn(1, "Unable to allocate memory for group id cache table"); + return(-1); + } + return(0); +} + +/* + * usrtb_start + * creates an an empty usrtb + * Return: + * 0 if ok, -1 otherwise + */ + +#if __STDC__ +int +usrtb_start(void) +#else +int +usrtb_start() +#endif +{ + static int fail = 0; + + if (usrtb != NULL) + return(0); + if (fail) + return(-1); + if ((usrtb = (UIDC **)calloc(UNM_SZ, sizeof(UIDC *))) == NULL) { + ++fail; + warn(1, "Unable to allocate memory for user name cache table"); + return(-1); + } + return(0); +} + +/* + * grptb_start + * creates an an empty grptb + * Return: + * 0 if ok, -1 otherwise + */ + +#if __STDC__ +int +grptb_start(void) +#else +int +grptb_start() +#endif +{ + static int fail = 0; + + if (grptb != NULL) + return(0); + if (fail) + return(-1); + if ((grptb = (GIDC **)calloc(GNM_SZ, sizeof(GIDC *))) == NULL) { + ++fail; + warn(1,"Unable to allocate memory for group name cache table"); + return(-1); + } + return(0); +} + +/* + * name_uid() + * caches the name (if any) for the uid. If frc set, we always return the + * the stored name (if valid or invalid match). We use a simple hash table. + * Return + * Pointer to stored name (or a empty string) + */ + +#if __STDC__ +char * +name_uid(uid_t uid, int frc) +#else +char * +name_uid(uid, frc) + uid_t uid; + int frc; +#endif +{ + register struct passwd *pw; + register UIDC *ptr; + + if ((uidtb == NULL) && (uidtb_start() < 0)) + return(""); + + /* + * see if we have this uid cached + */ + ptr = uidtb[uid % UID_SZ]; + if ((ptr != NULL) && (ptr->valid > 0) && (ptr->uid == uid)) { + /* + * have an entry for this uid + */ + if (frc || (ptr->valid == VALID)) + return(ptr->name); + return(""); + } + + /* + * No entry for this uid, we will add it + */ + if (!pwopn) { + setpassent(1); + ++pwopn; + } + if (ptr == NULL) + ptr = (UIDC *)malloc(sizeof(UIDC)); + + if ((pw = getpwuid(uid)) == NULL) { + /* + * no match for this uid in the local password file + * a string that is the uid in numberic format + */ + if (ptr == NULL) + return(""); + ptr->uid = uid; + ptr->valid = INVALID; +# ifdef NET2_STAT + (void)sprintf(ptr->name, "%u", uid); +# else + (void)sprintf(ptr->name, "%lu", uid); +# endif + if (frc == 0) + return(""); + } else { + /* + * there is an entry for this uid in the password file + */ + if (ptr == NULL) + return(pw->pw_name); + ptr->uid = uid; + (void)strncpy(ptr->name, pw->pw_name, UNMLEN); + ptr->name[UNMLEN-1] = '\0'; + ptr->valid = VALID; + } + return(ptr->name); +} + +/* + * name_gid() + * caches the name (if any) for the gid. If frc set, we always return the + * the stored name (if valid or invalid match). We use a simple hash table. + * Return + * Pointer to stored name (or a empty string) + */ + +#if __STDC__ +char * +name_gid(gid_t gid, int frc) +#else +char * +name_gid(gid, frc) + gid_t gid; + int frc; +#endif +{ + register struct group *gr; + register GIDC *ptr; + + if ((gidtb == NULL) && (gidtb_start() < 0)) + return(""); + + /* + * see if we have this gid cached + */ + ptr = gidtb[gid % GID_SZ]; + if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) { + /* + * have an entry for this gid + */ + if (frc || (ptr->valid == VALID)) + return(ptr->name); + return(""); + } + + /* + * No entry for this gid, we will add it + */ + if (!gropn) { + setgroupent(1); + ++gropn; + } + if (ptr == NULL) + ptr = (GIDC *)malloc(sizeof(GIDC)); + + if ((gr = getgrgid(gid)) == NULL) { + /* + * no match for this gid in the local group file, put in + * a string that is the gid in numberic format + */ + if (ptr == NULL) + return(""); + ptr->gid = gid; + ptr->valid = INVALID; +# ifdef NET2_STAT + (void)sprintf(ptr->name, "%u", gid); +# else + (void)sprintf(ptr->name, "%lu", gid); +# endif + if (frc == 0) + return(""); + } else { + /* + * there is an entry for this group in the group file + */ + if (ptr == NULL) + return(gr->gr_name); + ptr->gid = gid; + (void)strncpy(ptr->name, gr->gr_name, GNMLEN); + ptr->name[GNMLEN-1] = '\0'; + ptr->valid = VALID; + } + return(ptr->name); +} + +/* + * uid_name() + * caches the uid for a given user name. We use a simple hash table. + * Return + * the uid (if any) for a user name, or a -1 if no match can be found + */ + +#if __STDC__ +int +uid_name(char *name, uid_t *uid) +#else +int +uid_name(name, uid) + char *name; + uid_t *uid; +#endif +{ + register struct passwd *pw; + register UIDC *ptr; + register int namelen; + + /* + * return -1 for mangled names + */ + if (((namelen = strlen(name)) == 0) || (name[0] == '\0')) + return(-1); + if ((usrtb == NULL) && (usrtb_start() < 0)) + return(-1); + + /* + * look up in hash table, if found and valid return the uid, + * if found and invalid, return a -1 + */ + ptr = usrtb[st_hash(name, namelen, UNM_SZ)]; + if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) { + if (ptr->valid == INVALID) + return(-1); + *uid = ptr->uid; + return(0); + } + + if (!pwopn) { + setpassent(1); + ++pwopn; + } + + if (ptr == NULL) + ptr = (UIDC *)malloc(sizeof(UIDC)); + + /* + * no match, look it up, if no match store it as an invalid entry, + * or store the matching uid + */ + if (ptr == NULL) { + if ((pw = getpwnam(name)) == NULL) + return(-1); + *uid = pw->pw_uid; + return(0); + } + (void)strncpy(ptr->name, name, UNMLEN); + ptr->name[UNMLEN-1] = '\0'; + if ((pw = getpwnam(name)) == NULL) { + ptr->valid = INVALID; + return(-1); + } + ptr->valid = VALID; + *uid = ptr->uid = pw->pw_uid; + return(0); +} + +/* + * gid_name() + * caches the gid for a given group name. We use a simple hash table. + * Return + * the gid (if any) for a group name, or a -1 if no match can be found + */ + +#if __STDC__ +int +gid_name(char *name, gid_t *gid) +#else +int +gid_name(name, gid) + char *name; + gid_t *gid; +#endif +{ + register struct group *gr; + register GIDC *ptr; + register int namelen; + + /* + * return -1 for mangled names + */ + if (((namelen = strlen(name)) == 0) || (name[0] == '\0')) + return(-1); + if ((grptb == NULL) && (grptb_start() < 0)) + return(-1); + + /* + * look up in hash table, if found and valid return the uid, + * if found and invalid, return a -1 + */ + ptr = grptb[st_hash(name, namelen, GID_SZ)]; + if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) { + if (ptr->valid == INVALID) + return(-1); + *gid = ptr->gid; + return(0); + } + + if (!gropn) { + setgroupent(1); + ++gropn; + } + if (ptr == NULL) + ptr = (GIDC *)malloc(sizeof(GIDC)); + + /* + * no match, look it up, if no match store it as an invalid entry, + * or store the matching gid + */ + if (ptr == NULL) { + if ((gr = getgrnam(name)) == NULL) + return(-1); + *gid = gr->gr_gid; + return(0); + } + + (void)strncpy(ptr->name, name, GNMLEN); + ptr->name[GNMLEN-1] = '\0'; + if ((gr = getgrnam(name)) == NULL) { + ptr->valid = INVALID; + return(-1); + } + ptr->valid = VALID; + *gid = ptr->gid = gr->gr_gid; + return(0); +} diff --git a/bin/pax/cache.h b/bin/pax/cache.h new file mode 100644 index 0000000..b4bac4f --- /dev/null +++ b/bin/pax/cache.h @@ -0,0 +1,74 @@ +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * 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. + * + * @(#)cache.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * Constants and data structures used to implement group and password file + * caches. Traditional passwd/group cache routines perform quite poorly with + * archives. The chances of hitting a valid lookup with an archive is quite a + * bit worse than with files already resident on the file system. These misses + * create a MAJOR performance cost. To adress this problem, these routines + * cache both hits and misses. + * + * NOTE: name lengths must be as large as those stored in ANY PROTOCOL and + * as stored in the passwd and group files. CACHE SIZES MUST BE PRIME + */ +#define UNMLEN 32 /* >= user name found in any protocol */ +#define GNMLEN 32 /* >= group name found in any protocol */ +#define UID_SZ 317 /* size of user_name/uid cache */ +#define UNM_SZ 317 /* size of user_name/uid cache */ +#define GID_SZ 251 /* size of gid cache */ +#define GNM_SZ 317 /* size of group name cache */ +#define VALID 1 /* entry and name are valid */ +#define INVALID 2 /* entry valid, name NOT valid */ + +/* + * Node structures used in the user, group, uid, and gid caches. + */ + +typedef struct uidc { + int valid; /* is this a valid or a miss entry */ + char name[UNMLEN]; /* uid name */ + uid_t uid; /* cached uid */ +} UIDC; + +typedef struct gidc { + int valid; /* is this a valid or a miss entry */ + char name[GNMLEN]; /* gid name */ + gid_t gid; /* cached gid */ +} GIDC; diff --git a/bin/pax/cpio.c b/bin/pax/cpio.c new file mode 100644 index 0000000..d494c9a --- /dev/null +++ b/bin/pax/cpio.c @@ -0,0 +1,1278 @@ +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * 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[] = "@(#)cpio.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <string.h> +#include <ctype.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include "pax.h" +#include "cpio.h" +#include "extern.h" + +static int rd_nm __P((register ARCHD *, int)); +static int rd_ln_nm __P((register ARCHD *)); +static int com_rd __P((register ARCHD *)); + +/* + * Routines which support the different cpio versions + */ + +static int swp_head; /* binary cpio header byte swap */ + +/* + * Routines common to all versions of cpio + */ + +/* + * cpio_strd() + * Fire up the hard link detection code + * Return: + * 0 if ok -1 otherwise (the return values of lnk_start()) + */ + +#if __STDC__ +int +cpio_strd(void) +#else +int +cpio_strd() +#endif +{ + return(lnk_start()); +} + +/* + * cpio_trail() + * Called to determine if a header block is a valid trailer. We are + * passed the block, the in_sync flag (which tells us we are in resync + * mode; looking for a valid header), and cnt (which starts at zero) + * which is used to count the number of empty blocks we have seen so far. + * Return: + * 0 if a valid trailer, -1 if not a valid trailer, + */ + +#if __STDC__ +int +cpio_trail(register ARCHD *arcn) +#else +int +cpio_trail(arcn) + register ARCHD *arcn; +#endif +{ + /* + * look for trailer id in file we are about to process + */ + if ((strcmp(arcn->name, TRAILER) == 0) && (arcn->sb.st_size == 0)) + return(0); + return(-1); +} + +/* + * com_rd() + * operations common to all cpio read functions. + * Return: + * 0 + */ + +#if __STDC__ +static int +com_rd(register ARCHD *arcn) +#else +static int +com_rd(arcn) + register ARCHD *arcn; +#endif +{ + arcn->skip = 0; + arcn->pat = NULL; + arcn->org_name = arcn->name; + switch(arcn->sb.st_mode & C_IFMT) { + case C_ISFIFO: + arcn->type = PAX_FIF; + break; + case C_ISDIR: + arcn->type = PAX_DIR; + break; + case C_ISBLK: + arcn->type = PAX_BLK; + break; + case C_ISCHR: + arcn->type = PAX_CHR; + break; + case C_ISLNK: + arcn->type = PAX_SLK; + break; + case C_ISOCK: + arcn->type = PAX_SCK; + break; + case C_ISCTG: + case C_ISREG: + default: + /* + * we have file data, set up skip (pad is set in the format + * specific sections) + */ + arcn->sb.st_mode = (arcn->sb.st_mode & 0xfff) | C_ISREG; + arcn->type = PAX_REG; + arcn->skip = arcn->sb.st_size; + break; + } + if (chk_lnk(arcn) < 0) + return(-1); + return(0); +} + +/* + * cpio_end_wr() + * write the special file with the name trailer in the proper format + * Return: + * result of the write of the trailer from the cpio specific write func + */ + +#if __STDC__ +int +cpio_endwr(void) +#else +int +cpio_endwr() +#endif +{ + ARCHD last; + + /* + * create a trailer request and call the proper format write function + */ + bzero((char *)&last, sizeof(last)); + last.nlen = sizeof(TRAILER) - 1; + last.type = PAX_REG; + last.sb.st_nlink = 1; + (void)strcpy(last.name, TRAILER); + return((*frmt->wr)(&last)); +} + +/* + * rd_nam() + * read in the file name which follows the cpio header + * Return: + * 0 if ok, -1 otherwise + */ + +#if __STDC__ +static int +rd_nm(register ARCHD *arcn, int nsz) +#else +static int +rd_nm(arcn, nsz) + register ARCHD *arcn; + int nsz; +#endif +{ + /* + * do not even try bogus values + */ + if ((nsz == 0) || (nsz > sizeof(arcn->name))) { + warn(1, "Cpio file name length %d is out of range", nsz); + return(-1); + } + + /* + * read the name and make sure it is not empty and is \0 terminated + */ + if ((rd_wrbuf(arcn->name,nsz) != nsz) || (arcn->name[nsz-1] != '\0') || + (arcn->name[0] == '\0')) { + warn(1, "Cpio file name in header is corrupted"); + return(-1); + } + return(0); +} + +/* + * rd_ln_nm() + * read in the link name for a file with links. The link name is stored + * like file data (and is NOT \0 terminated!) + * Return: + * 0 if ok, -1 otherwise + */ + +#if __STDC__ +static int +rd_ln_nm(register ARCHD *arcn) +#else +static int +rd_ln_nm(arcn) + register ARCHD *arcn; +#endif +{ + /* + * check the length specified for bogus values + */ + if ((arcn->sb.st_size == 0) || + (arcn->sb.st_size >= sizeof(arcn->ln_name))) { +# ifdef NET2_STAT + warn(1, "Cpio link name length is invalid: %lu", + arcn->sb.st_size); +# else + warn(1, "Cpio link name length is invalid: %qu", + arcn->sb.st_size); +# endif + return(-1); + } + + /* + * read in the link name and \0 terminate it + */ + if (rd_wrbuf(arcn->ln_name, (int)arcn->sb.st_size) != + (int)arcn->sb.st_size) { + warn(1, "Cpio link name read error"); + return(-1); + } + arcn->ln_nlen = arcn->sb.st_size; + arcn->ln_name[arcn->ln_nlen] = '\0'; + + /* + * watch out for those empty link names + */ + if (arcn->ln_name[0] == '\0') { + warn(1, "Cpio link name is corrupt"); + return(-1); + } + return(0); +} + +/* + * Routines common to the extended byte oriented cpio format + */ + +/* + * cpio_id() + * determine if a block given to us is a valid extended byte oriented + * cpio header + * Return: + * 0 if a valid header, -1 otherwise + */ + +#if __STDC__ +int +cpio_id(char *blk, int size) +#else +int +cpio_id(blk, size) + char *blk; + int size; +#endif +{ + if ((size < sizeof(HD_CPIO)) || + (strncmp(blk, AMAGIC, sizeof(AMAGIC) - 1) != 0)) + return(-1); + return(0); +} + +/* + * cpio_rd() + * determine if a buffer is a byte oriented extended cpio archive entry. + * convert and store the values in the ARCHD parameter. + * Return: + * 0 if a valid header, -1 otherwise. + */ + +#if __STDC__ +int +cpio_rd(register ARCHD *arcn, register char *buf) +#else +int +cpio_rd(arcn, buf) + register ARCHD *arcn; + register char *buf; +#endif +{ + register int nsz; + register HD_CPIO *hd; + + /* + * check that this is a valid header, if not return -1 + */ + if (cpio_id(buf, sizeof(HD_CPIO)) < 0) + return(-1); + hd = (HD_CPIO *)buf; + + /* + * byte oriented cpio (posix) does not have padding! extract the octal + * ascii fields from the header + */ + arcn->pad = 0L; + arcn->sb.st_dev = (dev_t)asc_ul(hd->c_dev, sizeof(hd->c_dev), OCT); + arcn->sb.st_ino = (ino_t)asc_ul(hd->c_ino, sizeof(hd->c_ino), OCT); + arcn->sb.st_mode = (mode_t)asc_ul(hd->c_mode, sizeof(hd->c_mode), OCT); + arcn->sb.st_uid = (uid_t)asc_ul(hd->c_uid, sizeof(hd->c_uid), OCT); + arcn->sb.st_gid = (gid_t)asc_ul(hd->c_gid, sizeof(hd->c_gid), OCT); + arcn->sb.st_nlink = (nlink_t)asc_ul(hd->c_nlink, sizeof(hd->c_nlink), + OCT); + arcn->sb.st_rdev = (dev_t)asc_ul(hd->c_rdev, sizeof(hd->c_rdev), OCT); + arcn->sb.st_mtime = (time_t)asc_ul(hd->c_mtime, sizeof(hd->c_mtime), + OCT); + arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime; +# ifdef NET2_STAT + arcn->sb.st_size = (off_t)asc_ul(hd->c_filesize,sizeof(hd->c_filesize), + OCT); +# else + arcn->sb.st_size = (off_t)asc_uqd(hd->c_filesize,sizeof(hd->c_filesize), + OCT); +# endif + + /* + * check name size and if valid, read in the name of this entry (name + * follows header in the archive) + */ + if ((nsz = (int)asc_ul(hd->c_namesize,sizeof(hd->c_namesize),OCT)) < 2) + return(-1); + arcn->nlen = nsz - 1; + if (rd_nm(arcn, nsz) < 0) + return(-1); + + if (((arcn->sb.st_mode&C_IFMT) != C_ISLNK)||(arcn->sb.st_size == 0)) { + /* + * no link name to read for this file + */ + arcn->ln_nlen = 0; + arcn->ln_name[0] = '\0'; + return(com_rd(arcn)); + } + + /* + * check link name size and read in the link name. Link names are + * stored like file data. + */ + if (rd_ln_nm(arcn) < 0) + return(-1); + + /* + * we have a valid header (with a link) + */ + return(com_rd(arcn)); +} + +/* + * cpio_endrd() + * no cleanup needed here, just return size of the trailer (for append) + * Return: + * size of trailer header in this format + */ + +#if __STDC__ +off_t +cpio_endrd(void) +#else +off_t +cpio_endrd() +#endif +{ + return((off_t)(sizeof(HD_CPIO) + sizeof(TRAILER))); +} + +/* + * cpio_stwr() + * start up the device mapping table + * Return: + * 0 if ok, -1 otherwise (what dev_start() returns) + */ + +#if __STDC__ +int +cpio_stwr(void) +#else +int +cpio_stwr() +#endif +{ + return(dev_start()); +} + +/* + * cpio_wr() + * copy the data in the ARCHD to buffer in extended byte oriented cpio + * format. + * Return + * 0 if file has data to be written after the header, 1 if file has NO + * data to write after the header, -1 if archive write failed + */ + +#if __STDC__ +int +cpio_wr(register ARCHD *arcn) +#else +int +cpio_wr(arcn) + register ARCHD *arcn; +#endif +{ + register HD_CPIO *hd; + register int nsz; + char hdblk[sizeof(HD_CPIO)]; + + /* + * check and repair truncated device and inode fields in the header + */ + if (map_dev(arcn, (u_long)CPIO_MASK, (u_long)CPIO_MASK) < 0) + return(-1); + + arcn->pad = 0L; + nsz = arcn->nlen + 1; + hd = (HD_CPIO *)hdblk; + if ((arcn->type != PAX_BLK) && (arcn->type != PAX_CHR)) + arcn->sb.st_rdev = 0; + + switch(arcn->type) { + case PAX_CTG: + case PAX_REG: + case PAX_HRG: + /* + * set data size for file data + */ +# ifdef NET2_STAT + if (ul_asc((u_long)arcn->sb.st_size, hd->c_filesize, + sizeof(hd->c_filesize), OCT)) { +# else + if (uqd_asc((u_quad_t)arcn->sb.st_size, hd->c_filesize, + sizeof(hd->c_filesize), OCT)) { +# endif + warn(1,"File is too large for cpio format %s", + arcn->org_name); + return(1); + } + break; + case PAX_SLK: + /* + * set data size to hold link name + */ + if (ul_asc((u_long)arcn->ln_nlen, hd->c_filesize, + sizeof(hd->c_filesize), OCT)) + goto out; + break; + default: + /* + * all other file types have no file data + */ + if (ul_asc((u_long)0, hd->c_filesize, sizeof(hd->c_filesize), + OCT)) + goto out; + break; + } + + /* + * copy the values to the header using octal ascii + */ + if (ul_asc((u_long)MAGIC, hd->c_magic, sizeof(hd->c_magic), OCT) || + ul_asc((u_long)arcn->sb.st_dev, hd->c_dev, sizeof(hd->c_dev), + OCT) || + ul_asc((u_long)arcn->sb.st_ino, hd->c_ino, sizeof(hd->c_ino), + OCT) || + ul_asc((u_long)arcn->sb.st_mode, hd->c_mode, sizeof(hd->c_mode), + OCT) || + ul_asc((u_long)arcn->sb.st_uid, hd->c_uid, sizeof(hd->c_uid), + OCT) || + ul_asc((u_long)arcn->sb.st_gid, hd->c_gid, sizeof(hd->c_gid), + OCT) || + ul_asc((u_long)arcn->sb.st_nlink, hd->c_nlink, sizeof(hd->c_nlink), + OCT) || + ul_asc((u_long)arcn->sb.st_rdev, hd->c_rdev, sizeof(hd->c_rdev), + OCT) || + ul_asc((u_long)arcn->sb.st_mtime,hd->c_mtime,sizeof(hd->c_mtime), + OCT) || + ul_asc((u_long)nsz, hd->c_namesize, sizeof(hd->c_namesize), OCT)) + goto out; + + /* + * write the file name to the archive + */ + if ((wr_rdbuf(hdblk, (int)sizeof(HD_CPIO)) < 0) || + (wr_rdbuf(arcn->name, nsz) < 0)) { + warn(1, "Unable to write cpio header for %s", arcn->org_name); + return(-1); + } + + /* + * if this file has data, we are done. The caller will write the file + * data, if we are link tell caller we are done, go to next file + */ + if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG) || + (arcn->type == PAX_HRG)) + return(0); + if (arcn->type != PAX_SLK) + return(1); + + /* + * write the link name to the archive, tell the caller to go to the + * next file as we are done. + */ + if (wr_rdbuf(arcn->ln_name, arcn->ln_nlen) < 0) { + warn(1,"Unable to write cpio link name for %s",arcn->org_name); + return(-1); + } + return(1); + + out: + /* + * header field is out of range + */ + warn(1, "Cpio header field is too small to store file %s", + arcn->org_name); + return(1); +} + +/* + * Routines common to the system VR4 version of cpio (with/without file CRC) + */ + +/* + * vcpio_id() + * determine if a block given to us is a valid system VR4 cpio header + * WITHOUT crc. WATCH it the magic cookies are in OCTAL, the header + * uses HEX + * Return: + * 0 if a valid header, -1 otherwise + */ + +#if __STDC__ +int +vcpio_id(char *blk, int size) +#else +int +vcpio_id(blk, size) + char *blk; + int size; +#endif +{ + if ((size < sizeof(HD_VCPIO)) || + (strncmp(blk, AVMAGIC, sizeof(AVMAGIC) - 1) != 0)) + return(-1); + return(0); +} + +/* + * crc_id() + * determine if a block given to us is a valid system VR4 cpio header + * WITH crc. WATCH it the magic cookies are in OCTAL the header uses HEX + * Return: + * 0 if a valid header, -1 otherwise + */ + +#if __STDC__ +int +crc_id(char *blk, int size) +#else +int +crc_id(blk, size) + char *blk; + int size; +#endif +{ + if ((size < sizeof(HD_VCPIO)) || + (strncmp(blk, AVCMAGIC, sizeof(AVCMAGIC) - 1) != 0)) + return(-1); + return(0); +} + +/* + * crc_strd() + w set file data CRC calculations. Fire up the hard link detection code + * Return: + * 0 if ok -1 otherwise (the return values of lnk_start()) + */ + +#if __STDC__ +int +crc_strd(void) +#else +int +crc_strd() +#endif +{ + docrc = 1; + return(lnk_start()); +} + +/* + * vcpio_rd() + * determine if a buffer is a system VR4 archive entry. (with/without CRC) + * convert and store the values in the ARCHD parameter. + * Return: + * 0 if a valid header, -1 otherwise. + */ + +#if __STDC__ +int +vcpio_rd(register ARCHD *arcn, register char *buf) +#else +int +vcpio_rd(arcn, buf) + register ARCHD *arcn; + register char *buf; +#endif +{ + register HD_VCPIO *hd; + dev_t devminor; + dev_t devmajor; + register int nsz; + + /* + * during the id phase it was determined if we were using CRC, use the + * proper id routine. + */ + if (docrc) { + if (crc_id(buf, sizeof(HD_VCPIO)) < 0) + return(-1); + } else { + if (vcpio_id(buf, sizeof(HD_VCPIO)) < 0) + return(-1); + } + + hd = (HD_VCPIO *)buf; + arcn->pad = 0L; + + /* + * extract the hex ascii fields from the header + */ + arcn->sb.st_ino = (ino_t)asc_ul(hd->c_ino, sizeof(hd->c_ino), HEX); + arcn->sb.st_mode = (mode_t)asc_ul(hd->c_mode, sizeof(hd->c_mode), HEX); + arcn->sb.st_uid = (uid_t)asc_ul(hd->c_uid, sizeof(hd->c_uid), HEX); + arcn->sb.st_gid = (gid_t)asc_ul(hd->c_gid, sizeof(hd->c_gid), HEX); + arcn->sb.st_mtime = (time_t)asc_ul(hd->c_mtime,sizeof(hd->c_mtime),HEX); + arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime; +# ifdef NET2_STAT + arcn->sb.st_size = (off_t)asc_ul(hd->c_filesize, + sizeof(hd->c_filesize), HEX); +# else + arcn->sb.st_size = (off_t)asc_uqd(hd->c_filesize, + sizeof(hd->c_filesize), HEX); +# endif + arcn->sb.st_nlink = (nlink_t)asc_ul(hd->c_nlink, sizeof(hd->c_nlink), + HEX); + devmajor = (dev_t)asc_ul(hd->c_maj, sizeof(hd->c_maj), HEX); + devminor = (dev_t)asc_ul(hd->c_min, sizeof(hd->c_min), HEX); + arcn->sb.st_dev = TODEV(devmajor, devminor); + devmajor = (dev_t)asc_ul(hd->c_rmaj, sizeof(hd->c_maj), HEX); + devminor = (dev_t)asc_ul(hd->c_rmin, sizeof(hd->c_min), HEX); + arcn->sb.st_rdev = TODEV(devmajor, devminor); + arcn->crc = asc_ul(hd->c_chksum, sizeof(hd->c_chksum), HEX); + + /* + * check the length of the file name, if ok read it in, return -1 if + * bogus + */ + if ((nsz = (int)asc_ul(hd->c_namesize,sizeof(hd->c_namesize),HEX)) < 2) + return(-1); + arcn->nlen = nsz - 1; + if (rd_nm(arcn, nsz) < 0) + return(-1); + + /* + * skip padding. header + filename is aligned to 4 byte boundries + */ + if (rd_skip((off_t)(VCPIO_PAD(sizeof(HD_VCPIO) + nsz))) < 0) + return(-1); + + /* + * if not a link (or a file with no data), calculate pad size (for + * padding which follows the file data), clear the link name and return + */ + if (((arcn->sb.st_mode&C_IFMT) != C_ISLNK)||(arcn->sb.st_size == 0)) { + /* + * we have a valid header (not a link) + */ + arcn->ln_nlen = 0; + arcn->ln_name[0] = '\0'; + arcn->pad = VCPIO_PAD(arcn->sb.st_size); + return(com_rd(arcn)); + } + + /* + * read in the link name and skip over the padding + */ + if ((rd_ln_nm(arcn) < 0) || + (rd_skip((off_t)(VCPIO_PAD(arcn->sb.st_size))) < 0)) + return(-1); + + /* + * we have a valid header (with a link) + */ + return(com_rd(arcn)); +} + +/* + * vcpio_endrd() + * no cleanup needed here, just return size of the trailer (for append) + * Return: + * size of trailer header in this format + */ + +#if __STDC__ +off_t +vcpio_endrd(void) +#else +off_t +vcpio_endrd() +#endif +{ + return((off_t)(sizeof(HD_VCPIO) + sizeof(TRAILER) + + (VCPIO_PAD(sizeof(HD_VCPIO) + sizeof(TRAILER))))); +} + +/* + * crc_stwr() + * start up the device mapping table, enable crc file calculation + * Return: + * 0 if ok, -1 otherwise (what dev_start() returns) + */ + +#if __STDC__ +int +crc_stwr(void) +#else +int +crc_stwr() +#endif +{ + docrc = 1; + return(dev_start()); +} + +/* + * vcpio_wr() + * copy the data in the ARCHD to buffer in system VR4 cpio + * (with/without crc) format. + * Return + * 0 if file has data to be written after the header, 1 if file has + * NO data to write after the header, -1 if archive write failed + */ + +#if __STDC__ +int +vcpio_wr(register ARCHD *arcn) +#else +int +vcpio_wr(arcn) + register ARCHD *arcn; +#endif +{ + register HD_VCPIO *hd; + unsigned int nsz; + char hdblk[sizeof(HD_VCPIO)]; + + /* + * check and repair truncated device and inode fields in the cpio + * header + */ + if (map_dev(arcn, (u_long)VCPIO_MASK, (u_long)VCPIO_MASK) < 0) + return(-1); + nsz = arcn->nlen + 1; + hd = (HD_VCPIO *)hdblk; + if ((arcn->type != PAX_BLK) && (arcn->type != PAX_CHR)) + arcn->sb.st_rdev = 0; + + /* + * add the proper magic value depending whether we were asked for + * file data crc's, and the crc if needed. + */ + if (docrc) { + if (ul_asc((u_long)VCMAGIC, hd->c_magic, sizeof(hd->c_magic), + OCT) || + ul_asc((u_long)arcn->crc,hd->c_chksum,sizeof(hd->c_chksum), + HEX)) + goto out; + } else { + if (ul_asc((u_long)VMAGIC, hd->c_magic, sizeof(hd->c_magic), + OCT) || + ul_asc((u_long)0L, hd->c_chksum, sizeof(hd->c_chksum),HEX)) + goto out; + } + + switch(arcn->type) { + case PAX_CTG: + case PAX_REG: + case PAX_HRG: + /* + * caller will copy file data to the archive. tell him how + * much to pad. + */ + arcn->pad = VCPIO_PAD(arcn->sb.st_size); +# ifdef NET2_STAT + if (ul_asc((u_long)arcn->sb.st_size, hd->c_filesize, + sizeof(hd->c_filesize), HEX)) { +# else + if (uqd_asc((u_quad_t)arcn->sb.st_size, hd->c_filesize, + sizeof(hd->c_filesize), HEX)) { +# endif + warn(1,"File is too large for sv4cpio format %s", + arcn->org_name); + return(1); + } + break; + case PAX_SLK: + /* + * no file data for the caller to process, the file data has + * the size of the link + */ + arcn->pad = 0L; + if (ul_asc((u_long)arcn->ln_nlen, hd->c_filesize, + sizeof(hd->c_filesize), HEX)) + goto out; + break; + default: + /* + * no file data for the caller to process + */ + arcn->pad = 0L; + if (ul_asc((u_long)0L, hd->c_filesize, sizeof(hd->c_filesize), + HEX)) + goto out; + break; + } + + /* + * set the other fields in the header + */ + if (ul_asc((u_long)arcn->sb.st_ino, hd->c_ino, sizeof(hd->c_ino), + HEX) || + ul_asc((u_long)arcn->sb.st_mode, hd->c_mode, sizeof(hd->c_mode), + HEX) || + ul_asc((u_long)arcn->sb.st_uid, hd->c_uid, sizeof(hd->c_uid), + HEX) || + ul_asc((u_long)arcn->sb.st_gid, hd->c_gid, sizeof(hd->c_gid), + HEX) || + ul_asc((u_long)arcn->sb.st_mtime, hd->c_mtime, sizeof(hd->c_mtime), + HEX) || + ul_asc((u_long)arcn->sb.st_nlink, hd->c_nlink, sizeof(hd->c_nlink), + HEX) || + ul_asc((u_long)MAJOR(arcn->sb.st_dev),hd->c_maj, sizeof(hd->c_maj), + HEX) || + ul_asc((u_long)MINOR(arcn->sb.st_dev),hd->c_min, sizeof(hd->c_min), + HEX) || + ul_asc((u_long)MAJOR(arcn->sb.st_rdev),hd->c_rmaj,sizeof(hd->c_maj), + HEX) || + ul_asc((u_long)MINOR(arcn->sb.st_rdev),hd->c_rmin,sizeof(hd->c_min), + HEX) || + ul_asc((u_long)nsz, hd->c_namesize, sizeof(hd->c_namesize), HEX)) + goto out; + + /* + * write the header, the file name and padding as required. + */ + if ((wr_rdbuf(hdblk, (int)sizeof(HD_VCPIO)) < 0) || + (wr_rdbuf(arcn->name, (int)nsz) < 0) || + (wr_skip((off_t)(VCPIO_PAD(sizeof(HD_VCPIO) + nsz))) < 0)) { + warn(1,"Could not write sv4cpio header for %s",arcn->org_name); + return(-1); + } + + /* + * if we have file data, tell the caller we are done, copy the file + */ + if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG) || + (arcn->type == PAX_HRG)) + return(0); + + /* + * if we are not a link, tell the caller we are done, go to next file + */ + if (arcn->type != PAX_SLK) + return(1); + + /* + * write the link name, tell the caller we are done. + */ + if ((wr_rdbuf(arcn->ln_name, arcn->ln_nlen) < 0) || + (wr_skip((off_t)(VCPIO_PAD(arcn->ln_nlen))) < 0)) { + warn(1,"Could not write sv4cpio link name for %s", + arcn->org_name); + return(-1); + } + return(1); + + out: + /* + * header field is out of range + */ + warn(1,"Sv4cpio header field is too small for file %s",arcn->org_name); + return(1); +} + +/* + * Routines common to the old binary header cpio + */ + +/* + * bcpio_id() + * determine if a block given to us is a old binary cpio header + * (with/without header byte swapping) + * Return: + * 0 if a valid header, -1 otherwise + */ + +#if __STDC__ +int +bcpio_id(char *blk, int size) +#else +int +bcpio_id(blk, size) + char *blk; + int size; +#endif +{ + if (size < sizeof(HD_BCPIO)) + return(-1); + + /* + * check both normal and byte swapped magic cookies + */ + if (((u_short)SHRT_EXT(blk)) == MAGIC) + return(0); + if (((u_short)RSHRT_EXT(blk)) == MAGIC) { + if (!swp_head) + ++swp_head; + return(0); + } + return(-1); +} + +/* + * bcpio_rd() + * determine if a buffer is a old binary archive entry. (it may have byte + * swapped header) convert and store the values in the ARCHD parameter. + * This is a very old header format and should not really be used. + * Return: + * 0 if a valid header, -1 otherwise. + */ + +#if __STDC__ +int +bcpio_rd(register ARCHD *arcn, register char *buf) +#else +int +bcpio_rd(arcn, buf) + register ARCHD *arcn; + register char *buf; +#endif +{ + register HD_BCPIO *hd; + register int nsz; + + /* + * check the header + */ + if (bcpio_id(buf, sizeof(HD_BCPIO)) < 0) + return(-1); + + arcn->pad = 0L; + hd = (HD_BCPIO *)buf; + if (swp_head) { + /* + * header has swapped bytes on 16 bit boundries + */ + arcn->sb.st_dev = (dev_t)(RSHRT_EXT(hd->h_dev)); + arcn->sb.st_ino = (ino_t)(RSHRT_EXT(hd->h_ino)); + arcn->sb.st_mode = (mode_t)(RSHRT_EXT(hd->h_mode)); + arcn->sb.st_uid = (uid_t)(RSHRT_EXT(hd->h_uid)); + arcn->sb.st_gid = (gid_t)(RSHRT_EXT(hd->h_gid)); + arcn->sb.st_nlink = (nlink_t)(RSHRT_EXT(hd->h_nlink)); + arcn->sb.st_rdev = (dev_t)(RSHRT_EXT(hd->h_rdev)); + arcn->sb.st_mtime = (time_t)(RSHRT_EXT(hd->h_mtime_1)); + arcn->sb.st_mtime = (arcn->sb.st_mtime << 16) | + ((time_t)(RSHRT_EXT(hd->h_mtime_2))); + arcn->sb.st_size = (off_t)(RSHRT_EXT(hd->h_filesize_1)); + arcn->sb.st_size = (arcn->sb.st_size << 16) | + ((off_t)(RSHRT_EXT(hd->h_filesize_2))); + nsz = (int)(RSHRT_EXT(hd->h_namesize)); + } else { + arcn->sb.st_dev = (dev_t)(SHRT_EXT(hd->h_dev)); + arcn->sb.st_ino = (ino_t)(SHRT_EXT(hd->h_ino)); + arcn->sb.st_mode = (mode_t)(SHRT_EXT(hd->h_mode)); + arcn->sb.st_uid = (uid_t)(SHRT_EXT(hd->h_uid)); + arcn->sb.st_gid = (gid_t)(SHRT_EXT(hd->h_gid)); + arcn->sb.st_nlink = (nlink_t)(SHRT_EXT(hd->h_nlink)); + arcn->sb.st_rdev = (dev_t)(SHRT_EXT(hd->h_rdev)); + arcn->sb.st_mtime = (time_t)(SHRT_EXT(hd->h_mtime_1)); + arcn->sb.st_mtime = (arcn->sb.st_mtime << 16) | + ((time_t)(SHRT_EXT(hd->h_mtime_2))); + arcn->sb.st_size = (off_t)(SHRT_EXT(hd->h_filesize_1)); + arcn->sb.st_size = (arcn->sb.st_size << 16) | + ((off_t)(SHRT_EXT(hd->h_filesize_2))); + nsz = (int)(SHRT_EXT(hd->h_namesize)); + } + arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime; + + /* + * check the file name size, if bogus give up. otherwise read the file + * name + */ + if (nsz < 2) + return(-1); + arcn->nlen = nsz - 1; + if (rd_nm(arcn, nsz) < 0) + return(-1); + + /* + * header + file name are aligned to 2 byte boundries, skip if needed + */ + if (rd_skip((off_t)(BCPIO_PAD(sizeof(HD_BCPIO) + nsz))) < 0) + return(-1); + + /* + * if not a link (or a file with no data), calculate pad size (for + * padding which follows the file data), clear the link name and return + */ + if (((arcn->sb.st_mode & C_IFMT) != C_ISLNK)||(arcn->sb.st_size == 0)){ + /* + * we have a valid header (not a link) + */ + arcn->ln_nlen = 0; + arcn->ln_name[0] = '\0'; + arcn->pad = BCPIO_PAD(arcn->sb.st_size); + return(com_rd(arcn)); + } + + if ((rd_ln_nm(arcn) < 0) || + (rd_skip((off_t)(BCPIO_PAD(arcn->sb.st_size))) < 0)) + return(-1); + + /* + * we have a valid header (with a link) + */ + return(com_rd(arcn)); +} + +/* + * bcpio_endrd() + * no cleanup needed here, just return size of the trailer (for append) + * Return: + * size of trailer header in this format + */ + +#if __STDC__ +off_t +bcpio_endrd(void) +#else +off_t +bcpio_endrd() +#endif +{ + return((off_t)(sizeof(HD_BCPIO) + sizeof(TRAILER) + + (BCPIO_PAD(sizeof(HD_BCPIO) + sizeof(TRAILER))))); +} + +/* + * bcpio_wr() + * copy the data in the ARCHD to buffer in old binary cpio format + * There is a real chance of field overflow with this critter. So we + * always check the conversion is ok. nobody in his their right mind + * should write an achive in this format... + * Return + * 0 if file has data to be written after the header, 1 if file has NO + * data to write after the header, -1 if archive write failed + */ + +#if __STDC__ +int +bcpio_wr(register ARCHD *arcn) +#else +int +bcpio_wr(arcn) + register ARCHD *arcn; +#endif +{ + register HD_BCPIO *hd; + register int nsz; + char hdblk[sizeof(HD_BCPIO)]; + off_t t_offt; + int t_int; + time_t t_timet; + + /* + * check and repair truncated device and inode fields in the cpio + * header + */ + if (map_dev(arcn, (u_long)BCPIO_MASK, (u_long)BCPIO_MASK) < 0) + return(-1); + + if ((arcn->type != PAX_BLK) && (arcn->type != PAX_CHR)) + arcn->sb.st_rdev = 0; + hd = (HD_BCPIO *)hdblk; + + switch(arcn->type) { + case PAX_CTG: + case PAX_REG: + case PAX_HRG: + /* + * caller will copy file data to the archive. tell him how + * much to pad. + */ + arcn->pad = BCPIO_PAD(arcn->sb.st_size); + hd->h_filesize_1[0] = CHR_WR_0(arcn->sb.st_size); + hd->h_filesize_1[1] = CHR_WR_1(arcn->sb.st_size); + hd->h_filesize_2[0] = CHR_WR_2(arcn->sb.st_size); + hd->h_filesize_2[1] = CHR_WR_3(arcn->sb.st_size); + t_offt = (off_t)(SHRT_EXT(hd->h_filesize_1)); + t_offt = (t_offt<<16) | ((off_t)(SHRT_EXT(hd->h_filesize_2))); + if (arcn->sb.st_size != t_offt) { + warn(1,"File is too large for bcpio format %s", + arcn->org_name); + return(1); + } + break; + case PAX_SLK: + /* + * no file data for the caller to process, the file data has + * the size of the link + */ + arcn->pad = 0L; + hd->h_filesize_1[0] = CHR_WR_0(arcn->ln_nlen); + hd->h_filesize_1[1] = CHR_WR_1(arcn->ln_nlen); + hd->h_filesize_2[0] = CHR_WR_2(arcn->ln_nlen); + hd->h_filesize_2[1] = CHR_WR_3(arcn->ln_nlen); + t_int = (int)(SHRT_EXT(hd->h_filesize_1)); + t_int = (t_int << 16) | ((int)(SHRT_EXT(hd->h_filesize_2))); + if (arcn->ln_nlen != t_int) + goto out; + break; + default: + /* + * no file data for the caller to process + */ + arcn->pad = 0L; + hd->h_filesize_1[0] = (char)0; + hd->h_filesize_1[1] = (char)0; + hd->h_filesize_2[0] = (char)0; + hd->h_filesize_2[1] = (char)0; + break; + } + + /* + * build up the rest of the fields + */ + hd->h_magic[0] = CHR_WR_2(MAGIC); + hd->h_magic[1] = CHR_WR_3(MAGIC); + hd->h_dev[0] = CHR_WR_2(arcn->sb.st_dev); + hd->h_dev[1] = CHR_WR_3(arcn->sb.st_dev); + if (arcn->sb.st_dev != (dev_t)(SHRT_EXT(hd->h_dev))) + goto out; + hd->h_ino[0] = CHR_WR_2(arcn->sb.st_ino); + hd->h_ino[1] = CHR_WR_3(arcn->sb.st_ino); + if (arcn->sb.st_ino != (ino_t)(SHRT_EXT(hd->h_ino))) + goto out; + hd->h_mode[0] = CHR_WR_2(arcn->sb.st_mode); + hd->h_mode[1] = CHR_WR_3(arcn->sb.st_mode); + if (arcn->sb.st_mode != (mode_t)(SHRT_EXT(hd->h_mode))) + goto out; + hd->h_uid[0] = CHR_WR_2(arcn->sb.st_uid); + hd->h_uid[1] = CHR_WR_3(arcn->sb.st_uid); + if (arcn->sb.st_uid != (uid_t)(SHRT_EXT(hd->h_uid))) + goto out; + hd->h_gid[0] = CHR_WR_2(arcn->sb.st_gid); + hd->h_gid[1] = CHR_WR_3(arcn->sb.st_gid); + if (arcn->sb.st_gid != (gid_t)(SHRT_EXT(hd->h_gid))) + goto out; + hd->h_nlink[0] = CHR_WR_2(arcn->sb.st_nlink); + hd->h_nlink[1] = CHR_WR_3(arcn->sb.st_nlink); + if (arcn->sb.st_nlink != (nlink_t)(SHRT_EXT(hd->h_nlink))) + goto out; + hd->h_rdev[0] = CHR_WR_2(arcn->sb.st_rdev); + hd->h_rdev[1] = CHR_WR_3(arcn->sb.st_rdev); + if (arcn->sb.st_rdev != (dev_t)(SHRT_EXT(hd->h_rdev))) + goto out; + hd->h_mtime_1[0] = CHR_WR_0(arcn->sb.st_mtime); + hd->h_mtime_1[1] = CHR_WR_1(arcn->sb.st_mtime); + hd->h_mtime_2[0] = CHR_WR_2(arcn->sb.st_mtime); + hd->h_mtime_2[1] = CHR_WR_3(arcn->sb.st_mtime); + t_timet = (time_t)(SHRT_EXT(hd->h_mtime_1)); + t_timet = (t_timet << 16) | ((time_t)(SHRT_EXT(hd->h_mtime_2))); + if (arcn->sb.st_mtime != t_timet) + goto out; + nsz = arcn->nlen + 1; + hd->h_namesize[0] = CHR_WR_2(nsz); + hd->h_namesize[1] = CHR_WR_3(nsz); + if (nsz != (int)(SHRT_EXT(hd->h_namesize))) + goto out; + + /* + * write the header, the file name and padding as required. + */ + if ((wr_rdbuf(hdblk, (int)sizeof(HD_BCPIO)) < 0) || + (wr_rdbuf(arcn->name, nsz) < 0) || + (wr_skip((off_t)(BCPIO_PAD(sizeof(HD_BCPIO) + nsz))) < 0)) { + warn(1, "Could not write bcpio header for %s", arcn->org_name); + return(-1); + } + + /* + * if we have file data, tell the caller we are done + */ + if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG) || + (arcn->type == PAX_HRG)) + return(0); + + /* + * if we are not a link, tell the caller we are done, go to next file + */ + if (arcn->type != PAX_SLK) + return(1); + + /* + * write the link name, tell the caller we are done. + */ + if ((wr_rdbuf(arcn->ln_name, arcn->ln_nlen) < 0) || + (wr_skip((off_t)(BCPIO_PAD(arcn->ln_nlen))) < 0)) { + warn(1,"Could not write bcpio link name for %s",arcn->org_name); + return(-1); + } + return(1); + + out: + /* + * header field is out of range + */ + warn(1,"Bcpio header field is too small for file %s", arcn->org_name); + return(1); +} diff --git a/bin/pax/cpio.h b/bin/pax/cpio.h new file mode 100644 index 0000000..73429f9 --- /dev/null +++ b/bin/pax/cpio.h @@ -0,0 +1,151 @@ +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * 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. + * + * @(#)cpio.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * Defines common to all versions of cpio + */ +#define TRAILER "TRAILER!!!" /* name in last archive record */ + +/* + * Header encoding of the different file types + */ +#define C_ISDIR 040000 /* Directory */ +#define C_ISFIFO 010000 /* FIFO */ +#define C_ISREG 0100000 /* Regular file */ +#define C_ISBLK 060000 /* Block special file */ +#define C_ISCHR 020000 /* Character special file */ +#define C_ISCTG 0110000 /* Reserved for contiguous files */ +#define C_ISLNK 0120000 /* Reserved for symbolic links */ +#define C_ISOCK 0140000 /* Reserved for sockets */ +#define C_IFMT 0170000 /* type of file */ + +/* + * Data Interchange Format - Extended cpio header format - POSIX 1003.1-1990 + */ +typedef struct { + char c_magic[6]; /* magic cookie */ + char c_dev[6]; /* device number */ + char c_ino[6]; /* inode number */ + char c_mode[6]; /* file type/access */ + char c_uid[6]; /* owners uid */ + char c_gid[6]; /* owners gid */ + char c_nlink[6]; /* # of links at archive creation */ + char c_rdev[6]; /* block/char major/minor # */ + char c_mtime[11]; /* modification time */ + char c_namesize[6]; /* length of pathname */ + char c_filesize[11]; /* length of file in bytes */ +} HD_CPIO; + +#define MAGIC 070707 /* transportable archive id */ + +#ifdef _PAX_ +#define AMAGIC "070707" /* ascii equivalent string of MAGIC */ +#define CPIO_MASK 0x3ffff /* bits valid in the dev/ino fields */ + /* used for dev/inode remaps */ +#endif /* _PAX_ */ + +/* + * Binary cpio header structure + * + * CAUTION! CAUTION! CAUTION! + * Each field really represents a 16 bit short (NOT ASCII). Described as + * an array of chars in an attempt to improve portability!! + */ +typedef struct { + u_char h_magic[2]; + u_char h_dev[2]; + u_char h_ino[2]; + u_char h_mode[2]; + u_char h_uid[2]; + u_char h_gid[2]; + u_char h_nlink[2]; + u_char h_rdev[2]; + u_char h_mtime_1[2]; + u_char h_mtime_2[2]; + u_char h_namesize[2]; + u_char h_filesize_1[2]; + u_char h_filesize_2[2]; +} HD_BCPIO; + +#ifdef _PAX_ +/* + * extraction and creation macros for binary cpio + */ +#define SHRT_EXT(ch) ((((unsigned)(ch)[0])<<8) | (((unsigned)(ch)[1])&0xff)) +#define RSHRT_EXT(ch) ((((unsigned)(ch)[1])<<8) | (((unsigned)(ch)[0])&0xff)) +#define CHR_WR_0(val) ((char)(((val) >> 24) & 0xff)) +#define CHR_WR_1(val) ((char)(((val) >> 16) & 0xff)) +#define CHR_WR_2(val) ((char)(((val) >> 8) & 0xff)) +#define CHR_WR_3(val) ((char)((val) & 0xff)) + +/* + * binary cpio masks and pads + */ +#define BCPIO_PAD(x) ((2 - ((x) & 1)) & 1) /* pad to next 2 byte word */ +#define BCPIO_MASK 0xffff /* mask for dev/ino fields */ +#endif /* _PAX_ */ + +/* + * System VR4 cpio header structure (with/without file data crc) + */ +typedef struct { + char c_magic[6]; /* magic cookie */ + char c_ino[8]; /* inode number */ + char c_mode[8]; /* file type/access */ + char c_uid[8]; /* owners uid */ + char c_gid[8]; /* owners gid */ + char c_nlink[8]; /* # of links at archive creation */ + char c_mtime[8]; /* modification time */ + char c_filesize[8]; /* length of file in bytes */ + char c_maj[8]; /* block/char major # */ + char c_min[8]; /* block/char minor # */ + char c_rmaj[8]; /* special file major # */ + char c_rmin[8]; /* special file minor # */ + char c_namesize[8]; /* length of pathname */ + char c_chksum[8]; /* 0 OR CRC of bytes of FILE data */ +} HD_VCPIO; + +#define VMAGIC 070701 /* sVr4 new portable archive id */ +#define VCMAGIC 070702 /* sVr4 new portable archive id CRC */ +#ifdef _PAX_ +#define AVMAGIC "070701" /* ascii string of above */ +#define AVCMAGIC "070702" /* ascii string of above */ +#define VCPIO_PAD(x) ((4 - ((x) & 3)) & 3) /* pad to next 4 byte word */ +#define VCPIO_MASK 0xffffffff /* mask for dev/ino fields */ +#endif /* _PAX_ */ diff --git a/bin/pax/extern.h b/bin/pax/extern.h new file mode 100644 index 0000000..a1a10d0 --- /dev/null +++ b/bin/pax/extern.h @@ -0,0 +1,285 @@ +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * 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/18/94 + */ + +/* + * External references from each source file + */ + +#include <sys/cdefs.h> + +/* + * ar_io.c + */ +extern char *arcname; +int ar_open __P((char *)); +void ar_close __P((void)); +void ar_drain __P((void)); +int ar_set_wr __P((void)); +int ar_app_ok __P((void)); +int ar_read __P((register char *, register int)); +int ar_write __P((register char *, register int)); +int ar_rdsync __P((void)); +int ar_fow __P((off_t, off_t *)); +int ar_rev __P((off_t )); +int ar_next __P((void)); + +/* + * ar_subs.c + */ +extern u_long flcnt; +void list __P((void)); +void extract __P((void)); +void append __P((void)); +void archive __P((void)); +void copy __P((void)); + +/* + * buf_subs.c + */ +extern int blksz; +extern int wrblksz; +extern int maxflt; +extern int rdblksz; +extern off_t wrlimit; +extern off_t rdcnt; +extern off_t wrcnt; +int wr_start __P((void)); +int rd_start __P((void)); +void cp_start __P((void)); +int appnd_start __P((off_t)); +int rd_sync __P((void)); +void pback __P((char *, int)); +int rd_skip __P((off_t)); +void wr_fin __P((void)); +int wr_rdbuf __P((register char *, register int)); +int rd_wrbuf __P((register char *, register int)); +int wr_skip __P((off_t)); +int wr_rdfile __P((ARCHD *, int, off_t *)); +int rd_wrfile __P((ARCHD *, int, off_t *)); +void cp_file __P((ARCHD *, int, int)); +int buf_fill __P((void)); +int buf_flush __P((register int)); + +/* + * cache.c + */ +int uidtb_start __P((void)); +int gidtb_start __P((void)); +int usrtb_start __P((void)); +int grptb_start __P((void)); +char * name_uid __P((uid_t, int)); +char * name_gid __P((gid_t, int)); +int uid_name __P((char *, uid_t *)); +int gid_name __P((char *, gid_t *)); + +/* + * cpio.c + */ +int cpio_strd __P((void)); +int cpio_trail __P((register ARCHD *)); +int cpio_endwr __P((void)); +int cpio_id __P((char *, int)); +int cpio_rd __P((register ARCHD *, register char *)); +off_t cpio_endrd __P((void)); +int cpio_stwr __P((void)); +int cpio_wr __P((register ARCHD *)); +int vcpio_id __P((char *, int)); +int crc_id __P((char *, int)); +int crc_strd __P((void)); +int vcpio_rd __P((register ARCHD *, register char *)); +off_t vcpio_endrd __P((void)); +int crc_stwr __P((void)); +int vcpio_wr __P((register ARCHD *)); +int bcpio_id __P((char *, int)); +int bcpio_rd __P((register ARCHD *, register char *)); +off_t bcpio_endrd __P((void)); +int bcpio_wr __P((register ARCHD *)); + +/* + * file_subs.c + */ +int file_creat __P((register ARCHD *)); +void file_close __P((register ARCHD *, int)); +int lnk_creat __P((register ARCHD *)); +int cross_lnk __P((register ARCHD *)); +int chk_same __P((register ARCHD *)); +int node_creat __P((register ARCHD *)); +int unlnk_exist __P((register char *, register int)); +int chk_path __P((register char *, uid_t, gid_t)); +void set_ftime __P((char *fnm, time_t mtime, time_t atime, int frc)); +int set_ids __P((char *, uid_t, gid_t)); +void set_pmode __P((char *, mode_t)); +int file_write __P((int, char *, register int, int *, int *, int, char *)); +void file_flush __P((int, char *, int)); +void rdfile_close __P((register ARCHD *, register int *)); +int set_crc __P((register ARCHD *, register int)); + +/* + * ftree.c + */ +int ftree_start __P((void)); +int ftree_add __P((register char *)); +void ftree_sel __P((register ARCHD *)); +void ftree_chk __P((void)); +int next_file __P((register ARCHD *)); + +/* + * gen_subs.c + */ +void ls_list __P((register ARCHD *, time_t)); +void ls_tty __P((register ARCHD *)); +void zf_strncpy __P((register char *, register char *, int)); +int l_strncpy __P((register char *, register char *, int)); +u_long asc_ul __P((register char *, int, register int)); +int ul_asc __P((u_long, register char *, register int, register int)); +#ifndef NET2_STAT +u_quad_t asc_uqd __P((register char *, int, register int)); +int uqd_asc __P((u_quad_t, register char *, register int, register int)); +#endif + +/* + * options.c + */ +extern FSUB fsub[]; +extern int ford[]; +void options __P((register int, register char **)); +OPLIST * opt_next __P((void)); +int opt_add __P((register char *)); +int bad_opt __P((void)); + +/* + * pat_rep.c + */ +int rep_add __P((register char *)); +int pat_add __P((char *)); +void pat_chk __P((void)); +int pat_sel __P((register ARCHD *)); +int pat_match __P((register ARCHD *)); +int mod_name __P((register ARCHD *)); +int set_dest __P((register ARCHD *, char *, int)); + +/* + * pax.c + */ +extern int act; +extern FSUB *frmt; +extern int cflag; +extern int dflag; +extern int iflag; +extern int kflag; +extern int lflag; +extern int nflag; +extern int tflag; +extern int uflag; +extern int vflag; +extern int Dflag; +extern int Hflag; +extern int Lflag; +extern int Xflag; +extern int Yflag; +extern int Zflag; +extern int vfpart; +extern int patime; +extern int pmtime; +extern int pmode; +extern int pids; +extern int exit_val; +extern int docrc; +extern char *dirptr; +extern char *ltmfrmt; +extern char *argv0; +int main __P((int, char **)); +void sig_cleanup __P((int)); + +/* + * sel_subs.c + */ +int sel_chk __P((register ARCHD *)); +int grp_add __P((register char *)); +int usr_add __P((register char *)); +int trng_add __P((register char *)); + +/* + * tables.c + */ +int lnk_start __P((void)); +int chk_lnk __P((register ARCHD *)); +void purg_lnk __P((register ARCHD *)); +void lnk_end __P((void)); +int ftime_start __P((void)); +int chk_ftime __P((register ARCHD *)); +int name_start __P((void)); +int add_name __P((register char *, int, char *)); +void sub_name __P((register char *, int *)); +int dev_start __P((void)); +int add_dev __P((register ARCHD *)); +int map_dev __P((register ARCHD *, u_long, u_long)); +int atdir_start __P((void)); +void atdir_end __P((void)); +void add_atdir __P((char *, dev_t, ino_t, time_t, time_t)); +int get_atdir __P((dev_t, ino_t, time_t *, time_t *)); +int dir_start __P((void)); +void add_dir __P((char *, int, struct stat *, int)); +void proc_dir __P((void)); +u_int st_hash __P((char *, int, int)); + +/* + * tar.c + */ +int tar_endwr __P((void)); +off_t tar_endrd __P((void)); +int tar_trail __P((register char *, register int, register int *)); +int tar_id __P((register char *, int)); +int tar_opt __P((void)); +int tar_rd __P((register ARCHD *, register char *)); +int tar_wr __P((register ARCHD *)); +int ustar_strd __P((void)); +int ustar_stwr __P((void)); +int ustar_id __P((char *, int)); +int ustar_rd __P((register ARCHD *, register char *)); +int ustar_wr __P((register ARCHD *)); + +/* + * tty_subs.c + */ +int tty_init __P((void)); +void tty_prnt __P((char *, ...)); +int tty_read __P((char *, int)); +void warn __P((int, char *, ...)); +void syswarn __P((int, int, char *, ...)); diff --git a/bin/pax/file_subs.c b/bin/pax/file_subs.c new file mode 100644 index 0000000..f394707 --- /dev/null +++ b/bin/pax/file_subs.c @@ -0,0 +1,1055 @@ +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * 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_subs.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <unistd.h> +#include <sys/param.h> +#include <fcntl.h> +#include <string.h> +#include <stdio.h> +#include <ctype.h> +#include <errno.h> +#include <sys/uio.h> +#include <stdlib.h> +#include "pax.h" +#include "extern.h" + +static int +mk_link __P((register char *,register struct stat *,register char *, int)); + +/* + * routines that deal with file operations such as: creating, removing; + * and setting access modes, uid/gid and times of files + */ + +#define FILEBITS (S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO) +#define SETBITS (S_ISUID | S_ISGID) +#define ABITS (FILEBITS | SETBITS) + +/* + * file_creat() + * Create and open a file. + * Return: + * file descriptor or -1 for failure + */ + +#if __STDC__ +int +file_creat(register ARCHD *arcn) +#else +int +file_creat(arcn) + register ARCHD *arcn; +#endif +{ + int fd = -1; + mode_t file_mode; + int oerrno; + + /* + * assume file doesn't exist, so just try to create it, most times this + * works. We have to take special handling when the file does exist. To + * detect this, we use O_EXCL. For example when trying to create a + * file and a character device or fifo exists with the same name, we + * can accidently open the device by mistake (or block waiting to open) + * If we find that the open has failed, then figure spend the effore to + * figure out why. This strategy was found to have better average + * performance in common use than checking the file (and the path) + * first with lstat. + */ + file_mode = arcn->sb.st_mode & FILEBITS; + if ((fd = open(arcn->name, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, + file_mode)) >= 0) + return(fd); + + /* + * the file seems to exist. First we try to get rid of it (found to be + * the second most common failure when traced). If this fails, only + * then we go to the expense to check and create the path to the file + */ + if (unlnk_exist(arcn->name, arcn->type) != 0) + return(-1); + + for (;;) { + /* + * try to open it again, if this fails, check all the nodes in + * the path and give it a final try. if chk_path() finds that + * it cannot fix anything, we will skip the last attempt + */ + if ((fd = open(arcn->name, O_WRONLY | O_CREAT | O_TRUNC, + file_mode)) >= 0) + break; + oerrno = errno; + if (chk_path(arcn->name,arcn->sb.st_uid,arcn->sb.st_gid) < 0) { + syswarn(1, oerrno, "Unable to create %s", arcn->name); + return(-1); + } + } + return(fd); +} + +/* + * file_close() + * Close file descriptor to a file just created by pax. Sets modes, + * ownership and times as required. + * Return: + * 0 for success, -1 for failure + */ + +#if __STDC__ +void +file_close(register ARCHD *arcn, int fd) +#else +void +file_close(arcn, fd) + register ARCHD *arcn; + int fd; +#endif +{ + int res = 0; + + if (fd < 0) + return; + if (close(fd) < 0) + syswarn(0, errno, "Unable to close file descriptor on %s", + arcn->name); + + /* + * set owner/groups first as this may strip off mode bits we want + * then set file permission modes. Then set file access and + * modification times. + */ + if (pids) + res = set_ids(arcn->name, arcn->sb.st_uid, arcn->sb.st_gid); + + /* + * IMPORTANT SECURITY NOTE: + * if not preserving mode or we cannot set uid/gid, then PROHIBIT + * set uid/gid bits + */ + if (!pmode || res) + arcn->sb.st_mode &= ~(SETBITS); + if (pmode) + set_pmode(arcn->name, arcn->sb.st_mode); + if (patime || pmtime) + set_ftime(arcn->name, arcn->sb.st_mtime, arcn->sb.st_atime, 0); +} + +/* + * lnk_creat() + * Create a hard link to arcn->ln_name from arcn->name. arcn->ln_name + * must exist; + * Return: + * 0 if ok, -1 otherwise + */ + +#if __STDC__ +int +lnk_creat(register ARCHD *arcn) +#else +int +lnk_creat(arcn) + register ARCHD *arcn; +#endif +{ + struct stat sb; + + /* + * we may be running as root, so we have to be sure that link target + * is not a directory, so we lstat and check + */ + if (lstat(arcn->ln_name, &sb) < 0) { + syswarn(1,errno,"Unable to link to %s from %s", arcn->ln_name, + arcn->name); + return(-1); + } + + if (S_ISDIR(sb.st_mode)) { + warn(1, "A hard link to the directory %s is not allowed", + arcn->ln_name); + return(-1); + } + + return(mk_link(arcn->ln_name, &sb, arcn->name, 0)); +} + +/* + * cross_lnk() + * Create a hard link to arcn->org_name from arcn->name. Only used in copy + * with the -l flag. No warning or error if this does not succeed (we will + * then just create the file) + * Return: + * 1 if copy() should try to create this file node + * 0 if cross_lnk() ok, -1 for fatal flaw (like linking to self). + */ + +#if __STDC__ +int +cross_lnk(register ARCHD *arcn) +#else +int +cross_lnk(arcn) + register ARCHD *arcn; +#endif +{ + /* + * try to make a link to orginal file (-l flag in copy mode). make sure + * we do not try to link to directories in case we are running as root + * (and it might succeed). + */ + if (arcn->type == PAX_DIR) + return(1); + return(mk_link(arcn->org_name, &(arcn->sb), arcn->name, 1)); +} + +/* + * chk_same() + * In copy mode if we are not trying to make hard links between the src + * and destinations, make sure we are not going to overwrite ourselves by + * accident. This slows things down a little, but we have to protect all + * those people who make typing errors. + * Return: + * 1 the target does not exist, go ahead and copy + * 0 skip it file exists (-k) or may be the same as source file + */ + +#if __STDC__ +int +chk_same(register ARCHD *arcn) +#else +int +chk_same(arcn) + register ARCHD *arcn; +#endif +{ + struct stat sb; + + /* + * if file does not exist, return. if file exists and -k, skip it + * quietly + */ + if (lstat(arcn->name, &sb) < 0) + return(1); + if (kflag) + return(0); + + /* + * better make sure the user does not have src == dest by mistake + */ + if ((arcn->sb.st_dev == sb.st_dev) && (arcn->sb.st_ino == sb.st_ino)) { + warn(1, "Unable to copy %s, file would overwrite itself", + arcn->name); + return(0); + } + return(1); +} + +/* + * mk_link() + * try to make a hard link between two files. if ign set, we do not + * complain. + * Return: + * 0 if successful (or we are done with this file but no error, such as + * finding the from file exists and the user has set -k). + * 1 when ign was set to indicates we could not make the link but we + * should try to copy/extract the file as that might work (and is an + * allowed option). -1 an error occurred. + */ + +#if __STDC__ +static int +mk_link(register char *to, register struct stat *to_sb, register char *from, + int ign) +#else +static int +mk_link(to, to_sb, from, ign) + register char *to; + register struct stat *to_sb; + register char *from; + int ign; +#endif +{ + struct stat sb; + int oerrno; + + /* + * if from file exists, it has to be unlinked to make the link. If the + * file exists and -k is set, skip it quietly + */ + if (lstat(from, &sb) == 0) { + if (kflag) + return(0); + + /* + * make sure it is not the same file, protect the user + */ + if ((to_sb->st_dev==sb.st_dev)&&(to_sb->st_ino == sb.st_ino)) { + warn(1, "Unable to link file %s to itself", to); + return(-1);; + } + + /* + * try to get rid of the file, based on the type + */ + if (S_ISDIR(sb.st_mode)) { + if (rmdir(from) < 0) { + syswarn(1, errno, "Unable to remove %s", from); + return(-1); + } + } else if (unlink(from) < 0) { + if (!ign) { + syswarn(1, errno, "Unable to remove %s", from); + return(-1); + } + return(1); + } + } + + /* + * from file is gone (or did not exist), try to make the hard link. + * if it fails, check the path and try it again (if chk_path() says to + * try again) + */ + for (;;) { + if (link(to, from) == 0) + break; + oerrno = errno; + if (chk_path(from, to_sb->st_uid, to_sb->st_gid) == 0) + continue; + if (!ign) { + syswarn(1, oerrno, "Could not link to %s from %s", to, + from); + return(-1); + } + return(1); + } + + /* + * all right the link was made + */ + return(0); +} + +/* + * node_creat() + * create an entry in the file system (other than a file or hard link). + * If successful, sets uid/gid modes and times as required. + * Return: + * 0 if ok, -1 otherwise + */ + +#if __STDC__ +int +node_creat(register ARCHD *arcn) +#else +int +node_creat(arcn) + register ARCHD *arcn; +#endif +{ + register int res; + register int ign = 0; + register int oerrno; + register int pass = 0; + mode_t file_mode; + struct stat sb; + + /* + * create node based on type, if that fails try to unlink the node and + * try again. finally check the path and try again. As noted in the + * file and link creation routines, this method seems to exhibit the + * best performance in general use workloads. + */ + file_mode = arcn->sb.st_mode & FILEBITS; + + for (;;) { + switch(arcn->type) { + case PAX_DIR: + res = mkdir(arcn->name, file_mode); + if (ign) + res = 0; + break; + case PAX_CHR: + file_mode |= S_IFCHR; + res = mknod(arcn->name, file_mode, arcn->sb.st_rdev); + break; + case PAX_BLK: + file_mode |= S_IFBLK; + res = mknod(arcn->name, file_mode, arcn->sb.st_rdev); + break; + case PAX_FIF: + res = mkfifo(arcn->name, file_mode); + break; + case PAX_SCK: + /* + * Skip sockets, operation has no meaning under BSD + */ + warn(0, + "%s skipped. Sockets cannot be copied or extracted", + arcn->name); + return(-1); + case PAX_SLK: + if ((res = symlink(arcn->ln_name, arcn->name)) == 0) + return(0); + break; + case PAX_CTG: + case PAX_HLK: + case PAX_HRG: + case PAX_REG: + default: + /* + * we should never get here + */ + warn(0, "%s has an unknown file type, skipping", + arcn->name); + return(-1); + } + + /* + * if we were able to create the node break out of the loop, + * otherwise try to unlink the node and try again. if that + * fails check the full path and try a final time. + */ + if (res == 0) + break; + + /* + * we failed to make the node + */ + oerrno = errno; + if ((ign = unlnk_exist(arcn->name, arcn->type)) < 0) + return(-1); + + if (++pass <= 1) + continue; + + if (chk_path(arcn->name,arcn->sb.st_uid,arcn->sb.st_gid) < 0) { + syswarn(1, oerrno, "Could not create: %s", arcn->name); + return(-1); + } + } + + /* + * we were able to create the node. set uid/gid, modes and times + */ + if (pids) + res = set_ids(arcn->name, arcn->sb.st_uid, arcn->sb.st_gid); + else + res = 0; + + /* + * IMPORTANT SECURITY NOTE: + * if not preserving mode or we cannot set uid/gid, then PROHIBIT any + * set uid/gid bits + */ + if (!pmode || res) + arcn->sb.st_mode &= ~(SETBITS); + if (pmode) + set_pmode(arcn->name, arcn->sb.st_mode); + + if (arcn->type == PAX_DIR) { + /* + * Dirs must be processed again at end of extract to set times + * and modes to agree with those stored in the archive. However + * to allow extract to continue, we may have to also set owner + * rights. This allows nodes in the archive that are children + * of this directory to be extracted without failure. Both time + * and modes will be fixed after the entire archive is read and + * before pax exits. + */ + if (access(arcn->name, R_OK | W_OK | X_OK) < 0) { + if (lstat(arcn->name, &sb) < 0) { + syswarn(0, errno,"Could not access %s (stat)", + arcn->name); + set_pmode(arcn->name,file_mode | S_IRWXU); + } else { + /* + * We have to add rights to the dir, so we make + * sure to restore the mode. The mode must be + * restored AS CREATED and not as stored if + * pmode is not set. + */ + set_pmode(arcn->name, + ((sb.st_mode & FILEBITS) | S_IRWXU)); + if (!pmode) + arcn->sb.st_mode = sb.st_mode; + } + + /* + * we have to force the mode to what was set here, + * since we changed it from the default as created. + */ + add_dir(arcn->name, arcn->nlen, &(arcn->sb), 1); + } else if (pmode || patime || pmtime) + add_dir(arcn->name, arcn->nlen, &(arcn->sb), 0); + } + + if (patime || pmtime) + set_ftime(arcn->name, arcn->sb.st_mtime, arcn->sb.st_atime, 0); + return(0); +} + +/* + * unlnk_exist() + * Remove node from file system with the specified name. We pass the type + * of the node that is going to replace it. When we try to create a + * directory and find that it already exists, we allow processing to + * continue as proper modes etc will always be set for it later on. + * Return: + * 0 is ok to proceed, no file with the specified name exists + * -1 we were unable to remove the node, or we should not remove it (-k) + * 1 we found a directory and we were going to create a directory. + */ + +#if __STDC__ +int +unlnk_exist(register char *name, register int type) +#else +int +unlnk_exist(name, type) + register char *name; + register int type; +#endif +{ + struct stat sb; + + /* + * the file does not exist, or -k we are done + */ + if (lstat(name, &sb) < 0) + return(0); + if (kflag) + return(-1); + + if (S_ISDIR(sb.st_mode)) { + /* + * try to remove a directory, if it fails and we were going to + * create a directory anyway, tell the caller (return a 1) + */ + if (rmdir(name) < 0) { + if (type == PAX_DIR) + return(1); + syswarn(1,errno,"Unable to remove directory %s", name); + return(-1); + } + return(0); + } + + /* + * try to get rid of all non-directory type nodes + */ + if (unlink(name) < 0) { + syswarn(1, errno, "Could not unlink %s", name); + return(-1); + } + return(0); +} + +/* + * chk_path() + * We were trying to create some kind of node in the file system and it + * failed. chk_path() makes sure the path up to the node exists and is + * writeable. When we have to create a directory that is missing along the + * path somewhere, the directory we create will be set to the same + * uid/gid as the file has (when uid and gid are being preserved). + * NOTE: this routine is a real performance loss. It is only used as a + * last resort when trying to create entries in the file system. + * Return: + * -1 when it could find nothing it is allowed to fix. + * 0 otherwise + */ + +#if __STDC__ +int +chk_path( register char *name, uid_t st_uid, gid_t st_gid) +#else +int +chk_path(name, st_uid, st_gid) + register char *name; + uid_t st_uid; + gid_t st_gid; +#endif +{ + register char *spt = name; + struct stat sb; + int retval = -1; + + /* + * watch out for paths with nodes stored directly in / (e.g. /bozo) + */ + if (*spt == '/') + ++spt; + + for(;;) { + /* + * work foward from the first / and check each part of the path + */ + spt = strchr(spt, '/'); + if (spt == NULL) + break; + *spt = '\0'; + + /* + * if it exists we assume it is a directory, it is not within + * the spec (at least it seems to read that way) to alter the + * file system for nodes NOT EXPLICITLY stored on the archive. + * If that assumption is changed, you would test the node here + * and figure out how to get rid of it (probably like some + * recursive unlink()) or fix up the directory permissions if + * required (do an access()). + */ + if (lstat(name, &sb) == 0) { + *(spt++) = '/'; + continue; + } + + /* + * the path fails at this point, see if we can create the + * needed directory and continue on + */ + if (mkdir(name, S_IRWXU | S_IRWXG | S_IRWXO) < 0) { + *spt = '/'; + retval = -1; + break; + } + + /* + * we were able to create the directory. We will tell the + * caller that we found something to fix, and it is ok to try + * and create the node again. + */ + retval = 0; + if (pids) + (void)set_ids(name, st_uid, st_gid); + + /* + * make sure the user doen't have some strange umask that + * causes this newly created directory to be unusable. We fix + * the modes and restore them back to the creation default at + * the end of pax + */ + if ((access(name, R_OK | W_OK | X_OK) < 0) && + (lstat(name, &sb) == 0)) { + set_pmode(name, ((sb.st_mode & FILEBITS) | S_IRWXU)); + add_dir(name, spt - name, &sb, 1); + } + *(spt++) = '/'; + continue; + } + return(retval); +} + +/* + * set_ftime() + * Set the access time and modification time for a named file. If frc is + * non-zero we force these times to be set even if the the user did not + * request access and/or modification time preservation (this is also + * used by -t to reset access times). + * When ign is zero, only those times the user has asked for are set, the + * other ones are left alone. We do not assume the un-documented feature + * of many utimes() implementations that consider a 0 time value as a do + * not set request. + */ + +#if __STDC__ +void +set_ftime(char *fnm, time_t mtime, time_t atime, int frc) +#else +void +set_ftime(fnm, mtime, atime, frc) + char *fnm; + time_t mtime; + time_t atime; + int frc; +#endif +{ + static struct timeval tv[2] = {{0L, 0L}, {0L, 0L}}; + struct stat sb; + + tv[0].tv_sec = (long)atime; + tv[1].tv_sec = (long)mtime; + if (!frc && (!patime || !pmtime)) { + /* + * if we are not forcing, only set those times the user wants + * set. We get the current values of the times if we need them. + */ + if (lstat(fnm, &sb) == 0) { + if (!patime) + tv[0].tv_sec = (long)sb.st_atime; + if (!pmtime) + tv[1].tv_sec = (long)sb.st_mtime; + } else + syswarn(0,errno,"Unable to obtain file stats %s", fnm); + } + + /* + * set the times + */ + if (utimes(fnm, tv) < 0) + syswarn(1, errno, "Access/modification time set failed on: %s", + fnm); + return; +} + +/* + * set_ids() + * set the uid and gid of a file system node + * Return: + * 0 when set, -1 on failure + */ + +#if __STDC__ +int +set_ids(char *fnm, uid_t uid, gid_t gid) +#else +int +set_ids(fnm, uid, gid) + char *fnm; + uid_t uid; + gid_t gid; +#endif +{ + if (chown(fnm, uid, gid) < 0) { + syswarn(1, errno, "Unable to set file uid/gid of %s", fnm); + return(-1); + } + return(0); +} + +/* + * set_pmode() + * Set file access mode + */ + +#if __STDC__ +void +set_pmode(char *fnm, mode_t mode) +#else +void +set_pmode(fnm, mode) + char *fnm; + mode_t mode; +#endif +{ + mode &= ABITS; + if (chmod(fnm, mode) < 0) + syswarn(1, errno, "Could not set permissions on %s", fnm); + return; +} + +/* + * file_write() + * Write/copy a file (during copy or archive extract). This routine knows + * how to copy files with lseek holes in it. (Which are read as file + * blocks containing all 0's but do not have any file blocks associated + * with the data). Typical examples of these are files created by dbm + * variants (.pag files). While the file size of these files are huge, the + * actual storage is quite small (the files are sparse). The problem is + * the holes read as all zeros so are probably stored on the archive that + * way (there is no way to determine if the file block is really a hole, + * we only know that a file block of all zero's can be a hole). + * At this writing, no major archive format knows how to archive files + * with holes. However, on extraction (or during copy, -rw) we have to + * deal with these files. Without detecting the holes, the files can + * consume a lot of file space if just written to disk. This replacement + * for write when passed the basic allocation size of a file system block, + * uses lseek whenever it detects the input data is all 0 within that + * file block. In more detail, the strategy is as follows: + * While the input is all zero keep doing an lseek. Keep track of when we + * pass over file block boundries. Only write when we hit a non zero + * input. once we have written a file block, we continue to write it to + * the end (we stop looking at the input). When we reach the start of the + * next file block, start checking for zero blocks again. Working on file + * block boundries significantly reduces the overhead when copying files + * that are NOT very sparse. This overhead (when compared to a write) is + * almost below the measurement resolution on many systems. Without it, + * files with holes cannot be safely copied. It does has a side effect as + * it can put holes into files that did not have them before, but that is + * not a problem since the file contents are unchanged (in fact it saves + * file space). (Except on paging files for diskless clients. But since we + * cannot determine one of those file from here, we ignore them). If this + * ever ends up on a system where CTG files are supported and the holes + * are not desired, just do a conditional test in those routines that + * call file_write() and have it call write() instead. BEFORE CLOSING THE + * FILE, make sure to call file_flush() when the last write finishes with + * an empty block. A lot of file systems will not create an lseek hole at + * the end. In this case we drop a single 0 at the end to force the + * trailing 0's in the file. + * ---Parameters--- + * rem: how many bytes left in this file system block + * isempt: have we written to the file block yet (is it empty) + * sz: basic file block allocation size + * cnt: number of bytes on this write + * str: buffer to write + * Return: + * number of bytes written, -1 on write (or lseek) error. + */ + +#if __STDC__ +int +file_write(int fd, char *str, register int cnt, int *rem, int *isempt, int sz, + char *name) +#else +int +file_write(fd, str, cnt, rem, isempt, sz, name) + int fd; + char *str; + register int cnt; + int *rem; + int *isempt; + int sz; + char *name; +#endif +{ + register char *pt; + register char *end; + register int wcnt; + register char *st = str; + + /* + * while we have data to process + */ + while (cnt) { + if (!*rem) { + /* + * We are now at the start of file system block again + * (or what we think one is...). start looking for + * empty blocks again + */ + *isempt = 1; + *rem = sz; + } + + /* + * only examine up to the end of the current file block or + * remaining characters to write, whatever is smaller + */ + wcnt = MIN(cnt, *rem); + cnt -= wcnt; + *rem -= wcnt; + if (*isempt) { + /* + * have not written to this block yet, so we keep + * looking for zero's + */ + pt = st; + end = st + wcnt; + + /* + * look for a zero filled buffer + */ + while ((pt < end) && (*pt == '\0')) + ++pt; + + if (pt == end) { + /* + * skip, buf is empty so far + */ + if (lseek(fd, (off_t)wcnt, SEEK_CUR) < 0) { + syswarn(1,errno,"File seek on %s", + name); + return(-1); + } + st = pt; + continue; + } + /* + * drat, the buf is not zero filled + */ + *isempt = 0; + } + + /* + * have non-zero data in this file system block, have to write + */ + if (write(fd, st, wcnt) != wcnt) { + syswarn(1, errno, "Failed write to file %s", name); + return(-1); + } + st += wcnt; + } + return(st - str); +} + +/* + * file_flush() + * when the last file block in a file is zero, many file systems will not + * let us create a hole at the end. To get the last block with zeros, we + * write the last BYTE with a zero (back up one byte and write a zero). + */ + +#if __STDC__ +void +file_flush(int fd, char *fname, int isempt) +#else +void +file_flush(fd, fname, isempt) + int fd; + char *fname; + int isempt; +#endif +{ + static char blnk[] = "\0"; + + /* + * silly test, but make sure we are only called when the last block is + * filled with all zeros. + */ + if (!isempt) + return; + + /* + * move back one byte and write a zero + */ + if (lseek(fd, (off_t)-1, SEEK_CUR) < 0) { + syswarn(1, errno, "Failed seek on file %s", fname); + return; + } + + if (write(fd, blnk, 1) < 0) + syswarn(1, errno, "Failed write to file %s", fname); + return; +} + +/* + * rdfile_close() + * close a file we have beed reading (to copy or archive). If we have to + * reset access time (tflag) do so (the times are stored in arcn). + */ + +#if __STDC__ +void +rdfile_close(register ARCHD *arcn, register int *fd) +#else +void +rdfile_close(arcn, fd) + register ARCHD *arcn; + register int *fd; +#endif +{ + /* + * make sure the file is open + */ + if (*fd < 0) + return; + + (void)close(*fd); + *fd = -1; + if (!tflag) + return; + + /* + * user wants last access time reset + */ + set_ftime(arcn->org_name, arcn->sb.st_mtime, arcn->sb.st_atime, 1); + return; +} + +/* + * set_crc() + * read a file to calculate its crc. This is a real drag. Archive formats + * that have this, end up reading the file twice (we have to write the + * header WITH the crc before writing the file contents. Oh well... + * Return: + * 0 if was able to calculate the crc, -1 otherwise + */ + +#if __STDC__ +int +set_crc(register ARCHD *arcn, register int fd) +#else +int +set_crc(arcn, fd) + register ARCHD *arcn; + register int fd; +#endif +{ + register int i; + register int res; + off_t cpcnt = 0L; + u_long size; + unsigned long crc = 0L; + char tbuf[FILEBLK]; + struct stat sb; + + if (fd < 0) { + /* + * hmm, no fd, should never happen. well no crc then. + */ + arcn->crc = 0L; + return(0); + } + + if ((size = (u_long)arcn->sb.st_blksize) > (u_long)sizeof(tbuf)) + size = (u_long)sizeof(tbuf); + + /* + * read all the bytes we think that there are in the file. If the user + * is trying to archive an active file, forget this file. + */ + for(;;) { + if ((res = read(fd, tbuf, size)) <= 0) + break; + cpcnt += res; + for (i = 0; i < res; ++i) + crc += (tbuf[i] & 0xff); + } + + /* + * safety check. we want to avoid archiving files that are active as + * they can create inconsistant archive copies. + */ + if (cpcnt != arcn->sb.st_size) + warn(1, "File changed size %s", arcn->org_name); + else if (fstat(fd, &sb) < 0) + syswarn(1, errno, "Failed stat on %s", arcn->org_name); + else if (arcn->sb.st_mtime != sb.st_mtime) + warn(1, "File %s was modified during read", arcn->org_name); + else if (lseek(fd, (off_t)0L, SEEK_SET) < 0) + syswarn(1, errno, "File rewind failed on: %s", arcn->org_name); + else { + arcn->crc = crc; + return(0); + } + return(-1); +} diff --git a/bin/pax/ftree.c b/bin/pax/ftree.c new file mode 100644 index 0000000..62dd94d --- /dev/null +++ b/bin/pax/ftree.c @@ -0,0 +1,543 @@ +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * 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[] = "@(#)ftree.c 8.2 (Berkeley) 4/18/94"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <unistd.h> +#include <string.h> +#include <stdio.h> +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <fts.h> +#include "pax.h" +#include "ftree.h" +#include "extern.h" + +/* + * routines to interface with the fts library function. + * + * file args supplied to pax are stored on a single linked list (of type FTREE) + * and given to fts to be processed one at a time. pax "selects" files from + * the expansion of each arg into the corresponding file tree (if the arg is a + * directory, otherwise the node itself is just passed to pax). The selection + * is modified by the -n and -u flags. The user is informed when a specific + * file arg does not generate any selected files. -n keeps expanding the file + * tree arg until one of its files is selected, then skips to the next file + * arg. when the user does not supply the file trees as command line args to + * pax, they are read from stdin + */ + +static FTS *ftsp = NULL; /* curent FTS handle */ +static int ftsopts; /* options to be used on fts_open */ +static char *farray[2]; /* array for passing each arg to fts */ +static FTREE *fthead = NULL; /* head of linked list of file args */ +static FTREE *fttail = NULL; /* tail of linked list of file args */ +static FTREE *ftcur = NULL; /* current file arg being processed */ +static FTSENT *ftent = NULL; /* current file tree entry */ +static int ftree_skip; /* when set skip to next file arg */ + +static int ftree_arg __P((void)); + +/* + * ftree_start() + * initialize the options passed to fts_open() during this run of pax + * options are based on the selection of pax options by the user + * fts_start() also calls fts_arg() to open the first valid file arg. We + * also attempt to reset directory access times when -t (tflag) is set. + * Return: + * 0 if there is at least one valid file arg to process, -1 otherwise + */ + +#if __STDC__ +int +ftree_start(void) +#else +int +ftree_start() +#endif +{ + /* + * set up the operation mode of fts, open the first file arg. We must + * use FTS_NOCHDIR, as the user may have to open multiple archives and + * if fts did a chdir off into the boondocks, we may create an archive + * volume in an place where the user did not expect to. + */ + ftsopts = FTS_NOCHDIR; + + /* + * optional user flags that effect file traversal + * -H command line symlink follow only (half follow) + * -L follow sylinks (logical) + * -P do not follow sylinks (physical). This is the default. + * -X do not cross over mount points + * -t preserve access times on files read. + * -n select only the first member of a file tree when a match is found + * -d do not extract subtrees rooted at a directory arg. + */ + if (Lflag) + ftsopts |= FTS_LOGICAL; + else + ftsopts |= FTS_PHYSICAL; + if (Hflag) +# ifdef NET2_FTS + warn(0, "The -H flag is not supported on this version"); +# else + ftsopts |= FTS_COMFOLLOW; +# endif + if (Xflag) + ftsopts |= FTS_XDEV; + + if ((fthead == NULL) && ((farray[0] = malloc(PAXPATHLEN+2)) == NULL)) { + warn(1, "Unable to allocate memory for file name buffer"); + return(-1); + } + + if (ftree_arg() < 0) + return(-1); + if (tflag && (atdir_start() < 0)) + return(-1); + return(0); +} + +/* + * ftree_add() + * add the arg to the linked list of files to process. Each will be + * processed by fts one at a time + * Return: + * 0 if added to the linked list, -1 if failed + */ + +#if __STDC__ +int +ftree_add(register char *str) +#else +int +ftree_add(str) + register char *str; +#endif +{ + register FTREE *ft; + register int len; + + /* + * simple check for bad args + */ + if ((str == NULL) || (*str == '\0')) { + warn(0, "Invalid file name arguement"); + return(-1); + } + + /* + * allocate FTREE node and add to the end of the linked list (args are + * processed in the same order they were passed to pax). Get rid of any + * trailing / the user may pass us. (watch out for / by itself). + */ + if ((ft = (FTREE *)malloc(sizeof(FTREE))) == NULL) { + warn(0, "Unable to allocate memory for filename"); + return(-1); + } + + if (((len = strlen(str) - 1) > 0) && (str[len] == '/')) + str[len] = '\0'; + ft->fname = str; + ft->refcnt = 0; + ft->fow = NULL; + if (fthead == NULL) { + fttail = fthead = ft; + return(0); + } + fttail->fow = ft; + fttail = ft; + return(0); +} + +/* + * ftree_sel() + * this entry has been selected by pax. bump up reference count and handle + * -n and -d processing. + */ + +#if __STDC__ +void +ftree_sel(register ARCHD *arcn) +#else +void +ftree_sel(arcn) + register ARCHD *arcn; +#endif +{ + /* + * set reference bit for this pattern. This linked list is only used + * when file trees are supplied pax as args. The list is not used when + * the trees are read from stdin. + */ + if (ftcur != NULL) + ftcur->refcnt = 1; + + /* + * if -n we are done with this arg, force a skip to the next arg when + * pax asks for the next file in next_file(). + * if -d we tell fts only to match the directory (if the arg is a dir) + * and not the entire file tree rooted at that point. + */ + if (nflag) + ftree_skip = 1; + + if (!dflag || (arcn->type != PAX_DIR)) + return; + + if (ftent != NULL) + (void)fts_set(ftsp, ftent, FTS_SKIP); +} + +/* + * ftree_chk() + * called at end on pax execution. Prints all those file args that did not + * have a selected member (reference count still 0) + */ + +#if __STDC__ +void +ftree_chk(void) +#else +void +ftree_chk() +#endif +{ + register FTREE *ft; + register int wban = 0; + + /* + * make sure all dir access times were reset. + */ + if (tflag) + atdir_end(); + + /* + * walk down list and check reference count. Print out those members + * that never had a match + */ + for (ft = fthead; ft != NULL; ft = ft->fow) { + if (ft->refcnt > 0) + continue; + if (wban == 0) { + warn(1,"WARNING! These file names were not selected:"); + ++wban; + } + (void)fprintf(stderr, "%s\n", ft->fname); + } +} + +/* + * ftree_arg() + * Get the next file arg for fts to process. Can be from either the linked + * list or read from stdin when the user did not them as args to pax. Each + * arg is processed until the first successful fts_open(). + * Return: + * 0 when the next arg is ready to go, -1 if out of file args (or EOF on + * stdin). + */ + +#if __STDC__ +static int +ftree_arg(void) +#else +static int +ftree_arg() +#endif +{ + register char *pt; + + /* + * close off the current file tree + */ + if (ftsp != NULL) { + (void)fts_close(ftsp); + ftsp = NULL; + } + + /* + * keep looping until we get a valid file tree to process. Stop when we + * reach the end of the list (or get an eof on stdin) + */ + for(;;) { + if (fthead == NULL) { + /* + * the user didn't supply any args, get the file trees + * to process from stdin; + */ + if (fgets(farray[0], PAXPATHLEN+1, stdin) == NULL) + return(-1); + if ((pt = strchr(farray[0], '\n')) != NULL) + *pt = '\0'; + } else { + /* + * the user supplied the file args as arguements to pax + */ + if (ftcur == NULL) + ftcur = fthead; + else if ((ftcur = ftcur->fow) == NULL) + return(-1); + farray[0] = ftcur->fname; + } + + /* + * watch it, fts wants the file arg stored in a array of char + * ptrs, with the last one a null. we use a two element array + * and set farray[0] to point at the buffer with the file name + * in it. We cannnot pass all the file args to fts at one shot + * as we need to keep a handle on which file arg generates what + * files (the -n and -d flags need this). If the open is + * successful, return a 0. + */ + if ((ftsp = fts_open(farray, ftsopts, NULL)) != NULL) + break; + } + return(0); +} + +/* + * next_file() + * supplies the next file to process in the supplied archd structure. + * Return: + * 0 when contents of arcn have been set with the next file, -1 when done. + */ + +#if __STDC__ +int +next_file(register ARCHD *arcn) +#else +int +next_file(arcn) + register ARCHD *arcn; +#endif +{ + register int cnt; + time_t atime; + time_t mtime; + + /* + * ftree_sel() might have set the ftree_skip flag if the user has the + * -n option and a file was selected from this file arg tree. (-n says + * only one member is matched for each pattern) ftree_skip being 1 + * forces us to go to the next arg now. + */ + if (ftree_skip) { + /* + * clear and go to next arg + */ + ftree_skip = 0; + if (ftree_arg() < 0) + return(-1); + } + + /* + * loop until we get a valid file to process + */ + for(;;) { + if ((ftent = fts_read(ftsp)) == NULL) { + /* + * out of files in this tree, go to next arg, if none + * we are done + */ + if (ftree_arg() < 0) + return(-1); + continue; + } + + /* + * handle each type of fts_read() flag + */ + switch(ftent->fts_info) { + case FTS_D: + case FTS_DEFAULT: + case FTS_F: + case FTS_SL: + case FTS_SLNONE: + /* + * these are all ok + */ + break; + case FTS_DP: + /* + * already saw this directory. If the user wants file + * access times reset, we use this to restore the + * access time for this directory since this is the + * last time we will see it in this file subtree + * remember to force the time (this is -t on a read + * directory, not a created directory). + */ +# ifdef NET2_FTS + if (!tflag || (get_atdir(ftent->fts_statb.st_dev, + ftent->fts_statb.st_ino, &mtime, &atime) < 0)) +# else + if (!tflag || (get_atdir(ftent->fts_statp->st_dev, + ftent->fts_statp->st_ino, &mtime, &atime) < 0)) +# endif + continue; + set_ftime(ftent->fts_path, mtime, atime, 1); + continue; + case FTS_DC: + /* + * fts claims a file system cycle + */ + warn(1,"File system cycle found at %s",ftent->fts_path); + continue; + case FTS_DNR: +# ifdef NET2_FTS + syswarn(1, errno, +# else + syswarn(1, ftent->fts_errno, +# endif + "Unable to read directory %s", ftent->fts_path); + continue; + case FTS_ERR: +# ifdef NET2_FTS + syswarn(1, errno, +# else + syswarn(1, ftent->fts_errno, +# endif + "File system traversal error"); + continue; + case FTS_NS: + case FTS_NSOK: +# ifdef NET2_FTS + syswarn(1, errno, +# else + syswarn(1, ftent->fts_errno, +# endif + "Unable to access %s", ftent->fts_path); + continue; + } + + /* + * ok got a file tree node to process. copy info into arcn + * structure (initialize as required) + */ + arcn->skip = 0; + arcn->pad = 0; + arcn->ln_nlen = 0; + arcn->ln_name[0] = '\0'; +# ifdef NET2_FTS + arcn->sb = ftent->fts_statb; +# else + arcn->sb = *(ftent->fts_statp); +# endif + + /* + * file type based set up and copy into the arcn struct + * SIDE NOTE: + * we try to reset the access time on all files and directories + * we may read when the -t flag is specified. files are reset + * when we close them after copying. we reset the directories + * when we are done with their file tree (we also clean up at + * end in case we cut short a file tree traversal). However + * there is no way to reset access times on symlinks. + */ + switch(S_IFMT & arcn->sb.st_mode) { + case S_IFDIR: + arcn->type = PAX_DIR; + if (!tflag) + break; + add_atdir(ftent->fts_path, arcn->sb.st_dev, + arcn->sb.st_ino, arcn->sb.st_mtime, + arcn->sb.st_atime); + break; + case S_IFCHR: + arcn->type = PAX_CHR; + break; + case S_IFBLK: + arcn->type = PAX_BLK; + break; + case S_IFREG: + /* + * only regular files with have data to store on the + * archive. all others will store a zero length skip. + * the skip field is used by pax for actual data it has + * to read (or skip over). + */ + arcn->type = PAX_REG; + arcn->skip = arcn->sb.st_size; + break; + case S_IFLNK: + arcn->type = PAX_SLK; + /* + * have to read the symlink path from the file + */ + if ((cnt = readlink(ftent->fts_path, arcn->ln_name, + PAXPATHLEN)) < 0) { + syswarn(1, errno, "Unable to read symlink %s", + ftent->fts_path); + continue; + } + /* + * set link name length, watch out readlink does not + * allways null terminate the link path + */ + arcn->ln_name[cnt] = '\0'; + arcn->ln_nlen = cnt; + break; + case S_IFSOCK: + /* + * under BSD storing a socket is senseless but we will + * let the format specific write function make the + * decision of what to do with it. + */ + arcn->type = PAX_SCK; + break; + case S_IFIFO: + arcn->type = PAX_FIF; + break; + } + break; + } + + /* + * copy file name, set file name length + */ + arcn->nlen = l_strncpy(arcn->name, ftent->fts_path, PAXPATHLEN+1); + arcn->name[arcn->nlen] = '\0'; + arcn->org_name = ftent->fts_path; + return(0); +} diff --git a/bin/pax/ftree.h b/bin/pax/ftree.h new file mode 100644 index 0000000..f93eda6 --- /dev/null +++ b/bin/pax/ftree.h @@ -0,0 +1,50 @@ +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * 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. + * + * @(#)ftree.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * Data structure used by the ftree.c routines to store the file args to be + * handed to fts(). It keeps a reference count of which args generated a + * "selected" member + */ + +typedef struct ftree { + char *fname; /* file tree name */ + int refcnt; /* has tree had a selected file? */ + struct ftree *fow; /* pointer to next entry on list */ +} FTREE; diff --git a/bin/pax/gen_subs.c b/bin/pax/gen_subs.c new file mode 100644 index 0000000..390e5bd --- /dev/null +++ b/bin/pax/gen_subs.c @@ -0,0 +1,487 @@ +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * 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[] = "@(#)gen_subs.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <stdio.h> +#include <ctype.h> +#include <tzfile.h> +#include <utmp.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include "pax.h" +#include "extern.h" + +/* + * a collection of general purpose subroutines used by pax + */ + +/* + * constants used by ls_list() when printing out archive members + */ +#define MODELEN 20 +#define DATELEN 64 +#define SIXMONTHS ((DAYSPERNYEAR / 2) * SECSPERDAY) +#define CURFRMT "%b %e %H:%M" +#define OLDFRMT "%b %e %Y" +#ifndef UT_NAMESIZE +#define UT_NAMESIZE 8 +#endif +#define UT_GRPSIZE 6 + +/* + * ls_list() + * list the members of an archive in ls format + */ + +#if __STDC__ +void +ls_list(register ARCHD *arcn, time_t now) +#else +void +ls_list(arcn, now) + register ARCHD *arcn; + time_t now; +#endif +{ + register struct stat *sbp; + char f_mode[MODELEN]; + char f_date[DATELEN]; + char *timefrmt; + + /* + * if not verbose, just print the file name + */ + if (!vflag) { + (void)printf("%s\n", arcn->name); + (void)fflush(stdout); + return; + } + + /* + * user wants long mode + */ + sbp = &(arcn->sb); + strmode(sbp->st_mode, f_mode); + + if (ltmfrmt == NULL) { + /* + * no locale specified format. time format based on age + * compared to the time pax was started. + */ + if ((sbp->st_mtime + SIXMONTHS) <= now) + timefrmt = OLDFRMT; + else + timefrmt = CURFRMT; + } else + timefrmt = ltmfrmt; + + /* + * print file mode, link count, uid, gid and time + */ + if (strftime(f_date,DATELEN,timefrmt,localtime(&(sbp->st_mtime))) == 0) + f_date[0] = '\0'; + (void)printf("%s%2u %-*s %-*s ", f_mode, sbp->st_nlink, UT_NAMESIZE, + name_uid(sbp->st_uid, 1), UT_GRPSIZE, + name_gid(sbp->st_gid, 1)); + + /* + * print device id's for devices, or sizes for other nodes + */ + if ((arcn->type == PAX_CHR) || (arcn->type == PAX_BLK)) +# ifdef NET2_STAT + (void)printf("%4u,%4u ", MAJOR(sbp->st_rdev), +# else + (void)printf("%4lu,%4lu ", MAJOR(sbp->st_rdev), +# endif + MINOR(sbp->st_rdev)); + else { +# ifdef NET2_STAT + (void)printf("%9lu ", sbp->st_size); +# else + (void)printf("%9qu ", sbp->st_size); +# endif + } + + /* + * print name and link info for hard and soft links + */ + (void)printf("%s %s", f_date, arcn->name); + if ((arcn->type == PAX_HLK) || (arcn->type == PAX_HRG)) + (void)printf(" == %s\n", arcn->ln_name); + else if (arcn->type == PAX_SLK) + (void)printf(" => %s\n", arcn->ln_name); + else + (void)putchar('\n'); + (void)fflush(stdout); + return; +} + +/* + * tty_ls() + * print a short summary of file to tty. + */ + +#if __STDC__ +void +ls_tty(register ARCHD *arcn) +#else +void +ls_tty(arcn) + register ARCHD *arcn; +#endif +{ + char f_date[DATELEN]; + char f_mode[MODELEN]; + char *timefrmt; + + if (ltmfrmt == NULL) { + /* + * no locale specified format + */ + if ((arcn->sb.st_mtime + SIXMONTHS) <= time((time_t *)NULL)) + timefrmt = OLDFRMT; + else + timefrmt = CURFRMT; + } else + timefrmt = ltmfrmt; + + /* + * convert time to string, and print + */ + if (strftime(f_date, DATELEN, timefrmt, + localtime(&(arcn->sb.st_mtime))) == 0) + f_date[0] = '\0'; + strmode(arcn->sb.st_mode, f_mode); + tty_prnt("%s%s %s\n", f_mode, f_date, arcn->name); + return; +} + +/* + * zf_strncpy() + * copy src to dest up to len chars (stopping at first '\0'), when src is + * shorter than len, pads to len with '\0'. big performance win (and + * a lot easier to code) over strncpy(), then a strlen() then a + * bzero(). (or doing the bzero() first). + */ + +#if __STDC__ +void +zf_strncpy(register char *dest, register char *src, int len) +#else +void +zf_strncpy(dest, src, len) + register char *dest; + register char *src; + int len; +#endif +{ + register char *stop; + + stop = dest + len; + while ((dest < stop) && (*src != '\0')) + *dest++ = *src++; + while (dest < stop) + *dest++ = '\0'; + return; +} + +/* + * l_strncpy() + * copy src to dest up to len chars (stopping at first '\0') + * Return: + * number of chars copied. (Note this is a real performance win over + * doing a strncpy() then a strlen() + */ + +#if __STDC__ +int +l_strncpy(register char *dest, register char *src, int len) +#else +int +l_strncpy(dest, src, len) + register char *dest; + register char *src; + int len; +#endif +{ + register char *stop; + register char *start; + + stop = dest + len; + start = dest; + while ((dest < stop) && (*src != '\0')) + *dest++ = *src++; + if (dest < stop) + *dest = '\0'; + return(dest - start); +} + +/* + * asc_ul() + * convert hex/octal character string into a u_long. We do not have to + * check for overflow! (the headers in all supported formats are not large + * enough to create an overflow). + * NOTE: strings passed to us are NOT TERMINATED. + * Return: + * unsigned long value + */ + +#if __STDC__ +u_long +asc_ul(register char *str, int len, register int base) +#else +u_long +asc_ul(str, len, base) + register char *str; + int len; + register int base; +#endif +{ + register char *stop; + u_long tval = 0; + + stop = str + len; + + /* + * skip over leading blanks and zeros + */ + while ((str < stop) && ((*str == ' ') || (*str == '0'))) + ++str; + + /* + * for each valid digit, shift running value (tval) over to next digit + * and add next digit + */ + if (base == HEX) { + while (str < stop) { + if ((*str >= '0') && (*str <= '9')) + tval = (tval << 4) + (*str++ - '0'); + else if ((*str >= 'A') && (*str <= 'F')) + tval = (tval << 4) + 10 + (*str++ - 'A'); + else if ((*str >= 'a') && (*str <= 'f')) + tval = (tval << 4) + 10 + (*str++ - 'a'); + else + break; + } + } else { + while ((str < stop) && (*str >= '0') && (*str <= '7')) + tval = (tval << 3) + (*str++ - '0'); + } + return(tval); +} + +/* + * ul_asc() + * convert an unsigned long into an hex/oct ascii string. pads with LEADING + * ascii 0's to fill string completely + * NOTE: the string created is NOT TERMINATED. + */ + +#if __STDC__ +int +ul_asc(u_long val, register char *str, register int len, register int base) +#else +int +ul_asc(val, str, len, base) + u_long val; + register char *str; + register int len; + register int base; +#endif +{ + register char *pt; + u_long digit; + + /* + * WARNING str is not '\0' terminated by this routine + */ + pt = str + len - 1; + + /* + * do a tailwise conversion (start at right most end of string to place + * least significant digit). Keep shifting until conversion value goes + * to zero (all digits were converted) + */ + if (base == HEX) { + while (pt >= str) { + if ((digit = (val & 0xf)) < 10) + *pt-- = '0' + (char)digit; + else + *pt-- = 'a' + (char)(digit - 10); + if ((val = (val >> 4)) == (u_long)0) + break; + } + } else { + while (pt >= str) { + *pt-- = '0' + (char)(val & 0x7); + if ((val = (val >> 3)) == (u_long)0) + break; + } + } + + /* + * pad with leading ascii ZEROS. We return -1 if we ran out of space. + */ + while (pt >= str) + *pt-- = '0'; + if (val != (u_long)0) + return(-1); + return(0); +} + +#ifndef NET2_STAT +/* + * asc_uqd() + * convert hex/octal character string into a u_quad_t. We do not have to + * check for overflow! (the headers in all supported formats are not large + * enough to create an overflow). + * NOTE: strings passed to us are NOT TERMINATED. + * Return: + * u_quad_t value + */ + +#if __STDC__ +u_quad_t +asc_uqd(register char *str, int len, register int base) +#else +u_quad_t +asc_uqd(str, len, base) + register char *str; + int len; + register int base; +#endif +{ + register char *stop; + u_quad_t tval = 0; + + stop = str + len; + + /* + * skip over leading blanks and zeros + */ + while ((str < stop) && ((*str == ' ') || (*str == '0'))) + ++str; + + /* + * for each valid digit, shift running value (tval) over to next digit + * and add next digit + */ + if (base == HEX) { + while (str < stop) { + if ((*str >= '0') && (*str <= '9')) + tval = (tval << 4) + (*str++ - '0'); + else if ((*str >= 'A') && (*str <= 'F')) + tval = (tval << 4) + 10 + (*str++ - 'A'); + else if ((*str >= 'a') && (*str <= 'f')) + tval = (tval << 4) + 10 + (*str++ - 'a'); + else + break; + } + } else { + while ((str < stop) && (*str >= '0') && (*str <= '7')) + tval = (tval << 3) + (*str++ - '0'); + } + return(tval); +} + +/* + * uqd_asc() + * convert an u_quad_t into a hex/oct ascii string. pads with LEADING + * ascii 0's to fill string completely + * NOTE: the string created is NOT TERMINATED. + */ + +#if __STDC__ +int +uqd_asc(u_quad_t val, register char *str, register int len, register int base) +#else +int +uqd_asc(val, str, len, base) + u_quad_t val; + register char *str; + register int len; + register int base; +#endif +{ + register char *pt; + u_quad_t digit; + + /* + * WARNING str is not '\0' terminated by this routine + */ + pt = str + len - 1; + + /* + * do a tailwise conversion (start at right most end of string to place + * least significant digit). Keep shifting until conversion value goes + * to zero (all digits were converted) + */ + if (base == HEX) { + while (pt >= str) { + if ((digit = (val & 0xf)) < 10) + *pt-- = '0' + (char)digit; + else + *pt-- = 'a' + (char)(digit - 10); + if ((val = (val >> 4)) == (u_quad_t)0) + break; + } + } else { + while (pt >= str) { + *pt-- = '0' + (char)(val & 0x7); + if ((val = (val >> 3)) == (u_quad_t)0) + break; + } + } + + /* + * pad with leading ascii ZEROS. We return -1 if we ran out of space. + */ + while (pt >= str) + *pt-- = '0'; + if (val != (u_quad_t)0) + return(-1); + return(0); +} +#endif diff --git a/bin/pax/options.c b/bin/pax/options.c new file mode 100644 index 0000000..a7233fb --- /dev/null +++ b/bin/pax/options.c @@ -0,0 +1,1140 @@ +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * 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[] = "@(#)options.c 8.2 (Berkeley) 4/18/94"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/mtio.h> +#include <sys/param.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <limits.h> +#include "pax.h" +#include "options.h" +#include "cpio.h" +#include "tar.h" +#include "extern.h" + +/* + * Routines which handle command line options + */ + +static char flgch[] = FLGCH; /* list of all possible flags */ +static OPLIST *ophead = NULL; /* head for format specific options -x */ +static OPLIST *optail = NULL; /* option tail */ + +static int no_op __P((void)); +static void printflg __P((unsigned int)); +static int c_frmt __P((const void *, const void *)); +static off_t str_offt __P((char *)); +static void pax_options __P((register int, register char **)); +static void pax_usage __P((void)); +static void tar_options __P((register int, register char **)); +static void tar_usage __P((void)); +#ifdef notdef +static void cpio_options __P((register int, register char **)); +static void cpio_usage __P((void)); +#endif + +/* + * Format specific routine table - MUST BE IN SORTED ORDER BY NAME + * (see pax.h for description of each function) + * + * name, blksz, hdsz, udev, hlk, blkagn, inhead, id, st_read, + * read, end_read, st_write, write, end_write, trail, + * rd_data, wr_data, options + */ + +FSUB fsub[] = { +/* 0: OLD BINARY CPIO */ + "bcpio", 5120, sizeof(HD_BCPIO), 1, 0, 0, 1, bcpio_id, cpio_strd, + bcpio_rd, bcpio_endrd, cpio_stwr, bcpio_wr, cpio_endwr, cpio_trail, + rd_wrfile, wr_rdfile, bad_opt, + +/* 1: OLD OCTAL CHARACTER CPIO */ + "cpio", 5120, sizeof(HD_CPIO), 1, 0, 0, 1, cpio_id, cpio_strd, + cpio_rd, cpio_endrd, cpio_stwr, cpio_wr, cpio_endwr, cpio_trail, + rd_wrfile, wr_rdfile, bad_opt, + +/* 2: SVR4 HEX CPIO */ + "sv4cpio", 5120, sizeof(HD_VCPIO), 1, 0, 0, 1, vcpio_id, cpio_strd, + vcpio_rd, vcpio_endrd, cpio_stwr, vcpio_wr, cpio_endwr, cpio_trail, + rd_wrfile, wr_rdfile, bad_opt, + +/* 3: SVR4 HEX CPIO WITH CRC */ + "sv4crc", 5120, sizeof(HD_VCPIO), 1, 0, 0, 1, crc_id, crc_strd, + vcpio_rd, vcpio_endrd, crc_stwr, vcpio_wr, cpio_endwr, cpio_trail, + rd_wrfile, wr_rdfile, bad_opt, + +/* 4: OLD TAR */ + "tar", 10240, BLKMULT, 0, 1, BLKMULT, 0, tar_id, no_op, + tar_rd, tar_endrd, no_op, tar_wr, tar_endwr, tar_trail, + rd_wrfile, wr_rdfile, tar_opt, + +/* 5: POSIX USTAR */ + "ustar", 10240, BLKMULT, 0, 1, BLKMULT, 0, ustar_id, ustar_strd, + ustar_rd, tar_endrd, ustar_stwr, ustar_wr, tar_endwr, tar_trail, + rd_wrfile, wr_rdfile, bad_opt, +}; +#define F_TAR 4 /* format when called as tar */ +#define DEFLT 5 /* default write format from list above */ + +/* + * ford is the archive search order used by get_arc() to determine what kind + * of archive we are dealing with. This helps to properly id archive formats + * some formats may be subsets of others.... + */ +int ford[] = {5, 4, 3, 2, 1, 0, -1 }; + +/* + * options() + * figure out if we are pax, tar or cpio. Call the appropriate options + * parser + */ + +#if __STDC__ +void +options(register int argc, register char **argv) +#else +void +options(argc, argv) + register int argc; + register char **argv; +#endif +{ + + /* + * Are we acting like pax, tar or cpio (based on argv[0]) + */ + if ((argv0 = strrchr(argv[0], '/')) != NULL) + argv0++; + else + argv0 = argv[0]; + + if (strcmp(NM_TAR, argv0) == 0) + return(tar_options(argc, argv)); +# ifdef notdef + else if (strcmp(NM_CPIO, argv0) == 0) + return(cpio_options(argc, argv)); +# endif + /* + * assume pax as the default + */ + argv0 = NM_PAX; + return(pax_options(argc, argv)); +} + +/* + * pax_options() + * look at the user specified flags. set globals as required and check if + * the user specified a legal set of flags. If not, complain and exit + */ + +#if __STDC__ +static void +pax_options(register int argc, register char **argv) +#else +static void +pax_options(argc, argv) + register int argc; + register char **argv; +#endif +{ + register int c; + register int i; + unsigned int flg = 0; + unsigned int bflg = 0; + register char *pt; + FSUB tmp; + extern char *optarg; + extern int optind; + + /* + * process option flags + */ + while ((c=getopt(argc,argv,"ab:cdf:iklno:p:rs:tuvwx:B:DE:G:HLPT:U:XYZ")) + != EOF) { + switch (c) { + case 'a': + /* + * append + */ + flg |= AF; + break; + case 'b': + /* + * specify blocksize + */ + flg |= BF; + if ((wrblksz = (int)str_offt(optarg)) <= 0) { + warn(1, "Invalid block size %s", optarg); + pax_usage(); + } + break; + case 'c': + /* + * inverse match on patterns + */ + cflag = 1; + flg |= CF; + break; + case 'd': + /* + * match only dir on extract, not the subtree at dir + */ + dflag = 1; + flg |= DF; + break; + case 'f': + /* + * filename where the archive is stored + */ + arcname = optarg; + flg |= FF; + break; + case 'i': + /* + * interactive file rename + */ + iflag = 1; + flg |= IF; + break; + case 'k': + /* + * do not clobber files that exist + */ + kflag = 1; + flg |= KF; + break; + case 'l': + /* + * try to link src to dest with copy (-rw) + */ + lflag = 1; + flg |= LF; + break; + case 'n': + /* + * select first match for a pattern only + */ + nflag = 1; + flg |= NF; + break; + case 'o': + /* + * pass format specific options + */ + flg |= OF; + if (opt_add(optarg) < 0) + pax_usage(); + break; + case 'p': + /* + * specify file characteristic options + */ + for (pt = optarg; *pt != '\0'; ++pt) { + switch(*pt) { + case 'a': + /* + * do not preserve access time + */ + patime = 0; + break; + case 'e': + /* + * preserve user id, group id, file + * mode, access/modification times + */ + pids = 1; + pmode = 1; + patime = 1; + pmtime = 1; + break; + case 'm': + /* + * do not preserve modification time + */ + pmtime = 0; + break; + case 'o': + /* + * preserve uid/gid + */ + pids = 1; + break; + case 'p': + /* + * preserver file mode bits + */ + pmode = 1; + break; + default: + warn(1, "Invalid -p string: %c", *pt); + pax_usage(); + break; + } + } + flg |= PF; + break; + case 'r': + /* + * read the archive + */ + flg |= RF; + break; + case 's': + /* + * file name substitution name pattern + */ + if (rep_add(optarg) < 0) { + pax_usage(); + break; + } + flg |= SF; + break; + case 't': + /* + * preserve access time on filesystem nodes we read + */ + tflag = 1; + flg |= TF; + break; + case 'u': + /* + * ignore those older files + */ + uflag = 1; + flg |= UF; + break; + case 'v': + /* + * verbose operation mode + */ + vflag = 1; + flg |= VF; + break; + case 'w': + /* + * write an archive + */ + flg |= WF; + break; + case 'x': + /* + * specify an archive format on write + */ + tmp.name = optarg; + if (frmt = (FSUB *)bsearch((void *)&tmp, (void *)fsub, + sizeof(fsub)/sizeof(FSUB), sizeof(FSUB), c_frmt)) { + flg |= XF; + break; + } + warn(1, "Unknown -x format: %s", optarg); + (void)fputs("pax: Known -x formats are:", stderr); + for (i = 0; i < (sizeof(fsub)/sizeof(FSUB)); ++i) + (void)fprintf(stderr, " %s", fsub[i].name); + (void)fputs("\n\n", stderr); + pax_usage(); + break; + case 'B': + /* + * non-standard option on number of bytes written on a + * single archive volume. + */ + if ((wrlimit = str_offt(optarg)) <= 0) { + warn(1, "Invalid write limit %s", optarg); + pax_usage(); + } + if (wrlimit % BLKMULT) { + warn(1, "Write limit is not a %d byte multiple", + BLKMULT); + pax_usage(); + } + flg |= CBF; + break; + case 'D': + /* + * On extraction check file inode change time before the + * modification of the file name. Non standard option. + */ + Dflag = 1; + flg |= CDF; + break; + case 'E': + /* + * non-standard limit on read faults + * 0 indicates stop after first error, values + * indicate a limit, "NONE" try forever + */ + flg |= CEF; + if (strcmp(NONE, optarg) == 0) + maxflt = -1; + else if ((maxflt = atoi(optarg)) < 0) { + warn(1, "Error count value must be positive"); + pax_usage(); + } + break; + case 'G': + /* + * non-standard option for selecting files within an + * archive by group (gid or name) + */ + if (grp_add(optarg) < 0) { + pax_usage(); + break; + } + flg |= CGF; + break; + case 'H': + /* + * follow command line symlinks only + */ + Hflag = 1; + flg |= CHF; + break; + case 'L': + /* + * follow symlinks + */ + Lflag = 1; + flg |= CLF; + break; + case 'P': + /* + * do NOT follow symlinks (default) + */ + Lflag = 0; + flg |= CPF; + break; + case 'T': + /* + * non-standard option for selecting files within an + * archive by modification time range (lower,upper) + */ + if (trng_add(optarg) < 0) { + pax_usage(); + break; + } + flg |= CTF; + break; + case 'U': + /* + * non-standard option for selecting files within an + * archive by user (uid or name) + */ + if (usr_add(optarg) < 0) { + pax_usage(); + break; + } + flg |= CUF; + break; + case 'X': + /* + * do not pass over mount points in the file system + */ + Xflag = 1; + flg |= CXF; + break; + case 'Y': + /* + * On extraction check file inode change time after the + * modification of the file name. Non standard option. + */ + Yflag = 1; + flg |= CYF; + break; + case 'Z': + /* + * On extraction check modification time after the + * modification of the file name. Non standard option. + */ + Zflag = 1; + flg |= CZF; + break; + case '?': + default: + pax_usage(); + break; + } + } + + /* + * figure out the operation mode of pax read,write,extract,copy,append + * or list. check that we have not been given a bogus set of flags + * for the operation mode. + */ + if (ISLIST(flg)) { + act = LIST; + bflg = flg & BDLIST; + } else if (ISEXTRACT(flg)) { + act = EXTRACT; + bflg = flg & BDEXTR; + } else if (ISARCHIVE(flg)) { + act = ARCHIVE; + bflg = flg & BDARCH; + } else if (ISAPPND(flg)) { + act = APPND; + bflg = flg & BDARCH; + } else if (ISCOPY(flg)) { + act = COPY; + bflg = flg & BDCOPY; + } else + pax_usage(); + if (bflg) { + printflg(flg); + pax_usage(); + } + + /* + * if we are writing (ARCHIVE) we use the default format if the user + * did not specify a format. when we write during an APPEND, we will + * adopt the format of the existing archive if none was supplied. + */ + if (!(flg & XF) && (act == ARCHIVE)) + frmt = &(fsub[DEFLT]); + + /* + * process the args as they are interpreted by the operation mode + */ + switch (act) { + case LIST: + case EXTRACT: + for (; optind < argc; optind++) + if (pat_add(argv[optind]) < 0) + pax_usage(); + break; + case COPY: + if (optind >= argc) { + warn(0, "Destination directory was not supplied"); + pax_usage(); + } + --argc; + dirptr = argv[argc]; + /* FALL THROUGH */ + case ARCHIVE: + case APPND: + for (; optind < argc; optind++) + if (ftree_add(argv[optind]) < 0) + pax_usage(); + /* + * no read errors allowed on updates/append operation! + */ + maxflt = 0; + break; + } +} + + +/* + * tar_options() + * look at the user specified flags. set globals as required and check if + * the user specified a legal set of flags. If not, complain and exit + */ + +#if __STDC__ +static void +tar_options(register int argc, register char **argv) +#else +static void +tar_options(argc, argv) + register int argc; + register char **argv; +#endif +{ + register char *cp; + int fstdin = 0; + + if (argc < 2) + tar_usage(); + /* + * process option flags + */ + ++argv; + for (cp = *argv++; *cp != '\0'; ++cp) { + switch (*cp) { + case '-': + /* + * skip over - + */ + break; + case 'b': + /* + * specify blocksize + */ + if (*argv == (char *)NULL) { + warn(1,"blocksize must be specified with 'b'"); + tar_usage(); + } + if ((wrblksz = (int)str_offt(*argv)) <= 0) { + warn(1, "Invalid block size %s", *argv); + tar_usage(); + } + ++argv; + break; + case 'c': + /* + * create an archive + */ + act = ARCHIVE; + break; + case 'e': + /* + * stop after first error + */ + maxflt = 0; + break; + case 'f': + /* + * filename where the archive is stored + */ + if (*argv == (char *)NULL) { + warn(1, "filename must be specified with 'f'"); + tar_usage(); + } + if ((argv[0][0] == '-') && (argv[0][1]== '\0')) { + /* + * treat a - as stdin + */ + ++argv; + ++fstdin; + arcname = (char *)0; + break; + } + fstdin = 0; + arcname = *argv++; + break; + case 'm': + /* + * do not preserve modification time + */ + pmtime = 0; + break; + case 'o': + if (opt_add("write_opt=nodir") < 0) + tar_usage(); + break; + case 'p': + /* + * preserve user id, group id, file + * mode, access/modification times + */ + pids = 1; + pmode = 1; + patime = 1; + pmtime = 1; + break; + case 'r': + case 'u': + /* + * append to the archive + */ + act = APPND; + break; + case 't': + /* + * list contents of the tape + */ + act = LIST; + break; + case 'v': + /* + * verbose operation mode + */ + vflag = 1; + break; + case 'w': + /* + * interactive file rename + */ + iflag = 1; + break; + case 'x': + /* + * write an archive + */ + act = EXTRACT; + break; + case 'B': + /* + * Nothing to do here, this is pax default + */ + break; + case 'H': + /* + * follow command line symlinks only + */ + Hflag = 1; + break; + case 'L': + /* + * follow symlinks + */ + Lflag = 1; + break; + case 'P': + /* + * do not follow symlinks + */ + Lflag = 0; + break; + case 'X': + /* + * do not pass over mount points in the file system + */ + Xflag = 1; + break; + case '0': + arcname = DEV_0; + break; + case '1': + arcname = DEV_1; + break; + case '4': + arcname = DEV_4; + break; + case '5': + arcname = DEV_5; + break; + case '7': + arcname = DEV_7; + break; + case '8': + arcname = DEV_8; + break; + default: + tar_usage(); + break; + } + } + + /* + * if we are writing (ARCHIVE) specify tar, otherwise run like pax + */ + if (act == ARCHIVE) + frmt = &(fsub[F_TAR]); + + /* + * process the args as they are interpreted by the operation mode + */ + switch (act) { + case LIST: + case EXTRACT: + default: + while (*argv != (char *)NULL) + if (pat_add(*argv++) < 0) + tar_usage(); + break; + case ARCHIVE: + case APPND: + while (*argv != (char *)NULL) + if (ftree_add(*argv++) < 0) + tar_usage(); + /* + * no read errors allowed on updates/append operation! + */ + maxflt = 0; + break; + } + if (!fstdin && ((arcname == (char *)NULL) || (*arcname == '\0'))) { + arcname = getenv("TAPE"); + if ((arcname == (char *)NULL) || (*arcname == '\0')) + arcname = DEV_8; + } +} + +#ifdef notdef +/* + * cpio_options() + * look at the user specified flags. set globals as required and check if + * the user specified a legal set of flags. If not, complain and exit + */ + +#if __STDC__ +static void +cpio_options(register int argc, register char **argv) +#else +static void +cpio_options(argc, argv) + register int argc; + register char **argv; +#endif +{ +} +#endif + +/* + * printflg() + * print out those invalid flag sets found to the user + */ + +#if __STDC__ +static void +printflg(unsigned int flg) +#else +static void +printflg(flg) + unsigned int flg; +#endif +{ + int nxt; + int pos = 0; + + (void)fprintf(stderr,"%s: Invalid combination of options:", argv0); + while (nxt = ffs(flg)) { + flg = flg >> nxt; + pos += nxt; + (void)fprintf(stderr, " -%c", flgch[pos-1]); + } + (void)putc('\n', stderr); +} + +/* + * c_frmt() + * comparison routine used by bsearch to find the format specified + * by the user + */ + +#if __STDC__ +static int +c_frmt(const void *a, const void *b) +#else +static int +c_frmt(a, b) + void *a; + void *b; +#endif +{ + return(strcmp(((FSUB *)a)->name, ((FSUB *)b)->name)); +} + +/* + * opt_next() + * called by format specific options routines to get each format specific + * flag and value specified with -o + * Return: + * pointer to next OPLIST entry or NULL (end of list). + */ + +#if __STDC__ +OPLIST * +opt_next(void) +#else +OPLIST * +opt_next() +#endif +{ + OPLIST *opt; + + if ((opt = ophead) != NULL) + ophead = ophead->fow; + return(opt); +} + +/* + * bad_opt() + * generic routine used to complain about a format specific options + * when the format does not support options. + */ + +#if __STDC__ +int +bad_opt(void) +#else +int +bad_opt() +#endif +{ + register OPLIST *opt; + + if (ophead == NULL) + return(0); + /* + * print all we were given + */ + warn(1,"These format options are not supported"); + while ((opt = opt_next()) != NULL) + (void)fprintf(stderr, "\t%s = %s\n", opt->name, opt->value); + pax_usage(); + return(0); +} + +/* + * opt_add() + * breaks the value supplied to -o into a option name and value. options + * are given to -o in the form -o name-value,name=value + * mulltiple -o may be specified. + * Return: + * 0 if format in name=value format, -1 if -o is passed junk + */ + +#if __STDC__ +int +opt_add(register char *str) +#else +int +opt_add(str) + register char *str; +#endif +{ + register OPLIST *opt; + register char *frpt; + register char *pt; + register char *endpt; + + if ((str == NULL) || (*str == '\0')) { + warn(0, "Invalid option name"); + return(-1); + } + frpt = endpt = str; + + /* + * break into name and values pieces and stuff each one into a + * OPLIST structure. When we know the format, the format specific + * option function will go through this list + */ + while ((frpt != NULL) && (*frpt != '\0')) { + if ((endpt = strchr(frpt, ',')) != NULL) + *endpt = '\0'; + if ((pt = strchr(frpt, '=')) == NULL) { + warn(0, "Invalid options format"); + return(-1); + } + if ((opt = (OPLIST *)malloc(sizeof(OPLIST))) == NULL) { + warn(0, "Unable to allocate space for option list"); + return(-1); + } + *pt++ = '\0'; + opt->name = frpt; + opt->value = pt; + opt->fow = NULL; + if (endpt != NULL) + frpt = endpt + 1; + else + frpt = NULL; + if (ophead == NULL) { + optail = ophead = opt; + continue; + } + optail->fow = opt; + optail = opt; + } + return(0); +} + +/* + * str_offt() + * Convert an expression of the following forms to an off_t > 0. + * 1) A positive decimal number. + * 2) A positive decimal number followed by a b (mult by 512). + * 3) A positive decimal number followed by a k (mult by 1024). + * 4) A positive decimal number followed by a m (mult by 512). + * 5) A positive decimal number followed by a w (mult by sizeof int) + * 6) Two or more positive decimal numbers (with/without k,b or w). + * seperated by x (also * for backwards compatibility), specifying + * the product of the indicated values. + * Return: + * 0 for an error, a positive value o.w. + */ + +#if __STDC__ +static off_t +str_offt(char *val) +#else +static off_t +str_offt(val) + char *val; +#endif +{ + char *expr; + off_t num, t; + +# ifdef NET2_STAT + num = strtol(val, &expr, 0); + if ((num == LONG_MAX) || (num <= 0) || (expr == val)) +# else + num = strtoq(val, &expr, 0); + if ((num == QUAD_MAX) || (num <= 0) || (expr == val)) +# endif + return(0); + + switch(*expr) { + case 'b': + t = num; + num *= 512; + if (t > num) + return(0); + ++expr; + break; + case 'k': + t = num; + num *= 1024; + if (t > num) + return(0); + ++expr; + break; + case 'm': + t = num; + num *= 1048576; + if (t > num) + return(0); + ++expr; + break; + case 'w': + t = num; + num *= sizeof(int); + if (t > num) + return(0); + ++expr; + break; + } + + switch(*expr) { + case '\0': + break; + case '*': + case 'x': + t = num; + num *= str_offt(expr + 1); + if (t > num) + return(0); + break; + default: + return(0); + } + return(num); +} + +/* + * no_op() + * for those option functions where the archive format has nothing to do. + * Return: + * 0 + */ + +#if __STDC__ +static int +no_op(void) +#else +static int +no_op() +#endif +{ + return(0); +} + +/* + * pax_usage() + * print the usage summary to the user + */ + +#if __STDC__ +void +pax_usage(void) +#else +void +pax_usage() +#endif +{ + (void)fputs("usage: pax [-cdnv] [-E limit] [-f archive] ", stderr); + (void)fputs("[-s replstr] ... [-U user] ...", stderr); + (void)fputs("\n [-G group] ... ", stderr); + (void)fputs("[-T [from_date][,to_date]] ... ", stderr); + (void)fputs("[pattern ...]\n", stderr); + (void)fputs(" pax -r [-cdiknuvDYZ] [-E limit] ", stderr); + (void)fputs("[-f archive] [-o options] ... \n", stderr); + (void)fputs(" [-p string] ... [-s replstr] ... ", stderr); + (void)fputs("[-U user] ... [-G group] ...\n ", stderr); + (void)fputs("[-T [from_date][,to_date]] ... ", stderr); + (void)fputs(" [pattern ...]\n", stderr); + (void)fputs(" pax -w [-dituvHLPX] [-b blocksize] ", stderr); + (void)fputs("[ [-a] [-f archive] ] [-x format] \n", stderr); + (void)fputs(" [-B bytes] [-s replstr] ... ", stderr); + (void)fputs("[-o options] ... [-U user] ...", stderr); + (void)fputs("\n [-G group] ... ", stderr); + (void)fputs("[-T [from_date][,to_date][/[c][m]]] ... ", stderr); + (void)fputs("[file ...]\n", stderr); + (void)fputs(" pax -r -w [-diklntuvDHLPXYZ] ", stderr); + (void)fputs("[-p string] ... [-s replstr] ...", stderr); + (void)fputs("\n [-U user] ... [-G group] ... ", stderr); + (void)fputs("[-T [from_date][,to_date][/[c][m]]] ... ", stderr); + (void)fputs("\n [file ...] directory\n", stderr); + exit(1); +} + +/* + * tar_usage() + * print the usage summary to the user + */ + +#if __STDC__ +void +tar_usage(void) +#else +void +tar_usage() +#endif +{ + (void)fputs("usage: tar -{txru}[cevfbmopwBHLPX014578] [tapefile] ", + stderr); + (void)fputs("[blocksize] file1 file2...\n", stderr); + exit(1); +} + +#ifdef notdef +/* + * cpio_usage() + * print the usage summary to the user + */ + +#if __STDC__ +void +cpio_usage(void) +#else +void +cpio_usage() +#endif +{ + exit(1); +} +#endif diff --git a/bin/pax/options.h b/bin/pax/options.h new file mode 100644 index 0000000..5dc855b --- /dev/null +++ b/bin/pax/options.h @@ -0,0 +1,113 @@ +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * 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. + * + * @(#)options.h 8.2 (Berkeley) 4/18/94 + */ + +/* + * argv[0] names. Used for tar and cpio emulation + */ + +#define NM_TAR "tar" +#define NM_CPIO "cpio" +#define NM_PAX "pax" + +/* + * Constants used to specify the legal sets of flags in pax. For each major + * operation mode of pax, a set of illegal flags is defined. If any one of + * those illegal flags are found set, we scream and exit + */ +#define NONE "none" + +/* + * flags (one for each option). + */ +#define AF 0x00000001 +#define BF 0x00000002 +#define CF 0x00000004 +#define DF 0x00000008 +#define FF 0x00000010 +#define IF 0x00000020 +#define KF 0x00000040 +#define LF 0x00000080 +#define NF 0x00000100 +#define OF 0x00000200 +#define PF 0x00000400 +#define RF 0x00000800 +#define SF 0x00001000 +#define TF 0x00002000 +#define UF 0x00004000 +#define VF 0x00008000 +#define WF 0x00010000 +#define XF 0x00020000 +#define CBF 0x00040000 /* nonstandard extension */ +#define CDF 0x00080000 /* nonstandard extension */ +#define CEF 0x00100000 /* nonstandard extension */ +#define CGF 0x00200000 /* nonstandard extension */ +#define CHF 0x00400000 /* nonstandard extension */ +#define CLF 0x00800000 /* nonstandard extension */ +#define CPF 0x01000000 /* nonstandard extension */ +#define CTF 0x02000000 /* nonstandard extension */ +#define CUF 0x04000000 /* nonstandard extension */ +#define CXF 0x08000000 +#define CYF 0x10000000 /* nonstandard extension */ +#define CZF 0x20000000 /* nonstandard extension */ + +/* + * ascii string indexed by bit position above (alter the above and you must + * alter this string) used to tell the user what flags caused us to complain + */ +#define FLGCH "abcdfiklnoprstuvwxBDEGHLPTUXYZ" + +/* + * legal pax operation bit patterns + */ + +#define ISLIST(x) (((x) & (RF|WF)) == 0) +#define ISEXTRACT(x) (((x) & (RF|WF)) == RF) +#define ISARCHIVE(x) (((x) & (AF|RF|WF)) == WF) +#define ISAPPND(x) (((x) & (AF|RF|WF)) == (AF|WF)) +#define ISCOPY(x) (((x) & (RF|WF)) == (RF|WF)) +#define ISWRITE(x) (((x) & (RF|WF)) == WF) + +/* + * Illegal option flag subsets based on pax operation + */ + +#define BDEXTR (AF|BF|LF|TF|WF|XF|CBF|CHF|CLF|CPF|CXF) +#define BDARCH (CF|KF|LF|NF|PF|RF|CDF|CEF|CYF|CZF) +#define BDCOPY (AF|BF|FF|OF|XF|CBF|CEF) +#define BDLIST (AF|BF|IF|KF|LF|OF|PF|RF|TF|UF|WF|XF|CBF|CDF|CHF|CLF|CPF|CXF|CYF|CZF) diff --git a/bin/pax/pat_rep.c b/bin/pax/pat_rep.c new file mode 100644 index 0000000..2da5827 --- /dev/null +++ b/bin/pax/pat_rep.c @@ -0,0 +1,1196 @@ +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * 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[] = "@(#)pat_rep.c 8.2 (Berkeley) 4/18/94"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#ifdef NET2_REGEX +#include <regexp.h> +#else +#include <regex.h> +#endif +#include "pax.h" +#include "pat_rep.h" +#include "extern.h" + +/* + * routines to handle pattern matching, name modification (regular expression + * substitution and interactive renames), and destination name modification for + * copy (-rw). Both file name and link names are adjusted as required in these + * routines. + */ + +#define MAXSUBEXP 10 /* max subexpressions, DO NOT CHANGE */ +static PATTERN *pathead = NULL; /* file pattern match list head */ +static PATTERN *pattail = NULL; /* file pattern match list tail */ +static REPLACE *rephead = NULL; /* replacement string list head */ +static REPLACE *reptail = NULL; /* replacement string list tail */ + +static int rep_name __P((char *, int *, int)); +static int tty_rename __P((register ARCHD *)); +static int fix_path __P((char *, int *, char *, int)); +static int fn_match __P((register char *, register char *, char **)); +static char * range_match __P((register char *, register int)); +#ifdef NET2_REGEX +static int resub __P((regexp *, char *, char *, register char *)); +#else +static int resub __P((regex_t *, regmatch_t *, char *, char *, char *)); +#endif + +/* + * rep_add() + * parses the -s replacement string; compiles the regular expression + * and stores the compiled value and it's replacement string together in + * replacement string list. Input to this function is of the form: + * /old/new/pg + * The first char in the string specifies the delimiter used by this + * replacement string. "Old" is a regular expression in "ed" format which + * is compiled by regcomp() and is applied to filenames. "new" is the + * substitution string; p and g are options flags for printing and global + * replacement (over the single filename) + * Return: + * 0 if a proper replacement string and regular expression was added to + * the list of replacement patterns; -1 otherwise. + */ + +#if __STDC__ +int +rep_add(register char *str) +#else +int +rep_add(str) + register char *str; +#endif +{ + register char *pt1; + register char *pt2; + register REPLACE *rep; +# ifndef NET2_REGEX + register int res; + char rebuf[BUFSIZ]; +# endif + + /* + * throw out the bad parameters + */ + if ((str == NULL) || (*str == '\0')) { + warn(1, "Empty replacement string"); + return(-1); + } + + /* + * first character in the string specifies what the delimiter is for + * this expression + */ + if ((pt1 = strchr(str+1, *str)) == NULL) { + warn(1, "Invalid replacement string %s", str); + return(-1); + } + + /* + * allocate space for the node that handles this replacement pattern + * and split out the regular expression and try to compile it + */ + if ((rep = (REPLACE *)malloc(sizeof(REPLACE))) == NULL) { + warn(1, "Unable to allocate memory for replacement string"); + return(-1); + } + + *pt1 = '\0'; +# ifdef NET2_REGEX + if ((rep->rcmp = regcomp(str+1)) == NULL) { +# else + if ((res = regcomp(&(rep->rcmp), str+1, 0)) != 0) { + regerror(res, &(rep->rcmp), rebuf, sizeof(rebuf)); + warn(1, "%s while compiling regular expression %s", rebuf, str); +# endif + (void)free((char *)rep); + return(-1); + } + + /* + * put the delimiter back in case we need an error message and + * locate the delimiter at the end of the replacement string + * we then point the node at the new substitution string + */ + *pt1++ = *str; + if ((pt2 = strchr(pt1, *str)) == NULL) { +# ifdef NET2_REGEX + (void)free((char *)rep->rcmp); +# else + regfree(&(rep->rcmp)); +# endif + (void)free((char *)rep); + warn(1, "Invalid replacement string %s", str); + return(-1); + } + + *pt2 = '\0'; + rep->nstr = pt1; + pt1 = pt2++; + rep->flgs = 0; + + /* + * set the options if any + */ + while (*pt2 != '\0') { + switch(*pt2) { + case 'g': + case 'G': + rep->flgs |= GLOB; + break; + case 'p': + case 'P': + rep->flgs |= PRNT; + break; + default: +# ifdef NET2_REGEX + (void)free((char *)rep->rcmp); +# else + regfree(&(rep->rcmp)); +# endif + (void)free((char *)rep); + *pt1 = *str; + warn(1, "Invalid replacement string option %s", str); + return(-1); + } + ++pt2; + } + + /* + * all done, link it in at the end + */ + rep->fow = NULL; + if (rephead == NULL) { + reptail = rephead = rep; + return(0); + } + reptail->fow = rep; + reptail = rep; + return(0); +} + +/* + * pat_add() + * add a pattern match to the pattern match list. Pattern matches are used + * to select which archive members are extracted. (They appear as + * arguments to pax in the list and read modes). If no patterns are + * supplied to pax, all members in the archive will be selected (and the + * pattern match list is empty). + * Return: + * 0 if the pattern was added to the list, -1 otherwise + */ + +#if __STDC__ +int +pat_add(char *str) +#else +int +pat_add(str) + char *str; +#endif +{ + register PATTERN *pt; + + /* + * throw out the junk + */ + if ((str == NULL) || (*str == '\0')) { + warn(1, "Empty pattern string"); + return(-1); + } + + /* + * allocate space for the pattern and store the pattern. the pattern is + * part of argv so do not bother to copy it, just point at it. Add the + * node to the end of the pattern list + */ + if ((pt = (PATTERN *)malloc(sizeof(PATTERN))) == NULL) { + warn(1, "Unable to allocate memory for pattern string"); + return(-1); + } + + pt->pstr = str; + pt->pend = NULL; + pt->plen = strlen(str); + pt->fow = NULL; + pt->flgs = 0; + if (pathead == NULL) { + pattail = pathead = pt; + return(0); + } + pattail->fow = pt; + pattail = pt; + return(0); +} + +/* + * pat_chk() + * complain if any the user supplied pattern did not result in a match to + * a selected archive member. + */ + +#if __STDC__ +void +pat_chk(void) +#else +void +pat_chk() +#endif +{ + register PATTERN *pt; + register int wban = 0; + + /* + * walk down the list checking the flags to make sure MTCH was set, + * if not complain + */ + for (pt = pathead; pt != NULL; pt = pt->fow) { + if (pt->flgs & MTCH) + continue; + if (!wban) { + warn(1, "WARNING! These patterns were not matched:"); + ++wban; + } + (void)fprintf(stderr, "%s\n", pt->pstr); + } +} + +/* + * pat_sel() + * the archive member which matches a pattern was selected. Mark the + * pattern as having selected an archive member. arcn->pat points at the + * pattern that was matched. arcn->pat is set in pat_match() + * + * NOTE: When the -c option is used, we are called when there was no match + * by pat_match() (that means we did match before the inverted sense of + * the logic). Now this seems really strange at first, but with -c we + * need to keep track of those patterns that cause a archive member to NOT + * be selected (it found an archive member with a specified pattern) + * Return: + * 0 if the pattern pointed at by arcn->pat was tagged as creating a + * match, -1 otherwise. + */ + +#if __STDC__ +int +pat_sel(register ARCHD *arcn) +#else +int +pat_sel(arcn) + register ARCHD *arcn; +#endif +{ + register PATTERN *pt; + register PATTERN **ppt; + register int len; + + /* + * if no patterns just return + */ + if ((pathead == NULL) || ((pt = arcn->pat) == NULL)) + return(0); + + /* + * when we are NOT limited to a single match per pattern mark the + * pattern and return + */ + if (!nflag) { + pt->flgs |= MTCH; + return(0); + } + + /* + * we reach this point only when we allow a single selected match per + * pattern, if the pattern matches a directory and we do not have -d + * (dflag) we are done with this pattern. We may also be handed a file + * in the subtree of a directory. in that case when we are operating + * with -d, this pattern was already selected and we are done + */ + if (pt->flgs & DIR_MTCH) + return(0); + + if (!dflag && ((pt->pend != NULL) || (arcn->type == PAX_DIR))) { + /* + * ok we matched a directory and we are allowing + * subtree matches but because of the -n only its children will + * match. This is tagged as a DIR_MTCH type. + * WATCH IT, the code assumes that pt->pend points + * into arcn->name and arcn->name has not been modified. + * If not we will have a big mess. Yup this is another kludge + */ + + /* + * if this was a prefix match, remove trailing part of path + * so we can copy it. Future matches will be exact prefix match + */ + if (pt->pend != NULL) + *pt->pend = '\0'; + + if ((pt->pstr = strdup(arcn->name)) == NULL) { + warn(1, "Pattern select out of memory"); + if (pt->pend != NULL) + *pt->pend = '/'; + pt->pend = NULL; + return(-1); + } + + /* + * put the trailing / back in the source string + */ + if (pt->pend != NULL) { + *pt->pend = '/'; + pt->pend = NULL; + } + pt->plen = strlen(pt->pstr); + + /* + * strip off any trailing /, this should really never happen + */ + len = pt->plen - 1; + if (*(pt->pstr + len) == '/') { + *(pt->pstr + len) = '\0'; + pt->plen = len; + } + pt->flgs = DIR_MTCH | MTCH; + arcn->pat = pt; + return(0); + } + + /* + * we are then done with this pattern, so we delete it from the list + * because it can never be used for another match. + * Seems kind of strange to do for a -c, but the pax spec is really + * vague on the interaction of -c -n and -d. We assume that when -c + * and the pattern rejects a member (i.e. it matched it) it is done. + * In effect we place the order of the flags as having -c last. + */ + pt = pathead; + ppt = &pathead; + while ((pt != NULL) && (pt != arcn->pat)) { + ppt = &(pt->fow); + pt = pt->fow; + } + + if (pt == NULL) { + /* + * should never happen.... + */ + warn(1, "Pattern list inconsistant"); + return(-1); + } + *ppt = pt->fow; + (void)free((char *)pt); + arcn->pat = NULL; + return(0); +} + +/* + * pat_match() + * see if this archive member matches any supplied pattern, if a match + * is found, arcn->pat is set to point at the potential pattern. Later if + * this archive member is "selected" we process and mark the pattern as + * one which matched a selected archive member (see pat_sel()) + * Return: + * 0 if this archive member should be processed, 1 if it should be + * skipped and -1 if we are done with all patterns (and pax should quit + * looking for more members) + */ + +#if __STDC__ +int +pat_match(register ARCHD *arcn) +#else +int +pat_match(arcn) + register ARCHD *arcn; +#endif +{ + register PATTERN *pt; + + arcn->pat = NULL; + + /* + * if there are no more patterns and we have -n (and not -c) we are + * done. otherwise with no patterns to match, matches all + */ + if (pathead == NULL) { + if (nflag && !cflag) + return(-1); + return(0); + } + + /* + * have to search down the list one at a time looking for a match. + */ + pt = pathead; + while (pt != NULL) { + /* + * check for a file name match unless we have DIR_MTCH set in + * this pattern then we want a prefix match + */ + if (pt->flgs & DIR_MTCH) { + /* + * this pattern was matched before to a directory + * as we must have -n set for this (but not -d). We can + * only match CHILDREN of that directory so we must use + * an exact prefix match (no wildcards). + */ + if ((arcn->name[pt->plen] == '/') && + (strncmp(pt->pstr, arcn->name, pt->plen) == 0)) + break; + } else if (fn_match(pt->pstr, arcn->name, &pt->pend) == 0) + break; + pt = pt->fow; + } + + /* + * return the result, remember that cflag (-c) inverts the sense of a + * match + */ + if (pt == NULL) + return(cflag ? 0 : 1); + + /* + * we had a match, now when we invert the sense (-c) we reject this + * member. However we have to tag the pattern a being successful, (in a + * match, not in selecting a archive member) so we call pat_sel() here. + */ + arcn->pat = pt; + if (!cflag) + return(0); + + if (pat_sel(arcn) < 0) + return(-1); + arcn->pat = NULL; + return(1); +} + +/* + * fn_match() + * Return: + * 0 if this archive member should be processed, 1 if it should be + * skipped and -1 if we are done with all patterns (and pax should quit + * looking for more members) + * Note: *pend may be changed to show where the prefix ends. + */ + +#if __STDC__ +static int +fn_match(register char *pattern, register char *string, char **pend) +#else +static int +fn_match(pattern, string, pend) + register char *pattern; + register char *string; + char **pend; +#endif +{ + register char c; + char test; + + *pend = NULL; + for (;;) { + switch (c = *pattern++) { + case '\0': + /* + * Ok we found an exact match + */ + if (*string == '\0') + return(0); + + /* + * Check if it is a prefix match + */ + if ((dflag == 1) || (*string != '/')) + return(-1); + + /* + * It is a prefix match, remember where the trailing + * / is located + */ + *pend = string; + return(0); + case '?': + if ((test = *string++) == '\0') + return (-1); + break; + case '*': + c = *pattern; + /* + * Collapse multiple *'s. + */ + while (c == '*') + c = *++pattern; + + /* + * Optimized hack for pattern with a * at the end + */ + if (c == '\0') + return (0); + + /* + * General case, use recursion. + */ + while ((test = *string) != '\0') { + if (!fn_match(pattern, string, pend)) + return (0); + ++string; + } + return (-1); + case '[': + /* + * range match + */ + if (((test = *string++) == '\0') || + ((pattern = range_match(pattern, test)) == NULL)) + return (-1); + break; + case '\\': + default: + if (c != *string++) + return (-1); + break; + } + } + /* NOTREACHED */ +} + +#ifdef __STDC__ +static char * +range_match(register char *pattern, register int test) +#else +static char * +range_match(pattern, test) + register char *pattern; + register int test; +#endif +{ + register char c; + register char c2; + int negate; + int ok = 0; + + if (negate = (*pattern == '!')) + ++pattern; + + while ((c = *pattern++) != ']') { + /* + * Illegal pattern + */ + if (c == '\0') + return (NULL); + + if ((*pattern == '-') && ((c2 = pattern[1]) != '\0') && + (c2 != ']')) { + if ((c <= test) && (test <= c2)) + ok = 1; + pattern += 2; + } else if (c == test) + ok = 1; + } + return (ok == negate ? NULL : pattern); +} + +/* + * mod_name() + * modify a selected file name. first attempt to apply replacement string + * expressions, then apply interactive file rename. We apply replacement + * string expressions to both filenames and file links (if we didn't the + * links would point to the wrong place, and we could never be able to + * move an archive that has a file link in it). When we rename files + * interactively, we store that mapping (old name to user input name) so + * if we spot any file links to the old file name in the future, we will + * know exactly how to fix the file link. + * Return: + * 0 continue to process file, 1 skip this file, -1 pax is finished + */ + +#if __STDC__ +int +mod_name(register ARCHD *arcn) +#else +int +mod_name(arcn) + register ARCHD *arcn; +#endif +{ + register int res = 0; + + /* + * IMPORTANT: We have a problem. what do we do with symlinks? + * Modifying a hard link name makes sense, as we know the file it + * points at should have been seen already in the archive (and if it + * wasn't seen because of a read error or a bad archive, we lose + * anyway). But there are no such requirements for symlinks. On one + * hand the symlink that refers to a file in the archive will have to + * be modified to so it will still work at its new location in the + * file system. On the other hand a symlink that points elsewhere (and + * should continue to do so) should not be modified. There is clearly + * no perfect solution here. So we handle them like hardlinks. Clearly + * a replacement made by the interactive rename mapping is very likely + * to be correct since it applies to a single file and is an exact + * match. The regular expression replacements are a little harder to + * justify though. We claim that the symlink name is only likely + * to be replaced when it points within the file tree being moved and + * in that case it should be modified. what we really need to do is to + * call an oracle here. :) + */ + if (rephead != NULL) { + /* + * we have replacement strings, modify the name and the link + * name if any. + */ + if ((res = rep_name(arcn->name, &(arcn->nlen), 1)) != 0) + return(res); + + if (((arcn->type == PAX_SLK) || (arcn->type == PAX_HLK) || + (arcn->type == PAX_HRG)) && + ((res = rep_name(arcn->ln_name, &(arcn->ln_nlen), 0)) != 0)) + return(res); + } + + if (iflag) { + /* + * perform interactive file rename, then map the link if any + */ + if ((res = tty_rename(arcn)) != 0) + return(res); + if ((arcn->type == PAX_SLK) || (arcn->type == PAX_HLK) || + (arcn->type == PAX_HRG)) + sub_name(arcn->ln_name, &(arcn->ln_nlen)); + } + return(res); +} + +/* + * tty_rename() + * Prompt the user for a replacement file name. A "." keeps the old name, + * a empty line skips the file, and an EOF on reading the tty, will cause + * pax to stop processing and exit. Otherwise the file name input, replaces + * the old one. + * Return: + * 0 process this file, 1 skip this file, -1 we need to exit pax + */ + +#if __STDC__ +static int +tty_rename(register ARCHD *arcn) +#else +static int +tty_rename(arcn) + register ARCHD *arcn; +#endif +{ + char tmpname[PAXPATHLEN+2]; + int res; + + /* + * prompt user for the replacement name for a file, keep trying until + * we get some reasonable input. Archives may have more than one file + * on them with the same name (from updates etc). We print verbose info + * on the file so the user knows what is up. + */ + tty_prnt("\nATTENTION: %s interactive file rename operation.\n", argv0); + + for (;;) { + ls_tty(arcn); + tty_prnt("Input new name, or a \".\" to keep the old name, "); + tty_prnt("or a \"return\" to skip this file.\n"); + tty_prnt("Input > "); + if (tty_read(tmpname, sizeof(tmpname)) < 0) + return(-1); + if (strcmp(tmpname, "..") == 0) { + tty_prnt("Try again, illegal file name: ..\n"); + continue; + } + if (strlen(tmpname) > PAXPATHLEN) { + tty_prnt("Try again, file name too long\n"); + continue; + } + break; + } + + /* + * empty file name, skips this file. a "." leaves it alone + */ + if (tmpname[0] == '\0') { + tty_prnt("Skipping file.\n"); + return(1); + } + if ((tmpname[0] == '.') && (tmpname[1] == '\0')) { + tty_prnt("Processing continues, name unchanged.\n"); + return(0); + } + + /* + * ok the name changed. We may run into links that point at this + * file later. we have to remember where the user sent the file + * in order to repair any links. + */ + tty_prnt("Processing continues, name changed to: %s\n", tmpname); + res = add_name(arcn->name, arcn->nlen, tmpname); + arcn->nlen = l_strncpy(arcn->name, tmpname, PAXPATHLEN+1); + if (res < 0) + return(-1); + return(0); +} + +/* + * set_dest() + * fix up the file name and the link name (if any) so this file will land + * in the destination directory (used during copy() -rw). + * Return: + * 0 if ok, -1 if failure (name too long) + */ + +#if __STDC__ +int +set_dest(register ARCHD *arcn, char *dest_dir, int dir_len) +#else +int +set_dest(arcn, dest_dir, dir_len) + register ARCHD *arcn; + char *dest_dir; + int dir_len; +#endif +{ + if (fix_path(arcn->name, &(arcn->nlen), dest_dir, dir_len) < 0) + return(-1); + + /* + * It is really hard to deal with symlinks here, we cannot be sure + * if the name they point was moved (or will be moved). It is best to + * leave them alone. + */ + if ((arcn->type != PAX_HLK) && (arcn->type != PAX_HRG)) + return(0); + + if (fix_path(arcn->ln_name, &(arcn->ln_nlen), dest_dir, dir_len) < 0) + return(-1); + return(0); +} + +/* + * fix_path + * concatenate dir_name and or_name and store the result in or_name (if + * it fits). This is one ugly function. + * Return: + * 0 if ok, -1 if the final name is too long + */ + +#if __STDC__ +static int +fix_path( char *or_name, int *or_len, char *dir_name, int dir_len) +#else +static int +fix_path(or_name, or_len, dir_name, dir_len) + char *or_name; + int *or_len; + char *dir_name; + int dir_len; +#endif +{ + register char *src; + register char *dest; + register char *start; + int len; + + /* + * we shift the or_name to the right enough to tack in the dir_name + * at the front. We make sure we have enough space for it all before + * we start. since dest always ends in a slash, we skip of or_name + * if it also starts with one. + */ + start = or_name; + src = start + *or_len; + dest = src + dir_len; + if (*start == '/') { + ++start; + --dest; + } + if ((len = dest - or_name) > PAXPATHLEN) { + warn(1, "File name %s/%s, too long", dir_name, start); + return(-1); + } + *or_len = len; + + /* + * enough space, shift + */ + while (src >= start) + *dest-- = *src--; + src = dir_name + dir_len - 1; + + /* + * splice in the destination directory name + */ + while (src >= dir_name) + *dest-- = *src--; + + *(or_name + len) = '\0'; + return(0); +} + +/* + * rep_name() + * walk down the list of replacement strings applying each one in order. + * when we find one with a successful substitution, we modify the name + * as specified. if required, we print the results. if the resulting name + * is empty, we will skip this archive member. We use the regexp(3) + * routines (regexp() ought to win a prize as having the most cryptic + * library function manual page). + * --Parameters-- + * name is the file name we are going to apply the regular expressions to + * (and may be modified) + * nlen is the length of this name (and is modified to hold the length of + * the final string). + * prnt is a flag that says whether to print the final result. + * Return: + * 0 if substitution was successful, 1 if we are to skip the file (the name + * ended up empty) + */ + +#if __STDC__ +static int +rep_name(char *name, int *nlen, int prnt) +#else +static int +rep_name(name, nlen, prnt) + char *name; + int *nlen; + int prnt; +#endif +{ + register REPLACE *pt; + register char *inpt; + register char *outpt; + register char *endpt; + register char *rpt; + register int found = 0; + register int res; +# ifndef NET2_REGEX + regmatch_t pm[MAXSUBEXP]; +# endif + char nname[PAXPATHLEN+1]; /* final result of all replacements */ + char buf1[PAXPATHLEN+1]; /* where we work on the name */ + + /* + * copy the name into buf1, where we will work on it. We need to keep + * the orig string around so we can print out the result of the final + * replacement. We build up the final result in nname. inpt points at + * the string we apply the regular expression to. prnt is used to + * suppress printing when we handle replacements on the link field + * (the user already saw that substitution go by) + */ + pt = rephead; + (void)strcpy(buf1, name); + inpt = buf1; + outpt = nname; + endpt = outpt + PAXPATHLEN; + + /* + * try each replacement string in order + */ + while (pt != NULL) { + do { + /* + * check for a successful substitution, if not go to + * the next pattern, or cleanup if we were global + */ +# ifdef NET2_REGEX + if (regexec(pt->rcmp, inpt) == 0) +# else + if (regexec(&(pt->rcmp), inpt, MAXSUBEXP, pm, 0) != 0) +# endif + break; + + /* + * ok we found one. We have three parts, the prefix + * which did not match, the section that did and the + * tail (that also did not match). Copy the prefix to + * the final output buffer (watching to make sure we + * do not create a string too long). + */ + found = 1; +# ifdef NET2_REGEX + rpt = pt->rcmp->startp[0]; +# else + rpt = inpt + pm[0].rm_so; +# endif + + while ((inpt < rpt) && (outpt < endpt)) + *outpt++ = *inpt++; + if (outpt == endpt) + break; + + /* + * for the second part (which matched the regular + * expression) apply the substitution using the + * replacement string and place it the prefix in the + * final output. If we have problems, skip it. + */ +# ifdef NET2_REGEX + if ((res = resub(pt->rcmp,pt->nstr,outpt,endpt)) < 0) { +# else + if ((res = resub(&(pt->rcmp),pm,pt->nstr,outpt,endpt)) + < 0) { +# endif + if (prnt) + warn(1, "Replacement name error %s", + name); + return(1); + } + outpt += res; + + /* + * we set up to look again starting at the first + * character in the tail (of the input string right + * after the last character matched by the regular + * expression (inpt always points at the first char in + * the string to process). If we are not doing a global + * substitution, we will use inpt to copy the tail to + * the final result. Make sure we do not overrun the + * output buffer + */ +# ifdef NET2_REGEX + inpt = pt->rcmp->endp[0]; +# else + inpt += pm[0].rm_eo; +# endif + + if ((outpt == endpt) || (*inpt == '\0')) + break; + + /* + * if the user wants global we keep trying to + * substitute until it fails, then we are done. + */ + } while (pt->flgs & GLOB); + + if (found) + break; + + /* + * a successful substitution did NOT occur, try the next one + */ + pt = pt->fow; + } + + if (found) { + /* + * we had a substitution, copy the last tail piece (if there is + * room) to the final result + */ + while ((outpt < endpt) && (*inpt != '\0')) + *outpt++ = *inpt++; + + *outpt = '\0'; + if ((outpt == endpt) && (*inpt != '\0')) { + if (prnt) + warn(1,"Replacement name too long %s >> %s", + name, nname); + return(1); + } + + /* + * inform the user of the result if wanted + */ + if (prnt && (pt->flgs & PRNT)) { + if (*nname == '\0') + (void)fprintf(stderr,"%s >> <empty string>\n", + name); + else + (void)fprintf(stderr,"%s >> %s\n", name, nname); + } + + /* + * if empty inform the caller this file is to be skipped + * otherwise copy the new name over the orig name and return + */ + if (*nname == '\0') + return(1); + *nlen = l_strncpy(name, nname, PAXPATHLEN + 1); + } + return(0); +} + +#ifdef NET2_REGEX +/* + * resub() + * apply the replacement to the matched expression. expand out the old + * style ed(1) subexpression expansion. + * Return: + * -1 if error, or the number of characters added to the destination. + */ + +#if __STDC__ +static int +resub(regexp *prog, char *src, char *dest, register char *destend) +#else +static int +resub(prog, src, dest, destend) + regexp *prog; + char *src; + char *dest; + register char *destend; +#endif +{ + register char *spt; + register char *dpt; + register char c; + register int no; + register int len; + + spt = src; + dpt = dest; + while ((dpt < destend) && ((c = *spt++) != '\0')) { + if (c == '&') + no = 0; + else if ((c == '\\') && (*spt >= '0') && (*spt <= '9')) + no = *spt++ - '0'; + else { + if ((c == '\\') && ((*spt == '\\') || (*spt == '&'))) + c = *spt++; + *dpt++ = c; + continue; + } + if ((prog->startp[no] == NULL) || (prog->endp[no] == NULL) || + ((len = prog->endp[no] - prog->startp[no]) <= 0)) + continue; + + /* + * copy the subexpression to the destination. + * fail if we run out of space or the match string is damaged + */ + if (len > (destend - dpt)) + len = destend - dpt; + if (l_strncpy(dpt, prog->startp[no], len) != len) + return(-1); + dpt += len; + } + return(dpt - dest); +} + +#else + +/* + * resub() + * apply the replacement to the matched expression. expand out the old + * style ed(1) subexpression expansion. + * Return: + * -1 if error, or the number of characters added to the destination. + */ + +#if __STDC__ +static int +resub(regex_t *rp, register regmatch_t *pm, char *src, char *dest, + register char *destend) +#else +static int +resub(rp, pm, src, dest, destend) + regex_t *rp; + register regmatch_t *pm; + char *src; + char *dest; + register char *destend; +#endif +{ + register char *spt; + register char *dpt; + register char c; + register regmatch_t *pmpt; + register int len; + int subexcnt; + + spt = src; + dpt = dest; + subexcnt = rp->re_nsub; + while ((dpt < destend) && ((c = *spt++) != '\0')) { + /* + * see if we just have an ordinary replacement character + * or we refer to a subexpression. + */ + if (c == '&') { + pmpt = pm; + } else if ((c == '\\') && (*spt >= '0') && (*spt <= '9')) { + /* + * make sure there is a subexpression as specified + */ + if ((len = *spt++ - '0') > subexcnt) + return(-1); + pmpt = pm + len; + } else { + /* + * Ordinary character, just copy it + */ + if ((c == '\\') && ((*spt == '\\') || (*spt == '&'))) + c = *spt++; + *dpt++ = c; + continue; + } + + /* + * continue if the subexpression is bogus + */ + if ((pmpt->rm_so < 0) || (pmpt->rm_eo < 0) || + ((len = pmpt->rm_eo - pmpt->rm_so) <= 0)) + continue; + + /* + * copy the subexpression to the destination. + * fail if we run out of space or the match string is damaged + */ + if (len > (destend - dpt)) + len = destend - dpt; + if (l_strncpy(dpt, src + pmpt->rm_so, len) != len) + return(-1); + dpt += len; + } + return(dpt - dest); +} +#endif diff --git a/bin/pax/pat_rep.h b/bin/pax/pat_rep.h new file mode 100644 index 0000000..8d22cf0 --- /dev/null +++ b/bin/pax/pat_rep.h @@ -0,0 +1,54 @@ +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * 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. + * + * @(#)pat_rep.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * data structure for storing user supplied replacement strings (-s) + */ +typedef struct replace { + char *nstr; /* the new string we will substitute with */ +# ifdef NET2_REGEX + regexp *rcmp; /* compiled regular expression used to match */ +# else + regex_t rcmp; /* compiled regular expression used to match */ +# endif + int flgs; /* print conversions? global in operation? */ +#define PRNT 0x1 +#define GLOB 0x2 + struct replace *fow; /* pointer to next pattern */ +} REPLACE; diff --git a/bin/pax/pax.1 b/bin/pax/pax.1 new file mode 100644 index 0000000..a1d7565 --- /dev/null +++ b/bin/pax/pax.1 @@ -0,0 +1,1169 @@ +.\" Copyright (c) 1992 Keith Muller. +.\" Copyright (c) 1992, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Keith Muller of the University of California, San Diego. +.\" +.\" 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. +.\" +.\" @(#)pax.1 8.4 (Berkeley) 4/18/94 +.\" +.Dd April 18, 1994 +.Dt PAX 1 +.Os BSD 4.4 +.Sh NAME +.Nm pax +.Nd read and write file archives and copy directory hierarchies +.Sh SYNOPSIS +.Nm pax +.Op Fl cdnv +.Bk -words +.Op Fl f Ar archive +.Ek +.Bk -words +.Op Fl s Ar replstr +.Ar ... +.Ek +.Bk -words +.Op Fl U Ar user +.Ar ... +.Ek +.Bk -words +.Op Fl G Ar group +.Ar ... +.Ek +.Bk -words +.Oo +.Fl T +.Op Ar from_date +.Op Ar ,to_date +.Oc +.Ar ... +.Ek +.Op Ar pattern ... +.Nm pax +.Fl r +.Op Fl cdiknuvDYZ +.Bk -words +.Op Fl f Ar archive +.Ek +.Bk -words +.Op Fl o Ar options +.Ar ... +.Ek +.Bk -words +.Op Fl p Ar string +.Ar ... +.Ek +.Bk -words +.Op Fl s Ar replstr +.Ar ... +.Ek +.Op Fl E Ar limit +.Bk -words +.Op Fl U Ar user +.Ar ... +.Ek +.Bk -words +.Op Fl G Ar group +.Ar ... +.Ek +.Bk -words +.Oo +.Fl T +.Op Ar from_date +.Op Ar ,to_date +.Oc +.Ar ... +.Ek +.Op Ar pattern ... +.Nm pax +.Fl w +.Op Fl dituvHLPX +.Bk -words +.Op Fl b Ar blocksize +.Ek +.Oo +.Op Fl a +.Op Fl f Ar archive +.Oc +.Bk -words +.Op Fl x Ar format +.Ek +.Bk -words +.Op Fl s Ar replstr +.Ar ... +.Ek +.Bk -words +.Op Fl o Ar options +.Ar ... +.Ek +.Bk -words +.Op Fl U Ar user +.Ar ... +.Ek +.Bk -words +.Op Fl G Ar group +.Ar ... +.Ek +.Bk -words +.Op Fl B Ar bytes +.Ek +.Bk -words +.Oo +.Fl T +.Op Ar from_date +.Op Ar ,to_date +.Op Ar /[c][m] +.Oc +.Ar ... +.Ek +.Op Ar file ... +.Nm pax +.Fl r +.Fl w +.Op Fl diklntuvDHLPXYZ +.Bk -words +.Op Fl p Ar string +.Ar ... +.Ek +.Bk -words +.Op Fl s Ar replstr +.Ar ... +.Ek +.Bk -words +.Op Fl U Ar user +.Ar ... +.Ek +.Bk -words +.Op Fl G Ar group +.Ar ... +.Ek +.Bk -words +.Oo +.Fl T +.Op Ar from_date +.Op Ar ,to_date +.Op Ar /[c][m] +.Oc +.Ar ... +.Ek +.Op Ar file ... +.Ar directory +.Sh DESCRIPTION +.Nm Pax +will read, write, and list the members of an archive file, +and will copy directory hierarchies. +.Nm Pax +operation is independent of the specific archive format, +and supports a wide variety of different archive formats. +A list of supported archive formats can be found under the description of the +.Fl x +option. +.Pp +The presence of the +.Fl r +and the +.Fl w +options specifies which of the following functional modes +.Nm pax +will operate under: +.Em list , read , write , +and +.Em copy. +.Bl -tag -width 6n +.It <none> +.Em List . +.Nm Pax +will write to +.Dv standard output +a table of contents of the members of the archive file read from +.Dv standard input , +whose pathnames match the specified +.Ar patterns. +The table of contents contains one filename per line +and is written using single line buffering. +.It Fl r +.Em Read . +.Nm Pax +extracts the members of the archive file read from the +.Dv standard input , +with pathnames matching the specified +.Ar patterns. +The archive format and blocking is automatically determined on input. +When an extracted file is a directory, the entire file hierarchy +rooted at that directory is extracted. +All extracted files are created relative to the current file hierarchy. +The setting of ownership, access and modification times, and file mode of +the extracted files are discussed in more detail under the +.Fl p +option. +.It Fl w +.Em Write . +.Nm Pax +writes an archive containing the +.Ar file +operands to +.Dv standard output +using the specified archive format. +When no +.Ar file +operands are specified, a list of files to copy with one per line is read from +.Dv standard input . +When a +.Ar file +operand is also a directory, the entire file hierarchy rooted +at that directory will be included. +.It Fl r Fl w +.Em Copy . +.Nm Pax +copies the +.Ar file +operands to the destination +.Ar directory . +When no +.Ar file +operands are specified, a list of files to copy with one per line is read from +the +.Dv standard input . +When a +.Ar file +operand is also a directory the entire file +hierarchy rooted at that directory will be included. +The effect of the +.Em copy +is as if the copied files were written to an archive file and then +subsequently extracted, except that there may be hard links between +the original and the copied files (see the +.Fl l +option below). +.Pp +.Em Warning : +The destination +.Ar directory +must not be one of the +.Ar file +operands or a member of a file hierarchy rooted at one of the +.Ar file +operands. +The result of a +.Em copy +under these conditions is unpredictable. +.El +.Pp +While processing a damaged archive during a +.Em read +or +.Em list +operation, +.Nm pax +will attempt to recover from media defects and will search through the archive +to locate and process the largest number of archive members possible (see the +.Fl E +option for more details on error handling). +.Sh OPERANDS +.Pp +The +.Ar directory +operand specifies a destination directory pathname. +If the +.Ar directory +operand does not exist, or it is not writable by the user, +or it is not of type directory, +.Nm Pax +will exit with a non-zero exit status. +.Pp +The +.Ar pattern +operand is used to select one or more pathnames of archive members. +Archive members are selected using the pattern matching notation described +by +.Xr fnmatch 3 . +When the +.Ar pattern +operand is not supplied, all members of the archive will be selected. +When a +.Ar pattern +matches a directory, the entire file hierarchy rooted at that directory will +be selected. +When a +.Ar pattern +operand does not select at least one archive member, +.Nm pax +will write these +.Ar pattern +operands in a diagnostic message to +.Dv standard error +and then exit with a non-zero exit status. +.Pp +The +.Ar file +operand specifies the pathname of a file to be copied or archived. +When a +.Ar file +operand does not select at least one archive member, +.Nm pax +will write these +.Ar file +operand pathnames in a diagnostic message to +.Dv standard error +and then exit with a non-zero exit status. +.Sh OPTIONS +.Pp +The following options are supported: +.Bl -tag -width 4n +.It Fl r +Read an archive file from +.Dv standard input +and extract the specified +.Ar files . +If any intermediate directories are needed in order to extract an archive +member, these directories will be created as if +.Xr mkdir 2 +was called with the bitwise inclusive +.Dv OR +of +.Dv S_IRWXU , S_IRWXG , +and +.Dv S_IRWXO +as the mode argument. +When the selected archive format supports the specification of linked +files and these files cannot be linked while the archive is being extracted, +.Nm pax +will write a diagnostic message to +.Dv standard error +and exit with a non-zero exit status at the completion of operation. +.It Fl w +Write files to the +.Dv standard output +in the specified archive format. +When no +.Ar file +operands are specified, +.Dv standard input +is read for a list of pathnames with one per line without any leading or +trailing +.Aq blanks . +.It Fl a +Append +.Ar files +to the end of an archive that was previously written. +If an archive format is not specified with a +.Fl x +option, the format currently being used in the archive will be selected. +Any attempt to append to an archive in a format different from the +format already used in the archive will cause +.Nm pax +to exit immediately +with a non-zero exit status. +The blocking size used in the archive volume where writing starts +will continue to be used for the remainder of that archive volume. +.Pp +.Em Warning : +Many storage devices are not able to support the operations necessary +to perform an append operation. +Any attempt to append to an archive stored on such a device may damage the +archive or have other unpredictable results. +Tape drives in particular are more likely to not support an append operation. +An archive stored in a regular file system file or on a disk device will +usually support an append operation. +.It Fl b Ar blocksize +When +.Em writing +an archive, +block the output at a positive decimal integer number of +bytes per write to the archive file. +The +.Ar blocksize +must be a multiple of 512 bytes with a maximum of 32256 bytes. +A +.Ar blocksize +can end with +.Li k +or +.Li b +to specify multiplication by 1024 (1K) or 512, respectively. +A pair of +.Ar blocksizes +can be separated by +.Li x +to indicate a product. +A specific archive device may impose additional restrictions on the size +of blocking it will support. +When blocking is not specified, the default +.Ar blocksize +is dependent on the specific archive format being used (see the +.Fl x +option). +.It Fl c +Match all file or archive members +.Em except +those specified by the +.Ar pattern +and +.Ar file +operands. +.It Fl d +Cause files of type directory being copied or archived, or archive members of +type directory being extracted, to match only the directory file or archive +member and not the file hierarchy rooted at the directory. +.It Fl f Ar archive +Specify +.Ar archive +as the pathname of the input or output archive, overriding the default +.Dv standard input +(for +.Em list +and +.Em read ) +or +.Dv standard output +(for +.Em write ) . +A single archive may span multiple files and different archive devices. +When required, +.Nm pax +will prompt for the pathname of the file or device of the next volume in the +archive. +.It Fl i +Interactively rename files or archive members. +For each archive member matching a +.Ar pattern +operand or each file matching a +.Ar file +operand, +.Nm pax +will prompt to +.Pa /dev/tty +giving the name of the file, its file mode and its modification time. +.Nm Pax +will then read a line from +.Pa /dev/tty . +If this line is blank, the file or archive member is skipped. +If this line consists of a single period, the +file or archive member is processed with no modification to its name. +Otherwise, its name is replaced with the contents of the line. +.Nm Pax +will immediately exit with a non-zero exit status if +.Dv <EOF> +is encountered when reading a response or if +.Pa /dev/tty +cannot be opened for reading and writing. +.It Fl k +Do not overwrite existing files. +.It Fl l +Link files. (The letter ell). +In the +.Em copy +mode ( +.Fl r +.Fl w ) , +hard links are made between the source and destination file hierarchies +whenever possible. +.It Fl n +Select the first archive member that matches each +.Ar pattern +operand. +No more than one archive member is matched for each +.Ar pattern . +When members of type directory are matched, the file hierarchy rooted at that +directory is also matched (unless +.Fl d +is also specified). +.It Fl o Ar options +Information to modify the algorithm for extracting or writing archive files +which is specific to the archive format specified by +.Fl x . +In general, +.Ar options +take the form: +.Cm name=value +.It Fl p Ar string +Specify one or more file characteristic options (privileges). +The +.Ar string +option-argument is a string specifying file characteristics to be retained or +discarded on extraction. +The string consists of the specification characters +.Cm a , e , m , o , +and +.Cm p . +Multiple characteristics can be concatenated within the same string +and multiple +.Fl p +options can be specified. +The meaning of the specification characters are as follows: +.Bl -tag -width 2n +.It Cm a +Do not preserve file access times. +By default, file access times are preserved whenever possible. +.It Cm e +.Sq Preserve everything , +the user ID, group ID, file mode bits, +file access time, and file modification time. +This is intended to be used by +.Em root , +someone with all the appropriate privileges, in order to preserve all +aspects of the files as they are recorded in the archive. +The +.Cm e +flag is the sum of the +.Cm o +and +.Cm p +flags. +.It Cm m +Do not preserve file modification times. +By default, file modification times are preserved whenever possible. +.It Cm o +Preserve the user ID and group ID. +.It Cm p +.Sq Preserve +the file mode bits. +This intended to be used by a +.Em user +with regular privileges who wants to preserve all aspects of the file other +than the ownership. +The file times are preserved by default, but two other flags are offered to +disable this and use the time of extraction instead. +.El +.Pp +In the preceding list, +.Sq preserve +indicates that an attribute stored in the archive is given to the +extracted file, subject to the permissions of the invoking +process. +Otherwise the attribute of the extracted file is determined as +part of the normal file creation action. +If neither the +.Cm e +nor the +.Cm o +specification character is specified, or the user ID and group ID are not +preserved for any reason, +.Nm pax +will not set the +.Dv S_ISUID +.Em ( setuid ) +and +.Dv S_ISGID +.Em ( setgid ) +bits of the file mode. +If the preservation of any of these items fails for any reason, +.Nm pax +will write a diagnostic message to +.Dv standard error . +Failure to preserve these items will affect the final exit status, +but will not cause the extracted file to be deleted. +If the file characteristic letters in any of the string option-arguments are +duplicated or conflict with each other, the one(s) given last will take +precedence. +For example, if +.Dl Fl p Ar eme +is specified, file modification times are still preserved. +.It Fl s Ar replstr +Modify the file or archive member names specified by the +.Ar pattern +or +.Ar file +operands according to the substitution expression +.Ar replstr , +using the syntax of the +.Xr ed 1 +utility regular expressions. +The format of these regular expressions are: +.Dl /old/new/[gp] +As in +.Xr ed 1 , +.Cm old +is a basic regular expression and +.Cm new +can contain an ampersand (&), \\n (where n is a digit) back-references, +or subexpression matching. +The +.Cm old +string may also contain +.Dv <newline> +characters. +Any non-null character can be used as a delimiter (/ is shown here). +Multiple +.Fl s +expressions can be specified. +The expressions are applied in the order they are specified on the +command line, terminating with the first successful substitution. +The optional trailing +.Cm g +continues to apply the substitution expression to the pathname substring +which starts with the first character following the end of the last successful +substitution. The first unsuccessful substitution stops the operation of the +.Cm g +option. +The optional trailing +.Cm p +will cause the final result of a successful substitution to be written to +.Dv standard error +in the following format: +.Dl <original pathname> >> <new pathname> +File or archive member names that substitute to the empty string +are not selected and will be skipped. +.It Fl t +Reset the access times of any file or directory read or accessed by +.Nm pax +to be the same as they were before being read or accessed by +.Nm pax . +.It Fl u +Ignore files that are older (having a less recent file modification time) +than a pre-existing file or archive member with the same name. +During +.Em read , +an archive member with the same name as a file in the file system will be +extracted if the archive member is newer than the file. +During +.Em write , +a file system member with the same name as an archive member will be +written to the archive if it is newer than the archive member. +During +.Em copy , +the file in the destination hierarchy is replaced by the file in the source +hierarchy or by a link to the file in the source hierarchy if the file in +the source hierarchy is newer. +.It Fl v +During a +.Em list +operation, produce a verbose table of contents using the format of the +.Xr ls 1 +utility with the +.Fl l +option. +For pathnames representing a hard link to a previous member of the archive, +the output has the format: +.Dl <ls -l listing> == <link name> +For pathnames representing a symbolic link, the output has the format: +.Dl <ls -l listing> => <link name> +Where <ls -l listing> is the output format specified by the +.Xr ls 1 +utility when used with the +.Fl l +option. +Otherwise for all the other operational modes ( +.Em read , write , +and +.Em copy ) , +pathnames are written and flushed to +.Dv standard error +without a trailing +.Dv <newline> +as soon as processing begins on that file or +archive member. +The trailing +.Dv <newline> , +is not buffered, and is written only after the file has been read or written. +.It Fl x Ar format +Specify the output archive format, with the default format being +.Ar ustar . +.Nm Pax +currently supports the following formats: +.Bl -tag -width "sv4cpio" +.It Ar cpio +The extended cpio interchange format specified in the +.St -p1003.2 +standard. +The default blocksize for this format is 5120 bytes. +Inode and device information about a file (used for detecting file hard links +by this format) which may be truncated by this format is detected by +.Nm pax +and is repaired. +.It Ar bcpio +The old binary cpio format. +The default blocksize for this format is 5120 bytes. +This format is not very portable and should not be used when other formats +are available. +Inode and device information about a file (used for detecting file hard links +by this format) which may be truncated by this format is detected by +.Nm pax +and is repaired. +.It Ar sv4cpio +The System V release 4 cpio. +The default blocksize for this format is 5120 bytes. +Inode and device information about a file (used for detecting file hard links +by this format) which may be truncated by this format is detected by +.Nm pax +and is repaired. +.It Ar sv4crc +The System V release 4 cpio with file crc checksums. +The default blocksize for this format is 5120 bytes. +Inode and device information about a file (used for detecting file hard links +by this format) which may be truncated by this format is detected by +.Nm pax +and is repaired. +.It Ar tar +The old BSD tar format as found in BSD4.3. +The default blocksize for this format is 10240 bytes. +Pathnames stored by this format must be 100 characters or less in length. +Only +.Em regular +files, +.Em hard links , soft links , +and +.Em directories +will be archived (other file system types are not supported). +For backwards compatibility with even older tar formats, a +.Fl o +option can be used when writing an archive to omit the storage of directories. +This option takes the form: +.Dl Fl o Cm write_opt=nodir +.It Ar ustar +The extended tar interchange format specified in the +.St -p1003.2 +standard. +The default blocksize for this format is 10240 bytes. +Pathnames stored by this format must be 250 characters or less in length. +.El +.Pp +.Nm Pax +will detect and report any file that it is unable to store or extract +as the result of any specific archive format restrictions. +The individual archive formats may impose additional restrictions on use. +Typical archive format restrictions include (but are not limited to): +file pathname length, file size, link pathname length and the type of the file. +.It Fl B Ar bytes +Limit the number of bytes written to a single archive volume to +.Ar bytes . +The +.Ar bytes +limit can end with +.Li m , +.Li k , +or +.Li b +to specify multiplication by 1048576 (1M), 1024 (1K) or 512, respectively. +A pair of +.Ar bytes +limits can be separated by +.Li x +to indicate a product. +.Pp +.Em Warning : +Only use this option when writing an archive to a device which supports +an end of file read condition based on last (or largest) write offset +(such as a regular file or a tape drive). +The use of this option with a floppy or hard disk is not recommended. +.It Fl D +This option is the same as the +.Fl u +option, except that the file inode change time is checked instead of the +file modification time. +The file inode change time can be used to select files whose inode information +(e.g. uid, gid, etc.) is newer than a copy of the file in the destination +.Ar directory . +.It Fl E Ar limit +Limit the number of consecutive read faults while trying to read a flawed +archives to +.Ar limit . +With a positive +.Ar limit , +.Nm pax +will attempt to recover from an archive read error and will +continue processing starting with the next file stored in the archive. +A +.Ar limit +of 0 will cause +.Nm pax +to stop operation after the first read error is detected on an archive volume. +A +.Ar limit +of +.Li NONE +will cause +.Nm pax +to attempt to recover from read errors forever. +The default +.Ar limit +is a small positive number of retries. +.Pp +.Em Warning: +Using this option with +.Li NONE +should be used with extreme caution as +.Nm pax +may get stuck in an infinite loop on a very badly flawed archive. +.It Fl G Ar group +Select a file based on its +.Ar group +name, or when starting with a +.Cm # , +a numeric gid. +A '\\' can be used to escape the +.Cm # . +Multiple +.Fl G +options may be supplied and checking stops with the first match. +.It Fl H +Follow only command line symbolic links while performing a physical file +system traversal. +.It Fl L +Follow all symbolic links to perform a logical file system traversal. +.It Fl P +Do not follow symbolic links, perform a physical file system traversal. +This is the default mode. +.It Fl T Ar [from_date][,to_date][/[c][m]] +Allow files to be selected based on a file modification or inode change +time falling within a specified time range of +.Ar from_date +to +.Ar to_date +(the dates are inclusive). +If only a +.Ar from_date +is supplied, all files with a modification or inode change time +equal to or younger are selected. +If only a +.Ar to_date +is supplied, all files with a modification or inode change time +equal to or older will be selected. +When the +.Ar from_date +is equal to the +.Ar to_date , +only files with a modification or inode change time of exactly that +time will be selected. +.Pp +When +.Nm pax +is in the +.Em write +or +.Em copy +mode, the optional trailing field +.Ar [c][m] +can be used to determine which file time (inode change, file modification or +both) are used in the comparison. +If neither is specified, the default is to use file modification time only. +The +.Ar m +specifies the comparison of file modification time (the time when +the file was last written). +The +.Ar c +specifies the comparison of inode change time (the time when the file +inode was last changed; e.g. a change of owner, group, mode, etc). +When +.Ar c +and +.Ar m +are both specified, then the modification and inode change times are +both compared. +The inode change time comparison is useful in selecting files whose +attributes were recently changed or selecting files which were recently +created and had their modification time reset to an older time (as what +happens when a file is extracted from an archive and the modification time +is preserved). +Time comparisons using both file times is useful when +.Nm pax +is used to create a time based incremental archive (only files that were +changed during a specified time range will be archived). +.Pp +A time range is made up of six different fields and each field must contain two +digits. +The format is: +.Dl [yy[mm[dd[hh]]]]mm[.ss] +Where +.Cm yy +is the last two digits of the year, +the first +.Cm mm +is the month (from 01 to 12), +.Cm dd +is the day of the month (from 01 to 31), +.Cm hh +is the hour of the day (from 00 to 23), +the second +.Cm mm +is the minute (from 00 to 59), +and +.Cm ss +is the seconds (from 00 to 59). +The minute field +.Cm mm +is required, while the other fields are optional and must be added in the +following order: +.Dl Cm hh , dd , mm , yy . +The +.Cm ss +field may be added independently of the other fields. +Time ranges are relative to the current time, so +.Dl Fl T Ar 1234/cm +would select all files with a modification or inode change time +of 12:34 PM today or later. +Multiple +.Fl T +time range can be supplied and checking stops with the first match. +.It Fl U Ar user +Select a file based on its +.Ar user +name, or when starting with a +.Cm # , +a numeric uid. +A '\\' can be used to escape the +.Cm # . +Multiple +.Fl U +options may be supplied and checking stops with the first match. +.It Fl X +When traversing the file hierarchy specified by a pathname, +do not descend into directories that have a different device ID. +See the +.Li st_dev +field as described in +.Xr stat 2 +for more information about device ID's. +.It Fl Y +This option is the same as the +.Fl D +option, except that the inode change time is checked using the +pathname created after all the file name modifications have completed. +.It Fl Z +This option is the same as the +.Fl u +option, except that the modification time is checked using the +pathname created after all the file name modifications have completed. +.El +.Pp +The options that operate on the names of files or archive members ( +.Fl c , +.Fl i , +.Fl n , +.Fl s , +.Fl u , +.Fl v , +.Fl D , +.Fl G , +.Fl T , +.Fl U , +.Fl Y , +and +.Fl Z ) +interact as follows. +.Pp +When extracting files during a +.Em read +operation, archive members are +.Sq selected , +based only on the user specified pattern operands as modified by the +.Fl c , +.Fl n , +.Fl u , +.Fl D , +.Fl G , +.Fl T , +.Fl U +options. +Then any +.Fl s +and +.Fl i +options will modify in that order, the names of these selected files. +Then the +.Fl Y +and +.Fl Z +options will be applied based on the final pathname. +Finally the +.Fl v +option will write the names resulting from these modifications. +.Pp +When archiving files during a +.Em write +operation, or copying files during a +.Em copy +operation, archive members are +.Sq selected , +based only on the user specified pathnames as modified by the +.Fl n , +.Fl u , +.Fl D , +.Fl G , +.Fl T , +and +.Fl U +options (the +.Fl D +option only applies during a copy operation). +Then any +.Fl s +and +.Fl i +options will modify in that order, the names of these selected files. +Then during a +.Em copy +operation the +.Fl Y +and the +.Fl Z +options will be applied based on the final pathname. +Finally the +.Fl v +option will write the names resulting from these modifications. +.Pp +When one or both of the +.Fl u +or +.Fl D +options are specified along with the +.Fl n +option, a file is not considered selected unless it is newer +than the file to which it is compared. +.Sh EXAMPLES +The command: +.Dl pax -w -f /dev/rst0 .\ +copies the contents of the current directory to the device +.Pa /dev/rst0 . +.Pp +The command: +.Dl pax -r -v -f filename +gives the verbose table of contents for an archive stored in +.Pa filename . +.Pp +The following commands: +.Dl mkdir newdir +.Dl cd olddir +.Dl pax -rw .\ newdir +will copy the entire +.Pa olddir +directory hierarchy to +.Pa newdir . +.Pp +The command: +.Dl pax -r -s ',^//*usr//*,,' -f a.pax +reads the archive +.Pa a.pax , +with all files rooted in ``/usr'' into the archive extracted relative to the +current directory. +.Pp +The command: +.Dl pax -rw -i .\ dest_dir +can be used to interactively select the files to copy from the current +directory to +.Pa dest_dir . +.Pp +The command: +.Dl pax -r -pe -U root -G bin -f a.pax +will extract all files from the archive +.Pa a.pax +which are owned by +.Em root +with group +.Em bin +and will preserve all file permissions. +.Pp +The command: +.Dl pax -r -w -v -Y -Z home /backup +will update (and list) only those files in the destination directory +.Pa /backup +which are older (less recent inode change or file modification times) than +files with the same name found in the source file tree +.Pa home . +.Sh STANDARDS +The +.Nm pax +utility is a superset of the +.St -p1003.2 +standard. +The options +.Fl B , +.Fl D , +.Fl E , +.Fl G , +.Fl H , +.Fl L , +.Fl P , +.Fl T , +.Fl U , +.Fl Y , +.Fl Z , +the archive formats +.Ar bcpio , +.Ar sv4cpio , +.Ar sv4crc , +.Ar tar , +and the flawed archive handling during +.Ar list +and +.Ar read +operations are extensions to the +.Tn POSIX +standard. +.Sh AUTHOR +Keith Muller at the University of California, San Diego +.Sh ERRORS +.Nm pax +will exit with one of the following values: +.Bl -tag -width 2n +.It 0 +All files were processed successfully. +.It 1 +An error occurred. +.El +.Pp +Whenever +.Nm pax +cannot create a file or a link when reading an archive or cannot +find a file when writing an archive, or cannot preserve the user ID, +group ID, or file mode when the +.Fl p +option is specified, a diagnostic message is written to +.Dv standard error +and a non-zero exit status will be returned, but processing will continue. +In the case where pax cannot create a link to a file, +.Nm pax +will not create a second copy of the file. +.Pp +If the extraction of a file from an archive is prematurely terminated by +a signal or error, +.Nm pax +may have only partially extracted a file the user wanted. +Additionally, the file modes of extracted files and directories +may have incorrect file bits, and the modification and access times may be +wrong. +.Pp +If the creation of an archive is prematurely terminated by a signal or error, +.Nm pax +may have only partially created the archive which may violate the specific +archive format specification. +.Pp +If while doing a +.Em copy , +.Nm pax +detects a file is about to overwrite itself, the file is not copied, +a diagnostic message is written to +.Dv standard error +and when +.Nm pax +completes it will exit with a non-zero exit status. diff --git a/bin/pax/pax.c b/bin/pax/pax.c new file mode 100644 index 0000000..e8b6626 --- /dev/null +++ b/bin/pax/pax.c @@ -0,0 +1,405 @@ +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * 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) 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)pax.c 8.2 (Berkeley) 4/18/94"; +#endif /* not lint */ + +#include <stdio.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <signal.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include "pax.h" +#include "extern.h" +static int gen_init __P((void)); + +/* + * PAX main routines, general globals and some simple start up routines + */ + +/* + * Variables that can be accessed by any routine within pax + */ +int act = DEFOP; /* read/write/append/copy */ +FSUB *frmt = NULL; /* archive format type */ +int cflag; /* match all EXCEPT pattern/file */ +int dflag; /* directory member match only */ +int iflag; /* interactive file/archive rename */ +int kflag; /* do not overwrite existing files */ +int lflag; /* use hard links when possible */ +int nflag; /* select first archive member match */ +int tflag; /* restore access time after read */ +int uflag; /* ignore older modification time files */ +int vflag; /* produce verbose output */ +int Dflag; /* same as uflag except inode change time */ +int Hflag; /* follow command line symlinks (write only) */ +int Lflag; /* follow symlinks when writing */ +int Xflag; /* archive files with same device id only */ +int Yflag; /* same as Dflg except after name mode */ +int Zflag; /* same as uflg except after name mode */ +int vfpart; /* is partial verbose output in progress */ +int patime = 1; /* preserve file access time */ +int pmtime = 1; /* preserve file modification times */ +int pmode; /* preserve file mode bits */ +int pids; /* preserve file uid/gid */ +int exit_val; /* exit value */ +int docrc; /* check/create file crc */ +char *dirptr; /* destination dir in a copy */ +char *ltmfrmt; /* -v locale time format (if any) */ +char *argv0; /* root of argv[0] */ +sigset_t s_mask; /* signal mask for cleanup critical sect */ + +/* + * PAX - Portable Archive Interchange + * + * A utility to read, write, and write lists of the members of archive + * files and copy directory hierarchies. A variety of archive formats + * are supported (some are described in POSIX 1003.1 10.1): + * + * ustar - 10.1.1 extended tar interchange format + * cpio - 10.1.2 extended cpio interchange format + * tar - old BSD 4.3 tar format + * binary cpio - old cpio with binary header format + * sysVR4 cpio - with and without CRC + * + * This version is a superset of IEEE Std 1003.2b-d3 + * + * Summary of Extensions to the IEEE Standard: + * + * 1 READ ENHANCEMENTS + * 1.1 Operations which read archives will continue to operate even when + * processing archives which may be damaged, truncated, or fail to meet + * format specs in several different ways. Damaged sections of archives + * are detected and avoided if possible. Attempts will be made to resync + * archive read operations even with badly damaged media. + * 1.2 Blocksize requirements are not strictly enforced on archive read. + * Tapes which have variable sized records can be read without errors. + * 1.3 The user can specify via the non-standard option flag -E if error + * resync operation should stop on a media error, try a specified number + * of times to correct, or try to correct forever. + * 1.4 Sparse files (lseek holes) stored on the archive (but stored with blocks + * of all zeros will be restored with holes appropriate for the target + * filesystem + * 1.5 The user is notified whenever something is found during archive + * read operations which violates spec (but the read will continue). + * 1.6 Multiple archive volumes can be read and may span over different + * archive devices + * 1.7 Rigidly restores all file attributes exactly as they are stored on the + * archive. + * 1.8 Modification change time ranges can be specified via multiple -T + * options. These allow a user to select files whose modification time + * lies within a specific time range. + * 1.9 Files can be selected based on owner (user name or uid) via one or more + * -U options. + * 1.10 Files can be selected based on group (group name or gid) via one o + * more -G options. + * 1.11 File modification time can be checked against exisiting file after + * name modification (-Z) + * + * 2 WRITE ENHANCEMENTS + * 2.1 Write operation will stop instead of allowing a user to create a flawed + * flawed archive (due to any problem). + * 2.2 Archives writtens by pax are forced to strictly conform to both the + * archive and pax the spceific format specifications. + * 2.3 Blocking size and format is rigidly enforced on writes. + * 2.4 Formats which may exhibit header overflow problems (they have fields + * too small for large file systems, such as inode number storage), use + * routines designed to repair this problem. These techniques still + * conform to both pax and format specifications, but no longer truncate + * these fields. This removes any restrictions on using these archive + * formats on large file systems. + * 2.5 Multiple archive volumes can be written and may span over different + * archive devices + * 2.6 A archive volume record limit allows the user to specify the number + * of bytes stored on an archive volume. When reached the user is + * prompted for the next archive volume. This is specified with the + * non-standard -B flag. THe limit is rounded up to the next blocksize. + * 2.7 All archive padding during write use zero filled sections. This makes + * it much easier to pull data out of flawed archive during read + * operations. + * 2.8 Access time reset with the -t applies to all file nodes (including + * directories). + * 2.9 Symbolic links can be followed with -L (optional in the spec). + * 2.10 Modification or inode change time ranges can be specified via + * multiple -T options. These allow a user to select files whose + * modification or inode change time lies within a specific time range. + * 2.11 Files can be selected based on owner (user name or uid) via one or more + * -U options. + * 2.12 Files can be selected based on group (group name or gid) via one o + * more -G options. + * 2.13 Symlinks which appear on the command line can be followed (without + * following other symlinks; -H flag) + * + * 3 COPY ENHANCEMENTS + * 3.1 Sparse files (lseek holes) can be copied without expanding the holes + * into zero filled blocks. The file copy is created with holes which are + * appropriate for the target filesystem + * 3.2 Access time as well as modification time on copied file trees can be + * preserved with the appropriate -p options. + * 3.3 Access time reset with the -t applies to all file nodes (including + * directories). + * 3.4 Symbolic links can be followed with -L (optional in the spec). + * 3.5 Modification or inode change time ranges can be specified via + * multiple -T options. These allow a user to select files whose + * modification or inode change time lies within a specific time range. + * 3.6 Files can be selected based on owner (user name or uid) via one or more + * -U options. + * 3.7 Files can be selected based on group (group name or gid) via one o + * more -G options. + * 3.8 Symlinks which appear on the command line can be followed (without + * following other symlinks; -H flag) + * 3.9 File inode change time can be checked against exisiting file before + * name modification (-D) + * 3.10 File inode change time can be checked against exisiting file after + * name modification (-Y) + * 3.11 File modification time can be checked against exisiting file after + * name modification (-Z) + * + * 4 GENERAL ENHANCEMENTS + * 4.1 Internal structure is designed to isolate format dependent and + * independent functions. Formats are selected via a format driver table. + * This encourages the addition of new archive formats by only having to + * write those routines which id, read and write the archive header. + */ + +/* + * main() + * parse options, set up and operate as specified by the user. + * any operational flaw will set exit_val to non-zero + * Return: 0 if ok, 1 otherwise + */ + +#if __STDC__ +int +main(int argc, char **argv) +#else +int +main(argc, argv) + int argc; + char **argv; +#endif +{ + /* + * parse options, determine operational mode, general init + */ + options(argc, argv); + if ((gen_init() < 0) || (tty_init() < 0)) + return(exit_val); + + /* + * select a primary operation mode + */ + switch(act) { + case EXTRACT: + extract(); + break; + case ARCHIVE: + archive(); + break; + case APPND: + append(); + break; + case COPY: + copy(); + break; + default: + case LIST: + list(); + break; + } + return(exit_val); +} + +/* + * sig_cleanup() + * when interrupted we try to do whatever delayed processing we can. + * This is not critical, but we really ought to limit our damage when we + * are aborted by the user. + * Return: + * never.... + */ + +#if __STDC__ +void +sig_cleanup(int which_sig) +#else +void +sig_cleanup(which_sig) + int which_sig; +#endif +{ + /* + * restore modes and times for any dirs we may have created + * or any dirs we may have read. Set vflag and vfpart so the user + * will clearly see the message on a line by itself. + */ + vflag = vfpart = 1; + if (which_sig == SIGXCPU) + warn(0, "Cpu time limit reached, cleaning up."); + else + warn(0, "Signal caught, cleaning up."); + + ar_close(); + proc_dir(); + if (tflag) + atdir_end(); + exit(1); +} + +/* + * gen_init() + * general setup routines. Not all are required, but they really help + * when dealing with a medium to large sized archives. + */ + +#if __STDC__ +static int +gen_init(void) +#else +static int +gen_init() +#endif +{ + struct rlimit reslimit; + struct sigaction n_hand; + struct sigaction o_hand; + + /* + * Really needed to handle large archives. We can run out of memory for + * internal tables really fast when we have a whole lot of files... + */ + if (getrlimit(RLIMIT_DATA , &reslimit) == 0){ + reslimit.rlim_cur = reslimit.rlim_max; + (void)setrlimit(RLIMIT_DATA , &reslimit); + } + + /* + * should file size limits be waived? if the os limits us, this is + * needed if we want to write a large archive + */ + if (getrlimit(RLIMIT_FSIZE , &reslimit) == 0){ + reslimit.rlim_cur = reslimit.rlim_max; + (void)setrlimit(RLIMIT_FSIZE , &reslimit); + } + + /* + * increase the size the stack can grow to + */ + if (getrlimit(RLIMIT_STACK , &reslimit) == 0){ + reslimit.rlim_cur = reslimit.rlim_max; + (void)setrlimit(RLIMIT_STACK , &reslimit); + } + + /* + * not really needed, but doesn't hurt + */ + if (getrlimit(RLIMIT_RSS , &reslimit) == 0){ + reslimit.rlim_cur = reslimit.rlim_max; + (void)setrlimit(RLIMIT_RSS , &reslimit); + } + + /* + * Handle posix locale + * + * set user defines time printing format for -v option + */ + ltmfrmt = getenv("LC_TIME"); + + /* + * signal handling to reset stored directory times and modes. Since + * we deal with broken pipes via failed writes we ignore it. We also + * deal with any file size limit thorugh failed writes. Cpu time + * limits are caught and a cleanup is forced. + */ + if ((sigemptyset(&s_mask) < 0) || (sigaddset(&s_mask, SIGTERM) < 0) || + (sigaddset(&s_mask,SIGINT) < 0)||(sigaddset(&s_mask,SIGHUP) < 0) || + (sigaddset(&s_mask,SIGPIPE) < 0)||(sigaddset(&s_mask,SIGQUIT)<0) || + (sigaddset(&s_mask,SIGXCPU) < 0)||(sigaddset(&s_mask,SIGXFSZ)<0)) { + warn(1, "Unable to set up signal mask"); + return(-1); + } + n_hand.sa_mask = s_mask; + n_hand.sa_flags = 0; + n_hand.sa_handler = sig_cleanup; + + if ((sigaction(SIGHUP, &n_hand, &o_hand) < 0) && + (o_hand.sa_handler == SIG_IGN) && + (sigaction(SIGHUP, &o_hand, &o_hand) < 0)) + goto out; + + if ((sigaction(SIGTERM, &n_hand, &o_hand) < 0) && + (o_hand.sa_handler == SIG_IGN) && + (sigaction(SIGTERM, &o_hand, &o_hand) < 0)) + goto out; + + if ((sigaction(SIGINT, &n_hand, &o_hand) < 0) && + (o_hand.sa_handler == SIG_IGN) && + (sigaction(SIGINT, &o_hand, &o_hand) < 0)) + goto out; + + if ((sigaction(SIGQUIT, &n_hand, &o_hand) < 0) && + (o_hand.sa_handler == SIG_IGN) && + (sigaction(SIGQUIT, &o_hand, &o_hand) < 0)) + goto out; + + if ((sigaction(SIGXCPU, &n_hand, &o_hand) < 0) && + (o_hand.sa_handler == SIG_IGN) && + (sigaction(SIGXCPU, &o_hand, &o_hand) < 0)) + goto out; + + n_hand.sa_handler = SIG_IGN; + if ((sigaction(SIGPIPE, &n_hand, &o_hand) < 0) || + (sigaction(SIGXFSZ, &n_hand, &o_hand) < 0)) + goto out; + return(0); + + out: + syswarn(1, errno, "Unable to set up signal handler"); + return(-1); +} diff --git a/bin/pax/pax.h b/bin/pax/pax.h new file mode 100644 index 0000000..5658d76 --- /dev/null +++ b/bin/pax/pax.h @@ -0,0 +1,238 @@ +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * 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. + * + * @(#)pax.h 8.2 (Berkeley) 4/18/94 + */ + +/* + * BSD PAX global data structures and constants. + */ + +#define MAXBLK 32256 /* MAX blocksize supported (posix SPEC) */ + /* WARNING: increasing MAXBLK past 32256 */ + /* will violate posix spec. */ +#define BLKMULT 512 /* blocksize must be even mult of 512 bytes */ + /* Don't even think of changing this */ +#define DEVBLK 8192 /* default read blksize for devices */ +#define FILEBLK 10240 /* default read blksize for files */ +#define PAXPATHLEN 3072 /* maximium path length for pax. MUST be */ + /* longer than the system MAXPATHLEN */ + +/* + * Pax modes of operation + */ +#define LIST 0 /* List the file in an archive */ +#define EXTRACT 1 /* extract the files in an archive */ +#define ARCHIVE 2 /* write a new archive */ +#define APPND 3 /* append to the end of an archive */ +#define COPY 4 /* copy files to destination dir */ +#define DEFOP LIST /* if no flags default is to LIST */ + +/* + * Device type of the current archive volume + */ +#define ISREG 0 /* regular file */ +#define ISCHR 1 /* character device */ +#define ISBLK 2 /* block device */ +#define ISTAPE 3 /* tape drive */ +#define ISPIPE 4 /* pipe/socket */ + +/* + * Format Specific Routine Table + * + * The format specific routine table allows new archive formats to be quickly + * added. Overall pax operation is independent of the actual format used to + * form the archive. Only those routines which deal directly with the archive + * are tailored to the oddities of the specifc format. All other routines are + * independent of the archive format. Data flow in and out of the format + * dependent routines pass pointers to ARCHD structure (described below). + */ +typedef struct { + char *name; /* name of format, this is the name the user */ + /* gives to -x option to select it. */ + int bsz; /* default block size. used when the user */ + /* does not specify a blocksize for writing */ + /* Appends continue to with the blocksize */ + /* the archive is currently using.*/ + int hsz; /* Header size in bytes. this is the size of */ + /* the smallest header this format supports. */ + /* Headers are assumed to fit in a BLKMULT. */ + /* If they are bigger, get_head() and */ + /* get_arc() must be adjusted */ + int udev; /* does append require unique dev/ino? some */ + /* formats use the device and inode fields */ + /* to specify hard links. when members in */ + /* the archive have the same inode/dev they */ + /* are assumed to be hard links. During */ + /* append we may have to generate unique ids */ + /* to avoid creating incorrect hard links */ + int hlk; /* does archive store hard links info? if */ + /* not, we do not bother to look for them */ + /* during archive write operations */ + int blkalgn; /* writes must be aligned to blkalgn boundry */ + int inhead; /* is the trailer encoded in a valid header? */ + /* if not, trailers are assumed to be found */ + /* in invalid headers (i.e like tar) */ + int (*id)(); /* checks if a buffer is a valid header */ + /* returns 1 if it is, o.w. returns a 0 */ + int (*st_rd)(); /* initialize routine for read. so format */ + /* can set up tables etc before it starts */ + /* reading an archive */ + int (*rd)(); /* read header routine. passed a pointer to */ + /* ARCHD. It must extract the info from the */ + /* format and store it in the ARCHD struct. */ + /* This routine is expected to fill all the */ + /* fields in the ARCHD (including stat buf) */ + /* 0 is returned when a valid header is */ + /* found. -1 when not valid. This routine */ + /* set the skip and pad fields so the format */ + /* independent routines know the amount of */ + /* padding and the number of bytes of data */ + /* which follow the header. This info is */ + /* used skip to the next file header */ + off_t (*end_rd)(); /* read cleanup. Allows format to clean up */ + /* and MUST RETURN THE LENGTH OF THE TRAILER */ + /* RECORD (so append knows how many bytes */ + /* to move back to rewrite the trailer) */ + int (*st_wr)(); /* initialize routine for write operations */ + int (*wr)(); /* write archive header. Passed an ARCHD */ + /* filled with the specs on the next file to */ + /* archived. Returns a 1 if no file data is */ + /* is to be stored; 0 if file data is to be */ + /* added. A -1 is returned if a write */ + /* operation to the archive failed. this */ + /* function sets the skip and pad fields so */ + /* the proper padding can be added after */ + /* file data. This routine must NEVER write */ + /* a flawed archive header. */ + int (*end_wr)(); /* end write. write the trailer and do any */ + /* other format specific functions needed */ + /* at the ecnd of a archive write */ + int (*trail)(); /* returns 0 if a valid trailer, -1 if not */ + /* For formats which encode the trailer */ + /* outside of a valid header, a return value */ + /* of 1 indicates that the block passed to */ + /* it can never contain a valid header (skip */ + /* this block, no point in looking at it) */ + /* CAUTION: parameters to this function are */ + /* different for trailers inside or outside */ + /* of headers. See get_head() for details */ + int (*rd_data)(); /* read/process file data from the archive */ + int (*wr_data)(); /* write/process file data to the archive */ + int (*options)(); /* process format specific options (-o) */ +} FSUB; + +/* + * Pattern matching structure + * + * Used to store command line patterns + */ +typedef struct pattern { + char *pstr; /* pattern to match, user supplied */ + char *pend; /* end of a prefix match */ + int plen; /* length of pstr */ + int flgs; /* processing/state flags */ +#define MTCH 0x1 /* pattern has been matched */ +#define DIR_MTCH 0x2 /* pattern matched a directory */ + struct pattern *fow; /* next pattern */ +} PATTERN; + +/* + * General Archive Structure (used internal to pax) + * + * This structure is used to pass information about archive members between + * the format independent routines and the format specific routines. When + * new archive formats are added, they must accept requests and supply info + * encoded in a structure of this type. The name fields are declared statically + * here, as there is only ONE of these floating around, size is not a major + * consideration. Eventually converting the name fields to a dynamic length + * may be required if and when the supporting operating system removes all + * restrictions on the length of pathnames it will resolve. + */ +typedef struct { + int nlen; /* file name length */ + char name[PAXPATHLEN+1]; /* file name */ + int ln_nlen; /* link name length */ + char ln_name[PAXPATHLEN+1]; /* name to link to (if any) */ + char *org_name; /* orig name in file system */ + PATTERN *pat; /* ptr to pattern match (if any) */ + struct stat sb; /* stat buffer see stat(2) */ + off_t pad; /* bytes of padding after file xfer */ + off_t skip; /* bytes of real data after header */ + /* IMPORTANT. The st_size field does */ + /* not always indicate the amount of */ + /* data following the header. */ + u_long crc; /* file crc */ + int type; /* type of file node */ +#define PAX_DIR 1 /* directory */ +#define PAX_CHR 2 /* character device */ +#define PAX_BLK 3 /* block device */ +#define PAX_REG 4 /* regular file */ +#define PAX_SLK 5 /* symbolic link */ +#define PAX_SCK 6 /* socket */ +#define PAX_FIF 7 /* fifo */ +#define PAX_HLK 8 /* hard link */ +#define PAX_HRG 9 /* hard link to a regular file */ +#define PAX_CTG 10 /* high performance file */ +} ARCHD; + +/* + * Format Specific Options List + * + * Used to pass format options to the format options handler + */ +typedef struct oplist { + char *name; /* option variable name e.g. name= */ + char *value; /* value for option variable */ + struct oplist *fow; /* next option */ +} OPLIST; + +/* + * General Macros + */ +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif +#define MAJOR(x) (((unsigned)(x) >> 8) & 0xff) +#define MINOR(x) ((x) & 0xff) +#define TODEV(x, y) (((unsigned)(x) << 8) | ((unsigned)(y))) + +/* + * General Defines + */ +#define HEX 16 +#define OCT 8 +#define _PAX_ 1 diff --git a/bin/pax/sel_subs.c b/bin/pax/sel_subs.c new file mode 100644 index 0000000..fa7cb85 --- /dev/null +++ b/bin/pax/sel_subs.c @@ -0,0 +1,657 @@ +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * 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[] = "@(#)sel_subs.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <pwd.h> +#include <grp.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <strings.h> +#include <unistd.h> +#include <stdlib.h> +#include "pax.h" +#include "sel_subs.h" +#include "extern.h" + +static int str_sec __P((register char *, time_t *)); +static int usr_match __P((register ARCHD *)); +static int grp_match __P((register ARCHD *)); +static int trng_match __P((register ARCHD *)); + +static TIME_RNG *trhead = NULL; /* time range list head */ +static TIME_RNG *trtail = NULL; /* time range list tail */ +static USRT **usrtb = NULL; /* user selection table */ +static GRPT **grptb = NULL; /* group selection table */ + +/* + * Routines for selection of archive members + */ + +/* + * sel_chk() + * check if this file matches a specfied uid, gid or time range + * Return: + * 0 if this archive member should be processed, 1 if it should be skipped + */ + +#if __STDC__ +int +sel_chk(register ARCHD *arcn) +#else +int +sel_chk(arcn) + register ARCHD *arcn; +#endif +{ + if (((usrtb != NULL) && usr_match(arcn)) || + ((grptb != NULL) && grp_match(arcn)) || + ((trhead != NULL) && trng_match(arcn))) + return(1); + return(0); +} + +/* + * User/group selection routines + * + * Routines to handle user selection of files based on the file uid/gid. To + * add an entry, the user supplies either then name or the uid/gid starting with + * a # on the command line. A \# will eascape the #. + */ + +/* + * usr_add() + * add a user match to the user match hash table + * Return: + * 0 if added ok, -1 otherwise; + */ + +#if __STDC__ +int +usr_add(register char *str) +#else +int +usr_add(str) + register char *str; +#endif +{ + register u_int indx; + register USRT *pt; + register struct passwd *pw; + register uid_t uid; + + /* + * create the table if it doesn't exist + */ + if ((str == NULL) || (*str == '\0')) + return(-1); + if ((usrtb == NULL) && + ((usrtb = (USRT **)calloc(USR_TB_SZ, sizeof(USRT *))) == NULL)) { + warn(1, "Unable to allocate memory for user selection table"); + return(-1); + } + + /* + * figure out user spec + */ + if (str[0] != '#') { + /* + * it is a user name, \# escapes # as first char in user name + */ + if ((str[0] == '\\') && (str[1] == '#')) + ++str; + if ((pw = getpwnam(str)) == NULL) { + warn(1, "Unable to find uid for user: %s", str); + return(-1); + } + uid = (uid_t)pw->pw_uid; + } else +# ifdef NET2_STAT + uid = (uid_t)atoi(str+1); +# else + uid = (uid_t)strtoul(str+1, (char **)NULL, 10); +# endif + endpwent(); + + /* + * hash it and go down the hash chain (if any) looking for it + */ + indx = ((unsigned)uid) % USR_TB_SZ; + if ((pt = usrtb[indx]) != NULL) { + while (pt != NULL) { + if (pt->uid == uid) + return(0); + pt = pt->fow; + } + } + + /* + * uid is not yet in the table, add it to the front of the chain + */ + if ((pt = (USRT *)malloc(sizeof(USRT))) != NULL) { + pt->uid = uid; + pt->fow = usrtb[indx]; + usrtb[indx] = pt; + return(0); + } + warn(1, "User selection table out of memory"); + return(-1); +} + +/* + * usr_match() + * check if this files uid matches a selected uid. + * Return: + * 0 if this archive member should be processed, 1 if it should be skipped + */ + +#if __STDC__ +static int +usr_match(register ARCHD *arcn) +#else +static int +usr_match(arcn) + register ARCHD *arcn; +#endif +{ + register USRT *pt; + + /* + * hash and look for it in the table + */ + pt = usrtb[((unsigned)arcn->sb.st_uid) % USR_TB_SZ]; + while (pt != NULL) { + if (pt->uid == arcn->sb.st_uid) + return(0); + pt = pt->fow; + } + + /* + * not found + */ + return(1); +} + +/* + * grp_add() + * add a group match to the group match hash table + * Return: + * 0 if added ok, -1 otherwise; + */ + +#if __STDC__ +int +grp_add(register char *str) +#else +int +grp_add(str) + register char *str; +#endif +{ + register u_int indx; + register GRPT *pt; + register struct group *gr; + register gid_t gid; + + /* + * create the table if it doesn't exist + */ + if ((str == NULL) || (*str == '\0')) + return(-1); + if ((grptb == NULL) && + ((grptb = (GRPT **)calloc(GRP_TB_SZ, sizeof(GRPT *))) == NULL)) { + warn(1, "Unable to allocate memory fo group selection table"); + return(-1); + } + + /* + * figure out user spec + */ + if (str[0] != '#') { + /* + * it is a group name, \# escapes # as first char in group name + */ + if ((str[0] == '\\') && (str[1] == '#')) + ++str; + if ((gr = getgrnam(str)) == NULL) { + warn(1,"Cannot determine gid for group name: %s", str); + return(-1); + } + gid = (gid_t)gr->gr_gid; + } else +# ifdef NET2_STAT + gid = (gid_t)atoi(str+1); +# else + gid = (gid_t)strtoul(str+1, (char **)NULL, 10); +# endif + endgrent(); + + /* + * hash it and go down the hash chain (if any) looking for it + */ + indx = ((unsigned)gid) % GRP_TB_SZ; + if ((pt = grptb[indx]) != NULL) { + while (pt != NULL) { + if (pt->gid == gid) + return(0); + pt = pt->fow; + } + } + + /* + * gid not in the table, add it to the front of the chain + */ + if ((pt = (GRPT *)malloc(sizeof(GRPT))) != NULL) { + pt->gid = gid; + pt->fow = grptb[indx]; + grptb[indx] = pt; + return(0); + } + warn(1, "Group selection table out of memory"); + return(-1); +} + +/* + * grp_match() + * check if this files gid matches a selected gid. + * Return: + * 0 if this archive member should be processed, 1 if it should be skipped + */ + +#if __STDC__ +static int +grp_match(register ARCHD *arcn) +#else +static int +grp_match(arcn) + register ARCHD *arcn; +#endif +{ + register GRPT *pt; + + /* + * hash and look for it in the table + */ + pt = grptb[((unsigned)arcn->sb.st_gid) % GRP_TB_SZ]; + while (pt != NULL) { + if (pt->gid == arcn->sb.st_gid) + return(0); + pt = pt->fow; + } + + /* + * not found + */ + return(1); +} + +/* + * Time range selection routines + * + * Routines to handle user selection of files based on the modification and/or + * inode change time falling within a specified time range (the non-standard + * -T flag). The user may specify any number of different file time ranges. + * Time ranges are checked one at a time until a match is found (if at all). + * If the file has a mtime (and/or ctime) which lies within one of the time + * ranges, the file is selected. Time ranges may have a lower and/or a upper + * value. These ranges are inclusive. When no time ranges are supplied to pax + * with the -T option, all members in the archive will be selected by the time + * range routines. When only a lower range is supplied, only files with a + * mtime (and/or ctime) equal to or younger are selected. When only a upper + * range is supplied, only files with a mtime (and/or ctime) equal to or older + * are selected. When the lower time range is equal to the upper time range, + * only files with a mtime (or ctime) of exactly that time are selected. + */ + +/* + * trng_add() + * add a time range match to the time range list. + * This is a non-standard pax option. Lower and upper ranges are in the + * format: [yy[mm[dd[hh]]]]mm[.ss] and are comma separated. + * Time ranges are based on current time, so 1234 would specify a time of + * 12:34 today. + * Return: + * 0 if the time range was added to the list, -1 otherwise + */ + +#if __STDC__ +int +trng_add(register char *str) +#else +int +trng_add(str) + register char *str; +#endif +{ + register TIME_RNG *pt; + register char *up_pt = NULL; + register char *stpt; + register char *flgpt; + register int dot = 0; + + /* + * throw out the badly formed time ranges + */ + if ((str == NULL) || (*str == '\0')) { + warn(1, "Empty time range string"); + return(-1); + } + + /* + * locate optional flags suffix /{cm}. + */ + if ((flgpt = rindex(str, '/')) != NULL) + *flgpt++ = '\0'; + + for (stpt = str; *stpt != '\0'; ++stpt) { + if ((*stpt >= '0') && (*stpt <= '9')) + continue; + if ((*stpt == ',') && (up_pt == NULL)) { + *stpt = '\0'; + up_pt = stpt + 1; + dot = 0; + continue; + } + + /* + * allow only one dot per range (secs) + */ + if ((*stpt == '.') && (!dot)) { + ++dot; + continue; + } + warn(1, "Improperly specified time range: %s", str); + goto out; + } + + /* + * allocate space for the time range and store the limits + */ + if ((pt = (TIME_RNG *)malloc(sizeof(TIME_RNG))) == NULL) { + warn(1, "Unable to allocate memory for time range"); + return(-1); + } + + /* + * by default we only will check file mtime, but usee can specify + * mtime, ctime (inode change time) or both. + */ + if ((flgpt == NULL) || (*flgpt == '\0')) + pt->flgs = CMPMTME; + else { + pt->flgs = 0; + while (*flgpt != '\0') { + switch(*flgpt) { + case 'M': + case 'm': + pt->flgs |= CMPMTME; + break; + case 'C': + case 'c': + pt->flgs |= CMPCTME; + break; + default: + warn(1, "Bad option %c with time range %s", + *flgpt, str); + goto out; + } + ++flgpt; + } + } + + /* + * start off with the current time + */ + pt->low_time = pt->high_time = time((time_t *)NULL); + if (*str != '\0') { + /* + * add lower limit + */ + if (str_sec(str, &(pt->low_time)) < 0) { + warn(1, "Illegal lower time range %s", str); + (void)free((char *)pt); + goto out; + } + pt->flgs |= HASLOW; + } + + if ((up_pt != NULL) && (*up_pt != '\0')) { + /* + * add upper limit + */ + if (str_sec(up_pt, &(pt->high_time)) < 0) { + warn(1, "Illegal upper time range %s", up_pt); + (void)free((char *)pt); + goto out; + } + pt->flgs |= HASHIGH; + + /* + * check that the upper and lower do not overlap + */ + if (pt->flgs & HASLOW) { + if (pt->low_time > pt->high_time) { + warn(1, "Upper %s and lower %s time overlap", + up_pt, str); + (void)free((char *)pt); + return(-1); + } + } + } + + pt->fow = NULL; + if (trhead == NULL) { + trtail = trhead = pt; + return(0); + } + trtail->fow = pt; + trtail = pt; + return(0); + + out: + warn(1, "Time range format is: [yy[mm[dd[hh]]]]mm[.ss][/[c][m]]"); + return(-1); +} + +/* + * trng_match() + * check if this files mtime/ctime falls within any supplied time range. + * Return: + * 0 if this archive member should be processed, 1 if it should be skipped + */ + +#if __STDC__ +static int +trng_match(register ARCHD *arcn) +#else +static int +trng_match(arcn) + register ARCHD *arcn; +#endif +{ + register TIME_RNG *pt; + + /* + * have to search down the list one at a time looking for a match. + * remember time range limits are inclusive. + */ + pt = trhead; + while (pt != NULL) { + switch(pt->flgs & CMPBOTH) { + case CMPBOTH: + /* + * user wants both mtime and ctime checked for this + * time range + */ + if (((pt->flgs & HASLOW) && + (arcn->sb.st_mtime < pt->low_time) && + (arcn->sb.st_ctime < pt->low_time)) || + ((pt->flgs & HASHIGH) && + (arcn->sb.st_mtime > pt->high_time) && + (arcn->sb.st_ctime > pt->high_time))) { + pt = pt->fow; + continue; + } + break; + case CMPCTME: + /* + * user wants only ctime checked for this time range + */ + if (((pt->flgs & HASLOW) && + (arcn->sb.st_ctime < pt->low_time)) || + ((pt->flgs & HASHIGH) && + (arcn->sb.st_ctime > pt->high_time))) { + pt = pt->fow; + continue; + } + break; + case CMPMTME: + default: + /* + * user wants only mtime checked for this time range + */ + if (((pt->flgs & HASLOW) && + (arcn->sb.st_mtime < pt->low_time)) || + ((pt->flgs & HASHIGH) && + (arcn->sb.st_mtime > pt->high_time))) { + pt = pt->fow; + continue; + } + break; + } + break; + } + + if (pt == NULL) + return(1); + return(0); +} + +/* + * str_sec() + * Convert a time string in the format of [yy[mm[dd[hh]]]]mm[.ss] to gmt + * seconds. Tval already has current time loaded into it at entry. + * Return: + * 0 if converted ok, -1 otherwise + */ + +#if __STDC__ +static int +str_sec(register char *str, time_t *tval) +#else +static int +str_sec(str, tval) + register char *str; + time_t *tval; +#endif +{ + register struct tm *lt; + register char *dot = NULL; + + lt = localtime(tval); + if ((dot = index(str, '.')) != NULL) { + /* + * seconds (.ss) + */ + *dot++ = '\0'; + if (strlen(dot) != 2) + return(-1); + if ((lt->tm_sec = ATOI2(dot)) > 61) + return(-1); + } else + lt->tm_sec = 0; + + switch (strlen(str)) { + case 10: + /* + * year (yy) + * watch out for year 2000 + */ + if ((lt->tm_year = ATOI2(str)) < 69) + lt->tm_year += 100; + str += 2; + /* FALLTHROUGH */ + case 8: + /* + * month (mm) + * watch out months are from 0 - 11 internally + */ + if ((lt->tm_mon = ATOI2(str)) > 12) + return(-1); + --lt->tm_mon; + str += 2; + /* FALLTHROUGH */ + case 6: + /* + * day (dd) + */ + if ((lt->tm_mday = ATOI2(str)) > 31) + return(-1); + str += 2; + /* FALLTHROUGH */ + case 4: + /* + * hour (hh) + */ + if ((lt->tm_hour = ATOI2(str)) > 23) + return(-1); + str += 2; + /* FALLTHROUGH */ + case 2: + /* + * minute (mm) + */ + if ((lt->tm_min = ATOI2(str)) > 59) + return(-1); + break; + default: + return(-1); + } + /* + * convert broken-down time to GMT clock time seconds + */ + if ((*tval = mktime(lt)) == -1) + return(-1); + return(0); +} diff --git a/bin/pax/sel_subs.h b/bin/pax/sel_subs.h new file mode 100644 index 0000000..5040c73 --- /dev/null +++ b/bin/pax/sel_subs.h @@ -0,0 +1,73 @@ +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * 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. + * + * @(#)sel_subs.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * data structure for storing uid/grp selects (-U, -G non standard options) + */ + +#define USR_TB_SZ 317 /* user selection table size */ +#define GRP_TB_SZ 317 /* user selection table size */ + +typedef struct usrt { + uid_t uid; + struct usrt *fow; /* next uid */ +} USRT; + +typedef struct grpt { + gid_t gid; + struct grpt *fow; /* next gid */ +} GRPT; + +/* + * data structure for storing user supplied time ranges (-T option) + */ + +#define ATOI2(s) ((((s)[0] - '0') * 10) + ((s)[1] - '0')) + +typedef struct time_rng { + time_t low_time; /* lower inclusive time limit */ + time_t high_time; /* higher inclusive time limit */ + int flgs; /* option flags */ +#define HASLOW 0x01 /* has lower time limit */ +#define HASHIGH 0x02 /* has higher time limit */ +#define CMPMTME 0x04 /* compare file modification time */ +#define CMPCTME 0x08 /* compare inode change time */ +#define CMPBOTH (CMPMTME|CMPCTME) /* compare inode and mod time */ + struct time_rng *fow; /* next pattern */ +} TIME_RNG; diff --git a/bin/pax/tables.c b/bin/pax/tables.c new file mode 100644 index 0000000..3ac1c41 --- /dev/null +++ b/bin/pax/tables.c @@ -0,0 +1,1426 @@ +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * 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[] = "@(#)tables.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <sys/fcntl.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> +#include "pax.h" +#include "tables.h" +#include "extern.h" + +/* + * Routines for controlling the contents of all the different databases pax + * keeps. Tables are dynamically created only when they are needed. The + * goal was speed and the ability to work with HUGE archives. The databases + * were kept simple, but do have complex rules for when the contents change. + * As of this writing, the posix library functions were more complex than + * needed for this application (pax databases have very short lifetimes and + * do not survive after pax is finished). Pax is required to handle very + * large archives. These database routines carefully combine memory usage and + * temporary file storage in ways which will not significantly impact runtime + * performance while allowing the largest possible archives to be handled. + * Trying to force the fit to the posix databases routines was not considered + * time well spent. + */ + +static HRDLNK **ltab = NULL; /* hard link table for detecting hard links */ +static FTM **ftab = NULL; /* file time table for updating arch */ +static NAMT **ntab = NULL; /* interactive rename storage table */ +static DEVT **dtab = NULL; /* device/inode mapping tables */ +static ATDIR **atab = NULL; /* file tree directory time reset table */ +static int dirfd = -1; /* storage for setting created dir time/mode */ +static u_long dircnt; /* entries in dir time/mode storage */ +static int ffd = -1; /* tmp file for file time table name storage */ + +static DEVT *chk_dev __P((dev_t, int)); + +/* + * hard link table routines + * + * The hard link table tries to detect hard links to files using the device and + * inode values. We do this when writing an archive, so we can tell the format + * write routine that this file is a hard link to another file. The format + * write routine then can store this file in whatever way it wants (as a hard + * link if the format supports that like tar, or ignore this info like cpio). + * (Actually a field in the format driver table tells us if the format wants + * hard link info. if not, we do not waste time looking for them). We also use + * the same table when reading an archive. In that situation, this table is + * used by the format read routine to detect hard links from stored dev and + * inode numbers (like cpio). This will allow pax to create a link when one + * can be detected by the archive format. + */ + +/* + * lnk_start + * Creates the hard link table. + * Return: + * 0 if created, -1 if failure + */ + +#if __STDC__ +int +lnk_start(void) +#else +int +lnk_start() +#endif +{ + if (ltab != NULL) + return(0); + if ((ltab = (HRDLNK **)calloc(L_TAB_SZ, sizeof(HRDLNK *))) == NULL) { + warn(1, "Cannot allocate memory for hard link table"); + return(-1); + } + return(0); +} + +/* + * chk_lnk() + * Looks up entry in hard link hash table. If found, it copies the name + * of the file it is linked to (we already saw that file) into ln_name. + * lnkcnt is decremented and if goes to 1 the node is deleted from the + * database. (We have seen all the links to this file). If not found, + * we add the file to the database if it has the potential for having + * hard links to other files we may process (it has a link count > 1) + * Return: + * if found returns 1; if not found returns 0; -1 on error + */ + +#if __STDC__ +int +chk_lnk(register ARCHD *arcn) +#else +int +chk_lnk(arcn) + register ARCHD *arcn; +#endif +{ + register HRDLNK *pt; + register HRDLNK **ppt; + register u_int indx; + + if (ltab == NULL) + return(-1); + /* + * ignore those nodes that cannot have hard links + */ + if ((arcn->type == PAX_DIR) || (arcn->sb.st_nlink <= 1)) + return(0); + + /* + * hash inode number and look for this file + */ + indx = ((unsigned)arcn->sb.st_ino) % L_TAB_SZ; + if ((pt = ltab[indx]) != NULL) { + /* + * it's hash chain in not empty, walk down looking for it + */ + ppt = &(ltab[indx]); + while (pt != NULL) { + if ((pt->ino == arcn->sb.st_ino) && + (pt->dev == arcn->sb.st_dev)) + break; + ppt = &(pt->fow); + pt = pt->fow; + } + + if (pt != NULL) { + /* + * found a link. set the node type and copy in the + * name of the file it is to link to. we need to + * handle hardlinks to regular files differently than + * other links. + */ + arcn->ln_nlen = l_strncpy(arcn->ln_name, pt->name, + PAXPATHLEN+1); + if (arcn->type == PAX_REG) + arcn->type = PAX_HRG; + else + arcn->type = PAX_HLK; + + /* + * if we have found all the links to this file, remove + * it from the database + */ + if (--pt->nlink <= 1) { + *ppt = pt->fow; + (void)free((char *)pt->name); + (void)free((char *)pt); + } + return(1); + } + } + + /* + * we never saw this file before. It has links so we add it to the + * front of this hash chain + */ + if ((pt = (HRDLNK *)malloc(sizeof(HRDLNK))) != NULL) { + if ((pt->name = strdup(arcn->name)) != NULL) { + pt->dev = arcn->sb.st_dev; + pt->ino = arcn->sb.st_ino; + pt->nlink = arcn->sb.st_nlink; + pt->fow = ltab[indx]; + ltab[indx] = pt; + return(0); + } + (void)free((char *)pt); + } + + warn(1, "Hard link table out of memory"); + return(-1); +} + +/* + * purg_lnk + * remove reference for a file that we may have added to the data base as + * a potential source for hard links. We ended up not using the file, so + * we do not want to accidently point another file at it later on. + */ + +#if __STDC__ +void +purg_lnk(register ARCHD *arcn) +#else +void +purg_lnk(arcn) + register ARCHD *arcn; +#endif +{ + register HRDLNK *pt; + register HRDLNK **ppt; + register u_int indx; + + if (ltab == NULL) + return; + /* + * do not bother to look if it could not be in the database + */ + if ((arcn->sb.st_nlink <= 1) || (arcn->type == PAX_DIR) || + (arcn->type == PAX_HLK) || (arcn->type == PAX_HRG)) + return; + + /* + * find the hash chain for this inode value, if empty return + */ + indx = ((unsigned)arcn->sb.st_ino) % L_TAB_SZ; + if ((pt = ltab[indx]) == NULL) + return; + + /* + * walk down the list looking for the inode/dev pair, unlink and + * free if found + */ + ppt = &(ltab[indx]); + while (pt != NULL) { + if ((pt->ino == arcn->sb.st_ino) && + (pt->dev == arcn->sb.st_dev)) + break; + ppt = &(pt->fow); + pt = pt->fow; + } + if (pt == NULL) + return; + + /* + * remove and free it + */ + *ppt = pt->fow; + (void)free((char *)pt->name); + (void)free((char *)pt); +} + +/* + * lnk_end() + * pull apart a existing link table so we can reuse it. We do this between + * read and write phases of append with update. (The format may have + * used the link table, and we need to start with a fresh table for the + * write phase + */ + +#if __STDC__ +void +lnk_end(void) +#else +void +lnk_end() +#endif +{ + register int i; + register HRDLNK *pt; + register HRDLNK *ppt; + + if (ltab == NULL) + return; + + for (i = 0; i < L_TAB_SZ; ++i) { + if (ltab[i] == NULL) + continue; + pt = ltab[i]; + ltab[i] = NULL; + + /* + * free up each entry on this chain + */ + while (pt != NULL) { + ppt = pt; + pt = ppt->fow; + (void)free((char *)ppt->name); + (void)free((char *)ppt); + } + } + return; +} + +/* + * modification time table routines + * + * The modification time table keeps track of last modification times for all + * files stored in an archive during a write phase when -u is set. We only + * add a file to the archive if it is newer than a file with the same name + * already stored on the archive (if there is no other file with the same + * name on the archive it is added). This applies to writes and appends. + * An append with an -u must read the archive and store the modification time + * for every file on that archive before starting the write phase. It is clear + * that this is one HUGE database. To save memory space, the actual file names + * are stored in a scatch file and indexed by an in memory hash table. The + * hash table is indexed by hashing the file path. The nodes in the table store + * the length of the filename and the lseek offset within the scratch file + * where the actual name is stored. Since there are never any deletions to this + * table, fragmentation of the scratch file is never a issue. Lookups seem to + * not exhibit any locality at all (files in the database are rarely + * looked up more than once...). So caching is just a waste of memory. The + * only limitation is the amount of scatch file space available to store the + * path names. + */ + +/* + * ftime_start() + * create the file time hash table and open for read/write the scratch + * file. (after created it is unlinked, so when we exit we leave + * no witnesses). + * Return: + * 0 if the table and file was created ok, -1 otherwise + */ + +#if __STDC__ +int +ftime_start(void) +#else +int +ftime_start() +#endif +{ + char *pt; + + if (ftab != NULL) + return(0); + if ((ftab = (FTM **)calloc(F_TAB_SZ, sizeof(FTM *))) == NULL) { + warn(1, "Cannot allocate memory for file time table"); + return(-1); + } + + /* + * get random name and create temporary scratch file, unlink name + * so it will get removed on exit + */ + if ((pt = tempnam((char *)NULL, (char *)NULL)) == NULL) + return(-1); + (void)unlink(pt); + + if ((ffd = open(pt, O_RDWR | O_CREAT, S_IRWXU)) < 0) { + syswarn(1, errno, "Unable to open temporary file: %s", pt); + return(-1); + } + + (void)unlink(pt); + return(0); +} + +/* + * chk_ftime() + * looks up entry in file time hash table. If not found, the file is + * added to the hash table and the file named stored in the scratch file. + * If a file with the same name is found, the file times are compared and + * the most recent file time is retained. If the new file was younger (or + * was not in the database) the new file is selected for storage. + * Return: + * 0 if file should be added to the archive, 1 if it should be skipped, + * -1 on error + */ + +#if __STDC__ +int +chk_ftime(register ARCHD *arcn) +#else +int +chk_ftime(arcn) + register ARCHD *arcn; +#endif +{ + register FTM *pt; + register int namelen; + register u_int indx; + char ckname[PAXPATHLEN+1]; + + /* + * no info, go ahead and add to archive + */ + if (ftab == NULL) + return(0); + + /* + * hash the pathname and look up in table + */ + namelen = arcn->nlen; + indx = st_hash(arcn->name, namelen, F_TAB_SZ); + if ((pt = ftab[indx]) != NULL) { + /* + * the hash chain is not empty, walk down looking for match + * only read up the path names if the lengths match, speeds + * up the search a lot + */ + while (pt != NULL) { + if (pt->namelen == namelen) { + /* + * potential match, have to read the name + * from the scratch file. + */ + if (lseek(ffd,pt->seek,SEEK_SET) != pt->seek) { + syswarn(1, errno, + "Failed ftime table seek"); + return(-1); + } + if (read(ffd, ckname, namelen) != namelen) { + syswarn(1, errno, + "Failed ftime table read"); + return(-1); + } + + /* + * if the names match, we are done + */ + if (!strncmp(ckname, arcn->name, namelen)) + break; + } + + /* + * try the next entry on the chain + */ + pt = pt->fow; + } + + if (pt != NULL) { + /* + * found the file, compare the times, save the newer + */ + if (arcn->sb.st_mtime > pt->mtime) { + /* + * file is newer + */ + pt->mtime = arcn->sb.st_mtime; + return(0); + } + /* + * file is older + */ + return(1); + } + } + + /* + * not in table, add it + */ + if ((pt = (FTM *)malloc(sizeof(FTM))) != NULL) { + /* + * add the name at the end of the scratch file, saving the + * offset. add the file to the head of the hash chain + */ + if ((pt->seek = lseek(ffd, (off_t)0, SEEK_END)) >= 0) { + if (write(ffd, arcn->name, namelen) == namelen) { + pt->mtime = arcn->sb.st_mtime; + pt->namelen = namelen; + pt->fow = ftab[indx]; + ftab[indx] = pt; + return(0); + } + syswarn(1, errno, "Failed write to file time table"); + } else + syswarn(1, errno, "Failed seek on file time table"); + } else + warn(1, "File time table ran out of memory"); + + if (pt != NULL) + (void)free((char *)pt); + return(-1); +} + +/* + * Interactive rename table routines + * + * The interactive rename table keeps track of the new names that the user + * assignes to files from tty input. Since this map is unique for each file + * we must store it in case there is a reference to the file later in archive + * (a link). Otherwise we will be unable to find the file we know was + * extracted. The remapping of these files is stored in a memory based hash + * table (it is assumed since input must come from /dev/tty, it is unlikely to + * be a very large table). + */ + +/* + * name_start() + * create the interactive rename table + * Return: + * 0 if successful, -1 otherwise + */ + +#if __STDC__ +int +name_start(void) +#else +int +name_start() +#endif +{ + if (ntab != NULL) + return(0); + if ((ntab = (NAMT **)calloc(N_TAB_SZ, sizeof(NAMT *))) == NULL) { + warn(1, "Cannot allocate memory for interactive rename table"); + return(-1); + } + return(0); +} + +/* + * add_name() + * add the new name to old name mapping just created by the user. + * If an old name mapping is found (there may be duplicate names on an + * archive) only the most recent is kept. + * Return: + * 0 if added, -1 otherwise + */ + +#if __STDC__ +int +add_name(register char *oname, int onamelen, char *nname) +#else +int +add_name(oname, onamelen, nname) + register char *oname; + int onamelen; + char *nname; +#endif +{ + register NAMT *pt; + register u_int indx; + + if (ntab == NULL) { + /* + * should never happen + */ + warn(0, "No interactive rename table, links may fail\n"); + return(0); + } + + /* + * look to see if we have already mapped this file, if so we + * will update it + */ + indx = st_hash(oname, onamelen, N_TAB_SZ); + if ((pt = ntab[indx]) != NULL) { + /* + * look down the has chain for the file + */ + while ((pt != NULL) && (strcmp(oname, pt->oname) != 0)) + pt = pt->fow; + + if (pt != NULL) { + /* + * found an old mapping, replace it with the new one + * the user just input (if it is different) + */ + if (strcmp(nname, pt->nname) == 0) + return(0); + + (void)free((char *)pt->nname); + if ((pt->nname = strdup(nname)) == NULL) { + warn(1, "Cannot update rename table"); + return(-1); + } + return(0); + } + } + + /* + * this is a new mapping, add it to the table + */ + if ((pt = (NAMT *)malloc(sizeof(NAMT))) != NULL) { + if ((pt->oname = strdup(oname)) != NULL) { + if ((pt->nname = strdup(nname)) != NULL) { + pt->fow = ntab[indx]; + ntab[indx] = pt; + return(0); + } + (void)free((char *)pt->oname); + } + (void)free((char *)pt); + } + warn(1, "Interactive rename table out of memory"); + return(-1); +} + +/* + * sub_name() + * look up a link name to see if it points at a file that has been + * remapped by the user. If found, the link is adjusted to contain the + * new name (oname is the link to name) + */ + +#if __STDC__ +void +sub_name(register char *oname, int *onamelen) +#else +void +sub_name(oname, onamelen) + register char *oname; + int *onamelen; +#endif +{ + register NAMT *pt; + register u_int indx; + + if (ntab == NULL) + return; + /* + * look the name up in the hash table + */ + indx = st_hash(oname, *onamelen, N_TAB_SZ); + if ((pt = ntab[indx]) == NULL) + return; + + while (pt != NULL) { + /* + * walk down the hash cahin looking for a match + */ + if (strcmp(oname, pt->oname) == 0) { + /* + * found it, replace it with the new name + * and return (we know that oname has enough space) + */ + *onamelen = l_strncpy(oname, pt->nname, PAXPATHLEN+1); + return; + } + pt = pt->fow; + } + + /* + * no match, just return + */ + return; +} + +/* + * device/inode mapping table routines + * (used with formats that store device and inodes fields) + * + * device/inode mapping tables remap the device field in a archive header. The + * device/inode fields are used to determine when files are hard links to each + * other. However these values have very little meaning outside of that. This + * database is used to solve one of two different problems. + * + * 1) when files are appended to an archive, while the new files may have hard + * links to each other, you cannot determine if they have hard links to any + * file already stored on the archive from a prior run of pax. We must assume + * that these inode/device pairs are unique only within a SINGLE run of pax + * (which adds a set of files to an archive). So we have to make sure the + * inode/dev pairs we add each time are always unique. We do this by observing + * while the inode field is very dense, the use of the dev field is fairly + * sparse. Within each run of pax, we remap any device number of a new archive + * member that has a device number used in a prior run and already stored in a + * file on the archive. During the read phase of the append, we store the + * device numbers used and mark them to not be used by any file during the + * write phase. If during write we go to use one of those old device numbers, + * we remap it to a new value. + * + * 2) Often the fields in the archive header used to store these values are + * too small to store the entire value. The result is an inode or device value + * which can be truncated. This really can foul up an archive. With truncation + * we end up creating links between files that are really not links (after + * truncation the inodes are the same value). We address that by detecting + * truncation and forcing a remap of the device field to split truncated + * inodes away from each other. Each truncation creates a pattern of bits that + * are removed. We use this pattern of truncated bits to partition the inodes + * on a single device to many different devices (each one represented by the + * truncated bit pattern). All inodes on the same device that have the same + * truncation pattern are mapped to the same new device. Two inodes that + * truncate to the same value clearly will always have different truncation + * bit patterns, so they will be split from away each other. When we spot + * device truncation we remap the device number to a non truncated value. + * (for more info see table.h for the data structures involved). + */ + +/* + * dev_start() + * create the device mapping table + * Return: + * 0 if successful, -1 otherwise + */ + +#if __STDC__ +int +dev_start(void) +#else +int +dev_start() +#endif +{ + if (dtab != NULL) + return(0); + if ((dtab = (DEVT **)calloc(D_TAB_SZ, sizeof(DEVT *))) == NULL) { + warn(1, "Cannot allocate memory for device mapping table"); + return(-1); + } + return(0); +} + +/* + * add_dev() + * add a device number to the table. this will force the device to be + * remapped to a new value if it be used during a write phase. This + * function is called during the read phase of an append to prohibit the + * use of any device number already in the archive. + * Return: + * 0 if added ok, -1 otherwise + */ + +#if __STDC__ +int +add_dev(register ARCHD *arcn) +#else +int +add_dev(arcn) + register ARCHD *arcn; +#endif +{ + if (chk_dev(arcn->sb.st_dev, 1) == NULL) + return(-1); + return(0); +} + +/* + * chk_dev() + * check for a device value in the device table. If not found and the add + * flag is set, it is added. This does NOT assign any mapping values, just + * adds the device number as one that need to be remapped. If this device + * is alread mapped, just return with a pointer to that entry. + * Return: + * pointer to the entry for this device in the device map table. Null + * if the add flag is not set and the device is not in the table (it is + * not been seen yet). If add is set and the device cannot be added, null + * is returned (indicates an error). + */ + +#if __STDC__ +static DEVT * +chk_dev(dev_t dev, int add) +#else +static DEVT * +chk_dev(dev, add) + dev_t dev; + int add; +#endif +{ + register DEVT *pt; + register u_int indx; + + if (dtab == NULL) + return(NULL); + /* + * look to see if this device is already in the table + */ + indx = ((unsigned)dev) % D_TAB_SZ; + if ((pt = dtab[indx]) != NULL) { + while ((pt != NULL) && (pt->dev != dev)) + pt = pt->fow; + + /* + * found it, return a pointer to it + */ + if (pt != NULL) + return(pt); + } + + /* + * not in table, we add it only if told to as this may just be a check + * to see if a device number is being used. + */ + if (add == 0) + return(NULL); + + /* + * allocate a node for this device and add it to the front of the hash + * chain. Note we do not assign remaps values here, so the pt->list + * list must be NULL. + */ + if ((pt = (DEVT *)malloc(sizeof(DEVT))) == NULL) { + warn(1, "Device map table out of memory"); + return(NULL); + } + pt->dev = dev; + pt->list = NULL; + pt->fow = dtab[indx]; + dtab[indx] = pt; + return(pt); +} +/* + * map_dev() + * given an inode and device storage mask (the mask has a 1 for each bit + * the archive format is able to store in a header), we check for inode + * and device truncation and remap the device as required. Device mapping + * can also occur when during the read phase of append a device number was + * seen (and was marked as do not use during the write phase). WE ASSUME + * that unsigned longs are the same size or bigger than the fields used + * for ino_t and dev_t. If not the types will have to be changed. + * Return: + * 0 if all ok, -1 otherwise. + */ + +#if __STDC__ +int +map_dev(register ARCHD *arcn, u_long dev_mask, u_long ino_mask) +#else +int +map_dev(arcn, dev_mask, ino_mask) + register ARCHD *arcn; + u_long dev_mask; + u_long ino_mask; +#endif +{ + register DEVT *pt; + register DLIST *dpt; + static dev_t lastdev = 0; /* next device number to try */ + int trc_ino = 0; + int trc_dev = 0; + ino_t trunc_bits = 0; + ino_t nino; + + if (dtab == NULL) + return(0); + /* + * check for device and inode truncation, and extract the truncated + * bit pattern. + */ + if ((arcn->sb.st_dev & (dev_t)dev_mask) != arcn->sb.st_dev) + ++trc_dev; + if ((nino = arcn->sb.st_ino & (ino_t)ino_mask) != arcn->sb.st_ino) { + ++trc_ino; + trunc_bits = arcn->sb.st_ino & (ino_t)(~ino_mask); + } + + /* + * see if this device is already being mapped, look up the device + * then find the truncation bit pattern which applies + */ + if ((pt = chk_dev(arcn->sb.st_dev, 0)) != NULL) { + /* + * this device is already marked to be remapped + */ + for (dpt = pt->list; dpt != NULL; dpt = dpt->fow) + if (dpt->trunc_bits == trunc_bits) + break; + + if (dpt != NULL) { + /* + * we are being remapped for this device and pattern + * change the device number to be stored and return + */ + arcn->sb.st_dev = dpt->dev; + arcn->sb.st_ino = nino; + return(0); + } + } else { + /* + * this device is not being remapped YET. if we do not have any + * form of truncation, we do not need a remap + */ + if (!trc_ino && !trc_dev) + return(0); + + /* + * we have truncation, have to add this as a device to remap + */ + if ((pt = chk_dev(arcn->sb.st_dev, 1)) == NULL) + goto bad; + + /* + * if we just have a truncated inode, we have to make sure that + * all future inodes that do not truncate (they have the + * truncation pattern of all 0's) continue to map to the same + * device number. We probably have already written inodes with + * this device number to the archive with the truncation + * pattern of all 0's. So we add the mapping for all 0's to the + * same device number. + */ + if (!trc_dev && (trunc_bits != 0)) { + if ((dpt = (DLIST *)malloc(sizeof(DLIST))) == NULL) + goto bad; + dpt->trunc_bits = 0; + dpt->dev = arcn->sb.st_dev; + dpt->fow = pt->list; + pt->list = dpt; + } + } + + /* + * look for a device number not being used. We must watch for wrap + * around on lastdev (so we do not get stuck looking forever!) + */ + while (++lastdev > 0) { + if (chk_dev(lastdev, 0) != NULL) + continue; + /* + * found an unused value. If we have reached truncation point + * for this format we are hosed, so we give up. Otherwise we + * mark it as being used. + */ + if (((lastdev & ((dev_t)dev_mask)) != lastdev) || + (chk_dev(lastdev, 1) == NULL)) + goto bad; + break; + } + + if ((lastdev <= 0) || ((dpt = (DLIST *)malloc(sizeof(DLIST))) == NULL)) + goto bad; + + /* + * got a new device number, store it under this truncation pattern. + * change the device number this file is being stored with. + */ + dpt->trunc_bits = trunc_bits; + dpt->dev = lastdev; + dpt->fow = pt->list; + pt->list = dpt; + arcn->sb.st_dev = lastdev; + arcn->sb.st_ino = nino; + return(0); + + bad: + warn(1, "Unable to fix truncated inode/device field when storing %s", + arcn->name); + warn(0, "Archive may create improper hard links when extracted"); + return(0); +} + +/* + * directory access/mod time reset table routines (for directories READ by pax) + * + * The pax -t flag requires that access times of archive files to be the same + * before being read by pax. For regular files, access time is restored after + * the file has been copied. This database provides the same functionality for + * directories read during file tree traversal. Restoring directory access time + * is more complex than files since directories may be read several times until + * all the descendants in their subtree are visited by fts. Directory access + * and modification times are stored during the fts pre-order visit (done + * before any descendants in the subtree is visited) and restored after the + * fts post-order visit (after all the descendants have been visited). In the + * case of premature exit from a subtree (like from the effects of -n), any + * directory entries left in this database are reset during final cleanup + * operations of pax. Entries are hashed by inode number for fast lookup. + */ + +/* + * atdir_start() + * create the directory access time database for directories READ by pax. + * Return: + * 0 is created ok, -1 otherwise. + */ + +#if __STDC__ +int +atdir_start(void) +#else +int +atdir_start() +#endif +{ + if (atab != NULL) + return(0); + if ((atab = (ATDIR **)calloc(A_TAB_SZ, sizeof(ATDIR *))) == NULL) { + warn(1,"Cannot allocate space for directory access time table"); + return(-1); + } + return(0); +} + + +/* + * atdir_end() + * walk through the directory access time table and reset the access time + * of any directory who still has an entry left in the database. These + * entries are for directories READ by pax + */ + +#if __STDC__ +void +atdir_end(void) +#else +void +atdir_end() +#endif +{ + register ATDIR *pt; + register int i; + + if (atab == NULL) + return; + /* + * for each non-empty hash table entry reset all the directories + * chained there. + */ + for (i = 0; i < A_TAB_SZ; ++i) { + if ((pt = atab[i]) == NULL) + continue; + /* + * remember to force the times, set_ftime() looks at pmtime + * and patime, which only applies to things CREATED by pax, + * not read by pax. Read time reset is controlled by -t. + */ + for (; pt != NULL; pt = pt->fow) + set_ftime(pt->name, pt->mtime, pt->atime, 1); + } +} + +/* + * add_atdir() + * add a directory to the directory access time table. Table is hashed + * and chained by inode number. This is for directories READ by pax + */ + +#if __STDC__ +void +add_atdir(char *fname, dev_t dev, ino_t ino, time_t mtime, time_t atime) +#else +void +add_atdir(fname, dev, ino, mtime, atime) + char *fname; + dev_t dev; + ino_t ino; + time_t mtime; + time_t atime; +#endif +{ + register ATDIR *pt; + register u_int indx; + + if (atab == NULL) + return; + + /* + * make sure this directory is not already in the table, if so just + * return (the older entry always has the correct time). The only + * way this will happen is when the same subtree can be traversed by + * different args to pax and the -n option is aborting fts out of a + * subtree before all the post-order visits have been made). + */ + indx = ((unsigned)ino) % A_TAB_SZ; + if ((pt = atab[indx]) != NULL) { + while (pt != NULL) { + if ((pt->ino == ino) && (pt->dev == dev)) + break; + pt = pt->fow; + } + + /* + * oops, already there. Leave it alone. + */ + if (pt != NULL) + return; + } + + /* + * add it to the front of the hash chain + */ + if ((pt = (ATDIR *)malloc(sizeof(ATDIR))) != NULL) { + if ((pt->name = strdup(fname)) != NULL) { + pt->dev = dev; + pt->ino = ino; + pt->mtime = mtime; + pt->atime = atime; + pt->fow = atab[indx]; + atab[indx] = pt; + return; + } + (void)free((char *)pt); + } + + warn(1, "Directory access time reset table ran out of memory"); + return; +} + +/* + * get_atdir() + * look up a directory by inode and device number to obtain the access + * and modification time you want to set to. If found, the modification + * and access time parameters are set and the entry is removed from the + * table (as it is no longer needed). These are for directories READ by + * pax + * Return: + * 0 if found, -1 if not found. + */ + +#if __STDC__ +int +get_atdir(dev_t dev, ino_t ino, time_t *mtime, time_t *atime) +#else +int +get_atdir(dev, ino, mtime, atime) + dev_t dev; + ino_t ino; + time_t *mtime; + time_t *atime; +#endif +{ + register ATDIR *pt; + register ATDIR **ppt; + register u_int indx; + + if (atab == NULL) + return(-1); + /* + * hash by inode and search the chain for an inode and device match + */ + indx = ((unsigned)ino) % A_TAB_SZ; + if ((pt = atab[indx]) == NULL) + return(-1); + + ppt = &(atab[indx]); + while (pt != NULL) { + if ((pt->ino == ino) && (pt->dev == dev)) + break; + /* + * no match, go to next one + */ + ppt = &(pt->fow); + pt = pt->fow; + } + + /* + * return if we did not find it. + */ + if (pt == NULL) + return(-1); + + /* + * found it. return the times and remove the entry from the table. + */ + *ppt = pt->fow; + *mtime = pt->mtime; + *atime = pt->atime; + (void)free((char *)pt->name); + (void)free((char *)pt); + return(0); +} + +/* + * directory access mode and time storage routines (for directories CREATED + * by pax). + * + * Pax requires that extracted directories, by default, have their access/mod + * times and permissions set to the values specified in the archive. During the + * actions of extracting (and creating the destination subtree during -rw copy) + * directories extracted may be modified after being created. Even worse is + * that these directories may have been created with file permissions which + * prohibits any descendants of these directories from being extracted. When + * directories are created by pax, access rights may be added to permit the + * creation of files in their subtree. Every time pax creates a directory, the + * times and file permissions specified by the archive are stored. After all + * files have been extracted (or copied), these directories have their times + * and file modes reset to the stored values. The directory info is restored in + * reverse order as entries were added to the data file from root to leaf. To + * restore atime properly, we must go backwards. The data file consists of + * records with two parts, the file name followed by a DIRDATA trailer. The + * fixed sized trailer contains the size of the name plus the off_t location in + * the file. To restore we work backwards through the file reading the trailer + * then the file name. + */ + +/* + * dir_start() + * set up the directory time and file mode storage for directories CREATED + * by pax. + * Return: + * 0 if ok, -1 otherwise + */ + +#if __STDC__ +int +dir_start(void) +#else +int +dir_start() +#endif +{ + char *pt; + + if (dirfd != -1) + return(0); + if ((pt = tempnam((char *)NULL, (char *)NULL)) == NULL) + return(-1); + + /* + * unlink the file so it goes away at termination by itself + */ + (void)unlink(pt); + if ((dirfd = open(pt, O_RDWR|O_CREAT, 0600)) >= 0) { + (void)unlink(pt); + return(0); + } + warn(1, "Unable to create temporary file for directory times: %s", pt); + return(-1); +} + +/* + * add_dir() + * add the mode and times for a newly CREATED directory + * name is name of the directory, psb the stat buffer with the data in it, + * frc_mode is a flag that says whether to force the setting of the mode + * (ignoring the user set values for preserving file mode). Frc_mode is + * for the case where we created a file and found that the resulting + * directory was not writeable and the user asked for file modes to NOT + * be preserved. (we have to preserve what was created by default, so we + * have to force the setting at the end. this is stated explicitly in the + * pax spec) + */ + +#if __STDC__ +void +add_dir(char *name, int nlen, struct stat *psb, int frc_mode) +#else +void +add_dir(name, nlen, psb, frc_mode) + char *name; + int nlen; + struct stat *psb; + int frc_mode; +#endif +{ + DIRDATA dblk; + + if (dirfd < 0) + return; + + /* + * get current position (where file name will start) so we can store it + * in the trailer + */ + if ((dblk.npos = lseek(dirfd, 0L, SEEK_CUR)) < 0) { + warn(1,"Unable to store mode and times for directory: %s",name); + return; + } + + /* + * write the file name followed by the trailer + */ + dblk.nlen = nlen + 1; + dblk.mode = psb->st_mode & 0xffff; + dblk.mtime = psb->st_mtime; + dblk.atime = psb->st_atime; + dblk.frc_mode = frc_mode; + if ((write(dirfd, name, dblk.nlen) == dblk.nlen) && + (write(dirfd, (char *)&dblk, sizeof(dblk)) == sizeof(dblk))) { + ++dircnt; + return; + } + + warn(1,"Unable to store mode and times for created directory: %s",name); + return; +} + +/* + * proc_dir() + * process all file modes and times stored for directories CREATED + * by pax + */ + +#if __STDC__ +void +proc_dir(void) +#else +void +proc_dir() +#endif +{ + char name[PAXPATHLEN+1]; + DIRDATA dblk; + u_long cnt; + + if (dirfd < 0) + return; + /* + * read backwards through the file and process each directory + */ + for (cnt = 0; cnt < dircnt; ++cnt) { + /* + * read the trailer, then the file name, if this fails + * just give up. + */ + if (lseek(dirfd, -((off_t)sizeof(dblk)), SEEK_CUR) < 0) + break; + if (read(dirfd,(char *)&dblk, sizeof(dblk)) != sizeof(dblk)) + break; + if (lseek(dirfd, dblk.npos, SEEK_SET) < 0) + break; + if (read(dirfd, name, dblk.nlen) != dblk.nlen) + break; + if (lseek(dirfd, dblk.npos, SEEK_SET) < 0) + break; + + /* + * frc_mode set, make sure we set the file modes even if + * the user didn't ask for it (see file_subs.c for more info) + */ + if (pmode || dblk.frc_mode) + set_pmode(name, dblk.mode); + if (patime || pmtime) + set_ftime(name, dblk.mtime, dblk.atime, 0); + } + + (void)close(dirfd); + dirfd = -1; + if (cnt != dircnt) + warn(1,"Unable to set mode and times for created directories"); + return; +} + +/* + * database independent routines + */ + +/* + * st_hash() + * hashes filenames to a u_int for hashing into a table. Looks at the tail + * end of file, as this provides far better distribution than any other + * part of the name. For performance reasons we only care about the last + * MAXKEYLEN chars (should be at LEAST large enough to pick off the file + * name). Was tested on 500,000 name file tree traversal from the root + * and gave almost a perfectly uniform distribution of keys when used with + * prime sized tables (MAXKEYLEN was 128 in test). Hashes (sizeof int) + * chars at a time and pads with 0 for last addition. + * Return: + * the hash value of the string MOD (%) the table size. + */ + +#if __STDC__ +u_int +st_hash(char *name, int len, int tabsz) +#else +u_int +st_hash(name, len, tabsz) + char *name; + int len; + int tabsz; +#endif +{ + register char *pt; + register char *dest; + register char *end; + register int i; + register u_int key = 0; + register int steps; + register int res; + u_int val; + + /* + * only look at the tail up to MAXKEYLEN, we do not need to waste + * time here (remember these are pathnames, the tail is what will + * spread out the keys) + */ + if (len > MAXKEYLEN) { + pt = &(name[len - MAXKEYLEN]); + len = MAXKEYLEN; + } else + pt = name; + + /* + * calculate the number of u_int size steps in the string and if + * there is a runt to deal with + */ + steps = len/sizeof(u_int); + res = len % sizeof(u_int); + + /* + * add up the value of the string in unsigned integer sized pieces + * too bad we cannot have unsigned int aligned strings, then we + * could avoid the expensive copy. + */ + for (i = 0; i < steps; ++i) { + end = pt + sizeof(u_int); + dest = (char *)&val; + while (pt < end) + *dest++ = *pt++; + key += val; + } + + /* + * add in the runt padded with zero to the right + */ + if (res) { + val = 0; + end = pt + res; + dest = (char *)&val; + while (pt < end) + *dest++ = *pt++; + key += val; + } + + /* + * return the result mod the table size + */ + return(key % tabsz); +} diff --git a/bin/pax/tables.h b/bin/pax/tables.h new file mode 100644 index 0000000..a992613 --- /dev/null +++ b/bin/pax/tables.h @@ -0,0 +1,172 @@ +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * 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. + * + * @(#)tables.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * data structures and constants used by the different databases kept by pax + */ + +/* + * Hash Table Sizes MUST BE PRIME, if set too small performance suffers. + * Probably safe to expect 500000 inodes per tape. Assuming good key + * distribution (inodes) chains of under 50 long (worse case) is ok. + */ +#define L_TAB_SZ 2503 /* hard link hash table size */ +#define F_TAB_SZ 50503 /* file time hash table size */ +#define N_TAB_SZ 541 /* interactive rename hash table */ +#define D_TAB_SZ 317 /* unique device mapping table */ +#define A_TAB_SZ 317 /* ftree dir access time reset table */ +#define MAXKEYLEN 64 /* max number of chars for hash */ + +/* + * file hard link structure (hashed by dev/ino and chained) used to find the + * hard links in a file system or with some archive formats (cpio) + */ +typedef struct hrdlnk { + char *name; /* name of first file seen with this ino/dev */ + dev_t dev; /* files device number */ + ino_t ino; /* files inode number */ + u_long nlink; /* expected link count */ + struct hrdlnk *fow; +} HRDLNK; + +/* + * Archive write update file time table (the -u, -C flag), hashed by filename. + * Filenames are stored in a scratch file at seek offset into the file. The + * file time (mod time) and the file name length (for a quick check) are + * stored in a hash table node. We were forced to use a scratch file because + * with -u, the mtime for every node in the archive must always be available + * to compare against (and this data can get REALLY large with big archives). + * By being careful to read only when we have a good chance of a match, the + * performance loss is not measurable (and the size of the archive we can + * handle is greatly increased). + */ +typedef struct ftm { + int namelen; /* file name length */ + time_t mtime; /* files last modification time */ + off_t seek; /* loacation in scratch file */ + struct ftm *fow; +} FTM; + +/* + * Interactive rename table (-i flag), hashed by orig filename. + * We assume this will not be a large table as this mapping data can only be + * obtained through interactive input by the user. Nobody is going to type in + * changes for 500000 files? We use chaining to resolve collisions. + */ + +typedef struct namt { + char *oname; /* old name */ + char *nname; /* new name typed in by the user */ + struct namt *fow; +} NAMT; + +/* + * Unique device mapping tables. Some protocols (e.g. cpio) require that the + * <c_dev,c_ino> pair will uniquely identify a file in an archive unless they + * are links to the same file. Appending to archives can break this. For those + * protocols that have this requirement we map c_dev to a unique value not seen + * in the archive when we append. We also try to handle inode truncation with + * this table. (When the inode field in the archive header are too small, we + * remap the dev on writes to remove accidental collisions). + * + * The list is hashed by device number using chain collision resolution. Off of + * each DEVT are linked the various remaps for this device based on those bits + * in the inode which were truncated. For example if we are just remapping to + * avoid a device number during an update append, off the DEVT we would have + * only a single DLIST that has a truncation id of 0 (no inode bits were + * stripped for this device so far). When we spot inode truncation we create + * a new mapping based on the set of bits in the inode which were stripped off. + * so if the top four bits of the inode are stripped and they have a pattern of + * 0110...... (where . are those bits not truncated) we would have a mapping + * assigned for all inodes that has the same 0110.... pattern (with this dev + * number of course). This keeps the mapping sparse and should be able to store + * close to the limit of files which can be represented by the optimal + * combination of dev and inode bits, and without creating a fouled up archive. + * Note we also remap truncated devs in the same way (an exercise for the + * dedicated reader; always wanted to say that...:) + */ + +typedef struct devt { + dev_t dev; /* the orig device number we now have to map */ + struct devt *fow; /* new device map list */ + struct dlist *list; /* map list based on inode truncation bits */ +} DEVT; + +typedef struct dlist { + ino_t trunc_bits; /* truncation pattern for a specific map */ + dev_t dev; /* the new device id we use */ + struct dlist *fow; +} DLIST; + +/* + * ftree directory access time reset table. When we are done with with a + * subtree we reset the access and mod time of the directory when the tflag is + * set. Not really explicitly specified in the pax spec, but easy and fast to + * do (and this may have even been intended in the spec, it is not clear). + * table is hashed by inode with chaining. + */ + +typedef struct atdir { + char *name; /* name of directory to reset */ + dev_t dev; /* dev and inode for fast lookup */ + ino_t ino; + time_t mtime; /* access and mod time to reset to */ + time_t atime; + struct atdir *fow; +} ATDIR; + +/* + * created directory time and mode storage entry. After pax is finished during + * extraction or copy, we must reset directory access modes and times that + * may have been modified after creation (they no longer have the specified + * times and/or modes). We must reset time in the reverse order of creation, + * because entries are added from the top of the file tree to the bottom. + * We MUST reset times from leaf to root (it will not work the other + * direction). Entries are recorded into a spool file to make reverse + * reading faster. + */ + +typedef struct dirdata { + int nlen; /* length of the directory name (includes \0) */ + off_t npos; /* position in file where this dir name starts */ + mode_t mode; /* file mode to restore */ + time_t mtime; /* mtime to set */ + time_t atime; /* atime to set */ + int frc_mode; /* do we force mode settings? */ +} DIRDATA; diff --git a/bin/pax/tar.c b/bin/pax/tar.c new file mode 100644 index 0000000..246ae8e --- /dev/null +++ b/bin/pax/tar.c @@ -0,0 +1,1192 @@ +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * 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[] = "@(#)tar.c 8.2 (Berkeley) 4/18/94"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <string.h> +#include <stdio.h> +#include <ctype.h> +#include <unistd.h> +#include <stdlib.h> +#include "pax.h" +#include "extern.h" +#include "tar.h" + +/* + * Routines for reading, writing and header identify of various versions of tar + */ + +static u_long tar_chksm __P((register char *, register int)); +static char *name_split __P((register char *, register int)); +static int ul_oct __P((u_long, register char *, register int, int)); +#ifndef NET2_STAT +static int uqd_oct __P((u_quad_t, register char *, register int, int)); +#endif + +/* + * Routines common to all versions of tar + */ + +static int tar_nodir; /* do not write dirs under old tar */ + +/* + * tar_endwr() + * add the tar trailer of two null blocks + * Return: + * 0 if ok, -1 otherwise (what wr_skip returns) + */ + +#if __STDC__ +int +tar_endwr(void) +#else +int +tar_endwr() +#endif +{ + return(wr_skip((off_t)(NULLCNT*BLKMULT))); +} + +/* + * tar_endrd() + * no cleanup needed here, just return size of trailer (for append) + * Return: + * size of trailer (2 * BLKMULT) + */ + +#if __STDC__ +off_t +tar_endrd(void) +#else +off_t +tar_endrd() +#endif +{ + return((off_t)(NULLCNT*BLKMULT)); +} + +/* + * tar_trail() + * Called to determine if a header block is a valid trailer. We are passed + * the block, the in_sync flag (which tells us we are in resync mode; + * looking for a valid header), and cnt (which starts at zero) which is + * used to count the number of empty blocks we have seen so far. + * Return: + * 0 if a valid trailer, -1 if not a valid trailer, or 1 if the block + * could never contain a header. + */ + +#if __STDC__ +int +tar_trail(register char *buf, register int in_resync, register int *cnt) +#else +int +tar_trail(buf, in_resync, cnt) + register char *buf; + register int in_resync; + register int *cnt; +#endif +{ + register int i; + + /* + * look for all zero, trailer is two consecutive blocks of zero + */ + for (i = 0; i < BLKMULT; ++i) { + if (buf[i] != '\0') + break; + } + + /* + * if not all zero it is not a trailer, but MIGHT be a header. + */ + if (i != BLKMULT) + return(-1); + + /* + * When given a zero block, we must be careful! + * If we are not in resync mode, check for the trailer. Have to watch + * out that we do not mis-identify file data as the trailer, so we do + * NOT try to id a trailer during resync mode. During resync mode we + * might as well throw this block out since a valid header can NEVER be + * a block of all 0 (we must have a valid file name). + */ + if (!in_resync && (++*cnt >= NULLCNT)) + return(0); + return(1); +} + +/* + * ul_oct() + * convert an unsigned long to an octal string. many oddball field + * termination characters are used by the various versions of tar in the + * different fields. term selects which kind to use. str is BLANK padded + * at the front to len. we are unable to use only one format as many old + * tar readers are very cranky about this. + * Return: + * 0 if the number fit into the string, -1 otherwise + */ + +#if __STDC__ +static int +ul_oct(u_long val, register char *str, register int len, int term) +#else +static int +ul_oct(val, str, len, term) + u_long val; + register char *str; + register int len; + int term; +#endif +{ + register char *pt; + + /* + * term selects the appropriate character(s) for the end of the string + */ + pt = str + len - 1; + switch(term) { + case 3: + *pt-- = '\0'; + break; + case 2: + *pt-- = ' '; + *pt-- = '\0'; + break; + case 1: + *pt-- = ' '; + break; + case 0: + default: + *pt-- = '\0'; + *pt-- = ' '; + break; + } + + /* + * convert and blank pad if there is space + */ + while (pt >= str) { + *pt-- = '0' + (char)(val & 0x7); + if ((val = val >> 3) == (u_long)0) + break; + } + + while (pt >= str) + *pt-- = ' '; + if (val != (u_long)0) + return(-1); + return(0); +} + +#ifndef NET2_STAT +/* + * uqd_oct() + * convert an u_quad_t to an octal string. one of many oddball field + * termination characters are used by the various versions of tar in the + * different fields. term selects which kind to use. str is BLANK padded + * at the front to len. we are unable to use only one format as many old + * tar readers are very cranky about this. + * Return: + * 0 if the number fit into the string, -1 otherwise + */ + +#if __STDC__ +static int +uqd_oct(u_quad_t val, register char *str, register int len, int term) +#else +static int +uqd_oct(val, str, len, term) + u_quad_t val; + register char *str; + register int len; + int term; +#endif +{ + register char *pt; + + /* + * term selects the appropriate character(s) for the end of the string + */ + pt = str + len - 1; + switch(term) { + case 3: + *pt-- = '\0'; + break; + case 2: + *pt-- = ' '; + *pt-- = '\0'; + break; + case 1: + *pt-- = ' '; + break; + case 0: + default: + *pt-- = '\0'; + *pt-- = ' '; + break; + } + + /* + * convert and blank pad if there is space + */ + while (pt >= str) { + *pt-- = '0' + (char)(val & 0x7); + if ((val = val >> 3) == 0) + break; + } + + while (pt >= str) + *pt-- = ' '; + if (val != (u_quad_t)0) + return(-1); + return(0); +} +#endif + +/* + * tar_chksm() + * calculate the checksum for a tar block counting the checksum field as + * all blanks (BLNKSUM is that value pre-calculated, the sume of 8 blanks). + * NOTE: we use len to short circuit summing 0's on write since we ALWAYS + * pad headers with 0. + * Return: + * unsigned long checksum + */ + +#if __STDC__ +static u_long +tar_chksm(register char *blk, register int len) +#else +static u_long +tar_chksm(blk, len) + register char *blk; + register int len; +#endif +{ + register char *stop; + register char *pt; + u_long chksm = BLNKSUM; /* inital value is checksum field sum */ + + /* + * add the part of the block before the checksum field + */ + pt = blk; + stop = blk + CHK_OFFSET; + while (pt < stop) + chksm += (u_long)(*pt++ & 0xff); + /* + * move past the checksum field and keep going, spec counts the + * checksum field as the sum of 8 blanks (which is pre-computed as + * BLNKSUM). + * ASSUMED: len is greater than CHK_OFFSET. (len is where our 0 padding + * starts, no point in summing zero's) + */ + pt += CHK_LEN; + stop = blk + len; + while (pt < stop) + chksm += (u_long)(*pt++ & 0xff); + return(chksm); +} + +/* + * Routines for old BSD style tar (also made portable to sysV tar) + */ + +/* + * tar_id() + * determine if a block given to us is a valid tar header (and not a USTAR + * header). We have to be on the lookout for those pesky blocks of all + * zero's. + * Return: + * 0 if a tar header, -1 otherwise + */ + +#if __STDC__ +int +tar_id(register char *blk, int size) +#else +int +tar_id(blk, size) + register char *blk; + int size; +#endif +{ + register HD_TAR *hd; + register HD_USTAR *uhd; + + if (size < BLKMULT) + return(-1); + hd = (HD_TAR *)blk; + uhd = (HD_USTAR *)blk; + + /* + * check for block of zero's first, a simple and fast test, then make + * sure this is not a ustar header by looking for the ustar magic + * cookie. We should use TMAGLEN, but some USTAR archive programs are + * wrong and create archives missing the \0. Last we check the + * checksum. If this is ok we have to assume it is a valid header. + */ + if (hd->name[0] == '\0') + return(-1); + if (strncmp(uhd->magic, TMAGIC, TMAGLEN - 1) == 0) + return(-1); + if (asc_ul(hd->chksum,sizeof(hd->chksum),OCT) != tar_chksm(blk,BLKMULT)) + return(-1); + return(0); +} + +/* + * tar_opt() + * handle tar format specific -o options + * Return: + * 0 if ok -1 otherwise + */ + +#if __STDC__ +int +tar_opt(void) +#else +int +tar_opt() +#endif +{ + OPLIST *opt; + + while ((opt = opt_next()) != NULL) { + if (strcmp(opt->name, TAR_OPTION) || + strcmp(opt->value, TAR_NODIR)) { + warn(1, "Unknown tar format -o option/value pair %s=%s", + opt->name, opt->value); + warn(1,"%s=%s is the only supported tar format option", + TAR_OPTION, TAR_NODIR); + return(-1); + } + + /* + * we only support one option, and only when writing + */ + if ((act != APPND) && (act != ARCHIVE)) { + warn(1, "%s=%s is only supported when writing.", + opt->name, opt->value); + return(-1); + } + tar_nodir = 1; + } + return(0); +} + + +/* + * tar_rd() + * extract the values out of block already determined to be a tar header. + * store the values in the ARCHD parameter. + * Return: + * 0 + */ + +#if __STDC__ +int +tar_rd(register ARCHD *arcn, register char *buf) +#else +int +tar_rd(arcn, buf) + register ARCHD *arcn; + register char *buf; +#endif +{ + register HD_TAR *hd; + register char *pt; + + /* + * we only get proper sized buffers passed to us + */ + if (tar_id(buf, BLKMULT) < 0) + return(-1); + arcn->org_name = arcn->name; + arcn->sb.st_nlink = 1; + arcn->pat = NULL; + + /* + * copy out the name and values in the stat buffer + */ + hd = (HD_TAR *)buf; + arcn->nlen = l_strncpy(arcn->name, hd->name, sizeof(hd->name)); + arcn->name[arcn->nlen] = '\0'; + arcn->sb.st_mode = (mode_t)(asc_ul(hd->mode,sizeof(hd->mode),OCT) & + 0xfff); + arcn->sb.st_uid = (uid_t)asc_ul(hd->uid, sizeof(hd->uid), OCT); + arcn->sb.st_gid = (gid_t)asc_ul(hd->gid, sizeof(hd->gid), OCT); + arcn->sb.st_size = (size_t)asc_ul(hd->size, sizeof(hd->size), OCT); + arcn->sb.st_mtime = (time_t)asc_ul(hd->mtime, sizeof(hd->mtime), OCT); + arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime; + + /* + * have to look at the last character, it may be a '/' and that is used + * to encode this as a directory + */ + pt = &(arcn->name[arcn->nlen - 1]); + arcn->pad = 0; + arcn->skip = 0; + switch(hd->linkflag) { + case SYMTYPE: + /* + * symbolic link, need to get the link name and set the type in + * the st_mode so -v printing will look correct. + */ + arcn->type = PAX_SLK; + arcn->ln_nlen = l_strncpy(arcn->ln_name, hd->linkname, + sizeof(hd->linkname)); + arcn->ln_name[arcn->ln_nlen] = '\0'; + arcn->sb.st_mode |= S_IFLNK; + break; + case LNKTYPE: + /* + * hard link, need to get the link name, set the type in the + * st_mode and st_nlink so -v printing will look better. + */ + arcn->type = PAX_HLK; + arcn->sb.st_nlink = 2; + arcn->ln_nlen = l_strncpy(arcn->ln_name, hd->linkname, + sizeof(hd->linkname)); + arcn->ln_name[arcn->ln_nlen] = '\0'; + + /* + * no idea of what type this thing really points at, but + * we set something for printing only. + */ + arcn->sb.st_mode |= S_IFREG; + break; + case AREGTYPE: + case REGTYPE: + default: + /* + * If we have a trailing / this is a directory and NOT a file. + */ + arcn->ln_name[0] = '\0'; + arcn->ln_nlen = 0; + if (*pt == '/') { + /* + * it is a directory, set the mode for -v printing + */ + arcn->type = PAX_DIR; + arcn->sb.st_mode |= S_IFDIR; + arcn->sb.st_nlink = 2; + } else { + /* + * have a file that will be followed by data. Set the + * skip value to the size field and caluculate the size + * of the padding. + */ + arcn->type = PAX_REG; + arcn->sb.st_mode |= S_IFREG; + arcn->pad = TAR_PAD(arcn->sb.st_size); + arcn->skip = arcn->sb.st_size; + } + break; + } + + /* + * strip off any trailing slash. + */ + if (*pt == '/') { + *pt = '\0'; + --arcn->nlen; + } + return(0); +} + +/* + * tar_wr() + * write a tar header for the file specified in the ARCHD to the archive. + * Have to check for file types that cannot be stored and file names that + * are too long. Be careful of the term (last arg) to ul_oct, each field + * of tar has it own spec for the termination character(s). + * ASSUMED: space after header in header block is zero filled + * Return: + * 0 if file has data to be written after the header, 1 if file has NO + * data to write after the header, -1 if archive write failed + */ + +#if __STDC__ +int +tar_wr(register ARCHD *arcn) +#else +int +tar_wr(arcn) + register ARCHD *arcn; +#endif +{ + register HD_TAR *hd; + int len; + char hdblk[sizeof(HD_TAR)]; + + /* + * check for those file system types which tar cannot store + */ + switch(arcn->type) { + case PAX_DIR: + /* + * user asked that dirs not be written to the archive + */ + if (tar_nodir) + return(1); + break; + case PAX_CHR: + warn(1, "Tar cannot archive a character device %s", + arcn->org_name); + return(1); + case PAX_BLK: + warn(1, "Tar cannot archive a block device %s", arcn->org_name); + return(1); + case PAX_SCK: + warn(1, "Tar cannot archive a socket %s", arcn->org_name); + return(1); + case PAX_FIF: + warn(1, "Tar cannot archive a fifo %s", arcn->org_name); + return(1); + case PAX_SLK: + case PAX_HLK: + case PAX_HRG: + if (arcn->ln_nlen > sizeof(hd->linkname)) { + warn(1,"Link name too long for tar %s", arcn->ln_name); + return(1); + } + break; + case PAX_REG: + case PAX_CTG: + default: + break; + } + + /* + * check file name len, remember extra char for dirs (the / at the end) + */ + len = arcn->nlen; + if (arcn->type == PAX_DIR) + ++len; + if (len > sizeof(hd->name)) { + warn(1, "File name too long for tar %s", arcn->name); + return(1); + } + + /* + * copy the data out of the ARCHD into the tar header based on the type + * of the file. Remember many tar readers want the unused fields to be + * padded with zero. We set the linkflag field (type), the linkname + * (or zero if not used),the size, and set the padding (if any) to be + * added after the file data (0 for all other types, as they only have + * a header) + */ + hd = (HD_TAR *)hdblk; + zf_strncpy(hd->name, arcn->name, sizeof(hd->name)); + arcn->pad = 0; + + if (arcn->type == PAX_DIR) { + /* + * directories are the same as files, except have a filename + * that ends with a /, we add the slash here. No data follows, + * dirs, so no pad. + */ + hd->linkflag = AREGTYPE; + bzero(hd->linkname, sizeof(hd->linkname)); + hd->name[len-1] = '/'; + if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 1)) + goto out; + } else if (arcn->type == PAX_SLK) { + /* + * no data follows this file, so no pad + */ + hd->linkflag = SYMTYPE; + zf_strncpy(hd->linkname,arcn->ln_name, sizeof(hd->linkname)); + if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 1)) + goto out; + } else if ((arcn->type == PAX_HLK) || (arcn->type == PAX_HRG)) { + /* + * no data follows this file, so no pad + */ + hd->linkflag = LNKTYPE; + zf_strncpy(hd->linkname,arcn->ln_name, sizeof(hd->linkname)); + if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 1)) + goto out; + } else { + /* + * data follows this file, so set the pad + */ + hd->linkflag = AREGTYPE; + bzero(hd->linkname, sizeof(hd->linkname)); +# ifdef NET2_STAT + if (ul_oct((u_long)arcn->sb.st_size, hd->size, + sizeof(hd->size), 1)) { +# else + if (uqd_oct((u_quad_t)arcn->sb.st_size, hd->size, + sizeof(hd->size), 1)) { +# endif + warn(1,"File is too large for tar %s", arcn->org_name); + return(1); + } + arcn->pad = TAR_PAD(arcn->sb.st_size); + } + + /* + * copy those fields that are independent of the type + */ + if (ul_oct((u_long)arcn->sb.st_mode, hd->mode, sizeof(hd->mode), 0) || + ul_oct((u_long)arcn->sb.st_uid, hd->uid, sizeof(hd->uid), 0) || + ul_oct((u_long)arcn->sb.st_gid, hd->gid, sizeof(hd->gid), 0) || + ul_oct((u_long)arcn->sb.st_mtime, hd->mtime, sizeof(hd->mtime), 1)) + goto out; + + /* + * calculate and add the checksum, then write the header. A return of + * 0 tells the caller to now write the file data, 1 says no data needs + * to be written + */ + if (ul_oct(tar_chksm(hdblk, sizeof(HD_TAR)), hd->chksum, + sizeof(hd->chksum), 2)) + goto out; + if (wr_rdbuf(hdblk, sizeof(HD_TAR)) < 0) + return(-1); + if (wr_skip((off_t)(BLKMULT - sizeof(HD_TAR))) < 0) + return(-1); + if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG)) + return(0); + return(1); + + out: + /* + * header field is out of range + */ + warn(1, "Tar header field is too small for %s", arcn->org_name); + return(1); +} + +/* + * Routines for POSIX ustar + */ + +/* + * ustar_strd() + * initialization for ustar read + * Return: + * 0 if ok, -1 otherwise + */ + +#if __STDC__ +int +ustar_strd(void) +#else +int +ustar_strd() +#endif +{ + if ((usrtb_start() < 0) || (grptb_start() < 0)) + return(-1); + return(0); +} + +/* + * ustar_stwr() + * initialization for ustar write + * Return: + * 0 if ok, -1 otherwise + */ + +#if __STDC__ +int +ustar_stwr(void) +#else +int +ustar_stwr() +#endif +{ + if ((uidtb_start() < 0) || (gidtb_start() < 0)) + return(-1); + return(0); +} + +/* + * ustar_id() + * determine if a block given to us is a valid ustar header. We have to + * be on the lookout for those pesky blocks of all zero's + * Return: + * 0 if a ustar header, -1 otherwise + */ + +#if __STDC__ +int +ustar_id(char *blk, int size) +#else +int +ustar_id(blk, size) + char *blk; + int size; +#endif +{ + register HD_USTAR *hd; + + if (size < BLKMULT) + return(-1); + hd = (HD_USTAR *)blk; + + /* + * check for block of zero's first, a simple and fast test then check + * ustar magic cookie. We should use TMAGLEN, but some USTAR archive + * programs are fouled up and create archives missing the \0. Last we + * check the checksum. If ok we have to assume it is a valid header. + */ + if (hd->name[0] == '\0') + return(-1); + if (strncmp(hd->magic, TMAGIC, TMAGLEN - 1) != 0) + return(-1); + if (asc_ul(hd->chksum,sizeof(hd->chksum),OCT) != tar_chksm(blk,BLKMULT)) + return(-1); + return(0); +} + +/* + * ustar_rd() + * extract the values out of block already determined to be a ustar header. + * store the values in the ARCHD parameter. + * Return: + * 0 + */ + +#if __STDC__ +int +ustar_rd(register ARCHD *arcn, register char *buf) +#else +int +ustar_rd(arcn, buf) + register ARCHD *arcn; + register char *buf; +#endif +{ + register HD_USTAR *hd; + register char *dest; + register int cnt = 0; + dev_t devmajor; + dev_t devminor; + + /* + * we only get proper sized buffers + */ + if (ustar_id(buf, BLKMULT) < 0) + return(-1); + arcn->org_name = arcn->name; + arcn->sb.st_nlink = 1; + arcn->pat = NULL; + hd = (HD_USTAR *)buf; + + /* + * see if the filename is split into two parts. if, so joint the parts. + * we copy the prefix first and add a / between the prefix and name. + */ + dest = arcn->name; + if (*(hd->prefix) != '\0') { + cnt = l_strncpy(arcn->name, hd->prefix, sizeof(hd->prefix)); + dest = arcn->name + arcn->nlen; + *dest++ = '/'; + } + arcn->nlen = l_strncpy(dest, hd->name, sizeof(hd->name)); + arcn->nlen += cnt; + arcn->name[arcn->nlen] = '\0'; + + /* + * follow the spec to the letter. we should only have mode bits, strip + * off all other crud we may be passed. + */ + arcn->sb.st_mode = (mode_t)(asc_ul(hd->mode, sizeof(hd->mode), OCT) & + 0xfff); + arcn->sb.st_size = (size_t)asc_ul(hd->size, sizeof(hd->size), OCT); + arcn->sb.st_mtime = (time_t)asc_ul(hd->mtime, sizeof(hd->mtime), OCT); + arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime; + + /* + * If we can find the ascii names for gname and uname in the password + * and group files we will use the uid's and gid they bind. Otherwise + * we use the uid and gid values stored in the header. (This is what + * the posix spec wants). + */ + hd->gname[sizeof(hd->gname) - 1] = '\0'; + if (gid_name(hd->gname, &(arcn->sb.st_gid)) < 0) + arcn->sb.st_gid = (gid_t)asc_ul(hd->gid, sizeof(hd->gid), OCT); + hd->uname[sizeof(hd->uname) - 1] = '\0'; + if (uid_name(hd->uname, &(arcn->sb.st_uid)) < 0) + arcn->sb.st_uid = (uid_t)asc_ul(hd->uid, sizeof(hd->uid), OCT); + + /* + * set the defaults, these may be changed depending on the file type + */ + arcn->ln_name[0] = '\0'; + arcn->ln_nlen = 0; + arcn->pad = 0; + arcn->skip = 0; + arcn->sb.st_rdev = (dev_t)0; + + /* + * set the mode and PAX type according to the typeflag in the header + */ + switch(hd->typeflag) { + case FIFOTYPE: + arcn->type = PAX_FIF; + arcn->sb.st_mode |= S_IFIFO; + break; + case DIRTYPE: + arcn->type = PAX_DIR; + arcn->sb.st_mode |= S_IFDIR; + arcn->sb.st_nlink = 2; + + /* + * Some programs that create ustar archives append a '/' + * to the pathname for directories. This clearly violates + * ustar specs, but we will silently strip it off anyway. + */ + if (arcn->name[arcn->nlen - 1] == '/') + arcn->name[--arcn->nlen] = '\0'; + break; + case BLKTYPE: + case CHRTYPE: + /* + * this type requires the rdev field to be set. + */ + if (hd->typeflag == BLKTYPE) { + arcn->type = PAX_BLK; + arcn->sb.st_mode |= S_IFBLK; + } else { + arcn->type = PAX_CHR; + arcn->sb.st_mode |= S_IFCHR; + } + devmajor = (dev_t)asc_ul(hd->devmajor,sizeof(hd->devmajor),OCT); + devminor = (dev_t)asc_ul(hd->devminor,sizeof(hd->devminor),OCT); + arcn->sb.st_rdev = TODEV(devmajor, devminor); + break; + case SYMTYPE: + case LNKTYPE: + if (hd->typeflag == SYMTYPE) { + arcn->type = PAX_SLK; + arcn->sb.st_mode |= S_IFLNK; + } else { + arcn->type = PAX_HLK; + /* + * so printing looks better + */ + arcn->sb.st_mode |= S_IFREG; + arcn->sb.st_nlink = 2; + } + /* + * copy the link name + */ + arcn->ln_nlen = l_strncpy(arcn->ln_name, hd->linkname, + sizeof(hd->linkname)); + arcn->ln_name[arcn->ln_nlen] = '\0'; + break; + case CONTTYPE: + case AREGTYPE: + case REGTYPE: + default: + /* + * these types have file data that follows. Set the skip and + * pad fields. + */ + arcn->type = PAX_REG; + arcn->pad = TAR_PAD(arcn->sb.st_size); + arcn->skip = arcn->sb.st_size; + arcn->sb.st_mode |= S_IFREG; + break; + } + return(0); +} + +/* + * ustar_wr() + * write a ustar header for the file specified in the ARCHD to the archive + * Have to check for file types that cannot be stored and file names that + * are too long. Be careful of the term (last arg) to ul_oct, we only use + * '\0' for the termination character (this is different than picky tar) + * ASSUMED: space after header in header block is zero filled + * Return: + * 0 if file has data to be written after the header, 1 if file has NO + * data to write after the header, -1 if archive write failed + */ + +#if __STDC__ +int +ustar_wr(register ARCHD *arcn) +#else +int +ustar_wr(arcn) + register ARCHD *arcn; +#endif +{ + register HD_USTAR *hd; + register char *pt; + char hdblk[sizeof(HD_USTAR)]; + + /* + * check for those file system types ustar cannot store + */ + if (arcn->type == PAX_SCK) { + warn(1, "Ustar cannot archive a socket %s", arcn->org_name); + return(1); + } + + /* + * check the length of the linkname + */ + if (((arcn->type == PAX_SLK) || (arcn->type == PAX_HLK) || + (arcn->type == PAX_HRG)) && (arcn->ln_nlen > sizeof(hd->linkname))){ + warn(1, "Link name too long for ustar %s", arcn->ln_name); + return(1); + } + + /* + * split the path name into prefix and name fields (if needed). if + * pt != arcn->name, the name has to be split + */ + if ((pt = name_split(arcn->name, arcn->nlen)) == NULL) { + warn(1, "File name too long for ustar %s", arcn->name); + return(1); + } + hd = (HD_USTAR *)hdblk; + arcn->pad = 0L; + + /* + * split the name, or zero out the prefix + */ + if (pt != arcn->name) { + /* + * name was split, pt points at the / where the split is to + * occur, we remove the / and copy the first part to the prefix + */ + *pt = '\0'; + zf_strncpy(hd->prefix, arcn->name, sizeof(hd->prefix)); + *pt++ = '/'; + } else + bzero(hd->prefix, sizeof(hd->prefix)); + + /* + * copy the name part. this may be the whole path or the part after + * the prefix + */ + zf_strncpy(hd->name, pt, sizeof(hd->name)); + + /* + * set the fields in the header that are type dependent + */ + switch(arcn->type) { + case PAX_DIR: + hd->typeflag = DIRTYPE; + bzero(hd->linkname, sizeof(hd->linkname)); + bzero(hd->devmajor, sizeof(hd->devmajor)); + bzero(hd->devminor, sizeof(hd->devminor)); + if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 3)) + goto out; + break; + case PAX_CHR: + case PAX_BLK: + if (arcn->type == PAX_CHR) + hd->typeflag = CHRTYPE; + else + hd->typeflag = BLKTYPE; + bzero(hd->linkname, sizeof(hd->linkname)); + if (ul_oct((u_long)MAJOR(arcn->sb.st_rdev), hd->devmajor, + sizeof(hd->devmajor), 3) || + ul_oct((u_long)MINOR(arcn->sb.st_rdev), hd->devminor, + sizeof(hd->devminor), 3) || + ul_oct((u_long)0L, hd->size, sizeof(hd->size), 3)) + goto out; + break; + case PAX_FIF: + hd->typeflag = FIFOTYPE; + bzero(hd->linkname, sizeof(hd->linkname)); + bzero(hd->devmajor, sizeof(hd->devmajor)); + bzero(hd->devminor, sizeof(hd->devminor)); + if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 3)) + goto out; + break; + case PAX_SLK: + case PAX_HLK: + case PAX_HRG: + if (arcn->type == PAX_SLK) + hd->typeflag = SYMTYPE; + else + hd->typeflag = LNKTYPE; + zf_strncpy(hd->linkname,arcn->ln_name, sizeof(hd->linkname)); + bzero(hd->devmajor, sizeof(hd->devmajor)); + bzero(hd->devminor, sizeof(hd->devminor)); + if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 3)) + goto out; + break; + case PAX_REG: + case PAX_CTG: + default: + /* + * file data with this type, set the padding + */ + if (arcn->type == PAX_CTG) + hd->typeflag = CONTTYPE; + else + hd->typeflag = REGTYPE; + bzero(hd->linkname, sizeof(hd->linkname)); + bzero(hd->devmajor, sizeof(hd->devmajor)); + bzero(hd->devminor, sizeof(hd->devminor)); + arcn->pad = TAR_PAD(arcn->sb.st_size); +# ifdef NET2_STAT + if (ul_oct((u_long)arcn->sb.st_size, hd->size, + sizeof(hd->size), 3)) { +# else + if (uqd_oct((u_quad_t)arcn->sb.st_size, hd->size, + sizeof(hd->size), 3)) { +# endif + warn(1,"File is too long for ustar %s",arcn->org_name); + return(1); + } + break; + } + + zf_strncpy(hd->magic, TMAGIC, TMAGLEN); + zf_strncpy(hd->version, TVERSION, TVERSLEN); + + /* + * set the remaining fields. Some versions want all 16 bits of mode + * we better humor them (they really do not meet spec though).... + */ + if (ul_oct((u_long)arcn->sb.st_mode, hd->mode, sizeof(hd->mode), 3) || + ul_oct((u_long)arcn->sb.st_uid, hd->uid, sizeof(hd->uid), 3) || + ul_oct((u_long)arcn->sb.st_gid, hd->gid, sizeof(hd->gid), 3) || + ul_oct((u_long)arcn->sb.st_mtime,hd->mtime,sizeof(hd->mtime),3)) + goto out; + zf_strncpy(hd->uname,name_uid(arcn->sb.st_uid, 0),sizeof(hd->uname)); + zf_strncpy(hd->gname,name_gid(arcn->sb.st_gid, 0),sizeof(hd->gname)); + + /* + * calculate and store the checksum write the header to the archive + * return 0 tells the caller to now write the file data, 1 says no data + * needs to be written + */ + if (ul_oct(tar_chksm(hdblk, sizeof(HD_USTAR)), hd->chksum, + sizeof(hd->chksum), 3)) + goto out; + if (wr_rdbuf(hdblk, sizeof(HD_USTAR)) < 0) + return(-1); + if (wr_skip((off_t)(BLKMULT - sizeof(HD_USTAR))) < 0) + return(-1); + if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG)) + return(0); + return(1); + + out: + /* + * header field is out of range + */ + warn(1, "Ustar header field is too small for %s", arcn->org_name); + return(1); +} + +/* + * name_split() + * see if the name has to be split for storage in a ustar header. We try + * to fit the entire name in the name field without splitting if we can. + * The split point is always at a / + * Return + * character pointer to split point (always the / that is to be removed + * if the split is not needed, the points is set to the start of the file + * name (it would violate the spec to split there). A NULL is returned if + * the file name is too long + */ + +#if __STDC__ +static char * +name_split(register char *name, register int len) +#else +static char * +name_split(name, len) + register char *name; + register int len; +#endif +{ + register char *start; + + /* + * check to see if the file name is small enough to fit in the name + * field. if so just return a pointer to the name. + */ + if (len <= TNMSZ) + return(name); + if (len > (TPFSZ + TNMSZ + 1)) + return(NULL); + + /* + * we start looking at the biggest sized piece that fits in the name + * field. We walk foward looking for a slash to split at. The idea is + * to find the biggest piece to fit in the name field (or the smallest + * prefix we can find) (the -1 is correct the biggest piece would + * include the slash between the two parts that gets thrown away) + */ + start = name + len - TNMSZ - 1; + while ((*start != '\0') && (*start != '/')) + ++start; + + /* + * if we hit the end of the string, this name cannot be split, so we + * cannot store this file. + */ + if (*start == '\0') + return(NULL); + len = start - name; + + /* + * NOTE: /str where the length of str == TNMSZ can not be stored under + * the p1003.1-1990 spec for ustar. We could force a prefix of / and + * the file would then expand on extract to //str. The len == 0 below + * makes this special case follow the spec to the letter. + */ + if ((len > TPFSZ) || (len == 0)) + return(NULL); + + /* + * ok have a split point, return it to the caller + */ + return(start); +} diff --git a/bin/pax/tar.h b/bin/pax/tar.h new file mode 100644 index 0000000..dad2d77 --- /dev/null +++ b/bin/pax/tar.h @@ -0,0 +1,148 @@ +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * 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. + * + * @(#)tar.h 8.2 (Berkeley) 4/18/94 + */ + +/* + * defines and data structures common to all tar formats + */ +#define CHK_LEN 8 /* length of checksum field */ +#define TNMSZ 100 /* size of name field */ +#ifdef _PAX_ +#define NULLCNT 2 /* number of null blocks in trailer */ +#define CHK_OFFSET 148 /* start of chksum field */ +#define BLNKSUM 256L /* sum of checksum field using ' ' */ +#endif /* _PAX_ */ + +/* + * Values used in typeflag field in all tar formats + * (only REGTYPE, LNKTYPE and SYMTYPE are used in old bsd tar headers) + */ +#define REGTYPE '0' /* Regular File */ +#define AREGTYPE '\0' /* Regular File */ +#define LNKTYPE '1' /* Link */ +#define SYMTYPE '2' /* Symlink */ +#define CHRTYPE '3' /* Character Special File */ +#define BLKTYPE '4' /* Block Special File */ +#define DIRTYPE '5' /* Directory */ +#define FIFOTYPE '6' /* FIFO */ +#define CONTTYPE '7' /* high perf file */ + +/* + * Mode field encoding of the different file types - values in octal + */ +#define TSUID 04000 /* Set UID on execution */ +#define TSGID 02000 /* Set GID on execution */ +#define TSVTX 01000 /* Reserved */ +#define TUREAD 00400 /* Read by owner */ +#define TUWRITE 00200 /* Write by owner */ +#define TUEXEC 00100 /* Execute/Search by owner */ +#define TGREAD 00040 /* Read by group */ +#define TGWRITE 00020 /* Write by group */ +#define TGEXEC 00010 /* Execute/Search by group */ +#define TOREAD 00004 /* Read by other */ +#define TOWRITE 00002 /* Write by other */ +#define TOEXEC 00001 /* Execute/Search by other */ + +#ifdef _PAX_ +/* + * Pad with a bit mask, much faster than doing a mod but only works on powers + * of 2. Macro below is for block of 512 bytes. + */ +#define TAR_PAD(x) ((512 - ((x) & 511)) & 511) +#endif /* _PAX_ */ + +/* + * structure of an old tar header as it appeared in BSD releases + */ +typedef struct { + char name[TNMSZ]; /* name of entry */ + char mode[8]; /* mode */ + char uid[8]; /* uid */ + char gid[8]; /* gid */ + char size[12]; /* size */ + char mtime[12]; /* modification time */ + char chksum[CHK_LEN]; /* checksum */ + char linkflag; /* norm, hard, or sym. */ + char linkname[TNMSZ]; /* linked to name */ +} HD_TAR; + +#ifdef _PAX_ +/* + * -o options for BSD tar to not write directories to the archive + */ +#define TAR_NODIR "nodir" +#define TAR_OPTION "write_opt" + +/* + * default device names + */ +#define DEV_0 "/dev/rmt0" +#define DEV_1 "/dev/rmt1" +#define DEV_4 "/dev/rmt4" +#define DEV_5 "/dev/rmt5" +#define DEV_7 "/dev/rmt7" +#define DEV_8 "/dev/rmt8" +#endif /* _PAX_ */ + +/* + * Data Interchange Format - Extended tar header format - POSIX 1003.1-1990 + */ +#define TPFSZ 155 +#define TMAGIC "ustar" /* ustar and a null */ +#define TMAGLEN 6 +#define TVERSION "00" /* 00 and no null */ +#define TVERSLEN 2 + +typedef struct { + char name[TNMSZ]; /* name of entry */ + char mode[8]; /* mode */ + char uid[8]; /* uid */ + char gid[8]; /* gid */ + char size[12]; /* size */ + char mtime[12]; /* modification time */ + char chksum[CHK_LEN]; /* checksum */ + char typeflag; /* type of file. */ + char linkname[TNMSZ]; /* linked to name */ + char magic[TMAGLEN]; /* magic cookie */ + char version[TVERSLEN]; /* version */ + char uname[32]; /* ascii owner name */ + char gname[32]; /* ascii group name */ + char devmajor[8]; /* major device number */ + char devminor[8]; /* minor device number */ + char prefix[TPFSZ]; /* linked to name */ +} HD_USTAR; diff --git a/bin/pax/tty_subs.c b/bin/pax/tty_subs.c new file mode 100644 index 0000000..0c7e80d --- /dev/null +++ b/bin/pax/tty_subs.c @@ -0,0 +1,245 @@ +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * 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[] = "@(#)tty_subs.c 8.2 (Berkeley) 4/18/94"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <fcntl.h> +#include <stdio.h> +#include <ctype.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include "pax.h" +#include "extern.h" +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +/* + * routines that deal with I/O to and from the user + */ + +#define DEVTTY "/dev/tty" /* device for interactive i/o */ +static FILE *ttyoutf = NULL; /* output pointing at control tty */ +static FILE *ttyinf = NULL; /* input pointing at control tty */ + +/* + * tty_init() + * try to open the controlling termina (if any) for this process. if the + * open fails, future ops that require user input will get an EOF + */ + +#if __STDC__ +int +tty_init(void) +#else +int +tty_init() +#endif +{ + int ttyfd; + + if ((ttyfd = open(DEVTTY, O_RDWR)) >= 0) { + if ((ttyoutf = fdopen(ttyfd, "w")) != NULL) { + if ((ttyinf = fdopen(ttyfd, "r")) != NULL) + return(0); + (void)fclose(ttyoutf); + } + (void)close(ttyfd); + } + + if (iflag) { + warn(1, "Fatal error, cannot open %s", DEVTTY); + return(-1); + } + return(0); +} + +/* + * tty_prnt() + * print a message using the specified format to the controlling tty + * if there is no controlling terminal, just return. + */ + +#if __STDC__ +void +tty_prnt(char *fmt, ...) +#else +void +tty_prnt(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +# if __STDC__ + va_start(ap, fmt); +# else + va_start(ap); +# endif + if (ttyoutf == NULL) + return; + (void)vfprintf(ttyoutf, fmt, ap); + va_end(ap); + (void)fflush(ttyoutf); +} + +/* + * tty_read() + * read a string from the controlling terminal if it is open into the + * supplied buffer + * Return: + * 0 if data was read, -1 otherwise. + */ + +#if __STDC__ +int +tty_read(char *str, int len) +#else +int +tty_read(str, len) + char *str; + int len; +#endif +{ + register char *pt; + + if ((--len <= 0) || (ttyinf == NULL) || (fgets(str,len,ttyinf) == NULL)) + return(-1); + *(str + len) = '\0'; + + /* + * strip off that trailing newline + */ + if ((pt = strchr(str, '\n')) != NULL) + *pt = '\0'; + return(0); +} + +/* + * warn() + * write a warning message to stderr. if "set" the exit value of pax + * will be non-zero. + */ + +#if __STDC__ +void +warn(int set, char *fmt, ...) +#else +void +warn(set, fmt, va_alist) + int set; + char *fmt; + va_dcl +#endif +{ + va_list ap; +# if __STDC__ + va_start(ap, fmt); +# else + va_start(ap); +# endif + if (set) + exit_val = 1; + /* + * when vflag we better ship out an extra \n to get this message on a + * line by itself + */ + if (vflag && vfpart) { + (void)fputc('\n', stderr); + vfpart = 0; + } + (void)fprintf(stderr, "%s: ", argv0); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fputc('\n', stderr); +} + +/* + * syswarn() + * write a warning message to stderr. if "set" the exit value of pax + * will be non-zero. + */ + +#if __STDC__ +void +syswarn(int set, int errnum, char *fmt, ...) +#else +void +syswarn(set, errnum, fmt, va_alist) + int set; + int errnum; + char *fmt; + va_dcl +#endif +{ + va_list ap; +# if __STDC__ + va_start(ap, fmt); +# else + va_start(ap); +# endif + if (set) + exit_val = 1; + /* + * when vflag we better ship out an extra \n to get this message on a + * line by itself + */ + if (vflag && vfpart) { + (void)fputc('\n', stderr); + vfpart = 0; + } + (void)fprintf(stderr, "%s: ", argv0); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + + /* + * format and print the errno + */ + if (errnum > 0) + (void)fprintf(stderr, " <%s>", sys_errlist[errnum]); + (void)fputc('\n', stderr); +} diff --git a/bin/ps/Makefile b/bin/ps/Makefile new file mode 100644 index 0000000..b98abc2 --- /dev/null +++ b/bin/ps/Makefile @@ -0,0 +1,11 @@ +# @(#)Makefile 8.1 (Berkeley) 6/2/93 + +PROG= ps +SRCS= fmt.c keyword.c nlist.c print.c ps.c +CFLAGS+=-I/sys +DPADD= ${LIBMATH} ${LIBKVM} +LDADD= -lm -lkvm +BINGRP= kmem +BINMODE=2555 + +.include <bsd.prog.mk> diff --git a/bin/ps/devname.c b/bin/ps/devname.c new file mode 100644 index 0000000..df94b91 --- /dev/null +++ b/bin/ps/devname.c @@ -0,0 +1,80 @@ +/* + * Copyright (c) 1989, 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)devname.c 8.1 (Berkeley) 5/31/93"; +#endif /* LIBC_SCCS and not lint */ + +#include <sys/types.h> + +#include <db.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <paths.h> +#include <stdio.h> +#include <string.h> + +char * +devname(dev, type) + dev_t dev; + mode_t type; +{ + struct { + mode_t type; + dev_t dev; + } bkey; + static DB *db; + static int failure; + DBT data, key; + + if (!db && !failure && + !(db = dbopen(_PATH_DEVDB, O_RDONLY, 0, DB_HASH, NULL))) { + warn("warning: %s", _PATH_DEVDB); + failure = 1; + } + if (failure) + return ("??"); + + /* + * Keys are a mode_t followed by a dev_t. The former is the type of + * the file (mode & S_IFMT), the latter is the st_rdev field. Be + * sure to clear any padding that may be found in bkey. + */ + memset(&bkey, 0, sizeof(bkey)); + bkey.dev = dev; + bkey.type = type; + key.data = &bkey; + key.size = sizeof(bkey); + return ((db->get)(db, &key, &data, 0) ? "??" : (char *)data.data); +} diff --git a/bin/ps/extern.h b/bin/ps/extern.h new file mode 100644 index 0000000..a23f3ec --- /dev/null +++ b/bin/ps/extern.h @@ -0,0 +1,85 @@ +/*- + * 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.3 (Berkeley) 4/2/94 + */ + +struct kinfo; +struct nlist; +struct var; +struct varent; + +extern fixpt_t ccpu; +extern int eval, fscale, mempages, nlistread, rawcpu; +extern int sumrusage, termwidth, totwidth; +extern VAR var[]; +extern VARENT *vhead; + +__BEGIN_DECLS +void command __P((KINFO *, VARENT *)); +void cputime __P((KINFO *, VARENT *)); +int donlist __P((void)); +void evar __P((KINFO *, VARENT *)); +char *fmt_argv __P((char **, char *, int)); +double getpcpu __P((KINFO *)); +double getpmem __P((KINFO *)); +void logname __P((KINFO *, VARENT *)); +void longtname __P((KINFO *, VARENT *)); +void lstarted __P((KINFO *, VARENT *)); +void maxrss __P((KINFO *, VARENT *)); +void nlisterr __P((struct nlist *)); +void p_rssize __P((KINFO *, VARENT *)); +void pagein __P((KINFO *, VARENT *)); +void parsefmt __P((char *)); +void pcpu __P((KINFO *, VARENT *)); +void pmem __P((KINFO *, VARENT *)); +void pri __P((KINFO *, VARENT *)); +void printheader __P((void)); +void pvar __P((KINFO *, VARENT *)); +void rssize __P((KINFO *, VARENT *)); +void runame __P((KINFO *, VARENT *)); +void rvar __P((KINFO *, VARENT *)); +void showkey __P((void)); +void started __P((KINFO *, VARENT *)); +void state __P((KINFO *, VARENT *)); +void tdev __P((KINFO *, VARENT *)); +void tname __P((KINFO *, VARENT *)); +#ifndef NEWVM +void trss __P((KINFO *, VARENT *)); +#endif +void tsize __P((KINFO *, VARENT *)); +void ucomm __P((KINFO *, VARENT *)); +void uname __P((KINFO *, VARENT *)); +void uvar __P((KINFO *, VARENT *)); +void vsize __P((KINFO *, VARENT *)); +void wchan __P((KINFO *, VARENT *)); +__END_DECLS diff --git a/bin/ps/fmt.c b/bin/ps/fmt.c new file mode 100644 index 0000000..3b6a1ec --- /dev/null +++ b/bin/ps/fmt.c @@ -0,0 +1,115 @@ +/*- + * Copyright (c) 1992, 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[] = "@(#)fmt.c 8.4 (Berkeley) 4/15/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <vis.h> +#include "ps.h" + +static char *cmdpart __P((char *)); +static char *shquote __P((char **)); + +/* + * XXX + * This is a stub until marc does the real one. + */ +static char * +shquote(argv) + char **argv; +{ + char **p, *dst, *src; + static char buf[4096]; /* XXX */ + + if (*argv == 0) { + buf[0] = 0; + return (buf); + } + dst = buf; + for (p = argv; (src = *p++) != 0; ) { + if (*src == 0) + continue; + strvis(dst, src, VIS_NL | VIS_CSTYLE); + while (*dst) + dst++; + *dst++ = ' '; + } + *dst = '\0'; + return (buf); +} + +static char * +cmdpart(arg0) + char *arg0; +{ + char *cp; + + return ((cp = strrchr(arg0, '/')) != NULL ? cp + 1 : arg0); +} + +char * +fmt_argv(argv, cmd, maxlen) + char **argv; + char *cmd; + int maxlen; +{ + int len; + char *ap, *cp; + + if (argv == 0 || argv[0] == 0) { + if (cmd == NULL) + return (""); + ap = NULL; + len = maxlen + 3; + } else { + ap = shquote(argv); + len = strlen(ap) + maxlen + 4; + } + if ((cp = malloc(len)) == NULL) + return (NULL); + if (ap == NULL) + sprintf(cp, "(%.*s)", maxlen, cmd); + else if (strncmp(cmdpart(argv[0]), cmd, maxlen) != 0) + sprintf(cp, "%s (%.*s)", ap, maxlen, cmd); + else + (void) strcpy(cp, ap); + return (cp); +} diff --git a/bin/ps/keyword.c b/bin/ps/keyword.c new file mode 100644 index 0000000..fb1133b --- /dev/null +++ b/bin/ps/keyword.c @@ -0,0 +1,354 @@ +/*- + * Copyright (c) 1990, 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[] = "@(#)keyword.c 8.5 (Berkeley) 4/2/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/proc.h> + +#include <err.h> +#include <errno.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "ps.h" + +#ifdef P_PPWAIT +#define NEWVM +#endif + +#ifdef NEWVM +#include <sys/ucred.h> +#include <sys/sysctl.h> +#endif + +static VAR *findvar __P((char *)); +static int vcmp __P((const void *, const void *)); + +#ifdef NOTINUSE +int utime(), stime(), ixrss(), idrss(), isrss(); + {{"utime"}, "UTIME", USER, utime, 4}, + {{"stime"}, "STIME", USER, stime, 4}, + {{"ixrss"}, "IXRSS", USER, ixrss, 4}, + {{"idrss"}, "IDRSS", USER, idrss, 4}, + {{"isrss"}, "ISRSS", USER, isrss, 4}, +#endif + +/* Compute offset in common structures. */ +#define POFF(x) offsetof(struct proc, x) +#define EOFF(x) offsetof(struct eproc, x) +#define UOFF(x) offsetof(struct usave, x) +#define ROFF(x) offsetof(struct rusage, x) + +#define UIDFMT "u" +#define UIDLEN 5 +#define PIDFMT "d" +#define PIDLEN 5 +#define USERLEN 8 + +VAR var[] = { +#ifdef NEWVM + {"%cpu", "%CPU", NULL, 0, pcpu, 4}, + {"%mem", "%MEM", NULL, 0, pmem, 4}, + {"acflag", "ACFLG", NULL, 0, pvar, 3, POFF(p_acflag), USHORT, "x"}, + {"acflg", "", "acflag"}, + {"blocked", "", "sigmask"}, + {"caught", "", "sigcatch"}, + {"command", "COMMAND", NULL, COMM|LJUST|USER, command, 16}, + {"cpu", "CPU", NULL, 0, pvar, 3, POFF(p_estcpu), ULONG, "d"}, + {"cputime", "", "time"}, + {"f", "F", NULL, 0, pvar, 7, POFF(p_flag), LONG, "x"}, + {"flags", "", "f"}, + {"ignored", "", "sigignore"}, + {"inblk", "INBLK", NULL, USER, rvar, 4, ROFF(ru_inblock), LONG, "d"}, + {"inblock", "", "inblk"}, + {"jobc", "JOBC", NULL, 0, evar, 4, EOFF(e_jobc), SHORT, "d"}, + {"ktrace", "KTRACE", NULL, 0, pvar, 8, POFF(p_traceflag), LONG, "x"}, + {"ktracep", "KTRACEP", NULL, 0, pvar, 8, POFF(p_tracep), LONG, "x"}, + {"lim", "LIM", NULL, 0, maxrss, 5}, + {"login", "LOGIN", NULL, LJUST, logname, MAXLOGNAME}, + {"logname", "", "login"}, + {"lstart", "STARTED", NULL, LJUST|USER, lstarted, 28}, + {"majflt", "MAJFLT", NULL, USER, rvar, 4, ROFF(ru_majflt), LONG, "d"}, + {"minflt", "MINFLT", NULL, USER, rvar, 4, ROFF(ru_minflt), LONG, "d"}, + {"msgrcv", "MSGRCV", NULL, USER, rvar, 4, ROFF(ru_msgrcv), LONG, "d"}, + {"msgsnd", "MSGSND", NULL, USER, rvar, 4, ROFF(ru_msgsnd), LONG, "d"}, + {"ni", "", "nice"}, + {"nice", "NI", NULL, 0, pvar, 2, POFF(p_nice), CHAR, "d"}, + {"nivcsw", "NIVCSW", NULL, USER, rvar, 5, ROFF(ru_nivcsw), LONG, "d"}, + {"nsignals", "", "nsigs"}, + {"nsigs", "NSIGS", NULL, USER, rvar, 4, ROFF(ru_nsignals), LONG, "d"}, + {"nswap", "NSWAP", NULL, USER, rvar, 4, ROFF(ru_nswap), LONG, "d"}, + {"nvcsw", "NVCSW", NULL, USER, rvar, 5, ROFF(ru_nvcsw), LONG, "d"}, + {"nwchan", "WCHAN", NULL, 0, pvar, 6, POFF(p_wchan), KPTR, "x"}, + {"oublk", "OUBLK", NULL, USER, rvar, 4, ROFF(ru_oublock), LONG, "d"}, + {"oublock", "", "oublk"}, + {"p_ru", "P_RU", NULL, 0, pvar, 6, POFF(p_ru), KPTR, "x"}, + {"paddr", "PADDR", NULL, 0, evar, 6, EOFF(e_paddr), KPTR, "x"}, + {"pagein", "PAGEIN", NULL, USER, pagein, 6}, + {"pcpu", "", "%cpu"}, + {"pending", "", "sig"}, + {"pgid", "PGID", NULL, 0, evar, PIDLEN, EOFF(e_pgid), ULONG, PIDFMT}, + {"pid", "PID", NULL, 0, pvar, PIDLEN, POFF(p_pid), LONG, PIDFMT}, + {"pmem", "", "%mem"}, + {"ppid", "PPID", NULL, 0, evar, PIDLEN, EOFF(e_ppid), LONG, PIDFMT}, + {"pri", "PRI", NULL, 0, pri, 3}, + {"re", "RE", NULL, 0, pvar, 3, POFF(p_swtime), ULONG, "d"}, + {"rgid", "RGID", NULL, 0, evar, UIDLEN, EOFF(e_pcred.p_rgid), + ULONG, UIDFMT}, + {"rlink", "RLINK", NULL, 0, pvar, 8, POFF(p_back), KPTR, "x"}, + {"rss", "RSS", NULL, 0, p_rssize, 4}, + {"rssize", "", "rsz"}, + {"rsz", "RSZ", NULL, 0, rssize, 4}, + {"ruid", "RUID", NULL, 0, evar, UIDLEN, EOFF(e_pcred.p_ruid), + ULONG, UIDFMT}, + {"ruser", "RUSER", NULL, LJUST, runame, USERLEN}, + {"sess", "SESS", NULL, 0, evar, 6, EOFF(e_sess), KPTR, "x"}, + {"sig", "PENDING", NULL, 0, pvar, 8, POFF(p_siglist), LONG, "x"}, + {"sigcatch", "CAUGHT", NULL, 0, pvar, 8, POFF(p_sigcatch), LONG, "x"}, + {"sigignore", "IGNORED", + NULL, 0, pvar, 8, POFF(p_sigignore), LONG, "x"}, + {"sigmask", "BLOCKED", NULL, 0, pvar, 8, POFF(p_sigmask), LONG, "x"}, + {"sl", "SL", NULL, 0, pvar, 3, POFF(p_slptime), ULONG, "d"}, + {"start", "STARTED", NULL, LJUST|USER, started, 8}, + {"stat", "", "state"}, + {"state", "STAT", NULL, 0, state, 4}, + {"svgid", "SVGID", + NULL, 0, evar, UIDLEN, EOFF(e_pcred.p_svgid), ULONG, UIDFMT}, + {"svuid", "SVUID", + NULL, 0, evar, UIDLEN, EOFF(e_pcred.p_svuid), ULONG, UIDFMT}, + {"tdev", "TDEV", NULL, 0, tdev, 4}, + {"time", "TIME", NULL, USER, cputime, 9}, + {"tpgid", "TPGID", NULL, 0, evar, 4, EOFF(e_tpgid), ULONG, PIDFMT}, + {"tsess", "TSESS", NULL, 0, evar, 6, EOFF(e_tsess), KPTR, "x"}, + {"tsiz", "TSIZ", NULL, 0, tsize, 4}, + {"tt", "TT", NULL, LJUST, tname, 3}, + {"tty", "TTY", NULL, LJUST, longtname, 8}, + {"ucomm", "UCOMM", NULL, LJUST, ucomm, MAXCOMLEN}, + {"uid", "UID", NULL, 0, evar, UIDLEN, EOFF(e_ucred.cr_uid), + ULONG, UIDFMT}, + {"upr", "UPR", NULL, 0, pvar, 3, POFF(p_usrpri), CHAR, "d"}, + {"user", "USER", NULL, LJUST, uname, USERLEN}, + {"usrpri", "", "upr"}, + {"vsize", "", "vsz"}, + {"vsz", "VSZ", NULL, 0, vsize, 5}, + {"wchan", "WCHAN", NULL, LJUST, wchan, 6}, + {"xstat", "XSTAT", NULL, 0, pvar, 4, POFF(p_xstat), USHORT, "x"}, +#else + {"%cpu", "%CPU", NULL, 0, pcpu, 4}, + {"%mem", "%MEM", NULL, 0, pmem, 4}, + {"acflag", "ACFLG", NULL, USER, uvar, 3, UOFF(u_acflag), SHORT, "x"}, + {"acflg", "", "acflag"}, + {"blocked", "", "sigmask"}, + {"caught", "", "sigcatch"}, + {"command", "COMMAND", NULL, COMM|LJUST|USER, command, 16}, + {"cpu", "CPU", NULL, 0, pvar, 3, POFF(p_cpu), ULONG, "d"}, + {"cputime", "", "time"}, + {"f", "F", NULL, 0, pvar, 7, POFF(p_flag), LONG, "x"}, + {"flags", "", "f"}, + {"ignored", "", "sigignore"}, + {"inblk", "INBLK", NULL, USER, rvar, 4, ROFF(ru_inblock), LONG, "d"}, + {"inblock", "", "inblk"}, + {"jobc", "JOBC", NULL, 0, evar, 4, EOFF(e_jobc), SHORT, "d"}, + {"ktrace", "KTRACE", NULL, 0, pvar, 8, POFF(p_traceflag), LONG, "x"}, + {"ktracep", "KTRACEP", NULL, 0, pvar, 8, POFF(p_tracep), LONG, "x"}, + {"lim", "LIM", NULL, 0, maxrss, 5}, + {"logname", "LOGNAME", NULL, LJUST, logname, MAXLOGNAME}, + {"lstart", "STARTED", NULL, LJUST|USER, lstarted, 28}, + {"majflt", "MAJFLT", NULL, USER, rvar, 4, ROFF(ru_majflt), LONG, "d"}, + {"minflt", "MINFLT", NULL, USER, rvar, 4, ROFF(ru_minflt), LONG, "d"}, + {"msgrcv", "MSGRCV", NULL, USER, rvar, 4, ROFF(ru_msgrcv), LONG, "d"}, + {"msgsnd", "MSGSND", NULL, USER, rvar, 4, ROFF(ru_msgsnd), LONG, "d"}, + {"ni", "", "nice"}, + {"nice", "NI", NULL, 0, pvar, 2, POFF(p_nice), CHAR, "d"}, + {"nivcsw", "NIVCSW", NULL, USER, rvar, 5, ROFF(ru_nivcsw), LONG, "d"}, + {"nsignals", "", "nsigs"}, + {"nsigs", "NSIGS", NULL, USER, rvar, 4, ROFF(ru_nsignals), LONG, "d"}, + {"nswap", "NSWAP", NULL, USER, rvar, 4, ROFF(ru_nswap), LONG, "d"}, + {"nvcsw", "NVCSW", NULL, USER, rvar, 5, ROFF(ru_nvcsw), LONG, "d"}, + {"nwchan", "WCHAN", NULL, 0, pvar, 6, POFF(p_wchan), KPTR, "x"}, + {"oublk", "OUBLK", NULL, USER, rvar, 4, ROFF(ru_oublock), LONG, "d"}, + {"oublock", "", "oublk"}, + {"p_ru", "P_RU", NULL, 0, pvar, 6, POFF(p_ru), KPTR, "x"}, + {"paddr", "PADDR", NULL, 0, evar, 6, EOFF(e_paddr), KPTR, "x"}, + {"pagein", "PAGEIN", NULL, USER, pagein, 6}, + {"pcpu", "", "%cpu"}, + {"pending", "", "sig"}, + {"pgid", "PGID", NULL, 0, evar, PIDLEN, EOFF(e_pgid), ULONG, PIDFMT}, + {"pid", "PID", NULL, 0, pvar, PIDLEN, POFF(p_pid), LONG, PIDFMT}, + {"pmem", "", "%mem"}, + {"poip", "POIP", NULL, 0, pvar, 4, POFF(p_poip), SHORT, "d"}, + {"ppid", "PPID", NULL, 0, pvar, PIDLEN, POFF(p_ppid), LONG, PIDFMT}, + {"pri", "PRI", NULL, 0, pri, 3}, + {"re", "RE", NULL, 0, pvar, 3, POFF(p_swtime), ULONG, "d"}, + {"rgid", "RGID", NULL, 0, pvar, UIDLEN, POFF(p_rgid), USHORT, UIDFMT}, + {"rlink", "RLINK", NULL, 0, pvar, 8, POFF(p_rlink), KPTR, "x"}, + {"rss", "RSS", NULL, 0, p_rssize, 4}, + {"rssize", "", "rsz"}, + {"rsz", "RSZ", NULL, 0, rssize, 4}, + {"ruid", "RUID", NULL, 0, pvar, UIDLEN, POFF(p_ruid), USHORT, UIDFMT}, + {"ruser", "RUSER", NULL, LJUST, runame, USERLEN}, + {"sess", "SESS", NULL, 0, evar, 6, EOFF(e_sess), KPTR, "x"}, + {"sig", "PENDING", NULL, 0, pvar, 8, POFF(p_sig), LONG, "x"}, + {"sigcatch", "CAUGHT", NULL, 0, pvar, 8, POFF(p_sigcatch), LONG, "x"}, + {"sigignore", "IGNORED", + NULL, 0, pvar, 8, POFF(p_sigignore), LONG, "x"}, + {"sigmask", "BLOCKED", NULL, 0, pvar, 8, POFF(p_sigmask), LONG, "x"}, + {"sl", "SL", NULL, 0, pvar, 3, POFF(p_slptime), ULONG, "d"}, + {"start", "STARTED", NULL, LJUST|USER, started, 8}, + {"stat", "", "state"}, + {"state", "STAT", NULL, 0, state, 4}, + {"svgid", "SVGID", + NULL, 0, pvar, UIDLEN, POFF(p_svgid), USHORT, UIDFMT}, + {"svuid", "SVUID", + NULL, 0, pvar, UIDLEN, POFF(p_svuid), USHORT, UIDFMT}, + {"tdev", "TDEV", NULL, 0, tdev, 4}, + {"time", "TIME", NULL, USER, cputime, 9}, + {"tpgid", "TPGID", NULL, 0, evar, 4, EOFF(e_tpgid), ULONG, PIDFMT}, + {"trs", "TRS", NULL, 0, trss, 3}, + {"tsess", "TSESS", NULL, 0, evar, 6, EOFF(e_tsess), KPTR, "x"}, + {"tsiz", "TSIZ", NULL, 0, tsize, 4}, + {"tt", "TT", NULL, LJUST, tname, 3}, + {"tty", "TTY", NULL, LJUST, longtname, 8}, + {"ucomm", "UCOMM", NULL, LJUST, ucomm, MAXCOMLEN}, + {"uid", "UID", NULL, 0, pvar, UIDLEN, POFF(p_uid),USHORT, UIDFMT}, + {"upr", "UPR", NULL, 0, pvar, 3, POFF(p_usrpri), CHAR, "d"}, + {"uprocp", "UPROCP", NULL, USER, uvar, 6, UOFF(u_procp), KPTR, "x"}, + {"user", "USER", NULL, LJUST, uname, USERLEN}, + {"usrpri", "", "upr"}, + {"vsize", "", "vsz"}, + {"vsz", "VSZ", NULL, 0, vsize, 5}, + {"wchan", "WCHAN", NULL, LJUST, wchan, 6}, + {"xstat", "XSTAT", NULL, 0, pvar, 4, POFF(p_xstat), USHORT, "x"}, +#endif + {""}, +}; + +void +showkey() +{ + VAR *v; + int i; + char *p, *sep; + + i = 0; + sep = ""; + for (v = var; *(p = v->name); ++v) { + int len = strlen(p); + if (termwidth && (i += len + 1) > termwidth) { + i = len; + sep = "\n"; + } + (void) printf("%s%s", sep, p); + sep = " "; + } + (void) printf("\n"); +} + +void +parsefmt(p) + char *p; +{ + static struct varent *vtail; + +#define FMTSEP " \t,\n" + while (p && *p) { + char *cp; + VAR *v; + struct varent *vent; + + while ((cp = strsep(&p, FMTSEP)) != NULL && *cp == '\0') + /* void */; + if (!(v = findvar(cp))) + continue; + if ((vent = malloc(sizeof(struct varent))) == NULL) + err(1, NULL); + vent->var = v; + vent->next = NULL; + if (vhead == NULL) + vhead = vtail = vent; + else { + vtail->next = vent; + vtail = vent; + } + } + if (!vhead) + errx(1, "no valid keywords"); +} + +static VAR * +findvar(p) + char *p; +{ + VAR *v, key; + char *hp; + int vcmp(); + + key.name = p; + + hp = strchr(p, '='); + if (hp) + *hp++ = '\0'; + + key.name = p; + v = bsearch(&key, var, sizeof(var)/sizeof(VAR) - 1, sizeof(VAR), vcmp); + + if (v && v->alias) { + if (hp) { + warnx("%s: illegal keyword specification", p); + eval = 1; + } + parsefmt(v->alias); + return ((VAR *)NULL); + } + if (!v) { + warnx("%s: keyword not found", p); + eval = 1; + } + if (hp) + v->header = hp; + return (v); +} + +static int +vcmp(a, b) + const void *a, *b; +{ + return (strcmp(((VAR *)a)->name, ((VAR *)b)->name)); +} diff --git a/bin/ps/nlist.c b/bin/ps/nlist.c new file mode 100644 index 0000000..11199ca --- /dev/null +++ b/bin/ps/nlist.c @@ -0,0 +1,136 @@ +/*- + * Copyright (c) 1990, 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[] = "@(#)nlist.c 8.4 (Berkeley) 4/2/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/proc.h> +#include <sys/resource.h> + +#include <err.h> +#include <errno.h> +#include <kvm.h> +#include <nlist.h> +#include <stdio.h> +#include <string.h> + +#include "ps.h" + +#ifdef P_PPWAIT +#define NEWVM +#endif + +struct nlist psnl[] = { + {"_fscale"}, +#define X_FSCALE 0 + {"_ccpu"}, +#define X_CCPU 1 +#ifdef NEWVM + {"_avail_start"}, +#define X_AVAILSTART 2 + {"_avail_end"}, +#define X_AVAILEND 3 +#else + {"_ecmx"}, +#define X_ECMX 2 +#endif + {NULL} +}; + +fixpt_t ccpu; /* kernel _ccpu variable */ +int nlistread; /* if nlist already read. */ +int mempages; /* number of pages of phys. memory */ +int fscale; /* kernel _fscale variable */ + +extern kvm_t *kd; + +#define kread(x, v) \ + kvm_read(kd, psnl[x].n_value, (char *)&v, sizeof v) != sizeof(v) + +int +donlist() +{ + int rval; +#ifdef NEWVM + int tmp; +#endif + + rval = 0; + nlistread = 1; + if (kvm_nlist(kd, psnl)) { + nlisterr(psnl); + eval = 1; + return (1); + } + if (kread(X_FSCALE, fscale)) { + warnx("fscale: %s", kvm_geterr(kd)); + eval = rval = 1; + } +#ifdef NEWVM + if (kread(X_AVAILEND, mempages)) { + warnx("avail_start: %s", kvm_geterr(kd)); + eval = rval = 1; + } + if (kread(X_AVAILSTART, tmp)) { + warnx("avail_end: %s", kvm_geterr(kd)); + eval = rval = 1; + } + mempages -= tmp; +#else + if (kread(X_ECMX, mempages)) { + warnx("ecmx: %s", kvm_geterr(kd)); + eval = rval = 1; + } +#endif + if (kread(X_CCPU, ccpu)) { + warnx("ccpu: %s", kvm_geterr(kd)); + eval = rval = 1; + } + return (rval); +} + +void +nlisterr(nl) + struct nlist nl[]; +{ + int i; + + (void)fprintf(stderr, "ps: nlist: can't find following symbols:"); + for (i = 0; nl[i].n_name != NULL; i++) + if (nl[i].n_value == 0) + (void)fprintf(stderr, " %s", nl[i].n_name); + (void)fprintf(stderr, "\n"); +} diff --git a/bin/ps/print.c b/bin/ps/print.c new file mode 100644 index 0000000..1208efa --- /dev/null +++ b/bin/ps/print.c @@ -0,0 +1,741 @@ +/*- + * Copyright (c) 1990, 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[] = "@(#)print.c 8.6 (Berkeley) 4/16/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/proc.h> +#include <sys/stat.h> + +#ifdef P_PPWAIT +#define NEWVM +#endif + +#ifdef NEWVM +#include <sys/ucred.h> +#include <sys/sysctl.h> +#include <vm/vm.h> +#else +#include <machine/pte.h> +#include <sys/vmparam.h> +#include <sys/vm.h> +#endif + +#include <err.h> +#include <math.h> +#include <nlist.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <vis.h> +#include <tzfile.h> + +#include "ps.h" + +void +printheader() +{ + VAR *v; + struct varent *vent; + + for (vent = vhead; vent; vent = vent->next) { + v = vent->var; + if (v->flag & LJUST) { + if (vent->next == NULL) /* last one */ + (void)printf("%s", v->header); + else + (void)printf("%-*s", v->width, v->header); + } else + (void)printf("%*s", v->width, v->header); + if (vent->next != NULL) + (void)putchar(' '); + } + (void)putchar('\n'); +} + +void +command(k, ve) + KINFO *k; + VARENT *ve; +{ + VAR *v; + int left; + char *cp, *vis_env, *vis_args; + + if ((vis_args = malloc(strlen(k->ki_args) * 4 + 1)) == NULL) + err(1, NULL); + strvis(vis_args, k->ki_args, VIS_TAB | VIS_NL | VIS_NOSLASH); + if (k->ki_env) { + if ((vis_env = malloc(strlen(k->ki_env) * 4 + 1)) == NULL) + err(1, NULL); + strvis(vis_env, k->ki_env, VIS_TAB | VIS_NL | VIS_NOSLASH); + } else + vis_env = NULL; + + v = ve->var; + if (ve->next == NULL) { + /* last field */ + if (termwidth == UNLIMITED) { + if (vis_env) + (void)printf("%s ", vis_env); + (void)printf("%s", vis_args); + } else { + left = termwidth - (totwidth - v->width); + if (left < 1) /* already wrapped, just use std width */ + left = v->width; + if ((cp = vis_env) != NULL) { + while (--left >= 0 && *cp) + (void)putchar(*cp++); + if (--left >= 0) + putchar(' '); + } + for (cp = vis_args; --left >= 0 && *cp != '\0';) + (void)putchar(*cp++); + } + } else + /* XXX env? */ + (void)printf("%-*.*s", v->width, v->width, vis_args); + free(vis_args); + if (vis_env != NULL) + free(vis_env); +} + +void +ucomm(k, ve) + KINFO *k; + VARENT *ve; +{ + VAR *v; + + v = ve->var; + (void)printf("%-*s", v->width, KI_PROC(k)->p_comm); +} + +void +logname(k, ve) + KINFO *k; + VARENT *ve; +{ + VAR *v; + + v = ve->var; +#ifndef NEWVM + (void)printf("%-*s", v->width, KI_PROC(k)->p_logname); +#else + (void)printf("%-*s", v->width, KI_EPROC(k)->e_login); +#endif +} + +void +state(k, ve) + KINFO *k; + VARENT *ve; +{ + struct proc *p; + int flag; + char *cp; + VAR *v; + char buf[16]; + + v = ve->var; + p = KI_PROC(k); + flag = p->p_flag; + cp = buf; + + switch (p->p_stat) { + + case SSTOP: + *cp = 'T'; + break; + + case SSLEEP: + if (flag & P_SINTR) /* interuptable (long) */ + *cp = p->p_slptime >= MAXSLP ? 'I' : 'S'; + else + *cp = 'D'; + break; + + case SRUN: + case SIDL: + *cp = 'R'; + break; + + case SZOMB: + *cp = 'Z'; + break; + + default: + *cp = '?'; + } + cp++; + if (flag & P_INMEM) { +#ifndef NEWVM + if (p->p_rssize > p->p_maxrss) + *cp++ = '>'; +#endif + } else + *cp++ = 'W'; + if (p->p_nice < NZERO) + *cp++ = '<'; + else if (p->p_nice > NZERO) + *cp++ = 'N'; +#ifndef NEWVM + if (flag & SUANOM) + *cp++ = 'A'; + else if (flag & SSEQL) + *cp++ = 'S'; +#endif + if (flag & P_TRACED) + *cp++ = 'X'; + if (flag & P_WEXIT && p->p_stat != SZOMB) + *cp++ = 'E'; +#ifdef NEWVM + if (flag & P_PPWAIT) +#else + if (flag & SVFORK) +#endif + *cp++ = 'V'; +#ifdef NEWVM + if (flag & (P_SYSTEM | P_NOSWAP | P_PHYSIO)) +#else + if (flag & (SSYS|SLOCK|SULOCK|SKEEP|SPHYSIO)) +#endif + *cp++ = 'L'; + if (KI_EPROC(k)->e_flag & EPROC_SLEADER) + *cp++ = 's'; + if ((flag & P_CONTROLT) && KI_EPROC(k)->e_pgid == KI_EPROC(k)->e_tpgid) + *cp++ = '+'; + *cp = '\0'; + (void)printf("%-*s", v->width, buf); +} + +void +pri(k, ve) + KINFO *k; + VARENT *ve; +{ + VAR *v; + + v = ve->var; + (void)printf("%*d", v->width, KI_PROC(k)->p_priority - PZERO); +} + +void +uname(k, ve) + KINFO *k; + VARENT *ve; +{ + VAR *v; + + v = ve->var; +#ifndef NEWVM + (void)printf("%-*s", + (int)v->width, user_from_uid(KI_PROC(k)->p_uid, 0)); +#else + (void)printf("%-*s", + (int)v->width, user_from_uid(KI_EPROC(k)->e_ucred.cr_uid, 0)); +#endif +} + +void +runame(k, ve) + KINFO *k; + VARENT *ve; +{ + VAR *v; + + v = ve->var; +#ifndef NEWVM + (void)printf("%-*s", + (int)v->width, user_from_uid(KI_PROC(k)->p_ruid, 0)); +#else + (void)printf("%-*s", + (int)v->width, user_from_uid(KI_EPROC(k)->e_pcred.p_ruid, 0)); +#endif +} + +void +tdev(k, ve) + KINFO *k; + VARENT *ve; +{ + VAR *v; + dev_t dev; + char buff[16]; + + v = ve->var; + dev = KI_EPROC(k)->e_tdev; + if (dev == NODEV) + (void)printf("%*s", v->width, "??"); + else { + (void)snprintf(buff, sizeof(buff), + "%d/%d", major(dev), minor(dev)); + (void)printf("%*s", v->width, buff); + } +} + +void +tname(k, ve) + KINFO *k; + VARENT *ve; +{ + VAR *v; + dev_t dev; + char *ttname; + + v = ve->var; + dev = KI_EPROC(k)->e_tdev; + if (dev == NODEV || (ttname = devname(dev, S_IFCHR)) == NULL) + (void)printf("%-*s", v->width, "??"); + else { + if (strncmp(ttname, "tty", 3) == 0) + ttname += 3; + (void)printf("%*.*s%c", v->width-1, v->width-1, ttname, + KI_EPROC(k)->e_flag & EPROC_CTTY ? ' ' : '-'); + } +} + +void +longtname(k, ve) + KINFO *k; + VARENT *ve; +{ + VAR *v; + dev_t dev; + char *ttname; + + v = ve->var; + dev = KI_EPROC(k)->e_tdev; + if (dev == NODEV || (ttname = devname(dev, S_IFCHR)) == NULL) + (void)printf("%-*s", v->width, "??"); + else + (void)printf("%-*s", v->width, ttname); +} + +void +started(k, ve) + KINFO *k; + VARENT *ve; +{ + VAR *v; + static time_t now; + struct tm *tp; + char buf[100]; + + v = ve->var; + if (!k->ki_u.u_valid) { + (void)printf("%-*s", v->width, "-"); + return; + } + + tp = localtime(&k->ki_u.u_start.tv_sec); + if (!now) + (void)time(&now); + if (now - k->ki_u.u_start.tv_sec < 24 * SECSPERHOUR) { + /* I *hate* SCCS... */ + static char fmt[] = __CONCAT("%l:%", "M%p"); + (void)strftime(buf, sizeof(buf) - 1, fmt, tp); + } else if (now - k->ki_u.u_start.tv_sec < 7 * SECSPERDAY) { + /* I *hate* SCCS... */ + static char fmt[] = __CONCAT("%a%", "I%p"); + (void)strftime(buf, sizeof(buf) - 1, fmt, tp); + } else + (void)strftime(buf, sizeof(buf) - 1, "%e%b%y", tp); + (void)printf("%-*s", v->width, buf); +} + +void +lstarted(k, ve) + KINFO *k; + VARENT *ve; +{ + VAR *v; + char buf[100]; + + v = ve->var; + if (!k->ki_u.u_valid) { + (void)printf("%-*s", v->width, "-"); + return; + } + (void)strftime(buf, sizeof(buf) -1, "%C", + localtime(&k->ki_u.u_start.tv_sec)); + (void)printf("%-*s", v->width, buf); +} + +void +wchan(k, ve) + KINFO *k; + VARENT *ve; +{ + VAR *v; + + v = ve->var; + if (KI_PROC(k)->p_wchan) { + if (KI_PROC(k)->p_wmesg) + (void)printf("%-*.*s", v->width, v->width, + KI_EPROC(k)->e_wmesg); + else + (void)printf("%-*x", v->width, + (int)KI_PROC(k)->p_wchan &~ KERNBASE); + } else + (void)printf("%-*s", v->width, "-"); +} + +#define pgtok(a) (((a)*NBPG)/1024) + +void +vsize(k, ve) + KINFO *k; + VARENT *ve; +{ + VAR *v; + + v = ve->var; + (void)printf("%*d", v->width, +#ifndef NEWVM + pgtok(KI_PROC(k)->p_dsize + + KI_PROC(k)->p_ssize + KI_EPROC(k)->e_xsize)); +#else + pgtok(KI_EPROC(k)->e_vm.vm_dsize + KI_EPROC(k)->e_vm.vm_ssize + + KI_EPROC(k)->e_vm.vm_tsize)); +#endif +} + +void +rssize(k, ve) + KINFO *k; + VARENT *ve; +{ + VAR *v; + + v = ve->var; +#ifndef NEWVM + (void)printf("%*d", v->width, + pgtok(KI_PROC(k)->p_rssize + (KI_EPROC(k)->e_xccount ? + (KI_EPROC(k)->e_xrssize / KI_EPROC(k)->e_xccount) : 0))); +#else + /* XXX don't have info about shared */ + (void)printf("%*d", v->width, pgtok(KI_EPROC(k)->e_vm.vm_rssize)); +#endif +} + +void +p_rssize(k, ve) /* doesn't account for text */ + KINFO *k; + VARENT *ve; +{ + VAR *v; + + v = ve->var; +#ifndef NEWVM + (void)printf("%*d", v->width, pgtok(KI_PROC(k)->p_rssize)); +#else + (void)printf("%*d", v->width, pgtok(KI_EPROC(k)->e_vm.vm_rssize)); +#endif +} + +void +cputime(k, ve) + KINFO *k; + VARENT *ve; +{ + VAR *v; + long secs; + long psecs; /* "parts" of a second. first micro, then centi */ + char obuff[128]; + + v = ve->var; + if (KI_PROC(k)->p_stat == SZOMB || !k->ki_u.u_valid) { + secs = 0; + psecs = 0; + } else { + /* + * This counts time spent handling interrupts. We could + * fix this, but it is not 100% trivial (and interrupt + * time fractions only work on the sparc anyway). XXX + */ + secs = KI_PROC(k)->p_rtime.tv_sec; + psecs = KI_PROC(k)->p_rtime.tv_usec; + if (sumrusage) { + secs += k->ki_u.u_cru.ru_utime.tv_sec + + k->ki_u.u_cru.ru_stime.tv_sec; + psecs += k->ki_u.u_cru.ru_utime.tv_usec + + k->ki_u.u_cru.ru_stime.tv_usec; + } + /* + * round and scale to 100's + */ + psecs = (psecs + 5000) / 10000; + secs += psecs / 100; + psecs = psecs % 100; + } + (void)snprintf(obuff, sizeof(obuff), + "%3ld:%02ld.%02ld", secs/60, secs%60, psecs); + (void)printf("%*s", v->width, obuff); +} + +double +getpcpu(k) + KINFO *k; +{ + struct proc *p; + static int failure; + + if (!nlistread) + failure = donlist(); + if (failure) + return (0.0); + + p = KI_PROC(k); +#define fxtofl(fixpt) ((double)(fixpt) / fscale) + + /* XXX - I don't like this */ + if (p->p_swtime == 0 || (p->p_flag & P_INMEM) == 0) + return (0.0); + if (rawcpu) + return (100.0 * fxtofl(p->p_pctcpu)); + return (100.0 * fxtofl(p->p_pctcpu) / + (1.0 - exp(p->p_swtime * log(fxtofl(ccpu))))); +} + +void +pcpu(k, ve) + KINFO *k; + VARENT *ve; +{ + VAR *v; + + v = ve->var; + (void)printf("%*.1f", v->width, getpcpu(k)); +} + +double +getpmem(k) + KINFO *k; +{ + static int failure; + struct proc *p; + struct eproc *e; + double fracmem; + int szptudot; + + if (!nlistread) + failure = donlist(); + if (failure) + return (0.0); + + p = KI_PROC(k); + e = KI_EPROC(k); + if ((p->p_flag & P_INMEM) == 0) + return (0.0); +#ifndef NEWVM + szptudot = UPAGES + clrnd(ctopt(p->p_dsize + p->p_ssize + e->e_xsize)); + fracmem = ((float)p->p_rssize + szptudot)/CLSIZE/mempages; + if (p->p_textp && e->e_xccount) + fracmem += ((float)e->e_xrssize)/CLSIZE/e->e_xccount/mempages; +#else + /* XXX want pmap ptpages, segtab, etc. (per architecture) */ + szptudot = UPAGES; + /* XXX don't have info about shared */ + fracmem = ((float)e->e_vm.vm_rssize + szptudot)/CLSIZE/mempages; +#endif + return (100.0 * fracmem); +} + +void +pmem(k, ve) + KINFO *k; + VARENT *ve; +{ + VAR *v; + + v = ve->var; + (void)printf("%*.1f", v->width, getpmem(k)); +} + +void +pagein(k, ve) + KINFO *k; + VARENT *ve; +{ + VAR *v; + + v = ve->var; + (void)printf("%*d", v->width, + k->ki_u.u_valid ? k->ki_u.u_ru.ru_majflt : 0); +} + +void +maxrss(k, ve) + KINFO *k; + VARENT *ve; +{ + VAR *v; + + v = ve->var; +#ifndef NEWVM /* not yet */ + if (KI_PROC(k)->p_maxrss != (RLIM_INFINITY/NBPG)) + (void)printf("%*d", v->width, pgtok(KI_PROC(k)->p_maxrss)); + else +#endif + (void)printf("%*s", v->width, "-"); +} + +void +tsize(k, ve) + KINFO *k; + VARENT *ve; +{ + VAR *v; + + v = ve->var; +#ifndef NEWVM + (void)printf("%*d", v->width, pgtok(KI_EPROC(k)->e_xsize)); +#else + (void)printf("%*d", v->width, pgtok(KI_EPROC(k)->e_vm.vm_tsize)); +#endif +} + +#ifndef NEWVM +void +trss(k, ve) + KINFO *k; + VARENT *ve; +{ + VAR *v; + + v = ve->var; + (void)printf("%*d", v->width, pgtok(KI_EPROC(k)->e_xrssize)); +} +#endif + +/* + * Generic output routines. Print fields from various prototype + * structures. + */ +static void +printval(bp, v) + char *bp; + VAR *v; +{ + static char ofmt[32] = "%"; + char *fcp, *cp; + + cp = ofmt + 1; + fcp = v->fmt; + if (v->flag & LJUST) + *cp++ = '-'; + *cp++ = '*'; + while (*cp++ = *fcp++); + + switch (v->type) { + case CHAR: + (void)printf(ofmt, v->width, *(char *)bp); + break; + case UCHAR: + (void)printf(ofmt, v->width, *(u_char *)bp); + break; + case SHORT: + (void)printf(ofmt, v->width, *(short *)bp); + break; + case USHORT: + (void)printf(ofmt, v->width, *(u_short *)bp); + break; + case LONG: + (void)printf(ofmt, v->width, *(long *)bp); + break; + case ULONG: + (void)printf(ofmt, v->width, *(u_long *)bp); + break; + case KPTR: + (void)printf(ofmt, v->width, *(u_long *)bp &~ KERNBASE); + break; + default: + errx(1, "unknown type %d", v->type); + } +} + +void +pvar(k, ve) + KINFO *k; + VARENT *ve; +{ + VAR *v; + + v = ve->var; + printval((char *)((char *)KI_PROC(k) + v->off), v); +} + +void +evar(k, ve) + KINFO *k; + VARENT *ve; +{ + VAR *v; + + v = ve->var; + printval((char *)((char *)KI_EPROC(k) + v->off), v); +} + +void +uvar(k, ve) + KINFO *k; + VARENT *ve; +{ + VAR *v; + + v = ve->var; + if (k->ki_u.u_valid) + printval((char *)((char *)&k->ki_u + v->off), v); + else + (void)printf("%*s", v->width, "-"); +} + +void +rvar(k, ve) + KINFO *k; + VARENT *ve; +{ + VAR *v; + + v = ve->var; + if (k->ki_u.u_valid) + printval((char *)((char *)(&k->ki_u.u_ru) + v->off), v); + else + (void)printf("%*s", v->width, "-"); +} diff --git a/bin/ps/ps.1 b/bin/ps/ps.1 new file mode 100644 index 0000000..b0345f7 --- /dev/null +++ b/bin/ps/ps.1 @@ -0,0 +1,502 @@ +.\" Copyright (c) 1980, 1990, 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. +.\" +.\" @(#)ps.1 8.3 (Berkeley) 4/18/94 +.\" +.Dd April 18, 1994 +.Dt PS 1 +.Os BSD 4 +.Sh NAME +.Nm \&ps +.Nd process status +.Sh SYNOPSIS +.Nm \&ps +.Op Fl aCehjlmrSTuvwx +.Op Fl M Ar core +.Op Fl N Ar system +.Op Fl O Ar fmt +.Op Fl o Ar fmt +.Op Fl p Ar pid +.Op Fl t Ar tty +.Op Fl W Ar swap +.Nm ps +.Op Fl L +.Sh DESCRIPTION +.Nm \&Ps +displays a header line followed by lines containing information about your +processes that have controlling terminals. +This information is sorted by process +.Tn ID . +.Pp +The information displayed is selected based on a set of keywords (see the +.Fl L +.Fl O +and +.Fl o +options). +The default output format includes, for each process, the process' +.Tn ID , +controlling terminal, cpu time (including both user and system time), +state, and associated command. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl a +Display information about other users' processes as well as your own. +.It Fl C +Change the way the cpu percentage is calculated by using a ``raw'' +cpu calculation that ignores ``resident'' time (this normally has +no effect). +.It Fl e +Display the environment as well. +.It Fl h +Repeat the information header as often as necessary to guarantee one +header per page of information. +.It Fl j +Print information associated with the following keywords: +user, pid, ppid, pgid, sess, jobc, state, tt, time and command. +.It Fl L +List the set of available keywords. +.It Fl l +Display information associated with the following keywords: +uid, pid, ppid, cpu, pri, nice, vsz, rss, wchan, state, tt, time +and command. +.It Fl M +Extract values associated with the name list from the specified core +instead of the default +.Dq Pa /dev/kmem . +.It Fl m +Sort by memory usage, instead of by process +.Tn ID . +.It Fl N +Extract the name list from the specified system instead of the default +.Dq Pa /vmunix . +.It Fl O +Add the information associated with the space or comma separated list +of keywords specified, after the process +.Tn ID , +in the default information +display. +Keywords may be appended with an equals (``='') sign and a string. +This causes the printed header to use the specified string instead of +the standard header. +.It Fl o +Display information associated with the space or comma separated list +of keywords specified. +Keywords may be appended with an equals (``='') sign and a string. +This causes the printed header to use the specified string instead of +the standard header. +.It Fl p +Display information associated with the specified process +.Tn ID . +.It Fl r +Sort by current cpu usage, instead of by process +.Tn ID . +.It Fl S +Change the way the process time is calculated by summing all exited +children to their parent process. +.It Fl T +Display information about processes attached to the device associated +with the standard input. +.It Fl t +Display information about processes attached to the specified terminal +device. +.It Fl u +Display information associated with the following keywords: +user, pid, %cpu, %mem, vsz, rss, tt, state, start, time and command. +The +.Fl u +option implies the +.Fl r +option. +.It Fl v +Display information associated with the following keywords: +pid, state, time, sl, re, pagein, vsz, rss, lim, tsiz, +%cpu, %mem and command. +The +.Fl v +option implies the +.Fl m +option. +.It Fl W +Extract swap information from the specified file instead of the +default +.Dq Pa /dev/swap . +.It Fl w +Use 132 columns to display information, instead of the default which +is your window size. +If the +.Fl w +option is specified more than once, +.Nm \&ps +will use as many columns as necessary without regard for your window size. +.It Fl x +Display information about processes without controlling terminals. +.El +.Pp +A complete list of the available keywords are listed below. +Some of these keywords are further specified as follows: +.Bl -tag -width indent +.It %cpu +The cpu utilization of the process; this is a decaying average over up to +a minute of previous (real) time. +Since the time base over which this is computed varies (since processes may +be very young) it is possible for the sum of all +.Tn \&%CPU +fields to exceed 100%. +.It %mem +The percentage of real memory used by this process. +.It flags +The flags (in hexadecimal) associated with the process as in +the include file +.Aq Pa sys/proc.h : +.Bl -column SNOCLDSTOP SNOCLDSTOP +.It Dv "SLOAD" Ta No "0x0000001 in core" +.It Dv "SSYS" Ta No "0x0000002 swapper or pager process" +.It Dv "SLOCK" Ta No "0x0000004 process being swapped out" +.It Dv "SSWAP" Ta No "0x0000008 save area flag" +.It Dv "STRC" Ta No "0x0000010 process is being traced" +.It Dv "SWTED" Ta No "0x0000020 another tracing flag" +.It Dv "SSINTR" Ta No "0x0000040 sleep is interruptible" +.It Dv "SKEEP" Ta No "0x0000100 another flag to prevent swap out" +.It Dv "SOMASK" Ta No "0x0000200 restore old mask after taking signal" +.It Dv "SWEXIT" Ta No "0x0000400 working on exiting" +.It Dv "SPHYSIO" Ta No "0x0000800 doing physical" +.Tn I/O +.It Dv "SVFORK" Ta No "0x0001000 process resulted from" +.Xr vfork 2 +.It Dv "SVFDONE" Ta No "0x0002000 another" +.Xr vfork +flag +.It Dv "SNOVM" Ta No "0x0004000 no vm, parent in a" +.Xr vfork +.It Dv "SPAGV" Ta No "0x0008000 init data space on demand, from vnode" +.It Dv "SSEQL" Ta No "0x0010000 user warned of sequential vm behavior" +.It Dv "SUANOM" Ta No "0x0020000 user warned of random vm behavior" +.It Dv "STIMO" Ta No "0x0040000 timing out during sleep" +.It Dv "SNOCLDSTOP" Ta No "0x0080000 no" +.Dv SIGCHLD +when children stop +.It Dv "SCTTY" Ta No "0x0100000 has a controlling terminal" +.It Dv "SOWEUPC" Ta No "0x0200000 owe process an addupc() call at next ast" +.\" the routine addupc is not documented in the man pages +.It Dv "SSEL" Ta No "0x0400000 selecting; wakeup/waiting danger" +.It Dv "SEXEC" Ta No "0x0800000 process called" +.Xr exec 2 +.It Dv "SHPUX" Ta No "0x1000000 \\*(tNHP-UX\\*(sP process +.Pq Dv HPUXCOMPAT +.It Dv "SULOCK" Ta No "0x2000000 locked in core after swap error" +.It Dv "SPTECHG" Ta No "0x4000000 pte's for process have changed" +.El +.It lim +The soft limit on memory used, specified via a call to +.Xr setrlimit 2 . +.It lstart +The exact time the command started, using the ``%C'' format described in +.Xr strftime 3 . +.It nice +The process scheduling increment (see +.Xr setpriority 2 ) . +.It rss +the real memory (resident set) size of the process (in 1024 byte units). +.It start +The time the command started. +If the command started less than 24 hours ago, the start time is +displayed using the ``%l:ps.1p'' format described in +.Xr strftime 3 . +If the command started less than 7 days ago, the start time is +displayed using the ``%a6.15p'' format. +Otherwise, the start time is displayed using the ``%e%b%y'' format. +.It state +The state is given by a sequence of letters, for example, +.Dq Tn RWNA . +The first letter indicates the run state of the process: +.Pp +.Bl -tag -width indent -compact +.It D +Marks a process in disk (or other short term, uninterruptible) wait. +.It I +Marks a process that is idle (sleeping for longer than about 20 seconds). +.It R +Marks a runnable process. +.It S +Marks a process that is sleeping for less than about 20 seconds. +.It T +Marks a stopped process. +.It Z +Marks a dead process (a ``zombie''). +.El +.Pp +Additional characters after these, if any, indicate additional state +information: +.Pp +.Bl -tag -width indent -compact +.It + +The process is in the foreground process group of its control terminal. +.It < +The process has raised +.Tn CPU +scheduling priority. +.It > +The process has specified a soft limit on memory requirements and is +currently exceeding that limit; such a process is (necessarily) not +swapped. +.It A +the process has asked for random page replacement +.Pf ( Dv VA_ANOM , +from +.Xr vadvise 2 , +for example, +.Xr lisp 1 +in a garbage collect). +.It E +The process is trying to exit. +.It L +The process has pages locked in core (for example, for raw +.Tn I/O ) . +.It N +The process has reduced +.Tn CPU +scheduling priority (see +.Xr setpriority 2 ) . +.It S +The process has asked for +.Tn FIFO +page replacement +.Pf ( Dv VA_SEQL , +from +.Xr vadvise 2 , +for example, a large image processing program using virtual memory to +sequentially address voluminous data). +.It s +The process is a session leader. +.It V +The process is suspended during a +.Xr vfork . +.It W +The process is swapped out. +.It X +The process is being traced or debugged. +.El +.It tt +An abbreviation for the pathname of the controlling terminal, if any. +The abbreviation consists of the two letters following +.Dq Pa /dev/tty , +or, for the console, ``co''. +This is followed by a ``-'' if the process can no longer reach that +controlling terminal (i.e., it has been revoked). +.It wchan +The event (an address in the system) on which a process waits. +When printed numerically, the initial part of the address is +trimmed off and the result is printed in hex, for example, 0x80324000 prints +as 324000. +.El +.Pp +When printing using the command keyword, a process that has exited and +has a parent that has not yet waited for the process (in other words, a zombie) +is listed as ``<defunct>'', and a process which is blocked while trying +to exit is listed as ``<exiting>''. +.Nm \&Ps +makes an educated guess as to the file name and arguments given when the +process was created by examining memory or the swap area. +The method is inherently somewhat unreliable and in any event a process +is entitled to destroy this information, so the names cannot be depended +on too much. +The ucomm (accounting) keyword can, however, be depended on. +.Sh KEYWORDS +The following is a complete list of the available keywords and their +meanings. +Several of them have aliases (keywords which are synonyms). +.Pp +.Bl -tag -width sigignore -compact +.It %cpu +percentage cpu usage (alias pcpu) +.It %mem +percentage memory usage (alias pmem) +.It acflag +accounting flag (alias acflg) +.It command +command and arguments +.It cpu +short-term cpu usage factor (for scheduling) +.It flags +the process flags, in hexadecimal (alias f) +.It inblk +total blocks read (alias inblock) +.It jobc +job control count +.It ktrace +tracing flags +.It ktracep +tracing vnode +.It lim +memoryuse limit +.It logname +login name of user who started the process +.It lstart +time started +.It majflt +total page faults +.It minflt +total page reclaims +.It msgrcv +total messages received (reads from pipes/sockets) +.It msgsnd +total messages sent (writes on pipes/sockets) +.It nice +nice value (alias ni) +.It nivcsw +total involuntary context switches +.It nsigs +total signals taken (alias nsignals) +.It nswap +total swaps in/out +.It nvcsw +total voluntary context switches +.It nwchan +wait channel (as an address) +.It oublk +total blocks written (alias oublock) +.It p_ru +resource usage (valid only for zombie) +.It paddr +swap address +.It pagein +pageins (same as majflt) +.It pgid +process group number +.It pid +process +.Tn ID +.It poip +pageouts in progress +.It ppid +parent process +.Tn ID +.It pri +scheduling priority +.It re +core residency time (in seconds; 127 = infinity) +.It rgid +real group +.Tn ID +.It rlink +reverse link on run queue, or 0 +.It rss +resident set size +.It rsz +resident set size + (text size / text use count) (alias rssize) +.It ruid +real user +.Tn ID +.It ruser +user name (from ruid) +.It sess +session pointer +.It sig +pending signals (alias pending) +.It sigcatch +caught signals (alias caught) +.It sigignore +ignored signals (alias ignored) +.It sigmask +blocked signals (alias blocked) +.It sl +sleep time (in seconds; 127 = infinity) +.It start +time started +.It state +symbolic process state (alias stat) +.It svgid +saved gid from a setgid executable +.It svuid +saved uid from a setuid executable +.It tdev +control terminal device number +.It time +accumulated cpu time, user + system (alias cputime) +.It tpgid +control terminal process group +.Tn ID +.\".It trss +.\"text resident set size (in Kbytes) +.It tsess +control terminal session pointer +.It tsiz +text size (in Kbytes) +.It tt +control terminal name (two letter abbreviation) +.It tty +full name of control terminal +.It uprocp +process pointer +.It ucomm +name to be used for accounting +.It uid +effective user +.Tn ID +.It upr +scheduling priority on return from system call (alias usrpri) +.It user +user name (from uid) +.It vsz +virtual size in Kbytes (alias vsize) +.It wchan +wait channel (as a symbolic name) +.It xstat +exit or stop status (valid only for stopped or zombie process) +.El +.Sh FILES +.Bl -tag -width /var/run/kvm_vmunix.db -compact +.It Pa /dev +special files and device names +.It Pa /dev/drum +default swap device +.It Pa /dev/kmem +default kernel memory +.It Pa /var/run/dev.db +/dev name database +.It Pa /var/run/kvm_vmunix.db +system namelist database +.It Pa /vmunix +default system namelist +.El +.Sh SEE ALSO +.Xr kill 1 , +.Xr w 1 , +.Xr kvm 3 , +.Xr strftime 3 , +.Xr pstat 8 +.Sh BUGS +Since +.Nm \&ps +cannot run faster than the system and is run as any other scheduled +process, the information it displays can never be exact. diff --git a/bin/ps/ps.c b/bin/ps/ps.c new file mode 100644 index 0000000..06d1d5f --- /dev/null +++ b/bin/ps/ps.c @@ -0,0 +1,504 @@ +/*- + * Copyright (c) 1990, 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) 1990, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)ps.c 8.4 (Berkeley) 4/2/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/user.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/proc.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/sysctl.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <kvm.h> +#include <nlist.h> +#include <paths.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "ps.h" + +#ifdef P_PPWAIT +#define NEWVM +#endif + +KINFO *kinfo; +struct varent *vhead, *vtail; + +int eval; /* exit value */ +int rawcpu; /* -C */ +int sumrusage; /* -S */ +int termwidth; /* width of screen (0 == infinity) */ +int totwidth; /* calculated width of requested variables */ + +static int needuser, needcomm, needenv; + +enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT; + +static char *fmt __P((char **(*)(kvm_t *, const struct kinfo_proc *, int), + KINFO *, char *, int)); +static char *kludge_oldps_options __P((char *)); +static int pscomp __P((const void *, const void *)); +static void saveuser __P((KINFO *)); +static void scanvars __P((void)); +static void usage __P((void)); + +char dfmt[] = "pid tt state time command"; +char jfmt[] = "user pid ppid pgid sess jobc state tt time command"; +char lfmt[] = "uid pid ppid cpu pri nice vsz rss wchan state tt time command"; +char o1[] = "pid"; +char o2[] = "tt state time command"; +char ufmt[] = "user pid %cpu %mem vsz rss tt state start time command"; +char vfmt[] = "pid state time sl re pagein vsz rss lim tsiz %cpu %mem command"; + +kvm_t *kd; + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct kinfo_proc *kp; + struct varent *vent; + struct winsize ws; + dev_t ttydev; + pid_t pid; + uid_t uid; + int all, ch, flag, i, fmt, lineno, nentries; + int prtheader, wflag, what, xflg; + char *nlistf, *memf, *swapf, errbuf[256]; + + if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) == -1 && + ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) == -1 && + ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ws) == -1) || + ws.ws_col == 0) + termwidth = 79; + else + termwidth = ws.ws_col - 1; + + if (argc > 1) + argv[1] = kludge_oldps_options(argv[1]); + + all = fmt = prtheader = wflag = xflg = 0; + pid = -1; + uid = (uid_t) -1; + ttydev = NODEV; + memf = nlistf = swapf = NULL; + while ((ch = getopt(argc, argv, + "aCeghjLlM:mN:O:o:p:rSTt:uvW:wx")) != EOF) + switch((char)ch) { + case 'a': + all = 1; + break; + case 'e': /* XXX set ufmt */ + needenv = 1; + break; + case 'C': + rawcpu = 1; + break; + case 'g': + break; /* no-op */ + case 'h': + prtheader = ws.ws_row > 5 ? ws.ws_row : 22; + break; + case 'j': + parsefmt(jfmt); + fmt = 1; + jfmt[0] = '\0'; + break; + case 'L': + showkey(); + exit(0); + case 'l': + parsefmt(lfmt); + fmt = 1; + lfmt[0] = '\0'; + break; + case 'M': + memf = optarg; + break; + case 'm': + sortby = SORTMEM; + break; + case 'N': + nlistf = optarg; + break; + case 'O': + parsefmt(o1); + parsefmt(optarg); + parsefmt(o2); + o1[0] = o2[0] = '\0'; + fmt = 1; + break; + case 'o': + parsefmt(optarg); + fmt = 1; + break; + case 'p': + pid = atol(optarg); + xflg = 1; + break; + case 'r': + sortby = SORTCPU; + break; + case 'S': + sumrusage = 1; + break; + case 'T': + if ((optarg = ttyname(STDIN_FILENO)) == NULL) + errx(1, "stdin: not a terminal"); + /* FALLTHROUGH */ + case 't': { + struct stat sb; + char *ttypath, pathbuf[MAXPATHLEN]; + + if (strcmp(optarg, "co") == 0) + ttypath = _PATH_CONSOLE; + else if (*optarg != '/') + (void)snprintf(ttypath = pathbuf, + sizeof(pathbuf), "%s%s", _PATH_TTY, optarg); + else + ttypath = optarg; + if (stat(ttypath, &sb) == -1) + err(1, "%s", ttypath); + if (!S_ISCHR(sb.st_mode)) + errx(1, "%s: not a terminal", ttypath); + ttydev = sb.st_rdev; + break; + } + case 'u': + parsefmt(ufmt); + sortby = SORTCPU; + fmt = 1; + ufmt[0] = '\0'; + break; + case 'v': + parsefmt(vfmt); + sortby = SORTMEM; + fmt = 1; + vfmt[0] = '\0'; + break; + case 'W': + swapf = optarg; + break; + case 'w': + if (wflag) + termwidth = UNLIMITED; + else if (termwidth < 131) + termwidth = 131; + wflag++; + break; + case 'x': + xflg = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + +#define BACKWARD_COMPATIBILITY +#ifdef BACKWARD_COMPATIBILITY + if (*argv) { + nlistf = *argv; + if (*++argv) { + memf = *argv; + if (*++argv) + swapf = *argv; + } + } +#endif + /* + * Discard setgid privileges if not the running kernel so that bad + * guys can't print interesting stuff from kernel memory. + */ + if (nlistf != NULL || memf != NULL || swapf != NULL) + setgid(getgid()); + + kd = kvm_openfiles(nlistf, memf, swapf, O_RDONLY, errbuf); + if (kd == 0) + errx(1, "%s", errbuf); + + if (!fmt) + parsefmt(dfmt); + + if (!all && ttydev == NODEV && pid == -1) /* XXX - should be cleaner */ + uid = getuid(); + + /* + * scan requested variables, noting what structures are needed, + * and adjusting header widths as appropiate. + */ + scanvars(); + /* + * get proc list + */ + if (uid != (uid_t) -1) { + what = KERN_PROC_UID; + flag = uid; + } else if (ttydev != NODEV) { + what = KERN_PROC_TTY; + flag = ttydev; + } else if (pid != -1) { + what = KERN_PROC_PID; + flag = pid; + } else { + what = KERN_PROC_ALL; + flag = 0; + } + /* + * select procs + */ + if ((kp = kvm_getprocs(kd, what, flag, &nentries)) == 0) + errx(1, "%s", kvm_geterr(kd)); + if ((kinfo = malloc(nentries * sizeof(*kinfo))) == NULL) + err(1, NULL); + for (i = nentries; --i >= 0; ++kp) { + kinfo[i].ki_p = kp; + if (needuser) + saveuser(&kinfo[i]); + } + /* + * print header + */ + printheader(); + if (nentries == 0) + exit(0); + /* + * sort proc list + */ + qsort(kinfo, nentries, sizeof(KINFO), pscomp); + /* + * for each proc, call each variable output function. + */ + for (i = lineno = 0; i < nentries; i++) { + if (xflg == 0 && (KI_EPROC(&kinfo[i])->e_tdev == NODEV || + (KI_PROC(&kinfo[i])->p_flag & P_CONTROLT ) == 0)) + continue; + for (vent = vhead; vent; vent = vent->next) { + (vent->var->oproc)(&kinfo[i], vent); + if (vent->next != NULL) + (void)putchar(' '); + } + (void)putchar('\n'); + if (prtheader && lineno++ == prtheader - 4) { + (void)putchar('\n'); + printheader(); + lineno = 0; + } + } + exit(eval); +} + +static void +scanvars() +{ + struct varent *vent; + VAR *v; + int i; + + for (vent = vhead; vent; vent = vent->next) { + v = vent->var; + i = strlen(v->header); + if (v->width < i) + v->width = i; + totwidth += v->width + 1; /* +1 for space */ + if (v->flag & USER) + needuser = 1; + if (v->flag & COMM) + needcomm = 1; + } + totwidth--; +} + +static char * +fmt(fn, ki, comm, maxlen) + char **(*fn) __P((kvm_t *, const struct kinfo_proc *, int)); + KINFO *ki; + char *comm; + int maxlen; +{ + char *s; + + if ((s = + fmt_argv((*fn)(kd, ki->ki_p, termwidth), comm, maxlen)) == NULL) + err(1, NULL); + return (s); +} + +static void +saveuser(ki) + KINFO *ki; +{ + struct pstats pstats; + struct usave *usp; + + usp = &ki->ki_u; + if (kvm_read(kd, (u_long)&KI_PROC(ki)->p_addr->u_stats, + (char *)&pstats, sizeof(pstats)) == sizeof(pstats)) { + /* + * The u-area might be swapped out, and we can't get + * at it because we have a crashdump and no swap. + * If it's here fill in these fields, otherwise, just + * leave them 0. + */ + usp->u_start = pstats.p_start; + usp->u_ru = pstats.p_ru; + usp->u_cru = pstats.p_cru; + usp->u_valid = 1; + } else + usp->u_valid = 0; + /* + * save arguments if needed + */ + if (needcomm) + ki->ki_args = fmt(kvm_getargv, ki, KI_PROC(ki)->p_comm, + MAXCOMLEN); + else + ki->ki_args = NULL; + if (needenv) + ki->ki_env = fmt(kvm_getenvv, ki, (char *)NULL, 0); + else + ki->ki_env = NULL; +} + +static int +pscomp(a, b) + const void *a, *b; +{ + int i; +#ifdef NEWVM +#define VSIZE(k) (KI_EPROC(k)->e_vm.vm_dsize + KI_EPROC(k)->e_vm.vm_ssize + \ + KI_EPROC(k)->e_vm.vm_tsize) +#else +#define VSIZE(k) ((k)->ki_p->p_dsize + (k)->ki_p->p_ssize + (k)->ki_e->e_xsize) +#endif + + if (sortby == SORTCPU) + return (getpcpu((KINFO *)b) - getpcpu((KINFO *)a)); + if (sortby == SORTMEM) + return (VSIZE((KINFO *)b) - VSIZE((KINFO *)a)); + i = KI_EPROC((KINFO *)a)->e_tdev - KI_EPROC((KINFO *)b)->e_tdev; + if (i == 0) + i = KI_PROC((KINFO *)a)->p_pid - KI_PROC((KINFO *)b)->p_pid; + return (i); +} + +/* + * ICK (all for getopt), would rather hide the ugliness + * here than taint the main code. + * + * ps foo -> ps -foo + * ps 34 -> ps -p34 + * + * The old convention that 't' with no trailing tty arg means the users + * tty, is only supported if argv[1] doesn't begin with a '-'. This same + * feature is available with the option 'T', which takes no argument. + */ +static char * +kludge_oldps_options(s) + char *s; +{ + size_t len; + char *newopts, *ns, *cp; + + len = strlen(s); + if ((newopts = ns = malloc(len + 2)) == NULL) + err(1, NULL); + /* + * options begin with '-' + */ + if (*s != '-') + *ns++ = '-'; /* add option flag */ + /* + * gaze to end of argv[1] + */ + cp = s + len - 1; + /* + * if last letter is a 't' flag with no argument (in the context + * of the oldps options -- option string NOT starting with a '-' -- + * then convert to 'T' (meaning *this* terminal, i.e. ttyname(0)). + */ + if (*cp == 't' && *s != '-') + *cp = 'T'; + else { + /* + * otherwise check for trailing number, which *may* be a + * pid. + */ + while (cp >= s && isdigit(*cp)) + --cp; + } + cp++; + memmove(ns, s, (size_t)(cp - s)); /* copy up to trailing number */ + ns += cp - s; + /* + * if there's a trailing number, and not a preceding 'p' (pid) or + * 't' (tty) flag, then assume it's a pid and insert a 'p' flag. + */ + if (isdigit(*cp) && (cp == s || cp[-1] != 't' && cp[-1] != 'p' && + (cp - 1 == s || cp[-2] != 't'))) + *ns++ = 'p'; + (void)strcpy(ns, cp); /* and append the number */ + + return (newopts); +} + +static void +usage() +{ + + (void)fprintf(stderr, + "usage:\t%s\n\t %s\n\t%s\n", + "ps [-aChjlmrSTuvwx] [-O|o fmt] [-p pid] [-t tty]", + "[-M core] [-N system] [-W swap]", + "ps [-L]"); + exit(1); +} diff --git a/bin/ps/ps.h b/bin/ps/ps.h new file mode 100644 index 0000000..fe2513e --- /dev/null +++ b/bin/ps/ps.h @@ -0,0 +1,88 @@ +/*- + * Copyright (c) 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. + * + * @(#)ps.h 8.1 (Berkeley) 5/31/93 + */ + +#define UNLIMITED 0 /* unlimited terminal width */ +enum type { CHAR, UCHAR, SHORT, USHORT, LONG, ULONG, KPTR }; + +struct usave { + struct timeval u_start; + struct rusage u_ru; + struct rusage u_cru; + char u_acflag; + char u_valid; +}; + +#define KI_PROC(ki) (&(ki)->ki_p->kp_proc) +#define KI_EPROC(ki) (&(ki)->ki_p->kp_eproc) + +typedef struct kinfo { + struct kinfo_proc *ki_p; /* proc structure */ + struct usave ki_u; /* interesting parts of user */ + char *ki_args; /* exec args */ + char *ki_env; /* environment */ +} KINFO; + +/* Variables. */ +typedef struct varent { + struct varent *next; + struct var *var; +} VARENT; + +typedef struct var { + char *name; /* name(s) of variable */ + char *header; /* default header */ + char *alias; /* aliases */ +#define COMM 0x01 /* needs exec arguments and environment (XXX) */ +#define LJUST 0x02 /* left adjust on output (trailing blanks) */ +#define USER 0x04 /* needs user structure */ + u_int flag; + /* output routine */ + void (*oproc) __P((struct kinfo *, struct varent *)); + short width; /* printing width */ + /* + * The following (optional) elements are hooks for passing information + * to the generic output routines: pvar, evar, uvar (those which print + * simple elements from well known structures: proc, eproc, usave) + */ + int off; /* offset in structure */ + enum type type; /* type of element */ + char *fmt; /* printf format */ + char *time; /* time format */ + /* + * glue to link selected fields together + */ +} VAR; + +#include "extern.h" diff --git a/bin/pwd/Makefile b/bin/pwd/Makefile new file mode 100644 index 0000000..06a1218 --- /dev/null +++ b/bin/pwd/Makefile @@ -0,0 +1,5 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 + +PROG= pwd + +.include <bsd.prog.mk> diff --git a/bin/pwd/pwd.1 b/bin/pwd/pwd.1 new file mode 100644 index 0000000..b0d6ee3 --- /dev/null +++ b/bin/pwd/pwd.1 @@ -0,0 +1,68 @@ +.\" Copyright (c) 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. +.\" +.\" @(#)pwd.1 8.1 (Berkeley) 5/31/93 +.\" +.Dd May 31, 1993 +.Dt PWD 1 +.Os BSD 4 +.Sh NAME +.Nm pwd +.Nd return working directory name +.Sh SYNOPSIS +.Nm pwd +.Sh DESCRIPTION +.Nm Pwd +writes the absolute pathname of the current working directory to +the standard output. +.Pp +The pwd utility exits 0 on success, and >0 if an error occurs. +.Sh STANDARDS +The +.Nm pwd +command is expected to be +.St -p1003.2 +compatible . +.Sh SEE ALSO +.Xr cd 1 , +.Xr csh 1 , +.Xr getwd 3 +.Sh BUGS +In +.Xr csh 1 +the command +.Ic dirs +is always faster (although it can give a different answer in the rare case +that the current directory or a containing directory was moved after +the shell descended into it). diff --git a/bin/pwd/pwd.c b/bin/pwd/pwd.c new file mode 100644 index 0000000..5f330c0 --- /dev/null +++ b/bin/pwd/pwd.c @@ -0,0 +1,92 @@ +/* + * 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 copyright[] = +"@(#) Copyright (c) 1991, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)pwd.c 8.3 (Berkeley) 4/1/94"; +#endif /* not lint */ + +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch; + char *p; + + /* + * Flags for pwd are a bit strange. The POSIX 1003.2B/D9 document + * has an optional -P flag for physical, which is what this program + * will produce by default. The logical flag, -L, should fail, as + * there's no way to display a logical path after forking. We don't + * document either flag, only adding -P for future portability. + */ + while ((ch = getopt(argc, argv, "P")) != EOF) + switch (ch) { + case 'P': + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 0) + usage(); + + if ((p = getcwd(NULL, 0)) == NULL) + err(1, NULL); + (void)printf("%s\n", p); + exit(0); +} + +void +usage() +{ + + (void)fprintf(stderr, "usage: pwd\n"); + exit(1); +} diff --git a/bin/rcp/Makefile b/bin/rcp/Makefile new file mode 100644 index 0000000..898f9a7 --- /dev/null +++ b/bin/rcp/Makefile @@ -0,0 +1,13 @@ +# @(#)Makefile 8.1 (Berkeley) 7/19/93 + +PROG= rcp +SRCS= rcp.c krcmd.c kcmd.c util.c +CFLAGS+=-DKERBEROS -DCRYPT +DPADD= ${LIBKRB} ${LIBDES} +LDADD= -lkrb -ldes +BINOWN= root +BINMODE=4555 +INSTALLFLAGS=-fschg +.PATH: ${.CURDIR}/../../usr.bin/rlogin + +.include <bsd.prog.mk> diff --git a/bin/rcp/extern.h b/bin/rcp/extern.h new file mode 100644 index 0000000..de49dc6 --- /dev/null +++ b/bin/rcp/extern.h @@ -0,0 +1,50 @@ +/*- + * Copyright (c) 1992, 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 + */ + +typedef struct { + int cnt; + char *buf; +} BUF; + +extern int iamremote; + +BUF *allocbuf __P((BUF *, int, int)); +char *colon __P((char *)); +void lostconn __P((int)); +void nospace __P((void)); +int okname __P((char *)); +void run_err __P((const char *, ...)); +int susystem __P((char *, int)); +void verifydir __P((char *)); diff --git a/bin/rcp/pathnames.h b/bin/rcp/pathnames.h new file mode 100644 index 0000000..c150e95 --- /dev/null +++ b/bin/rcp/pathnames.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 1989, 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. + * + * @(#)pathnames.h 8.1 (Berkeley) 5/31/93 + */ + +#include <paths.h> + +#define _PATH_CP "/bin/cp" +#define _PATH_RSH "/usr/bin/rsh" diff --git a/bin/rcp/rcp.1 b/bin/rcp/rcp.1 new file mode 100644 index 0000000..bff0a01 --- /dev/null +++ b/bin/rcp/rcp.1 @@ -0,0 +1,159 @@ +.\" Copyright (c) 1983, 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. +.\" +.\" @(#)rcp.1 8.1 (Berkeley) 5/31/93 +.\" +.Dd May 31, 1993 +.Dt RCP 1 +.Os BSD 4.3r +.Sh NAME +.Nm rcp +.Nd remote file copy +.Sh SYNOPSIS +.Nm rcp +.Op Fl Kpx +.Op Fl k Ar realm +.Ar file1 file2 +.Nm rcp +.Op Fl Kprx +.Op Fl k Ar realm +.Ar file ... +.Ar directory +.Sh DESCRIPTION +.Nm Rcp +copies files between machines. Each +.Ar file +or +.Ar directory +argument is either a remote file name of the +form ``rname@rhost:path'', or a local file name (containing no `:' characters, +or a `/' before any `:'s). +.Pp +.Bl -tag -width flag +.It Fl K +The +.Fl K +option turns off all Kerberos authentication. +.It Fl k +The +.Fl k +option requests +.Nm rcp +to obtain tickets +for the remote host in realm +.Ar realm +instead of the remote host's realm as determined by +.Xr krb_realmofhost 3 . +.It Fl p +The +.Fl p +option causes +.Nm rcp +to attempt to preserve (duplicate) in its copies the modification +times and modes of the source files, ignoring the +.Ar umask . +By default, the mode and owner of +.Ar file2 +are preserved if it already existed; otherwise the mode of the source file +modified by the +.Xr umask 2 +on the destination host is used. +.It Fl r +If any of the source files are directories, +.Nm rcp +copies each subtree rooted at that name; in this case +the destination must be a directory. +.It Fl x +The +.Fl x +option turns on +.Tn DES +encryption for all data passed by +.Nm rcp . +This may impact response time and +.Tn CPU +utilization, but provides +increased security. +.El +.Pp +If +.Ar path +is not a full path name, it is interpreted relative to +the login directory of the specified user +.Ar ruser +on +.Ar rhost , +or your current user name if no other remote user name is specified. +A +.Ar path +on a remote host may be quoted (using \e, ", or \(aa) +so that the metacharacters are interpreted remotely. +.Pp +.Nm Rcp +does not prompt for passwords; it performs remote execution +via +.Xr rsh 1 , +and requires the same authorization. +.Pp +.Nm Rcp +handles third party copies, where neither source nor target files +are on the current machine. +.Sh SEE ALSO +.Xr cp 1 , +.Xr ftp 1 , +.Xr rsh 1 , +.Xr rlogin 1 +.Sh HISTORY +The +.Nm rcp +command appeared in +.Bx 4.2 . +The version of +.Nm rcp +described here +has been reimplemented with Kerberos in +.Bx 4.3 Reno . +.Sh BUGS +Doesn't detect all cases where the target of a copy might +be a file in cases where only a directory should be legal. +.Pp +Is confused by any output generated by commands in a +.Pa \&.login , +.Pa \&.profile , +or +.Pa \&.cshrc +file on the remote host. +.Pp +The destination user and hostname may have to be specified as +``rhost.rname'' when the destination machine is running the +.Bx 4.2 +version of +.Nm rcp . diff --git a/bin/rcp/rcp.c b/bin/rcp/rcp.c new file mode 100644 index 0000000..fc593a5 --- /dev/null +++ b/bin/rcp/rcp.c @@ -0,0 +1,924 @@ +/* + * Copyright (c) 1983, 1990, 1992, 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) 1983, 1990, 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)rcp.c 8.2 (Berkeley) 4/2/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> + +#include <ctype.h> +#include <dirent.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <netdb.h> +#include <pwd.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <string.h> +#include <unistd.h> + +#include "pathnames.h" +#include "extern.h" + +#ifdef KERBEROS +#include <kerberosIV/des.h> +#include <kerberosIV/krb.h> + +char dst_realm_buf[REALM_SZ]; +char *dest_realm = NULL; +int use_kerberos = 1; +CREDENTIALS cred; +Key_schedule schedule; +extern char *krb_realmofhost(); +#ifdef CRYPT +int doencrypt = 0; +#define OPTIONS "dfKk:prtx" +#else +#define OPTIONS "dfKk:prt" +#endif +#else +#define OPTIONS "dfprt" +#endif + +struct passwd *pwd; +u_short port; +uid_t userid; +int errs, rem; +int pflag, iamremote, iamrecursive, targetshouldbedirectory; + +#define CMDNEEDS 64 +char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ + +#ifdef KERBEROS +int kerberos __P((char **, char *, char *, char *)); +void oldw __P((const char *, ...)); +#endif +int response __P((void)); +void rsource __P((char *, struct stat *)); +void sink __P((int, char *[])); +void source __P((int, char *[])); +void tolocal __P((int, char *[])); +void toremote __P((char *, int, char *[])); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct servent *sp; + int ch, fflag, tflag; + char *targ, *shell; + + fflag = tflag = 0; + while ((ch = getopt(argc, argv, OPTIONS)) != EOF) + switch(ch) { /* User-visible flags. */ + case 'K': +#ifdef KERBEROS + use_kerberos = 0; +#endif + break; +#ifdef KERBEROS + case 'k': + dest_realm = dst_realm_buf; + (void)strncpy(dst_realm_buf, optarg, REALM_SZ); + break; +#ifdef CRYPT + case 'x': + doencrypt = 1; + /* des_set_key(cred.session, schedule); */ + break; +#endif +#endif + case 'p': + pflag = 1; + break; + case 'r': + iamrecursive = 1; + break; + /* Server options. */ + case 'd': + targetshouldbedirectory = 1; + break; + case 'f': /* "from" */ + iamremote = 1; + fflag = 1; + break; + case 't': /* "to" */ + iamremote = 1; + tflag = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + +#ifdef KERBEROS + if (use_kerberos) { +#ifdef CRYPT + shell = doencrypt ? "ekshell" : "kshell"; +#else + shell = "kshell"; +#endif + if ((sp = getservbyname(shell, "tcp")) == NULL) { + use_kerberos = 0; + oldw("can't get entry for %s/tcp service", shell); + sp = getservbyname(shell = "shell", "tcp"); + } + } else + sp = getservbyname(shell = "shell", "tcp"); +#else + sp = getservbyname(shell = "shell", "tcp"); +#endif + if (sp == NULL) + errx(1, "%s/tcp: unknown service", shell); + port = sp->s_port; + + if ((pwd = getpwuid(userid = getuid())) == NULL) + errx(1, "unknown user %d", (int)userid); + + rem = STDIN_FILENO; /* XXX */ + + if (fflag) { /* Follow "protocol", send data. */ + (void)response(); + (void)setuid(userid); + source(argc, argv); + exit(errs); + } + + if (tflag) { /* Receive data. */ + (void)setuid(userid); + sink(argc, argv); + exit(errs); + } + + if (argc < 2) + usage(); + if (argc > 2) + targetshouldbedirectory = 1; + + rem = -1; + /* Command to be executed on remote system using "rsh". */ +#ifdef KERBEROS + (void)snprintf(cmd, sizeof(cmd), + "rcp%s%s%s%s", iamrecursive ? " -r" : "", +#ifdef CRYPT + (doencrypt && use_kerberos ? " -x" : ""), +#else + "", +#endif + pflag ? " -p" : "", targetshouldbedirectory ? " -d" : ""); +#else + (void)snprintf(cmd, sizeof(cmd), "rcp%s%s%s", + iamrecursive ? " -r" : "", pflag ? " -p" : "", + targetshouldbedirectory ? " -d" : ""); +#endif + + (void)signal(SIGPIPE, lostconn); + + if (targ = colon(argv[argc - 1])) /* Dest is remote host. */ + toremote(targ, argc, argv); + else { + tolocal(argc, argv); /* Dest is local host. */ + if (targetshouldbedirectory) + verifydir(argv[argc - 1]); + } + exit(errs); +} + +void +toremote(targ, argc, argv) + char *targ, *argv[]; + int argc; +{ + int i, len, tos; + char *bp, *host, *src, *suser, *thost, *tuser; + + *targ++ = 0; + if (*targ == 0) + targ = "."; + + if (thost = strchr(argv[argc - 1], '@')) { + /* user@host */ + *thost++ = 0; + tuser = argv[argc - 1]; + if (*tuser == '\0') + tuser = NULL; + else if (!okname(tuser)) + exit(1); + } else { + thost = argv[argc - 1]; + tuser = NULL; + } + + for (i = 0; i < argc - 1; i++) { + src = colon(argv[i]); + if (src) { /* remote to remote */ + *src++ = 0; + if (*src == 0) + src = "."; + host = strchr(argv[i], '@'); + len = strlen(_PATH_RSH) + strlen(argv[i]) + + strlen(src) + (tuser ? strlen(tuser) : 0) + + strlen(thost) + strlen(targ) + CMDNEEDS + 20; + if (!(bp = malloc(len))) + err(1, NULL); + if (host) { + *host++ = 0; + suser = argv[i]; + if (*suser == '\0') + suser = pwd->pw_name; + else if (!okname(suser)) + continue; + (void)snprintf(bp, len, + "%s %s -l %s -n %s %s '%s%s%s:%s'", + _PATH_RSH, host, suser, cmd, src, + tuser ? tuser : "", tuser ? "@" : "", + thost, targ); + } else + (void)snprintf(bp, len, + "exec %s %s -n %s %s '%s%s%s:%s'", + _PATH_RSH, argv[i], cmd, src, + tuser ? tuser : "", tuser ? "@" : "", + thost, targ); + (void)susystem(bp, userid); + (void)free(bp); + } else { /* local to remote */ + if (rem == -1) { + len = strlen(targ) + CMDNEEDS + 20; + if (!(bp = malloc(len))) + err(1, NULL); + (void)snprintf(bp, len, "%s -t %s", cmd, targ); + host = thost; +#ifdef KERBEROS + if (use_kerberos) + rem = kerberos(&host, bp, + pwd->pw_name, + tuser ? tuser : pwd->pw_name); + else +#endif + rem = rcmd(&host, port, pwd->pw_name, + tuser ? tuser : pwd->pw_name, + bp, 0); + if (rem < 0) + exit(1); + tos = IPTOS_THROUGHPUT; + if (setsockopt(rem, IPPROTO_IP, IP_TOS, + &tos, sizeof(int)) < 0) + warn("TOS (ignored)"); + if (response() < 0) + exit(1); + (void)free(bp); + (void)setuid(userid); + } + source(1, argv+i); + } + } +} + +void +tolocal(argc, argv) + int argc; + char *argv[]; +{ + int i, len, tos; + char *bp, *host, *src, *suser; + + for (i = 0; i < argc - 1; i++) { + if (!(src = colon(argv[i]))) { /* Local to local. */ + len = strlen(_PATH_CP) + strlen(argv[i]) + + strlen(argv[argc - 1]) + 20; + if (!(bp = malloc(len))) + err(1, NULL); + (void)snprintf(bp, len, "exec %s%s%s %s %s", _PATH_CP, + iamrecursive ? " -r" : "", pflag ? " -p" : "", + argv[i], argv[argc - 1]); + if (susystem(bp, userid)) + ++errs; + (void)free(bp); + continue; + } + *src++ = 0; + if (*src == 0) + src = "."; + if ((host = strchr(argv[i], '@')) == NULL) { + host = argv[i]; + suser = pwd->pw_name; + } else { + *host++ = 0; + suser = argv[i]; + if (*suser == '\0') + suser = pwd->pw_name; + else if (!okname(suser)) + continue; + } + len = strlen(src) + CMDNEEDS + 20; + if ((bp = malloc(len)) == NULL) + err(1, NULL); + (void)snprintf(bp, len, "%s -f %s", cmd, src); + rem = +#ifdef KERBEROS + use_kerberos ? + kerberos(&host, bp, pwd->pw_name, suser) : +#endif + rcmd(&host, port, pwd->pw_name, suser, bp, 0); + (void)free(bp); + if (rem < 0) { + ++errs; + continue; + } + (void)seteuid(userid); + tos = IPTOS_THROUGHPUT; + if (setsockopt(rem, IPPROTO_IP, IP_TOS, &tos, sizeof(int)) < 0) + warn("TOS (ignored)"); + sink(1, argv + argc - 1); + (void)seteuid(0); + (void)close(rem); + rem = -1; + } +} + +void +source(argc, argv) + int argc; + char *argv[]; +{ + struct stat stb; + static BUF buffer; + BUF *bp; + off_t i; + int amt, fd, haderr, indx, result; + char *last, *name, buf[BUFSIZ]; + + for (indx = 0; indx < argc; ++indx) { + name = argv[indx]; + if ((fd = open(name, O_RDONLY, 0)) < 0) + goto syserr; + if (fstat(fd, &stb)) { +syserr: run_err("%s: %s", name, strerror(errno)); + goto next; + } + switch (stb.st_mode & S_IFMT) { + case S_IFREG: + break; + case S_IFDIR: + if (iamrecursive) { + rsource(name, &stb); + goto next; + } + /* FALLTHROUGH */ + default: + run_err("%s: not a regular file", name); + goto next; + } + if ((last = strrchr(name, '/')) == NULL) + last = name; + else + ++last; + if (pflag) { + /* + * Make it compatible with possible future + * versions expecting microseconds. + */ + (void)snprintf(buf, sizeof(buf), "T%ld 0 %ld 0\n", + stb.st_mtimespec.ts_sec, stb.st_atimespec.ts_sec); + (void)write(rem, buf, strlen(buf)); + if (response() < 0) + goto next; + } +#define MODEMASK (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO) + (void)snprintf(buf, sizeof(buf), "C%04o %qd %s\n", + stb.st_mode & MODEMASK, stb.st_size, last); + (void)write(rem, buf, strlen(buf)); + if (response() < 0) + goto next; + if ((bp = allocbuf(&buffer, fd, BUFSIZ)) == NULL) { +next: (void)close(fd); + continue; + } + + /* Keep writing after an error so that we stay sync'd up. */ + for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { + amt = bp->cnt; + if (i + amt > stb.st_size) + amt = stb.st_size - i; + if (!haderr) { + result = read(fd, bp->buf, amt); + if (result != amt) + haderr = result >= 0 ? EIO : errno; + } + if (haderr) + (void)write(rem, bp->buf, amt); + else { + result = write(rem, bp->buf, amt); + if (result != amt) + haderr = result >= 0 ? EIO : errno; + } + } + if (close(fd) && !haderr) + haderr = errno; + if (!haderr) + (void)write(rem, "", 1); + else + run_err("%s: %s", name, strerror(haderr)); + (void)response(); + } +} + +void +rsource(name, statp) + char *name; + struct stat *statp; +{ + DIR *dirp; + struct dirent *dp; + char *last, *vect[1], path[MAXPATHLEN]; + + if (!(dirp = opendir(name))) { + run_err("%s: %s", name, strerror(errno)); + return; + } + last = strrchr(name, '/'); + if (last == 0) + last = name; + else + last++; + if (pflag) { + (void)snprintf(path, sizeof(path), "T%ld 0 %ld 0\n", + statp->st_mtimespec.ts_sec, statp->st_atimespec.ts_sec); + (void)write(rem, path, strlen(path)); + if (response() < 0) { + closedir(dirp); + return; + } + } + (void)snprintf(path, sizeof(path), + "D%04o %d %s\n", statp->st_mode & MODEMASK, 0, last); + (void)write(rem, path, strlen(path)); + if (response() < 0) { + closedir(dirp); + return; + } + while (dp = readdir(dirp)) { + if (dp->d_ino == 0) + continue; + if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) + continue; + if (strlen(name) + 1 + strlen(dp->d_name) >= MAXPATHLEN - 1) { + run_err("%s/%s: name too long", name, dp->d_name); + continue; + } + (void)snprintf(path, sizeof(path), "%s/%s", name, dp->d_name); + vect[0] = path; + source(1, vect); + } + (void)closedir(dirp); + (void)write(rem, "E\n", 2); + (void)response(); +} + +void +sink(argc, argv) + int argc; + char *argv[]; +{ + static BUF buffer; + struct stat stb; + struct timeval tv[2]; + enum { YES, NO, DISPLAYED } wrerr; + BUF *bp; + off_t i, j; + int amt, count, exists, first, mask, mode, ofd, omode; + int setimes, size, targisdir, wrerrno; + char ch, *cp, *np, *targ, *why, *vect[1], buf[BUFSIZ]; + +#define atime tv[0] +#define mtime tv[1] +#define SCREWUP(str) { why = str; goto screwup; } + + setimes = targisdir = 0; + mask = umask(0); + if (!pflag) + (void)umask(mask); + if (argc != 1) { + run_err("ambiguous target"); + exit(1); + } + targ = *argv; + if (targetshouldbedirectory) + verifydir(targ); + (void)write(rem, "", 1); + if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) + targisdir = 1; + for (first = 1;; first = 0) { + cp = buf; + if (read(rem, cp, 1) <= 0) + return; + if (*cp++ == '\n') + SCREWUP("unexpected <newline>"); + do { + if (read(rem, &ch, sizeof(ch)) != sizeof(ch)) + SCREWUP("lost connection"); + *cp++ = ch; + } while (cp < &buf[BUFSIZ - 1] && ch != '\n'); + *cp = 0; + + if (buf[0] == '\01' || buf[0] == '\02') { + if (iamremote == 0) + (void)write(STDERR_FILENO, + buf + 1, strlen(buf + 1)); + if (buf[0] == '\02') + exit(1); + ++errs; + continue; + } + if (buf[0] == 'E') { + (void)write(rem, "", 1); + return; + } + + if (ch == '\n') + *--cp = 0; + +#define getnum(t) (t) = 0; while (isdigit(*cp)) (t) = (t) * 10 + (*cp++ - '0'); + cp = buf; + if (*cp == 'T') { + setimes++; + cp++; + getnum(mtime.tv_sec); + if (*cp++ != ' ') + SCREWUP("mtime.sec not delimited"); + getnum(mtime.tv_usec); + if (*cp++ != ' ') + SCREWUP("mtime.usec not delimited"); + getnum(atime.tv_sec); + if (*cp++ != ' ') + SCREWUP("atime.sec not delimited"); + getnum(atime.tv_usec); + if (*cp++ != '\0') + SCREWUP("atime.usec not delimited"); + (void)write(rem, "", 1); + continue; + } + if (*cp != 'C' && *cp != 'D') { + /* + * Check for the case "rcp remote:foo\* local:bar". + * In this case, the line "No match." can be returned + * by the shell before the rcp command on the remote is + * executed so the ^Aerror_message convention isn't + * followed. + */ + if (first) { + run_err("%s", cp); + exit(1); + } + SCREWUP("expected control record"); + } + mode = 0; + for (++cp; cp < buf + 5; cp++) { + if (*cp < '0' || *cp > '7') + SCREWUP("bad mode"); + mode = (mode << 3) | (*cp - '0'); + } + if (*cp++ != ' ') + SCREWUP("mode not delimited"); + + for (size = 0; isdigit(*cp);) + size = size * 10 + (*cp++ - '0'); + if (*cp++ != ' ') + SCREWUP("size not delimited"); + if (targisdir) { + static char *namebuf; + static int cursize; + size_t need; + + need = strlen(targ) + strlen(cp) + 250; + if (need > cursize) { + if (!(namebuf = malloc(need))) + run_err("%s", strerror(errno)); + } + (void)snprintf(namebuf, need, "%s%s%s", targ, + *targ ? "/" : "", cp); + np = namebuf; + } else + np = targ; + exists = stat(np, &stb) == 0; + if (buf[0] == 'D') { + int mod_flag = pflag; + if (exists) { + if (!S_ISDIR(stb.st_mode)) { + errno = ENOTDIR; + goto bad; + } + if (pflag) + (void)chmod(np, mode); + } else { + /* Handle copying from a read-only directory */ + mod_flag = 1; + if (mkdir(np, mode | S_IRWXU) < 0) + goto bad; + } + vect[0] = np; + sink(1, vect); + if (setimes) { + setimes = 0; + if (utimes(np, tv) < 0) + run_err("%s: set times: %s", + np, strerror(errno)); + } + if (mod_flag) + (void)chmod(np, mode); + continue; + } + omode = mode; + mode |= S_IWRITE; + if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { +bad: run_err("%s: %s", np, strerror(errno)); + continue; + } + (void)write(rem, "", 1); + if ((bp = allocbuf(&buffer, ofd, BUFSIZ)) == NULL) { + (void)close(ofd); + continue; + } + cp = bp->buf; + wrerr = NO; + for (count = i = 0; i < size; i += BUFSIZ) { + amt = BUFSIZ; + if (i + amt > size) + amt = size - i; + count += amt; + do { + j = read(rem, cp, amt); + if (j <= 0) { + run_err("%s", j ? strerror(errno) : + "dropped connection"); + exit(1); + } + amt -= j; + cp += j; + } while (amt > 0); + if (count == bp->cnt) { + /* Keep reading so we stay sync'd up. */ + if (wrerr == NO) { + j = write(ofd, bp->buf, count); + if (j != count) { + wrerr = YES; + wrerrno = j >= 0 ? EIO : errno; + } + } + count = 0; + cp = bp->buf; + } + } + if (count != 0 && wrerr == NO && + (j = write(ofd, bp->buf, count)) != count) { + wrerr = YES; + wrerrno = j >= 0 ? EIO : errno; + } + if (ftruncate(ofd, size)) { + run_err("%s: truncate: %s", np, strerror(errno)); + wrerr = DISPLAYED; + } + if (pflag) { + if (exists || omode != mode) + if (fchmod(ofd, omode)) + run_err("%s: set mode: %s", + np, strerror(errno)); + } else { + if (!exists && omode != mode) + if (fchmod(ofd, omode & ~mask)) + run_err("%s: set mode: %s", + np, strerror(errno)); + } + (void)close(ofd); + (void)response(); + if (setimes && wrerr == NO) { + setimes = 0; + if (utimes(np, tv) < 0) { + run_err("%s: set times: %s", + np, strerror(errno)); + wrerr = DISPLAYED; + } + } + switch(wrerr) { + case YES: + run_err("%s: %s", np, strerror(wrerrno)); + break; + case NO: + (void)write(rem, "", 1); + break; + case DISPLAYED: + break; + } + } +screwup: + run_err("protocol error: %s", why); + exit(1); +} + +#ifdef KERBEROS +int +kerberos(host, bp, locuser, user) + char **host, *bp, *locuser, *user; +{ + struct servent *sp; + +again: + if (use_kerberos) { + rem = KSUCCESS; + errno = 0; + if (dest_realm == NULL) + dest_realm = krb_realmofhost(*host); + rem = +#ifdef CRYPT + doencrypt ? + krcmd_mutual(host, + port, user, bp, 0, dest_realm, &cred, schedule) : +#endif + krcmd(host, port, user, bp, 0, dest_realm); + + if (rem < 0) { + use_kerberos = 0; + if ((sp = getservbyname("shell", "tcp")) == NULL) + errx(1, "unknown service shell/tcp"); + if (errno == ECONNREFUSED) + oldw("remote host doesn't support Kerberos"); + else if (errno == ENOENT) + oldw("can't provide Kerberos authentication data"); + port = sp->s_port; + goto again; + } + } else { +#ifdef CRYPT + if (doencrypt) + errx(1, + "the -x option requires Kerberos authentication"); +#endif + rem = rcmd(host, port, locuser, user, bp, 0); + } + return (rem); +} +#endif /* KERBEROS */ + +int +response() +{ + char ch, *cp, resp, rbuf[BUFSIZ]; + + if (read(rem, &resp, sizeof(resp)) != sizeof(resp)) + lostconn(0); + + cp = rbuf; + switch(resp) { + case 0: /* ok */ + return (0); + default: + *cp++ = resp; + /* FALLTHROUGH */ + case 1: /* error, followed by error msg */ + case 2: /* fatal error, "" */ + do { + if (read(rem, &ch, sizeof(ch)) != sizeof(ch)) + lostconn(0); + *cp++ = ch; + } while (cp < &rbuf[BUFSIZ] && ch != '\n'); + + if (!iamremote) + (void)write(STDERR_FILENO, rbuf, cp - rbuf); + ++errs; + if (resp == 1) + return (-1); + exit(1); + } + /* NOTREACHED */ +} + +void +usage() +{ +#ifdef KERBEROS +#ifdef CRYPT + (void)fprintf(stderr, "%s\n\t%s\n", + "usage: rcp [-Kpx] [-k realm] f1 f2", + "or: rcp [-Kprx] [-k realm] f1 ... fn directory"); +#else + (void)fprintf(stderr, "%s\n\t%s\n", + "usage: rcp [-Kp] [-k realm] f1 f2", + "or: rcp [-Kpr] [-k realm] f1 ... fn directory"); +#endif +#else + (void)fprintf(stderr, + "usage: rcp [-p] f1 f2; or: rcp [-pr] f1 ... fn directory\n"); +#endif + exit(1); +} + +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +#ifdef KERBEROS +void +#if __STDC__ +oldw(const char *fmt, ...) +#else +oldw(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)fprintf(stderr, "rcp: "); + (void)vfprintf(stderr, fmt, ap); + (void)fprintf(stderr, ", using standard rcp\n"); + va_end(ap); +} +#endif + +void +#if __STDC__ +run_err(const char *fmt, ...) +#else +run_err(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + static FILE *fp; + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + + ++errs; + if (fp == NULL && !(fp = fdopen(rem, "w"))) + return; + (void)fprintf(fp, "%c", 0x01); + (void)fprintf(fp, "rcp: "); + (void)vfprintf(fp, fmt, ap); + (void)fprintf(fp, "\n"); + (void)fflush(fp); + + if (!iamremote) + vwarnx(fmt, ap); + + va_end(ap); +} diff --git a/bin/rcp/util.c b/bin/rcp/util.c new file mode 100644 index 0000000..239a9cb --- /dev/null +++ b/bin/rcp/util.c @@ -0,0 +1,167 @@ +/*- + * Copyright (c) 1992, 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[] = "@(#)util.c 8.2 (Berkeley) 4/2/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <paths.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "extern.h" + +char * +colon(cp) + char *cp; +{ + if (*cp == ':') /* Leading colon is part of file name. */ + return (0); + + for (; *cp; ++cp) { + if (*cp == ':') + return (cp); + if (*cp == '/') + return (0); + } + return (0); +} + +void +verifydir(cp) + char *cp; +{ + struct stat stb; + + if (!stat(cp, &stb)) { + if (S_ISDIR(stb.st_mode)) + return; + errno = ENOTDIR; + } + run_err("%s: %s", cp, strerror(errno)); + exit(1); +} + +int +okname(cp0) + char *cp0; +{ + int c; + char *cp; + + cp = cp0; + do { + c = *cp; + if (c & 0200) + goto bad; + if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-') + goto bad; + } while (*++cp); + return (1); + +bad: warnx("%s: invalid user name", cp0); + return (0); +} + +int +susystem(s, userid) + int userid; + char *s; +{ + sig_t istat, qstat; + int status, w; + pid_t pid; + + pid = vfork(); + switch (pid) { + case -1: + return (127); + + case 0: + (void)setuid(userid); + execl(_PATH_BSHELL, "sh", "-c", s, NULL); + _exit(127); + } + istat = signal(SIGINT, SIG_IGN); + qstat = signal(SIGQUIT, SIG_IGN); + if (waitpid(pid, &status, 0) < 0) + status = -1; + (void)signal(SIGINT, istat); + (void)signal(SIGQUIT, qstat); + return (status); +} + +BUF * +allocbuf(bp, fd, blksize) + BUF *bp; + int fd, blksize; +{ + struct stat stb; + size_t size; + + if (fstat(fd, &stb) < 0) { + run_err("fstat: %s", strerror(errno)); + return (0); + } + size = roundup(stb.st_blksize, blksize); + if (size == 0) + size = blksize; + if (bp->cnt >= size) + return (bp); + if ((bp->buf = realloc(bp->buf, size)) == NULL) { + bp->cnt = 0; + run_err("%s", strerror(errno)); + return (0); + } + bp->cnt = size; + return (bp); +} + +void +lostconn(signo) + int signo; +{ + if (!iamremote) + warnx("lost connection"); + exit(1); +} diff --git a/bin/rm/Makefile b/bin/rm/Makefile new file mode 100644 index 0000000..e3c2624 --- /dev/null +++ b/bin/rm/Makefile @@ -0,0 +1,5 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 + +PROG= rm + +.include <bsd.prog.mk> diff --git a/bin/rm/rm.1 b/bin/rm/rm.1 new file mode 100644 index 0000000..6316d35 --- /dev/null +++ b/bin/rm/rm.1 @@ -0,0 +1,150 @@ +.\" Copyright (c) 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. +.\" +.\" @(#)rm.1 8.2 (Berkeley) 4/18/94 +.\" +.Dd April 18, 1994 +.Dt RM 1 +.Os +.Sh NAME +.Nm rm +.Nd remove directory entries +.Sh SYNOPSIS +.Nm rm +.Op Fl f | Fl i +.Op Fl dPRr +.Ar file ... +.Sh DESCRIPTION +The +.Nm rm +utility attempts to remove the non-directory type files specified on the +command line. +If the permissions of the file do not permit writing, and the standard +input device is a terminal, the user is prompted (on the standard error +output) for confirmation. +.Pp +The options are as follows: +.Bl -tag -width flag +.It Fl d +Attempt to remove directories as well as other types of files. +.It Fl f +Attempt to remove the files without prompting for confirmation, +regardless of the file's permissions. +If the file does not exist, do not display a diagnostic message or modify +the exit status to reflect an error. +The +.Fl f +option overrides any previous +.Fl i +options. +.It Fl i +Request confirmation before attempting to remove each file, regardless of +the file's permissions, or whether or not the standard input device is a +terminal. +The +.Fl i +option overrides any previous +.Fl f +options. +.It Fl P +Overwrite regular files before deleting them. +Files are overwritten three times, first with the byte pattern 0xff, +then 0x00, and then 0xff again, before they are deleted. +.It Fl R +Attempt to remove the file hierarchy rooted in each file argument. +The +.Fl R +option implies the +.Fl d +option. +If the +.Fl i +option is specified, the user is prompted for confirmation before +each directory's contents are processed (as well as before the attempt +is made to remove the directory). +If the user does not respond affirmatively, the file hierarchy rooted in +that directory is skipped. +.Pp +.It Fl r +Equivalent to +.Fl R . +.El +.Pp +The +.Nm rm +utility removes symbolic links, not the files referenced by the links. +.Pp +It is an error to attempt to remove the files ``.'' and ``..''. +.Pp +The +.Nm rm +utility exits 0 if all of the named files or file hierarchies were removed, +or if the +.Fl f +option was specified and all of the existing files or file hierarchies were +removed. +If an error occurs, +.Nm rm +exits with a value >0. +.Sh SEE ALSO +.Xr rmdir 1 , +.Xr unlink 2 , +.Xr fts 3 , +.Xr symlink 7 +.Sh BUGS +The +.Fl P +option assumes that the underlying file system is a fixed-block file +system. +UFS is a fixed-block file system, LFS is not. +In addition, only regular files are overwritten, other types of files +are not. +.Sh COMPATIBILITY +The +.Nm rm +utility differs from historical implementations in that the +.Fl f +option only masks attempts to remove non-existent files instead of +masking a large variety of errors. +.Pp +Also, historical +.Bx +implementations prompted on the standard output, +not the standard error output. +.Sh STANDARDS +The +.Nm rm +command is expected to be +.St -p1003.2 +compatible. diff --git a/bin/rm/rm.c b/bin/rm/rm.c new file mode 100644 index 0000000..4cdf055 --- /dev/null +++ b/bin/rm/rm.c @@ -0,0 +1,378 @@ +/*- + * Copyright (c) 1990, 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) 1990, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)rm.c 8.5 (Berkeley) 4/18/94"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.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> + +int dflag, eval, fflag, iflag, Pflag, stdin_ok; + +int check __P((char *, char *, struct stat *)); +void checkdot __P((char **)); +void rm_file __P((char **)); +void rm_overwrite __P((char *, struct stat *)); +void rm_tree __P((char **)); +void usage __P((void)); + +/* + * rm -- + * This rm is different from historic rm's, but is expected to match + * POSIX 1003.2 behavior. The most visible difference is that -f + * has two specific effects now, ignore non-existent files and force + * file removal. + */ +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch, rflag; + + Pflag = rflag = 0; + while ((ch = getopt(argc, argv, "dfiPRr")) != EOF) + switch(ch) { + case 'd': + dflag = 1; + break; + case 'f': + fflag = 1; + iflag = 0; + break; + case 'i': + fflag = 0; + iflag = 1; + break; + case 'P': + Pflag = 1; + break; + case 'R': + case 'r': /* Compatibility. */ + rflag = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc < 1) + usage(); + + checkdot(argv); + if (!*argv) + exit (eval); + + stdin_ok = isatty(STDIN_FILENO); + + if (rflag) + rm_tree(argv); + else + rm_file(argv); + exit (eval); +} + +void +rm_tree(argv) + char **argv; +{ + FTS *fts; + FTSENT *p; + int needstat; + + /* + * Remove a file hierarchy. If forcing removal (-f), or interactive + * (-i) or can't ask anyway (stdin_ok), don't stat the file. + */ + needstat = !fflag && !iflag && stdin_ok; + + /* + * If the -i option is specified, the user can skip on the pre-order + * visit. The fts_number field flags skipped directories. + */ +#define SKIPPED 1 + + if (!(fts = fts_open(argv, + needstat ? FTS_PHYSICAL : FTS_PHYSICAL|FTS_NOSTAT, + (int (*)())NULL))) + err(1, NULL); + while ((p = fts_read(fts)) != NULL) { + switch (p->fts_info) { + case FTS_DNR: + if (!fflag || p->fts_errno != ENOENT) { + warnx("%s: %s", + p->fts_path, strerror(p->fts_errno)); + eval = 1; + } + continue; + case FTS_ERR: + errx(1, "%s: %s", p->fts_path, strerror(p->fts_errno)); + case FTS_NS: + /* + * FTS_NS: assume that if can't stat the file, it + * can't be unlinked. + */ + if (!needstat) + break; + if (!fflag || p->fts_errno != ENOENT) { + warnx("%s: %s", + p->fts_path, strerror(p->fts_errno)); + eval = 1; + } + continue; + case FTS_D: + /* Pre-order: give user chance to skip. */ + if (iflag && !check(p->fts_path, p->fts_accpath, + p->fts_statp)) { + (void)fts_set(fts, p, FTS_SKIP); + p->fts_number = SKIPPED; + } + continue; + case FTS_DP: + /* Post-order: see if user skipped. */ + if (p->fts_number == SKIPPED) + continue; + break; + } + if (!fflag && + !check(p->fts_path, p->fts_accpath, p->fts_statp)) + continue; + + /* + * If we can't read or search the directory, may still be + * able to remove it. Don't print out the un{read,search}able + * message unless the remove fails. + */ + if (p->fts_info == FTS_DP || p->fts_info == FTS_DNR) { + if (!rmdir(p->fts_accpath)) + continue; + if (errno == ENOENT) { + if (fflag) + continue; + } else if (p->fts_info != FTS_DP) + warnx("%s: unable to read", p->fts_path); + } else { + if (Pflag) + rm_overwrite(p->fts_accpath, NULL); + if (!unlink(p->fts_accpath) || fflag && errno == ENOENT) + continue; + } + warn("%s", p->fts_path); + eval = 1; + } + if (errno) + err(1, "fts_read"); +} + +void +rm_file(argv) + char **argv; +{ + struct stat sb; + int df, rval; + char *f; + + df = dflag; + /* + * Remove a file. POSIX 1003.2 states that, by default, attempting + * to remove a directory is an error, so must always stat the file. + */ + while ((f = *argv++) != NULL) { + /* Assume if can't stat the file, can't unlink it. */ + if (lstat(f, &sb)) { + if (!fflag || errno != ENOENT) { + warn("%s", f); + eval = 1; + } + continue; + } + if (S_ISDIR(sb.st_mode) && !df) { + warnx("%s: is a directory", f); + eval = 1; + continue; + } + if (!fflag && !check(f, f, &sb)) + continue; + if (S_ISDIR(sb.st_mode)) + rval = rmdir(f); + else { + if (Pflag) + rm_overwrite(f, &sb); + rval = unlink(f); + } + if (rval && (!fflag || errno != ENOENT)) { + warn("%s", f); + eval = 1; + } + } +} + +/* + * rm_overwrite -- + * Overwrite the file 3 times with varying bit patterns. + * + * XXX + * This is a cheap way to *really* delete files. Note that only regular + * files are deleted, directories (and therefore names) will remain. + * Also, this assumes a fixed-block file system (like FFS, or a V7 or a + * System V file system). In a logging file system, you'll have to have + * kernel support. + */ +void +rm_overwrite(file, sbp) + char *file; + struct stat *sbp; +{ + struct stat sb; + off_t len; + int fd, wlen; + char buf[8 * 1024]; + + fd = -1; + if (sbp == NULL) { + if (lstat(file, &sb)) + goto err; + sbp = &sb; + } + if (!S_ISREG(sbp->st_mode)) + return; + if ((fd = open(file, O_WRONLY, 0)) == -1) + goto err; + +#define PASS(byte) { \ + memset(buf, byte, sizeof(buf)); \ + for (len = sbp->st_size; len > 0; len -= wlen) { \ + wlen = len < sizeof(buf) ? len : sizeof(buf); \ + if (write(fd, buf, wlen) != wlen) \ + goto err; \ + } \ +} + PASS(0xff); + if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) + goto err; + PASS(0x00); + if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) + goto err; + PASS(0xff); + if (!fsync(fd) && !close(fd)) + return; + +err: eval = 1; + warn("%s", file); +} + + +int +check(path, name, sp) + char *path, *name; + struct stat *sp; +{ + int ch, first; + char modep[15]; + + /* Check -i first. */ + if (iflag) + (void)fprintf(stderr, "remove %s? ", path); + else { + /* + * If it's not a symbolic link and it's unwritable and we're + * talking to a terminal, ask. Symbolic links are excluded + * because their permissions are meaningless. Check stdin_ok + * first because we may not have stat'ed the file. + */ + if (!stdin_ok || S_ISLNK(sp->st_mode) || !access(name, W_OK)) + return (1); + strmode(sp->st_mode, modep); + (void)fprintf(stderr, "override %s%s%s/%s for %s? ", + modep + 1, modep[9] == ' ' ? "" : " ", + user_from_uid(sp->st_uid, 0), + group_from_gid(sp->st_gid, 0), path); + } + (void)fflush(stderr); + + first = ch = getchar(); + while (ch != '\n' && ch != EOF) + ch = getchar(); + return (first == 'y'); +} + +#define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || (a)[1] == '.' && !(a)[2])) +void +checkdot(argv) + char **argv; +{ + char *p, **save, **t; + int complained; + + complained = 0; + for (t = argv; *t;) { + if ((p = strrchr(*t, '/')) != NULL) + ++p; + else + p = *t; + if (ISDOT(p)) { + if (!complained++) + warnx("\".\" and \"..\" may not be removed"); + eval = 1; + for (save = t; (t[0] = t[1]) != NULL; ++t); + t = save; + } else + ++t; + } +} + +void +usage() +{ + + (void)fprintf(stderr, "usage: rm [-dfiRr] file ...\n"); + exit(1); +} diff --git a/bin/rmail/Makefile b/bin/rmail/Makefile new file mode 100644 index 0000000..eb2fb48 --- /dev/null +++ b/bin/rmail/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 + +PROG= rmail +MAN8= rmail.0 + +.include <bsd.prog.mk> diff --git a/bin/rmail/rmail.8 b/bin/rmail/rmail.8 new file mode 100644 index 0000000..2079d4e --- /dev/null +++ b/bin/rmail/rmail.8 @@ -0,0 +1,71 @@ +.\" Copyright (c) 1983, 1990 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. +.\" +.\" @(#)rmail.8 6.10 (Berkeley) 4/29/93 +.\" +.Dd April 29, 1993 +.Dt RMAIL 8 +.Os BSD 4.2 +.Sh NAME +.Nm rmail +.Nd handle remote mail received via uucp +.Sh SYNOPSIS +.Nm rmail +.Ar user ... +.Sh DESCRIPTION +.Nm Rmail +interprets incoming mail received via +.Xr uucp 1 , +collapsing ``From'' lines in the form generated +by +.Xr mail.local 8 +into a single line of the form ``return-path!sender'', +and passing the processed mail on to +.Xr sendmail 8 . +.Pp +.Nm Rmail +is explicitly designed for use with +.Xr uucp +and +.Xr sendmail . +.Sh SEE ALSO +.Xr uucp 1 , +.Xr mail.local 8 , +.Xr sendmail 8 +.Sh HISTORY +The +.Nm rmail +program appeared in +.Bx 4.2 . +.Sh BUGS +.Nm Rmail +should not reside in +.Pa /bin . diff --git a/bin/rmail/rmail.c b/bin/rmail/rmail.c new file mode 100644 index 0000000..aac400a --- /dev/null +++ b/bin/rmail/rmail.c @@ -0,0 +1,359 @@ +/* + * Copyright (c) 1988, 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) 1988, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)rmail.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +/* + * RMAIL -- UUCP mail server. + * + * This program reads the >From ... remote from ... lines that UUCP is so + * fond of and turns them into something reasonable. It then execs sendmail + * with various options built from these lines. + * + * The expected syntax is: + * + * <user> := [-a-z0-9]+ + * <date> := ctime format + * <site> := [-a-z0-9!]+ + * <blank line> := "^\n$" + * <from> := "From" <space> <user> <space> <date> + * [<space> "remote from" <space> <site>] + * <forward> := ">" <from> + * msg := <from> <forward>* <blank-line> <body> + * + * The output of rmail(8) compresses the <forward> lines into a single + * from path. + * + * The err(3) routine is included here deliberately to make this code + * a bit more portable. + */ +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include <ctype.h> +#include <fcntl.h> +#include <paths.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> + +void err __P((int, const char *, ...)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; + extern int errno, optind; + FILE *fp; + struct stat sb; + size_t fplen, fptlen, len; + off_t offset; + int ch, debug, i, pdes[2], pid, status; + char *addrp, *domain, *p, *t; + char *from_path, *from_sys, *from_user; + char *args[100], buf[2048], lbuf[2048]; + + debug = 0; + domain = "UUCP"; /* Default "domain". */ + while ((ch = getopt(argc, argv, "D:T")) != EOF) + switch (ch) { + case 'T': + debug = 1; + break; + case 'D': + domain = optarg; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc < 1) + usage(); + + from_path = from_sys = from_user = NULL; + for (offset = 0;;) { + + /* Get and nul-terminate the line. */ + if (fgets(lbuf, sizeof(lbuf), stdin) == NULL) + exit (EX_DATAERR); + if ((p = strchr(lbuf, '\n')) == NULL) + err(EX_DATAERR, "line too long"); + *p = '\0'; + + /* Parse lines until reach a non-"From" line. */ + if (!strncmp(lbuf, "From ", 5)) + addrp = lbuf + 5; + else if (!strncmp(lbuf, ">From ", 6)) + addrp = lbuf + 6; + else if (offset == 0) + err(EX_DATAERR, + "missing or empty From line: %s", lbuf); + else { + *p = '\n'; + break; + } + + if (*addrp == '\0') + err(EX_DATAERR, "corrupted From line: %s", lbuf); + + /* Use the "remote from" if it exists. */ + for (p = addrp; (p = strchr(p + 1, 'r')) != NULL;) + if (!strncmp(p, "remote from ", 12)) { + for (t = p += 12; *t && !isspace(*t); ++t); + *t = '\0'; + if (debug) + (void)fprintf(stderr, + "remote from: %s\n", p); + break; + } + + /* Else use the string up to the last bang. */ + if (p == NULL) + if (*addrp == '!') + err(EX_DATAERR, + "bang starts address: %s", addrp); + else if ((t = strrchr(addrp, '!')) != NULL) { + *t = '\0'; + p = addrp; + addrp = t + 1; + if (*addrp == '\0') + err(EX_DATAERR, + "corrupted From line: %s", lbuf); + if (debug) + (void)fprintf(stderr, "bang: %s\n", p); + } + + /* 'p' now points to any system string from this line. */ + if (p != NULL) { + /* Nul terminate it as necessary. */ + for (t = p; *t && !isspace(*t); ++t); + *t = '\0'; + + /* If the first system, copy to the from_sys string. */ + if (from_sys == NULL) { + if ((from_sys = strdup(p)) == NULL) + err(EX_TEMPFAIL, NULL); + if (debug) + (void)fprintf(stderr, + "from_sys: %s\n", from_sys); + } + + /* Concatenate to the path string. */ + len = t - p; + if (from_path == NULL) { + fplen = 0; + if ((from_path = malloc(fptlen = 256)) == NULL) + err(EX_TEMPFAIL, NULL); + } + if (fplen + len + 2 > fptlen) { + fptlen += MAX(fplen + len + 2, 256); + if ((from_path = + realloc(from_path, fptlen)) == NULL) + err(EX_TEMPFAIL, NULL); + } + memmove(from_path + fplen, p, len); + fplen += len; + from_path[fplen++] = '!'; + from_path[fplen] = '\0'; + } + + /* Save off from user's address; the last one wins. */ + for (p = addrp; *p && !isspace(*p); ++p); + *p = '\0'; + if (from_user != NULL) + free(from_user); + if ((from_user = strdup(addrp)) == NULL) + err(EX_TEMPFAIL, NULL); + + if (debug) { + if (from_path != NULL) + (void)fprintf(stderr, + "from_path: %s\n", from_path); + (void)fprintf(stderr, "from_user: %s\n", from_user); + } + + if (offset != -1) + offset = (off_t)ftell(stdin); + } + + i = 0; + args[i++] = _PATH_SENDMAIL; /* Build sendmail's argument list. */ + args[i++] = "-oee"; /* No errors, just status. */ + args[i++] = "-odq"; /* Queue it, don't try to deliver. */ + args[i++] = "-oi"; /* Ignore '.' on a line by itself. */ + + if (from_sys != NULL) { /* Set sender's host name. */ + if (strchr(from_sys, '.') == NULL) + (void)snprintf(buf, sizeof(buf), + "-oMs%s.%s", from_sys, domain); + else + (void)snprintf(buf, sizeof(buf), "-oMs%s", from_sys); + if ((args[i++] = strdup(buf)) == NULL) + err(EX_TEMPFAIL, NULL); + } + /* Set protocol used. */ + (void)snprintf(buf, sizeof(buf), "-oMr%s", domain); + if ((args[i++] = strdup(buf)) == NULL) + err(EX_TEMPFAIL, NULL); + + /* Set name of ``from'' person. */ + (void)snprintf(buf, sizeof(buf), "-f%s%s", + from_path ? from_path : "", from_user); + if ((args[i++] = strdup(buf)) == NULL) + err(EX_TEMPFAIL, NULL); + + /* + * Don't copy arguments beginning with - as they will be + * passed to sendmail and could be interpreted as flags. + */ + do { + if (*argv && **argv == '-') + err(EX_USAGE, "dash precedes argument: %s", *argv); + } while ((args[i++] = *argv++) != NULL); + + if (debug) { + (void)fprintf(stderr, "Sendmail arguments:\n"); + for (i = 0; args[i]; i++) + (void)fprintf(stderr, "\t%s\n", args[i]); + } + + /* + * If called with a regular file as standard input, seek to the right + * position in the file and just exec sendmail. Could probably skip + * skip the stat, but it's not unreasonable to believe that a failed + * seek will cause future reads to fail. + */ + if (!fstat(STDIN_FILENO, &sb) && S_ISREG(sb.st_mode)) { + if (lseek(STDIN_FILENO, offset, SEEK_SET) != offset) + err(EX_TEMPFAIL, "stdin seek"); + execv(_PATH_SENDMAIL, args); + err(EX_OSERR, "%s", _PATH_SENDMAIL); + } + + if (pipe(pdes) < 0) + err(EX_OSERR, NULL); + + switch (pid = vfork()) { + case -1: /* Err. */ + err(EX_OSERR, NULL); + case 0: /* Child. */ + if (pdes[0] != STDIN_FILENO) { + (void)dup2(pdes[0], STDIN_FILENO); + (void)close(pdes[0]); + } + (void)close(pdes[1]); + execv(_PATH_SENDMAIL, args); + _exit(127); + /* NOTREACHED */ + } + + if ((fp = fdopen(pdes[1], "w")) == NULL) + err(EX_OSERR, NULL); + (void)close(pdes[0]); + + /* Copy the file down the pipe. */ + do { + (void)fprintf(fp, "%s", lbuf); + } while (fgets(lbuf, sizeof(lbuf), stdin) != NULL); + + if (ferror(stdin)) + err(EX_TEMPFAIL, "stdin: %s", strerror(errno)); + + if (fclose(fp)) + err(EX_OSERR, NULL); + + if ((waitpid(pid, &status, 0)) == -1) + err(EX_OSERR, "%s", _PATH_SENDMAIL); + + if (!WIFEXITED(status)) + err(EX_OSERR, + "%s: did not terminate normally", _PATH_SENDMAIL); + + if (WEXITSTATUS(status)) + err(status, "%s: terminated with %d (non-zero) status", + _PATH_SENDMAIL, WEXITSTATUS(status)); + exit(EX_OK); +} + +void +usage() +{ + (void)fprintf(stderr, "usage: rmail [-T] [-D domain] user ...\n"); + exit(EX_USAGE); +} + +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +void +#ifdef __STDC__ +err(int eval, const char *fmt, ...) +#else +err(eval, fmt, va_alist) + int eval; + const char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)fprintf(stderr, "rmail: "); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); + exit(eval); +} diff --git a/bin/rmdir/Makefile b/bin/rmdir/Makefile new file mode 100644 index 0000000..12e7120 --- /dev/null +++ b/bin/rmdir/Makefile @@ -0,0 +1,5 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 + +PROG= rmdir + +.include <bsd.prog.mk> diff --git a/bin/rmdir/rmdir.1 b/bin/rmdir/rmdir.1 new file mode 100644 index 0000000..7649882 --- /dev/null +++ b/bin/rmdir/rmdir.1 @@ -0,0 +1,92 @@ +.\" Copyright (c) 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. +.\" +.\" @(#)rmdir.1 8.1 (Berkeley) 5/31/93 +.\" +.Dd May 31, 1993 +.Dt RMDIR 1 +.Os +.Sh NAME +.Nm rmdir +.Nd remove directories +.Sh SYNOPSIS +.Nm rmdir +.\" .Op Fl p +.Ar directory ... +.Sh DESCRIPTION +The rmdir utility removes the directory entry specified by +each +.Ar directory +argument, provided it is empty. +.Pp +Arguments are processed in the order given. +In order to remove both a parent directory and a subdirectory +of that parent, the subdirectory +must be specified first so the parent directory +is empty when +.Nm rmdir +tries to remove it. +.\" .Pp +.\" The following option is available: +.\" .Bl -tag -width Ds +.\" .It Fl p +.\" Each +.\" .Ar directory +.\" argument is treated as a pathname of which all +.\" components will be removed, if they are empty, +.\" starting with the last most component. +.\" (See +.\" .Xr rm 1 +.\" for fully non-discriminant recursive removal). +.\" .El +.Pp +The +.Nm rmdir +utility exits with one of the following values: +.Bl -tag -width flag +.It Li \&0 +Each directory entry specified by a dir operand +referred to an empty directory and was removed +successfully. +.It Li \&>\&0 +An error occurred. +.El +.Sh SEE ALSO +.Xr rm 1 +.Sh STANDARDS +The +.Nm rmdir +command is expected to be +.St -p1003.2 +compatible. diff --git a/bin/rmdir/rmdir.c b/bin/rmdir/rmdir.c new file mode 100644 index 0000000..f093a70 --- /dev/null +++ b/bin/rmdir/rmdir.c @@ -0,0 +1,86 @@ +/*- + * Copyright (c) 1992, 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) 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)rmdir.c 8.3 (Berkeley) 4/2/94"; +#endif /* not lint */ + +#include <err.h> +#include <errno.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[]; +{ + int ch, errors; + + while ((ch = getopt(argc, argv, "")) != EOF) + switch(ch) { + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc == 0) + usage(); + + for (errors = 0; *argv; ++argv) + if (rmdir(*argv) < 0) { + warn("%s", *argv); + errors = 1; + } + exit(errors); +} + +void +usage() +{ + + (void)fprintf(stderr, "usage: rmdir directory ...\n"); + exit(1); +} diff --git a/bin/sh/Makefile b/bin/sh/Makefile new file mode 100644 index 0000000..3114da6 --- /dev/null +++ b/bin/sh/Makefile @@ -0,0 +1,49 @@ +# @(#)Makefile 8.1 (Berkeley) 6/8/93 + +PROG= sh +SRCS= alias.c builtins.c cd.c echo.c error.c eval.c exec.c expand.c \ + histedit.c input.c jobs.c mail.c main.c memalloc.c miscbltin.c \ + mystring.c nodes.c options.c parser.c printf.c redir.c show.c \ + signames.c syntax.c trap.c output.c var.c +OBJS+= init.o arith.o arith_lex.o +LDADD= -ll -ledit -ltermcap -lcompat +LFLAGS= -8 # 8-bit lex scanner for arithmetic +CFLAGS+=-DSHELL -I. -I${.CURDIR} +.PATH: ${.CURDIR}/bltin ${.CURDIR}/../../usr.bin/printf +CLEANFILES+=\ + builtins.c builtins.h init.c mkinit mknodes mksignames mksyntax \ + nodes.c nodes.h signames.c signames.h syntax.c syntax.h token.def \ + y.tab.h + +.depend parser.o: token.def +token.def: mktokens + sh ${.CURDIR}/mktokens + +builtins.h builtins.c: ${.CURDIR}/mkbuiltins ${.CURDIR}/builtins + cd ${.CURDIR}; sh mkbuiltins + +init.c: mkinit ${SRCS} + ./mkinit '${CC} -c ${CFLAGS} init.c' ${.ALLSRC} + +mkinit: ${.CURDIR}/mkinit.c + ${CC} ${CFLAGS} ${.CURDIR}/mkinit.c -o $@ + +nodes.c nodes.h: mknodes ${.CURDIR}/nodetypes ${.CURDIR}/nodes.c.pat + ./mknodes ${.CURDIR}/nodetypes ${.CURDIR}/nodes.c.pat + +mknodes: ${.CURDIR}/mknodes.c + ${CC} ${CFLAGS} ${.CURDIR}/mknodes.c -o $@ + +signames.c signames.h: mksignames + ./mksignames + +mksignames: ${.CURDIR}/mksignames.c + ${CC} ${CFLAGS} ${.CURDIR}/mksignames.c -o $@ + +syntax.c syntax.h: mksyntax + ./mksyntax + +mksyntax: ${.CURDIR}/mksyntax.c ${.CURDIR}/parser.h + ${CC} ${CFLAGS} ${.CURDIR}/mksyntax.c -o $@ + +.include <bsd.prog.mk> diff --git a/bin/sh/TOUR b/bin/sh/TOUR new file mode 100644 index 0000000..3514a4e --- /dev/null +++ b/bin/sh/TOUR @@ -0,0 +1,356 @@ +# @(#)TOUR 8.1 (Berkeley) 5/31/93 + +NOTE -- This is the original TOUR paper distributed with ash and +does not represent the current state of the shell. It is provided anyway +since it provides helpful information for how the shell is structured, +but be warned that things have changed -- the current shell is +still under development. + +================================================================ + + A Tour through Ash + + Copyright 1989 by Kenneth Almquist. + + +DIRECTORIES: The subdirectory bltin contains commands which can +be compiled stand-alone. The rest of the source is in the main +ash directory. + +SOURCE CODE GENERATORS: Files whose names begin with "mk" are +programs that generate source code. A complete list of these +programs is: + + program intput files generates + ------- ------------ --------- + mkbuiltins builtins builtins.h builtins.c + mkinit *.c init.c + mknodes nodetypes nodes.h nodes.c + mksignames - signames.h signames.c + mksyntax - syntax.h syntax.c + mktokens - token.def + bltin/mkexpr unary_op binary_op operators.h operators.c + +There are undoubtedly too many of these. Mkinit searches all the +C source files for entries looking like: + + INIT { + x = 1; /* executed during initialization */ + } + + RESET { + x = 2; /* executed when the shell does a longjmp + back to the main command loop */ + } + + SHELLPROC { + x = 3; /* executed when the shell runs a shell procedure */ + } + +It pulls this code out into routines which are when particular +events occur. The intent is to improve modularity by isolating +the information about which modules need to be explicitly +initialized/reset within the modules themselves. + +Mkinit recognizes several constructs for placing declarations in +the init.c file. + INCLUDE "file.h" +includes a file. The storage class MKINIT makes a declaration +available in the init.c file, for example: + MKINIT int funcnest; /* depth of function calls */ +MKINIT alone on a line introduces a structure or union declara- +tion: + MKINIT + struct redirtab { + short renamed[10]; + }; +Preprocessor #define statements are copied to init.c without any +special action to request this. + +INDENTATION: The ash source is indented in multiples of six +spaces. The only study that I have heard of on the subject con- +cluded that the optimal amount to indent is in the range of four +to six spaces. I use six spaces since it is not too big a jump +from the widely used eight spaces. If you really hate six space +indentation, use the adjind (source included) program to change +it to something else. + +EXCEPTIONS: Code for dealing with exceptions appears in +exceptions.c. The C language doesn't include exception handling, +so I implement it using setjmp and longjmp. The global variable +exception contains the type of exception. EXERROR is raised by +calling error. EXINT is an interrupt. EXSHELLPROC is an excep- +tion which is raised when a shell procedure is invoked. The pur- +pose of EXSHELLPROC is to perform the cleanup actions associated +with other exceptions. After these cleanup actions, the shell +can interpret a shell procedure itself without exec'ing a new +copy of the shell. + +INTERRUPTS: In an interactive shell, an interrupt will cause an +EXINT exception to return to the main command loop. (Exception: +EXINT is not raised if the user traps interrupts using the trap +command.) The INTOFF and INTON macros (defined in exception.h) +provide uninterruptable critical sections. Between the execution +of INTOFF and the execution of INTON, interrupt signals will be +held for later delivery. INTOFF and INTON can be nested. + +MEMALLOC.C: Memalloc.c defines versions of malloc and realloc +which call error when there is no memory left. It also defines a +stack oriented memory allocation scheme. Allocating off a stack +is probably more efficient than allocation using malloc, but the +big advantage is that when an exception occurs all we have to do +to free up the memory in use at the time of the exception is to +restore the stack pointer. The stack is implemented using a +linked list of blocks. + +STPUTC: If the stack were contiguous, it would be easy to store +strings on the stack without knowing in advance how long the +string was going to be: + p = stackptr; + *p++ = c; /* repeated as many times as needed */ + stackptr = p; +The folloing three macros (defined in memalloc.h) perform these +operations, but grow the stack if you run off the end: + STARTSTACKSTR(p); + STPUTC(c, p); /* repeated as many times as needed */ + grabstackstr(p); + +We now start a top-down look at the code: + +MAIN.C: The main routine performs some initialization, executes +the user's profile if necessary, and calls cmdloop. Cmdloop is +repeatedly parses and executes commands. + +OPTIONS.C: This file contains the option processing code. It is +called from main to parse the shell arguments when the shell is +invoked, and it also contains the set builtin. The -i and -j op- +tions (the latter turns on job control) require changes in signal +handling. The routines setjobctl (in jobs.c) and setinteractive +(in trap.c) are called to handle changes to these options. + +PARSING: The parser code is all in parser.c. A recursive des- +cent parser is used. Syntax tables (generated by mksyntax) are +used to classify characters during lexical analysis. There are +three tables: one for normal use, one for use when inside single +quotes, and one for use when inside double quotes. The tables +are machine dependent because they are indexed by character vari- +ables and the range of a char varies from machine to machine. + +PARSE OUTPUT: The output of the parser consists of a tree of +nodes. The various types of nodes are defined in the file node- +types. + +Nodes of type NARG are used to represent both words and the con- +tents of here documents. An early version of ash kept the con- +tents of here documents in temporary files, but keeping here do- +cuments in memory typically results in significantly better per- +formance. It would have been nice to make it an option to use +temporary files for here documents, for the benefit of small +machines, but the code to keep track of when to delete the tem- +porary files was complex and I never fixed all the bugs in it. +(AT&T has been maintaining the Bourne shell for more than ten +years, and to the best of my knowledge they still haven't gotten +it to handle temporary files correctly in obscure cases.) + +The text field of a NARG structure points to the text of the +word. The text consists of ordinary characters and a number of +special codes defined in parser.h. The special codes are: + + CTLVAR Variable substitution + CTLENDVAR End of variable substitution + CTLBACKQ Command substitution + CTLBACKQ|CTLQUOTE Command substitution inside double quotes + CTLESC Escape next character + +A variable substitution contains the following elements: + + CTLVAR type name '=' [ alternative-text CTLENDVAR ] + +The type field is a single character specifying the type of sub- +stitution. The possible types are: + + VSNORMAL $var + VSMINUS ${var-text} + VSMINUS|VSNUL ${var:-text} + VSPLUS ${var+text} + VSPLUS|VSNUL ${var:+text} + VSQUESTION ${var?text} + VSQUESTION|VSNUL ${var:?text} + VSASSIGN ${var=text} + VSASSIGN|VSNUL ${var=text} + +In addition, the type field will have the VSQUOTE flag set if the +variable is enclosed in double quotes. The name of the variable +comes next, terminated by an equals sign. If the type is not +VSNORMAL, then the text field in the substitution follows, ter- +minated by a CTLENDVAR byte. + +Commands in back quotes are parsed and stored in a linked list. +The locations of these commands in the string are indicated by +CTLBACKQ and CTLBACKQ+CTLQUOTE characters, depending upon whether +the back quotes were enclosed in double quotes. + +The character CTLESC escapes the next character, so that in case +any of the CTL characters mentioned above appear in the input, +they can be passed through transparently. CTLESC is also used to +escape '*', '?', '[', and '!' characters which were quoted by the +user and thus should not be used for file name generation. + +CTLESC characters have proved to be particularly tricky to get +right. In the case of here documents which are not subject to +variable and command substitution, the parser doesn't insert any +CTLESC characters to begin with (so the contents of the text +field can be written without any processing). Other here docu- +ments, and words which are not subject to splitting and file name +generation, have the CTLESC characters removed during the vari- +able and command substitution phase. Words which are subject +splitting and file name generation have the CTLESC characters re- +moved as part of the file name phase. + +EXECUTION: Command execution is handled by the following files: + eval.c The top level routines. + redir.c Code to handle redirection of input and output. + jobs.c Code to handle forking, waiting, and job control. + exec.c Code to to path searches and the actual exec sys call. + expand.c Code to evaluate arguments. + var.c Maintains the variable symbol table. Called from expand.c. + +EVAL.C: Evaltree recursively executes a parse tree. The exit +status is returned in the global variable exitstatus. The alter- +native entry evalbackcmd is called to evaluate commands in back +quotes. It saves the result in memory if the command is a buil- +tin; otherwise it forks off a child to execute the command and +connects the standard output of the child to a pipe. + +JOBS.C: To create a process, you call makejob to return a job +structure, and then call forkshell (passing the job structure as +an argument) to create the process. Waitforjob waits for a job +to complete. These routines take care of process groups if job +control is defined. + +REDIR.C: Ash allows file descriptors to be redirected and then +restored without forking off a child process. This is accom- +plished by duplicating the original file descriptors. The redir- +tab structure records where the file descriptors have be dupli- +cated to. + +EXEC.C: The routine find_command locates a command, and enters +the command in the hash table if it is not already there. The +third argument specifies whether it is to print an error message +if the command is not found. (When a pipeline is set up, +find_command is called for all the commands in the pipeline be- +fore any forking is done, so to get the commands into the hash +table of the parent process. But to make command hashing as +transparent as possible, we silently ignore errors at that point +and only print error messages if the command cannot be found +later.) + +The routine shellexec is the interface to the exec system call. + +EXPAND.C: Arguments are processed in three passes. The first +(performed by the routine argstr) performs variable and command +substitution. The second (ifsbreakup) performs word splitting +and the third (expandmeta) performs file name generation. If the +"/u" directory is simulated, then when "/u/username" is replaced +by the user's home directory, the flag "didudir" is set. This +tells the cd command that it should print out the directory name, +just as it would if the "/u" directory were implemented using +symbolic links. + +VAR.C: Variables are stored in a hash table. Probably we should +switch to extensible hashing. The variable name is stored in the +same string as the value (using the format "name=value") so that +no string copying is needed to create the environment of a com- +mand. Variables which the shell references internally are preal- +located so that the shell can reference the values of these vari- +ables without doing a lookup. + +When a program is run, the code in eval.c sticks any environment +variables which precede the command (as in "PATH=xxx command") in +the variable table as the simplest way to strip duplicates, and +then calls "environment" to get the value of the environment. +There are two consequences of this. First, if an assignment to +PATH precedes the command, the value of PATH before the assign- +ment must be remembered and passed to shellexec. Second, if the +program turns out to be a shell procedure, the strings from the +environment variables which preceded the command must be pulled +out of the table and replaced with strings obtained from malloc, +since the former will automatically be freed when the stack (see +the entry on memalloc.c) is emptied. + +BUILTIN COMMANDS: The procedures for handling these are scat- +tered throughout the code, depending on which location appears +most appropriate. They can be recognized because their names al- +ways end in "cmd". The mapping from names to procedures is +specified in the file builtins, which is processed by the mkbuil- +tins command. + +A builtin command is invoked with argc and argv set up like a +normal program. A builtin command is allowed to overwrite its +arguments. Builtin routines can call nextopt to do option pars- +ing. This is kind of like getopt, but you don't pass argc and +argv to it. Builtin routines can also call error. This routine +normally terminates the shell (or returns to the main command +loop if the shell is interactive), but when called from a builtin +command it causes the builtin command to terminate with an exit +status of 2. + +The directory bltins contains commands which can be compiled in- +dependently but can also be built into the shell for efficiency +reasons. The makefile in this directory compiles these programs +in the normal fashion (so that they can be run regardless of +whether the invoker is ash), but also creates a library named +bltinlib.a which can be linked with ash. The header file bltin.h +takes care of most of the differences between the ash and the +stand-alone environment. The user should call the main routine +"main", and #define main to be the name of the routine to use +when the program is linked into ash. This #define should appear +before bltin.h is included; bltin.h will #undef main if the pro- +gram is to be compiled stand-alone. + +CD.C: This file defines the cd and pwd builtins. The pwd com- +mand runs /bin/pwd the first time it is invoked (unless the user +has already done a cd to an absolute pathname), but then +remembers the current directory and updates it when the cd com- +mand is run, so subsequent pwd commands run very fast. The main +complication in the cd command is in the docd command, which +resolves symbolic links into actual names and informs the user +where the user ended up if he crossed a symbolic link. + +SIGNALS: Trap.c implements the trap command. The routine set- +signal figures out what action should be taken when a signal is +received and invokes the signal system call to set the signal ac- +tion appropriately. When a signal that a user has set a trap for +is caught, the routine "onsig" sets a flag. The routine dotrap +is called at appropriate points to actually handle the signal. +When an interrupt is caught and no trap has been set for that +signal, the routine "onint" in error.c is called. + +OUTPUT: Ash uses it's own output routines. There are three out- +put structures allocated. "Output" represents the standard out- +put, "errout" the standard error, and "memout" contains output +which is to be stored in memory. This last is used when a buil- +tin command appears in backquotes, to allow its output to be col- +lected without doing any I/O through the UNIX operating system. +The variables out1 and out2 normally point to output and errout, +respectively, but they are set to point to memout when appropri- +ate inside backquotes. + +INPUT: The basic input routine is pgetc, which reads from the +current input file. There is a stack of input files; the current +input file is the top file on this stack. The code allows the +input to come from a string rather than a file. (This is for the +-c option and the "." and eval builtin commands.) The global +variable plinno is saved and restored when files are pushed and +popped from the stack. The parser routines store the number of +the current line in this variable. + +DEBUGGING: If DEBUG is defined in shell.h, then the shell will +write debugging information to the file $HOME/trace. Most of +this is done using the TRACE macro, which takes a set of printf +arguments inside two sets of parenthesis. Example: +"TRACE(("n=%d0, n))". The double parenthesis are necessary be- +cause the preprocessor can't handle functions with a variable +number of arguments. Defining DEBUG also causes the shell to +generate a core dump if it is sent a quit signal. The tracing +code is in show.c. diff --git a/bin/sh/alias.c b/bin/sh/alias.c new file mode 100644 index 0000000..3cf38a9 --- /dev/null +++ b/bin/sh/alias.c @@ -0,0 +1,251 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)alias.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include "shell.h" +#include "input.h" +#include "output.h" +#include "error.h" +#include "memalloc.h" +#include "mystring.h" +#include "alias.h" +#include "options.h" /* XXX for argptr (should remove?) */ + +#define ATABSIZE 39 + +struct alias *atab[ATABSIZE]; + +STATIC struct alias **hashalias __P((char *)); + +STATIC +setalias(name, val) + char *name, *val; + { + struct alias *ap, **app; + + app = hashalias(name); + for (ap = *app; ap; ap = ap->next) { + if (equal(name, ap->name)) { + INTOFF; + ckfree(ap->val); + ap->val = savestr(val); + INTON; + return; + } + } + /* not found */ + INTOFF; + ap = ckmalloc(sizeof (struct alias)); + ap->name = savestr(name); + /* + * XXX - HACK: in order that the parser will not finish reading the + * alias value off the input before processing the next alias, we + * dummy up an extra space at the end of the alias. This is a crock + * and should be re-thought. The idea (if you feel inclined to help) + * is to avoid alias recursions. The mechanism used is: when + * expanding an alias, the value of the alias is pushed back on the + * input as a string and a pointer to the alias is stored with the + * string. The alias is marked as being in use. When the input + * routine finishes reading the string, it markes the alias not + * in use. The problem is synchronization with the parser. Since + * it reads ahead, the alias is marked not in use before the + * resulting token(s) is next checked for further alias sub. The + * H A C K is that we add a little fluff after the alias value + * so that the string will not be exhausted. This is a good + * idea ------- ***NOT*** + */ +#ifdef notyet + ap->val = savestr(val); +#else /* hack */ + { + int len = strlen(val); + ap->val = ckmalloc(len + 2); + bcopy(val, ap->val, len); + ap->val[len] = ' '; /* fluff */ + ap->val[len+1] = '\0'; + } +#endif + ap->next = *app; + *app = ap; + INTON; +} + +STATIC int +unalias(name) + char *name; + { + struct alias *ap, **app; + + app = hashalias(name); + + for (ap = *app; ap; app = &(ap->next), ap = ap->next) { + if (equal(name, ap->name)) { + /* + * if the alias is currently in use (i.e. its + * buffer is being used by the input routine) we + * just null out the name instead of freeing it. + * We could clear it out later, but this situation + * is so rare that it hardly seems worth it. + */ + if (ap->flag & ALIASINUSE) + *ap->name = '\0'; + else { + INTOFF; + *app = ap->next; + ckfree(ap->name); + ckfree(ap->val); + ckfree(ap); + INTON; + } + return (0); + } + } + + return (1); +} + +#ifdef mkinit +MKINIT void rmaliases(); + +SHELLPROC { + rmaliases(); +} +#endif + +void +rmaliases() { + struct alias *ap, *tmp; + int i; + + INTOFF; + for (i = 0; i < ATABSIZE; i++) { + ap = atab[i]; + atab[i] = NULL; + while (ap) { + ckfree(ap->name); + ckfree(ap->val); + tmp = ap; + ap = ap->next; + ckfree(tmp); + } + } + INTON; +} + +struct alias * +lookupalias(name, check) + char *name; + { + struct alias *ap = *hashalias(name); + + for (; ap; ap = ap->next) { + if (equal(name, ap->name)) { + if (check && (ap->flag & ALIASINUSE)) + return (NULL); + return (ap); + } + } + + return (NULL); +} + +/* + * TODO - sort output + */ +aliascmd(argc, argv) + char **argv; + { + char *n, *v; + int ret = 0; + struct alias *ap; + + if (argc == 1) { + int i; + + for (i = 0; i < ATABSIZE; i++) + for (ap = atab[i]; ap; ap = ap->next) { + if (*ap->name != '\0') + out1fmt("alias %s=%s\n", ap->name, ap->val); + } + return (0); + } + while (n = *++argv) { + if ((v = strchr(n+1, '=')) == NULL) /* n+1: funny ksh stuff */ + if ((ap = lookupalias(n, 0)) == NULL) { + outfmt(out2, "alias: %s not found\n", n); + ret = 1; + } else + out1fmt("alias %s=%s\n", n, ap->val); + else { + *v++ = '\0'; + setalias(n, v); + } + } + + return (ret); +} + +unaliascmd(argc, argv) + char **argv; + { + int i; + + while ((i = nextopt("a")) != '\0') { + if (i == 'a') { + rmaliases(); + return (0); + } + } + for (i = 0; *argptr; argptr++) + i = unalias(*argptr); + + return (i); +} + +STATIC struct alias ** +hashalias(p) + register char *p; + { + unsigned int hashval; + + hashval = *p << 4; + while (*p) + hashval+= *p++; + return &atab[hashval % ATABSIZE]; +} diff --git a/bin/sh/alias.h b/bin/sh/alias.h new file mode 100644 index 0000000..adc56a9 --- /dev/null +++ b/bin/sh/alias.h @@ -0,0 +1,48 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)alias.h 8.1 (Berkeley) 5/31/93 + */ + +#define ALIASINUSE 1 + +struct alias { + struct alias *next; + char *name; + char *val; + int flag; +}; + +struct alias *lookupalias(); diff --git a/bin/sh/arith.y b/bin/sh/arith.y new file mode 100644 index 0000000..4b2e78f --- /dev/null +++ b/bin/sh/arith.y @@ -0,0 +1,169 @@ +%token ARITH_OR ARITH_AND ARITH_ADD ARITH_SUBT ARITH_MULT ARITH_DIV ARITH_REM ARITH_EQ ARITH_GT ARITH_GEQ ARITH_LT ARITH_LEQ ARITH_NEQ +%token ARITH_NUM ARITH_LPAREN ARITH_RPAREN ARITH_NOT ARITH_UNARYMINUS + +%left ARITH_OR +%left ARITH_AND +%left ARITH_EQ ARITH_NEQ +%left ARITH_LT ARITH_GT ARITH_GEQ ARITH_LEQ +%left ARITH_ADD ARITH_SUBT +%left ARITH_MULT ARITH_DIV ARITH_REM +%left ARITH_UNARYMINUS ARITH_NOT +%% + +exp: expr = { + return ($1); + } + ; + + +expr: ARITH_LPAREN expr ARITH_RPAREN = { $$ = $2; } + | expr ARITH_OR expr = { $$ = $1 ? $1 : $3 ? $3 : 0; } + | expr ARITH_AND expr = { $$ = $1 ? ( $3 ? $3 : 0 ) : 0; } + | expr ARITH_EQ expr = { $$ = $1 == $3; } + | expr ARITH_GT expr = { $$ = $1 > $3; } + | expr ARITH_GEQ expr = { $$ = $1 >= $3; } + | expr ARITH_LT expr = { $$ = $1 < $3; } + | expr ARITH_LEQ expr = { $$ = $1 <= $3; } + | expr ARITH_NEQ expr = { $$ = $1 != $3; } + | expr ARITH_ADD expr = { $$ = $1 + $3; } + | expr ARITH_SUBT expr = { $$ = $1 - $3; } + | expr ARITH_MULT expr = { $$ = $1 * $3; } + | expr ARITH_DIV expr = { + if ($3 == 0) + yyerror("division by zero"); + $$ = $1 / $3; + } + | expr ARITH_REM expr = { $$ = $1 % $3; } + | ARITH_NOT expr = { $$ = !($2); } + | ARITH_UNARYMINUS expr = { $$ = -($2); } + | ARITH_NUM + ; +%% +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)arith.y 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include "shell.h" +#include "error.h" +#include "output.h" +#include "memalloc.h" + +char *arith_buf, *arith_startbuf; + +arith(s) + char *s; +{ + extern arith_wasoper; + long result; + + arith_wasoper = 1; + arith_buf = arith_startbuf = s; + + INTOFF; + result = yyparse(); + arith_lex_reset(); /* reprime lex */ + INTON; + + return (result); +} + +yyerror(s) + char *s; +{ + extern yytext, yylval; + + yyerrok; + yyclearin; + arith_lex_reset(); /* reprime lex */ + error("arithmetic expression: %s: \"%s\"", s, arith_startbuf); +} + +/* + * The exp(1) builtin. + */ +expcmd(argc, argv) + char **argv; +{ + char *p; + char *concat; + char **ap; + long i; + + if (argc > 1) { + p = argv[1]; + if (argc > 2) { + /* + * concatenate arguments + */ + STARTSTACKSTR(concat); + ap = argv + 2; + for (;;) { + while (*p) + STPUTC(*p++, concat); + if ((p = *ap++) == NULL) + break; + STPUTC(' ', concat); + } + STPUTC('\0', concat); + p = grabstackstr(concat); + } + } else + p = ""; + + i = arith(p); + + out1fmt("%d\n", i); + return (! i); +} + +/*************************/ +#ifdef TEST_ARITH +#include <stdio.h> +main(argc, argv) + char *argv[]; +{ + printf("%d\n", exp(argv[1])); +} +error(s) + char *s; +{ + fprintf(stderr, "exp: %s\n", s); + exit(1); +} +#endif diff --git a/bin/sh/arith_lex.l b/bin/sh/arith_lex.l new file mode 100644 index 0000000..0fb1c7b --- /dev/null +++ b/bin/sh/arith_lex.l @@ -0,0 +1,86 @@ +%{ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)arith_lex.l 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include "y.tab.h" + +extern yylval; +extern char *arith_buf, *arith_startbuf; +int arith_wasoper; +#undef YY_INPUT +#define YY_INPUT(buf,result,max) \ + result = (*buf = *arith_buf++) ? 1 : YY_NULL; +%} + +%% +[ \t\n] { ; } +[0-9]+ { arith_wasoper = 0; yylval = atol(yytext); return(ARITH_NUM); } +"(" { arith_wasoper = 1; return(ARITH_LPAREN); } +")" { arith_wasoper = 0; return(ARITH_RPAREN); } +"||" { arith_wasoper = 1; return(ARITH_OR); } +"&&" { arith_wasoper = 1; return(ARITH_AND); } +"==" { arith_wasoper = 1; return(ARITH_EQ); } +">" { arith_wasoper = 1; return(ARITH_GT); } +">=" { arith_wasoper = 1; return(ARITH_GEQ); } +"<" { arith_wasoper = 1; return(ARITH_LT); } +"<=" { arith_wasoper = 1; return(ARITH_LEQ); } +"!=" { arith_wasoper = 1; return(ARITH_NEQ); } +"*" { arith_wasoper = 1; return(ARITH_MULT); } +"/" { arith_wasoper = 1; return(ARITH_DIV); } +"%" { arith_wasoper = 1; return(ARITH_REM); } +"+" { if (!arith_wasoper) { /* ignore unary plus */ + arith_wasoper = 1; + return(ARITH_ADD); + } + } +"-" { if (arith_wasoper) { + return(ARITH_UNARYMINUS); + } else { + arith_wasoper = 1; + return(ARITH_SUBT); + } + } +"!" { arith_wasoper = 1; return(ARITH_NOT); } +. { error("arith: syntax error: \"%s\"\n", arith_startbuf); } +%% + +arith_lex_reset() { + YY_NEW_FILE; +} diff --git a/bin/sh/bltin/bltin.h b/bin/sh/bltin/bltin.h new file mode 100644 index 0000000..c8bf8d5 --- /dev/null +++ b/bin/sh/bltin/bltin.h @@ -0,0 +1,74 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)bltin.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * This file is included by programs which are optionally built into the + * shell. If SHELL is defined, we try to map the standard UNIX library + * routines to ash routines using defines. + */ + +#include "../shell.h" +#include "../mystring.h" +#ifdef SHELL +#include "../output.h" +#define stdout out1 +#define stderr out2 +#define printf out1fmt +#define putc(c, file) outc(c, file) +#define putchar(c) out1c(c) +#define fprintf outfmt +#define fputs outstr +#define fflush flushout +#define INITARGS(argv) +#else +#undef NULL +#include <stdio.h> +#undef main +#define INITARGS(argv) if ((commandname = argv[0]) == NULL) {fputs("Argc is zero\n", stderr); exit(2);} else +#endif + +#ifdef __STDC__ +pointer stalloc(int); +void error(char *, ...); +#else +pointer stalloc(); +void error(); +#endif + + +extern char *commandname; diff --git a/bin/sh/bltin/echo.1 b/bin/sh/bltin/echo.1 new file mode 100644 index 0000000..d2cb8d1 --- /dev/null +++ b/bin/sh/bltin/echo.1 @@ -0,0 +1,111 @@ +.\" Copyright (c) 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Kenneth Almquist. +.\" Copyright 1989 by Kenneth Almquist +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)echo.1 8.1 (Berkeley) 5/31/93 +.\" +.Dd May 31, 1993 +.Dt ECHO 1 +.Os BSD 4.4 +.Sh NAME +.Nm echo +.Nd produce message in a shell script +.Sh SYNOPSIS +.Nm echo +.Op Fl n | Fl e +.Ar args... +.Sh DESCRIPTION +.Nm Echo +prints its arguments on the standard output, separated by spaces. +Unless the +.Fl n +option is present, a newline is output following the arguments. +The +.Fl e +option causes +.Nm echo +to treat the escape sequences specially, as described in the following +paragraph. +The +.Fl e +option is the default, and is provided solely for compatibility with +other systems. +Only one of the options +.Fl n +and +.Fl e +may be given. +.Pp +If any of the following sequences of characters is encountered during +output, the sequence is not output. Instead, the specified action is +performed: +.Bl -tag -width indent +.It Li \eb +A backspace character is output. +.It Li \ec +Subsequent output is suppressed. This is normally used at the end of the +last argument to suppress the trailing newline that +.Nm echo +would otherwise output. +.It Li \ef +Output a form feed. +.It Li \en +Output a newline character. +.It Li \er +Output a carriage return. +.It Li \et +Output a (horizontal) tab character. +.It Li \ev +Output a vertical tab. +.It Li \e0 Ns Ar digits +Output the character whose value is given by zero to three digits. +If there are zero digits, a nul character is output. +.It Li \e\e +Output a backslash. +.El +.Sh HINTS +Remember that backslash is special to the shell and needs to be escaped. +To output a message to standard error, say +.Pp +.D1 echo message >&2 +.Sh BUGS +The octal character escape mechanism +.Pq Li \e0 Ns Ar digits +differs from the +C language mechanism. +.Pp +There is no way to force +.Nm echo +to treat its arguments literally, rather than interpreting them as +options and escape sequences. diff --git a/bin/sh/bltin/echo.c b/bin/sh/bltin/echo.c new file mode 100644 index 0000000..c1d8970 --- /dev/null +++ b/bin/sh/bltin/echo.c @@ -0,0 +1,104 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)echo.c 8.1 (Berkeley) 5/31/93 + */ + +/* + * Echo command. + */ + +#define main echocmd + +#include "bltin.h" + +/* #define eflag 1 */ + +main(argc, argv) char **argv; { + register char **ap; + register char *p; + register char c; + int count; + int nflag = 0; +#ifndef eflag + int eflag = 0; +#endif + + ap = argv; + if (argc) + ap++; + if ((p = *ap) != NULL) { + if (equal(p, "-n")) { + nflag++; + ap++; + } else if (equal(p, "-e")) { +#ifndef eflag + eflag++; +#endif + ap++; + } + } + while ((p = *ap++) != NULL) { + while ((c = *p++) != '\0') { + if (c == '\\' && eflag) { + switch (*p++) { + case 'b': c = '\b'; break; + case 'c': return 0; /* exit */ + case 'f': c = '\f'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case 'v': c = '\v'; break; + case '\\': break; /* c = '\\' */ + case '0': + c = 0; + count = 3; + while (--count >= 0 && (unsigned)(*p - '0') < 8) + c = (c << 3) + (*p++ - '0'); + break; + default: + p--; + break; + } + } + putchar(c); + } + if (*ap) + putchar(' '); + } + if (! nflag) + putchar('\n'); + return 0; +} diff --git a/bin/sh/builtins b/bin/sh/builtins new file mode 100755 index 0000000..57409b1 --- /dev/null +++ b/bin/sh/builtins @@ -0,0 +1,90 @@ +#!/bin/sh - +# +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)builtins 8.1 (Berkeley) 5/31/93 + +# +# This file lists all the builtin commands. The first column is the name +# of a C routine. The -j flag, if present, specifies that this command +# is to be excluded from systems without job control. The rest of the line +# specifies the command name or names used to run the command. The entry +# for bltincmd, which is run when the user does not specify a command, must +# come first. +# +# Copyright (C) 1989 by Kenneth Almquist. All rights reserved. +# This file is part of ash, which is distributed under the terms specified +# by the Ash General Public License. See the file named LICENSE. +# +# NOTE: bltincmd must come first! + +bltincmd command +#alloccmd alloc +bgcmd -j bg +breakcmd break continue +#catfcmd catf +cdcmd cd chdir +dotcmd . +echocmd echo +evalcmd eval +execcmd exec +exitcmd exit +expcmd exp let +exportcmd export readonly +#exprcmd expr test [ +histcmd fc +fgcmd -j fg +getoptscmd getopts +hashcmd hash +jobidcmd jobid +jobscmd jobs +#linecmd line +localcmd local +#nlechocmd nlecho +printfcmd printf +pwdcmd pwd +readcmd read +returncmd return +setcmd set +setvarcmd setvar +shiftcmd shift +trapcmd trap +truecmd : true +umaskcmd umask +unaliascmd unalias +unsetcmd unset +waitcmd wait +#foocmd foo +aliascmd alias diff --git a/bin/sh/cd.c b/bin/sh/cd.c new file mode 100644 index 0000000..d1984d9 --- /dev/null +++ b/bin/sh/cd.c @@ -0,0 +1,372 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)cd.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +/* + * The cd and pwd commands. + */ + +#include "shell.h" +#include "var.h" +#include "nodes.h" /* for jobs.h */ +#include "jobs.h" +#include "options.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> + + +#ifdef __STDC__ +STATIC int docd(char *, int); +STATIC void updatepwd(char *); +STATIC void getpwd(void); +STATIC char *getcomponent(void); +#else +STATIC int docd(); +STATIC void updatepwd(); +STATIC void getpwd(); +STATIC char *getcomponent(); +#endif + + +char *curdir; /* current working directory */ +char *prevdir; /* previous working directory */ +STATIC char *cdcomppath; + +int +cdcmd(argc, argv) char **argv; { + char *dest; + char *path; + char *p; + struct stat statb; + char *padvance(); + int print = 0; + + nextopt(nullstr); + if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME", 1)) == NULL) + error("HOME not set"); + if (dest[0] == '-' && dest[1] == '\0') { + dest = prevdir ? prevdir : curdir; + print = 1; + } + if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL) + path = nullstr; + while ((p = padvance(&path, dest)) != NULL) { + if (stat(p, &statb) >= 0 + && (statb.st_mode & S_IFMT) == S_IFDIR) { + if (!print) { + /* + * XXX - rethink + */ + if (p[0] == '.' && p[1] == '/') + p += 2; + print = strcmp(p, dest); + } + if (docd(p, print) >= 0) + return 0; + + } + } + error("can't cd to %s", dest); +} + + +/* + * Actually do the chdir. If the name refers to symbolic links, we + * compute the actual directory name before doing the cd. In an + * interactive shell, print the directory name if "print" is nonzero + * or if the name refers to a symbolic link. We also print the name + * if "/u/logname" was expanded in it, since this is similar to a + * symbolic link. (The check for this breaks if the user gives the + * cd command some additional, unused arguments.) + */ + +#if SYMLINKS == 0 +STATIC int +docd(dest, print) + char *dest; + { + INTOFF; + if (chdir(dest) < 0) { + INTON; + return -1; + } + updatepwd(dest); + INTON; + if (print && iflag) + out1fmt("%s\n", stackblock()); + return 0; +} + +#else + + + +STATIC int +docd(dest, print) + char *dest; + { + register char *p; + register char *q; + char *symlink; + char *component; + struct stat statb; + int first; + int i; + + TRACE(("docd(\"%s\", %d) called\n", dest, print)); + +top: + cdcomppath = dest; + STARTSTACKSTR(p); + if (*dest == '/') { + STPUTC('/', p); + cdcomppath++; + } + first = 1; + while ((q = getcomponent()) != NULL) { + if (q[0] == '\0' || q[0] == '.' && q[1] == '\0') + continue; + if (! first) + STPUTC('/', p); + first = 0; + component = q; + while (*q) + STPUTC(*q++, p); + if (equal(component, "..")) + continue; + STACKSTRNUL(p); + if (lstat(stackblock(), &statb) < 0) + error("lstat %s failed", stackblock()); + if ((statb.st_mode & S_IFMT) != S_IFLNK) + continue; + + /* Hit a symbolic link. We have to start all over again. */ + print = 1; + STPUTC('\0', p); + symlink = grabstackstr(p); + i = (int)statb.st_size + 2; /* 2 for '/' and '\0' */ + if (cdcomppath != NULL) + i += strlen(cdcomppath); + p = stalloc(i); + if (readlink(symlink, p, (int)statb.st_size) < 0) { + error("readlink %s failed", stackblock()); + } + if (cdcomppath != NULL) { + p[(int)statb.st_size] = '/'; + scopy(cdcomppath, p + (int)statb.st_size + 1); + } else { + p[(int)statb.st_size] = '\0'; + } + if (p[0] != '/') { /* relative path name */ + char *r; + q = r = symlink; + while (*q) { + if (*q++ == '/') + r = q; + } + *r = '\0'; + dest = stalloc(strlen(symlink) + strlen(p) + 1); + scopy(symlink, dest); + strcat(dest, p); + } else { + dest = p; + } + goto top; + } + STPUTC('\0', p); + p = grabstackstr(p); + INTOFF; + if (chdir(p) < 0) { + INTON; + return -1; + } + updatepwd(p); + INTON; + if (print && iflag) + out1fmt("%s\n", p); + return 0; +} +#endif /* SYMLINKS */ + + + +/* + * Get the next component of the path name pointed to by cdcomppath. + * This routine overwrites the string pointed to by cdcomppath. + */ + +STATIC char * +getcomponent() { + register char *p; + char *start; + + if ((p = cdcomppath) == NULL) + return NULL; + start = cdcomppath; + while (*p != '/' && *p != '\0') + p++; + if (*p == '\0') { + cdcomppath = NULL; + } else { + *p++ = '\0'; + cdcomppath = p; + } + return start; +} + + + +/* + * Update curdir (the name of the current directory) in response to a + * cd command. We also call hashcd to let the routines in exec.c know + * that the current directory has changed. + */ + +void hashcd(); + +STATIC void +updatepwd(dir) + char *dir; + { + char *new; + char *p; + + hashcd(); /* update command hash table */ + cdcomppath = stalloc(strlen(dir) + 1); + scopy(dir, cdcomppath); + STARTSTACKSTR(new); + if (*dir != '/') { + if (curdir == NULL) + return; + p = curdir; + while (*p) + STPUTC(*p++, new); + if (p[-1] == '/') + STUNPUTC(new); + } + while ((p = getcomponent()) != NULL) { + if (equal(p, "..")) { + while (new > stackblock() && (STUNPUTC(new), *new) != '/'); + } else if (*p != '\0' && ! equal(p, ".")) { + STPUTC('/', new); + while (*p) + STPUTC(*p++, new); + } + } + if (new == stackblock()) + STPUTC('/', new); + STACKSTRNUL(new); + INTOFF; + if (prevdir) + ckfree(prevdir); + prevdir = curdir; + curdir = savestr(stackblock()); + INTON; +} + + + +int +pwdcmd(argc, argv) char **argv; { + getpwd(); + out1str(curdir); + out1c('\n'); + return 0; +} + + + +/* + * Run /bin/pwd to find out what the current directory is. We suppress + * interrupts throughout most of this, but the user can still break out + * of it by killing the pwd program. If we already know the current + * directory, this routine returns immediately. + */ + +#define MAXPWD 256 + +STATIC void +getpwd() { + char buf[MAXPWD]; + char *p; + int i; + int status; + struct job *jp; + int pip[2]; + + if (curdir) + return; + INTOFF; + if (pipe(pip) < 0) + error("Pipe call failed"); + jp = makejob((union node *)NULL, 1); + if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) { + close(pip[0]); + if (pip[1] != 1) { + close(1); + copyfd(pip[1], 1); + close(pip[1]); + } + execl("/bin/pwd", "pwd", (char *)0); + error("Cannot exec /bin/pwd"); + } + close(pip[1]); + pip[1] = -1; + p = buf; + while ((i = read(pip[0], p, buf + MAXPWD - p)) > 0 + || i == -1 && errno == EINTR) { + if (i > 0) + p += i; + } + close(pip[0]); + pip[0] = -1; + status = waitforjob(jp); + if (status != 0) + error((char *)0); + if (i < 0 || p == buf || p[-1] != '\n') + error("pwd command failed"); + p[-1] = '\0'; + curdir = savestr(buf); + INTON; +} diff --git a/bin/sh/errmsg.c b/bin/sh/errmsg.c new file mode 100644 index 0000000..2a0303b --- /dev/null +++ b/bin/sh/errmsg.c @@ -0,0 +1,127 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)errmsg.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include "shell.h" +#include "output.h" +#include "errmsg.h" +#include <errno.h> + + +#define ALL (E_OPEN|E_CREAT|E_EXEC) + + +struct errname { + short errcode; /* error number */ + short action; /* operation which encountered the error */ + char *msg; /* text describing the error */ +}; + + +STATIC const struct errname errormsg[] = { + EINTR, ALL, "interrupted", + EACCES, ALL, "permission denied", + EIO, ALL, "I/O error", + ENOENT, E_OPEN, "no such file", + ENOENT, E_CREAT, "directory nonexistent", + ENOENT, E_EXEC, "not found", + ENOTDIR, E_OPEN, "no such file", + ENOTDIR, E_CREAT, "directory nonexistent", + ENOTDIR, E_EXEC, "not found", + EISDIR, ALL, "is a directory", +/* EMFILE, ALL, "too many open files", */ + ENFILE, ALL, "file table overflow", + ENOSPC, ALL, "file system full", +#ifdef EDQUOT + EDQUOT, ALL, "disk quota exceeded", +#endif +#ifdef ENOSR + ENOSR, ALL, "no streams resources", +#endif + ENXIO, ALL, "no such device or address", + EROFS, ALL, "read-only file system", + ETXTBSY, ALL, "text busy", +#ifdef SYSV + EAGAIN, E_EXEC, "not enough memory", +#endif + ENOMEM, ALL, "not enough memory", +#ifdef ENOLINK + ENOLINK, ALL, "remote access failed" +#endif +#ifdef EMULTIHOP + EMULTIHOP, ALL, "remote access failed", +#endif +#ifdef ECOMM + ECOMM, ALL, "remote access failed", +#endif +#ifdef ESTALE + ESTALE, ALL, "remote access failed", +#endif +#ifdef ETIMEDOUT + ETIMEDOUT, ALL, "remote access failed", +#endif +#ifdef ELOOP + ELOOP, ALL, "symbolic link loop", +#endif + E2BIG, E_EXEC, "argument list too long", +#ifdef ELIBACC + ELIBACC, E_EXEC, "shared library missing", +#endif + 0, 0, NULL +}; + + +/* + * Return a string describing an error. The returned string may be a + * pointer to a static buffer that will be overwritten on the next call. + * Action describes the operation that got the error. + */ + +char * +errmsg(e, action) { + struct errname const *ep; + static char buf[12]; + + for (ep = errormsg ; ep->errcode ; ep++) { + if (ep->errcode == e && (ep->action & action) != 0) + return ep->msg; + } + fmtstr(buf, sizeof buf, "error %d", e); + return buf; +} diff --git a/bin/sh/errmsg.h b/bin/sh/errmsg.h new file mode 100644 index 0000000..03cb605 --- /dev/null +++ b/bin/sh/errmsg.h @@ -0,0 +1,47 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)errmsg.h 8.1 (Berkeley) 5/31/93 + */ + +#define E_OPEN 01 +#define E_CREAT 02 +#define E_EXEC 04 + +#ifdef __STDC__ +char *errmsg(int, int); +#else +char *errmsg(); +#endif diff --git a/bin/sh/error.c b/bin/sh/error.c new file mode 100644 index 0000000..b6853b6 --- /dev/null +++ b/bin/sh/error.c @@ -0,0 +1,249 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)error.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +/* + * Errors and exceptions. + */ + +#include "shell.h" +#include "main.h" +#include "options.h" +#include "output.h" +#include "error.h" +#include <signal.h> +#ifdef __STDC__ +#include "stdarg.h" +#else +#include <varargs.h> +#endif +#include <errno.h> + + +/* + * Code to handle exceptions in C. + */ + +struct jmploc *handler; +int exception; +volatile int suppressint; +volatile int intpending; +char *commandname; + + +/* + * Called to raise an exception. Since C doesn't include exceptions, we + * just do a longjmp to the exception handler. The type of exception is + * stored in the global variable "exception". + */ + +void +exraise(e) { + if (handler == NULL) + abort(); + exception = e; + longjmp(handler->loc, 1); +} + + +/* + * Called from trap.c when a SIGINT is received. (If the user specifies + * that SIGINT is to be trapped or ignored using the trap builtin, then + * this routine is not called.) Suppressint is nonzero when interrupts + * are held using the INTOFF macro. The call to _exit is necessary because + * there is a short period after a fork before the signal handlers are + * set to the appropriate value for the child. (The test for iflag is + * just defensive programming.) + */ + +void +onint() { + if (suppressint) { + intpending++; + return; + } + intpending = 0; +#ifdef BSD + sigsetmask(0); +#endif + if (rootshell && iflag) + exraise(EXINT); + else + _exit(128 + SIGINT); +} + + + +void +error2(a, b) + char *a, *b; + { + error("%s: %s", a, b); +} + + +/* + * Error is called to raise the error exception. If the first argument + * is not NULL then error prints an error message using printf style + * formatting. It then raises the error exception. + */ + +#ifdef __STDC__ +void +error(char *msg, ...) { +#else +void +error(va_alist) + va_dcl + { + char *msg; +#endif + va_list ap; + + CLEAR_PENDING_INT; + INTOFF; +#ifdef __STDC__ + va_start(ap, msg); +#else + va_start(ap); + msg = va_arg(ap, char *); +#endif +#ifdef DEBUG + if (msg) + TRACE(("error(\"%s\") pid=%d\n", msg, getpid())); + else + TRACE(("error(NULL) pid=%d\n", getpid())); +#endif + if (msg) { + if (commandname) + outfmt(&errout, "%s: ", commandname); + doformat(&errout, msg, ap); + out2c('\n'); + } + va_end(ap); + flushall(); + exraise(EXERROR); +} + + + +/* + * Table of error messages. + */ + +struct errname { + short errcode; /* error number */ + short action; /* operation which encountered the error */ + char *msg; /* text describing the error */ +}; + + +#define ALL (E_OPEN|E_CREAT|E_EXEC) + +STATIC const struct errname errormsg[] = { + EINTR, ALL, "interrupted", + EACCES, ALL, "permission denied", + EIO, ALL, "I/O error", + ENOENT, E_OPEN, "no such file", + ENOENT, E_CREAT, "directory nonexistent", + ENOENT, E_EXEC, "not found", + ENOTDIR, E_OPEN, "no such file", + ENOTDIR, E_CREAT, "directory nonexistent", + ENOTDIR, E_EXEC, "not found", + EISDIR, ALL, "is a directory", +/* EMFILE, ALL, "too many open files", */ + ENFILE, ALL, "file table overflow", + ENOSPC, ALL, "file system full", +#ifdef EDQUOT + EDQUOT, ALL, "disk quota exceeded", +#endif +#ifdef ENOSR + ENOSR, ALL, "no streams resources", +#endif + ENXIO, ALL, "no such device or address", + EROFS, ALL, "read-only file system", + ETXTBSY, ALL, "text busy", +#ifdef SYSV + EAGAIN, E_EXEC, "not enough memory", +#endif + ENOMEM, ALL, "not enough memory", +#ifdef ENOLINK + ENOLINK, ALL, "remote access failed", +#endif +#ifdef EMULTIHOP + EMULTIHOP, ALL, "remote access failed", +#endif +#ifdef ECOMM + ECOMM, ALL, "remote access failed", +#endif +#ifdef ESTALE + ESTALE, ALL, "remote access failed", +#endif +#ifdef ETIMEDOUT + ETIMEDOUT, ALL, "remote access failed", +#endif +#ifdef ELOOP + ELOOP, ALL, "symbolic link loop", +#endif + E2BIG, E_EXEC, "argument list too long", +#ifdef ELIBACC + ELIBACC, E_EXEC, "shared library missing", +#endif + 0, 0, NULL +}; + + +/* + * Return a string describing an error. The returned string may be a + * pointer to a static buffer that will be overwritten on the next call. + * Action describes the operation that got the error. + */ + +char * +errmsg(e, action) { + struct errname const *ep; + static char buf[12]; + + for (ep = errormsg ; ep->errcode ; ep++) { + if (ep->errcode == e && (ep->action & action) != 0) + return ep->msg; + } + fmtstr(buf, sizeof buf, "error %d", e); + return buf; +} diff --git a/bin/sh/error.h b/bin/sh/error.h new file mode 100644 index 0000000..8f87caf --- /dev/null +++ b/bin/sh/error.h @@ -0,0 +1,113 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)error.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * Types of operations (passed to the errmsg routine). + */ + +#define E_OPEN 01 /* opening a file */ +#define E_CREAT 02 /* creating a file */ +#define E_EXEC 04 /* executing a program */ + + +/* + * We enclose jmp_buf in a structure so that we can declare pointers to + * jump locations. The global variable handler contains the location to + * jump to when an exception occurs, and the global variable exception + * contains a code identifying the exeception. To implement nested + * exception handlers, the user should save the value of handler on entry + * to an inner scope, set handler to point to a jmploc structure for the + * inner scope, and restore handler on exit from the scope. + */ + +#include <setjmp.h> + +struct jmploc { + jmp_buf loc; +}; + +extern struct jmploc *handler; +extern int exception; + +/* exceptions */ +#define EXINT 0 /* SIGINT received */ +#define EXERROR 1 /* a generic error */ +#define EXSHELLPROC 2 /* execute a shell procedure */ + + +/* + * These macros allow the user to suspend the handling of interrupt signals + * over a period of time. This is similar to SIGHOLD to or sigblock, but + * much more efficient and portable. (But hacking the kernel is so much + * more fun than worrying about efficiency and portability. :-)) + */ + +extern volatile int suppressint; +extern volatile int intpending; +extern char *commandname; /* name of command--printed on error */ + +#define INTOFF suppressint++ +#define INTON if (--suppressint == 0 && intpending) onint(); else +#define FORCEINTON {suppressint = 0; if (intpending) onint();} +#define CLEAR_PENDING_INT intpending = 0 +#define int_pending() intpending + +#ifdef __STDC__ +void exraise(int); +void onint(void); +void error2(char *, char *); +void error(char *, ...); +char *errmsg(int, int); +#else +void exraise(); +void onint(); +void error2(); +void error(); +char *errmsg(); +#endif + + +/* + * BSD setjmp saves the signal mask, which violates ANSI C and takes time, + * so we use _setjmp instead. + */ + +#ifdef BSD +#define setjmp(jmploc) _setjmp(jmploc) +#define longjmp(jmploc, val) _longjmp(jmploc, val) +#endif diff --git a/bin/sh/eval.c b/bin/sh/eval.c new file mode 100644 index 0000000..48a9f99 --- /dev/null +++ b/bin/sh/eval.c @@ -0,0 +1,933 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)eval.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +/* + * Evaluate a command. + */ + +#include "shell.h" +#include "nodes.h" +#include "syntax.h" +#include "expand.h" +#include "parser.h" +#include "jobs.h" +#include "eval.h" +#include "builtins.h" +#include "options.h" +#include "exec.h" +#include "redir.h" +#include "input.h" +#include "output.h" +#include "trap.h" +#include "var.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" +#include "myhistedit.h" +#include <signal.h> + + +/* flags in argument to evaltree */ +#define EV_EXIT 01 /* exit after evaluating tree */ +#define EV_TESTED 02 /* exit status is checked; ignore -e flag */ +#define EV_BACKCMD 04 /* command executing within back quotes */ + + +/* reasons for skipping commands (see comment on breakcmd routine) */ +#define SKIPBREAK 1 +#define SKIPCONT 2 +#define SKIPFUNC 3 + +MKINIT int evalskip; /* set if we are skipping commands */ +STATIC int skipcount; /* number of levels to skip */ +MKINIT int loopnest; /* current loop nesting level */ +int funcnest; /* depth of function calls */ + + +char *commandname; +struct strlist *cmdenviron; +int exitstatus; /* exit status of last command */ + + +#ifdef __STDC__ +STATIC void evalloop(union node *); +STATIC void evalfor(union node *); +STATIC void evalcase(union node *, int); +STATIC void evalsubshell(union node *, int); +STATIC void expredir(union node *); +STATIC void evalpipe(union node *); +STATIC void evalcommand(union node *, int, struct backcmd *); +STATIC void prehash(union node *); +#else +STATIC void evalloop(); +STATIC void evalfor(); +STATIC void evalcase(); +STATIC void evalsubshell(); +STATIC void expredir(); +STATIC void evalpipe(); +STATIC void evalcommand(); +STATIC void prehash(); +#endif + + + +/* + * Called to reset things after an exception. + */ + +#ifdef mkinit +INCLUDE "eval.h" + +RESET { + evalskip = 0; + loopnest = 0; + funcnest = 0; +} + +SHELLPROC { + exitstatus = 0; +} +#endif + + + +/* + * The eval commmand. + */ + +evalcmd(argc, argv) + char **argv; +{ + char *p; + char *concat; + char **ap; + + if (argc > 1) { + p = argv[1]; + if (argc > 2) { + STARTSTACKSTR(concat); + ap = argv + 2; + for (;;) { + while (*p) + STPUTC(*p++, concat); + if ((p = *ap++) == NULL) + break; + STPUTC(' ', concat); + } + STPUTC('\0', concat); + p = grabstackstr(concat); + } + evalstring(p); + } + return exitstatus; +} + + +/* + * Execute a command or commands contained in a string. + */ + +void +evalstring(s) + char *s; + { + union node *n; + struct stackmark smark; + + setstackmark(&smark); + setinputstring(s, 1); + while ((n = parsecmd(0)) != NEOF) { + evaltree(n, 0); + popstackmark(&smark); + } + popfile(); + popstackmark(&smark); +} + + + +/* + * Evaluate a parse tree. The value is left in the global variable + * exitstatus. + */ + +void +evaltree(n, flags) + union node *n; + { + if (n == NULL) { + TRACE(("evaltree(NULL) called\n")); + exitstatus = 0; + goto out; + } + displayhist = 1; /* show history substitutions done with fc */ + TRACE(("evaltree(0x%x: %d) called\n", (int)n, n->type)); + switch (n->type) { + case NSEMI: + evaltree(n->nbinary.ch1, 0); + if (evalskip) + goto out; + evaltree(n->nbinary.ch2, flags); + break; + case NAND: + evaltree(n->nbinary.ch1, EV_TESTED); + if (evalskip || exitstatus != 0) + goto out; + evaltree(n->nbinary.ch2, flags); + break; + case NOR: + evaltree(n->nbinary.ch1, EV_TESTED); + if (evalskip || exitstatus == 0) + goto out; + evaltree(n->nbinary.ch2, flags); + break; + case NREDIR: + expredir(n->nredir.redirect); + redirect(n->nredir.redirect, REDIR_PUSH); + evaltree(n->nredir.n, flags); + popredir(); + break; + case NSUBSHELL: + evalsubshell(n, flags); + break; + case NBACKGND: + evalsubshell(n, flags); + break; + case NIF: { + int status = 0; + + evaltree(n->nif.test, EV_TESTED); + if (evalskip) + goto out; + if (exitstatus == 0) { + evaltree(n->nif.ifpart, flags); + status = exitstatus; + } else if (n->nif.elsepart) { + evaltree(n->nif.elsepart, flags); + status = exitstatus; + } + exitstatus = status; + break; + } + case NWHILE: + case NUNTIL: + evalloop(n); + break; + case NFOR: + evalfor(n); + break; + case NCASE: + evalcase(n, flags); + break; + case NDEFUN: + defun(n->narg.text, n->narg.next); + exitstatus = 0; + break; + case NNOT: + evaltree(n->nnot.com, EV_TESTED); + exitstatus = !exitstatus; + break; + + case NPIPE: + evalpipe(n); + break; + case NCMD: + evalcommand(n, flags, (struct backcmd *)NULL); + break; + default: + out1fmt("Node type = %d\n", n->type); + flushout(&output); + break; + } +out: + if (pendingsigs) + dotrap(); + if ((flags & EV_EXIT) || (eflag && exitstatus && !(flags & EV_TESTED))) + exitshell(exitstatus); +} + + +STATIC void +evalloop(n) + union node *n; + { + int status; + + loopnest++; + status = 0; + for (;;) { + evaltree(n->nbinary.ch1, EV_TESTED); + if (evalskip) { +skipping: if (evalskip == SKIPCONT && --skipcount <= 0) { + evalskip = 0; + continue; + } + if (evalskip == SKIPBREAK && --skipcount <= 0) + evalskip = 0; + break; + } + if (n->type == NWHILE) { + if (exitstatus != 0) + break; + } else { + if (exitstatus == 0) + break; + } + evaltree(n->nbinary.ch2, 0); + status = exitstatus; + if (evalskip) + goto skipping; + } + loopnest--; + exitstatus = status; +} + + + +STATIC void +evalfor(n) + union node *n; + { + struct arglist arglist; + union node *argp; + struct strlist *sp; + struct stackmark smark; + + setstackmark(&smark); + arglist.lastp = &arglist.list; + for (argp = n->nfor.args ; argp ; argp = argp->narg.next) { + expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); + if (evalskip) + goto out; + } + *arglist.lastp = NULL; + + exitstatus = 0; + loopnest++; + for (sp = arglist.list ; sp ; sp = sp->next) { + setvar(n->nfor.var, sp->text, 0); + evaltree(n->nfor.body, 0); + if (evalskip) { + if (evalskip == SKIPCONT && --skipcount <= 0) { + evalskip = 0; + continue; + } + if (evalskip == SKIPBREAK && --skipcount <= 0) + evalskip = 0; + break; + } + } + loopnest--; +out: + popstackmark(&smark); +} + + + +STATIC void +evalcase(n, flags) + union node *n; + { + union node *cp; + union node *patp; + struct arglist arglist; + struct stackmark smark; + + setstackmark(&smark); + arglist.lastp = &arglist.list; + expandarg(n->ncase.expr, &arglist, EXP_TILDE); + for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) { + for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) { + if (casematch(patp, arglist.list->text)) { + if (evalskip == 0) { + evaltree(cp->nclist.body, flags); + } + goto out; + } + } + } +out: + popstackmark(&smark); +} + + + +/* + * Kick off a subshell to evaluate a tree. + */ + +STATIC void +evalsubshell(n, flags) + union node *n; + { + struct job *jp; + int backgnd = (n->type == NBACKGND); + + expredir(n->nredir.redirect); + jp = makejob(n, 1); + if (forkshell(jp, n, backgnd) == 0) { + if (backgnd) + flags &=~ EV_TESTED; + redirect(n->nredir.redirect, 0); + evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */ + } + if (! backgnd) { + INTOFF; + exitstatus = waitforjob(jp); + INTON; + } +} + + + +/* + * Compute the names of the files in a redirection list. + */ + +STATIC void +expredir(n) + union node *n; + { + register union node *redir; + + for (redir = n ; redir ; redir = redir->nfile.next) { + if (redir->type == NFROM + || redir->type == NTO + || redir->type == NAPPEND) { + struct arglist fn; + fn.lastp = &fn.list; + expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR); + redir->nfile.expfname = fn.list->text; + } + } +} + + + +/* + * Evaluate a pipeline. All the processes in the pipeline are children + * of the process creating the pipeline. (This differs from some versions + * of the shell, which make the last process in a pipeline the parent + * of all the rest.) + */ + +STATIC void +evalpipe(n) + union node *n; + { + struct job *jp; + struct nodelist *lp; + int pipelen; + int prevfd; + int pip[2]; + + TRACE(("evalpipe(0x%x) called\n", (int)n)); + pipelen = 0; + for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) + pipelen++; + INTOFF; + jp = makejob(n, pipelen); + prevfd = -1; + for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { + prehash(lp->n); + pip[1] = -1; + if (lp->next) { + if (pipe(pip) < 0) { + close(prevfd); + error("Pipe call failed"); + } + } + if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) { + INTON; + if (prevfd > 0) { + close(0); + copyfd(prevfd, 0); + close(prevfd); + } + if (pip[1] >= 0) { + close(pip[0]); + if (pip[1] != 1) { + close(1); + copyfd(pip[1], 1); + close(pip[1]); + } + } + evaltree(lp->n, EV_EXIT); + } + if (prevfd >= 0) + close(prevfd); + prevfd = pip[0]; + close(pip[1]); + } + INTON; + if (n->npipe.backgnd == 0) { + INTOFF; + exitstatus = waitforjob(jp); + TRACE(("evalpipe: job done exit status %d\n", exitstatus)); + INTON; + } +} + + + +/* + * Execute a command inside back quotes. If it's a builtin command, we + * want to save its output in a block obtained from malloc. Otherwise + * we fork off a subprocess and get the output of the command via a pipe. + * Should be called with interrupts off. + */ + +void +evalbackcmd(n, result) + union node *n; + struct backcmd *result; + { + int pip[2]; + struct job *jp; + struct stackmark smark; /* unnecessary */ + + setstackmark(&smark); + result->fd = -1; + result->buf = NULL; + result->nleft = 0; + result->jp = NULL; + exitstatus = 0; + if (n == NULL) + goto out; + if (n->type == NCMD) { + evalcommand(n, EV_BACKCMD, result); + } else { + if (pipe(pip) < 0) + error("Pipe call failed"); + jp = makejob(n, 1); + if (forkshell(jp, n, FORK_NOJOB) == 0) { + FORCEINTON; + close(pip[0]); + if (pip[1] != 1) { + close(1); + copyfd(pip[1], 1); + close(pip[1]); + } + evaltree(n, EV_EXIT); + } + close(pip[1]); + result->fd = pip[0]; + result->jp = jp; + } +out: + popstackmark(&smark); + TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n", + result->fd, result->buf, result->nleft, result->jp)); +} + + + +/* + * Execute a simple command. + */ + +STATIC void +evalcommand(cmd, flags, backcmd) + union node *cmd; + struct backcmd *backcmd; + { + struct stackmark smark; + union node *argp; + struct arglist arglist; + struct arglist varlist; + char **argv; + int argc; + char **envp; + int varflag; + struct strlist *sp; + register char *p; + int mode; + int pip[2]; + struct cmdentry cmdentry; + struct job *jp; + struct jmploc jmploc; + struct jmploc *volatile savehandler; + char *volatile savecmdname; + volatile struct shparam saveparam; + struct localvar *volatile savelocalvars; + volatile int e; + char *lastarg; + + /* First expand the arguments. */ + TRACE(("evalcommand(0x%x, %d) called\n", (int)cmd, flags)); + setstackmark(&smark); + arglist.lastp = &arglist.list; + varlist.lastp = &varlist.list; + varflag = 1; + for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { + p = argp->narg.text; + if (varflag && is_name(*p)) { + do { + p++; + } while (is_in_name(*p)); + if (*p == '=') { + expandarg(argp, &varlist, EXP_VARTILDE); + continue; + } + } + expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); + varflag = 0; + } + *arglist.lastp = NULL; + *varlist.lastp = NULL; + expredir(cmd->ncmd.redirect); + argc = 0; + for (sp = arglist.list ; sp ; sp = sp->next) + argc++; + argv = stalloc(sizeof (char *) * (argc + 1)); + + for (sp = arglist.list ; sp ; sp = sp->next) { + TRACE(("evalcommand arg: %s\n", sp->text)); + *argv++ = sp->text; + } + *argv = NULL; + lastarg = NULL; + if (iflag && funcnest == 0 && argc > 0) + lastarg = argv[-1]; + argv -= argc; + + /* Print the command if xflag is set. */ + if (xflag) { + outc('+', &errout); + for (sp = varlist.list ; sp ; sp = sp->next) { + outc(' ', &errout); + out2str(sp->text); + } + for (sp = arglist.list ; sp ; sp = sp->next) { + outc(' ', &errout); + out2str(sp->text); + } + outc('\n', &errout); + flushout(&errout); + } + + /* Now locate the command. */ + if (argc == 0) { + cmdentry.cmdtype = CMDBUILTIN; + cmdentry.u.index = BLTINCMD; + } else { + find_command(argv[0], &cmdentry, 1); + if (cmdentry.cmdtype == CMDUNKNOWN) { /* command not found */ + exitstatus = 2; + flushout(&errout); + return; + } + /* implement the bltin builtin here */ + if (cmdentry.cmdtype == CMDBUILTIN && cmdentry.u.index == BLTINCMD) { + for (;;) { + argv++; + if (--argc == 0) + break; + if ((cmdentry.u.index = find_builtin(*argv)) < 0) { + outfmt(&errout, "%s: not found\n", *argv); + exitstatus = 2; + flushout(&errout); + return; + } + if (cmdentry.u.index != BLTINCMD) + break; + } + } + } + + /* Fork off a child process if necessary. */ + if (cmd->ncmd.backgnd + || cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0 + || (flags & EV_BACKCMD) != 0 + && (cmdentry.cmdtype != CMDBUILTIN + || cmdentry.u.index == DOTCMD + || cmdentry.u.index == EVALCMD)) { + jp = makejob(cmd, 1); + mode = cmd->ncmd.backgnd; + if (flags & EV_BACKCMD) { + mode = FORK_NOJOB; + if (pipe(pip) < 0) + error("Pipe call failed"); + } + if (forkshell(jp, cmd, mode) != 0) + goto parent; /* at end of routine */ + if (flags & EV_BACKCMD) { + FORCEINTON; + close(pip[0]); + if (pip[1] != 1) { + close(1); + copyfd(pip[1], 1); + close(pip[1]); + } + } + flags |= EV_EXIT; + } + + /* This is the child process if a fork occurred. */ + /* Execute the command. */ + if (cmdentry.cmdtype == CMDFUNCTION) { + trputs("Shell function: "); trargs(argv); + redirect(cmd->ncmd.redirect, REDIR_PUSH); + saveparam = shellparam; + shellparam.malloc = 0; + shellparam.nparam = argc - 1; + shellparam.p = argv + 1; + shellparam.optnext = NULL; + INTOFF; + savelocalvars = localvars; + localvars = NULL; + INTON; + if (setjmp(jmploc.loc)) { + if (exception == EXSHELLPROC) + freeparam((struct shparam *)&saveparam); + else { + freeparam(&shellparam); + shellparam = saveparam; + } + poplocalvars(); + localvars = savelocalvars; + handler = savehandler; + longjmp(handler->loc, 1); + } + savehandler = handler; + handler = &jmploc; + for (sp = varlist.list ; sp ; sp = sp->next) + mklocal(sp->text); + funcnest++; + evaltree(cmdentry.u.func, 0); + funcnest--; + INTOFF; + poplocalvars(); + localvars = savelocalvars; + freeparam(&shellparam); + shellparam = saveparam; + handler = savehandler; + popredir(); + INTON; + if (evalskip == SKIPFUNC) { + evalskip = 0; + skipcount = 0; + } + if (flags & EV_EXIT) + exitshell(exitstatus); + } else if (cmdentry.cmdtype == CMDBUILTIN) { + trputs("builtin command: "); trargs(argv); + mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH; + if (flags == EV_BACKCMD) { + memout.nleft = 0; + memout.nextc = memout.buf; + memout.bufsize = 64; + mode |= REDIR_BACKQ; + } + redirect(cmd->ncmd.redirect, mode); + savecmdname = commandname; + cmdenviron = varlist.list; + e = -1; + if (setjmp(jmploc.loc)) { + e = exception; + exitstatus = (e == EXINT)? SIGINT+128 : 2; + goto cmddone; + } + savehandler = handler; + handler = &jmploc; + commandname = argv[0]; + argptr = argv + 1; + optptr = NULL; /* initialize nextopt */ + exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv); + flushall(); +cmddone: + out1 = &output; + out2 = &errout; + freestdout(); + if (e != EXSHELLPROC) { + commandname = savecmdname; + if (flags & EV_EXIT) { + exitshell(exitstatus); + } + } + handler = savehandler; + if (e != -1) { + if (e != EXERROR || cmdentry.u.index == BLTINCMD + || cmdentry.u.index == DOTCMD + || cmdentry.u.index == EVALCMD + || cmdentry.u.index == HISTCMD + || cmdentry.u.index == EXECCMD) + exraise(e); + FORCEINTON; + } + if (cmdentry.u.index != EXECCMD) + popredir(); + if (flags == EV_BACKCMD) { + backcmd->buf = memout.buf; + backcmd->nleft = memout.nextc - memout.buf; + memout.buf = NULL; + } + } else { + trputs("normal command: "); trargs(argv); + clearredir(); + redirect(cmd->ncmd.redirect, 0); + if (varlist.list) { + p = stalloc(strlen(pathval()) + 1); + scopy(pathval(), p); + } else { + p = pathval(); + } + for (sp = varlist.list ; sp ; sp = sp->next) + setvareq(sp->text, VEXPORT|VSTACK); + envp = environment(); + shellexec(argv, envp, p, cmdentry.u.index); + /*NOTREACHED*/ + } + goto out; + +parent: /* parent process gets here (if we forked) */ + if (mode == 0) { /* argument to fork */ + INTOFF; + exitstatus = waitforjob(jp); + INTON; + } else if (mode == 2) { + backcmd->fd = pip[0]; + close(pip[1]); + backcmd->jp = jp; + } + +out: + if (lastarg) + setvar("_", lastarg, 0); + popstackmark(&smark); +} + + + +/* + * Search for a command. This is called before we fork so that the + * location of the command will be available in the parent as well as + * the child. The check for "goodname" is an overly conservative + * check that the name will not be subject to expansion. + */ + +STATIC void +prehash(n) + union node *n; + { + struct cmdentry entry; + + if (n->type == NCMD && goodname(n->ncmd.args->narg.text)) + find_command(n->ncmd.args->narg.text, &entry, 0); +} + + + +/* + * Builtin commands. Builtin commands whose functions are closely + * tied to evaluation are implemented here. + */ + +/* + * No command given, or a bltin command with no arguments. Set the + * specified variables. + */ + +bltincmd(argc, argv) char **argv; { + listsetvar(cmdenviron); + return exitstatus; +} + + +/* + * Handle break and continue commands. Break, continue, and return are + * all handled by setting the evalskip flag. The evaluation routines + * above all check this flag, and if it is set they start skipping + * commands rather than executing them. The variable skipcount is + * the number of loops to break/continue, or the number of function + * levels to return. (The latter is always 1.) It should probably + * be an error to break out of more loops than exist, but it isn't + * in the standard shell so we don't make it one here. + */ + +breakcmd(argc, argv) char **argv; { + int n; + + n = 1; + if (argc > 1) + n = number(argv[1]); + if (n > loopnest) + n = loopnest; + if (n > 0) { + evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK; + skipcount = n; + } + return 0; +} + + +/* + * The return command. + */ + +returncmd(argc, argv) char **argv; { + int ret; + + ret = exitstatus; + if (argc > 1) + ret = number(argv[1]); + if (funcnest) { + evalskip = SKIPFUNC; + skipcount = 1; + } + return ret; +} + + +truecmd(argc, argv) char **argv; { + return 0; +} + + +execcmd(argc, argv) char **argv; { + if (argc > 1) { + iflag = 0; /* exit on error */ + mflag = 0; + optschanged(); + shellexec(argv + 1, environment(), pathval(), 0); + + } + return 0; +} diff --git a/bin/sh/eval.h b/bin/sh/eval.h new file mode 100644 index 0000000..5789bcb --- /dev/null +++ b/bin/sh/eval.h @@ -0,0 +1,65 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)eval.h 8.1 (Berkeley) 5/31/93 + */ + +extern char *commandname; /* currently executing command */ +extern int exitstatus; /* exit status of last command */ +extern struct strlist *cmdenviron; /* environment for builtin command */ + + +struct backcmd { /* result of evalbackcmd */ + int fd; /* file descriptor to read from */ + char *buf; /* buffer */ + int nleft; /* number of chars in buffer */ + struct job *jp; /* job structure for command */ +}; + + +#ifdef __STDC__ +void evalstring(char *); +union node; /* BLETCH for ansi C */ +void evaltree(union node *, int); +void evalbackcmd(union node *, struct backcmd *); +#else +void evalstring(); +void evaltree(); +void evalbackcmd(); +#endif + +/* in_function returns nonzero if we are currently evaluating a function */ +#define in_function() funcnest +extern int funcnest; diff --git a/bin/sh/exec.c b/bin/sh/exec.c new file mode 100644 index 0000000..30c7f73 --- /dev/null +++ b/bin/sh/exec.c @@ -0,0 +1,828 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)exec.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +/* + * When commands are first encountered, they are entered in a hash table. + * This ensures that a full path search will not have to be done for them + * on each invocation. + * + * We should investigate converting to a linear search, even though that + * would make the command name "hash" a misnomer. + */ + +#include "shell.h" +#include "main.h" +#include "nodes.h" +#include "parser.h" +#include "redir.h" +#include "eval.h" +#include "exec.h" +#include "builtins.h" +#include "var.h" +#include "options.h" +#include "input.h" +#include "output.h" +#include "syntax.h" +#include "memalloc.h" +#include "error.h" +#include "init.h" +#include "mystring.h" +#include "jobs.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> + + +#define CMDTABLESIZE 31 /* should be prime */ +#define ARB 1 /* actual size determined at run time */ + + + +struct tblentry { + struct tblentry *next; /* next entry in hash chain */ + union param param; /* definition of builtin function */ + short cmdtype; /* index identifying command */ + char rehash; /* if set, cd done since entry created */ + char cmdname[ARB]; /* name of command */ +}; + + +STATIC struct tblentry *cmdtable[CMDTABLESIZE]; +STATIC int builtinloc = -1; /* index in path of %builtin, or -1 */ + + +#ifdef __STDC__ +STATIC void tryexec(char *, char **, char **); +STATIC void execinterp(char **, char **); +STATIC void printentry(struct tblentry *, int); +STATIC void clearcmdentry(int); +STATIC struct tblentry *cmdlookup(char *, int); +STATIC void delete_cmd_entry(void); +#else +STATIC void tryexec(); +STATIC void execinterp(); +STATIC void printentry(); +STATIC void clearcmdentry(); +STATIC struct tblentry *cmdlookup(); +STATIC void delete_cmd_entry(); +#endif + + + +/* + * Exec a program. Never returns. If you change this routine, you may + * have to change the find_command routine as well. + */ + +void +shellexec(argv, envp, path, index) + char **argv, **envp; + char *path; + { + char *cmdname; + int e; + + if (strchr(argv[0], '/') != NULL) { + tryexec(argv[0], argv, envp); + e = errno; + } else { + e = ENOENT; + while ((cmdname = padvance(&path, argv[0])) != NULL) { + if (--index < 0 && pathopt == NULL) { + tryexec(cmdname, argv, envp); + if (errno != ENOENT && errno != ENOTDIR) + e = errno; + } + stunalloc(cmdname); + } + } + error2(argv[0], errmsg(e, E_EXEC)); +} + + +STATIC void +tryexec(cmd, argv, envp) + char *cmd; + char **argv; + char **envp; + { + int e; + char *p; + +#ifdef SYSV + do { + execve(cmd, argv, envp); + } while (errno == EINTR); +#else + execve(cmd, argv, envp); +#endif + e = errno; + if (e == ENOEXEC) { + initshellproc(); + setinputfile(cmd, 0); + commandname = arg0 = savestr(argv[0]); +#ifndef BSD + pgetc(); pungetc(); /* fill up input buffer */ + p = parsenextc; + if (parsenleft > 2 && p[0] == '#' && p[1] == '!') { + argv[0] = cmd; + execinterp(argv, envp); + } +#endif + setparam(argv + 1); + exraise(EXSHELLPROC); + /*NOTREACHED*/ + } + errno = e; +} + + +#ifndef BSD +/* + * Execute an interpreter introduced by "#!", for systems where this + * feature has not been built into the kernel. If the interpreter is + * the shell, return (effectively ignoring the "#!"). If the execution + * of the interpreter fails, exit. + * + * This code peeks inside the input buffer in order to avoid actually + * reading any input. It would benefit from a rewrite. + */ + +#define NEWARGS 5 + +STATIC void +execinterp(argv, envp) + char **argv, **envp; + { + int n; + char *inp; + char *outp; + char c; + char *p; + char **ap; + char *newargs[NEWARGS]; + int i; + char **ap2; + char **new; + + n = parsenleft - 2; + inp = parsenextc + 2; + ap = newargs; + for (;;) { + while (--n >= 0 && (*inp == ' ' || *inp == '\t')) + inp++; + if (n < 0) + goto bad; + if ((c = *inp++) == '\n') + break; + if (ap == &newargs[NEWARGS]) +bad: error("Bad #! line"); + STARTSTACKSTR(outp); + do { + STPUTC(c, outp); + } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n'); + STPUTC('\0', outp); + n++, inp--; + *ap++ = grabstackstr(outp); + } + if (ap == newargs + 1) { /* if no args, maybe no exec is needed */ + p = newargs[0]; + for (;;) { + if (equal(p, "sh") || equal(p, "ash")) { + return; + } + while (*p != '/') { + if (*p == '\0') + goto break2; + p++; + } + p++; + } +break2:; + } + i = (char *)ap - (char *)newargs; /* size in bytes */ + if (i == 0) + error("Bad #! line"); + for (ap2 = argv ; *ap2++ != NULL ; ); + new = ckmalloc(i + ((char *)ap2 - (char *)argv)); + ap = newargs, ap2 = new; + while ((i -= sizeof (char **)) >= 0) + *ap2++ = *ap++; + ap = argv; + while (*ap2++ = *ap++); + shellexec(new, envp, pathval(), 0); +} +#endif + + + +/* + * Do a path search. The variable path (passed by reference) should be + * set to the start of the path before the first call; padvance will update + * this value as it proceeds. Successive calls to padvance will return + * the possible path expansions in sequence. If an option (indicated by + * a percent sign) appears in the path entry then the global variable + * pathopt will be set to point to it; otherwise pathopt will be set to + * NULL. + */ + +char *pathopt; + +char * +padvance(path, name) + char **path; + char *name; + { + register char *p, *q; + char *start; + int len; + + if (*path == NULL) + return NULL; + start = *path; + for (p = start ; *p && *p != ':' && *p != '%' ; p++); + len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ + while (stackblocksize() < len) + growstackblock(); + q = stackblock(); + if (p != start) { + bcopy(start, q, p - start); + q += p - start; + *q++ = '/'; + } + strcpy(q, name); + pathopt = NULL; + if (*p == '%') { + pathopt = ++p; + while (*p && *p != ':') p++; + } + if (*p == ':') + *path = p + 1; + else + *path = NULL; + return stalloc(len); +} + + + +/*** Command hashing code ***/ + + +hashcmd(argc, argv) char **argv; { + struct tblentry **pp; + struct tblentry *cmdp; + int c; + int verbose; + struct cmdentry entry; + char *name; + + verbose = 0; + while ((c = nextopt("rv")) != '\0') { + if (c == 'r') { + clearcmdentry(0); + } else if (c == 'v') { + verbose++; + } + } + if (*argptr == NULL) { + for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { + for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { + printentry(cmdp, verbose); + } + } + return 0; + } + while ((name = *argptr) != NULL) { + if ((cmdp = cmdlookup(name, 0)) != NULL + && (cmdp->cmdtype == CMDNORMAL + || cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)) + delete_cmd_entry(); + find_command(name, &entry, 1); + if (verbose) { + if (entry.cmdtype != CMDUNKNOWN) { /* if no error msg */ + cmdp = cmdlookup(name, 0); + printentry(cmdp, verbose); + } + flushall(); + } + argptr++; + } + return 0; +} + + +STATIC void +printentry(cmdp, verbose) + struct tblentry *cmdp; + int verbose; + { + int index; + char *path; + char *name; + + if (cmdp->cmdtype == CMDNORMAL) { + index = cmdp->param.index; + path = pathval(); + do { + name = padvance(&path, cmdp->cmdname); + stunalloc(name); + } while (--index >= 0); + out1str(name); + } else if (cmdp->cmdtype == CMDBUILTIN) { + out1fmt("builtin %s", cmdp->cmdname); + } else if (cmdp->cmdtype == CMDFUNCTION) { + out1fmt("function %s", cmdp->cmdname); + if (verbose) { + INTOFF; + name = commandtext(cmdp->param.func); + out1c(' '); + out1str(name); + ckfree(name); + INTON; + } +#ifdef DEBUG + } else { + error("internal error: cmdtype %d", cmdp->cmdtype); +#endif + } + if (cmdp->rehash) + out1c('*'); + out1c('\n'); +} + + + +/* + * Resolve a command name. If you change this routine, you may have to + * change the shellexec routine as well. + */ + +void +find_command(name, entry, printerr) + char *name; + struct cmdentry *entry; + { + struct tblentry *cmdp; + int index; + int prev; + char *path; + char *fullname; + struct stat statb; + int e; + int i; + + /* If name contains a slash, don't use the hash table */ + if (strchr(name, '/') != NULL) { + entry->cmdtype = CMDNORMAL; + entry->u.index = 0; + return; + } + + /* If name is in the table, and not invalidated by cd, we're done */ + if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) + goto success; + + /* If %builtin not in path, check for builtin next */ + if (builtinloc < 0 && (i = find_builtin(name)) >= 0) { + INTOFF; + cmdp = cmdlookup(name, 1); + cmdp->cmdtype = CMDBUILTIN; + cmdp->param.index = i; + INTON; + goto success; + } + + /* We have to search path. */ + prev = -1; /* where to start */ + if (cmdp) { /* doing a rehash */ + if (cmdp->cmdtype == CMDBUILTIN) + prev = builtinloc; + else + prev = cmdp->param.index; + } + + path = pathval(); + e = ENOENT; + index = -1; +loop: + while ((fullname = padvance(&path, name)) != NULL) { + stunalloc(fullname); + index++; + if (pathopt) { + if (prefix("builtin", pathopt)) { + if ((i = find_builtin(name)) < 0) + goto loop; + INTOFF; + cmdp = cmdlookup(name, 1); + cmdp->cmdtype = CMDBUILTIN; + cmdp->param.index = i; + INTON; + goto success; + } else if (prefix("func", pathopt)) { + /* handled below */ + } else { + goto loop; /* ignore unimplemented options */ + } + } + /* if rehash, don't redo absolute path names */ + if (fullname[0] == '/' && index <= prev) { + if (index < prev) + goto loop; + TRACE(("searchexec \"%s\": no change\n", name)); + goto success; + } + while (stat(fullname, &statb) < 0) { +#ifdef SYSV + if (errno == EINTR) + continue; +#endif + if (errno != ENOENT && errno != ENOTDIR) + e = errno; + goto loop; + } + e = EACCES; /* if we fail, this will be the error */ + if ((statb.st_mode & S_IFMT) != S_IFREG) + goto loop; + if (pathopt) { /* this is a %func directory */ + stalloc(strlen(fullname) + 1); + readcmdfile(fullname); + if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION) + error("%s not defined in %s", name, fullname); + stunalloc(fullname); + goto success; + } +#ifdef notdef + if (statb.st_uid == geteuid()) { + if ((statb.st_mode & 0100) == 0) + goto loop; + } else if (statb.st_gid == getegid()) { + if ((statb.st_mode & 010) == 0) + goto loop; + } else { + if ((statb.st_mode & 01) == 0) + goto loop; + } +#endif + TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname)); + INTOFF; + cmdp = cmdlookup(name, 1); + cmdp->cmdtype = CMDNORMAL; + cmdp->param.index = index; + INTON; + goto success; + } + + /* We failed. If there was an entry for this command, delete it */ + if (cmdp) + delete_cmd_entry(); + if (printerr) + outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC)); + entry->cmdtype = CMDUNKNOWN; + return; + +success: + cmdp->rehash = 0; + entry->cmdtype = cmdp->cmdtype; + entry->u = cmdp->param; +} + + + +/* + * Search the table of builtin commands. + */ + +int +find_builtin(name) + char *name; + { + const register struct builtincmd *bp; + + for (bp = builtincmd ; bp->name ; bp++) { + if (*bp->name == *name && equal(bp->name, name)) + return bp->code; + } + return -1; +} + + + +/* + * Called when a cd is done. Marks all commands so the next time they + * are executed they will be rehashed. + */ + +void +hashcd() { + struct tblentry **pp; + struct tblentry *cmdp; + + for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { + for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { + if (cmdp->cmdtype == CMDNORMAL + || cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0) + cmdp->rehash = 1; + } + } +} + + + +/* + * Called before PATH is changed. The argument is the new value of PATH; + * pathval() still returns the old value at this point. Called with + * interrupts off. + */ + +void +changepath(newval) + char *newval; + { + char *old, *new; + int index; + int firstchange; + int bltin; + int hasdot; + + old = pathval(); + new = newval; + firstchange = 9999; /* assume no change */ + index = hasdot = 0; + bltin = -1; + if (*new == ':') + hasdot++; + for (;;) { + if (*old != *new) { + firstchange = index; + if (*old == '\0' && *new == ':' + || *old == ':' && *new == '\0') + firstchange++; + old = new; /* ignore subsequent differences */ + } + if (*new == '\0') + break; + if (*new == '%' && bltin < 0 && prefix("builtin", new + 1)) + bltin = index; + if (*new == ':') { + char c = *(new+1); + + index++; + if (c == ':' || c == '\0' || (c == '.' && + ((c = *(new+2)) == ':' || c == '\0'))) + hasdot++; + } + new++, old++; + } + if (hasdot && geteuid() == 0) + out2str("sh: warning: running as root with dot in PATH\n"); + if (builtinloc < 0 && bltin >= 0) + builtinloc = bltin; /* zap builtins */ + if (builtinloc >= 0 && bltin < 0) + firstchange = 0; + clearcmdentry(firstchange); + builtinloc = bltin; +} + + +/* + * Clear out command entries. The argument specifies the first entry in + * PATH which has changed. + */ + +STATIC void +clearcmdentry(firstchange) { + struct tblentry **tblp; + struct tblentry **pp; + struct tblentry *cmdp; + + INTOFF; + for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { + pp = tblp; + while ((cmdp = *pp) != NULL) { + if (cmdp->cmdtype == CMDNORMAL && cmdp->param.index >= firstchange + || cmdp->cmdtype == CMDBUILTIN && builtinloc >= firstchange) { + *pp = cmdp->next; + ckfree(cmdp); + } else { + pp = &cmdp->next; + } + } + } + INTON; +} + + +/* + * Delete all functions. + */ + +#ifdef mkinit +MKINIT void deletefuncs(); + +SHELLPROC { + deletefuncs(); +} +#endif + +void +deletefuncs() { + struct tblentry **tblp; + struct tblentry **pp; + struct tblentry *cmdp; + + INTOFF; + for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { + pp = tblp; + while ((cmdp = *pp) != NULL) { + if (cmdp->cmdtype == CMDFUNCTION) { + *pp = cmdp->next; + freefunc(cmdp->param.func); + ckfree(cmdp); + } else { + pp = &cmdp->next; + } + } + } + INTON; +} + + + +/* + * Locate a command in the command hash table. If "add" is nonzero, + * add the command to the table if it is not already present. The + * variable "lastcmdentry" is set to point to the address of the link + * pointing to the entry, so that delete_cmd_entry can delete the + * entry. + */ + +struct tblentry **lastcmdentry; + + +STATIC struct tblentry * +cmdlookup(name, add) + char *name; + { + int hashval; + register char *p; + struct tblentry *cmdp; + struct tblentry **pp; + + p = name; + hashval = *p << 4; + while (*p) + hashval += *p++; + hashval &= 0x7FFF; + pp = &cmdtable[hashval % CMDTABLESIZE]; + for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { + if (equal(cmdp->cmdname, name)) + break; + pp = &cmdp->next; + } + if (add && cmdp == NULL) { + INTOFF; + cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB + + strlen(name) + 1); + cmdp->next = NULL; + cmdp->cmdtype = CMDUNKNOWN; + cmdp->rehash = 0; + strcpy(cmdp->cmdname, name); + INTON; + } + lastcmdentry = pp; + return cmdp; +} + +/* + * Delete the command entry returned on the last lookup. + */ + +STATIC void +delete_cmd_entry() { + struct tblentry *cmdp; + + INTOFF; + cmdp = *lastcmdentry; + *lastcmdentry = cmdp->next; + ckfree(cmdp); + INTON; +} + + + +#ifdef notdef +void +getcmdentry(name, entry) + char *name; + struct cmdentry *entry; + { + struct tblentry *cmdp = cmdlookup(name, 0); + + if (cmdp) { + entry->u = cmdp->param; + entry->cmdtype = cmdp->cmdtype; + } else { + entry->cmdtype = CMDUNKNOWN; + entry->u.index = 0; + } +} +#endif + + +/* + * Add a new command entry, replacing any existing command entry for + * the same name. + */ + +void +addcmdentry(name, entry) + char *name; + struct cmdentry *entry; + { + struct tblentry *cmdp; + + INTOFF; + cmdp = cmdlookup(name, 1); + if (cmdp->cmdtype == CMDFUNCTION) { + freefunc(cmdp->param.func); + } + cmdp->cmdtype = entry->cmdtype; + cmdp->param = entry->u; + INTON; +} + + +/* + * Define a shell function. + */ + +void +defun(name, func) + char *name; + union node *func; + { + struct cmdentry entry; + + INTOFF; + entry.cmdtype = CMDFUNCTION; + entry.u.func = copyfunc(func); + addcmdentry(name, &entry); + INTON; +} + + +/* + * Delete a function if it exists. + */ + +int +unsetfunc(name) + char *name; + { + struct tblentry *cmdp; + + if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) { + freefunc(cmdp->param.func); + delete_cmd_entry(); + return (0); + } + return (1); +} diff --git a/bin/sh/exec.h b/bin/sh/exec.h new file mode 100644 index 0000000..a82b0d1 --- /dev/null +++ b/bin/sh/exec.h @@ -0,0 +1,75 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)exec.h 8.1 (Berkeley) 5/31/93 + */ + +/* values of cmdtype */ +#define CMDUNKNOWN -1 /* no entry in table for command */ +#define CMDNORMAL 0 /* command is an executable program */ +#define CMDBUILTIN 1 /* command is a shell builtin */ +#define CMDFUNCTION 2 /* command is a shell function */ + + +struct cmdentry { + int cmdtype; + union param { + int index; + union node *func; + } u; +}; + + +extern char *pathopt; /* set by padvance */ + +#ifdef __STDC__ +void shellexec(char **, char **, char *, int); +char *padvance(char **, char *); +void find_command(char *, struct cmdentry *, int); +int find_builtin(char *); +void hashcd(void); +void changepath(char *); +void defun(char *, union node *); +int unsetfunc(char *); +#else +void shellexec(); +char *padvance(); +void find_command(); +int find_builtin(); +void hashcd(); +void changepath(); +void defun(); +int unsetfunc(); +#endif diff --git a/bin/sh/expand.c b/bin/sh/expand.c new file mode 100644 index 0000000..edee495 --- /dev/null +++ b/bin/sh/expand.c @@ -0,0 +1,1160 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)expand.c 8.2 (Berkeley) 10/22/93"; +#endif /* not lint */ + +/* + * Routines to expand arguments to commands. We have to deal with + * backquotes, shell variables, and file metacharacters. + */ + +#include "shell.h" +#include "main.h" +#include "nodes.h" +#include "eval.h" +#include "expand.h" +#include "syntax.h" +#include "parser.h" +#include "jobs.h" +#include "options.h" +#include "var.h" +#include "input.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <errno.h> +#include <dirent.h> +#include <pwd.h> + +/* + * Structure specifying which parts of the string should be searched + * for IFS characters. + */ + +struct ifsregion { + struct ifsregion *next; /* next region in list */ + int begoff; /* offset of start of region */ + int endoff; /* offset of end of region */ + int nulonly; /* search for nul bytes only */ +}; + + +char *expdest; /* output of current string */ +struct nodelist *argbackq; /* list of back quote expressions */ +struct ifsregion ifsfirst; /* first struct in list of ifs regions */ +struct ifsregion *ifslastp; /* last struct in list */ +struct arglist exparg; /* holds expanded arg list */ + +#ifdef __STDC__ +STATIC void argstr(char *, int); +STATIC void expbackq(union node *, int, int); +STATIC char *evalvar(char *, int); +STATIC int varisset(int); +STATIC void varvalue(int, int, int); +STATIC void recordregion(int, int, int); +STATIC void ifsbreakup(char *, struct arglist *); +STATIC void expandmeta(struct strlist *, int); +STATIC void expmeta(char *, char *); +STATIC void expari(int); +STATIC void addfname(char *); +STATIC struct strlist *expsort(struct strlist *); +STATIC struct strlist *msort(struct strlist *, int); +STATIC int pmatch(char *, char *); +STATIC char *exptilde(char *, int); +#else +STATIC void argstr(); +STATIC void expbackq(); +STATIC char *evalvar(); +STATIC int varisset(); +STATIC void varvalue(); +STATIC void recordregion(); +STATIC void ifsbreakup(); +STATIC void expandmeta(); +STATIC void expmeta(); +STATIC void expari(); +STATIC void addfname(); +STATIC struct strlist *expsort(); +STATIC struct strlist *msort(); +STATIC int pmatch(); +STATIC char *exptilde(); +#endif + +/* + * Expand shell variables and backquotes inside a here document. + */ + +void +expandhere(arg, fd) + union node *arg; /* the document */ + int fd; /* where to write the expanded version */ + { + herefd = fd; + expandarg(arg, (struct arglist *)NULL, 0); + xwrite(fd, stackblock(), expdest - stackblock()); +} + + +/* + * Perform variable substitution and command substitution on an argument, + * placing the resulting list of arguments in arglist. If EXP_FULL is true, + * perform splitting and file name expansion. When arglist is NULL, perform + * here document expansion. + */ + +void +expandarg(arg, arglist, flag) + union node *arg; + struct arglist *arglist; + { + struct strlist *sp; + char *p; + + argbackq = arg->narg.backquote; + STARTSTACKSTR(expdest); + ifsfirst.next = NULL; + ifslastp = NULL; + argstr(arg->narg.text, flag); + if (arglist == NULL) { + return; /* here document expanded */ + } + STPUTC('\0', expdest); + p = grabstackstr(expdest); + exparg.lastp = &exparg.list; + /* + * TODO - EXP_REDIR + */ + if (flag & EXP_FULL) { + ifsbreakup(p, &exparg); + *exparg.lastp = NULL; + exparg.lastp = &exparg.list; + expandmeta(exparg.list, flag); + } else { + if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ + rmescapes(p); + sp = (struct strlist *)stalloc(sizeof (struct strlist)); + sp->text = p; + *exparg.lastp = sp; + exparg.lastp = &sp->next; + } + while (ifsfirst.next != NULL) { + struct ifsregion *ifsp; + INTOFF; + ifsp = ifsfirst.next->next; + ckfree(ifsfirst.next); + ifsfirst.next = ifsp; + INTON; + } + *exparg.lastp = NULL; + if (exparg.list) { + *arglist->lastp = exparg.list; + arglist->lastp = exparg.lastp; + } +} + + + +/* + * Perform variable and command substitution. If EXP_FULL is set, output CTLESC + * characters to allow for further processing. Otherwise treat + * $@ like $* since no splitting will be performed. + */ + +STATIC void +argstr(p, flag) + register char *p; + { + register char c; + int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */ + int firsteq = 1; + + if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) + p = exptilde(p, flag); + for (;;) { + switch (c = *p++) { + case '\0': + case CTLENDVAR: /* ??? */ + goto breakloop; + case CTLESC: + if (quotes) + STPUTC(c, expdest); + c = *p++; + STPUTC(c, expdest); + break; + case CTLVAR: + p = evalvar(p, flag); + break; + case CTLBACKQ: + case CTLBACKQ|CTLQUOTE: + expbackq(argbackq->n, c & CTLQUOTE, flag); + argbackq = argbackq->next; + break; + case CTLENDARI: + expari(flag); + break; + case ':': + case '=': + /* + * sort of a hack - expand tildes in variable + * assignments (after the first '=' and after ':'s). + */ + STPUTC(c, expdest); + if (flag & EXP_VARTILDE && *p == '~') { + if (c == '=') { + if (firsteq) + firsteq = 0; + else + break; + } + p = exptilde(p, flag); + } + break; + default: + STPUTC(c, expdest); + } + } +breakloop:; +} + +STATIC char * +exptilde(p, flag) + char *p; + { + char c, *startp = p; + struct passwd *pw; + char *home; + int quotes = flag & (EXP_FULL | EXP_CASE); + + while (c = *p) { + switch(c) { + case CTLESC: + return (startp); + case ':': + if (flag & EXP_VARTILDE) + goto done; + break; + case '/': + goto done; + } + p++; + } +done: + *p = '\0'; + if (*(startp+1) == '\0') { + if ((home = lookupvar("HOME")) == NULL) + goto lose; + } else { + if ((pw = getpwnam(startp+1)) == NULL) + goto lose; + home = pw->pw_dir; + } + if (*home == '\0') + goto lose; + *p = c; + while (c = *home++) { + if (quotes && SQSYNTAX[c] == CCTL) + STPUTC(CTLESC, expdest); + STPUTC(c, expdest); + } + return (p); +lose: + *p = c; + return (startp); +} + + +/* + * Expand arithmetic expression. Backup to start of expression, + * evaluate, place result in (backed up) result, adjust string position. + */ +void +expari(flag) + { + char *p, *start; + int result; + int quotes = flag & (EXP_FULL | EXP_CASE); + + /* + * This routine is slightly over-compilcated for + * efficiency. First we make sure there is + * enough space for the result, which may be bigger + * than the expression if we add exponentation. Next we + * scan backwards looking for the start of arithmetic. If the + * next previous character is a CTLESC character, then we + * have to rescan starting from the beginning since CTLESC + * characters have to be processed left to right. + */ + CHECKSTRSPACE(8, expdest); + USTPUTC('\0', expdest); + start = stackblock(); + p = expdest; + while (*p != CTLARI && p >= start) + --p; + if (*p != CTLARI) + error("missing CTLARI (shouldn't happen)"); + if (p > start && *(p-1) == CTLESC) + for (p = start; *p != CTLARI; p++) + if (*p == CTLESC) + p++; + if (quotes) + rmescapes(p+1); + result = arith(p+1); + fmtstr(p, 10, "%d", result); + while (*p++) + ; + result = expdest - p + 1; + STADJUST(-result, expdest); +} + + +/* + * Expand stuff in backwards quotes. + */ + +STATIC void +expbackq(cmd, quoted, flag) + union node *cmd; + { + struct backcmd in; + int i; + char buf[128]; + char *p; + char *dest = expdest; + struct ifsregion saveifs, *savelastp; + struct nodelist *saveargbackq; + char lastc; + int startloc = dest - stackblock(); + char const *syntax = quoted? DQSYNTAX : BASESYNTAX; + int saveherefd; + int quotes = flag & (EXP_FULL | EXP_CASE); + + INTOFF; + saveifs = ifsfirst; + savelastp = ifslastp; + saveargbackq = argbackq; + saveherefd = herefd; + herefd = -1; + p = grabstackstr(dest); + evalbackcmd(cmd, &in); + ungrabstackstr(p, dest); + ifsfirst = saveifs; + ifslastp = savelastp; + argbackq = saveargbackq; + herefd = saveherefd; + + p = in.buf; + lastc = '\0'; + for (;;) { + if (--in.nleft < 0) { + if (in.fd < 0) + break; + while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR); + TRACE(("expbackq: read returns %d\n", i)); + if (i <= 0) + break; + p = buf; + in.nleft = i - 1; + } + lastc = *p++; + if (lastc != '\0') { + if (quotes && syntax[lastc] == CCTL) + STPUTC(CTLESC, dest); + STPUTC(lastc, dest); + } + } + if (lastc == '\n') { + STUNPUTC(dest); + } + if (in.fd >= 0) + close(in.fd); + if (in.buf) + ckfree(in.buf); + if (in.jp) + exitstatus = waitforjob(in.jp); + if (quoted == 0) + recordregion(startloc, dest - stackblock(), 0); + TRACE(("evalbackq: size=%d: \"%.*s\"\n", + (dest - stackblock()) - startloc, + (dest - stackblock()) - startloc, + stackblock() + startloc)); + expdest = dest; + INTON; +} + + + +/* + * Expand a variable, and return a pointer to the next character in the + * input string. + */ + +STATIC char * +evalvar(p, flag) + char *p; + { + int subtype; + int varflags; + char *var; + char *val; + int c; + int set; + int special; + int startloc; + int quotes = flag & (EXP_FULL | EXP_CASE); + + varflags = *p++; + subtype = varflags & VSTYPE; + var = p; + special = 0; + if (! is_name(*p)) + special = 1; + p = strchr(p, '=') + 1; +again: /* jump here after setting a variable with ${var=text} */ + if (special) { + set = varisset(*var); + val = NULL; + } else { + val = lookupvar(var); + if (val == NULL || (varflags & VSNUL) && val[0] == '\0') { + val = NULL; + set = 0; + } else + set = 1; + } + startloc = expdest - stackblock(); + if (set && subtype != VSPLUS) { + /* insert the value of the variable */ + if (special) { + varvalue(*var, varflags & VSQUOTE, flag & EXP_FULL); + } else { + char const *syntax = (varflags & VSQUOTE)? DQSYNTAX : BASESYNTAX; + + while (*val) { + if (quotes && syntax[*val] == CCTL) + STPUTC(CTLESC, expdest); + STPUTC(*val++, expdest); + } + } + } + if (subtype == VSPLUS) + set = ! set; + if (((varflags & VSQUOTE) == 0 || (*var == '@' && shellparam.nparam != 1)) + && (set || subtype == VSNORMAL)) + recordregion(startloc, expdest - stackblock(), varflags & VSQUOTE); + if (! set && subtype != VSNORMAL) { + if (subtype == VSPLUS || subtype == VSMINUS) { + argstr(p, flag); + } else { + char *startp; + int saveherefd = herefd; + struct nodelist *saveargbackq = argbackq; + herefd = -1; + argstr(p, 0); + STACKSTRNUL(expdest); + herefd = saveherefd; + argbackq = saveargbackq; + startp = stackblock() + startloc; + if (subtype == VSASSIGN) { + setvar(var, startp, 0); + STADJUST(startp - expdest, expdest); + varflags &=~ VSNUL; + goto again; + } + /* subtype == VSQUESTION */ + if (*p != CTLENDVAR) { + outfmt(&errout, "%s\n", startp); + error((char *)NULL); + } + error("%.*s: parameter %snot set", p - var - 1, + var, (varflags & VSNUL)? "null or " : nullstr); + } + } + if (subtype != VSNORMAL) { /* skip to end of alternative */ + int nesting = 1; + for (;;) { + if ((c = *p++) == CTLESC) + p++; + else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { + if (set) + argbackq = argbackq->next; + } else if (c == CTLVAR) { + if ((*p++ & VSTYPE) != VSNORMAL) + nesting++; + } else if (c == CTLENDVAR) { + if (--nesting == 0) + break; + } + } + } + return p; +} + + + +/* + * Test whether a specialized variable is set. + */ + +STATIC int +varisset(name) + char name; + { + char **ap; + + if (name == '!') { + if (backgndpid == -1) + return 0; + } else if (name == '@' || name == '*') { + if (*shellparam.p == NULL) + return 0; + } else if ((unsigned)(name -= '1') <= '9' - '1') { + ap = shellparam.p; + do { + if (*ap++ == NULL) + return 0; + } while (--name >= 0); + } + return 1; +} + + + +/* + * Add the value of a specialized variable to the stack string. + */ + +STATIC void +varvalue(name, quoted, allow_split) + char name; + { + int num; + char temp[32]; + char *p; + int i; + extern int exitstatus; + char sep; + char **ap; + char const *syntax; + +#define STRTODEST(p) \ + do {\ + if (allow_split) { \ + syntax = quoted? DQSYNTAX : BASESYNTAX; \ + while (*p) { \ + if (syntax[*p] == CCTL) \ + STPUTC(CTLESC, expdest); \ + STPUTC(*p++, expdest); \ + } \ + } else \ + while (*p) \ + STPUTC(*p++, expdest); \ + } while (0) + + + switch (name) { + case '$': + num = rootpid; + goto numvar; + case '?': + num = exitstatus; + goto numvar; + case '#': + num = shellparam.nparam; + goto numvar; + case '!': + num = backgndpid; +numvar: + p = temp + 31; + temp[31] = '\0'; + do { + *--p = num % 10 + '0'; + } while ((num /= 10) != 0); + while (*p) + STPUTC(*p++, expdest); + break; + case '-': + for (i = 0 ; i < NOPTS ; i++) { + if (optlist[i].val) + STPUTC(optlist[i].letter, expdest); + } + break; + case '@': + if (allow_split) { + sep = '\0'; + goto allargs; + } + /* fall through */ + case '*': + sep = ' '; +allargs: + for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { + STRTODEST(p); + if (*ap) + STPUTC(sep, expdest); + } + break; + case '0': + p = arg0; + STRTODEST(p); + break; + default: + if ((unsigned)(name -= '1') <= '9' - '1') { + p = shellparam.p[name]; + STRTODEST(p); + } + break; + } +} + + + +/* + * Record the the fact that we have to scan this region of the + * string for IFS characters. + */ + +STATIC void +recordregion(start, end, nulonly) { + register struct ifsregion *ifsp; + + if (ifslastp == NULL) { + ifsp = &ifsfirst; + } else { + ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); + ifslastp->next = ifsp; + } + ifslastp = ifsp; + ifslastp->next = NULL; + ifslastp->begoff = start; + ifslastp->endoff = end; + ifslastp->nulonly = nulonly; +} + + + +/* + * Break the argument string into pieces based upon IFS and add the + * strings to the argument list. The regions of the string to be + * searched for IFS characters have been stored by recordregion. + */ + +STATIC void +ifsbreakup(string, arglist) + char *string; + struct arglist *arglist; + { + struct ifsregion *ifsp; + struct strlist *sp; + char *start; + register char *p; + char *q; + char *ifs; + + start = string; + if (ifslastp != NULL) { + ifsp = &ifsfirst; + do { + p = string + ifsp->begoff; + ifs = ifsp->nulonly? nullstr : ifsval(); + while (p < string + ifsp->endoff) { + q = p; + if (*p == CTLESC) + p++; + if (strchr(ifs, *p++)) { + if (q > start || *ifs != ' ') { + *q = '\0'; + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = start; + *arglist->lastp = sp; + arglist->lastp = &sp->next; + } + if (*ifs == ' ') { + for (;;) { + if (p >= string + ifsp->endoff) + break; + q = p; + if (*p == CTLESC) + p++; + if (strchr(ifs, *p++) == NULL) { + p = q; + break; + } + } + } + start = p; + } + } + } while ((ifsp = ifsp->next) != NULL); + if (*start || (*ifs != ' ' && start > string)) { + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = start; + *arglist->lastp = sp; + arglist->lastp = &sp->next; + } + } else { + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = start; + *arglist->lastp = sp; + arglist->lastp = &sp->next; + } +} + + + +/* + * Expand shell metacharacters. At this point, the only control characters + * should be escapes. The results are stored in the list exparg. + */ + +char *expdir; + + +STATIC void +expandmeta(str, flag) + struct strlist *str; + { + char *p; + struct strlist **savelastp; + struct strlist *sp; + char c; + /* TODO - EXP_REDIR */ + + while (str) { + if (fflag) + goto nometa; + p = str->text; + for (;;) { /* fast check for meta chars */ + if ((c = *p++) == '\0') + goto nometa; + if (c == '*' || c == '?' || c == '[' || c == '!') + break; + } + savelastp = exparg.lastp; + INTOFF; + if (expdir == NULL) { + int i = strlen(str->text); + expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ + } + + expmeta(expdir, str->text); + ckfree(expdir); + expdir = NULL; + INTON; + if (exparg.lastp == savelastp) { + /* + * no matches + */ +nometa: + *exparg.lastp = str; + rmescapes(str->text); + exparg.lastp = &str->next; + } else { + *exparg.lastp = NULL; + *savelastp = sp = expsort(*savelastp); + while (sp->next != NULL) + sp = sp->next; + exparg.lastp = &sp->next; + } + str = str->next; + } +} + + +/* + * Do metacharacter (i.e. *, ?, [...]) expansion. + */ + +STATIC void +expmeta(enddir, name) + char *enddir; + char *name; + { + register char *p; + char *q; + char *start; + char *endname; + int metaflag; + struct stat statb; + DIR *dirp; + struct dirent *dp; + int atend; + int matchdot; + + metaflag = 0; + start = name; + for (p = name ; ; p++) { + if (*p == '*' || *p == '?') + metaflag = 1; + else if (*p == '[') { + q = p + 1; + if (*q == '!') + q++; + for (;;) { + if (*q == CTLESC) + q++; + if (*q == '/' || *q == '\0') + break; + if (*++q == ']') { + metaflag = 1; + break; + } + } + } else if (*p == '!' && p[1] == '!' && (p == name || p[-1] == '/')) { + metaflag = 1; + } else if (*p == '\0') + break; + else if (*p == CTLESC) + p++; + if (*p == '/') { + if (metaflag) + break; + start = p + 1; + } + } + if (metaflag == 0) { /* we've reached the end of the file name */ + if (enddir != expdir) + metaflag++; + for (p = name ; ; p++) { + if (*p == CTLESC) + p++; + *enddir++ = *p; + if (*p == '\0') + break; + } + if (metaflag == 0 || stat(expdir, &statb) >= 0) + addfname(expdir); + return; + } + endname = p; + if (start != name) { + p = name; + while (p < start) { + if (*p == CTLESC) + p++; + *enddir++ = *p++; + } + } + if (enddir == expdir) { + p = "."; + } else if (enddir == expdir + 1 && *expdir == '/') { + p = "/"; + } else { + p = expdir; + enddir[-1] = '\0'; + } + if ((dirp = opendir(p)) == NULL) + return; + if (enddir != expdir) + enddir[-1] = '/'; + if (*endname == 0) { + atend = 1; + } else { + atend = 0; + *endname++ = '\0'; + } + matchdot = 0; + if (start[0] == '.' || start[0] == CTLESC && start[1] == '.') + matchdot++; + while (! int_pending() && (dp = readdir(dirp)) != NULL) { + if (dp->d_name[0] == '.' && ! matchdot) + continue; + if (patmatch(start, dp->d_name)) { + if (atend) { + scopy(dp->d_name, enddir); + addfname(expdir); + } else { + char *q; + for (p = enddir, q = dp->d_name ; *p++ = *q++ ;); + p[-1] = '/'; + expmeta(p, endname); + } + } + } + closedir(dirp); + if (! atend) + endname[-1] = '/'; +} + + +/* + * Add a file name to the list. + */ + +STATIC void +addfname(name) + char *name; + { + char *p; + struct strlist *sp; + + p = stalloc(strlen(name) + 1); + scopy(name, p); + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = p; + *exparg.lastp = sp; + exparg.lastp = &sp->next; +} + + +/* + * Sort the results of file name expansion. It calculates the number of + * strings to sort and then calls msort (short for merge sort) to do the + * work. + */ + +STATIC struct strlist * +expsort(str) + struct strlist *str; + { + int len; + struct strlist *sp; + + len = 0; + for (sp = str ; sp ; sp = sp->next) + len++; + return msort(str, len); +} + + +STATIC struct strlist * +msort(list, len) + struct strlist *list; + { + struct strlist *p, *q; + struct strlist **lpp; + int half; + int n; + + if (len <= 1) + return list; + half = len >> 1; + p = list; + for (n = half ; --n >= 0 ; ) { + q = p; + p = p->next; + } + q->next = NULL; /* terminate first half of list */ + q = msort(list, half); /* sort first half of list */ + p = msort(p, len - half); /* sort second half */ + lpp = &list; + for (;;) { + if (strcmp(p->text, q->text) < 0) { + *lpp = p; + lpp = &p->next; + if ((p = *lpp) == NULL) { + *lpp = q; + break; + } + } else { + *lpp = q; + lpp = &q->next; + if ((q = *lpp) == NULL) { + *lpp = p; + break; + } + } + } + return list; +} + + + +/* + * Returns true if the pattern matches the string. + */ + +int +patmatch(pattern, string) + char *pattern; + char *string; + { +#ifdef notdef + if (pattern[0] == '!' && pattern[1] == '!') + return 1 - pmatch(pattern + 2, string); + else +#endif + return pmatch(pattern, string); +} + + +STATIC int +pmatch(pattern, string) + char *pattern; + char *string; + { + register char *p, *q; + register char c; + + p = pattern; + q = string; + for (;;) { + switch (c = *p++) { + case '\0': + goto breakloop; + case CTLESC: + if (*q++ != *p++) + return 0; + break; + case '?': + if (*q++ == '\0') + return 0; + break; + case '*': + c = *p; + if (c != CTLESC && c != '?' && c != '*' && c != '[') { + while (*q != c) { + if (*q == '\0') + return 0; + q++; + } + } + do { + if (pmatch(p, q)) + return 1; + } while (*q++ != '\0'); + return 0; + case '[': { + char *endp; + int invert, found; + char chr; + + endp = p; + if (*endp == '!') + endp++; + for (;;) { + if (*endp == '\0') + goto dft; /* no matching ] */ + if (*endp == CTLESC) + endp++; + if (*++endp == ']') + break; + } + invert = 0; + if (*p == '!') { + invert++; + p++; + } + found = 0; + chr = *q++; + c = *p++; + do { + if (c == CTLESC) + c = *p++; + if (*p == '-' && p[1] != ']') { + p++; + if (*p == CTLESC) + p++; + if (chr >= c && chr <= *p) + found = 1; + p++; + } else { + if (chr == c) + found = 1; + } + } while ((c = *p++) != ']'); + if (found == invert) + return 0; + break; + } +dft: default: + if (*q++ != c) + return 0; + break; + } + } +breakloop: + if (*q != '\0') + return 0; + return 1; +} + + + +/* + * Remove any CTLESC characters from a string. + */ + +void +rmescapes(str) + char *str; + { + register char *p, *q; + + p = str; + while (*p != CTLESC) { + if (*p++ == '\0') + return; + } + q = p; + while (*p) { + if (*p == CTLESC) + p++; + *q++ = *p++; + } + *q = '\0'; +} + + + +/* + * See if a pattern matches in a case statement. + */ + +int +casematch(pattern, val) + union node *pattern; + char *val; + { + struct stackmark smark; + int result; + char *p; + + setstackmark(&smark); + argbackq = pattern->narg.backquote; + STARTSTACKSTR(expdest); + ifslastp = NULL; + argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); + STPUTC('\0', expdest); + p = grabstackstr(expdest); + result = patmatch(p, val); + popstackmark(&smark); + return result; +} diff --git a/bin/sh/expand.h b/bin/sh/expand.h new file mode 100644 index 0000000..5c6758f --- /dev/null +++ b/bin/sh/expand.h @@ -0,0 +1,73 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)expand.h 8.1 (Berkeley) 5/31/93 + */ + +struct strlist { + struct strlist *next; + char *text; +}; + + +struct arglist { + struct strlist *list; + struct strlist **lastp; +}; + +/* + * expandarg() flags + */ +#define EXP_FULL 0x1 /* perform word splitting & file globbing */ +#define EXP_TILDE 0x2 /* do normal tilde expansion */ +#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */ +#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */ +#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */ + + +#ifdef __STDC__ +union node; +void expandarg(union node *, struct arglist *, int); +void expandhere(union node *, int); +int patmatch(char *, char *); +void rmescapes(char *); +int casematch(union node *, char *); +#else +void expandarg(); +void expandhere(); +int patmatch(); +void rmescapes(); +int casematch(); +#endif diff --git a/bin/sh/funcs/cmv b/bin/sh/funcs/cmv new file mode 100644 index 0000000..a0921ef --- /dev/null +++ b/bin/sh/funcs/cmv @@ -0,0 +1,49 @@ +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)cmv 8.1 (Berkeley) 5/31/93 + +# Conditional move--don't replace an existing file. + +cmv() { + if test $# != 2 + then echo "cmv: arg count" + return 2 + fi + if test -f "$2" -o -w "$2" + then echo "$2 exists" + return 2 + fi + /bin/mv "$1" "$2" +} diff --git a/bin/sh/funcs/dirs b/bin/sh/funcs/dirs new file mode 100644 index 0000000..04b7fe1 --- /dev/null +++ b/bin/sh/funcs/dirs @@ -0,0 +1,73 @@ +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)dirs 8.1 (Berkeley) 5/31/93 + +# pushd, popd, and dirs --- written by Chris Bertin +# Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris +# as modified by Patrick Elam of GTRI and Kenneth Almquist at UW + +pushd () { + SAVE=`pwd` + if [ "$1" = "" ] + then if [ "$DSTACK" = "" ] + then echo "pushd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 || return + shift 1 + DSTACK="$*" + else cd $1 > /dev/null || return + fi + DSTACK="$SAVE $DSTACK" + dirs +} + +popd () { + if [ "$DSTACK" = "" ] + then echo "popd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 + shift + DSTACK=$* + dirs +} + +dirs () { + echo "`pwd` $DSTACK" + return 0 +} diff --git a/bin/sh/funcs/kill b/bin/sh/funcs/kill new file mode 100644 index 0000000..da670e9 --- /dev/null +++ b/bin/sh/funcs/kill @@ -0,0 +1,49 @@ +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)kill 8.1 (Berkeley) 5/31/93 + +# Convert job names to process ids and then run /bin/kill. + +kill() { + local args x + args= + for x in "$@" + do case $x in + %*) x=`jobid "$x"` ;; + esac + args="$args $x" + done + /bin/kill $args +} diff --git a/bin/sh/funcs/login b/bin/sh/funcs/login new file mode 100644 index 0000000..d8e9d6f --- /dev/null +++ b/bin/sh/funcs/login @@ -0,0 +1,38 @@ +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)login 8.1 (Berkeley) 5/31/93 + +# replaces the login builtin in the BSD shell +login () exec login "$@" diff --git a/bin/sh/funcs/newgrp b/bin/sh/funcs/newgrp new file mode 100644 index 0000000..d5aa63e --- /dev/null +++ b/bin/sh/funcs/newgrp @@ -0,0 +1,37 @@ +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)newgrp 8.1 (Berkeley) 5/31/93 + +newgrp() exec newgrp "$@" diff --git a/bin/sh/funcs/popd b/bin/sh/funcs/popd new file mode 100644 index 0000000..2f4de3a --- /dev/null +++ b/bin/sh/funcs/popd @@ -0,0 +1,73 @@ +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)popd 8.1 (Berkeley) 5/31/93 + +# pushd, popd, and dirs --- written by Chris Bertin +# Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris +# as modified by Patrick Elam of GTRI and Kenneth Almquist at UW + +pushd () { + SAVE=`pwd` + if [ "$1" = "" ] + then if [ "$DSTACK" = "" ] + then echo "pushd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 || return + shift 1 + DSTACK="$*" + else cd $1 > /dev/null || return + fi + DSTACK="$SAVE $DSTACK" + dirs +} + +popd () { + if [ "$DSTACK" = "" ] + then echo "popd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 + shift + DSTACK=$* + dirs +} + +dirs () { + echo "`pwd` $DSTACK" + return 0 +} diff --git a/bin/sh/funcs/pushd b/bin/sh/funcs/pushd new file mode 100644 index 0000000..0c50353 --- /dev/null +++ b/bin/sh/funcs/pushd @@ -0,0 +1,73 @@ +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)pushd 8.1 (Berkeley) 5/31/93 + +# pushd, popd, and dirs --- written by Chris Bertin +# Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris +# as modified by Patrick Elam of GTRI and Kenneth Almquist at UW + +pushd () { + SAVE=`pwd` + if [ "$1" = "" ] + then if [ "$DSTACK" = "" ] + then echo "pushd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 || return + shift 1 + DSTACK="$*" + else cd $1 > /dev/null || return + fi + DSTACK="$SAVE $DSTACK" + dirs +} + +popd () { + if [ "$DSTACK" = "" ] + then echo "popd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 + shift + DSTACK=$* + dirs +} + +dirs () { + echo "`pwd` $DSTACK" + return 0 +} diff --git a/bin/sh/funcs/suspend b/bin/sh/funcs/suspend new file mode 100644 index 0000000..4483965 --- /dev/null +++ b/bin/sh/funcs/suspend @@ -0,0 +1,41 @@ +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)suspend 8.1 (Berkeley) 5/31/93 + +suspend() { + local - + set +j + kill -TSTP 0 +} diff --git a/bin/sh/histedit.c b/bin/sh/histedit.c new file mode 100644 index 0000000..0093c17 --- /dev/null +++ b/bin/sh/histedit.c @@ -0,0 +1,442 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)histedit.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +/* + * Editline and history functions (and glue). + */ +#include <sys/param.h> +#include <paths.h> +#include <stdio.h> +#include "shell.h" +#include "parser.h" +#include "var.h" +#include "options.h" +#include "mystring.h" +#include "error.h" +#include "histedit.h" +#include "memalloc.h" + +#define MAXHISTLOOPS 4 /* max recursions through fc */ +#define DEFEDITOR "ed" /* default editor *should* be $EDITOR */ + +History *hist; /* history cookie */ +EditLine *el; /* editline cookie */ +int displayhist; +static FILE *el_in, *el_out; + +STATIC char *fc_replace __P((const char *, char *, char *)); + +/* + * Set history and editing status. Called whenever the status may + * have changed (figures out what to do). + */ +histedit() { + +#define editing (Eflag || Vflag) + + if (iflag) { + if (!hist) { + /* + * turn history on + */ + INTOFF; + hist = history_init(); + INTON; + + if (hist != NULL) + sethistsize(); + else + out2str("sh: can't initialize history\n"); + } + if (editing && !el && isatty(0)) { /* && isatty(2) ??? */ + /* + * turn editing on + */ + INTOFF; + if (el_in == NULL) + el_in = fdopen(0, "r"); + if (el_out == NULL) + el_out = fdopen(2, "w"); + if (el_in == NULL || el_out == NULL) + goto bad; + el = el_init(arg0, el_in, el_out); + if (el != NULL) { + if (hist) + el_set(el, EL_HIST, history, hist); + el_set(el, EL_PROMPT, getprompt); + } else { +bad: + out2str("sh: can't initialize editing\n"); + } + INTON; + } else if (!editing && el) { + INTOFF; + el_end(el); + el = NULL; + INTON; + } + if (el) { + if (Vflag) + el_set(el, EL_EDITOR, "vi"); + else if (Eflag) + el_set(el, EL_EDITOR, "emacs"); + } + } else { + INTOFF; + if (el) { /* no editing if not interactive */ + el_end(el); + el = NULL; + } + if (hist) { + history_end(hist); + hist = NULL; + } + INTON; + } +} + +sethistsize() { + char *cp; + int histsize; + + if (hist != NULL) { + cp = lookupvar("HISTSIZE"); + if (cp == NULL || *cp == '\0' || + (histsize = atoi(cp)) < 0) + histsize = 100; + history(hist, H_EVENT, histsize); + } +} + +/* + * This command is provided since POSIX decided to standardize + * the Korn shell fc command. Oh well... + */ +histcmd(argc, argv) + char *argv[]; +{ + extern char *optarg; + extern int optind, optopt, optreset; + int ch; + char *editor = NULL; + const HistEvent *he; + int lflg = 0, nflg = 0, rflg = 0, sflg = 0; + int i; + char *firststr, *laststr; + int first, last, direction; + char *pat = NULL, *repl; /* ksh "fc old=new" crap */ + static int active = 0; + struct jmploc jmploc; + struct jmploc *volatile savehandler; + char editfile[MAXPATHLEN + 1]; + FILE *efp; + + if (hist == NULL) + error("history not active"); + + if (argc == 1) + error("missing history argument"); + + optreset = 1; optind = 1; /* initialize getopt */ + while (not_fcnumber(argv[optind]) && + (ch = getopt(argc, argv, ":e:lnrs")) != EOF) + switch ((char)ch) { + case 'e': + editor = optarg; + break; + case 'l': + lflg = 1; + break; + case 'n': + nflg = 1; + break; + case 'r': + rflg = 1; + break; + case 's': + sflg = 1; + break; + case ':': + error("option -%c expects argument", optopt); + case '?': + default: + error("unknown option: -%c", optopt); + } + argc -= optind, argv += optind; + + /* + * If executing... + */ + if (lflg == 0 || editor || sflg) { + lflg = 0; /* ignore */ + editfile[0] = '\0'; + /* + * Catch interrupts to reset active counter and + * cleanup temp files. + */ + if (setjmp(jmploc.loc)) { + active = 0; + if (*editfile) + unlink(editfile); + handler = savehandler; + longjmp(handler->loc, 1); + } + savehandler = handler; + handler = &jmploc; + if (++active > MAXHISTLOOPS) { + active = 0; + displayhist = 0; + error("called recursively too many times"); + } + /* + * Set editor. + */ + if (sflg == 0) { + if (editor == NULL && + (editor = bltinlookup("FCEDIT", 1)) == NULL && + (editor = bltinlookup("EDITOR", 1)) == NULL) + editor = DEFEDITOR; + if (editor[0] == '-' && editor[1] == '\0') { + sflg = 1; /* no edit */ + editor = NULL; + } + } + } + + /* + * If executing, parse [old=new] now + */ + if (lflg == 0 && argc > 0 && + ((repl = strchr(argv[0], '=')) != NULL)) { + pat = argv[0]; + *repl++ = '\0'; + argc--, argv++; + } + /* + * determine [first] and [last] + */ + switch (argc) { + case 0: + firststr = lflg ? "-16" : "-1"; + laststr = "-1"; + break; + case 1: + firststr = argv[0]; + laststr = lflg ? "-1" : argv[0]; + break; + case 2: + firststr = argv[0]; + laststr = argv[1]; + break; + default: + error("too many args"); + } + /* + * Turn into event numbers. + */ + first = str_to_event(firststr, 0); + last = str_to_event(laststr, 1); + + if (rflg) { + i = last; + last = first; + first = i; + } + /* + * XXX - this should not depend on the event numbers + * always increasing. Add sequence numbers or offset + * to the history element in next (diskbased) release. + */ + direction = first < last ? H_PREV : H_NEXT; + + /* + * If editing, grab a temp file. + */ + if (editor) { + int fd; + INTOFF; /* easier */ + sprintf(editfile, "%s/_shXXXXXX", _PATH_TMP); + if ((fd = mkstemp(editfile)) < 0) + error("can't create temporary file %s", editfile); + if ((efp = fdopen(fd, "w")) == NULL) { + close(fd); + error("can't allocate stdio buffer for temp\n"); + } + } + + /* + * Loop through selected history events. If listing or executing, + * do it now. Otherwise, put into temp file and call the editor + * after. + * + * The history interface needs rethinking, as the following + * convolutions will demonstrate. + */ + history(hist, H_FIRST); + he = history(hist, H_NEXT_EVENT, first); + for (;he != NULL; he = history(hist, direction)) { + if (lflg) { + if (!nflg) + out1fmt("%5d ", he->num); + out1str(he->str); + } else { + char *s = pat ? + fc_replace(he->str, pat, repl) : (char *)he->str; + + if (sflg) { + if (displayhist) { + out2str(s); + } + evalstring(s); + if (displayhist && hist) { + /* + * XXX what about recursive and + * relative histnums. + */ + history(hist, H_ENTER, s); + } + } else + fputs(s, efp); + } + /* + * At end? (if we were to loose last, we'd sure be + * messed up). + */ + if (he->num == last) + break; + } + if (editor) { + char *editcmd; + + fclose(efp); + editcmd = stalloc(strlen(editor) + strlen(editfile) + 2); + sprintf(editcmd, "%s %s", editor, editfile); + evalstring(editcmd); /* XXX - should use no JC command */ + INTON; + readcmdfile(editfile); /* XXX - should read back - quick tst */ + unlink(editfile); + } + + if (lflg == 0 && active > 0) + --active; + if (displayhist) + displayhist = 0; +} + +STATIC char * +fc_replace(s, p, r) + const char *s; + char *p, *r; +{ + char *dest; + int plen = strlen(p); + + STARTSTACKSTR(dest); + while (*s) { + if (*s == *p && strncmp(s, p, plen) == 0) { + while (*r) + STPUTC(*r++, dest); + s += plen; + *p = '\0'; /* so no more matches */ + } else + STPUTC(*s++, dest); + } + STACKSTRNUL(dest); + dest = grabstackstr(dest); + + return (dest); +} + +not_fcnumber(s) + char *s; +{ + if (*s == '-') + s++; + return (!is_number(s)); +} + +str_to_event(str, last) + char *str; + int last; +{ + const HistEvent *he; + char *s = str; + int relative = 0; + int i, j; + + he = history(hist, H_FIRST); + switch (*s) { + case '-': + relative = 1; + /*FALLTHROUGH*/ + case '+': + s++; + } + if (is_number(s)) { + i = atoi(s); + if (relative) { + while (he != NULL && i--) { + he = history(hist, H_NEXT); + } + if (he == NULL) + he = history(hist, H_LAST); + } else { + he = history(hist, H_NEXT_EVENT, i); + if (he == NULL) { + /* + * the notion of first and last is + * backwards to that of the history package + */ + he = history(hist, last ? H_FIRST : H_LAST); + } + } + if (he == NULL) + error("history number %s not found (internal error)", + str); + } else { + /* + * pattern + */ + he = history(hist, H_PREV_STR, str); + if (he == NULL) + error("history pattern not found: %s", str); + } + return (he->num); +} diff --git a/bin/sh/init.h b/bin/sh/init.h new file mode 100644 index 0000000..b184361 --- /dev/null +++ b/bin/sh/init.h @@ -0,0 +1,47 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)init.h 8.1 (Berkeley) 5/31/93 + */ + +#ifdef __STDC__ +void init(void); +void reset(void); +void initshellproc(void); +#else +void init(); +void reset(); +void initshellproc(); +#endif diff --git a/bin/sh/input.c b/bin/sh/input.c new file mode 100644 index 0000000..d1d84e1 --- /dev/null +++ b/bin/sh/input.c @@ -0,0 +1,474 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)input.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +/* + * This file implements the input routines used by the parser. + */ + +#include <stdio.h> /* defines BUFSIZ */ +#include "shell.h" +#include <fcntl.h> +#include <errno.h> +#include "syntax.h" +#include "input.h" +#include "output.h" +#include "options.h" +#include "memalloc.h" +#include "error.h" +#include "alias.h" +#include "parser.h" +#include "myhistedit.h" + +#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */ + +MKINIT +struct strpush { + struct strpush *prev; /* preceding string on stack */ + char *prevstring; + int prevnleft; + struct alias *ap; /* if push was associated with an alias */ +}; + +/* + * The parsefile structure pointed to by the global variable parsefile + * contains information about the current file being read. + */ + +MKINIT +struct parsefile { + struct parsefile *prev; /* preceding file on stack */ + int linno; /* current line */ + int fd; /* file descriptor (or -1 if string) */ + int nleft; /* number of chars left in buffer */ + char *nextc; /* next char in buffer */ + char *buf; /* input buffer */ + struct strpush *strpush; /* for pushing strings at this level */ + struct strpush basestrpush; /* so pushing one is fast */ +}; + + +int plinno = 1; /* input line number */ +MKINIT int parsenleft; /* copy of parsefile->nleft */ +char *parsenextc; /* copy of parsefile->nextc */ +MKINIT struct parsefile basepf; /* top level input file */ +char basebuf[BUFSIZ]; /* buffer for top level input file */ +struct parsefile *parsefile = &basepf; /* current input file */ +char *pushedstring; /* copy of parsenextc when text pushed back */ +int pushednleft; /* copy of parsenleft when text pushed back */ +int init_editline = 0; /* editline library initialized? */ +int whichprompt; /* 1 == PS1, 2 == PS2 */ + +EditLine *el; /* cookie for editline package */ + +#ifdef __STDC__ +STATIC void pushfile(void); +#else +STATIC void pushfile(); +#endif + + + +#ifdef mkinit +INCLUDE "input.h" +INCLUDE "error.h" + +INIT { + extern char basebuf[]; + + basepf.nextc = basepf.buf = basebuf; +} + +RESET { + if (exception != EXSHELLPROC) + parsenleft = 0; /* clear input buffer */ + popallfiles(); +} + +SHELLPROC { + popallfiles(); +} +#endif + + +/* + * Read a line from the script. + */ + +char * +pfgets(line, len) + char *line; + { + register char *p = line; + int nleft = len; + int c; + + while (--nleft > 0) { + c = pgetc_macro(); + if (c == PEOF) { + if (p == line) + return NULL; + break; + } + *p++ = c; + if (c == '\n') + break; + } + *p = '\0'; + return line; +} + + + +/* + * Read a character from the script, returning PEOF on end of file. + * Nul characters in the input are silently discarded. + */ + +int +pgetc() { + return pgetc_macro(); +} + + +/* + * Refill the input buffer and return the next input character: + * + * 1) If a string was pushed back on the input, pop it; + * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading + * from a string so we can't refill the buffer, return EOF. + * 3) Call read to read in the characters. + * 4) Delete all nul characters from the buffer. + */ + +int +preadbuffer() { + register char *p, *q; + register int i; + register int something; + extern EditLine *el; + + if (parsefile->strpush) { + popstring(); + if (--parsenleft >= 0) + return (*parsenextc++); + } + if (parsenleft == EOF_NLEFT || parsefile->buf == NULL) + return PEOF; + flushout(&output); + flushout(&errout); +retry: + p = parsenextc = parsefile->buf; + if (parsefile->fd == 0 && el) { + const char *rl_cp; + int len; + + rl_cp = el_gets(el, &len); + if (rl_cp == NULL) { + i = 0; + goto eof; + } + strcpy(p, rl_cp); /* XXX - BUFSIZE should redesign so not necessary */ + i = len; + + } else { +regular_read: + i = read(parsefile->fd, p, BUFSIZ - 1); + } +eof: + if (i <= 0) { + if (i < 0) { + if (errno == EINTR) + goto retry; + if (parsefile->fd == 0 && errno == EWOULDBLOCK) { + int flags = fcntl(0, F_GETFL, 0); + if (flags >= 0 && flags & O_NONBLOCK) { + flags &=~ O_NONBLOCK; + if (fcntl(0, F_SETFL, flags) >= 0) { + out2str("sh: turning off NDELAY mode\n"); + goto retry; + } + } + } + } + parsenleft = EOF_NLEFT; + return PEOF; + } + parsenleft = i - 1; /* we're returning one char in this call */ + + /* delete nul characters */ + something = 0; + for (;;) { + if (*p == '\0') + break; + if (*p != ' ' && *p != '\t' && *p != '\n') + something = 1; + p++; + if (--i <= 0) { + *p = '\0'; + goto done; /* no nul characters */ + } + } + /* + * remove nuls + */ + q = p++; + while (--i > 0) { + if (*p != '\0') + *q++ = *p; + p++; + } + *q = '\0'; + if (q == parsefile->buf) + goto retry; /* buffer contained nothing but nuls */ + parsenleft = q - parsefile->buf - 1; + +done: + if (parsefile->fd == 0 && hist && something) { + INTOFF; + history(hist, whichprompt == 1 ? H_ENTER : H_ADD, + parsefile->buf); + INTON; + } + if (vflag) { + /* + * This isn't right. Most shells coordinate it with + * reading a line at a time. I honestly don't know if its + * worth it. + */ + i = parsenleft + 1; + p = parsefile->buf; + for (; i--; p++) + out2c(*p) + flushout(out2); + } + return *parsenextc++; +} + +/* + * Undo the last call to pgetc. Only one character may be pushed back. + * PEOF may be pushed back. + */ + +void +pungetc() { + parsenleft++; + parsenextc--; +} + +/* + * Push a string back onto the input at this current parsefile level. + * We handle aliases this way. + */ +void +pushstring(s, len, ap) + char *s; + int len; + void *ap; + { + struct strpush *sp; + + INTOFF; +/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/ + if (parsefile->strpush) { + sp = ckmalloc(sizeof (struct strpush)); + sp->prev = parsefile->strpush; + parsefile->strpush = sp; + } else + sp = parsefile->strpush = &(parsefile->basestrpush); + sp->prevstring = parsenextc; + sp->prevnleft = parsenleft; + sp->ap = (struct alias *)ap; + if (ap) + ((struct alias *)ap)->flag |= ALIASINUSE; + parsenextc = s; + parsenleft = len; + INTON; +} + +popstring() +{ + struct strpush *sp = parsefile->strpush; + + INTOFF; + parsenextc = sp->prevstring; + parsenleft = sp->prevnleft; +/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/ + if (sp->ap) + sp->ap->flag &= ~ALIASINUSE; + parsefile->strpush = sp->prev; + if (sp != &(parsefile->basestrpush)) + ckfree(sp); + INTON; +} + +/* + * Set the input to take input from a file. If push is set, push the + * old input onto the stack first. + */ + +void +setinputfile(fname, push) + char *fname; + { + int fd; + int fd2; + + INTOFF; + if ((fd = open(fname, O_RDONLY)) < 0) + error("Can't open %s", fname); + if (fd < 10) { + fd2 = copyfd(fd, 10); + close(fd); + if (fd2 < 0) + error("Out of file descriptors"); + fd = fd2; + } + setinputfd(fd, push); + INTON; +} + + +/* + * Like setinputfile, but takes an open file descriptor. Call this with + * interrupts off. + */ + +void +setinputfd(fd, push) { + if (push) { + pushfile(); + parsefile->buf = ckmalloc(BUFSIZ); + } + if (parsefile->fd > 0) + close(parsefile->fd); + parsefile->fd = fd; + if (parsefile->buf == NULL) + parsefile->buf = ckmalloc(BUFSIZ); + parsenleft = 0; + plinno = 1; +} + + +/* + * Like setinputfile, but takes input from a string. + */ + +void +setinputstring(string, push) + char *string; + { + INTOFF; + if (push) + pushfile(); + parsenextc = string; + parsenleft = strlen(string); + parsefile->buf = NULL; + plinno = 1; + INTON; +} + + + +/* + * To handle the "." command, a stack of input files is used. Pushfile + * adds a new entry to the stack and popfile restores the previous level. + */ + +STATIC void +pushfile() { + struct parsefile *pf; + + parsefile->nleft = parsenleft; + parsefile->nextc = parsenextc; + parsefile->linno = plinno; + pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile)); + pf->prev = parsefile; + pf->fd = -1; + pf->strpush = NULL; + pf->basestrpush.prev = NULL; + parsefile = pf; +} + + +void +popfile() { + struct parsefile *pf = parsefile; + + INTOFF; + if (pf->fd >= 0) + close(pf->fd); + if (pf->buf) + ckfree(pf->buf); + while (pf->strpush) + popstring(); + parsefile = pf->prev; + ckfree(pf); + parsenleft = parsefile->nleft; + parsenextc = parsefile->nextc; + plinno = parsefile->linno; + INTON; +} + + +/* + * Return to top level. + */ + +void +popallfiles() { + while (parsefile != &basepf) + popfile(); +} + + + +/* + * Close the file(s) that the shell is reading commands from. Called + * after a fork is done. + */ + +void +closescript() { + popallfiles(); + if (parsefile->fd > 0) { + close(parsefile->fd); + parsefile->fd = 0; + } +} diff --git a/bin/sh/input.h b/bin/sh/input.h new file mode 100644 index 0000000..754eda0 --- /dev/null +++ b/bin/sh/input.h @@ -0,0 +1,78 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)input.h 8.1 (Berkeley) 5/31/93 + */ + +/* PEOF (the end of file marker) is defined in syntax.h */ + +/* + * The input line number. Input.c just defines this variable, and saves + * and restores it when files are pushed and popped. The user of this + * package must set its value. + */ +extern int plinno; +extern int parsenleft; /* number of characters left in input buffer */ +extern char *parsenextc; /* next character in input buffer */ +extern int init_editline; /* 0 == not setup, 1 == OK, -1 == failed */ + + +#ifdef __STDC__ +char *pfgets(char *, int); +int pgetc(void); +int preadbuffer(void); +void pungetc(void); +void pushstring(char *, int, void *); +void setinputfile(char *, int); +void setinputfd(int, int); +void setinputstring(char *, int); +void popfile(void); +void popallfiles(void); +void closescript(void); +#else +char *pfgets(); +int pgetc(); +int preadbuffer(); +void pungetc(); +void setinputfile(); +void setinputfd(); +void setinputstring(); +void popfile(); +void popallfiles(); +void pushstring(); +void closescript(); +#endif + +#define pgetc_macro() (--parsenleft >= 0? *parsenextc++ : preadbuffer()) diff --git a/bin/sh/jobs.c b/bin/sh/jobs.c new file mode 100644 index 0000000..60f66ed --- /dev/null +++ b/bin/sh/jobs.c @@ -0,0 +1,1046 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)jobs.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include "shell.h" +#if JOBS +#include "sgtty.h" +#undef CEOF /* syntax.h redefines this */ +#endif +#include "main.h" +#include "parser.h" +#include "nodes.h" +#include "jobs.h" +#include "options.h" +#include "trap.h" +#include "signames.h" +#include "syntax.h" +#include "input.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" +#include <fcntl.h> +#include <signal.h> +#include <errno.h> +#ifdef BSD +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/time.h> +#include <sys/resource.h> +#endif + + + +struct job *jobtab; /* array of jobs */ +int njobs; /* size of array */ +MKINIT short backgndpid = -1; /* pid of last background process */ +#if JOBS +int initialpgrp; /* pgrp of shell on invocation */ +short curjob; /* current job */ +#endif + +#ifdef __STDC__ +STATIC void restartjob(struct job *); +STATIC struct job *getjob(char *); +STATIC void freejob(struct job *); +STATIC int procrunning(int); +STATIC int dowait(int, struct job *); +STATIC int waitproc(int, int *); +#else +STATIC void restartjob(); +STATIC struct job *getjob(); +STATIC void freejob(); +STATIC int procrunning(); +STATIC int dowait(); +STATIC int waitproc(); +#endif + + + +/* + * Turn job control on and off. + * + * Note: This code assumes that the third arg to ioctl is a character + * pointer, which is true on Berkeley systems but not System V. Since + * System V doesn't have job control yet, this isn't a problem now. + */ + +MKINIT int jobctl; + +void +setjobctl(on) { +#ifdef OLD_TTY_DRIVER + int ldisc; +#endif + + if (on == jobctl || rootshell == 0) + return; + if (on) { + do { /* while we are in the background */ + if (ioctl(2, TIOCGPGRP, (char *)&initialpgrp) < 0) { + out2str("sh: can't access tty; job control turned off\n"); + mflag = 0; + return; + } + if (initialpgrp == -1) + initialpgrp = getpgrp(0); + else if (initialpgrp != getpgrp(0)) { + killpg(initialpgrp, SIGTTIN); + continue; + } + } while (0); +#ifdef OLD_TTY_DRIVER + if (ioctl(2, TIOCGETD, (char *)&ldisc) < 0 || ldisc != NTTYDISC) { + out2str("sh: need new tty driver to run job control; job control turned off\n"); + mflag = 0; + return; + } +#endif + setsignal(SIGTSTP); + setsignal(SIGTTOU); + setsignal(SIGTTIN); + setpgrp(0, rootpid); + ioctl(2, TIOCSPGRP, (char *)&rootpid); + } else { /* turning job control off */ + setpgrp(0, initialpgrp); + ioctl(2, TIOCSPGRP, (char *)&initialpgrp); + setsignal(SIGTSTP); + setsignal(SIGTTOU); + setsignal(SIGTTIN); + } + jobctl = on; +} + + +#ifdef mkinit + +SHELLPROC { + backgndpid = -1; +#if JOBS + jobctl = 0; +#endif +} + +#endif + + + +#if JOBS +fgcmd(argc, argv) char **argv; { + struct job *jp; + int pgrp; + int status; + + jp = getjob(argv[1]); + if (jp->jobctl == 0) + error("job not created under job control"); + pgrp = jp->ps[0].pid; + ioctl(2, TIOCSPGRP, (char *)&pgrp); + restartjob(jp); + INTOFF; + status = waitforjob(jp); + INTON; + return status; +} + + +bgcmd(argc, argv) char **argv; { + struct job *jp; + + do { + jp = getjob(*++argv); + if (jp->jobctl == 0) + error("job not created under job control"); + restartjob(jp); + } while (--argc > 1); + return 0; +} + + +STATIC void +restartjob(jp) + struct job *jp; + { + struct procstat *ps; + int i; + + if (jp->state == JOBDONE) + return; + INTOFF; + killpg(jp->ps[0].pid, SIGCONT); + for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) { + if ((ps->status & 0377) == 0177) { + ps->status = -1; + jp->state = 0; + } + } + INTON; +} +#endif + + +int +jobscmd(argc, argv) char **argv; { + showjobs(0); + return 0; +} + + +/* + * Print a list of jobs. If "change" is nonzero, only print jobs whose + * statuses have changed since the last call to showjobs. + * + * If the shell is interrupted in the process of creating a job, the + * result may be a job structure containing zero processes. Such structures + * will be freed here. + */ + +void +showjobs(change) { + int jobno; + int procno; + int i; + struct job *jp; + struct procstat *ps; + int col; + char s[64]; + + TRACE(("showjobs(%d) called\n", change)); + while (dowait(0, (struct job *)NULL) > 0); + for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) { + if (! jp->used) + continue; + if (jp->nprocs == 0) { + freejob(jp); + continue; + } + if (change && ! jp->changed) + continue; + procno = jp->nprocs; + for (ps = jp->ps ; ; ps++) { /* for each process */ + if (ps == jp->ps) + fmtstr(s, 64, "[%d] %d ", jobno, ps->pid); + else + fmtstr(s, 64, " %d ", ps->pid); + out1str(s); + col = strlen(s); + s[0] = '\0'; + if (ps->status == -1) { + /* don't print anything */ + } else if ((ps->status & 0xFF) == 0) { + fmtstr(s, 64, "Exit %d", ps->status >> 8); + } else { + i = ps->status; +#if JOBS + if ((i & 0xFF) == 0177) + i >>= 8; +#endif + if ((i & 0x7F) <= MAXSIG && sigmesg[i & 0x7F]) + scopy(sigmesg[i & 0x7F], s); + else + fmtstr(s, 64, "Signal %d", i & 0x7F); + if (i & 0x80) + strcat(s, " (core dumped)"); + } + out1str(s); + col += strlen(s); + do { + out1c(' '); + col++; + } while (col < 30); + out1str(ps->cmd); + out1c('\n'); + if (--procno <= 0) + break; + } + jp->changed = 0; + if (jp->state == JOBDONE) { + freejob(jp); + } + } +} + + +/* + * Mark a job structure as unused. + */ + +STATIC void +freejob(jp) + struct job *jp; + { + struct procstat *ps; + int i; + + INTOFF; + for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) { + if (ps->cmd != nullstr) + ckfree(ps->cmd); + } + if (jp->ps != &jp->ps0) + ckfree(jp->ps); + jp->used = 0; +#if JOBS + if (curjob == jp - jobtab + 1) + curjob = 0; +#endif + INTON; +} + + + +int +waitcmd(argc, argv) char **argv; { + struct job *job; + int status; + struct job *jp; + + if (argc > 1) { + job = getjob(argv[1]); + } else { + job = NULL; + } + for (;;) { /* loop until process terminated or stopped */ + if (job != NULL) { + if (job->state) { + status = job->ps[job->nprocs - 1].status; + if ((status & 0xFF) == 0) + status = status >> 8 & 0xFF; +#if JOBS + else if ((status & 0xFF) == 0177) + status = (status >> 8 & 0x7F) + 128; +#endif + else + status = (status & 0x7F) + 128; + if (! iflag) + freejob(job); + return status; + } + } else { + for (jp = jobtab ; ; jp++) { + if (jp >= jobtab + njobs) { /* no running procs */ + return 0; + } + if (jp->used && jp->state == 0) + break; + } + } + dowait(1, (struct job *)NULL); + } +} + + + +jobidcmd(argc, argv) char **argv; { + struct job *jp; + int i; + + jp = getjob(argv[1]); + for (i = 0 ; i < jp->nprocs ; ) { + out1fmt("%d", jp->ps[i].pid); + out1c(++i < jp->nprocs? ' ' : '\n'); + } + return 0; +} + + + +/* + * Convert a job name to a job structure. + */ + +STATIC struct job * +getjob(name) + char *name; + { + int jobno; + register struct job *jp; + int pid; + int i; + + if (name == NULL) { +#if JOBS +currentjob: + if ((jobno = curjob) == 0 || jobtab[jobno - 1].used == 0) + error("No current job"); + return &jobtab[jobno - 1]; +#else + error("No current job"); +#endif + } else if (name[0] == '%') { + if (is_digit(name[1])) { + jobno = number(name + 1); + if (jobno > 0 && jobno <= njobs + && jobtab[jobno - 1].used != 0) + return &jobtab[jobno - 1]; +#if JOBS + } else if (name[1] == '%' && name[2] == '\0') { + goto currentjob; +#endif + } else { + register struct job *found = NULL; + for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { + if (jp->used && jp->nprocs > 0 + && prefix(name + 1, jp->ps[0].cmd)) { + if (found) + error("%s: ambiguous", name); + found = jp; + } + } + if (found) + return found; + } + } else if (is_number(name)) { + pid = number(name); + for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { + if (jp->used && jp->nprocs > 0 + && jp->ps[jp->nprocs - 1].pid == pid) + return jp; + } + } + error("No such job: %s", name); +} + + + +/* + * Return a new job structure, + */ + +struct job * +makejob(node, nprocs) + union node *node; + { + int i; + struct job *jp; + + for (i = njobs, jp = jobtab ; ; jp++) { + if (--i < 0) { + INTOFF; + if (njobs == 0) { + jobtab = ckmalloc(4 * sizeof jobtab[0]); + } else { + jp = ckmalloc((njobs + 4) * sizeof jobtab[0]); + bcopy(jobtab, jp, njobs * sizeof jp[0]); + ckfree(jobtab); + jobtab = jp; + } + jp = jobtab + njobs; + for (i = 4 ; --i >= 0 ; jobtab[njobs++].used = 0); + INTON; + break; + } + if (jp->used == 0) + break; + } + INTOFF; + jp->state = 0; + jp->used = 1; + jp->changed = 0; + jp->nprocs = 0; +#if JOBS + jp->jobctl = jobctl; +#endif + if (nprocs > 1) { + jp->ps = ckmalloc(nprocs * sizeof (struct procstat)); + } else { + jp->ps = &jp->ps0; + } + INTON; + TRACE(("makejob(0x%x, %d) returns %%%d\n", (int)node, nprocs, jp - jobtab + 1)); + return jp; +} + + +/* + * Fork of a subshell. If we are doing job control, give the subshell its + * own process group. Jp is a job structure that the job is to be added to. + * N is the command that will be evaluated by the child. Both jp and n may + * be NULL. The mode parameter can be one of the following: + * FORK_FG - Fork off a foreground process. + * FORK_BG - Fork off a background process. + * FORK_NOJOB - Like FORK_FG, but don't give the process its own + * process group even if job control is on. + * + * When job control is turned off, background processes have their standard + * input redirected to /dev/null (except for the second and later processes + * in a pipeline). + */ + +int +forkshell(jp, n, mode) + union node *n; + struct job *jp; + { + int pid; + int pgrp; + + TRACE(("forkshell(%%%d, 0x%x, %d) called\n", jp - jobtab, (int)n, mode)); + INTOFF; + pid = fork(); + if (pid == -1) { + TRACE(("Fork failed, errno=%d\n", errno)); + INTON; + error("Cannot fork"); + } + if (pid == 0) { + struct job *p; + int wasroot; + int i; + + TRACE(("Child shell %d\n", getpid())); + wasroot = rootshell; + rootshell = 0; + for (i = njobs, p = jobtab ; --i >= 0 ; p++) + if (p->used) + freejob(p); + closescript(); + INTON; + clear_traps(); +#if JOBS + jobctl = 0; /* do job control only in root shell */ + if (wasroot && mode != FORK_NOJOB && mflag) { + if (jp == NULL || jp->nprocs == 0) + pgrp = getpid(); + else + pgrp = jp->ps[0].pid; + setpgrp(0, pgrp); + if (mode == FORK_FG) { + /*** this causes superfluous TIOCSPGRPS ***/ + if (ioctl(2, TIOCSPGRP, (char *)&pgrp) < 0) + error("TIOCSPGRP failed, errno=%d\n", errno); + } + setsignal(SIGTSTP); + setsignal(SIGTTOU); + } else if (mode == FORK_BG) { + ignoresig(SIGINT); + ignoresig(SIGQUIT); + if ((jp == NULL || jp->nprocs == 0) && + ! fd0_redirected_p ()) { + close(0); + if (open("/dev/null", O_RDONLY) != 0) + error("Can't open /dev/null"); + } + } +#else + if (mode == FORK_BG) { + ignoresig(SIGINT); + ignoresig(SIGQUIT); + if ((jp == NULL || jp->nprocs == 0) && + ! fd0_redirected_p ()) { + close(0); + if (open("/dev/null", O_RDONLY) != 0) + error("Can't open /dev/null"); + } + } +#endif + if (wasroot && iflag) { + setsignal(SIGINT); + setsignal(SIGQUIT); + setsignal(SIGTERM); + } + return pid; + } + if (rootshell && mode != FORK_NOJOB && mflag) { + if (jp == NULL || jp->nprocs == 0) + pgrp = pid; + else + pgrp = jp->ps[0].pid; + setpgrp(pid, pgrp); + } + if (mode == FORK_BG) + backgndpid = pid; /* set $! */ + if (jp) { + struct procstat *ps = &jp->ps[jp->nprocs++]; + ps->pid = pid; + ps->status = -1; + ps->cmd = nullstr; + if (iflag && rootshell && n) + ps->cmd = commandtext(n); + } + INTON; + TRACE(("In parent shell: child = %d\n", pid)); + return pid; +} + + + +/* + * Wait for job to finish. + * + * Under job control we have the problem that while a child process is + * running interrupts generated by the user are sent to the child but not + * to the shell. This means that an infinite loop started by an inter- + * active user may be hard to kill. With job control turned off, an + * interactive user may place an interactive program inside a loop. If + * the interactive program catches interrupts, the user doesn't want + * these interrupts to also abort the loop. The approach we take here + * is to have the shell ignore interrupt signals while waiting for a + * forground process to terminate, and then send itself an interrupt + * signal if the child process was terminated by an interrupt signal. + * Unfortunately, some programs want to do a bit of cleanup and then + * exit on interrupt; unless these processes terminate themselves by + * sending a signal to themselves (instead of calling exit) they will + * confuse this approach. + */ + +int +waitforjob(jp) + register struct job *jp; + { +#if JOBS + int mypgrp = getpgrp(0); +#endif + int status; + int st; + + INTOFF; + TRACE(("waitforjob(%%%d) called\n", jp - jobtab + 1)); + while (jp->state == 0) { + dowait(1, jp); + } +#if JOBS + if (jp->jobctl) { + if (ioctl(2, TIOCSPGRP, (char *)&mypgrp) < 0) + error("TIOCSPGRP failed, errno=%d\n", errno); + } + if (jp->state == JOBSTOPPED) + curjob = jp - jobtab + 1; +#endif + status = jp->ps[jp->nprocs - 1].status; + /* convert to 8 bits */ + if ((status & 0xFF) == 0) + st = status >> 8 & 0xFF; +#if JOBS + else if ((status & 0xFF) == 0177) + st = (status >> 8 & 0x7F) + 128; +#endif + else + st = (status & 0x7F) + 128; + if (! JOBS || jp->state == JOBDONE) + freejob(jp); + CLEAR_PENDING_INT; + if ((status & 0x7F) == SIGINT) + kill(getpid(), SIGINT); + INTON; + return st; +} + + + +/* + * Wait for a process to terminate. + */ + +STATIC int +dowait(block, job) + struct job *job; + { + int pid; + int status; + struct procstat *sp; + struct job *jp; + struct job *thisjob; + int done; + int stopped; + int core; + + TRACE(("dowait(%d) called\n", block)); + do { + pid = waitproc(block, &status); + TRACE(("wait returns %d, status=%d\n", pid, status)); + } while (pid == -1 && errno == EINTR); + if (pid <= 0) + return pid; + INTOFF; + thisjob = NULL; + for (jp = jobtab ; jp < jobtab + njobs ; jp++) { + if (jp->used) { + done = 1; + stopped = 1; + for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) { + if (sp->pid == -1) + continue; + if (sp->pid == pid) { + TRACE(("Changin status of proc %d from 0x%x to 0x%x\n", pid, sp->status, status)); + sp->status = status; + thisjob = jp; + } + if (sp->status == -1) + stopped = 0; + else if ((sp->status & 0377) == 0177) + done = 0; + } + if (stopped) { /* stopped or done */ + int state = done? JOBDONE : JOBSTOPPED; + if (jp->state != state) { + TRACE(("Job %d: changing state from %d to %d\n", jp - jobtab + 1, jp->state, state)); + jp->state = state; +#if JOBS + if (done && curjob == jp - jobtab + 1) + curjob = 0; /* no current job */ +#endif + } + } + } + } + INTON; + if (! rootshell || ! iflag || (job && thisjob == job)) { +#if JOBS + if ((status & 0xFF) == 0177) + status >>= 8; +#endif + core = status & 0x80; + status &= 0x7F; + if (status != 0 && status != SIGINT && status != SIGPIPE) { + if (thisjob != job) + outfmt(out2, "%d: ", pid); +#if JOBS + if (status == SIGTSTP && rootshell && iflag) + outfmt(out2, "%%%d ", job - jobtab + 1); +#endif + if (status <= MAXSIG && sigmesg[status]) + out2str(sigmesg[status]); + else + outfmt(out2, "Signal %d", status); + if (core) + out2str(" - core dumped"); + out2c('\n'); + flushout(&errout); + } else { + TRACE(("Not printing status: status=%d\n", status)); + } + } else { + TRACE(("Not printing status, rootshell=%d, job=0x%x\n", rootshell, job)); + if (thisjob) + thisjob->changed = 1; + } + return pid; +} + + + +/* + * Do a wait system call. If job control is compiled in, we accept + * stopped processes. If block is zero, we return a value of zero + * rather than blocking. + * + * System V doesn't have a non-blocking wait system call. It does + * have a SIGCLD signal that is sent to a process when one of it's + * children dies. The obvious way to use SIGCLD would be to install + * a handler for SIGCLD which simply bumped a counter when a SIGCLD + * was received, and have waitproc bump another counter when it got + * the status of a process. Waitproc would then know that a wait + * system call would not block if the two counters were different. + * This approach doesn't work because if a process has children that + * have not been waited for, System V will send it a SIGCLD when it + * installs a signal handler for SIGCLD. What this means is that when + * a child exits, the shell will be sent SIGCLD signals continuously + * until is runs out of stack space, unless it does a wait call before + * restoring the signal handler. The code below takes advantage of + * this (mis)feature by installing a signal handler for SIGCLD and + * then checking to see whether it was called. If there are any + * children to be waited for, it will be. + * + * If neither SYSV nor BSD is defined, we don't implement nonblocking + * waits at all. In this case, the user will not be informed when + * a background process until the next time she runs a real program + * (as opposed to running a builtin command or just typing return), + * and the jobs command may give out of date information. + */ + +#ifdef SYSV +STATIC int gotsigchild; + +STATIC int onsigchild() { + gotsigchild = 1; +} +#endif + + +STATIC int +waitproc(block, status) + int *status; + { +#ifdef BSD + int flags; + +#if JOBS + flags = WUNTRACED; +#else + flags = 0; +#endif + if (block == 0) + flags |= WNOHANG; + return wait3(status, flags, (struct rusage *)NULL); +#else +#ifdef SYSV + int (*save)(); + + if (block == 0) { + gotsigchild = 0; + save = signal(SIGCLD, onsigchild); + signal(SIGCLD, save); + if (gotsigchild == 0) + return 0; + } + return wait(status); +#else + if (block == 0) + return 0; + return wait(status); +#endif +#endif +} + +/* + * return 1 if there are stopped jobs, otherwise 0 + */ +int job_warning = 0; +int +stoppedjobs() +{ + register int jobno; + register struct job *jp; + + if (job_warning) + return (0); + for (jobno = 1, jp = jobtab; jobno <= njobs; jobno++, jp++) { + if (jp->used == 0) + continue; + if (jp->state == JOBSTOPPED) { + out2str("You have stopped jobs.\n"); + job_warning = 2; + return (1); + } + } + + return (0); +} + +/* + * Return a string identifying a command (to be printed by the + * jobs command. + */ + +STATIC char *cmdnextc; +STATIC int cmdnleft; +STATIC void cmdtxt(), cmdputs(); +#define MAXCMDTEXT 200 + +char * +commandtext(n) + union node *n; + { + char *name; + + cmdnextc = name = ckmalloc(MAXCMDTEXT); + cmdnleft = MAXCMDTEXT - 4; + cmdtxt(n); + *cmdnextc = '\0'; + return name; +} + + +STATIC void +cmdtxt(n) + union node *n; + { + union node *np; + struct nodelist *lp; + char *p; + int i; + char s[2]; + + if (n == NULL) + return; + switch (n->type) { + case NSEMI: + cmdtxt(n->nbinary.ch1); + cmdputs("; "); + cmdtxt(n->nbinary.ch2); + break; + case NAND: + cmdtxt(n->nbinary.ch1); + cmdputs(" && "); + cmdtxt(n->nbinary.ch2); + break; + case NOR: + cmdtxt(n->nbinary.ch1); + cmdputs(" || "); + cmdtxt(n->nbinary.ch2); + break; + case NPIPE: + for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { + cmdtxt(lp->n); + if (lp->next) + cmdputs(" | "); + } + break; + case NSUBSHELL: + cmdputs("("); + cmdtxt(n->nredir.n); + cmdputs(")"); + break; + case NREDIR: + case NBACKGND: + cmdtxt(n->nredir.n); + break; + case NIF: + cmdputs("if "); + cmdtxt(n->nif.test); + cmdputs("; then "); + cmdtxt(n->nif.ifpart); + cmdputs("..."); + break; + case NWHILE: + cmdputs("while "); + goto until; + case NUNTIL: + cmdputs("until "); +until: + cmdtxt(n->nbinary.ch1); + cmdputs("; do "); + cmdtxt(n->nbinary.ch2); + cmdputs("; done"); + break; + case NFOR: + cmdputs("for "); + cmdputs(n->nfor.var); + cmdputs(" in ..."); + break; + case NCASE: + cmdputs("case "); + cmdputs(n->ncase.expr->narg.text); + cmdputs(" in ..."); + break; + case NDEFUN: + cmdputs(n->narg.text); + cmdputs("() ..."); + break; + case NCMD: + for (np = n->ncmd.args ; np ; np = np->narg.next) { + cmdtxt(np); + if (np->narg.next) + cmdputs(" "); + } + for (np = n->ncmd.redirect ; np ; np = np->nfile.next) { + cmdputs(" "); + cmdtxt(np); + } + break; + case NARG: + cmdputs(n->narg.text); + break; + case NTO: + p = ">"; i = 1; goto redir; + case NAPPEND: + p = ">>"; i = 1; goto redir; + case NTOFD: + p = ">&"; i = 1; goto redir; + case NFROM: + p = "<"; i = 0; goto redir; + case NFROMFD: + p = "<&"; i = 0; goto redir; +redir: + if (n->nfile.fd != i) { + s[0] = n->nfile.fd + '0'; + s[1] = '\0'; + cmdputs(s); + } + cmdputs(p); + if (n->type == NTOFD || n->type == NFROMFD) { + s[0] = n->ndup.dupfd + '0'; + s[1] = '\0'; + cmdputs(s); + } else { + cmdtxt(n->nfile.fname); + } + break; + case NHERE: + case NXHERE: + cmdputs("<<..."); + break; + default: + cmdputs("???"); + break; + } +} + + + +STATIC void +cmdputs(s) + char *s; + { + register char *p, *q; + register char c; + int subtype = 0; + + if (cmdnleft <= 0) + return; + p = s; + q = cmdnextc; + while ((c = *p++) != '\0') { + if (c == CTLESC) + *q++ = *p++; + else if (c == CTLVAR) { + *q++ = '$'; + if (--cmdnleft > 0) + *q++ = '{'; + subtype = *p++; + } else if (c == '=' && subtype != 0) { + *q++ = "}-+?="[(subtype & VSTYPE) - VSNORMAL]; + subtype = 0; + } else if (c == CTLENDVAR) { + *q++ = '}'; + } else if (c == CTLBACKQ | c == CTLBACKQ+CTLQUOTE) + cmdnleft++; /* ignore it */ + else + *q++ = c; + if (--cmdnleft <= 0) { + *q++ = '.'; + *q++ = '.'; + *q++ = '.'; + break; + } + } + cmdnextc = q; +} diff --git a/bin/sh/jobs.h b/bin/sh/jobs.h new file mode 100644 index 0000000..2464850 --- /dev/null +++ b/bin/sh/jobs.h @@ -0,0 +1,99 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)jobs.h 8.1 (Berkeley) 5/31/93 + */ + +/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ +#define FORK_FG 0 +#define FORK_BG 1 +#define FORK_NOJOB 2 + + +/* + * A job structure contains information about a job. A job is either a + * single process or a set of processes contained in a pipeline. In the + * latter case, pidlist will be non-NULL, and will point to a -1 terminated + * array of pids. + */ + +struct procstat { + short pid; /* process id */ + short status; /* status flags (defined above) */ + char *cmd; /* text of command being run */ +}; + + +/* states */ +#define JOBSTOPPED 1 /* all procs are stopped */ +#define JOBDONE 2 /* all procs are completed */ + + +struct job { + struct procstat ps0; /* status of process */ + struct procstat *ps; /* status or processes when more than one */ + short nprocs; /* number of processes */ + short pgrp; /* process group of this job */ + char state; /* true if job is finished */ + char used; /* true if this entry is in used */ + char changed; /* true if status has changed */ +#if JOBS + char jobctl; /* job running under job control */ +#endif +}; + +extern short backgndpid; /* pid of last background process */ +extern int job_warning; /* user was warned about stopped jobs */ + + +#ifdef __STDC__ +void setjobctl(int); +void showjobs(int); +struct job *makejob(union node *, int); +int forkshell(struct job *, union node *, int); +int waitforjob(struct job *); +char *commandtext(union node *); +#else +void setjobctl(); +void showjobs(); +struct job *makejob(); +int forkshell(); +int waitforjob(); +char *commandtext(); +#endif + +#if ! JOBS +#define setjobctl(on) /* do nothing */ +#endif diff --git a/bin/sh/machdep.h b/bin/sh/machdep.h new file mode 100644 index 0000000..1d50dfb --- /dev/null +++ b/bin/sh/machdep.h @@ -0,0 +1,51 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)machdep.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * Most machines require the value returned from malloc to be aligned + * in some way. The following macro will get this right on many machines. + */ + +#ifndef ALIGN +union align { + int i; + char *cp; +}; + +#define ALIGN(nbytes) ((nbytes) + sizeof(union align) - 1 &~ (sizeof(union align) - 1)) +#endif diff --git a/bin/sh/mail.c b/bin/sh/mail.c new file mode 100644 index 0000000..0f74ad8 --- /dev/null +++ b/bin/sh/mail.c @@ -0,0 +1,114 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)mail.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +/* + * Routines to check for mail. (Perhaps make part of main.c?) + */ + +#include "shell.h" +#include "exec.h" /* defines padvance() */ +#include "var.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include <sys/types.h> +#include <sys/stat.h> + + +#define MAXMBOXES 10 + + +STATIC int nmboxes; /* number of mailboxes */ +STATIC time_t mailtime[MAXMBOXES]; /* times of mailboxes */ + + + +/* + * Print appropriate message(s) if mail has arrived. If the argument is + * nozero, then the value of MAIL has changed, so we just update the + * values. + */ + +void +chkmail(silent) { + register int i; + char *mpath; + char *p; + register char *q; + struct stackmark smark; + struct stat statb; + + if (silent) + nmboxes = 10; + if (nmboxes == 0) + return; + setstackmark(&smark); + mpath = mpathset()? mpathval() : mailval(); + for (i = 0 ; i < nmboxes ; i++) { + p = padvance(&mpath, nullstr); + if (p == NULL) + break; + if (*p == '\0') + continue; + for (q = p ; *q ; q++); + if (q[-1] != '/') + abort(); + q[-1] = '\0'; /* delete trailing '/' */ +#ifdef notdef /* this is what the System V shell claims to do (it lies) */ + if (stat(p, &statb) < 0) + statb.st_mtime = 0; + if (statb.st_mtime > mailtime[i] && ! silent) { + out2str(pathopt? pathopt : "you have mail"); + out2c('\n'); + } + mailtime[i] = statb.st_mtime; +#else /* this is what it should do */ + if (stat(p, &statb) < 0) + statb.st_size = 0; + if (statb.st_size > mailtime[i] && ! silent) { + out2str(pathopt? pathopt : "you have mail"); + out2c('\n'); + } + mailtime[i] = statb.st_size; +#endif + } + nmboxes = i; + popstackmark(&smark); +} diff --git a/bin/sh/mail.h b/bin/sh/mail.h new file mode 100644 index 0000000..d859e53 --- /dev/null +++ b/bin/sh/mail.h @@ -0,0 +1,43 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mail.h 8.1 (Berkeley) 5/31/93 + */ + +#ifdef __STDC__ +void chkmail(int); +#else +void chkmail(); +#endif diff --git a/bin/sh/main.c b/bin/sh/main.c new file mode 100644 index 0000000..dab9e64 --- /dev/null +++ b/bin/sh/main.c @@ -0,0 +1,304 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include <signal.h> +#include <fcntl.h> +#include "shell.h" +#include "main.h" +#include "mail.h" +#include "options.h" +#include "output.h" +#include "parser.h" +#include "nodes.h" +#include "eval.h" +#include "jobs.h" +#include "input.h" +#include "trap.h" +#include "var.h" +#include "memalloc.h" +#include "error.h" +#include "init.h" +#include "mystring.h" + +#define PROFILE 0 + +int rootpid; +int rootshell; +STATIC union node *curcmd; +STATIC union node *prevcmd; +extern int errno; +#if PROFILE +short profile_buf[16384]; +extern int etext(); +#endif + +#ifdef __STDC__ +STATIC void read_profile(char *); +char *getenv(char *); +#else +STATIC void read_profile(); +char *getenv(); +#endif + + +/* + * Main routine. We initialize things, parse the arguments, execute + * profiles if we're a login shell, and then call cmdloop to execute + * commands. The setjmp call sets up the location to jump to when an + * exception occurs. When an exception occurs the variable "state" + * is used to figure out how far we had gotten. + */ + +main(argc, argv) char **argv; { + struct jmploc jmploc; + struct stackmark smark; + volatile int state; + char *shinit; + +#if PROFILE + monitor(4, etext, profile_buf, sizeof profile_buf, 50); +#endif + state = 0; + if (setjmp(jmploc.loc)) { + /* + * When a shell procedure is executed, we raise the + * exception EXSHELLPROC to clean up before executing + * the shell procedure. + */ + if (exception == EXSHELLPROC) { + rootpid = getpid(); + rootshell = 1; + minusc = NULL; + state = 3; + } else if (state == 0 || iflag == 0 || ! rootshell) + exitshell(2); + reset(); + if (exception == EXINT +#if ATTY + && (! attyset() || equal(termval(), "emacs")) +#endif + ) { + out2c('\n'); + flushout(&errout); + } + popstackmark(&smark); + FORCEINTON; /* enable interrupts */ + if (state == 1) + goto state1; + else if (state == 2) + goto state2; + else if (state == 3) + goto state3; + else + goto state4; + } + handler = &jmploc; +#ifdef DEBUG + opentrace(); + trputs("Shell args: "); trargs(argv); +#endif + rootpid = getpid(); + rootshell = 1; + init(); + setstackmark(&smark); + procargs(argc, argv); + if (argv[0] && argv[0][0] == '-') { + state = 1; + read_profile("/etc/profile"); +state1: + state = 2; + read_profile(".profile"); + } +state2: + state = 3; + if ((shinit = lookupvar("ENV")) != NULL && + *shinit != '\0') { + state = 3; + read_profile(shinit); + } +state3: + state = 4; + if (minusc) { + evalstring(minusc); + } + if (sflag || minusc == NULL) { +state4: /* XXX ??? - why isn't this before the "if" statement */ + cmdloop(1); + } +#if PROFILE + monitor(0); +#endif + exitshell(exitstatus); +} + + +/* + * Read and execute commands. "Top" is nonzero for the top level command + * loop; it turns on prompting if the shell is interactive. + */ + +void +cmdloop(top) { + union node *n; + struct stackmark smark; + int inter; + int numeof = 0; + + TRACE(("cmdloop(%d) called\n", top)); + setstackmark(&smark); + for (;;) { + if (pendingsigs) + dotrap(); + inter = 0; + if (iflag && top) { + inter++; + showjobs(1); + chkmail(0); + flushout(&output); + } + n = parsecmd(inter); + /* showtree(n); DEBUG */ + if (n == NEOF) { + if (!top || numeof >= 50) + break; + if (!stoppedjobs()) { + if (!Iflag) + break; + out2str("\nUse \"exit\" to leave shell.\n"); + } + numeof++; + } else if (n != NULL && nflag == 0) { + job_warning = (job_warning == 2) ? 1 : 0; + numeof = 0; + evaltree(n, 0); + } + popstackmark(&smark); + } + popstackmark(&smark); /* unnecessary */ +} + + + +/* + * Read /etc/profile or .profile. Return on error. + */ + +STATIC void +read_profile(name) + char *name; + { + int fd; + + INTOFF; + if ((fd = open(name, O_RDONLY)) >= 0) + setinputfd(fd, 1); + INTON; + if (fd < 0) + return; + cmdloop(0); + popfile(); +} + + + +/* + * Read a file containing shell functions. + */ + +void +readcmdfile(name) + char *name; + { + int fd; + + INTOFF; + if ((fd = open(name, O_RDONLY)) >= 0) + setinputfd(fd, 1); + else + error("Can't open %s", name); + INTON; + cmdloop(0); + popfile(); +} + + + +/* + * Take commands from a file. To be compatable we should do a path + * search for the file, but a path search doesn't make any sense. + */ + +dotcmd(argc, argv) char **argv; { + exitstatus = 0; + if (argc >= 2) { /* That's what SVR2 does */ + setinputfile(argv[1], 1); + commandname = argv[1]; + cmdloop(0); + popfile(); + } + return exitstatus; +} + + +exitcmd(argc, argv) char **argv; { + if (stoppedjobs()) + return; + if (argc > 1) + exitstatus = number(argv[1]); + exitshell(exitstatus); +} + + +#ifdef notdef +/* + * Should never be called. + */ + +void +exit(exitstatus) { + _exit(exitstatus); +} +#endif diff --git a/bin/sh/main.h b/bin/sh/main.h new file mode 100644 index 0000000..b403b2c --- /dev/null +++ b/bin/sh/main.h @@ -0,0 +1,48 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)main.h 8.1 (Berkeley) 5/31/93 + */ + +extern int rootpid; /* pid of main shell */ +extern int rootshell; /* true if we aren't a child of the main shell */ + +#ifdef __STDC__ +void readcmdfile(char *); +void cmdloop(int); +#else +void readcmdfile(); +void cmdloop(); +#endif diff --git a/bin/sh/memalloc.c b/bin/sh/memalloc.c new file mode 100644 index 0000000..5fa246a --- /dev/null +++ b/bin/sh/memalloc.c @@ -0,0 +1,292 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)memalloc.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include "shell.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "machdep.h" +#include "mystring.h" + +/* + * Like malloc, but returns an error when out of space. + */ + +pointer +ckmalloc(nbytes) { + register pointer p; + pointer malloc(); + + if ((p = malloc(nbytes)) == NULL) + error("Out of space"); + return p; +} + + +/* + * Same for realloc. + */ + +pointer +ckrealloc(p, nbytes) + register pointer p; + { + pointer realloc(); + + if ((p = realloc(p, nbytes)) == NULL) + error("Out of space"); + return p; +} + + +/* + * Make a copy of a string in safe storage. + */ + +char * +savestr(s) + char *s; + { + register char *p; + + p = ckmalloc(strlen(s) + 1); + scopy(s, p); + return p; +} + + +/* + * Parse trees for commands are allocated in lifo order, so we use a stack + * to make this more efficient, and also to avoid all sorts of exception + * handling code to handle interrupts in the middle of a parse. + * + * The size 504 was chosen because the Ultrix malloc handles that size + * well. + */ + +#define MINSIZE 504 /* minimum size of a block */ + + +struct stack_block { + struct stack_block *prev; + char space[MINSIZE]; +}; + +struct stack_block stackbase; +struct stack_block *stackp = &stackbase; +char *stacknxt = stackbase.space; +int stacknleft = MINSIZE; +int sstrnleft; +int herefd = -1; + + + +pointer +stalloc(nbytes) { + register char *p; + + nbytes = ALIGN(nbytes); + if (nbytes > stacknleft) { + int blocksize; + struct stack_block *sp; + + blocksize = nbytes; + if (blocksize < MINSIZE) + blocksize = MINSIZE; + INTOFF; + sp = ckmalloc(sizeof(struct stack_block) - MINSIZE + blocksize); + sp->prev = stackp; + stacknxt = sp->space; + stacknleft = blocksize; + stackp = sp; + INTON; + } + p = stacknxt; + stacknxt += nbytes; + stacknleft -= nbytes; + return p; +} + + +void +stunalloc(p) + pointer p; + { + if (p == NULL) { /*DEBUG */ + write(2, "stunalloc\n", 10); + abort(); + } + stacknleft += stacknxt - (char *)p; + stacknxt = p; +} + + + +void +setstackmark(mark) + struct stackmark *mark; + { + mark->stackp = stackp; + mark->stacknxt = stacknxt; + mark->stacknleft = stacknleft; +} + + +void +popstackmark(mark) + struct stackmark *mark; + { + struct stack_block *sp; + + INTOFF; + while (stackp != mark->stackp) { + sp = stackp; + stackp = sp->prev; + ckfree(sp); + } + stacknxt = mark->stacknxt; + stacknleft = mark->stacknleft; + INTON; +} + + +/* + * When the parser reads in a string, it wants to stick the string on the + * stack and only adjust the stack pointer when it knows how big the + * string is. Stackblock (defined in stack.h) returns a pointer to a block + * of space on top of the stack and stackblocklen returns the length of + * this block. Growstackblock will grow this space by at least one byte, + * possibly moving it (like realloc). Grabstackblock actually allocates the + * part of the block that has been used. + */ + +void +growstackblock() { + char *p; + int newlen = stacknleft * 2 + 100; + char *oldspace = stacknxt; + int oldlen = stacknleft; + struct stack_block *sp; + + if (stacknxt == stackp->space && stackp != &stackbase) { + INTOFF; + sp = stackp; + stackp = sp->prev; + sp = ckrealloc((pointer)sp, sizeof(struct stack_block) - MINSIZE + newlen); + sp->prev = stackp; + stackp = sp; + stacknxt = sp->space; + stacknleft = newlen; + INTON; + } else { + p = stalloc(newlen); + bcopy(oldspace, p, oldlen); + stacknxt = p; /* free the space */ + stacknleft += newlen; /* we just allocated */ + } +} + + + +void +grabstackblock(len) { + len = ALIGN(len); + stacknxt += len; + stacknleft -= len; +} + + + +/* + * The following routines are somewhat easier to use that the above. + * The user declares a variable of type STACKSTR, which may be declared + * to be a register. The macro STARTSTACKSTR initializes things. Then + * the user uses the macro STPUTC to add characters to the string. In + * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is + * grown as necessary. When the user is done, she can just leave the + * string there and refer to it using stackblock(). Or she can allocate + * the space for it using grabstackstr(). If it is necessary to allow + * someone else to use the stack temporarily and then continue to grow + * the string, the user should use grabstack to allocate the space, and + * then call ungrabstr(p) to return to the previous mode of operation. + * + * USTPUTC is like STPUTC except that it doesn't check for overflow. + * CHECKSTACKSPACE can be called before USTPUTC to ensure that there + * is space for at least one character. + */ + + +char * +growstackstr() { + int len = stackblocksize(); + if (herefd >= 0 && len >= 1024) { + xwrite(herefd, stackblock(), len); + sstrnleft = len - 1; + return stackblock(); + } + growstackblock(); + sstrnleft = stackblocksize() - len - 1; + return stackblock() + len; +} + + +/* + * Called from CHECKSTRSPACE. + */ + +char * +makestrspace() { + int len = stackblocksize() - sstrnleft; + growstackblock(); + sstrnleft = stackblocksize() - len; + return stackblock() + len; +} + + + +void +ungrabstackstr(s, p) + char *s; + char *p; + { + stacknleft += stacknxt - s; + stacknxt = s; + sstrnleft = stacknleft - (p - s); +} diff --git a/bin/sh/memalloc.h b/bin/sh/memalloc.h new file mode 100644 index 0000000..3f2c800 --- /dev/null +++ b/bin/sh/memalloc.h @@ -0,0 +1,95 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)memalloc.h 8.1 (Berkeley) 5/31/93 + */ + +struct stackmark { + struct stack_block *stackp; + char *stacknxt; + int stacknleft; +}; + + +extern char *stacknxt; +extern int stacknleft; +extern int sstrnleft; +extern int herefd; + +#ifdef __STDC__ +pointer ckmalloc(int); +pointer ckrealloc(pointer, int); +void free(pointer); /* defined in C library */ +char *savestr(char *); +pointer stalloc(int); +void stunalloc(pointer); +void setstackmark(struct stackmark *); +void popstackmark(struct stackmark *); +void growstackblock(void); +void grabstackblock(int); +char *growstackstr(void); +char *makestrspace(void); +void ungrabstackstr(char *, char *); +#else +pointer ckmalloc(); +pointer ckrealloc(); +void free(); /* defined in C library */ +char *savestr(); +pointer stalloc(); +void stunalloc(); +void setstackmark(); +void popstackmark(); +void growstackblock(); +void grabstackblock(); +char *growstackstr(); +char *makestrspace(); +void ungrabstackstr(); +#endif + + + +#define stackblock() stacknxt +#define stackblocksize() stacknleft +#define STARTSTACKSTR(p) p = stackblock(), sstrnleft = stackblocksize() +#define STPUTC(c, p) (--sstrnleft >= 0? (*p++ = (c)) : (p = growstackstr(), *p++ = (c))) +#define CHECKSTRSPACE(n, p) if (sstrnleft < n) p = makestrspace(); else +#define USTPUTC(c, p) (--sstrnleft, *p++ = (c)) +#define STACKSTRNUL(p) (sstrnleft == 0? (p = growstackstr(), *p = '\0') : (*p = '\0')) +#define STUNPUTC(p) (++sstrnleft, --p) +#define STTOPC(p) p[-1] +#define STADJUST(amount, p) (p += (amount), sstrnleft -= (amount)) +#define grabstackstr(p) stalloc(stackblocksize() - sstrnleft) + +#define ckfree(p) free((pointer)(p)) diff --git a/bin/sh/miscbltin.c b/bin/sh/miscbltin.c new file mode 100644 index 0000000..4ea3a4c --- /dev/null +++ b/bin/sh/miscbltin.c @@ -0,0 +1,166 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)miscbltin.c 8.2 (Berkeley) 4/16/94"; +#endif /* not lint */ + +/* + * Miscelaneous builtins. + */ + +#include "shell.h" +#include "options.h" +#include "var.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" + +#undef eflag + +extern char **argptr; /* argument list for builtin command */ + + +/* + * The read builtin. The -e option causes backslashes to escape the + * following character. + * + * This uses unbuffered input, which may be avoidable in some cases. + */ + +readcmd(argc, argv) char **argv; { + char **ap; + int backslash; + char c; + int eflag; + char *prompt; + char *ifs; + char *p; + int startword; + int status; + int i; + + eflag = 0; + prompt = NULL; + while ((i = nextopt("ep:")) != '\0') { + if (i == 'p') + prompt = optarg; + else + eflag = 1; + } + if (prompt && isatty(0)) { + out2str(prompt); + flushall(); + } + if (*(ap = argptr) == NULL) + error("arg count"); + if ((ifs = bltinlookup("IFS", 1)) == NULL) + ifs = nullstr; + status = 0; + startword = 1; + backslash = 0; + STARTSTACKSTR(p); + for (;;) { + if (read(0, &c, 1) != 1) { + status = 1; + break; + } + if (c == '\0') + continue; + if (backslash) { + backslash = 0; + if (c != '\n') + STPUTC(c, p); + continue; + } + if (eflag && c == '\\') { + backslash++; + continue; + } + if (c == '\n') + break; + if (startword && *ifs == ' ' && strchr(ifs, c)) { + continue; + } + startword = 0; + if (backslash && c == '\\') { + if (read(0, &c, 1) != 1) { + status = 1; + break; + } + STPUTC(c, p); + } else if (ap[1] != NULL && strchr(ifs, c) != NULL) { + STACKSTRNUL(p); + setvar(*ap, stackblock(), 0); + ap++; + startword = 1; + STARTSTACKSTR(p); + } else { + STPUTC(c, p); + } + } + STACKSTRNUL(p); + setvar(*ap, stackblock(), 0); + while (*++ap != NULL) + setvar(*ap, nullstr, 0); + return status; +} + + + +umaskcmd(argc, argv) char **argv; { + int mask; + char *p; + int i; + + if ((p = argv[1]) == NULL) { + INTOFF; + mask = umask(0); + umask(mask); + INTON; + out1fmt("%.4o\n", mask); /* %#o might be better */ + } else { + mask = 0; + do { + if ((unsigned)(i = *p - '0') >= 8) + error("Illegal number: %s", argv[1]); + mask = (mask << 3) + i; + } while (*++p != '\0'); + umask(mask); + } + return 0; +} diff --git a/bin/sh/mkbuiltins b/bin/sh/mkbuiltins new file mode 100755 index 0000000..48fc572 --- /dev/null +++ b/bin/sh/mkbuiltins @@ -0,0 +1,87 @@ +#!/bin/sh - +# +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)mkbuiltins 8.1 (Berkeley) 5/31/93 + +temp=/tmp/ka$$ +havejobs=0 +if grep '^#define JOBS[ ]*1' shell.h > /dev/null +then havejobs=1 +fi +exec > obj/builtins.c +cat <<\! +/* + * This file was generated by the mkbuiltins program. + */ + +#include "shell.h" +#include "builtins.h" + +! +awk '/^[^#]/ {if('$havejobs' || $2 != "-j") print $0}' builtins | + sed 's/-j//' > $temp +awk '{ printf "int %s();\n", $1}' $temp +echo ' +int (*const builtinfunc[])() = {' +awk '/^[^#]/ { printf "\t%s,\n", $1}' $temp +echo '}; + +const struct builtincmd builtincmd[] = {' +awk '{ for (i = 2 ; i <= NF ; i++) { + printf "\t\"%s\", %d,\n", $i, NR-1 + }}' $temp +echo ' NULL, 0 +};' + +exec > obj/builtins.h +cat <<\! +/* + * This file was generated by the mkbuiltins program. + */ + +#include <sys/cdefs.h> +! +tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ < $temp | + awk '{ printf "#define %s %d\n", $1, NR-1}' +echo ' +struct builtincmd { + char *name; + int code; +}; + +extern int (*const builtinfunc[])(); +extern const struct builtincmd builtincmd[];' +rm -f $temp diff --git a/bin/sh/mkinit.c b/bin/sh/mkinit.c new file mode 100644 index 0000000..eac0374 --- /dev/null +++ b/bin/sh/mkinit.c @@ -0,0 +1,547 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mkinit.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +/* + * This program scans all the source files for code to handle various + * special events and combines this code into one file. This (allegedly) + * improves the structure of the program since there is no need for + * anyone outside of a module to know that that module performs special + * operations on particular events. The command is executed iff init.c + * is actually changed. + * + * Usage: mkinit command sourcefile... + */ + + +#include <sys/cdefs.h> +#include <sys/types.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> + + +/* + * OUTFILE is the name of the output file. Output is initially written + * to the file OUTTEMP, which is then moved to OUTFILE if OUTTEMP and + * OUTFILE are different. + */ + +#define OUTFILE "init.c" +#define OUTTEMP "init.c.new" +#define OUTOBJ "init.o" + + +/* + * A text structure is basicly just a string that grows as more characters + * are added onto the end of it. It is implemented as a linked list of + * blocks of characters. The routines addstr and addchar append a string + * or a single character, respectively, to a text structure. Writetext + * writes the contents of a text structure to a file. + */ + +#define BLOCKSIZE 512 + +struct text { + char *nextc; + int nleft; + struct block *start; + struct block *last; +}; + +struct block { + struct block *next; + char text[BLOCKSIZE]; +}; + + +/* + * There is one event structure for each event that mkinit handles. + */ + +struct event { + char *name; /* name of event (e.g. INIT) */ + char *routine; /* name of routine called on event */ + char *comment; /* comment describing routine */ + struct text code; /* code for handling event */ +}; + + +char writer[] = "\ +/*\n\ + * This file was generated by the mkinit program.\n\ + */\n\ +\n"; + +char init[] = "\ +/*\n\ + * Initialization code.\n\ + */\n"; + +char reset[] = "\ +/*\n\ + * This routine is called when an error or an interrupt occurs in an\n\ + * interactive shell and control is returned to the main command loop.\n\ + */\n"; + +char shellproc[] = "\ +/*\n\ + * This routine is called to initialize the shell to run a shell procedure.\n\ + */\n"; + + +struct event event[] = { + {"INIT", "init", init}, + {"RESET", "reset", reset}, + {"SHELLPROC", "initshellproc", shellproc}, + {NULL, NULL} +}; + + +char *curfile; /* current file */ +int linno; /* current line */ +char *header_files[200]; /* list of header files */ +struct text defines; /* #define statements */ +struct text decls; /* declarations */ +int amiddecls; /* for formatting */ + + +void readfile(), doevent(), doinclude(), dodecl(), output(); +void addstr(), addchar(), writetext(); + +#define equal(s1, s2) (strcmp(s1, s2) == 0) + +FILE *ckfopen(); +char *savestr(); +void *ckmalloc __P((int)); +void error(); + +main(argc, argv) + char **argv; + { + char **ap; + int fd; + char c; + + if (argc < 2) + error("Usage: mkinit command file..."); + header_files[0] = "\"shell.h\""; + header_files[1] = "\"mystring.h\""; + for (ap = argv + 2 ; *ap ; ap++) + readfile(*ap); + output(); + if (file_changed()) { + unlink(OUTFILE); + link(OUTTEMP, OUTFILE); + unlink(OUTTEMP); + } else { + unlink(OUTTEMP); + if (touch(OUTOBJ)) + exit(0); /* no compilation necessary */ + } + printf("%s\n", argv[1]); + execl("/bin/sh", "sh", "-c", argv[1], (char *)0); + error("Can't exec shell"); +} + + +/* + * Parse an input file. + */ + +void +readfile(fname) + char *fname; + { + FILE *fp; + char line[1024]; + struct event *ep; + + fp = ckfopen(fname, "r"); + curfile = fname; + linno = 0; + amiddecls = 0; + while (fgets(line, sizeof line, fp) != NULL) { + linno++; + for (ep = event ; ep->name ; ep++) { + if (line[0] == ep->name[0] && match(ep->name, line)) { + doevent(ep, fp, fname); + break; + } + } + if (line[0] == 'I' && match("INCLUDE", line)) + doinclude(line); + if (line[0] == 'M' && match("MKINIT", line)) + dodecl(line, fp); + if (line[0] == '#' && gooddefine(line)) + addstr(line, &defines); + } + fclose(fp); +} + + +int +match(name, line) + char *name; + char *line; + { + register char *p, *q; + + p = name, q = line; + while (*p) { + if (*p++ != *q++) + return 0; + } + if (*q != '{' && *q != ' ' && *q != '\t' && *q != '\n') + return 0; + return 1; +} + + +int +gooddefine(line) + char *line; + { + register char *p; + + if (! match("#define", line)) + return 0; /* not a define */ + p = line + 7; + while (*p == ' ' || *p == '\t') + p++; + while (*p != ' ' && *p != '\t') { + if (*p == '(') + return 0; /* macro definition */ + p++; + } + while (*p != '\n' && *p != '\0') + p++; + if (p[-1] == '\\') + return 0; /* multi-line definition */ + return 1; +} + + +void +doevent(ep, fp, fname) + register struct event *ep; + FILE *fp; + char *fname; + { + char line[1024]; + int indent; + char *p; + + sprintf(line, "\n /* from %s: */\n", fname); + addstr(line, &ep->code); + addstr(" {\n", &ep->code); + for (;;) { + linno++; + if (fgets(line, sizeof line, fp) == NULL) + error("Unexpected EOF"); + if (equal(line, "}\n")) + break; + indent = 6; + for (p = line ; *p == '\t' ; p++) + indent += 8; + for ( ; *p == ' ' ; p++) + indent++; + if (*p == '\n' || *p == '#') + indent = 0; + while (indent >= 8) { + addchar('\t', &ep->code); + indent -= 8; + } + while (indent > 0) { + addchar(' ', &ep->code); + indent--; + } + addstr(p, &ep->code); + } + addstr(" }\n", &ep->code); +} + + +void +doinclude(line) + char *line; + { + register char *p; + char *name; + register char **pp; + + for (p = line ; *p != '"' && *p != '<' && *p != '\0' ; p++); + if (*p == '\0') + error("Expecting '\"' or '<'"); + name = p; + while (*p != ' ' && *p != '\t' && *p != '\n') + p++; + if (p[-1] != '"' && p[-1] != '>') + error("Missing terminator"); + *p = '\0'; + + /* name now contains the name of the include file */ + for (pp = header_files ; *pp && ! equal(*pp, name) ; pp++); + if (*pp == NULL) + *pp = savestr(name); +} + + +void +dodecl(line1, fp) + char *line1; + FILE *fp; + { + char line[1024]; + register char *p, *q; + + if (strcmp(line1, "MKINIT\n") == 0) { /* start of struct/union decl */ + addchar('\n', &decls); + do { + linno++; + if (fgets(line, sizeof line, fp) == NULL) + error("Unterminated structure declaration"); + addstr(line, &decls); + } while (line[0] != '}'); + amiddecls = 0; + } else { + if (! amiddecls) + addchar('\n', &decls); + q = NULL; + for (p = line1 + 6 ; *p != '=' && *p != '/' ; p++); + if (*p == '=') { /* eliminate initialization */ + for (q = p ; *q && *q != ';' ; q++); + if (*q == '\0') + q = NULL; + else { + while (p[-1] == ' ') + p--; + *p = '\0'; + } + } + addstr("extern", &decls); + addstr(line1 + 6, &decls); + if (q != NULL) + addstr(q, &decls); + amiddecls = 1; + } +} + + + +/* + * Write the output to the file OUTTEMP. + */ + +void +output() { + FILE *fp; + char **pp; + struct event *ep; + + fp = ckfopen(OUTTEMP, "w"); + fputs(writer, fp); + for (pp = header_files ; *pp ; pp++) + fprintf(fp, "#include %s\n", *pp); + fputs("\n\n\n", fp); + writetext(&defines, fp); + fputs("\n\n", fp); + writetext(&decls, fp); + for (ep = event ; ep->name ; ep++) { + fputs("\n\n\n", fp); + fputs(ep->comment, fp); + fprintf(fp, "\nvoid\n%s() {\n", ep->routine); + writetext(&ep->code, fp); + fprintf(fp, "}\n"); + } + fclose(fp); +} + + +/* + * Return true if the new output file is different from the old one. + */ + +int +file_changed() { + register FILE *f1, *f2; + register int c; + + if ((f1 = fopen(OUTFILE, "r")) == NULL + || (f2 = fopen(OUTTEMP, "r")) == NULL) + return 1; + while ((c = getc(f1)) == getc(f2)) { + if (c == EOF) + return 0; + } + return 1; +} + + +/* + * Touch a file. Returns 0 on failure, 1 on success. + */ + +int +touch(file) + char *file; + { + int fd; + char c; + + if ((fd = open(file, O_RDWR)) < 0) + return 0; + if (read(fd, &c, 1) != 1) { + close(fd); + return 0; + } + lseek(fd, (off_t)0, 0); + write(fd, &c, 1); + close(fd); + return 1; +} + + + +/* + * A text structure is simply a block of text that is kept in memory. + * Addstr appends a string to the text struct, and addchar appends a single + * character. + */ + +void +addstr(s, text) + register char *s; + register struct text *text; + { + while (*s) { + if (--text->nleft < 0) + addchar(*s++, text); + else + *text->nextc++ = *s++; + } +} + + +void +addchar(c, text) + register struct text *text; + { + struct block *bp; + + if (--text->nleft < 0) { + bp = ckmalloc(sizeof *bp); + if (text->start == NULL) + text->start = bp; + else + text->last->next = bp; + text->last = bp; + text->nextc = bp->text; + text->nleft = BLOCKSIZE - 1; + } + *text->nextc++ = c; +} + +/* + * Write the contents of a text structure to a file. + */ +void +writetext(text, fp) + struct text *text; + FILE *fp; + { + struct block *bp; + + if (text->start != NULL) { + for (bp = text->start ; bp != text->last ; bp = bp->next) + fwrite(bp->text, sizeof (char), BLOCKSIZE, fp); + fwrite(bp->text, sizeof (char), BLOCKSIZE - text->nleft, fp); + } +} + +FILE * +ckfopen(file, mode) + char *file; + char *mode; + { + FILE *fp; + + if ((fp = fopen(file, mode)) == NULL) { + fprintf(stderr, "Can't open %s\n", file); + exit(2); + } + return fp; +} + +void * +ckmalloc(nbytes) { + register char *p; + char *malloc(); + + if ((p = malloc(nbytes)) == NULL) + error("Out of space"); + return p; +} + +char * +savestr(s) + char *s; + { + register char *p; + + p = ckmalloc(strlen(s) + 1); + strcpy(p, s); + return p; +} + +void +error(msg) + char *msg; + { + if (curfile != NULL) + fprintf(stderr, "%s:%d: ", curfile, linno); + fprintf(stderr, "%s\n", msg); + exit(2); +} diff --git a/bin/sh/mknodes.c b/bin/sh/mknodes.c new file mode 100644 index 0000000..d93ebc0 --- /dev/null +++ b/bin/sh/mknodes.c @@ -0,0 +1,431 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mknodes.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +/* + * This program reads the nodetypes file and nodes.c.pat file. It generates + * the files nodes.h and nodes.c. + */ + +#include <stdio.h> + + +#define MAXTYPES 50 /* max number of node types */ +#define MAXFIELDS 20 /* max fields in a structure */ +#define BUFLEN 100 /* size of character buffers */ + +/* field types */ +#define T_NODE 1 /* union node *field */ +#define T_NODELIST 2 /* struct nodelist *field */ +#define T_STRING 3 +#define T_INT 4 /* int field */ +#define T_OTHER 5 /* other */ +#define T_TEMP 6 /* don't copy this field */ + + +struct field { /* a structure field */ + char *name; /* name of field */ + int type; /* type of field */ + char *decl; /* declaration of field */ +}; + + +struct str { /* struct representing a node structure */ + char *tag; /* structure tag */ + int nfields; /* number of fields in the structure */ + struct field field[MAXFIELDS]; /* the fields of the structure */ + int done; /* set if fully parsed */ +}; + + +int ntypes; /* number of node types */ +char *nodename[MAXTYPES]; /* names of the nodes */ +struct str *nodestr[MAXTYPES]; /* type of structure used by the node */ +int nstr; /* number of structures */ +struct str str[MAXTYPES]; /* the structures */ +struct str *curstr; /* current structure */ + + +FILE *infp = stdin; +char line[1024]; +int linno; +char *linep; + + +char *savestr(); +#define equal(s1, s2) (strcmp(s1, s2) == 0) + + +main(argc, argv) + char **argv; + { + if (argc != 3) + error("usage: mknodes file\n"); + if ((infp = fopen(argv[1], "r")) == NULL) + error("Can't open %s", argv[1]); + while (readline()) { + if (line[0] == ' ' || line[0] == '\t') + parsefield(); + else if (line[0] != '\0') + parsenode(); + } + output(argv[2]); + exit(0); +} + + + +parsenode() { + char name[BUFLEN]; + char tag[BUFLEN]; + struct str *sp; + + if (curstr && curstr->nfields > 0) + curstr->done = 1; + nextfield(name); + if (! nextfield(tag)) + error("Tag expected"); + if (*linep != '\0') + error("Garbage at end of line"); + nodename[ntypes] = savestr(name); + for (sp = str ; sp < str + nstr ; sp++) { + if (equal(sp->tag, tag)) + break; + } + if (sp >= str + nstr) { + sp->tag = savestr(tag); + sp->nfields = 0; + curstr = sp; + nstr++; + } + nodestr[ntypes] = sp; + ntypes++; +} + + +parsefield() { + char name[BUFLEN]; + char type[BUFLEN]; + char decl[2 * BUFLEN]; + struct field *fp; + + if (curstr == NULL || curstr->done) + error("No current structure to add field to"); + if (! nextfield(name)) + error("No field name"); + if (! nextfield(type)) + error("No field type"); + fp = &curstr->field[curstr->nfields]; + fp->name = savestr(name); + if (equal(type, "nodeptr")) { + fp->type = T_NODE; + sprintf(decl, "union node *%s", name); + } else if (equal(type, "nodelist")) { + fp->type = T_NODELIST; + sprintf(decl, "struct nodelist *%s", name); + } else if (equal(type, "string")) { + fp->type = T_STRING; + sprintf(decl, "char *%s", name); + } else if (equal(type, "int")) { + fp->type = T_INT; + sprintf(decl, "int %s", name); + } else if (equal(type, "other")) { + fp->type = T_OTHER; + } else if (equal(type, "temp")) { + fp->type = T_TEMP; + } else { + error("Unknown type %s", type); + } + if (fp->type == T_OTHER || fp->type == T_TEMP) { + skipbl(); + fp->decl = savestr(linep); + } else { + if (*linep) + error("Garbage at end of line"); + fp->decl = savestr(decl); + } + curstr->nfields++; +} + + +char writer[] = "\ +/*\n\ + * This file was generated by the mknodes program.\n\ + */\n\ +\n"; + +output(file) + char *file; + { + FILE *hfile; + FILE *cfile; + FILE *patfile; + int i; + struct str *sp; + struct field *fp; + char *p; + + if ((patfile = fopen(file, "r")) == NULL) + error("Can't open %s", file); + if ((hfile = fopen("nodes.h", "w")) == NULL) + error("Can't create nodes.h"); + if ((cfile = fopen("nodes.c", "w")) == NULL) + error("Can't create nodes.c"); + fputs(writer, hfile); + for (i = 0 ; i < ntypes ; i++) + fprintf(hfile, "#define %s %d\n", nodename[i], i); + fputs("\n\n\n", hfile); + for (sp = str ; sp < &str[nstr] ; sp++) { + fprintf(hfile, "struct %s {\n", sp->tag); + for (i = sp->nfields, fp = sp->field ; --i >= 0 ; fp++) { + fprintf(hfile, " %s;\n", fp->decl); + } + fputs("};\n\n\n", hfile); + } + fputs("union node {\n", hfile); + fprintf(hfile, " int type;\n"); + for (sp = str ; sp < &str[nstr] ; sp++) { + fprintf(hfile, " struct %s %s;\n", sp->tag, sp->tag); + } + fputs("};\n\n\n", hfile); + fputs("struct nodelist {\n", hfile); + fputs("\tstruct nodelist *next;\n", hfile); + fputs("\tunion node *n;\n", hfile); + fputs("};\n\n\n", hfile); + fputs("#ifdef __STDC__\n", hfile); + fputs("union node *copyfunc(union node *);\n", hfile); + fputs("void freefunc(union node *);\n", hfile); + fputs("#else\n", hfile); + fputs("union node *copyfunc();\n", hfile); + fputs("void freefunc();\n", hfile); + fputs("#endif\n", hfile); + + fputs(writer, cfile); + while (fgets(line, sizeof line, patfile) != NULL) { + for (p = line ; *p == ' ' || *p == '\t' ; p++); + if (equal(p, "%SIZES\n")) + outsizes(cfile); + else if (equal(p, "%CALCSIZE\n")) + outfunc(cfile, 1); + else if (equal(p, "%COPY\n")) + outfunc(cfile, 0); + else + fputs(line, cfile); + } +} + + + +outsizes(cfile) + FILE *cfile; + { + int i; + + fprintf(cfile, "static const short nodesize[%d] = {\n", ntypes); + for (i = 0 ; i < ntypes ; i++) { + fprintf(cfile, " ALIGN(sizeof (struct %s)),\n", nodestr[i]->tag); + } + fprintf(cfile, "};\n"); +} + + +outfunc(cfile, calcsize) + FILE *cfile; + { + struct str *sp; + struct field *fp; + int i; + + fputs(" if (n == NULL)\n", cfile); + if (calcsize) + fputs(" return;\n", cfile); + else + fputs(" return NULL;\n", cfile); + if (calcsize) + fputs(" funcblocksize += nodesize[n->type];\n", cfile); + else { + fputs(" new = funcblock;\n", cfile); + fputs(" funcblock += nodesize[n->type];\n", cfile); + } + fputs(" switch (n->type) {\n", cfile); + for (sp = str ; sp < &str[nstr] ; sp++) { + for (i = 0 ; i < ntypes ; i++) { + if (nodestr[i] == sp) + fprintf(cfile, " case %s:\n", nodename[i]); + } + for (i = sp->nfields ; --i >= 1 ; ) { + fp = &sp->field[i]; + switch (fp->type) { + case T_NODE: + if (calcsize) { + indent(12, cfile); + fprintf(cfile, "calcsize(n->%s.%s);\n", + sp->tag, fp->name); + } else { + indent(12, cfile); + fprintf(cfile, "new->%s.%s = copynode(n->%s.%s);\n", + sp->tag, fp->name, sp->tag, fp->name); + } + break; + case T_NODELIST: + if (calcsize) { + indent(12, cfile); + fprintf(cfile, "sizenodelist(n->%s.%s);\n", + sp->tag, fp->name); + } else { + indent(12, cfile); + fprintf(cfile, "new->%s.%s = copynodelist(n->%s.%s);\n", + sp->tag, fp->name, sp->tag, fp->name); + } + break; + case T_STRING: + if (calcsize) { + indent(12, cfile); + fprintf(cfile, "funcstringsize += strlen(n->%s.%s) + 1;\n", + sp->tag, fp->name); + } else { + indent(12, cfile); + fprintf(cfile, "new->%s.%s = nodesavestr(n->%s.%s);\n", + sp->tag, fp->name, sp->tag, fp->name); + } + break; + case T_INT: + case T_OTHER: + if (! calcsize) { + indent(12, cfile); + fprintf(cfile, "new->%s.%s = n->%s.%s;\n", + sp->tag, fp->name, sp->tag, fp->name); + } + break; + } + } + indent(12, cfile); + fputs("break;\n", cfile); + } + fputs(" };\n", cfile); + if (! calcsize) + fputs(" new->type = n->type;\n", cfile); +} + + +indent(amount, fp) + FILE *fp; + { + while (amount >= 8) { + putc('\t', fp); + amount -= 8; + } + while (--amount >= 0) { + putc(' ', fp); + } +} + + +int +nextfield(buf) + char *buf; + { + register char *p, *q; + + p = linep; + while (*p == ' ' || *p == '\t') + p++; + q = buf; + while (*p != ' ' && *p != '\t' && *p != '\0') + *q++ = *p++; + *q = '\0'; + linep = p; + return (q > buf); +} + + +skipbl() { + while (*linep == ' ' || *linep == '\t') + linep++; +} + + +int +readline() { + register char *p; + + if (fgets(line, 1024, infp) == NULL) + return 0; + for (p = line ; *p != '#' && *p != '\n' && *p != '\0' ; p++); + while (p > line && (p[-1] == ' ' || p[-1] == '\t')) + p--; + *p = '\0'; + linep = line; + linno++; + if (p - line > BUFLEN) + error("Line too long"); + return 1; +} + + + +error(msg, a1, a2, a3, a4, a5, a6) + char *msg; + { + fprintf(stderr, "line %d: ", linno); + fprintf(stderr, msg, a1, a2, a3, a4, a5, a6); + putc('\n', stderr); + exit(2); +} + + + +char * +savestr(s) + char *s; + { + register char *p; + char *malloc(); + + if ((p = malloc(strlen(s) + 1)) == NULL) + error("Out of space"); + strcpy(p, s); + return p; +} diff --git a/bin/sh/mksignames.c b/bin/sh/mksignames.c new file mode 100644 index 0000000..fddbe1c --- /dev/null +++ b/bin/sh/mksignames.c @@ -0,0 +1,197 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mksignames.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +/* + * This program generates the signames.h and signames.c files. + */ +#include <stdio.h> +#include <signal.h> + + + +struct sig { + int signo; /* signal number */ + char *name; /* signal name (without leading "SIG") */ + char *mesg; /* description */ +}; + + +struct sig sigtab[] = { + SIGHUP, "HUP", "Hangup", + SIGINT, "INT", "Interrupt", /* normally don't print message */ + SIGQUIT, "QUIT", "Quit", + SIGILL, "ILL", "Illegal instruction", + SIGTRAP, "TRAP", "Trace/BPT trap", +#ifdef SIGABRT + SIGABRT, "ABRT", "abort", +#endif +#if defined(SIGIOT) && (! defined(SIGABRT) || SIGABRT != SIGIOT) + SIGIOT, "IOT", "abort", +#endif +#ifdef SIGEMT + SIGEMT, "EMT", "EMT trap", +#endif + SIGFPE, "FPE", "Floating exception", + SIGKILL, "KILL", "Killed", + SIGBUS, "BUS", "Bus error", + SIGSEGV, "SEGV", "Memory fault", + SIGSYS, "SYS", "Bad system call", + SIGPIPE, "PIPE", "Broken pipe", /* normally don't print message */ + SIGALRM, "ALRM", "Alarm call", + SIGTERM, "TERM", "Terminated", +#ifdef SIGUSR1 + SIGUSR1, "USR1", "User signal 1", +#endif +#ifdef SIGUSR2 + SIGUSR2, "USR2", "User signal 2", +#endif +#ifdef SIGCLD + SIGCLD, "CLD", NULL, +#endif +#if defined(SIGCHLD) && ! defined(SIGCLD) + SIGCHLD, "CLD", NULL, +#endif +#ifdef SIGPWR + SIGPWR, "PWR", "Power fail", +#endif +#ifdef SIGPOLL + SIGPOLL, "POLL", "Poll", +#endif + /* Now for the BSD signals */ +#ifdef SIGURG + SIGURG, "URG", NULL, +#endif +#ifdef SIGSTOP + SIGSTOP, "STOP", "Stopped", +#endif +#ifdef SIGTSTP + SIGTSTP, "TSTP", "Stopped", +#endif +#ifdef SIGCONT + SIGCONT, "CONT", NULL, +#endif +#ifdef SIGTTIN + SIGTTIN, "TTIN", "Stopped (input)", +#endif +#ifdef SIGTTOU + SIGTTOU, "TTOU", "Stopped (output)", +#endif +#ifdef SIGIO + SIGIO, "IO", NULL, +#endif +#ifdef SIGXCPU + SIGXCPU, "XCPU", "Time limit exceeded", +#endif +#ifdef SIGXFSZ + SIGXFSZ, "XFSZ", NULL, +#endif +#ifdef SIGVTALARM + SIGVTALARM, "VTALARM", "Virtual alarm", +#endif +#ifdef SIGPROF + SIGPROF, "PROF", "Profiling alarm", +#endif +#ifdef SIGWINCH + SIGWINCH, "WINCH", NULL, +#endif + 0, NULL, NULL +}; + + +#define MAXSIG 64 + + +char *sigmesg[MAXSIG + 1]; + + +char writer[] = "\ +/*\n\ + * This file was generated by the mksignames program.\n\ + */\n\ +\n"; + + + +main(argc, argv) char **argv; { + FILE *cfile, *hfile; + struct sig *sigp; + int maxsig; + int i; + + if ((cfile = fopen("signames.c", "w")) == NULL) { + fputs("Can't create signames.c\n", stderr); + exit(2); + } + if ((hfile = fopen("signames.h", "w")) == NULL) { + fputs("Can't create signames.h\n", stderr); + exit(2); + } + maxsig = 0; + for (sigp = sigtab ; sigp->signo != 0 ; sigp++) { + if (sigp->signo < 0 || sigp->signo > MAXSIG) + continue; + sigmesg[sigp->signo] = sigp->mesg; + if (maxsig < sigp->signo) + maxsig = sigp->signo; + } + + fputs(writer, hfile); + fprintf(hfile, "#define MAXSIG %d\n\n", maxsig); + fprintf(hfile, "extern char *const sigmesg[MAXSIG+1];\n"); + + fputs(writer, cfile); + fprintf(cfile, "#include \"shell.h\"\n\n"); + fprintf(cfile, "char *const sigmesg[%d] = {\n", maxsig + 1); + for (i = 0 ; i <= maxsig ; i++) { + if (sigmesg[i] == NULL) { + fprintf(cfile, " 0,\n"); + } else { + fprintf(cfile, " \"%s\",\n", sigmesg[i]); + } + } + fprintf(cfile, "};\n"); + exit(0); +} diff --git a/bin/sh/mksyntax.c b/bin/sh/mksyntax.c new file mode 100644 index 0000000..1ab323d --- /dev/null +++ b/bin/sh/mksyntax.c @@ -0,0 +1,373 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mksyntax.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +/* + * This program creates syntax.h and syntax.c. + */ + +#include <stdio.h> +#include "parser.h" + + +struct synclass { + char *name; + char *comment; +}; + +/* Syntax classes */ +struct synclass synclass[] = { + "CWORD", "character is nothing special", + "CNL", "newline character", + "CBACK", "a backslash character", + "CSQUOTE", "single quote", + "CDQUOTE", "double quote", + "CENDQUOTE", "a terminating quote", + "CBQUOTE", "backwards single quote", + "CVAR", "a dollar sign", + "CENDVAR", "a '}' character", + "CLP", "a left paren in arithmetic", + "CRP", "a right paren in arithmetic", + "CEOF", "end of file", + "CCTL", "like CWORD, except it must be escaped", + "CSPCL", "these terminate a word", + NULL, NULL +}; + + +/* + * Syntax classes for is_ functions. Warning: if you add new classes + * you may have to change the definition of the is_in_name macro. + */ +struct synclass is_entry[] = { + "ISDIGIT", "a digit", + "ISUPPER", "an upper case letter", + "ISLOWER", "a lower case letter", + "ISUNDER", "an underscore", + "ISSPECL", "the name of a special parameter", + NULL, NULL, +}; + +char writer[] = "\ +/*\n\ + * This file was generated by the mksyntax program.\n\ + */\n\ +\n"; + + +FILE *cfile; +FILE *hfile; +char *syntax[513]; +int base; +int size; /* number of values which a char variable can have */ +int nbits; /* number of bits in a character */ +int digit_contig; /* true if digits are contiguous */ + + +main() { + char c; + char d; + int sign; + int i; + char buf[80]; + int pos; + static char digit[] = "0123456789"; + + /* Create output files */ + if ((cfile = fopen("syntax.c", "w")) == NULL) { + perror("syntax.c"); + exit(2); + } + if ((hfile = fopen("syntax.h", "w")) == NULL) { + perror("syntax.h"); + exit(2); + } + fputs(writer, hfile); + fputs(writer, cfile); + + /* Determine the characteristics of chars. */ + c = -1; + if (c < 0) + sign = 1; + else + sign = 0; + for (nbits = 1 ; ; nbits++) { + d = (1 << nbits) - 1; + if (d == c) + break; + } + printf("%s %d bit chars\n", sign? "signed" : "unsigned", nbits); + if (nbits > 9) { + fputs("Characters can't have more than 9 bits\n", stderr); + exit(2); + } + size = (1 << nbits) + 1; + base = 1; + if (sign) + base += 1 << (nbits - 1); + digit_contig = 1; + for (i = 0 ; i < 10 ; i++) { + if (digit[i] != '0' + i) + digit_contig = 0; + } + + fputs("#include <sys/cdefs.h>\n", hfile); + + /* Generate the #define statements in the header file */ + fputs("/* Syntax classes */\n", hfile); + for (i = 0 ; synclass[i].name ; i++) { + sprintf(buf, "#define %s %d", synclass[i].name, i); + fputs(buf, hfile); + for (pos = strlen(buf) ; pos < 32 ; pos = pos + 8 &~ 07) + putc('\t', hfile); + fprintf(hfile, "/* %s */\n", synclass[i].comment); + } + putc('\n', hfile); + fputs("/* Syntax classes for is_ functions */\n", hfile); + for (i = 0 ; is_entry[i].name ; i++) { + sprintf(buf, "#define %s %#o", is_entry[i].name, 1 << i); + fputs(buf, hfile); + for (pos = strlen(buf) ; pos < 32 ; pos = pos + 8 &~ 07) + putc('\t', hfile); + fprintf(hfile, "/* %s */\n", is_entry[i].comment); + } + putc('\n', hfile); + fprintf(hfile, "#define SYNBASE %d\n", base); + fprintf(hfile, "#define PEOF %d\n\n", -base); + putc('\n', hfile); + fputs("#define BASESYNTAX (basesyntax + SYNBASE)\n", hfile); + fputs("#define DQSYNTAX (dqsyntax + SYNBASE)\n", hfile); + fputs("#define SQSYNTAX (sqsyntax + SYNBASE)\n", hfile); + fputs("#define ARISYNTAX (arisyntax + SYNBASE)\n", hfile); + putc('\n', hfile); + output_type_macros(); /* is_digit, etc. */ + putc('\n', hfile); + + /* Generate the syntax tables. */ + fputs("#include \"shell.h\"\n", cfile); + fputs("#include \"syntax.h\"\n\n", cfile); + init(); + fputs("/* syntax table used when not in quotes */\n", cfile); + add("\n", "CNL"); + add("\\", "CBACK"); + add("'", "CSQUOTE"); + add("\"", "CDQUOTE"); + add("`", "CBQUOTE"); + add("$", "CVAR"); + add("}", "CENDVAR"); + add("<>();&| \t", "CSPCL"); + print("basesyntax"); + init(); + fputs("\n/* syntax table used when in double quotes */\n", cfile); + add("\n", "CNL"); + add("\\", "CBACK"); + add("\"", "CENDQUOTE"); + add("`", "CBQUOTE"); + add("$", "CVAR"); + add("}", "CENDVAR"); + add("!*?[=~:/", "CCTL"); /* ':/' for tilde - yuck */ + print("dqsyntax"); + init(); + fputs("\n/* syntax table used when in single quotes */\n", cfile); + add("\n", "CNL"); + add("'", "CENDQUOTE"); + add("!*?[=~:/", "CCTL"); /* ':/' for tilde - yuck */ + print("sqsyntax"); + init(); + fputs("\n/* syntax table used when in arithmetic */\n", cfile); + add("\n", "CNL"); + add("\\", "CBACK"); + add("`", "CBQUOTE"); + add("'", "CSQUOTE"); + add("\"", "CDQUOTE"); + add("$", "CVAR"); + add("}", "CENDVAR"); + add("(", "CLP"); + add(")", "CRP"); + print("arisyntax"); + filltable("0"); + fputs("\n/* character classification table */\n", cfile); + add("0123456789", "ISDIGIT"); + add("abcdefghijklmnopqrstucvwxyz", "ISLOWER"); + add("ABCDEFGHIJKLMNOPQRSTUCVWXYZ", "ISUPPER"); + add("_", "ISUNDER"); + add("#?$!-*@", "ISSPECL"); + print("is_type"); + if (! digit_contig) + digit_convert(); + exit(0); +} + + + +/* + * Clear the syntax table. + */ + +filltable(dftval) + char *dftval; + { + int i; + + for (i = 0 ; i < size ; i++) + syntax[i] = dftval; +} + + +/* + * Initialize the syntax table with default values. + */ + +init() { + filltable("CWORD"); + syntax[0] = "CEOF"; + syntax[base + CTLESC] = "CCTL"; + syntax[base + CTLVAR] = "CCTL"; + syntax[base + CTLENDVAR] = "CCTL"; + syntax[base + CTLBACKQ] = "CCTL"; + syntax[base + CTLBACKQ + CTLQUOTE] = "CCTL"; + syntax[base + CTLARI] = "CCTL"; + syntax[base + CTLENDARI] = "CCTL"; +} + + +/* + * Add entries to the syntax table. + */ + +add(p, type) + char *p, *type; + { + while (*p) + syntax[*p++ + base] = type; +} + + + +/* + * Output the syntax table. + */ + +print(name) + char *name; + { + int i; + int col; + + fprintf(hfile, "extern const char %s[];\n", name); + fprintf(cfile, "const char %s[%d] = {\n", name, size); + col = 0; + for (i = 0 ; i < size ; i++) { + if (i == 0) { + fputs(" ", cfile); + } else if ((i & 03) == 0) { + fputs(",\n ", cfile); + col = 0; + } else { + putc(',', cfile); + while (++col < 9 * (i & 03)) + putc(' ', cfile); + } + fputs(syntax[i], cfile); + col += strlen(syntax[i]); + } + fputs("\n};\n", cfile); +} + + + +/* + * Output character classification macros (e.g. is_digit). If digits are + * contiguous, we can test for them quickly. + */ + +char *macro[] = { + "#define is_digit(c)\t((is_type+SYNBASE)[c] & ISDIGIT)", + "#define is_alpha(c)\t((is_type+SYNBASE)[c] & (ISUPPER|ISLOWER))", + "#define is_name(c)\t((is_type+SYNBASE)[c] & (ISUPPER|ISLOWER|ISUNDER))", + "#define is_in_name(c)\t((is_type+SYNBASE)[c] & (ISUPPER|ISLOWER|ISUNDER|ISDIGIT))", + "#define is_special(c)\t((is_type+SYNBASE)[c] & (ISSPECL|ISDIGIT))", + NULL +}; + +output_type_macros() { + char **pp; + + if (digit_contig) + macro[0] = "#define is_digit(c)\t((unsigned)((c) - '0') <= 9)"; + for (pp = macro ; *pp ; pp++) + fprintf(hfile, "%s\n", *pp); + if (digit_contig) + fputs("#define digit_val(c)\t((c) - '0')\n", hfile); + else + fputs("#define digit_val(c)\t(digit_value[c])\n", hfile); +} + + + +/* + * Output digit conversion table (if digits are not contiguous). + */ + +digit_convert() { + int maxdigit; + static char digit[] = "0123456789"; + char *p; + int i; + + maxdigit = 0; + for (p = digit ; *p ; p++) + if (*p > maxdigit) + maxdigit = *p; + fputs("extern const char digit_value[];\n", hfile); + fputs("\n\nconst char digit_value[] = {\n", cfile); + for (i = 0 ; i <= maxdigit ; i++) { + for (p = digit ; *p && *p != i ; p++); + if (*p == '\0') + p = digit; + fprintf(cfile, " %d,\n", p - digit); + } + fputs("};\n", cfile); +} diff --git a/bin/sh/mktokens b/bin/sh/mktokens new file mode 100644 index 0000000..41c0145 --- /dev/null +++ b/bin/sh/mktokens @@ -0,0 +1,94 @@ +#!/bin/sh - +# +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)mktokens 8.1 (Berkeley) 5/31/93 + +# The following is a list of tokens. The second column is nonzero if the +# token marks the end of a list. The third column is the name to print in +# error messages. + +cat > /tmp/ka$$ <<\! +TEOF 1 end of file +TNL 0 newline +TSEMI 0 ";" +TBACKGND 0 "&" +TAND 0 "&&" +TOR 0 "||" +TPIPE 0 "|" +TLP 0 "(" +TRP 1 ")" +TENDCASE 1 ";;" +TENDBQUOTE 1 "`" +TREDIR 0 redirection +TWORD 0 word +TIF 0 "if" +TTHEN 1 "then" +TELSE 1 "else" +TELIF 1 "elif" +TFI 1 "fi" +TWHILE 0 "while" +TUNTIL 0 "until" +TFOR 0 "for" +TDO 1 "do" +TDONE 1 "done" +TBEGIN 0 "{" +TEND 1 "}" +TCASE 0 "case" +TESAC 1 "esac" +TNOT 0 "!" +! +nl=`wc -l /tmp/ka$$` +exec > token.def +awk '{print "#define " $1 " " NR-1}' /tmp/ka$$ +echo ' +/* Array indicating which tokens mark the end of a list */ +const char tokendlist[] = {' +awk '{print "\t" $2 ","}' /tmp/ka$$ +echo '}; + +char *const tokname[] = {' +sed -e 's/"/\\"/g' \ + -e 's/[^ ]*[ ][ ]*[^ ]*[ ][ ]*\(.*\)/ "\1",/' \ + /tmp/ka$$ +echo '}; +' +sed 's/"//g' /tmp/ka$$ | awk ' +/TIF/{print "#define KWDOFFSET " NR-1; print ""; print "char *const parsekwd[] = {"} +/TIF/,/neverfound/{print " \"" $3 "\","}' +echo ' 0 +};' + +rm /tmp/ka$$ diff --git a/bin/sh/myhistedit.h b/bin/sh/myhistedit.h new file mode 100644 index 0000000..bfa4c11 --- /dev/null +++ b/bin/sh/myhistedit.h @@ -0,0 +1,40 @@ +/*- + * Copyright (c) 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. + * + * @(#)myhistedit.h 8.1 (Berkeley) 5/31/93 + */ + +#include <histedit.h> + +extern History *hist; +extern EditLine *el; +extern int displayhist; diff --git a/bin/sh/mystring.c b/bin/sh/mystring.c new file mode 100644 index 0000000..1b9eeea --- /dev/null +++ b/bin/sh/mystring.c @@ -0,0 +1,136 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)mystring.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +/* + * String functions. + * + * equal(s1, s2) Return true if strings are equal. + * scopy(from, to) Copy a string. + * scopyn(from, to, n) Like scopy, but checks for overflow. + * number(s) Convert a string of digits to an integer. + * is_number(s) Return true if s is a string of digits. + */ + +#include "shell.h" +#include "syntax.h" +#include "error.h" +#include "mystring.h" + + +char nullstr[1]; /* zero length string */ + +/* + * equal - #defined in mystring.h + */ + +/* + * scopy - #defined in mystring.h + */ + + +/* + * scopyn - copy a string from "from" to "to", truncating the string + * if necessary. "To" is always nul terminated, even if + * truncation is performed. "Size" is the size of "to". + */ + +void +scopyn(from, to, size) + register char const *from; + register char *to; + register int size; + { + + while (--size > 0) { + if ((*to++ = *from++) == '\0') + return; + } + *to = '\0'; +} + + +/* + * prefix -- see if pfx is a prefix of string. + */ + +int +prefix(pfx, string) + register char const *pfx; + register char const *string; + { + while (*pfx) { + if (*pfx++ != *string++) + return 0; + } + return 1; +} + + +/* + * Convert a string of digits to an integer, printing an error message on + * failure. + */ + +int +number(s) + const char *s; + { + + if (! is_number(s)) + error2("Illegal number", (char *)s); + return atoi(s); +} + + + +/* + * Check for a valid number. This should be elsewhere. + */ + +int +is_number(p) + register const char *p; + { + do { + if (! is_digit(*p)) + return 0; + } while (*++p != '\0'); + return 1; +} diff --git a/bin/sh/mystring.h b/bin/sh/mystring.h new file mode 100644 index 0000000..27ec4ed --- /dev/null +++ b/bin/sh/mystring.h @@ -0,0 +1,54 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mystring.h 8.1 (Berkeley) 5/31/93 + */ + +#include <string.h> + +#ifdef __STDC__ +void scopyn(const char *, char *, int); +int prefix(const char *, const char *); +int number(const char *); +int is_number(const char *); +#else +void scopyn(); +int prefix(); +int number(); +int is_number(); +#endif + +#define equal(s1, s2) (strcmp(s1, s2) == 0) +#define scopy(s1, s2) ((void)strcpy(s2, s1)) diff --git a/bin/sh/nodes.c.pat b/bin/sh/nodes.c.pat new file mode 100644 index 0000000..81c96bf --- /dev/null +++ b/bin/sh/nodes.c.pat @@ -0,0 +1,177 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)nodes.c.pat 8.1 (Berkeley) 5/31/93 + */ + +/* + * Routine for dealing with parsed shell commands. + */ + +#include "shell.h" +#include "nodes.h" +#include "memalloc.h" +#include "machdep.h" +#include "mystring.h" + + +int funcblocksize; /* size of structures in function */ +int funcstringsize; /* size of strings in node */ +#ifdef __STDC__ +pointer funcblock; /* block to allocate function from */ +#else +char *funcblock; /* block to allocate function from */ +#endif +char *funcstring; /* block to allocate strings from */ + +%SIZES + + +#ifdef __STDC__ +STATIC void calcsize(union node *); +STATIC void sizenodelist(struct nodelist *); +STATIC union node *copynode(union node *); +STATIC struct nodelist *copynodelist(struct nodelist *); +STATIC char *nodesavestr(char *); +#else +STATIC void calcsize(); +STATIC void sizenodelist(); +STATIC union node *copynode(); +STATIC struct nodelist *copynodelist(); +STATIC char *nodesavestr(); +#endif + + + +/* + * Make a copy of a parse tree. + */ + +union node * +copyfunc(n) + union node *n; + { + if (n == NULL) + return NULL; + funcblocksize = 0; + funcstringsize = 0; + calcsize(n); + funcblock = ckmalloc(funcblocksize + funcstringsize); + funcstring = funcblock + funcblocksize; + return copynode(n); +} + + + +STATIC void +calcsize(n) + union node *n; + { + %CALCSIZE +} + + + +STATIC void +sizenodelist(lp) + struct nodelist *lp; + { + while (lp) { + funcblocksize += ALIGN(sizeof (struct nodelist)); + calcsize(lp->n); + lp = lp->next; + } +} + + + +STATIC union node * +copynode(n) + union node *n; + { + union node *new; + + %COPY + return new; +} + + +STATIC struct nodelist * +copynodelist(lp) + struct nodelist *lp; + { + struct nodelist *start; + struct nodelist **lpp; + + lpp = &start; + while (lp) { + *lpp = funcblock; + funcblock += ALIGN(sizeof (struct nodelist)); + (*lpp)->n = copynode(lp->n); + lp = lp->next; + lpp = &(*lpp)->next; + } + *lpp = NULL; + return start; +} + + + +STATIC char * +nodesavestr(s) + char *s; + { + register char *p = s; + register char *q = funcstring; + char *rtn = funcstring; + + while (*q++ = *p++); + funcstring = q; + return rtn; +} + + + +/* + * Free a parse tree. + */ + +void +freefunc(n) + union node *n; + { + if (n) + ckfree(n); +} diff --git a/bin/sh/nodetypes b/bin/sh/nodetypes new file mode 100644 index 0000000..d60e769 --- /dev/null +++ b/bin/sh/nodetypes @@ -0,0 +1,143 @@ +# +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)nodetypes 8.1 (Berkeley) 5/31/93 + +# This file describes the nodes used in parse trees. Unindented lines +# contain a node type followed by a structure tag. Subsequent indented +# lines specify the fields of the structure. Several node types can share +# the same structure, in which case the fields of the structure should be +# specified only once. +# +# A field of a structure is described by the name of the field followed +# by a type. The currently implemented types are: +# nodeptr - a pointer to a node +# nodelist - a pointer to a list of nodes +# string - a pointer to a nul terminated string +# int - an integer +# other - any type that can be copied by assignment +# temp - a field that doesn't have to be copied when the node is copied +# The last two types should be followed by the text of a C declaration for +# the field. + +NSEMI nbinary # two commands separated by a semicolon + type int + ch1 nodeptr # the first child + ch2 nodeptr # the second child + +NCMD ncmd # a simple command + type int + backgnd int # set to run command in background + args nodeptr # the arguments + redirect nodeptr # list of file redirections + +NPIPE npipe # a pipeline + type int + backgnd int # set to run pipeline in background + cmdlist nodelist # the commands in the pipeline + +NREDIR nredir # redirection (of a compex command) + type int + n nodeptr # the command + redirect nodeptr # list of file redirections + +NBACKGND nredir # run command in background +NSUBSHELL nredir # run command in a subshell + +NAND nbinary # the && operator +NOR nbinary # the || operator + +NIF nif # the if statement. Elif clauses are handled + type int # using multiple if nodes. + test nodeptr # if test + ifpart nodeptr # then ifpart + elsepart nodeptr # else elsepart + +NWHILE nbinary # the while statement. First child is the test +NUNTIL nbinary # the until statement + +NFOR nfor # the for statement + type int + args nodeptr # for var in args + body nodeptr # do body; done + var string # the for variable + +NCASE ncase # a case statement + type int + expr nodeptr # the word to switch on + cases nodeptr # the list of cases (NCLIST nodes) + +NCLIST nclist # a case + type int + next nodeptr # the next case in list + pattern nodeptr # list of patterns for this case + body nodeptr # code to execute for this case + + +NDEFUN narg # define a function. The "next" field contains + # the body of the function. + +NARG narg # represents a word + type int + next nodeptr # next word in list + text string # the text of the word + backquote nodelist # list of commands in back quotes + +NTO nfile # fd> fname +NFROM nfile # fd< fname +NAPPEND nfile # fd>> fname + type int + next nodeptr # next redirection in list + fd int # file descriptor being redirected + fname nodeptr # file name, in a NARG node + expfname temp char *expfname # actual file name + +NTOFD ndup # fd<&dupfd +NFROMFD ndup # fd>&dupfd + type int + next nodeptr # next redirection in list + fd int # file descriptor being redirected + dupfd int # file descriptor to duplicate + +NHERE nhere # fd<<\! +NXHERE nhere # fd<<! + type int + next nodeptr # next redirection in list + fd int # file descriptor being redirected + doc nodeptr # input to command (NARG node) + +NNOT nnot # ! command (actually pipeline) + type int + com nodeptr diff --git a/bin/sh/options.c b/bin/sh/options.c new file mode 100644 index 0000000..9363e62 --- /dev/null +++ b/bin/sh/options.c @@ -0,0 +1,433 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)options.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include "shell.h" +#define DEFINE_OPTIONS +#include "options.h" +#undef DEFINE_OPTIONS +#include "nodes.h" /* for other header files */ +#include "eval.h" +#include "jobs.h" +#include "input.h" +#include "output.h" +#include "trap.h" +#include "var.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" + +char *arg0; /* value of $0 */ +struct shparam shellparam; /* current positional parameters */ +char **argptr; /* argument list for builtin commands */ +char *optarg; /* set by nextopt (like getopt) */ +char *optptr; /* used by nextopt */ + +char *minusc; /* argument to -c option */ + + +#ifdef __STDC__ +STATIC void options(int); +STATIC void setoption(int, int); +STATIC void minus_o(char *, int); +#else +STATIC void options(); +STATIC void setoption(); +STATIC void minus_o(); +#endif + + + +/* + * Process the shell command line arguments. + */ + +void +procargs(argc, argv) + char **argv; + { + int i; + + argptr = argv; + if (argc > 0) + argptr++; + for (i = 0; i < NOPTS; i++) + optlist[i].val = 2; + options(1); + if (*argptr == NULL && minusc == NULL) + sflag = 1; + if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1)) + iflag = 1; + if (mflag == 2) + mflag = iflag; + for (i = 0; i < NOPTS; i++) + if (optlist[i].val == 2) + optlist[i].val = 0; + arg0 = argv[0]; + if (sflag == 0 && minusc == NULL) { + commandname = arg0 = *argptr++; + setinputfile(commandname, 0); + } + shellparam.p = argptr; + /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */ + while (*argptr) { + shellparam.nparam++; + argptr++; + } + optschanged(); +} + + +optschanged() { + setinteractive(iflag); + histedit(); + setjobctl(mflag); +} + +/* + * Process shell options. The global variable argptr contains a pointer + * to the argument list; we advance it past the options. + */ + +STATIC void +options(cmdline) { + register char *p; + int val; + int c; + + if (cmdline) + minusc = NULL; + while ((p = *argptr) != NULL) { + argptr++; + if ((c = *p++) == '-') { + val = 1; + if (p[0] == '\0' || p[0] == '-' && p[1] == '\0') { + if (!cmdline) { + /* "-" means turn off -x and -v */ + if (p[0] == '\0') + xflag = vflag = 0; + /* "--" means reset params */ + else if (*argptr == NULL) + setparam(argptr); + } + break; /* "-" or "--" terminates options */ + } + } else if (c == '+') { + val = 0; + } else { + argptr--; + break; + } + while ((c = *p++) != '\0') { + if (c == 'c' && cmdline) { + char *q; +#ifdef NOHACK /* removing this code allows sh -ce 'foo' for compat */ + if (*p == '\0') +#endif + q = *argptr++; + if (q == NULL || minusc != NULL) + error("Bad -c option"); + minusc = q; +#ifdef NOHACK + break; +#endif + } else if (c == 'o') { + minus_o(*argptr, val); + if (*argptr) + argptr++; + } else { + setoption(c, val); + } + } + } +} + +STATIC void +minus_o(name, val) + char *name; + int val; +{ + int i; + + if (name == NULL) { + out1str("Current option settings\n"); + for (i = 0; i < NOPTS; i++) + out1fmt("%-16s%s\n", optlist[i].name, + optlist[i].val ? "on" : "off"); + } else { + for (i = 0; i < NOPTS; i++) + if (equal(name, optlist[i].name)) { + setoption(optlist[i].letter, val); + return; + } + error("Illegal option -o %s", name); + } +} + + +STATIC void +setoption(flag, val) + char flag; + int val; + { + int i; + + for (i = 0; i < NOPTS; i++) + if (optlist[i].letter == flag) { + optlist[i].val = val; + if (val) { + /* #%$ hack for ksh semantics */ + if (flag == 'V') + Eflag = 0; + else if (flag == 'E') + Vflag = 0; + } + return; + } + error("Illegal option -%c", flag); +} + + + +#ifdef mkinit +INCLUDE "options.h" + +SHELLPROC { + int i; + + for (i = 0; i < NOPTS; i++) + optlist[i].val = 0; + optschanged(); + +} +#endif + + +/* + * Set the shell parameters. + */ + +void +setparam(argv) + char **argv; + { + char **newparam; + char **ap; + int nparam; + + for (nparam = 0 ; argv[nparam] ; nparam++); + ap = newparam = ckmalloc((nparam + 1) * sizeof *ap); + while (*argv) { + *ap++ = savestr(*argv++); + } + *ap = NULL; + freeparam(&shellparam); + shellparam.malloc = 1; + shellparam.nparam = nparam; + shellparam.p = newparam; + shellparam.optnext = NULL; +} + + +/* + * Free the list of positional parameters. + */ + +void +freeparam(param) + struct shparam *param; + { + char **ap; + + if (param->malloc) { + for (ap = param->p ; *ap ; ap++) + ckfree(*ap); + ckfree(param->p); + } +} + + + +/* + * The shift builtin command. + */ + +shiftcmd(argc, argv) char **argv; { + int n; + char **ap1, **ap2; + + n = 1; + if (argc > 1) + n = number(argv[1]); + if (n > shellparam.nparam) + error("can't shift that many"); + INTOFF; + shellparam.nparam -= n; + for (ap1 = shellparam.p ; --n >= 0 ; ap1++) { + if (shellparam.malloc) + ckfree(*ap1); + } + ap2 = shellparam.p; + while ((*ap2++ = *ap1++) != NULL); + shellparam.optnext = NULL; + INTON; + return 0; +} + + + +/* + * The set command builtin. + */ + +setcmd(argc, argv) char **argv; { + if (argc == 1) + return showvarscmd(argc, argv); + INTOFF; + options(0); + optschanged(); + if (*argptr != NULL) { + setparam(argptr); + } + INTON; + return 0; +} + + +/* + * The getopts builtin. Shellparam.optnext points to the next argument + * to be processed. Shellparam.optptr points to the next character to + * be processed in the current argument. If shellparam.optnext is NULL, + * then it's the first time getopts has been called. + */ + +getoptscmd(argc, argv) char **argv; { + register char *p, *q; + char c; + char s[10]; + + if (argc != 3) + error("Usage: getopts optstring var"); + if (shellparam.optnext == NULL) { + shellparam.optnext = shellparam.p; + shellparam.optptr = NULL; + } + if ((p = shellparam.optptr) == NULL || *p == '\0') { + p = *shellparam.optnext; + if (p == NULL || *p != '-' || *++p == '\0') { +atend: + fmtstr(s, 10, "%d", shellparam.optnext - shellparam.p + 1); + setvar("OPTIND", s, 0); + shellparam.optnext = NULL; + return 1; + } + shellparam.optnext++; + if (p[0] == '-' && p[1] == '\0') /* check for "--" */ + goto atend; + } + c = *p++; + for (q = argv[1] ; *q != c ; ) { + if (*q == '\0') { + out1fmt("Illegal option -%c\n", c); + c = '?'; + goto out; + } + if (*++q == ':') + q++; + } + if (*++q == ':') { + if (*p == '\0' && (p = *shellparam.optnext) == NULL) { + out1fmt("No arg for -%c option\n", c); + c = '?'; + goto out; + } + shellparam.optnext++; + setvar("OPTARG", p, 0); + p = NULL; + } +out: + shellparam.optptr = p; + s[0] = c; + s[1] = '\0'; + setvar(argv[2], s, 0); + return 0; +} + +/* + * XXX - should get rid of. have all builtins use getopt(3). the + * library getopt must have the BSD extension static variable "optreset" + * otherwise it can't be used within the shell safely. + * + * Standard option processing (a la getopt) for builtin routines. The + * only argument that is passed to nextopt is the option string; the + * other arguments are unnecessary. It return the character, or '\0' on + * end of input. + */ + +int +nextopt(optstring) + char *optstring; + { + register char *p, *q; + char c; + + if ((p = optptr) == NULL || *p == '\0') { + p = *argptr; + if (p == NULL || *p != '-' || *++p == '\0') + return '\0'; + argptr++; + if (p[0] == '-' && p[1] == '\0') /* check for "--" */ + return '\0'; + } + c = *p++; + for (q = optstring ; *q != c ; ) { + if (*q == '\0') + error("Illegal option -%c", c); + if (*++q == ':') + q++; + } + if (*++q == ':') { + if (*p == '\0' && (p = *argptr++) == NULL) + error("No arg for -%c option", c); + optarg = p; + p = NULL; + } + optptr = p; + return c; +} diff --git a/bin/sh/options.h b/bin/sh/options.h new file mode 100644 index 0000000..7d00f55 --- /dev/null +++ b/bin/sh/options.h @@ -0,0 +1,114 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)options.h 8.1 (Berkeley) 5/31/93 + */ + +struct shparam { + int nparam; /* number of positional parameters (without $0) */ + char malloc; /* true if parameter list dynamicly allocated */ + char **p; /* parameter list */ + char **optnext; /* next parameter to be processed by getopts */ + char *optptr; /* used by getopts */ +}; + + + +#define eflag optlist[0].val +#define fflag optlist[1].val +#define Iflag optlist[2].val +#define iflag optlist[3].val +#define mflag optlist[4].val +#define nflag optlist[5].val +#define sflag optlist[6].val +#define xflag optlist[7].val +#define vflag optlist[8].val +#define Vflag optlist[9].val +#define Eflag optlist[10].val +#define Cflag optlist[11].val +#define aflag optlist[12].val +#define bflag optlist[13].val +#define uflag optlist[14].val + +#define NOPTS 15 + +struct optent { + const char *name; + const char letter; + char val; +}; + +#ifdef DEFINE_OPTIONS +struct optent optlist[NOPTS] = { + "errexit", 'e', 0, + "noglob", 'f', 0, + "ignoreeof", 'I', 0, + "interactive", 'i', 0, + "monitor", 'm', 0, + "noexec", 'n', 0, + "stdin", 's', 0, + "xtrace", 'x', 0, + "verbose", 'v', 0, + "vi", 'V', 0, + "emacs", 'E', 0, + "noclobber", 'C', 0, + "allexport", 'a', 0, + "notify", 'b', 0, + "nounset", 'u', 0, +}; +#else +extern struct optent optlist[NOPTS]; +#endif + + +extern char *minusc; /* argument to -c option */ +extern char *arg0; /* $0 */ +extern struct shparam shellparam; /* $@ */ +extern char **argptr; /* argument list for builtin commands */ +extern char *optarg; /* set by nextopt */ +extern char *optptr; /* used by nextopt */ + + +#ifdef __STDC__ +void procargs(int, char **); +void setparam(char **); +void freeparam(struct shparam *); +int nextopt(char *); +#else +void procargs(); +void setparam(); +void freeparam(); +int nextopt(); +#endif diff --git a/bin/sh/output.c b/bin/sh/output.c new file mode 100644 index 0000000..88ef764 --- /dev/null +++ b/bin/sh/output.c @@ -0,0 +1,554 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)output.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +/* + * Shell output routines. We use our own output routines because: + * When a builtin command is interrupted we have to discard + * any pending output. + * When a builtin command appears in back quotes, we want to + * save the output of the command in a region obtained + * via malloc, rather than doing a fork and reading the + * output of the command via a pipe. + * Our output routines may be smaller than the stdio routines. + */ + +#include <stdio.h> /* defines BUFSIZ */ +#include "shell.h" +#include "syntax.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#ifdef __STDC__ +#include "stdarg.h" +#else +#include <varargs.h> +#endif +#include <errno.h> + + +#define OUTBUFSIZ BUFSIZ +#define BLOCK_OUT -2 /* output to a fixed block of memory */ +#define MEM_OUT -3 /* output to dynamically allocated memory */ +#define OUTPUT_ERR 01 /* error occurred on output */ + + +struct output output = {NULL, 0, NULL, OUTBUFSIZ, 1, 0}; +struct output errout = {NULL, 0, NULL, 100, 2, 0};; +struct output memout = {NULL, 0, NULL, 0, MEM_OUT, 0}; +struct output *out1 = &output; +struct output *out2 = &errout; + + + +#ifdef mkinit + +INCLUDE "output.h" +INCLUDE "memalloc.h" + +RESET { + out1 = &output; + out2 = &errout; + if (memout.buf != NULL) { + ckfree(memout.buf); + memout.buf = NULL; + } +} + +#endif + + +#ifdef notdef /* no longer used */ +/* + * Set up an output file to write to memory rather than a file. + */ + +void +open_mem(block, length, file) + char *block; + int length; + struct output *file; + { + file->nextc = block; + file->nleft = --length; + file->fd = BLOCK_OUT; + file->flags = 0; +} +#endif + + +void +out1str(p) + char *p; + { + outstr(p, out1); +} + + +void +out2str(p) + char *p; + { + outstr(p, out2); +} + + +void +outstr(p, file) + register char *p; + register struct output *file; + { + while (*p) + outc(*p++, file); + if (file == out2) + flushout(file); +} + + +char out_junk[16]; + + +void +emptyoutbuf(dest) + struct output *dest; + { + int offset; + + if (dest->fd == BLOCK_OUT) { + dest->nextc = out_junk; + dest->nleft = sizeof out_junk; + dest->flags |= OUTPUT_ERR; + } else if (dest->buf == NULL) { + INTOFF; + dest->buf = ckmalloc(dest->bufsize); + dest->nextc = dest->buf; + dest->nleft = dest->bufsize; + INTON; + } else if (dest->fd == MEM_OUT) { + offset = dest->bufsize; + INTOFF; + dest->bufsize <<= 1; + dest->buf = ckrealloc(dest->buf, dest->bufsize); + dest->nleft = dest->bufsize - offset; + dest->nextc = dest->buf + offset; + INTON; + } else { + flushout(dest); + } + dest->nleft--; +} + + +void +flushall() { + flushout(&output); + flushout(&errout); +} + + +void +flushout(dest) + struct output *dest; + { + + if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0) + return; + if (xwrite(dest->fd, dest->buf, dest->nextc - dest->buf) < 0) + dest->flags |= OUTPUT_ERR; + dest->nextc = dest->buf; + dest->nleft = dest->bufsize; +} + + +void +freestdout() { + INTOFF; + if (output.buf) { + ckfree(output.buf); + output.buf = NULL; + output.nleft = 0; + } + INTON; +} + + +#ifdef __STDC__ +void +outfmt(struct output *file, char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + doformat(file, fmt, ap); + va_end(ap); +} + + +void +out1fmt(char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + doformat(out1, fmt, ap); + va_end(ap); +} + +void +dprintf(char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + doformat(out2, fmt, ap); + va_end(ap); + flushout(out2); +} + +void +fmtstr(char *outbuf, int length, char *fmt, ...) { + va_list ap; + struct output strout; + + va_start(ap, fmt); + strout.nextc = outbuf; + strout.nleft = length; + strout.fd = BLOCK_OUT; + strout.flags = 0; + doformat(&strout, fmt, ap); + outc('\0', &strout); + if (strout.flags & OUTPUT_ERR) + outbuf[length - 1] = '\0'; +} + +#else /* not __STDC__ */ + +void +outfmt(va_alist) + va_dcl + { + va_list ap; + struct output *file; + char *fmt; + + va_start(ap); + file = va_arg(ap, struct output *); + fmt = va_arg(ap, char *); + doformat(file, fmt, ap); + va_end(ap); +} + + +void +out1fmt(va_alist) + va_dcl + { + va_list ap; + char *fmt; + + va_start(ap); + fmt = va_arg(ap, char *); + doformat(out1, fmt, ap); + va_end(ap); +} + +void +dprintf(va_alist) + va_dcl + { + va_list ap; + char *fmt; + + va_start(ap); + fmt = va_arg(ap, char *); + doformat(out2, fmt, ap); + va_end(ap); + flushout(out2); +} + +void +fmtstr(va_alist) + va_dcl + { + va_list ap; + struct output strout; + char *outbuf; + int length; + char *fmt; + + va_start(ap); + outbuf = va_arg(ap, char *); + length = va_arg(ap, int); + fmt = va_arg(ap, char *); + strout.nextc = outbuf; + strout.nleft = length; + strout.fd = BLOCK_OUT; + strout.flags = 0; + doformat(&strout, fmt, ap); + outc('\0', &strout); + if (strout.flags & OUTPUT_ERR) + outbuf[length - 1] = '\0'; +} +#endif /* __STDC__ */ + + +/* + * Formatted output. This routine handles a subset of the printf formats: + * - Formats supported: d, u, o, X, s, and c. + * - The x format is also accepted but is treated like X. + * - The l modifier is accepted. + * - The - and # flags are accepted; # only works with the o format. + * - Width and precision may be specified with any format except c. + * - An * may be given for the width or precision. + * - The obsolete practice of preceding the width with a zero to get + * zero padding is not supported; use the precision field. + * - A % may be printed by writing %% in the format string. + */ + +#define TEMPSIZE 24 + +#ifdef __STDC__ +static const char digit[16] = "0123456789ABCDEF"; +#else +static const char digit[17] = "0123456789ABCDEF"; +#endif + + +void +doformat(dest, f, ap) + register struct output *dest; + register char *f; /* format string */ + va_list ap; + { + register char c; + char temp[TEMPSIZE]; + int flushleft; + int sharp; + int width; + int prec; + int islong; + char *p; + int sign; + long l; + unsigned long num; + unsigned base; + int len; + int size; + int pad; + + while ((c = *f++) != '\0') { + if (c != '%') { + outc(c, dest); + continue; + } + flushleft = 0; + sharp = 0; + width = 0; + prec = -1; + islong = 0; + for (;;) { + if (*f == '-') + flushleft++; + else if (*f == '#') + sharp++; + else + break; + f++; + } + if (*f == '*') { + width = va_arg(ap, int); + f++; + } else { + while (is_digit(*f)) { + width = 10 * width + digit_val(*f++); + } + } + if (*f == '.') { + if (*++f == '*') { + prec = va_arg(ap, int); + f++; + } else { + prec = 0; + while (is_digit(*f)) { + prec = 10 * prec + digit_val(*f++); + } + } + } + if (*f == 'l') { + islong++; + f++; + } + switch (*f) { + case 'd': + if (islong) + l = va_arg(ap, long); + else + l = va_arg(ap, int); + sign = 0; + num = l; + if (l < 0) { + num = -l; + sign = 1; + } + base = 10; + goto number; + case 'u': + base = 10; + goto uns_number; + case 'o': + base = 8; + goto uns_number; + case 'x': + /* we don't implement 'x'; treat like 'X' */ + case 'X': + base = 16; +uns_number: /* an unsigned number */ + sign = 0; + if (islong) + num = va_arg(ap, unsigned long); + else + num = va_arg(ap, unsigned int); +number: /* process a number */ + p = temp + TEMPSIZE - 1; + *p = '\0'; + while (num) { + *--p = digit[num % base]; + num /= base; + } + len = (temp + TEMPSIZE - 1) - p; + if (prec < 0) + prec = 1; + if (sharp && *f == 'o' && prec <= len) + prec = len + 1; + pad = 0; + if (width) { + size = len; + if (size < prec) + size = prec; + size += sign; + pad = width - size; + if (flushleft == 0) { + while (--pad >= 0) + outc(' ', dest); + } + } + if (sign) + outc('-', dest); + prec -= len; + while (--prec >= 0) + outc('0', dest); + while (*p) + outc(*p++, dest); + while (--pad >= 0) + outc(' ', dest); + break; + case 's': + p = va_arg(ap, char *); + pad = 0; + if (width) { + len = strlen(p); + if (prec >= 0 && len > prec) + len = prec; + pad = width - len; + if (flushleft == 0) { + while (--pad >= 0) + outc(' ', dest); + } + } + prec++; + while (--prec != 0 && *p) + outc(*p++, dest); + while (--pad >= 0) + outc(' ', dest); + break; + case 'c': + c = va_arg(ap, int); + outc(c, dest); + break; + default: + outc(*f, dest); + break; + } + f++; + } +} + + + +/* + * Version of write which resumes after a signal is caught. + */ + +int +xwrite(fd, buf, nbytes) + int fd; + char *buf; + int nbytes; + { + int ntry; + int i; + int n; + + n = nbytes; + ntry = 0; + for (;;) { + i = write(fd, buf, n); + if (i > 0) { + if ((n -= i) <= 0) + return nbytes; + buf += i; + ntry = 0; + } else if (i == 0) { + if (++ntry > 10) + return nbytes - n; + } else if (errno != EINTR) { + return -1; + } + } +} + + +/* + * Version of ioctl that retries after a signal is caught. + */ + +int +xioctl(fd, request, arg) { + int i; + + while ((i = ioctl(fd, request, arg)) == -1 && errno == EINTR); + return i; +} diff --git a/bin/sh/output.h b/bin/sh/output.h new file mode 100644 index 0000000..5b4764c --- /dev/null +++ b/bin/sh/output.h @@ -0,0 +1,94 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)output.h 8.1 (Berkeley) 5/31/93 + */ + +#ifndef OUTPUT_INCL + +struct output { + char *nextc; + int nleft; + char *buf; + int bufsize; + short fd; + short flags; +}; + +extern struct output output; +extern struct output errout; +extern struct output memout; +extern struct output *out1; +extern struct output *out2; + + +#ifdef __STDC__ +void outstr(char *, struct output *); +void out1str(char *); +void out2str(char *); +void outfmt(struct output *, char *, ...); +void out1fmt(char *, ...); +void fmtstr(char *, int, char *, ...); +/* void doformat(struct output *, char *, va_list); */ +void doformat(); +void emptyoutbuf(struct output *); +void flushall(void); +void flushout(struct output *); +void freestdout(void); +int xwrite(int, char *, int); +int xioctl(int, int, int); +#else +void outstr(); +void out1str(); +void out2str(); +void outfmt(); +void out1fmt(); +void fmtstr(); +/* void doformat(); */ +void doformat(); +void emptyoutbuf(); +void flushall(); +void flushout(); +void freestdout(); +int xwrite(); +int xioctl(); +#endif + +#define outc(c, file) (--(file)->nleft < 0? (emptyoutbuf(file), *(file)->nextc++ = (c)) : (*(file)->nextc++ = (c))) +#define out1c(c) outc(c, out1); +#define out2c(c) outc(c, out2); + +#define OUTPUT_INCL +#endif diff --git a/bin/sh/parser.c b/bin/sh/parser.c new file mode 100644 index 0000000..898349f --- /dev/null +++ b/bin/sh/parser.c @@ -0,0 +1,1363 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)parser.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include "shell.h" +#include "parser.h" +#include "nodes.h" +#include "expand.h" /* defines rmescapes() */ +#include "redir.h" /* defines copyfd() */ +#include "syntax.h" +#include "options.h" +#include "input.h" +#include "output.h" +#include "var.h" +#include "error.h" +#include "memalloc.h" +#include "mystring.h" +#include "alias.h" +#include "myhistedit.h" + + +/* + * Shell command parser. + */ + +#define EOFMARKLEN 79 + +/* values returned by readtoken */ +#include "token.def" + + + +struct heredoc { + struct heredoc *next; /* next here document in list */ + union node *here; /* redirection node */ + char *eofmark; /* string indicating end of input */ + int striptabs; /* if set, strip leading tabs */ +}; + + + +struct heredoc *heredoclist; /* list of here documents to read */ +int parsebackquote; /* nonzero if we are inside backquotes */ +int doprompt; /* if set, prompt the user */ +int needprompt; /* true if interactive and at start of line */ +int lasttoken; /* last token read */ +MKINIT int tokpushback; /* last token pushed back */ +char *wordtext; /* text of last word returned by readtoken */ +MKINIT int checkkwd; /* 1 == check for kwds, 2 == also eat newlines */ +struct nodelist *backquotelist; +union node *redirnode; +struct heredoc *heredoc; +int quoteflag; /* set if (part of) last token was quoted */ +int startlinno; /* line # where last token started */ + + +#define GDB_HACK 1 /* avoid local declarations which gdb can't handle */ +#ifdef GDB_HACK +static const char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'}; +static const char types[] = "}-+?="; +#endif + + +STATIC union node *list __P((int)); +STATIC union node *andor __P((void)); +STATIC union node *pipeline __P((void)); +STATIC union node *command __P((void)); +STATIC union node *simplecmd __P((union node **, union node *)); +STATIC void parsefname __P((void)); +STATIC void parseheredoc __P((void)); +STATIC int readtoken __P((void)); +STATIC int readtoken1 __P((int, char const *, char *, int)); +STATIC void attyline __P((void)); +STATIC int noexpand __P((char *)); +STATIC void synexpect __P((int)); +STATIC void synerror __P((char *)); +STATIC void setprompt __P((int)); + +/* + * Read and parse a command. Returns NEOF on end of file. (NULL is a + * valid parse tree indicating a blank line.) + */ + +union node * +parsecmd(interact) { + int t; + + doprompt = interact; + if (doprompt) + setprompt(1); + else + setprompt(0); + needprompt = 0; + t = readtoken(); + if (t == TEOF) + return NEOF; + if (t == TNL) + return NULL; + tokpushback++; + return list(1); +} + + +STATIC union node * +list(nlflag) { + union node *n1, *n2, *n3; + + checkkwd = 2; + if (nlflag == 0 && tokendlist[peektoken()]) + return NULL; + n1 = andor(); + for (;;) { + switch (readtoken()) { + case TBACKGND: + if (n1->type == NCMD || n1->type == NPIPE) { + n1->ncmd.backgnd = 1; + } else if (n1->type == NREDIR) { + n1->type = NBACKGND; + } else { + n3 = (union node *)stalloc(sizeof (struct nredir)); + n3->type = NBACKGND; + n3->nredir.n = n1; + n3->nredir.redirect = NULL; + n1 = n3; + } + goto tsemi; + case TNL: + tokpushback++; + /* fall through */ +tsemi: case TSEMI: + if (readtoken() == TNL) { + parseheredoc(); + if (nlflag) + return n1; + } else { + tokpushback++; + } + checkkwd = 2; + if (tokendlist[peektoken()]) + return n1; + n2 = andor(); + n3 = (union node *)stalloc(sizeof (struct nbinary)); + n3->type = NSEMI; + n3->nbinary.ch1 = n1; + n3->nbinary.ch2 = n2; + n1 = n3; + break; + case TEOF: + if (heredoclist) + parseheredoc(); + else + pungetc(); /* push back EOF on input */ + return n1; + default: + if (nlflag) + synexpect(-1); + tokpushback++; + return n1; + } + } +} + + + +STATIC union node * +andor() { + union node *n1, *n2, *n3; + int t; + + n1 = pipeline(); + for (;;) { + if ((t = readtoken()) == TAND) { + t = NAND; + } else if (t == TOR) { + t = NOR; + } else { + tokpushback++; + return n1; + } + n2 = pipeline(); + n3 = (union node *)stalloc(sizeof (struct nbinary)); + n3->type = t; + n3->nbinary.ch1 = n1; + n3->nbinary.ch2 = n2; + n1 = n3; + } +} + + + +STATIC union node * +pipeline() { + union node *n1, *pipenode, *notnode; + struct nodelist *lp, *prev; + int negate = 0; + + TRACE(("pipeline: entered\n")); + while (readtoken() == TNOT) { + TRACE(("pipeline: TNOT recognized\n")); + negate = !negate; + } + tokpushback++; + n1 = command(); + if (readtoken() == TPIPE) { + pipenode = (union node *)stalloc(sizeof (struct npipe)); + pipenode->type = NPIPE; + pipenode->npipe.backgnd = 0; + lp = (struct nodelist *)stalloc(sizeof (struct nodelist)); + pipenode->npipe.cmdlist = lp; + lp->n = n1; + do { + prev = lp; + lp = (struct nodelist *)stalloc(sizeof (struct nodelist)); + lp->n = command(); + prev->next = lp; + } while (readtoken() == TPIPE); + lp->next = NULL; + n1 = pipenode; + } + tokpushback++; + if (negate) { + notnode = (union node *)stalloc(sizeof (struct nnot)); + notnode->type = NNOT; + notnode->nnot.com = n1; + n1 = notnode; + } + return n1; +} + + + +STATIC union node * +command() { + union node *n1, *n2; + union node *ap, **app; + union node *cp, **cpp; + union node *redir, **rpp; + int t; + + checkkwd = 2; + redir = 0; + rpp = &redir; + /* Check for redirection which may precede command */ + while (readtoken() == TREDIR) { + *rpp = n2 = redirnode; + rpp = &n2->nfile.next; + parsefname(); + } + tokpushback++; + + switch (readtoken()) { + case TIF: + n1 = (union node *)stalloc(sizeof (struct nif)); + n1->type = NIF; + n1->nif.test = list(0); + if (readtoken() != TTHEN) + synexpect(TTHEN); + n1->nif.ifpart = list(0); + n2 = n1; + while (readtoken() == TELIF) { + n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif)); + n2 = n2->nif.elsepart; + n2->type = NIF; + n2->nif.test = list(0); + if (readtoken() != TTHEN) + synexpect(TTHEN); + n2->nif.ifpart = list(0); + } + if (lasttoken == TELSE) + n2->nif.elsepart = list(0); + else { + n2->nif.elsepart = NULL; + tokpushback++; + } + if (readtoken() != TFI) + synexpect(TFI); + checkkwd = 1; + break; + case TWHILE: + case TUNTIL: { + int got; + n1 = (union node *)stalloc(sizeof (struct nbinary)); + n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL; + n1->nbinary.ch1 = list(0); + if ((got=readtoken()) != TDO) { +TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : "")); + synexpect(TDO); + } + n1->nbinary.ch2 = list(0); + if (readtoken() != TDONE) + synexpect(TDONE); + checkkwd = 1; + break; + } + case TFOR: + if (readtoken() != TWORD || quoteflag || ! goodname(wordtext)) + synerror("Bad for loop variable"); + n1 = (union node *)stalloc(sizeof (struct nfor)); + n1->type = NFOR; + n1->nfor.var = wordtext; + if (readtoken() == TWORD && ! quoteflag && equal(wordtext, "in")) { + app = ≈ + while (readtoken() == TWORD) { + n2 = (union node *)stalloc(sizeof (struct narg)); + n2->type = NARG; + n2->narg.text = wordtext; + n2->narg.backquote = backquotelist; + *app = n2; + app = &n2->narg.next; + } + *app = NULL; + n1->nfor.args = ap; + if (lasttoken != TNL && lasttoken != TSEMI) + synexpect(-1); + } else { +#ifndef GDB_HACK + static const char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE, + '@', '=', '\0'}; +#endif + n2 = (union node *)stalloc(sizeof (struct narg)); + n2->type = NARG; + n2->narg.text = (char *)argvars; + n2->narg.backquote = NULL; + n2->narg.next = NULL; + n1->nfor.args = n2; + /* + * Newline or semicolon here is optional (but note + * that the original Bourne shell only allowed NL). + */ + if (lasttoken != TNL && lasttoken != TSEMI) + tokpushback++; + } + checkkwd = 2; + if ((t = readtoken()) == TDO) + t = TDONE; + else if (t == TBEGIN) + t = TEND; + else + synexpect(-1); + n1->nfor.body = list(0); + if (readtoken() != t) + synexpect(t); + checkkwd = 1; + break; + case TCASE: + n1 = (union node *)stalloc(sizeof (struct ncase)); + n1->type = NCASE; + if (readtoken() != TWORD) + synexpect(TWORD); + n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg)); + n2->type = NARG; + n2->narg.text = wordtext; + n2->narg.backquote = backquotelist; + n2->narg.next = NULL; + while (readtoken() == TNL); + if (lasttoken != TWORD || ! equal(wordtext, "in")) + synerror("expecting \"in\""); + cpp = &n1->ncase.cases; + while (checkkwd = 2, readtoken() == TWORD) { + *cpp = cp = (union node *)stalloc(sizeof (struct nclist)); + cp->type = NCLIST; + app = &cp->nclist.pattern; + for (;;) { + *app = ap = (union node *)stalloc(sizeof (struct narg)); + ap->type = NARG; + ap->narg.text = wordtext; + ap->narg.backquote = backquotelist; + if (readtoken() != TPIPE) + break; + app = &ap->narg.next; + if (readtoken() != TWORD) + synexpect(TWORD); + } + ap->narg.next = NULL; + if (lasttoken != TRP) + synexpect(TRP); + cp->nclist.body = list(0); + if ((t = readtoken()) == TESAC) + tokpushback++; + else if (t != TENDCASE) + synexpect(TENDCASE); + cpp = &cp->nclist.next; + } + *cpp = NULL; + if (lasttoken != TESAC) + synexpect(TESAC); + checkkwd = 1; + break; + case TLP: + n1 = (union node *)stalloc(sizeof (struct nredir)); + n1->type = NSUBSHELL; + n1->nredir.n = list(0); + n1->nredir.redirect = NULL; + if (readtoken() != TRP) + synexpect(TRP); + checkkwd = 1; + break; + case TBEGIN: + n1 = list(0); + if (readtoken() != TEND) + synexpect(TEND); + checkkwd = 1; + break; + /* Handle an empty command like other simple commands. */ + case TNL: + case TWORD: + tokpushback++; + return simplecmd(rpp, redir); + default: + synexpect(-1); + } + + /* Now check for redirection which may follow command */ + while (readtoken() == TREDIR) { + *rpp = n2 = redirnode; + rpp = &n2->nfile.next; + parsefname(); + } + tokpushback++; + *rpp = NULL; + if (redir) { + if (n1->type != NSUBSHELL) { + n2 = (union node *)stalloc(sizeof (struct nredir)); + n2->type = NREDIR; + n2->nredir.n = n1; + n1 = n2; + } + n1->nredir.redirect = redir; + } + return n1; +} + + +STATIC union node * +simplecmd(rpp, redir) + union node **rpp, *redir; + { + union node *args, **app; + union node **orig_rpp = rpp; + union node *n; + + /* If we don't have any redirections already, then we must reset */ + /* rpp to be the address of the local redir variable. */ + if (redir == 0) + rpp = &redir; + + args = NULL; + app = &args; + /* + * We save the incoming value, because we need this for shell + * functions. There can not be a redirect or an argument between + * the function name and the open parenthesis. + */ + orig_rpp = rpp; + + for (;;) { + if (readtoken() == TWORD) { + n = (union node *)stalloc(sizeof (struct narg)); + n->type = NARG; + n->narg.text = wordtext; + n->narg.backquote = backquotelist; + *app = n; + app = &n->narg.next; + } else if (lasttoken == TREDIR) { + *rpp = n = redirnode; + rpp = &n->nfile.next; + parsefname(); /* read name of redirection file */ + } else if (lasttoken == TLP && app == &args->narg.next + && rpp == orig_rpp) { + /* We have a function */ + if (readtoken() != TRP) + synexpect(TRP); +#ifdef notdef + if (! goodname(n->narg.text)) + synerror("Bad function name"); +#endif + n->type = NDEFUN; + n->narg.next = command(); + return n; + } else { + tokpushback++; + break; + } + } + *app = NULL; + *rpp = NULL; + n = (union node *)stalloc(sizeof (struct ncmd)); + n->type = NCMD; + n->ncmd.backgnd = 0; + n->ncmd.args = args; + n->ncmd.redirect = redir; + return n; +} + + +STATIC void +parsefname() { + union node *n = redirnode; + + if (readtoken() != TWORD) + synexpect(-1); + if (n->type == NHERE) { + struct heredoc *here = heredoc; + struct heredoc *p; + int i; + + if (quoteflag == 0) + n->type = NXHERE; + TRACE(("Here document %d\n", n->type)); + if (here->striptabs) { + while (*wordtext == '\t') + wordtext++; + } + if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN) + synerror("Illegal eof marker for << redirection"); + rmescapes(wordtext); + here->eofmark = wordtext; + here->next = NULL; + if (heredoclist == NULL) + heredoclist = here; + else { + for (p = heredoclist ; p->next ; p = p->next); + p->next = here; + } + } else if (n->type == NTOFD || n->type == NFROMFD) { + if (is_digit(wordtext[0])) + n->ndup.dupfd = digit_val(wordtext[0]); + else if (wordtext[0] == '-') + n->ndup.dupfd = -1; + else + goto bad; + if (wordtext[1] != '\0') { +bad: + synerror("Bad fd number"); + } + } else { + n->nfile.fname = (union node *)stalloc(sizeof (struct narg)); + n = n->nfile.fname; + n->type = NARG; + n->narg.next = NULL; + n->narg.text = wordtext; + n->narg.backquote = backquotelist; + } +} + + +/* + * Input any here documents. + */ + +STATIC void +parseheredoc() { + struct heredoc *here; + union node *n; + + while (heredoclist) { + here = heredoclist; + heredoclist = here->next; + if (needprompt) { + setprompt(2); + needprompt = 0; + } + readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX, + here->eofmark, here->striptabs); + n = (union node *)stalloc(sizeof (struct narg)); + n->narg.type = NARG; + n->narg.next = NULL; + n->narg.text = wordtext; + n->narg.backquote = backquotelist; + here->here->nhere.doc = n; + } +} + +STATIC int +peektoken() { + int t; + + t = readtoken(); + tokpushback++; + return (t); +} + +STATIC int xxreadtoken(); + +STATIC int +readtoken() { + int t; + int savecheckkwd = checkkwd; + struct alias *ap; +#ifdef DEBUG + int alreadyseen = tokpushback; +#endif + + top: + t = xxreadtoken(); + + if (checkkwd) { + /* + * eat newlines + */ + if (checkkwd == 2) { + checkkwd = 0; + while (t == TNL) { + parseheredoc(); + t = xxreadtoken(); + } + } else + checkkwd = 0; + /* + * check for keywords and aliases + */ + if (t == TWORD && !quoteflag) { + register char * const *pp, *s; + + for (pp = (char **)parsekwd; *pp; pp++) { + if (**pp == *wordtext && equal(*pp, wordtext)) { + lasttoken = t = pp - parsekwd + KWDOFFSET; + TRACE(("keyword %s recognized\n", tokname[t])); + goto out; + } + } + if (ap = lookupalias(wordtext, 1)) { + pushstring(ap->val, strlen(ap->val), ap); + checkkwd = savecheckkwd; + goto top; + } + } +out: + checkkwd = 0; + } +#ifdef DEBUG + if (!alreadyseen) + TRACE(("token %s %s\n", tokname[t], t == TWORD ? wordtext : "")); + else + TRACE(("reread token %s %s\n", tokname[t], t == TWORD ? wordtext : "")); +#endif + return (t); +} + + +/* + * Read the next input token. + * If the token is a word, we set backquotelist to the list of cmds in + * backquotes. We set quoteflag to true if any part of the word was + * quoted. + * If the token is TREDIR, then we set redirnode to a structure containing + * the redirection. + * In all cases, the variable startlinno is set to the number of the line + * on which the token starts. + * + * [Change comment: here documents and internal procedures] + * [Readtoken shouldn't have any arguments. Perhaps we should make the + * word parsing code into a separate routine. In this case, readtoken + * doesn't need to have any internal procedures, but parseword does. + * We could also make parseoperator in essence the main routine, and + * have parseword (readtoken1?) handle both words and redirection.] + */ + +#define RETURN(token) return lasttoken = token + +STATIC int +xxreadtoken() { + register c; + + if (tokpushback) { + tokpushback = 0; + return lasttoken; + } + if (needprompt) { + setprompt(2); + needprompt = 0; + } + startlinno = plinno; + for (;;) { /* until token or start of word found */ + c = pgetc_macro(); + if (c == ' ' || c == '\t') + continue; /* quick check for white space first */ + switch (c) { + case ' ': case '\t': + continue; + case '#': + while ((c = pgetc()) != '\n' && c != PEOF); + pungetc(); + continue; + case '\\': + if (pgetc() == '\n') { + startlinno = ++plinno; + if (doprompt) + setprompt(2); + else + setprompt(0); + continue; + } + pungetc(); + goto breakloop; + case '\n': + plinno++; + needprompt = doprompt; + RETURN(TNL); + case PEOF: + RETURN(TEOF); + case '&': + if (pgetc() == '&') + RETURN(TAND); + pungetc(); + RETURN(TBACKGND); + case '|': + if (pgetc() == '|') + RETURN(TOR); + pungetc(); + RETURN(TPIPE); + case ';': + if (pgetc() == ';') + RETURN(TENDCASE); + pungetc(); + RETURN(TSEMI); + case '(': + RETURN(TLP); + case ')': + RETURN(TRP); + default: + goto breakloop; + } + } +breakloop: + return readtoken1(c, BASESYNTAX, (char *)NULL, 0); +#undef RETURN +} + + + +/* + * If eofmark is NULL, read a word or a redirection symbol. If eofmark + * is not NULL, read a here document. In the latter case, eofmark is the + * word which marks the end of the document and striptabs is true if + * leading tabs should be stripped from the document. The argument firstc + * is the first character of the input token or document. + * + * Because C does not have internal subroutines, I have simulated them + * using goto's to implement the subroutine linkage. The following macros + * will run code that appears at the end of readtoken1. + */ + +#define CHECKEND() {goto checkend; checkend_return:;} +#define PARSEREDIR() {goto parseredir; parseredir_return:;} +#define PARSESUB() {goto parsesub; parsesub_return:;} +#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;} +#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;} +#define PARSEARITH() {goto parsearith; parsearith_return:;} + +STATIC int +readtoken1(firstc, syntax, eofmark, striptabs) + int firstc; + char const *syntax; + char *eofmark; + int striptabs; + { + register c = firstc; + register char *out; + int len; + char line[EOFMARKLEN + 1]; + struct nodelist *bqlist; + int quotef; + int dblquote; + int varnest; /* levels of variables expansion */ + int arinest; /* levels of arithmetic expansion */ + int parenlevel; /* levels of parens in arithmetic */ + int oldstyle; + char const *prevsyntax; /* syntax before arithmetic */ + + startlinno = plinno; + dblquote = 0; + if (syntax == DQSYNTAX) + dblquote = 1; + quotef = 0; + bqlist = NULL; + varnest = 0; + arinest = 0; + parenlevel = 0; + + STARTSTACKSTR(out); + loop: { /* for each line, until end of word */ +#if ATTY + if (c == '\034' && doprompt + && attyset() && ! equal(termval(), "emacs")) { + attyline(); + if (syntax == BASESYNTAX) + return readtoken(); + c = pgetc(); + goto loop; + } +#endif + CHECKEND(); /* set c to PEOF if at end of here document */ + for (;;) { /* until end of line or end of word */ + CHECKSTRSPACE(3, out); /* permit 3 calls to USTPUTC */ + if (parsebackquote && c == '\\') { + c = pgetc(); /* XXX - compat with old /bin/sh */ + if (c != '\\' && c != '`' && c != '$') { + pungetc(); + c = '\\'; + } + } + switch(syntax[c]) { + case CNL: /* '\n' */ + if (syntax == BASESYNTAX) + goto endword; /* exit outer loop */ + USTPUTC(c, out); + plinno++; + if (doprompt) + setprompt(2); + else + setprompt(0); + c = pgetc(); + goto loop; /* continue outer loop */ + case CWORD: + USTPUTC(c, out); + break; + case CCTL: + if (eofmark == NULL || dblquote) + USTPUTC(CTLESC, out); + USTPUTC(c, out); + break; + case CBACK: /* backslash */ + c = pgetc(); + if (c == PEOF) { + USTPUTC('\\', out); + pungetc(); + } else if (c == '\n') { + if (doprompt) + setprompt(2); + else + setprompt(0); + } else { + if (dblquote && c != '\\' && c != '`' && c != '$' + && (c != '"' || eofmark != NULL)) + USTPUTC('\\', out); + if (SQSYNTAX[c] == CCTL) + USTPUTC(CTLESC, out); + USTPUTC(c, out); + quotef++; + } + break; + case CSQUOTE: + syntax = SQSYNTAX; + break; + case CDQUOTE: + syntax = DQSYNTAX; + dblquote = 1; + break; + case CENDQUOTE: + if (eofmark) { + USTPUTC(c, out); + } else { + if (arinest) + syntax = ARISYNTAX; + else + syntax = BASESYNTAX; + quotef++; + dblquote = 0; + } + break; + case CVAR: /* '$' */ + PARSESUB(); /* parse substitution */ + break; + case CENDVAR: /* '}' */ + if (varnest > 0) { + varnest--; + USTPUTC(CTLENDVAR, out); + } else { + USTPUTC(c, out); + } + break; + case CLP: /* '(' in arithmetic */ + parenlevel++; + USTPUTC(c, out); + break; + case CRP: /* ')' in arithmetic */ + if (parenlevel > 0) { + USTPUTC(c, out); + --parenlevel; + } else { + if (pgetc() == ')') { + if (--arinest == 0) { + USTPUTC(CTLENDARI, out); + syntax = prevsyntax; + } else + USTPUTC(')', out); + } else { + /* + * unbalanced parens + * (don't 2nd guess - no error) + */ + pungetc(); + USTPUTC(')', out); + } + } + break; + case CBQUOTE: /* '`' */ + PARSEBACKQOLD(); + break; + case CEOF: + goto endword; /* exit outer loop */ + default: + if (varnest == 0) + goto endword; /* exit outer loop */ + USTPUTC(c, out); + } + c = pgetc_macro(); + } + } +endword: + if (syntax == ARISYNTAX) + synerror("Missing '))'"); + if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL) + synerror("Unterminated quoted string"); + if (varnest != 0) { + startlinno = plinno; + synerror("Missing '}'"); + } + USTPUTC('\0', out); + len = out - stackblock(); + out = stackblock(); + if (eofmark == NULL) { + if ((c == '>' || c == '<') + && quotef == 0 + && len <= 2 + && (*out == '\0' || is_digit(*out))) { + PARSEREDIR(); + return lasttoken = TREDIR; + } else { + pungetc(); + } + } + quoteflag = quotef; + backquotelist = bqlist; + grabstackblock(len); + wordtext = out; + return lasttoken = TWORD; +/* end of readtoken routine */ + + + +/* + * Check to see whether we are at the end of the here document. When this + * is called, c is set to the first character of the next input line. If + * we are at the end of the here document, this routine sets the c to PEOF. + */ + +checkend: { + if (eofmark) { + if (striptabs) { + while (c == '\t') + c = pgetc(); + } + if (c == *eofmark) { + if (pfgets(line, sizeof line) != NULL) { + register char *p, *q; + + p = line; + for (q = eofmark + 1 ; *q && *p == *q ; p++, q++); + if (*p == '\n' && *q == '\0') { + c = PEOF; + plinno++; + needprompt = doprompt; + } else { + pushstring(line, strlen(line), NULL); + } + } + } + } + goto checkend_return; +} + + +/* + * Parse a redirection operator. The variable "out" points to a string + * specifying the fd to be redirected. The variable "c" contains the + * first character of the redirection operator. + */ + +parseredir: { + char fd = *out; + union node *np; + + np = (union node *)stalloc(sizeof (struct nfile)); + if (c == '>') { + np->nfile.fd = 1; + c = pgetc(); + if (c == '>') + np->type = NAPPEND; + else if (c == '&') + np->type = NTOFD; + else { + np->type = NTO; + pungetc(); + } + } else { /* c == '<' */ + np->nfile.fd = 0; + c = pgetc(); + if (c == '<') { + if (sizeof (struct nfile) != sizeof (struct nhere)) { + np = (union node *)stalloc(sizeof (struct nhere)); + np->nfile.fd = 0; + } + np->type = NHERE; + heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc)); + heredoc->here = np; + if ((c = pgetc()) == '-') { + heredoc->striptabs = 1; + } else { + heredoc->striptabs = 0; + pungetc(); + } + } else if (c == '&') + np->type = NFROMFD; + else { + np->type = NFROM; + pungetc(); + } + } + if (fd != '\0') + np->nfile.fd = digit_val(fd); + redirnode = np; + goto parseredir_return; +} + + +/* + * Parse a substitution. At this point, we have read the dollar sign + * and nothing else. + */ + +parsesub: { + int subtype; + int typeloc; + int flags; + char *p; +#ifndef GDB_HACK + static const char types[] = "}-+?="; +#endif + + c = pgetc(); + if (c != '(' && c != '{' && !is_name(c) && !is_special(c)) { + USTPUTC('$', out); + pungetc(); + } else if (c == '(') { /* $(command) or $((arith)) */ + if (pgetc() == '(') { + PARSEARITH(); + } else { + pungetc(); + PARSEBACKQNEW(); + } + } else { + USTPUTC(CTLVAR, out); + typeloc = out - stackblock(); + USTPUTC(VSNORMAL, out); + subtype = VSNORMAL; + if (c == '{') { + c = pgetc(); + subtype = 0; + } + if (is_name(c)) { + do { + STPUTC(c, out); + c = pgetc(); + } while (is_in_name(c)); + } else { + if (! is_special(c)) +badsub: synerror("Bad substitution"); + USTPUTC(c, out); + c = pgetc(); + } + STPUTC('=', out); + flags = 0; + if (subtype == 0) { + if (c == ':') { + flags = VSNUL; + c = pgetc(); + } + p = strchr(types, c); + if (p == NULL) + goto badsub; + subtype = p - types + VSNORMAL; + } else { + pungetc(); + } + if (dblquote || arinest) + flags |= VSQUOTE; + *(stackblock() + typeloc) = subtype | flags; + if (subtype != VSNORMAL) + varnest++; + } + goto parsesub_return; +} + + +/* + * Called to parse command substitutions. Newstyle is set if the command + * is enclosed inside $(...); nlpp is a pointer to the head of the linked + * list of commands (passed by reference), and savelen is the number of + * characters on the top of the stack which must be preserved. + */ + +parsebackq: { + struct nodelist **nlpp; + int savepbq; + union node *n; + char *volatile str; + struct jmploc jmploc; + struct jmploc *volatile savehandler; + int savelen; + + savepbq = parsebackquote; + if (setjmp(jmploc.loc)) { + if (str) + ckfree(str); + parsebackquote = 0; + handler = savehandler; + longjmp(handler->loc, 1); + } + INTOFF; + str = NULL; + savelen = out - stackblock(); + if (savelen > 0) { + str = ckmalloc(savelen); + bcopy(stackblock(), str, savelen); + } + savehandler = handler; + handler = &jmploc; + INTON; + if (oldstyle) { + /* We must read until the closing backquote, giving special + treatment to some slashes, and then push the string and + reread it as input, interpreting it normally. */ + register char *out; + register c; + int savelen; + char *str; + + STARTSTACKSTR(out); + while ((c = pgetc ()) != '`') { + if (c == '\\') { + c = pgetc (); + if (c != '\\' && c != '`' && c != '$' + && (!dblquote || c != '"')) + STPUTC('\\', out); + } + STPUTC(c, out); + } + STPUTC('\0', out); + savelen = out - stackblock(); + if (savelen > 0) { + str = ckmalloc(savelen); + bcopy(stackblock(), str, savelen); + } + setinputstring(str, 1); + } + nlpp = &bqlist; + while (*nlpp) + nlpp = &(*nlpp)->next; + *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist)); + (*nlpp)->next = NULL; + parsebackquote = oldstyle; + n = list(0); + if (!oldstyle && (readtoken() != TRP)) + synexpect(TRP); + (*nlpp)->n = n; + /* Start reading from old file again. */ + if (oldstyle) + popfile(); + while (stackblocksize() <= savelen) + growstackblock(); + STARTSTACKSTR(out); + if (str) { + bcopy(str, out, savelen); + STADJUST(savelen, out); + INTOFF; + ckfree(str); + str = NULL; + INTON; + } + parsebackquote = savepbq; + handler = savehandler; + if (arinest || dblquote) + USTPUTC(CTLBACKQ | CTLQUOTE, out); + else + USTPUTC(CTLBACKQ, out); + if (oldstyle) + goto parsebackq_oldreturn; + else + goto parsebackq_newreturn; +} + +/* + * Parse an arithmetic expansion (indicate start of one and set state) + */ +parsearith: { + + if (++arinest == 1) { + prevsyntax = syntax; + syntax = ARISYNTAX; + USTPUTC(CTLARI, out); + } else { + /* + * we collapse embedded arithmetic expansion to + * parenthesis, which should be equivalent + */ + USTPUTC('(', out); + } + goto parsearith_return; +} + +} /* end of readtoken */ + + + +#ifdef mkinit +RESET { + tokpushback = 0; + checkkwd = 0; +} +#endif + +/* + * Returns true if the text contains nothing to expand (no dollar signs + * or backquotes). + */ + +STATIC int +noexpand(text) + char *text; + { + register char *p; + register char c; + + p = text; + while ((c = *p++) != '\0') { + if (c == CTLESC) + p++; + else if (BASESYNTAX[c] == CCTL) + return 0; + } + return 1; +} + + +/* + * Return true if the argument is a legal variable name (a letter or + * underscore followed by zero or more letters, underscores, and digits). + */ + +int +goodname(name) + char *name; + { + register char *p; + + p = name; + if (! is_name(*p)) + return 0; + while (*++p) { + if (! is_in_name(*p)) + return 0; + } + return 1; +} + + +/* + * Called when an unexpected token is read during the parse. The argument + * is the token that is expected, or -1 if more than one type of token can + * occur at this point. + */ + +STATIC void +synexpect(token) { + char msg[64]; + + if (token >= 0) { + fmtstr(msg, 64, "%s unexpected (expecting %s)", + tokname[lasttoken], tokname[token]); + } else { + fmtstr(msg, 64, "%s unexpected", tokname[lasttoken]); + } + synerror(msg); +} + + +STATIC void +synerror(msg) + char *msg; + { + if (commandname) + outfmt(&errout, "%s: %d: ", commandname, startlinno); + outfmt(&errout, "Syntax error: %s\n", msg); + error((char *)NULL); +} + +STATIC void +setprompt(which) + int which; + { + whichprompt = which; + + if (!el) + out2str(getprompt(NULL)); +} + +/* + * called by editline -- any expansions to the prompt + * should be added here. + */ +char * +getprompt(unused) + void *unused; + { + switch (whichprompt) { + case 0: + return ""; + case 1: + return ps1val(); + case 2: + return ps2val(); + default: + return "<internal prompt error>"; + } +} diff --git a/bin/sh/parser.h b/bin/sh/parser.h new file mode 100644 index 0000000..e1c3c23 --- /dev/null +++ b/bin/sh/parser.h @@ -0,0 +1,80 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)parser.h 8.1 (Berkeley) 5/31/93 + */ + +/* control characters in argument strings */ +#define CTLESC '\201' +#define CTLVAR '\202' +#define CTLENDVAR '\203' +#define CTLBACKQ '\204' +#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */ +/* CTLBACKQ | CTLQUOTE == '\205' */ +#define CTLARI '\206' +#define CTLENDARI '\207' + +/* variable substitution byte (follows CTLVAR) */ +#define VSTYPE 07 /* type of variable substitution */ +#define VSNUL 040 /* colon--treat the empty string as unset */ +#define VSQUOTE 0100 /* inside double quotes--suppress splitting */ + +/* values of VSTYPE field */ +#define VSNORMAL 1 /* normal variable: $var or ${var} */ +#define VSMINUS 2 /* ${var-text} */ +#define VSPLUS 3 /* ${var+text} */ +#define VSQUESTION 4 /* ${var?message} */ +#define VSASSIGN 5 /* ${var=text} */ + + +/* + * NEOF is returned by parsecmd when it encounters an end of file. It + * must be distinct from NULL, so we use the address of a variable that + * happens to be handy. + */ +extern int tokpushback; +#define NEOF ((union node *)&tokpushback) +extern int whichprompt; /* 1 == PS1, 2 == PS2 */ + + +#ifdef __STDC__ +union node *parsecmd(int); +int goodname(char *); +char *getprompt(void *); +#else +union node *parsecmd(); +int goodname(); +char *getprompt(); +#endif diff --git a/bin/sh/redir.c b/bin/sh/redir.c new file mode 100644 index 0000000..b91ce5a --- /dev/null +++ b/bin/sh/redir.c @@ -0,0 +1,343 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)redir.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +/* + * Code for dealing with input/output redirection. + */ + +#include "shell.h" +#include "nodes.h" +#include "jobs.h" +#include "expand.h" +#include "redir.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include <sys/types.h> +#include <signal.h> +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> + + +#define EMPTY -2 /* marks an unused slot in redirtab */ +#define PIPESIZE 4096 /* amount of buffering in a pipe */ + + +MKINIT +struct redirtab { + struct redirtab *next; + short renamed[10]; +}; + + +MKINIT struct redirtab *redirlist; + +/* + * We keep track of whether or not fd0 has been redirected. This is for + * background commands, where we want to redirect fd0 to /dev/null only + * if it hasn't already been redirected. +*/ +int fd0_redirected = 0; + +#ifdef __STDC__ +STATIC void openredirect(union node *, char *); +STATIC int openhere(union node *); +#else +STATIC void openredirect(); +STATIC int openhere(); +#endif + + + +/* + * Process a list of redirection commands. If the REDIR_PUSH flag is set, + * old file descriptors are stashed away so that the redirection can be + * undone by calling popredir. If the REDIR_BACKQ flag is set, then the + * standard output, and the standard error if it becomes a duplicate of + * stdout, is saved in memory. + */ + +void +redirect(redir, flags) + union node *redir; + int flags; + { + union node *n; + struct redirtab *sv; + int i; + int fd; + char memory[10]; /* file descriptors to write to memory */ + + for (i = 10 ; --i >= 0 ; ) + memory[i] = 0; + memory[1] = flags & REDIR_BACKQ; + if (flags & REDIR_PUSH) { + sv = ckmalloc(sizeof (struct redirtab)); + for (i = 0 ; i < 10 ; i++) + sv->renamed[i] = EMPTY; + sv->next = redirlist; + redirlist = sv; + } + for (n = redir ; n ; n = n->nfile.next) { + fd = n->nfile.fd; + if ((flags & REDIR_PUSH) && sv->renamed[fd] == EMPTY) { + INTOFF; + if ((i = copyfd(fd, 10)) != EMPTY) { + sv->renamed[fd] = i; + close(fd); + } + INTON; + if (i == EMPTY) + error("Out of file descriptors"); + } else { + close(fd); + } + if (fd == 0) + fd0_redirected++; + openredirect(n, memory); + } + if (memory[1]) + out1 = &memout; + if (memory[2]) + out2 = &memout; +} + + +STATIC void +openredirect(redir, memory) + union node *redir; + char memory[10]; + { + int fd = redir->nfile.fd; + char *fname; + int f; + + /* + * We suppress interrupts so that we won't leave open file + * descriptors around. This may not be such a good idea because + * an open of a device or a fifo can block indefinitely. + */ + INTOFF; + memory[fd] = 0; + switch (redir->nfile.type) { + case NFROM: + fname = redir->nfile.expfname; + if ((f = open(fname, O_RDONLY)) < 0) + error("cannot open %s: %s", fname, errmsg(errno, E_OPEN)); +movefd: + if (f != fd) { + copyfd(f, fd); + close(f); + } + break; + case NTO: + fname = redir->nfile.expfname; +#ifdef O_CREAT + if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) + error("cannot create %s: %s", fname, errmsg(errno, E_CREAT)); +#else + if ((f = creat(fname, 0666)) < 0) + error("cannot create %s: %s", fname, errmsg(errno, E_CREAT)); +#endif + goto movefd; + case NAPPEND: + fname = redir->nfile.expfname; +#ifdef O_APPEND + if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0) + error("cannot create %s: %s", fname, errmsg(errno, E_CREAT)); +#else + if ((f = open(fname, O_WRONLY)) < 0 + && (f = creat(fname, 0666)) < 0) + error("cannot create %s: %s", fname, errmsg(errno, E_CREAT)); + lseek(f, (off_t)0, 2); +#endif + goto movefd; + case NTOFD: + case NFROMFD: + if (redir->ndup.dupfd >= 0) { /* if not ">&-" */ + if (memory[redir->ndup.dupfd]) + memory[fd] = 1; + else + copyfd(redir->ndup.dupfd, fd); + } + break; + case NHERE: + case NXHERE: + f = openhere(redir); + goto movefd; + default: + abort(); + } + INTON; +} + + +/* + * Handle here documents. Normally we fork off a process to write the + * data to a pipe. If the document is short, we can stuff the data in + * the pipe without forking. + */ + +STATIC int +openhere(redir) + union node *redir; + { + int pip[2]; + int len; + + if (pipe(pip) < 0) + error("Pipe call failed"); + if (redir->type == NHERE) { + len = strlen(redir->nhere.doc->narg.text); + if (len <= PIPESIZE) { + xwrite(pip[1], redir->nhere.doc->narg.text, len); + goto out; + } + } + if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) { + close(pip[0]); + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + signal(SIGHUP, SIG_IGN); +#ifdef SIGTSTP + signal(SIGTSTP, SIG_IGN); +#endif + signal(SIGPIPE, SIG_DFL); + if (redir->type == NHERE) + xwrite(pip[1], redir->nhere.doc->narg.text, len); + else + expandhere(redir->nhere.doc, pip[1]); + _exit(0); + } +out: + close(pip[1]); + return pip[0]; +} + + + +/* + * Undo the effects of the last redirection. + */ + +void +popredir() { + register struct redirtab *rp = redirlist; + int i; + + for (i = 0 ; i < 10 ; i++) { + if (rp->renamed[i] != EMPTY) { + if (i == 0) + fd0_redirected--; + close(i); + if (rp->renamed[i] >= 0) { + copyfd(rp->renamed[i], i); + close(rp->renamed[i]); + } + } + } + INTOFF; + redirlist = rp->next; + ckfree(rp); + INTON; +} + +/* + * Undo all redirections. Called on error or interrupt. + */ + +#ifdef mkinit + +INCLUDE "redir.h" + +RESET { + while (redirlist) + popredir(); +} + +SHELLPROC { + clearredir(); +} + +#endif + +/* Return true if fd 0 has already been redirected at least once. */ +int +fd0_redirected_p () { + return fd0_redirected != 0; +} + +/* + * Discard all saved file descriptors. + */ + +void +clearredir() { + register struct redirtab *rp; + int i; + + for (rp = redirlist ; rp ; rp = rp->next) { + for (i = 0 ; i < 10 ; i++) { + if (rp->renamed[i] >= 0) { + close(rp->renamed[i]); + } + rp->renamed[i] = EMPTY; + } + } +} + + + +/* + * Copy a file descriptor to be >= to. Returns -1 + * if the source file descriptor is closed, EMPTY if there are no unused + * file descriptors left. + */ + +int +copyfd(from, to) { + int newfd; + + newfd = fcntl(from, F_DUPFD, to); + if (newfd < 0 && errno == EMFILE) + return EMPTY; + return newfd; +} diff --git a/bin/sh/redir.h b/bin/sh/redir.h new file mode 100644 index 0000000..81cc760 --- /dev/null +++ b/bin/sh/redir.h @@ -0,0 +1,56 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)redir.h 8.1 (Berkeley) 5/31/93 + */ + +/* flags passed to redirect */ +#define REDIR_PUSH 01 /* save previous values of file descriptors */ +#define REDIR_BACKQ 02 /* save the command output in memory */ + +#ifdef __STDC__ +union node; +void redirect(union node *, int); +void popredir(void); +void clearredir(void); +int copyfd(int, int); +int fd0_redirected_p(void); +#else +void redirect(); +void popredir(); +void clearredir(); +int copyfd(); +int fd0_redirected_p(); +#endif diff --git a/bin/sh/sh.1 b/bin/sh/sh.1 new file mode 100644 index 0000000..8bec2e7 --- /dev/null +++ b/bin/sh/sh.1 @@ -0,0 +1,1307 @@ +.\" Copyright (c) 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Kenneth Almquist. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)sh.1 8.4 (Berkeley) 4/18/94 +.\" +.na +.TH SH 1 +.SH NAME +sh \- command interpreter (shell) +.SH SYNOPSIS +sh [-/+aCefnuvxIimsVEb] [-/+o longname] [arg ...] +.SH DESCRIPTION +.LP +Sh is the standard command interpreter for the system. +The current version of sh is in the process of being changed to +conform with the POSIX 1003.2 and 1003.2a specifications for +the shell. This version has many features which make it appear +similar in some respects to the Korn shell, but it is not a Korn +shell clone (run GNU's bash if you want that). Only features +designated by POSIX, plus a few Berkeley extensions, are being +incorporated into this shell. We expect POSIX conformance by the +time 4.4 BSD is released. +This man page is not intended to be a tutorial or a complete +specification of the shell. +.sp 2 +.B Overview +.sp +.LP +The shell is a command that reads lines from +either a file or the terminal, interprets them, and +generally executes other commands. It is the program that is running +when a user logs into the system (although a user can select +a different shell with the chsh(1) command). +The shell +implements a language that has flow control constructs, +a macro facility that provides a variety of features in +addition to data storage, along with built in history and line +editing capabilities. It incorporates many features to +aid interactive use and has the advantage that the interpretative +language is common to both interactive and non-interactive +use (shell scripts). That is, commands can be typed directly +to the running shell or can be put into a file and the file +can be executed directly by the shell. +.sp 2 +.B Invocation +.sp +.LP +If no args are present and if the standard input of the shell +is connected to a terminal (or if the -i flag is set), the shell +is considered an interactive shell. An interactive shell +generally prompts before each command and handles programming +and command errors differently (as described below). +When first starting, the shell inspects argument 0, and +if it begins with a dash '-', the shell is also considered +a login shell. This is normally done automatically by the system +when the user first logs in. A login shell first reads commands +from the files /etc/profile and .profile if they exist. +If the environment variable ENV is set on entry to a shell, +or is set in the .profile of a login shell, the shell next reads +commands from the file named in ENV. Therefore, a user should +place commands that are to be executed only at login time in +the .profile file, and commands that are executed for every +shell inside the ENV file. To set the ENV variable to some +file, place the following line in your .profile of your home +directory +.nf + + ENV=$HOME/.shinit; export ENV + +.fi +substituting for ``.shinit'' any filename you wish. +Since the ENV file is read for +every invocation of the shell, including shell scripts and +non-interactive shells, the following paradigm is useful +for restricting commands in the ENV file to interactive invocations. +Place commands within the ``case'' and ``esac'' below (these +commands are described later): +.nf + + case $- in *i*) + # commands for interactive use only + ... + esac + +.fi +If command line arguments besides the options have been +specified, then the shell treats the first argument as the +name of a file from which to read commands (a shell script), and +the remaining arguments are set as the positional parameters +of the shell ($1, $2, etc). Otherwise, the shell reads commands +from its standard input. +.sp 2 +.B Argument List Processing +.sp +.LP +All of the single letter options have a corresponding name +that can be used as an argument to the '-o' option. The +set -o name is provided next to the single letter option in +the description below. +Specifying a dash ``-'' turns the option on, while using a plus ``+'' +disables the option. +The following options can be set from the command line or +with the set(1) builtin (described later). +.TP +-a allexport +Export all variables assigned to. +(UNIMPLEMENTED for 4.4alpha) +.TP +-C noclobber +Don't overwrite existing files with ``>''. +(UNIMPLEMENTED for 4.4alpha) +.TP +-e errexit +If not interactive, exit immediately if any +untested command fails. +The exit status of a command is considered to be +explicitly tested if the command is used to control +an if, elif, while, or until; or if the command is the left +hand operand of an ``&&'' or ``||'' operator. + +.TP +-f noglob +Disable pathname expansion. +.TP +-n noexec +If not interactive, read commands but do not +execute them. This is useful for checking the +syntax of shell scripts. +.TP +-u nounset +Write a message to standard error when attempting +to expand a variable that is not set, and if the +shell is not interactive, exit immediately. +(UNIMPLEMENTED for 4.4alpha) +.TP +-v verbose +The shell writes its input to standard error +as it is read. Useful for debugging. +.TP +-x xtrace +Write each command to standard error (preceded +by a '+ ') before it is executed. Useful for +debugging. +.TP +-I ignoreeof +Ignore EOF's from input when interactive. +.TP +-i interactive +Force the shell to behave interactively. +.TP +-m monitor +Turn on job control (set automatically when +interactive). +.TP +-s stdin +Read commands from standard input (set automatically +if no file arguments are present). This option has +no effect when set after the shell has already started +running (i.e. with set(1)). +.TP +-V vi +Enable the builtin vi(1) command line editor (disables +-E if it has been set). +.TP +-E emacs +Enable the builtin emacs(1) command line editor (disables +-V if it has been set). +.TP +-b notify +Enable asynchronous notification of background job +completion. +(UNIMPLEMENTED for 4.4alpha) +.LP +.sp 2 +.B Lexical Structure +.sp +.LP +The shell reads input in terms of lines from a file and breaks +it up into words at whitespace (blanks and tabs), and at +certain sequences of +characters that are special to the shell called ``operators''. +There are two types of operators: control operators and +redirection operators (their meaning is discussed later). +Following is a list of operators: +.nf +.sp +Control operators: & && ( ) ; ;; | || <newline> +.sp +Redirection operator: < > >| << >> <& >& <<- <> +.sp +.fi +.sp 2 +.B Quoting +.sp +.LP +Quoting is used to remove the special meaning of certain characters +or words to the shell, such as operators, whitespace, or +keywords. There are three types of quoting: matched single quotes, +matched double quotes, and backslash. +.sp 2 +.B Backslash +.sp +.LP +A backslash preserves the literal meaning of the following +character, with the exception of <newline>. A backslash preceding +a <newline> is treated as a line continuation. +.sp 2 +.B Single Quotes +.sp +.LP +Enclosing characters in single quotes preserves the literal +meaning of all the characters. +.sp 2 +.B Double Quotes +.sp +.LP +Enclosing characters within double quotes preserves the literal +meaning of all characters except dollarsign ($), backquote (`), +and backslash (\\). The backslash inside double quotes is +historically weird, and serves to quote only the following +characters: $ ` " \\ <newline>. +Otherwise it remains literal. +.sp 2 +.B Reserved Words +.sp +.LP +Reserved words are words that have special meaning to the +shell and are recognized at the beginning of a line and +after a control operator. The following are reserved words: +.nf + + ! elif fi while case + else for then { } + do done until if esac + +.fi +Their meaning is discussed later. +.sp 2 +.B Aliases +.sp +.LP +An alias is a name and corresponding value set using the alias(1) +builtin command. Whenever a reserved word may occur (see above), +and after checking for reserved words, the shell +checks the word to see if it matches an alias. If it does, +it replaces it in the input stream with its value. For example, +if there is an alias called ``lf'' with the value ``ls -F'', +then the input +.nf + + lf foobar <return> + + would become + + ls -F foobar <return> + +.fi +.LP +Aliases provide a convenient way for naive users to +create shorthands for commands without having to learn how +to create functions with arguments. They can also be +used to create lexically obscure code. This use is discouraged. +.sp 2 +.B Commands +.sp +.LP +The shell interprets the words it reads according to a +language, the specification of which is outside the scope +of this man page (refer to the BNF in the POSIX 1003.2 +document). Essentially though, a line is read and if +the first word of the line (or after a control operator) +is not a reserved word, then the shell has recognized a +simple command. Otherwise, a complex command or some +other special construct may have been recognized. +.sp 2 +.B Simple Commands +.sp +.LP +If a simple command has been recognized, the shell performs +the following actions: +.sp +1) Leading words of the form ``name=value'' are +stripped off and assigned to the environment of +the simple command. Redirection operators and +their arguments (as described below) are stripped +off and saved for processing. +.sp +2) The remaining words are expanded as described in +the section called ``Expansions'', and the +first remaining word is considered the command +name and the command is located. The remaining +words are considered the arguments of the command. +If no command name resulted, then the ``name=value'' +variable assignments recognized in 1) affect the +current shell. +.sp +3) Redirections are performed as described in +the next section. +.sp 2 +.B Redirections +.sp +.LP +Redirections are used to change where a command reads its input +or sends its output. In general, redirections open, close, or +duplicate an existing reference to a file. The overall format +used for redirection is: +.nf + + [n] redir-op file + +.fi +where redir-op is one of the redirection operators mentioned +previously. Following is a list of the possible redirections. +The [n] is an optional number, as in '3' (not '[3]'), that +refers to a file descriptor. +.TP +[n]> file +Redirect standard output (or n) to file. +.TP +[n]>| file +Same, but override the -C option. +.TP +[n]>> file +Append standard output (or n) to file. +.TP +[n]< file +Redirect standard input (or n) from file. +.TP +[n1]<&n2 +Duplicate standard input (or n1) from +file descriptor n2. +.TP +[n]<&- +Close standard input (or n). +.TP +[n1]>&n2 +Duplicate standard output (or n) from +n2. +.TP +[n]>&- +Close standard output (or n). +.TP +[n]<> file +Open file for reading and writing on +standard input (or n). +.LP +The following redirection is often called a ``here-document''. +.nf + + [n]<< delimiter + here-doc-text... + delimiter + +.fi +All the text on successive lines up to the delimiter is +saved away and made available to the command on standard +input, or file descriptor n if it is specified. If the delimiter +as specified on the initial line is quoted, then the here-doc-text +is treated literally, otherwise the text is subjected to +parameter expansion, command substitution, and arithmetic +expansion (as described in the section on ``Expansions''). If +the operator is ``<<-'' instead of ``<<'', then leading tabs +in the here-doc-text are stripped. +.sp 2 +.B Search and Execution +.sp +.LP +There are three types of commands: shell functions, builtin commands, and normal programs -- and the +command is searched for (by name) in that order. They +each are executed in a different way. +.LP +When a shell function is executed, all of the shell positional parameters (except $0, which remains unchanged) are +set to the arguments of the shell function. +The variables which are explicitly placed in the environment of +the command (by placing assignments to them before the +function name) are made local to the function and are set +to the values given. Then the command given in the function +definition is executed. The positional parameters are +restored to their original values when the command completes. +.LP +Shell builtins are executed internally to the shell, without spawning a new process. +.LP +Otherwise, if the command name doesn't match a function +or builtin, the command is searched for as a normal +program in the filesystem (as described in the next section). +When a normal program is executed, the shell runs the program, +passing the arguments and the environment to the +program. If the program is a shell procedure, the shell +will interpret the program in a subshell. The shell will +reinitialize itself in this case, so that the effect will +be as if a new shell had been invoked to handle the shell +procedure, except that the location of commands located in +the parent shell will be remembered by the child. +.sp 2 +.B Path Search +.sp +.LP +When locating a command, the shell first looks to see if +it has a shell function by that name. Then it looks for a +builtin command by that name. +Finally, it searches each +entry in PATH in turn for the command. +.LP +The value of the PATH variable should be a series of +entries separated by colons. Each entry consists of a +directory name. +The current directory +may be indicated by an empty directory name. +.LP +Command names containing a slash are simply executed without performing any of the above searches. +.sp 2 +.B Command Exit Status +.sp +.LP +Each command has an exit status that can influence the behavior +of other shell commands. The paradigm is that a command exits +with zero for normal or success, and non-zero for failure, +error, or a false indication. The man page for each command +should indicate the various exit codes and what they mean. +Additionally, the builtin commands return exit codes, as does +an executed function. +.sp 2 +.B Complex Commands +.sp +.LP +Complex commands are combinations of simple commands +with control operators or reserved words, together creating a larger complex +command. More generally, a command is one of the following: +.nf + + - simple command + + - pipeline + + - list or compound-list + + - compound command + + - function definition + +.fi +.LP +Unless otherwise stated, the exit status of a command is +that of the last simple command executed by the command. +.sp 2 +.B Pipeline +.sp +.LP +A pipeline is a sequence of one or more commands separated +by the control operator |. The standard output of all but +the last command is connected to the standard input +of the next command. +.LP +The format for a pipeline is: +.nf + +[!] command1 [ | command2 ...] + +.fi +.LP +The standard output of command1 is connected to the standard +input of command2. The standard input, standard output, or +both of a command is considered to be assigned by the +pipeline before any redirection specified by redirection +operators that are part of the command. +.LP +If the pipeline is not in the background (discussed later), +the shell waits for all commands to complete. +.LP +If the reserved word ! does not precede the pipeline, the +exit status is the exit status of the last command specified +in the pipeline. Otherwise, the exit status is the logical +NOT of the exit status of the last command. That is, if +the last command returns zero, the exit status is 1; if +the last command returns greater than zero, the exit status +is zero. +.LP +Because pipeline assignment of standard input or standard +output or both takes place before redirection, it can be +modified by redirection. For example: +.nf + +$ command1 2>&1 | command2 + +.fi +sends both the standard output and standard error of command1 +to the standard input of command2. +.LP +A ; or <newline> terminator causes the preceding +AND-OR-list (described next) to be executed sequentially; a & causes +asynchronous execution of the preceding AND-OR-list. +.sp 2 +.B Background Commands -- & +.sp +.LP +If a command is terminated by the control operator ampersand +(&), the shell executes the command asynchronously -- that is, +the shell does not wait for +the command to finish before executing the next command. +.LP +The format for running a command in background is: +.nf + +command1 & [command2 & ...] + +.fi +If the shell is not interactive, the standard input of an +asynchronous command is set to /dev/null. +.sp 2 +.B Lists -- Generally Speaking +.sp +.LP +A list is a sequence of zero or more commands separated by +newlines, semicolons, or ampersands, +and optionally terminated by one of these three characters. +The commands in a +list are executed in the order they are written. +If command is followed by an ampersand, the shell starts the +command and immediately proceed onto the next command; +otherwise it waits for the command to terminate before +proceeding to the next one. +.LP +``&&'' and ``||'' are AND-OR list operators. ``&&'' executes +the first command, and then executes the second command +iff the exit status of the first command is zero. ``||'' +is similar, but executes the second command iff the exit +status of the first command is nonzero. ``&&'' and ``||'' +both have the same priority. +.LP +The syntax of the if command is +.nf + + if list + then list + [ elif list + then list ] ... + [ else list ] + fi + +.fi +The syntax of the while command is +.nf + + while list + do list + done + +.fi +The two lists are executed repeatedly while the exit status of the first list is zero. The until command is similar, but has the word until in place of while +repeat until the exit status of the first list is zero. +.LP +The syntax of the for command is +.nf + + for variable in word... + do list + done + +.fi +The words are expanded, and then the list is executed +repeatedly with the variable set to each word in turn. do +and done may be replaced with ``{'' and ``}''. +.LP +The syntax of the break and continue command is +.nf + + break [ num ] + continue [ num ] + +.fi +Break terminates the num innermost for or while loops. +Continue continues with the next iteration of the innermost loop. These are implemented as builtin commands. +.LP +The syntax of the case command is +.nf + + case word in + pattern) list ;; + ... + esac + +.fi +.LP +The pattern can actually be one or more patterns (see Shell +Patterns described later), separated by ``|'' characters. + +.LP +Commands may be grouped by writing either +.nf + + (list) + +.fi +or +.nf + + { list; } + +.fi +The first of these executes the commands in a subshell. +.sp 2 +.B Functions +.sp +.LP +The syntax of a function definition is +.nf + + name ( ) command + +.fi +.LP +A function definition is an executable statement; when +executed it installs a function named name and returns an +exit status of zero. The command is normally a list +enclosed between ``{'' and ``}''. +.LP +Variables may be declared to be local to a function by +using a local command. This should appear as the first +statement of a function, and the syntax is +.nf + + local [ variable | - ] ... + +.fi +Local is implemented as a builtin command. +.LP +When a variable is made local, it inherits the initial +value and exported and readonly flags from the variable +with the same name in the surrounding scope, if there is +one. Otherwise, the variable is initially unset. The shell +uses dynamic scoping, so that if you make the variable x +local to function f, which then calls function g, references to the variable x made inside g will refer to the +variable x declared inside f, not to the global variable +named x. +.LP +The only special parameter than can be made local is +``-''. Making ``-'' local any shell options that are +changed via the set command inside the function to be +restored to their original values when the function +returns. +.LP +The syntax of the return command is +.nf + + return [ exitstatus ] + +.fi +It terminates the currently executing function. Return is +implemented as a builtin command. +.sp 2 +.B Variables and Parameters +.sp +.LP +The shell maintains a set of parameters. A parameter +denoted by a name is called a variable. When starting up, +the shell turns all the environment variables into shell +variables. New variables can be set using the form +.nf + + name=value + +.fi +.LP +Variables set by the user must have a name consisting solely +of alphabetics, numerics, and underscores - the first of which +must not be numeric. A parameter can also be denoted by a number +or a special character as explained below. +.sp 2 +.B Positional Parameters +.sp +.LP +A positional parameter is a parameter denoted by a number (n > 0). +The shell sets these initially to the values of its command +line arguments that follow the name of the shell script. +The set(1) builtin can also be used to set or reset them. +.sp 2 +.B Special Parameters +.sp +.LP +A special parameter is a parameter denoted by one of the following +special characters. The value of the parameter is listed +next to its character. +.TP +* +Expands to the positional parameters, starting from one. When +the expansion occurs within a double-quoted string +it expands to a single field with the value of each parameter +separated by the first character of the IFS variable, or by a +<space> if IFS is unset. +.TP +@ +Expands to the positional parameters, starting from one. When +the expansion occurs within double-quotes, each positional +parameter expands as a separate argument. +If there are no positional parameters, the +expansion of @ generates zero arguments, even when @ is +double-quoted. What this basically means, for example, is +if $1 is ``abc'' and $2 is ``def ghi'', then "$@" expands to +the two arguments: + +"abc" "def ghi" +.TP +# +Expands to the number of positional parameters. +.TP +? +Expands to the exit status of the most recent pipeline. +.TP +- (Hyphen) +Expands to the current option flags (the single-letter +option names concatenated into a string) as specified on +invocation, by the set builtin command, or implicitly +by the shell. +.TP +$ +Expands to the process ID of the invoked shell. A subshell +retains the same value of $ as its parent. +.TP +! +Expands to the process ID of the most recent background +command executed from the current shell. For a +pipeline, the process ID is that of the last command in the +pipeline. +.TP +0 (Zero.) +Expands to the name of the shell or shell script. +.LP +.sp 2 +.B Word Expansions +.sp +.LP +This clause describes the various expansions that are +performed on words. Not all expansions are performed on +every word, as explained later. +.LP +Tilde expansions, parameter expansions, command substitutions, +arithmetic expansions, and quote removals that occur within +a single word expand to a single field. It is only field +splitting or pathname expansion that can create multiple +fields from a single word. The single exception to this +rule is the expansion of the special parameter @ within +double-quotes, as was described above. +.LP +The order of word expansion is: +.LP +(1) Tilde Expansion, Parameter Expansion, Command Substitution, +Arithmetic Expansion (these all occur at the same time). +.LP +(2) Field Splitting is performed on fields +generated by step (1) unless the IFS variable is null. +.LP +(3) Pathname Expansion (unless set -f is in effect). +.LP +(4) Quote Removal. +.LP +The $ character is used to introduce parameter expansion, command +substitution, or arithmetic evaluation. +.sp 2 +.B Tilde Expansion (substituting a user's home directory) +.sp +.LP +A word beginning with an unquoted tilde character (~) is +subjected to tilde expansion. All the characters up to +a slash (/) or the end of the word are treated as a username +and are replaced with the user's home directory. If the +username is missing (as in ~/foobar), the tilde is replaced +with the value of the HOME variable (the current user's +home directory). + +.sp 2 +.B Parameter Expansion +.sp +.LP +The format for parameter expansion is as follows: +.nf + + ${expression} + +.fi +where expression consists of all characters until the matching }. Any } +escaped by a backslash or within a quoted string, and characters in +embedded arithmetic expansions, command substitutions, and variable +expansions, are not examined in determining the matching }. +.LP +The simplest form for parameter expansion is: +.nf + + ${parameter} + +.fi +The value, if any, of parameter is substituted. +.LP +The parameter name or symbol can be enclosed in braces, which are +optional except for positional parameters with more than one digit or +when parameter is followed by a character that could be interpreted as +part of the name. +If a parameter expansion occurs inside +double-quotes: +.LP +1) Pathname expansion is not performed on the results of the +expansion. +.LP +2) Field splitting is not performed on the results of the +expansion, with the exception of @. +.LP +In addition, a parameter expansion can be modified by using one of the +following formats. +.sp +.TP +${parameter:-word} +Use Default Values. If parameter is unset or +null, the expansion of word is +substituted; otherwise, the value of +parameter is substituted. +.TP +${parameter:=word} +Assign Default Values. If parameter is unset +or null, the expansion of word is +assigned to parameter. In all cases, the +final value of parameter is +substituted. Only variables, not positional +parameters or special parameters, can be +assigned in this way. +.TP +${parameter:?[word]} +Indicate Error if Null or Unset. If +parameter is unset or null, the expansion of +word (or a message indicating it is unset if +word is omitted) is written to standard +error and the shell exits with a nonzero +exit status. Otherwise, the value of +parameter is substituted. An +interactive shell need not exit. +.TP +${parameter:+word} +Use Alternate Value. If parameter is unset +or null, null is substituted; +otherwise, the expansion of word is +substituted. +.LP +In the parameter expansions shown previously, use of the colon in the +format results in a test for a parameter that is unset or null; omission +of the colon results in a test for a parameter that is only unset. +.TP +${#parameter} +String Length. The length in characters of +the value of parameter. +.LP +The following four varieties of parameter expansion provide for substring +processing. In each case, pattern matching notation (see Shell Patterns), rather +than regular expression notation, is used to evaluate the patterns. +If parameter is * or @, the result of the expansion is unspecified. +Enclosing the full parameter expansion string in double-quotes does not +cause the following four varieties of pattern characters to be quoted, +whereas quoting characters within the braces has this effect. +(UNIMPLEMENTED IN 4.4alpha) +.TP +${parameter%word} +Remove Smallest Suffix Pattern. The word +is expanded to produce a pattern. The +parameter expansion then results in +parameter, with the smallest portion of the +suffix matched by the pattern deleted. + +.TP +${parameter%%word} +Remove Largest Suffix Pattern. The word +is expanded to produce a pattern. The +parameter expansion then results in +parameter, with the largest portion of the +suffix matched by the pattern deleted. +.TP +${parameter#word} +Remove Smallest Prefix Pattern. The word +is expanded to produce a pattern. The +parameter expansion then results in +parameter, with the smallest portion of the +prefix matched by the pattern deleted. +.TP +${parameter##word} +Remove Largest Prefix Pattern. The word +is expanded to produce a pattern. The +parameter expansion then results in +parameter, with the largest portion of the +prefix matched by the pattern deleted. +.LP +.sp 2 +.B Command Substitution +.sp +.LP +Command substitution allows the output of a command to be substituted in +place of the command name itself. Command substitution occurs when +the command is enclosed as follows: +.nf + + $(command) + +.fi +or (``backquoted'' version): +.nf + + `command` + +.fi +.LP +The shell expands the command substitution by executing command in a +subshell environment and replacing the command substitution +with the +standard output of the command, removing sequences of one or more +<newline>s at the end of the substitution. (Embedded <newline>s before +the end of the output are not removed; however, during field +splitting, they may be translated into <space>s, depending on the value +of IFS and quoting that is in effect.) + +.sp 2 +.B Arithmetic Expansion +.sp +.LP +Arithmetic expansion provides a mechanism for evaluating an arithmetic +expression and substituting its value. The format for arithmetic +expansion is as follows: +.nf + + $((expression)) + +.fi +The expression is treated as if it were in double-quotes, except +that a double-quote inside the expression is not treated specially. The +shell expands all tokens in the expression for parameter expansion, +command substitution, and quote removal. +.LP +Next, the shell treats this as an arithmetic expression and +substitutes the value of the expression. + +.sp 2 +.B White Space Splitting (Field Splitting) +.sp +.LP +After parameter expansion, command substitution, and +arithmetic expansion the shell scans the results of +expansions and substitutions that did not occur in double-quotes for +field splitting and multiple fields can result. +.LP +The shell treats each character of the IFS as a delimiter and use +the delimiters to split the results of parameter expansion and command +substitution into fields. + +.sp 2 +.B Pathname Expansion (File Name Generation) +.sp +.LP +Unless the -f flag is set, file name generation is performed after word splitting is complete. Each word is +viewed as a series of patterns, separated by slashes. The +process of expansion replaces the word with the names of +all existing files whose names can be formed by replacing +each pattern with a string that matches the specified pattern. +There are two restrictions on this: first, a pattern cannot match a string containing a slash, and second, +a pattern cannot match a string starting with a period +unless the first character of the pattern is a period. +The next section describes the patterns used for both +Pathname Expansion and the case(1) command. + +.sp 2 +.B Shell Patterns +.sp +.LP +A pattern consists of normal characters, which match themselves, and meta-characters. The meta-characters are +``!'', ``*'', ``?'', and ``[''. These characters lose +there special meanings if they are quoted. When command +or variable substitution is performed and the dollar sign +or back quotes are not double quoted, the value of the +variable or the output of the command is scanned for these +characters and they are turned into meta-characters. +.LP +An asterisk (``*'') matches any string of characters. A +question mark matches any single character. A left +bracket (``['') introduces a character class. The end of +the character class is indicated by a ``]''; if the ``]'' +is missing then the ``['' matches a ``['' rather than +introducing a character class. A character class matches +any of the characters between the square brackets. A +range of characters may be specified using a minus sign. +The character class may be complemented by making an +exclamation point the first character of the character +class. +.LP +To include a ``]'' in a character class, make it the first +character listed (after the ``!'', if any). To include a +minus sign, make it the first or last character listed + +.sp 2 +.B Builtins +.sp +.LP +This section lists the builtin commands which +are builtin because they need to perform some operation +that can't be performed by a separate process. In addition to these, there are several other commands that may +be builtin for efficiency (e.g. printf(1), echo(1), test(1), +etc). +.TP +alias [ name[=string] ... ] +If name=string is specified, the shell defines the +alias ``name'' with value ``string''. If just ``name'' +is specified, the value of the alias ``name'' is printed. +With no arguments, the alias builtin prints the +names and values of all defined aliases (see unalias). +.TP +bg [ job ] ... +Continue the specified jobs (or the current job if no +jobs are given) in the background. +.TP +command command arg... +Execute the specified builtin command. (This is useful when you have a shell function with the same name +as a builtin command.) +.TP +cd [ directory ] +Switch to the specified directory (default $HOME). +If the an entry for CDPATH appears in the environment +of the cd command or the shell variable CDPATH is set +and the directory name does not begin with a slash, +then the directories listed in CDPATH will be +searched for the specified directory. The format of +CDPATH is the same as that of PATH. In an interactive shell, the cd command will print out the name of +the directory that it actually switched to if this is +different from the name that the user gave. These +may be different either because the CDPATH mechanism +was used or because a symbolic link was crossed. +.TP +\&. file +The commands in the specified file are read and executed by the shell. +.TP +eval string... +Concatenate all the arguments with spaces. Then +re-parse and execute the command. +.TP +exec [ command arg... ] +Unless command is omitted, the shell process is +replaced with the specified program (which must be a +real program, not a shell builtin or function). Any +redirections on the exec command are marked as permanent, so that they are not undone when the exec command finishes. +.TP +exit [ exitstatus ] +Terminate the shell process. If exitstatus is given +it is used as the exit status of the shell; otherwise +the exit status of the preceding command is used. +.TP +export name... +The specified names are exported so that they will +appear in the environment of subsequent commands. +The only way to un-export a variable is to unset it. +The shell allows the value of a variable to be set at the +same time it is exported by writing +.nf + + export name=value + +.fi +With no arguments the export command lists the names +of all exported variables. +.TP +fc [-e editor] [first [last]] +.TP +fc -l [-nr] [first [last]] +.TP +fc -s [old=new] [first] +The fc builtin lists, or edits and re-executes, commands +previously entered to an interactive shell. +.RS +.5i +.TP 2 +-e editor +Use the editor named by editor to edit the commands. The +editor string is a command name, subject to search via the +PATH variable. The value in the FCEDIT variable +is used as a default when -e is not specified. If +FCEDIT is null or unset, the value of the EDITOR +variable is used. If EDITOR is null or unset, +ed(1) is used as the editor. +.TP 2 +-l (ell) +List the commands rather than invoking +an editor on them. The commands are written in the +sequence indicated by the first and last operands, as +affected by -r, with each command preceded by the command +number. +.TP 2 +-n +Suppress command numbers when listing with -l. +.TP 2 +-r +Reverse the order of the commands listed (with -l) or +edited (with neither -l nor -s). +.TP 2 +-s +Re-execute the command without invoking an editor. +.TP 2 +first +.TP 2 +last +Select the commands to list or edit. The number of +previous commands that can be accessed are determined +by the value of the HISTSIZE variable. The value of first +or last or both are one of the following: +.TP 2 +[+]number +A positive number representing a command +number; command numbers can be displayed +with the -l option. +.TP 2 +-number +A negative decimal number representing the +command that was executed number of +commands previously. For example, -1 is +the immediately previous command. +.TP 2 +string +A string indicating the most recently +entered command that begins with that +string. If the old=new operand is not also +specified with -s, the string form of the +first operand cannot contain an embedded +equal sign. +.TP +The following environment variables affect the execution of fc: +.TP 2 +FCEDIT +Name of the editor to use. +.TP 2 +HISTSIZE +The number of previous commands that are accessable. +.RE +.TP +fg [ job ] +Move the specified job or the current job to the +foreground. +.TP +getopts optstring var +The POSIX getopts command. +.TP +hash -rv command... +The shell maintains a hash table which remembers the +locations of commands. With no arguments whatsoever, +the hash command prints out the contents of this +table. Entries which have not been looked at since +the last cd command are marked with an asterisk; it +is possible for these entries to be invalid. +.sp +With arguments, the hash command removes the specified commands from the hash table (unless they are +functions) and then locates them. With the -v +option, hash prints the locations of the commands as +it finds them. The -r option causes the hash command +to delete all the entries in the hash table except +for functions. +.TP +jobid [ job ] +Print the process id's of the processes in the job. +If the job argument is omitted, use the current job. +.TP +jobs +This command lists out all the background processes +which are children of the current shell process. +.TP +pwd +Print the current directory. The builtin command may +differ from the program of the same name because the +builtin command remembers what the current directory +is rather than recomputing it each time. This makes +it faster. However, if the current directory is +renamed, the builtin version of pwd will continue to +print the old name for the directory. +.TP +read [ -p prompt ] [ -e ] variable... +The prompt is printed if the -p option is specified +and the standard input is a terminal. Then a line is +read from the standard input. The trailing newline +is deleted from the line and the line is split as +described in the section on word splitting above, and +the pieces are assigned to the variables in order. +If there are more pieces than variables, the remaining pieces (along with the characters in IFS that +separated them) are assigned to the last variable. +If there are more variables than pieces, the remaining variables are assigned the null string. +.sp +The -e option causes any backslashes in the input to +be treated specially. If a backslash is followed by +a newline, the backslash and the newline will be +deleted. If a backslash is followed by any other +character, the backslash will be deleted and the following character will be treated as though it were +not in IFS, even if it is. +.TP +readonly name... +The specified names are marked as read only, so that +they cannot be subsequently modified or unset. The shell +allows the value of a variable to be set at the same +time it is marked read only by writing +.TP +readonly name=value +With no arguments the readonly command lists the +names of all read only variables. +.TP +set [ { -options | +options | -- } ] arg... +The set command performs three different functions. +.sp +With no arguments, it lists the values of all shell +variables. +.sp +If options are given, it sets the specified option +flags, or clears them as described in the section +called ``Argument List Processing''. +.sp +The third use of the set command is to set the values +of the shell's positional parameters to the specified +args. To change the positional parameters without +changing any options, use ``--'' as the first argument to set. If no args are present, the set command +will clear all the positional parameters (equivalent +to executing ``shift $#''. +.TP +setvar variable value +Assigns value to variable. (In general it is better +to write variable=value rather than using setvar. +Setvar is intended to be used in functions that +assign values to variables whose names are passed as +parameters.) +.TP +shift [ n ] +Shift the positional parameters n times. A shift +sets the value of $1 to the value of $2, the value of +$2 to the value of $3, and so on, decreasing the +value of $# by one. If there are zero positional +parameters, shifting doesn't do anything. +.TP +trap [ action ] signal... +Cause the shell to parse and execute action when any +of the specified signals are received. The signals +are specified by signal number. Action may be null +or omitted; the former causes the specified signal to +be ignored and the latter causes the default action +to be taken. When the shell forks off a subshell, it +resets trapped (but not ignored) signals to the +default action. The trap command has no effect on +signals that were ignored on entry to the shell. +.TP +umask [ mask ] +Set the value of umask (see umask(2)) to the specified octal value. +If the argument is omitted, the +umask value is printed. +.TP +unalias [-a] [name] +If ``name'' is specified, the shell removes that alias. +If ``-a'' is specified, all aliases are removed. +.TP +unset name... +The specified variables and functions are unset and +unexported. If a given name corresponds to both a +variable and a function, both the variable and the +function are unset. +.TP +wait [ job ] +Wait for the specified job to complete and return the +exit status of the last process in the job. If the +argument is omitted, wait for all jobs to complete +and the return an exit status of zero. +.LP +.sp 2 +.B Command Line Editing +.sp +.LP +When sh is being used interactively from a terminal, the current command +and the command history (see fc in Builtins) can be edited using vi-mode +command-line editing. This mode uses commands, described below, similar +to a subset of those described in the vi man page. +The command set -o vi enables vi-mode editing and place sh into vi +insert mode. +With vi-mode enabled, sh can be switched between insert mode and command +mode. The editor is not described in full here, but will be in a later +document. It's similar to vi: typing <ESC> will throw you into +command VI command mode. Hitting <return> while in command mode +will pass the line to the shell. diff --git a/bin/sh/shell.h b/bin/sh/shell.h new file mode 100644 index 0000000..87f4795 --- /dev/null +++ b/bin/sh/shell.h @@ -0,0 +1,84 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)shell.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * The follow should be set to reflect the type of system you have: + * JOBS -> 1 if you have Berkeley job control, 0 otherwise. + * SYMLINKS -> 1 if your system includes symbolic links, 0 otherwise. + * SHORTNAMES -> 1 if your linker cannot handle long names. + * define BSD if you are running 4.2 BSD or later. + * define SYSV if you are running under System V. + * define DEBUG=1 to compile in debugging (set global "debug" to turn on) + * define DEBUG=2 to compile in and turn on debugging. + * + * When debugging is on, debugging info will be written to $HOME/trace and + * a quit signal will generate a core dump. + */ + + +#define JOBS 1 +#define SYMLINKS 1 +#ifndef BSD +#define BSD 1 +#endif +#define DEBUG 1 + +#ifdef __STDC__ +typedef void *pointer; +#ifndef NULL +#define NULL (void *)0 +#endif +#else /* not __STDC__ */ +typedef char *pointer; +#ifndef NULL +#define NULL 0 +#endif +#endif /* not __STDC__ */ +#define STATIC /* empty */ +#define MKINIT /* empty */ + +#include <sys/cdefs.h> + +extern char nullstr[1]; /* null string */ + + +#ifdef DEBUG +#define TRACE(param) trace param +#else +#define TRACE(param) +#endif diff --git a/bin/sh/show.c b/bin/sh/show.c new file mode 100644 index 0000000..9807c99 --- /dev/null +++ b/bin/sh/show.c @@ -0,0 +1,378 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)show.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include <stdio.h> +#include "shell.h" +#include "parser.h" +#include "nodes.h" +#include "mystring.h" + + +#ifdef DEBUG +static shtree(), shcmd(), sharg(), indent(); + + +showtree(n) + union node *n; + { + trputs("showtree called\n"); + shtree(n, 1, NULL, stdout); +} + + +static +shtree(n, ind, pfx, fp) + union node *n; + char *pfx; + FILE *fp; + { + struct nodelist *lp; + char *s; + + indent(ind, pfx, fp); + switch(n->type) { + case NSEMI: + s = "; "; + goto binop; + case NAND: + s = " && "; + goto binop; + case NOR: + s = " || "; +binop: + shtree(n->nbinary.ch1, ind, NULL, fp); + /* if (ind < 0) */ + fputs(s, fp); + shtree(n->nbinary.ch2, ind, NULL, fp); + break; + case NCMD: + shcmd(n, fp); + if (ind >= 0) + putc('\n', fp); + break; + case NPIPE: + for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { + shcmd(lp->n, fp); + if (lp->next) + fputs(" | ", fp); + } + if (n->npipe.backgnd) + fputs(" &", fp); + if (ind >= 0) + putc('\n', fp); + break; + default: + fprintf(fp, "<node type %d>", n->type); + if (ind >= 0) + putc('\n', fp); + break; + } +} + + + +static +shcmd(cmd, fp) + union node *cmd; + FILE *fp; + { + union node *np; + int first; + char *s; + int dftfd; + + first = 1; + for (np = cmd->ncmd.args ; np ; np = np->narg.next) { + if (! first) + putchar(' '); + sharg(np, fp); + first = 0; + } + for (np = cmd->ncmd.redirect ; np ; np = np->nfile.next) { + if (! first) + putchar(' '); + switch (np->nfile.type) { + case NTO: s = ">"; dftfd = 1; break; + case NAPPEND: s = ">>"; dftfd = 1; break; + case NTOFD: s = ">&"; dftfd = 1; break; + case NFROM: s = "<"; dftfd = 0; break; + case NFROMFD: s = "<&"; dftfd = 0; break; + } + if (np->nfile.fd != dftfd) + fprintf(fp, "%d", np->nfile.fd); + fputs(s, fp); + if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) { + fprintf(fp, "%d", np->ndup.dupfd); + } else { + sharg(np->nfile.fname, fp); + } + first = 0; + } +} + + + +static +sharg(arg, fp) + union node *arg; + FILE *fp; + { + char *p; + struct nodelist *bqlist; + int subtype; + + if (arg->type != NARG) { + printf("<node type %d>\n", arg->type); + fflush(stdout); + abort(); + } + bqlist = arg->narg.backquote; + for (p = arg->narg.text ; *p ; p++) { + switch (*p) { + case CTLESC: + putc(*++p, fp); + break; + case CTLVAR: + putc('$', fp); + putc('{', fp); + subtype = *++p; + while (*p != '=') + putc(*p++, fp); + if (subtype & VSNUL) + putc(':', fp); + switch (subtype & VSTYPE) { + case VSNORMAL: + putc('}', fp); + break; + case VSMINUS: + putc('-', fp); + break; + case VSPLUS: + putc('+', fp); + break; + case VSQUESTION: + putc('?', fp); + break; + case VSASSIGN: + putc('=', fp); + break; + default: + printf("<subtype %d>", subtype); + } + break; + case CTLENDVAR: + putc('}', fp); + break; + case CTLBACKQ: + case CTLBACKQ|CTLQUOTE: + putc('$', fp); + putc('(', fp); + shtree(bqlist->n, -1, NULL, fp); + putc(')', fp); + break; + default: + putc(*p, fp); + break; + } + } +} + + +static +indent(amount, pfx, fp) + char *pfx; + FILE *fp; + { + int i; + + for (i = 0 ; i < amount ; i++) { + if (pfx && i == amount - 1) + fputs(pfx, fp); + putc('\t', fp); + } +} +#endif + + + +/* + * Debugging stuff. + */ + + +FILE *tracefile; + +#if DEBUG == 2 +int debug = 1; +#else +int debug = 0; +#endif + + +trputc(c) { +#ifdef DEBUG + if (tracefile == NULL) + return; + putc(c, tracefile); + if (c == '\n') + fflush(tracefile); +#endif +} + + +trace(fmt, a1, a2, a3, a4, a5, a6, a7, a8) + char *fmt; + { +#ifdef DEBUG + if (tracefile == NULL) + return; + fprintf(tracefile, fmt, a1, a2, a3, a4, a5, a6, a7, a8); + if (strchr(fmt, '\n')) + fflush(tracefile); +#endif +} + + +trputs(s) + char *s; + { +#ifdef DEBUG + if (tracefile == NULL) + return; + fputs(s, tracefile); + if (strchr(s, '\n')) + fflush(tracefile); +#endif +} + + +trstring(s) + char *s; + { + register char *p; + char c; + +#ifdef DEBUG + if (tracefile == NULL) + return; + putc('"', tracefile); + for (p = s ; *p ; p++) { + switch (*p) { + case '\n': c = 'n'; goto backslash; + case '\t': c = 't'; goto backslash; + case '\r': c = 'r'; goto backslash; + case '"': c = '"'; goto backslash; + case '\\': c = '\\'; goto backslash; + case CTLESC: c = 'e'; goto backslash; + case CTLVAR: c = 'v'; goto backslash; + case CTLVAR+CTLQUOTE: c = 'V'; goto backslash; + case CTLBACKQ: c = 'q'; goto backslash; + case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash; +backslash: putc('\\', tracefile); + putc(c, tracefile); + break; + default: + if (*p >= ' ' && *p <= '~') + putc(*p, tracefile); + else { + putc('\\', tracefile); + putc(*p >> 6 & 03, tracefile); + putc(*p >> 3 & 07, tracefile); + putc(*p & 07, tracefile); + } + break; + } + } + putc('"', tracefile); +#endif +} + + +trargs(ap) + char **ap; + { +#ifdef DEBUG + if (tracefile == NULL) + return; + while (*ap) { + trstring(*ap++); + if (*ap) + putc(' ', tracefile); + else + putc('\n', tracefile); + } + fflush(tracefile); +#endif +} + + +opentrace() { + char s[100]; + char *p; + char *getenv(); + int flags; + +#ifdef DEBUG + if (!debug) + return; +#ifdef not_this_way + if ((p = getenv("HOME")) == NULL) { + if (geteuid() == 0) + p = "/"; + else + p = "/tmp"; + } + scopy(p, s); + strcat(s, "/trace"); +#else + scopy("./trace", s); +#endif /* not_this_way */ + if ((tracefile = fopen(s, "a")) == NULL) { + fprintf(stderr, "Can't open %s\n", s); + return; + } +#ifdef O_APPEND + if ((flags = fcntl(fileno(tracefile), F_GETFL, 0)) >= 0) + fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND); +#endif + fputs("\nTracing started.\n", tracefile); + fflush(tracefile); +#endif /* DEBUG */ +} diff --git a/bin/sh/trap.c b/bin/sh/trap.c new file mode 100644 index 0000000..dde6dbc --- /dev/null +++ b/bin/sh/trap.c @@ -0,0 +1,348 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)trap.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include "shell.h" +#include "main.h" +#include "nodes.h" /* for other headers */ +#include "eval.h" +#include "jobs.h" +#include "options.h" +#include "syntax.h" +#include "signames.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "trap.h" +#include "mystring.h" +#include <signal.h> + + +/* + * Sigmode records the current value of the signal handlers for the various + * modes. A value of zero means that the current handler is not known. + * S_HARD_IGN indicates that the signal was ignored on entry to the shell, + */ + +#define S_DFL 1 /* default signal handling (SIG_DFL) */ +#define S_CATCH 2 /* signal is caught */ +#define S_IGN 3 /* signal is ignored (SIG_IGN) */ +#define S_HARD_IGN 4 /* signal is ignored permenantly */ +#define S_RESET 5 /* temporary - to reset a hard ignored sig */ + + +extern char nullstr[1]; /* null string */ + +char *trap[MAXSIG+1]; /* trap handler commands */ +MKINIT char sigmode[MAXSIG]; /* current value of signal */ +char gotsig[MAXSIG]; /* indicates specified signal received */ +int pendingsigs; /* indicates some signal received */ + +/* + * The trap builtin. + */ + +trapcmd(argc, argv) char **argv; { + char *action; + char **ap; + int signo; + + if (argc <= 1) { + for (signo = 0 ; signo <= MAXSIG ; signo++) { + if (trap[signo] != NULL) + out1fmt("%d: %s\n", signo, trap[signo]); + } + return 0; + } + ap = argv + 1; + if (is_number(*ap)) + action = NULL; + else + action = *ap++; + while (*ap) { + if ((signo = number(*ap)) < 0 || signo > MAXSIG) + error("%s: bad trap", *ap); + INTOFF; + if (action) + action = savestr(action); + if (trap[signo]) + ckfree(trap[signo]); + trap[signo] = action; + if (signo != 0) + setsignal(signo); + INTON; + ap++; + } + return 0; +} + + + +/* + * Clear traps on a fork. + */ + +void +clear_traps() { + char **tp; + + for (tp = trap ; tp <= &trap[MAXSIG] ; tp++) { + if (*tp && **tp) { /* trap not NULL or SIG_IGN */ + INTOFF; + ckfree(*tp); + *tp = NULL; + if (tp != &trap[0]) + setsignal(tp - trap); + INTON; + } + } +} + + + +/* + * Set the signal handler for the specified signal. The routine figures + * out what it should be set to. + */ + +int +setsignal(signo) { + int action; + sig_t sigact; + char *t; + extern void onsig(); + extern sig_t getsigaction(); + + if ((t = trap[signo]) == NULL) + action = S_DFL; + else if (*t != '\0') + action = S_CATCH; + else + action = S_IGN; + if (rootshell && action == S_DFL) { + switch (signo) { + case SIGINT: + if (iflag) + action = S_CATCH; + break; + case SIGQUIT: +#ifdef DEBUG + { + extern int debug; + + if (debug) + break; + } +#endif + /* FALLTHROUGH */ + case SIGTERM: + if (iflag) + action = S_IGN; + break; +#if JOBS + case SIGTSTP: + case SIGTTOU: + if (mflag) + action = S_IGN; + break; +#endif + } + } + t = &sigmode[signo - 1]; + if (*t == 0) { + /* + * current setting unknown + */ + sigact = getsigaction(signo); + if (sigact == SIG_IGN) { + if (mflag && (signo == SIGTSTP || + signo == SIGTTIN || signo == SIGTTOU)) { + *t = S_IGN; /* don't hard ignore these */ + } else + *t = S_HARD_IGN; + } else { + *t = S_RESET; /* force to be set */ + } + } + if (*t == S_HARD_IGN || *t == action) + return 0; + switch (action) { + case S_DFL: sigact = SIG_DFL; break; + case S_CATCH: sigact = onsig; break; + case S_IGN: sigact = SIG_IGN; break; + } + *t = action; + return (int)signal(signo, sigact); +} + +/* + * Return the current setting for sig w/o changing it. + */ +sig_t +getsigaction(signo) { + struct sigaction sa; + + if (sigaction(signo, (struct sigaction *)0, &sa) == -1) + error("Sigaction system call failed"); + + return sa.sa_handler; +} + +/* + * Ignore a signal. + */ + +void +ignoresig(signo) { + if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) { + signal(signo, SIG_IGN); + } + sigmode[signo - 1] = S_HARD_IGN; +} + + +#ifdef mkinit +INCLUDE "signames.h" +INCLUDE "trap.h" + +SHELLPROC { + char *sm; + + clear_traps(); + for (sm = sigmode ; sm < sigmode + MAXSIG ; sm++) { + if (*sm == S_IGN) + *sm = S_HARD_IGN; + } +} +#endif + + + +/* + * Signal handler. + */ + +void +onsig(signo) { + signal(signo, onsig); + if (signo == SIGINT && trap[SIGINT] == NULL) { + onint(); + return; + } + gotsig[signo - 1] = 1; + pendingsigs++; +} + + + +/* + * Called to execute a trap. Perhaps we should avoid entering new trap + * handlers while we are executing a trap handler. + */ + +void +dotrap() { + int i; + int savestatus; + + for (;;) { + for (i = 1 ; ; i++) { + if (gotsig[i - 1]) + break; + if (i >= MAXSIG) + goto done; + } + gotsig[i - 1] = 0; + savestatus=exitstatus; + evalstring(trap[i]); + exitstatus=savestatus; + } +done: + pendingsigs = 0; +} + + + +/* + * Controls whether the shell is interactive or not. + */ + + +void +setinteractive(on) { + static int is_interactive; + + if (on == is_interactive) + return; + setsignal(SIGINT); + setsignal(SIGQUIT); + setsignal(SIGTERM); + is_interactive = on; +} + + + +/* + * Called to exit the shell. + */ + +void +exitshell(status) { + struct jmploc loc1, loc2; + char *p; + + TRACE(("exitshell(%d) pid=%d\n", status, getpid())); + if (setjmp(loc1.loc)) { + goto l1; + } + if (setjmp(loc2.loc)) { + goto l2; + } + handler = &loc1; + if ((p = trap[0]) != NULL && *p != '\0') { + trap[0] = NULL; + evalstring(p); + } +l1: handler = &loc2; /* probably unnecessary */ + flushall(); +#if JOBS + setjobctl(0); +#endif +l2: _exit(status); +} diff --git a/bin/sh/trap.h b/bin/sh/trap.h new file mode 100644 index 0000000..d59ee60 --- /dev/null +++ b/bin/sh/trap.h @@ -0,0 +1,55 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)trap.h 8.1 (Berkeley) 5/31/93 + */ + +extern int pendingsigs; + +#ifdef __STDC__ +void clear_traps(void); +int setsignal(int); +void ignoresig(int); +void dotrap(void); +void setinteractive(int); +void exitshell(int); +#else +void clear_traps(); +int setsignal(); +void ignoresig(); +void dotrap(); +void setinteractive(); +void exitshell(); +#endif diff --git a/bin/sh/var.c b/bin/sh/var.c new file mode 100644 index 0000000..0629de8 --- /dev/null +++ b/bin/sh/var.c @@ -0,0 +1,670 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)var.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +/* + * Shell variables. + */ + +#include "shell.h" +#include "output.h" +#include "expand.h" +#include "nodes.h" /* for other headers */ +#include "eval.h" /* defines cmdenviron */ +#include "exec.h" +#include "syntax.h" +#include "options.h" +#include "mail.h" +#include "var.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" + + +#define VTABSIZE 39 + + +struct varinit { + struct var *var; + int flags; + char *text; +}; + + +#if ATTY +struct var vatty; +#endif +struct var vhistsize; +struct var vifs; +struct var vmail; +struct var vmpath; +struct var vpath; +struct var vps1; +struct var vps2; +struct var vvers; +#if ATTY +struct var vterm; +#endif + +const struct varinit varinit[] = { +#if ATTY + {&vatty, VSTRFIXED|VTEXTFIXED|VUNSET, "ATTY="}, +#endif + {&vhistsize, VSTRFIXED|VTEXTFIXED|VUNSET, "HISTSIZE="}, + {&vifs, VSTRFIXED|VTEXTFIXED, "IFS= \t\n"}, + {&vmail, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL="}, + {&vmpath, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH="}, + {&vpath, VSTRFIXED|VTEXTFIXED, "PATH=:/bin:/usr/bin"}, + /* + * vps1 depends on uid + */ + {&vps2, VSTRFIXED|VTEXTFIXED, "PS2=> "}, +#if ATTY + {&vterm, VSTRFIXED|VTEXTFIXED|VUNSET, "TERM="}, +#endif + {NULL, 0, NULL} +}; + +struct var *vartab[VTABSIZE]; + +STATIC int unsetvar __P((char *)); +STATIC struct var **hashvar __P((char *)); +STATIC int varequal __P((char *, char *)); + +/* + * Initialize the varable symbol tables and import the environment + */ + +#ifdef mkinit +INCLUDE "var.h" +INIT { + char **envp; + extern char **environ; + + initvar(); + for (envp = environ ; *envp ; envp++) { + if (strchr(*envp, '=')) { + setvareq(*envp, VEXPORT|VTEXTFIXED); + } + } +} +#endif + + +/* + * This routine initializes the builtin variables. It is called when the + * shell is initialized and again when a shell procedure is spawned. + */ + +void +initvar() { + const struct varinit *ip; + struct var *vp; + struct var **vpp; + + for (ip = varinit ; (vp = ip->var) != NULL ; ip++) { + if ((vp->flags & VEXPORT) == 0) { + vpp = hashvar(ip->text); + vp->next = *vpp; + *vpp = vp; + vp->text = ip->text; + vp->flags = ip->flags; + } + } + /* + * PS1 depends on uid + */ + if ((vps1.flags & VEXPORT) == 0) { + vpp = hashvar("PS1="); + vps1.next = *vpp; + *vpp = &vps1; + vps1.text = geteuid() ? "PS1=$ " : "PS1=# "; + vps1.flags = VSTRFIXED|VTEXTFIXED; + } +} + +/* + * Set the value of a variable. The flags argument is ored with the + * flags of the variable. If val is NULL, the variable is unset. + */ + +void +setvar(name, val, flags) + char *name, *val; + { + char *p, *q; + int len; + int namelen; + char *nameeq; + int isbad; + + isbad = 0; + p = name; + if (! is_name(*p++)) + isbad = 1; + for (;;) { + if (! is_in_name(*p)) { + if (*p == '\0' || *p == '=') + break; + isbad = 1; + } + p++; + } + namelen = p - name; + if (isbad) + error("%.*s: bad variable name", namelen, name); + len = namelen + 2; /* 2 is space for '=' and '\0' */ + if (val == NULL) { + flags |= VUNSET; + } else { + len += strlen(val); + } + p = nameeq = ckmalloc(len); + q = name; + while (--namelen >= 0) + *p++ = *q++; + *p++ = '='; + *p = '\0'; + if (val) + scopy(val, p); + setvareq(nameeq, flags); +} + + + +/* + * Same as setvar except that the variable and value are passed in + * the first argument as name=value. Since the first argument will + * be actually stored in the table, it should not be a string that + * will go away. + */ + +void +setvareq(s, flags) + char *s; + { + struct var *vp, **vpp; + + vpp = hashvar(s); + for (vp = *vpp ; vp ; vp = vp->next) { + if (varequal(s, vp->text)) { + if (vp->flags & VREADONLY) { + int len = strchr(s, '=') - s; + error("%.*s: is read only", len, s); + } + INTOFF; + if (vp == &vpath) + changepath(s + 5); /* 5 = strlen("PATH=") */ + if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) + ckfree(vp->text); + vp->flags &=~ (VTEXTFIXED|VSTACK|VUNSET); + vp->flags |= flags; + vp->text = s; + if (vp == &vmpath || (vp == &vmail && ! mpathset())) + chkmail(1); + if (vp == &vhistsize) + sethistsize(); + INTON; + return; + } + } + /* not found */ + vp = ckmalloc(sizeof (*vp)); + vp->flags = flags; + vp->text = s; + vp->next = *vpp; + *vpp = vp; +} + + + +/* + * Process a linked list of variable assignments. + */ + +void +listsetvar(list) + struct strlist *list; + { + struct strlist *lp; + + INTOFF; + for (lp = list ; lp ; lp = lp->next) { + setvareq(savestr(lp->text), 0); + } + INTON; +} + + + +/* + * Find the value of a variable. Returns NULL if not set. + */ + +char * +lookupvar(name) + char *name; + { + struct var *v; + + for (v = *hashvar(name) ; v ; v = v->next) { + if (varequal(v->text, name)) { + if (v->flags & VUNSET) + return NULL; + return strchr(v->text, '=') + 1; + } + } + return NULL; +} + + + +/* + * Search the environment of a builtin command. If the second argument + * is nonzero, return the value of a variable even if it hasn't been + * exported. + */ + +char * +bltinlookup(name, doall) + char *name; + { + struct strlist *sp; + struct var *v; + + for (sp = cmdenviron ; sp ; sp = sp->next) { + if (varequal(sp->text, name)) + return strchr(sp->text, '=') + 1; + } + for (v = *hashvar(name) ; v ; v = v->next) { + if (varequal(v->text, name)) { + if (v->flags & VUNSET + || ! doall && (v->flags & VEXPORT) == 0) + return NULL; + return strchr(v->text, '=') + 1; + } + } + return NULL; +} + + + +/* + * Generate a list of exported variables. This routine is used to construct + * the third argument to execve when executing a program. + */ + +char ** +environment() { + int nenv; + struct var **vpp; + struct var *vp; + char **env, **ep; + + nenv = 0; + for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { + for (vp = *vpp ; vp ; vp = vp->next) + if (vp->flags & VEXPORT) + nenv++; + } + ep = env = stalloc((nenv + 1) * sizeof *env); + for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { + for (vp = *vpp ; vp ; vp = vp->next) + if (vp->flags & VEXPORT) + *ep++ = vp->text; + } + *ep = NULL; + return env; +} + + +/* + * Called when a shell procedure is invoked to clear out nonexported + * variables. It is also necessary to reallocate variables of with + * VSTACK set since these are currently allocated on the stack. + */ + +#ifdef mkinit +MKINIT void shprocvar(); + +SHELLPROC { + shprocvar(); +} +#endif + +void +shprocvar() { + struct var **vpp; + struct var *vp, **prev; + + for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { + for (prev = vpp ; (vp = *prev) != NULL ; ) { + if ((vp->flags & VEXPORT) == 0) { + *prev = vp->next; + if ((vp->flags & VTEXTFIXED) == 0) + ckfree(vp->text); + if ((vp->flags & VSTRFIXED) == 0) + ckfree(vp); + } else { + if (vp->flags & VSTACK) { + vp->text = savestr(vp->text); + vp->flags &=~ VSTACK; + } + prev = &vp->next; + } + } + } + initvar(); +} + + + +/* + * Command to list all variables which are set. Currently this command + * is invoked from the set command when the set command is called without + * any variables. + */ + +int +showvarscmd(argc, argv) char **argv; { + struct var **vpp; + struct var *vp; + + for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { + for (vp = *vpp ; vp ; vp = vp->next) { + if ((vp->flags & VUNSET) == 0) + out1fmt("%s\n", vp->text); + } + } + return 0; +} + + + +/* + * The export and readonly commands. + */ + +int +exportcmd(argc, argv) char **argv; { + struct var **vpp; + struct var *vp; + char *name; + char *p; + int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT; + + listsetvar(cmdenviron); + if (argc > 1) { + while ((name = *argptr++) != NULL) { + if ((p = strchr(name, '=')) != NULL) { + p++; + } else { + vpp = hashvar(name); + for (vp = *vpp ; vp ; vp = vp->next) { + if (varequal(vp->text, name)) { + vp->flags |= flag; + goto found; + } + } + } + setvar(name, p, flag); +found:; + } + } else { + for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { + for (vp = *vpp ; vp ; vp = vp->next) { + if (vp->flags & flag) { + for (p = vp->text ; *p != '=' ; p++) + out1c(*p); + out1c('\n'); + } + } + } + } + return 0; +} + + +/* + * The "local" command. + */ + +localcmd(argc, argv) char **argv; { + char *name; + + if (! in_function()) + error("Not in a function"); + while ((name = *argptr++) != NULL) { + mklocal(name); + } + return 0; +} + + +/* + * Make a variable a local variable. When a variable is made local, it's + * value and flags are saved in a localvar structure. The saved values + * will be restored when the shell function returns. We handle the name + * "-" as a special case. + */ + +void +mklocal(name) + char *name; + { + struct localvar *lvp; + struct var **vpp; + struct var *vp; + + INTOFF; + lvp = ckmalloc(sizeof (struct localvar)); + if (name[0] == '-' && name[1] == '\0') { + lvp->text = ckmalloc(sizeof optlist); + bcopy(optlist, lvp->text, sizeof optlist); + vp = NULL; + } else { + vpp = hashvar(name); + for (vp = *vpp ; vp && ! varequal(vp->text, name) ; vp = vp->next); + if (vp == NULL) { + if (strchr(name, '=')) + setvareq(savestr(name), VSTRFIXED); + else + setvar(name, NULL, VSTRFIXED); + vp = *vpp; /* the new variable */ + lvp->text = NULL; + lvp->flags = VUNSET; + } else { + lvp->text = vp->text; + lvp->flags = vp->flags; + vp->flags |= VSTRFIXED|VTEXTFIXED; + if (strchr(name, '=')) + setvareq(savestr(name), 0); + } + } + lvp->vp = vp; + lvp->next = localvars; + localvars = lvp; + INTON; +} + + +/* + * Called after a function returns. + */ + +void +poplocalvars() { + struct localvar *lvp; + struct var *vp; + + while ((lvp = localvars) != NULL) { + localvars = lvp->next; + vp = lvp->vp; + if (vp == NULL) { /* $- saved */ + bcopy(lvp->text, optlist, sizeof optlist); + ckfree(lvp->text); + } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) { + (void)unsetvar(vp->text); + } else { + if ((vp->flags & VTEXTFIXED) == 0) + ckfree(vp->text); + vp->flags = lvp->flags; + vp->text = lvp->text; + } + ckfree(lvp); + } +} + + +setvarcmd(argc, argv) char **argv; { + if (argc <= 2) + return unsetcmd(argc, argv); + else if (argc == 3) + setvar(argv[1], argv[2], 0); + else + error("List assignment not implemented"); + return 0; +} + + +/* + * The unset builtin command. We unset the function before we unset the + * variable to allow a function to be unset when there is a readonly variable + * with the same name. + */ + +unsetcmd(argc, argv) char **argv; { + char **ap; + int i; + int flg_func = 0; + int flg_var = 0; + int ret = 0; + + while ((i = nextopt("vf")) != '\0') { + if (i == 'f') + flg_func = 1; + else + flg_var = 1; + } + if (flg_func == 0 && flg_var == 0) + flg_var = 1; + + for (ap = argptr; *ap ; ap++) { + if (flg_func) + ret |= unsetfunc(*ap); + if (flg_var) + ret |= unsetvar(*ap); + } + return ret; +} + + +/* + * Unset the specified variable. + */ + +STATIC int +unsetvar(s) + char *s; + { + struct var **vpp; + struct var *vp; + + vpp = hashvar(s); + for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) { + if (varequal(vp->text, s)) { + if (vp->flags & VREADONLY) + return (1); + INTOFF; + if (*(strchr(vp->text, '=') + 1) != '\0') + setvar(s, nullstr, 0); + vp->flags &=~ VEXPORT; + vp->flags |= VUNSET; + if ((vp->flags & VSTRFIXED) == 0) { + if ((vp->flags & VTEXTFIXED) == 0) + ckfree(vp->text); + *vpp = vp->next; + ckfree(vp); + } + INTON; + return (0); + } + } + + return (1); +} + + + +/* + * Find the appropriate entry in the hash table from the name. + */ + +STATIC struct var ** +hashvar(p) + register char *p; + { + unsigned int hashval; + + hashval = *p << 4; + while (*p && *p != '=') + hashval += *p++; + return &vartab[hashval % VTABSIZE]; +} + + + +/* + * Returns true if the two strings specify the same varable. The first + * variable name is terminated by '='; the second may be terminated by + * either '=' or '\0'. + */ + +STATIC int +varequal(p, q) + register char *p, *q; + { + while (*p == *q++) { + if (*p++ == '=') + return 1; + } + if (*p == '=' && *(q - 1) == '\0') + return 1; + return 0; +} diff --git a/bin/sh/var.h b/bin/sh/var.h new file mode 100644 index 0000000..92b4d0d --- /dev/null +++ b/bin/sh/var.h @@ -0,0 +1,127 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)var.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * Shell variables. + */ + +/* flags */ +#define VEXPORT 01 /* variable is exported */ +#define VREADONLY 02 /* variable cannot be modified */ +#define VSTRFIXED 04 /* variable struct is staticly allocated */ +#define VTEXTFIXED 010 /* text is staticly allocated */ +#define VSTACK 020 /* text is allocated on the stack */ +#define VUNSET 040 /* the variable is not set */ + + +struct var { + struct var *next; /* next entry in hash list */ + int flags; /* flags are defined above */ + char *text; /* name=value */ +}; + + +struct localvar { + struct localvar *next; /* next local variable in list */ + struct var *vp; /* the variable that was made local */ + int flags; /* saved flags */ + char *text; /* saved text */ +}; + + +struct localvar *localvars; + +#if ATTY +extern struct var vatty; +#endif +extern struct var vifs; +extern struct var vmail; +extern struct var vmpath; +extern struct var vpath; +extern struct var vps1; +extern struct var vps2; +#if ATTY +extern struct var vterm; +#endif + +/* + * The following macros access the values of the above variables. + * They have to skip over the name. They return the null string + * for unset variables. + */ + +#define ifsval() (vifs.text + 4) +#define mailval() (vmail.text + 5) +#define mpathval() (vmpath.text + 9) +#define pathval() (vpath.text + 5) +#define ps1val() (vps1.text + 4) +#define ps2val() (vps2.text + 4) +#if ATTY +#define termval() (vterm.text + 5) +#endif + +#if ATTY +#define attyset() ((vatty.flags & VUNSET) == 0) +#endif +#define mpathset() ((vmpath.flags & VUNSET) == 0) + + +#ifdef __STDC__ +void initvar(); +void setvar(char *, char *, int); +void setvareq(char *, int); +struct strlist; +void listsetvar(struct strlist *); +char *lookupvar(char *); +char *bltinlookup(char *, int); +char **environment(); +int showvarscmd(int, char **); +void mklocal(char *); +void poplocalvars(void); +#else +void initvar(); +void setvar(); +void setvareq(); +void listsetvar(); +char *lookupvar(); +char *bltinlookup(); +char **environment(); +int showvarscmd(); +void mklocal(); +void poplocalvars(); +#endif diff --git a/bin/sleep/Makefile b/bin/sleep/Makefile new file mode 100644 index 0000000..c31f19c --- /dev/null +++ b/bin/sleep/Makefile @@ -0,0 +1,5 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 + +PROG= sleep + +.include <bsd.prog.mk> diff --git a/bin/sleep/sleep.1 b/bin/sleep/sleep.1 new file mode 100644 index 0000000..2148b7a --- /dev/null +++ b/bin/sleep/sleep.1 @@ -0,0 +1,116 @@ +.\" Copyright (c) 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. +.\" +.\" @(#)sleep.1 8.3 (Berkeley) 4/18/94 +.\" +.Dd April 18, 1994 +.Dt SLEEP 1 +.Os +.Sh NAME +.Nm sleep +.Nd suspend execution for an interval of time +.Sh SYNOPSIS +.Nm sleep +.Ar seconds +.Sh DESCRIPTION +The +.Nm sleep +command +suspends execution for a minimum of +.Ar seconds . +.Nm Sleep +is used to schedule the execution of other commands (see +.Sx EXAMPLES +below). +.Pp +The +.Nm Sleep +utility exits with one of the following values: +.Bl -tag -width flag +.It Li \&0 +On successful completion, or if the signal +.Dv SIGALRM +was received. +.It Li \&>\&0 +An error occurred. +.El +.Sh EXAMPLES +To schedule the execution of a command for +.Va x +number seconds later: +.Pp +.Dl (sleep 1800; sh command_file >& errors)& +.Pp +This incantation would wait a half hour before +running the script command_file. (See the +.Xr at 1 +utility.) +.Pp +To reiteratively run a command (with the +.Xr csh 1 ) : +.Pp +.Bd -literal -offset indent -compact +while (1) + if (! -r zzz.rawdata) then + sleep 300 + else + foreach i (`ls *.rawdata`) + sleep 70 + awk -f collapse_data $i >> results + end + break + endif +end +.Ed +.Pp +The scenario for a script such as this might be: a program currently +running is taking longer than expected to process a series of +files, and it would be nice to have +another program start processing the files created by the first +program as soon as it is finished (when zzz.rawdata is created). +The script checks every five minutes for the file zzz.rawdata, +when the file is found, then another portion processing +is done courteously by sleeping for 70 seconds in between each +awk job. +.Sh SEE ALSO +.Xr setitimer 2 , +.Xr alarm 3 , +.Xr sleep 3 , +.Xr at 1 +.Sh STANDARDS +The +.Nm sleep +command is expected to be +.St -p1003.2 +compatible. diff --git a/bin/sleep/sleep.c b/bin/sleep/sleep.c new file mode 100644 index 0000000..365d39e --- /dev/null +++ b/bin/sleep/sleep.c @@ -0,0 +1,80 @@ +/* + * Copyright (c) 1988, 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) 1988, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)sleep.c 8.3 (Berkeley) 4/2/94"; +#endif /* not lint */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch, secs; + + while ((ch = getopt(argc, argv, "")) != EOF) + switch(ch) { + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 1) + usage(); + + if ((secs = atoi(*argv)) > 0) + (void)sleep(secs); + exit(0); +} + +void +usage() +{ + + (void)fprintf(stderr, "usage: sleep seconds\n"); + exit(1); +} diff --git a/bin/stty/Makefile b/bin/stty/Makefile new file mode 100644 index 0000000..69bda05 --- /dev/null +++ b/bin/stty/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 + +PROG= stty +SRCS= cchar.c gfmt.c key.c modes.c print.c stty.c util.c + +.include <bsd.prog.mk> diff --git a/bin/stty/cchar.c b/bin/stty/cchar.c new file mode 100644 index 0000000..79cb90a --- /dev/null +++ b/bin/stty/cchar.c @@ -0,0 +1,145 @@ +/*- + * 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[] = "@(#)cchar.c 8.5 (Berkeley) 4/2/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <err.h> +#include <limits.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +#include "stty.h" +#include "extern.h" + +/* + * Special control characters. + * + * Cchars1 are the standard names, cchars2 are the old aliases. + * The first are displayed, but both are recognized on the + * command line. + */ +struct cchar cchars1[] = { + { "discard", VDISCARD, CDISCARD }, + { "dsusp", VDSUSP, CDSUSP }, + { "eof", VEOF, CEOF }, + { "eol", VEOL, CEOL }, + { "eol2", VEOL2, CEOL }, + { "erase", VERASE, CERASE }, + { "intr", VINTR, CINTR }, + { "kill", VKILL, CKILL }, + { "lnext", VLNEXT, CLNEXT }, + { "min", VMIN, CMIN }, + { "quit", VQUIT, CQUIT }, + { "reprint", VREPRINT, CREPRINT }, + { "start", VSTART, CSTART }, + { "status", VSTATUS, CSTATUS }, + { "stop", VSTOP, CSTOP }, + { "susp", VSUSP, CSUSP }, + { "time", VTIME, CTIME }, + { "werase", VWERASE, CWERASE }, + { NULL }, +}; + +struct cchar cchars2[] = { + { "brk", VEOL, CEOL }, + { "flush", VDISCARD, CDISCARD }, + { "rprnt", VREPRINT, CREPRINT }, + { NULL }, +}; + +static int +c_cchar(a, b) + const void *a, *b; +{ + + return (strcmp(((struct cchar *)a)->name, ((struct cchar *)b)->name)); +} + +int +csearch(argvp, ip) + char ***argvp; + struct info *ip; +{ + struct cchar *cp, tmp; + long val; + char *arg, *ep, *name; + + name = **argvp; + + tmp.name = name; + if (!(cp = (struct cchar *)bsearch(&tmp, cchars1, + sizeof(cchars1)/sizeof(struct cchar) - 1, sizeof(struct cchar), + c_cchar)) && !(cp = (struct cchar *)bsearch(&tmp, cchars1, + sizeof(cchars1)/sizeof(struct cchar) - 1, sizeof(struct cchar), + c_cchar))) + return (0); + + arg = *++*argvp; + if (!arg) { + warnx("option requires an argument -- %s", name); + usage(); + } + +#define CHK(s) (*arg == s[0] && !strcmp(arg, s)) + if (CHK("undef") || CHK("<undef>")) + ip->t.c_cc[cp->sub] = _POSIX_VDISABLE; + else if (cp->sub == VMIN || cp->sub == VTIME) { + val = strtol(arg, &ep, 10); + if (val == _POSIX_VDISABLE) { + warnx("value of %ld would disable the option -- %s", + val, name); + usage(); + } + if (val > UCHAR_MAX) { + warnx("maximum option value is %d -- %s", + UCHAR_MAX, name); + usage(); + } + if (*ep != '\0') { + warnx("option requires a numeric argument -- %s", name); + usage(); + } + ip->t.c_cc[cp->sub] = val; + } else if (arg[0] == '^') + ip->t.c_cc[cp->sub] = (arg[1] == '?') ? 0177 : + (arg[1] == '-') ? _POSIX_VDISABLE : arg[1] & 037; + else + ip->t.c_cc[cp->sub] = arg[0]; + ip->set = 1; + return (1); +} diff --git a/bin/stty/extern.h b/bin/stty/extern.h new file mode 100644 index 0000000..a600ca9 --- /dev/null +++ b/bin/stty/extern.h @@ -0,0 +1,48 @@ +/*- + * 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 + */ + +int c_cchars __P((const void *, const void *)); +int c_modes __P((const void *, const void *)); +int csearch __P((char ***, struct info *)); +void checkredirect __P((void)); +void gprint __P((struct termios *, struct winsize *, int)); +void gread __P((struct termios *, char *)); +int ksearch __P((char ***, struct info *)); +int msearch __P((char ***, struct info *)); +void optlist __P((void)); +void print __P((struct termios *, struct winsize *, int, enum FMT)); +void usage __P((void)); + +extern struct cchar cchars1[], cchars2[]; diff --git a/bin/stty/gfmt.c b/bin/stty/gfmt.c new file mode 100644 index 0000000..97f980a --- /dev/null +++ b/bin/stty/gfmt.c @@ -0,0 +1,129 @@ +/*- + * 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[] = "@(#)gfmt.c 8.6 (Berkeley) 4/2/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <err.h> +#include <stdio.h> +#include <string.h> + +#include "stty.h" +#include "extern.h" + +static void +gerr(s) + char *s; +{ + if (s) + errx(1, "illegal gfmt1 option -- %s", s); + else + errx(1, "illegal gfmt1 option"); +} + +void +gprint(tp, wp, ldisc) + struct termios *tp; + struct winsize *wp; + int ldisc; +{ + struct cchar *cp; + + (void)printf("gfmt1:cflag=%x:iflag=%x:lflag=%x:oflag=%x:", + tp->c_cflag, tp->c_iflag, tp->c_lflag, tp->c_oflag); + for (cp = cchars1; cp->name; ++cp) + (void)printf("%s=%x:", cp->name, tp->c_cc[cp->sub]); + (void)printf("ispeed=%d:ospeed=%d\n", cfgetispeed(tp), cfgetospeed(tp)); +} + +void +gread(tp, s) + struct termios *tp; + char *s; +{ + struct cchar *cp; + char *ep, *p; + long tmp; + + if ((s = strchr(s, ':')) == NULL) + gerr(NULL); + for (++s; s != NULL;) { + p = strsep(&s, ":\0"); + if (!p || !*p) + break; + if (!(ep = strchr(p, '='))) + gerr(p); + *ep++ = '\0'; + (void)sscanf(ep, "%lx", &tmp); + +#define CHK(s) (*p == s[0] && !strcmp(p, s)) + if (CHK("cflag")) { + tp->c_cflag = tmp; + continue; + } + if (CHK("iflag")) { + tp->c_iflag = tmp; + continue; + } + if (CHK("ispeed")) { + (void)sscanf(ep, "%ld", &tmp); + tp->c_ispeed = tmp; + continue; + } + if (CHK("lflag")) { + tp->c_lflag = tmp; + continue; + } + if (CHK("oflag")) { + tp->c_oflag = tmp; + continue; + } + if (CHK("ospeed")) { + (void)sscanf(ep, "%ld", &tmp); + tp->c_ospeed = tmp; + continue; + } + for (cp = cchars1; cp->name != NULL; ++cp) + if (CHK(cp->name)) { + if (cp->sub == VMIN || cp->sub == VTIME) + (void)sscanf(ep, "%ld", &tmp); + tp->c_cc[cp->sub] = tmp; + break; + } + if (cp->name == NULL) + gerr(p); + } +} diff --git a/bin/stty/key.c b/bin/stty/key.c new file mode 100644 index 0000000..cf4fde4 --- /dev/null +++ b/bin/stty/key.c @@ -0,0 +1,299 @@ +/*- + * 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[] = "@(#)key.c 8.3 (Berkeley) 4/2/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <err.h> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "stty.h" +#include "extern.h" + +__BEGIN_DECLS +void f_all __P((struct info *)); +void f_cbreak __P((struct info *)); +void f_columns __P((struct info *)); +void f_dec __P((struct info *)); +void f_everything __P((struct info *)); +void f_extproc __P((struct info *)); +void f_ispeed __P((struct info *)); +void f_nl __P((struct info *)); +void f_ospeed __P((struct info *)); +void f_raw __P((struct info *)); +void f_rows __P((struct info *)); +void f_sane __P((struct info *)); +void f_size __P((struct info *)); +void f_speed __P((struct info *)); +void f_tty __P((struct info *)); +__END_DECLS + +static struct key { + char *name; /* name */ + void (*f) __P((struct info *)); /* function */ +#define F_NEEDARG 0x01 /* needs an argument */ +#define F_OFFOK 0x02 /* can turn off */ + int flags; +} keys[] = { + { "all", f_all, 0 }, + { "cbreak", f_cbreak, F_OFFOK }, + { "cols", f_columns, F_NEEDARG }, + { "columns", f_columns, F_NEEDARG }, + { "cooked", f_sane, 0 }, + { "dec", f_dec, 0 }, + { "everything", f_everything, 0 }, + { "extproc", f_extproc, F_OFFOK }, + { "ispeed", f_ispeed, F_NEEDARG }, + { "new", f_tty, 0 }, + { "nl", f_nl, F_OFFOK }, + { "old", f_tty, 0 }, + { "ospeed", f_ospeed, F_NEEDARG }, + { "raw", f_raw, F_OFFOK }, + { "rows", f_rows, F_NEEDARG }, + { "sane", f_sane, 0 }, + { "size", f_size, 0 }, + { "speed", f_speed, 0 }, + { "tty", f_tty, 0 }, +}; + +static int +c_key(a, b) + const void *a, *b; +{ + + return (strcmp(((struct key *)a)->name, ((struct key *)b)->name)); +} + +int +ksearch(argvp, ip) + char ***argvp; + struct info *ip; +{ + char *name; + struct key *kp, tmp; + + name = **argvp; + if (*name == '-') { + ip->off = 1; + ++name; + } else + ip->off = 0; + + tmp.name = name; + if (!(kp = (struct key *)bsearch(&tmp, keys, + sizeof(keys)/sizeof(struct key), sizeof(struct key), c_key))) + return (0); + if (!(kp->flags & F_OFFOK) && ip->off) { + errx(1, "illegal option -- %s", name); + usage(); + } + if (kp->flags & F_NEEDARG && !(ip->arg = *++*argvp)) { + errx(1, "option requires an argument -- %s", name); + usage(); + } + kp->f(ip); + return (1); +} + +void +f_all(ip) + struct info *ip; +{ + print(&ip->t, &ip->win, ip->ldisc, BSD); +} + +void +f_cbreak(ip) + struct info *ip; +{ + + if (ip->off) + f_sane(ip); + else { + ip->t.c_iflag |= BRKINT|IXON|IMAXBEL; + ip->t.c_oflag |= OPOST; + ip->t.c_lflag |= ISIG|IEXTEN; + ip->t.c_lflag &= ~ICANON; + ip->set = 1; + } +} + +void +f_columns(ip) + struct info *ip; +{ + + ip->win.ws_col = atoi(ip->arg); + ip->wset = 1; +} + +void +f_dec(ip) + struct info *ip; +{ + + ip->t.c_cc[VERASE] = (u_char)0177; + ip->t.c_cc[VKILL] = CTRL('u'); + ip->t.c_cc[VINTR] = CTRL('c'); + ip->t.c_lflag &= ~ECHOPRT; + ip->t.c_lflag |= ECHOE|ECHOKE|ECHOCTL; + ip->t.c_iflag &= ~IXANY; + ip->set = 1; +} + +void +f_everything(ip) + struct info *ip; +{ + + print(&ip->t, &ip->win, ip->ldisc, BSD); +} + +void +f_extproc(ip) + struct info *ip; +{ + + if (ip->set) { + int tmp = 1; + (void)ioctl(ip->fd, TIOCEXT, &tmp); + } else { + int tmp = 0; + (void)ioctl(ip->fd, TIOCEXT, &tmp); + } +} + +void +f_ispeed(ip) + struct info *ip; +{ + + cfsetispeed(&ip->t, atoi(ip->arg)); + ip->set = 1; +} + +void +f_nl(ip) + struct info *ip; +{ + + if (ip->off) { + ip->t.c_iflag |= ICRNL; + ip->t.c_oflag |= ONLCR; + } else { + ip->t.c_iflag &= ~ICRNL; + ip->t.c_oflag &= ~ONLCR; + } + ip->set = 1; +} + +void +f_ospeed(ip) + struct info *ip; +{ + + cfsetospeed(&ip->t, atoi(ip->arg)); + ip->set = 1; +} + +void +f_raw(ip) + struct info *ip; +{ + + if (ip->off) + f_sane(ip); + else { + cfmakeraw(&ip->t); + ip->t.c_cflag &= ~(CSIZE|PARENB); + ip->t.c_cflag |= CS8; + ip->set = 1; + } +} + +void +f_rows(ip) + struct info *ip; +{ + + ip->win.ws_row = atoi(ip->arg); + ip->wset = 1; +} + +void +f_sane(ip) + struct info *ip; +{ + + ip->t.c_cflag = TTYDEF_CFLAG | (ip->t.c_cflag & CLOCAL); + ip->t.c_iflag = TTYDEF_IFLAG; + ip->t.c_iflag |= ICRNL; + /* preserve user-preference flags in lflag */ +#define LKEEP (ECHOKE|ECHOE|ECHOK|ECHOPRT|ECHOCTL|ALTWERASE|TOSTOP|NOFLSH) + ip->t.c_lflag = TTYDEF_LFLAG | (ip->t.c_lflag & LKEEP); + ip->t.c_oflag = TTYDEF_OFLAG; + ip->set = 1; +} + +void +f_size(ip) + struct info *ip; +{ + + (void)printf("%d %d\n", ip->win.ws_row, ip->win.ws_col); +} + +void +f_speed(ip) + struct info *ip; +{ + + (void)printf("%d\n", cfgetospeed(&ip->t)); +} + +void +f_tty(ip) + struct info *ip; +{ + int tmp; + + tmp = TTYDISC; + if (ioctl(0, TIOCSETD, &tmp) < 0) + err(1, "TIOCSETD"); +} diff --git a/bin/stty/modes.c b/bin/stty/modes.c new file mode 100644 index 0000000..d900da9 --- /dev/null +++ b/bin/stty/modes.c @@ -0,0 +1,230 @@ +/*- + * 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[] = "@(#)modes.c 8.3 (Berkeley) 4/2/94"; +#endif /* not lint */ + +#include <sys/types.h> +#include <stddef.h> +#include <string.h> +#include "stty.h" + +struct modes { + char *name; + long set; + long unset; +}; + +/* + * The code in optlist() depends on minus options following regular + * options, i.e. "foo" must immediately precede "-foo". + */ +struct modes cmodes[] = { + { "cs5", CS5, CSIZE }, + { "cs6", CS6, CSIZE }, + { "cs7", CS7, CSIZE }, + { "cs8", CS8, CSIZE }, + { "cstopb", CSTOPB, 0 }, + { "-cstopb", 0, CSTOPB }, + { "cread", CREAD, 0 }, + { "-cread", 0, CREAD }, + { "parenb", PARENB, 0 }, + { "-parenb", 0, PARENB }, + { "parodd", PARODD, 0 }, + { "-parodd", 0, PARODD }, + { "parity", PARENB | CS7, PARODD | CSIZE }, + { "-parity", CS8, PARODD | PARENB | CSIZE }, + { "evenp", PARENB | CS7, PARODD | CSIZE }, + { "-evenp", CS8, PARODD | PARENB | CSIZE }, + { "oddp", PARENB | CS7 | PARODD, CSIZE }, + { "-oddp", CS8, PARODD | PARENB | CSIZE }, + { "pass8", CS8, PARODD | PARENB | CSIZE }, + { "-pass8", PARENB | CS7, PARODD | CSIZE }, + { "hupcl", HUPCL, 0 }, + { "-hupcl", 0, HUPCL }, + { "hup", HUPCL, 0 }, + { "-hup", 0, HUPCL }, + { "clocal", CLOCAL, 0 }, + { "-clocal", 0, CLOCAL }, + { "crtscts", CRTSCTS, 0 }, + { "-crtscts", 0, CRTSCTS }, + { "mdmbuf", MDMBUF, 0 }, + { "-mdmbuf", 0, MDMBUF }, + { NULL }, +}; + +struct modes imodes[] = { + { "ignbrk", IGNBRK, 0 }, + { "-ignbrk", 0, IGNBRK }, + { "brkint", BRKINT, 0 }, + { "-brkint", 0, BRKINT }, + { "ignpar", IGNPAR, 0 }, + { "-ignpar", 0, IGNPAR }, + { "parmrk", PARMRK, 0 }, + { "-parmrk", 0, PARMRK }, + { "inpck", INPCK, 0 }, + { "-inpck", 0, INPCK }, + { "istrip", ISTRIP, 0 }, + { "-istrip", 0, ISTRIP }, + { "inlcr", INLCR, 0 }, + { "-inlcr", 0, INLCR }, + { "igncr", IGNCR, 0 }, + { "-igncr", 0, IGNCR }, + { "icrnl", ICRNL, 0 }, + { "-icrnl", 0, ICRNL }, + { "ixon", IXON, 0 }, + { "-ixon", 0, IXON }, + { "flow", IXON, 0 }, + { "-flow", 0, IXON }, + { "ixoff", IXOFF, 0 }, + { "-ixoff", 0, IXOFF }, + { "tandem", IXOFF, 0 }, + { "-tandem", 0, IXOFF }, + { "ixany", IXANY, 0 }, + { "-ixany", 0, IXANY }, + { "decctlq", 0, IXANY }, + { "-decctlq", IXANY, 0 }, + { "imaxbel", IMAXBEL, 0 }, + { "-imaxbel", 0, IMAXBEL }, + { NULL }, +}; + +struct modes lmodes[] = { + { "echo", ECHO, 0 }, + { "-echo", 0, ECHO }, + { "echoe", ECHOE, 0 }, + { "-echoe", 0, ECHOE }, + { "crterase", ECHOE, 0 }, + { "-crterase", 0, ECHOE }, + { "crtbs", ECHOE, 0 }, /* crtbs not supported, close enough */ + { "-crtbs", 0, ECHOE }, + { "echok", ECHOK, 0 }, + { "-echok", 0, ECHOK }, + { "echoke", ECHOKE, 0 }, + { "-echoke", 0, ECHOKE }, + { "crtkill", ECHOKE, 0 }, + { "-crtkill", 0, ECHOKE }, + { "altwerase", ALTWERASE, 0 }, + { "-altwerase", 0, ALTWERASE }, + { "iexten", IEXTEN, 0 }, + { "-iexten", 0, IEXTEN }, + { "echonl", ECHONL, 0 }, + { "-echonl", 0, ECHONL }, + { "echoctl", ECHOCTL, 0 }, + { "-echoctl", 0, ECHOCTL }, + { "ctlecho", ECHOCTL, 0 }, + { "-ctlecho", 0, ECHOCTL }, + { "echoprt", ECHOPRT, 0 }, + { "-echoprt", 0, ECHOPRT }, + { "prterase", ECHOPRT, 0 }, + { "-prterase", 0, ECHOPRT }, + { "isig", ISIG, 0 }, + { "-isig", 0, ISIG }, + { "icanon", ICANON, 0 }, + { "-icanon", 0, ICANON }, + { "noflsh", NOFLSH, 0 }, + { "-noflsh", 0, NOFLSH }, + { "tostop", TOSTOP, 0 }, + { "-tostop", 0, TOSTOP }, + { "flusho", FLUSHO, 0 }, + { "-flusho", 0, FLUSHO }, + { "pendin", PENDIN, 0 }, + { "-pendin", 0, PENDIN }, + { "crt", ECHOE|ECHOKE|ECHOCTL, ECHOK|ECHOPRT }, + { "-crt", ECHOK, ECHOE|ECHOKE|ECHOCTL }, + { "newcrt", ECHOE|ECHOKE|ECHOCTL, ECHOK|ECHOPRT }, + { "-newcrt", ECHOK, ECHOE|ECHOKE|ECHOCTL }, + { "nokerninfo", NOKERNINFO, 0 }, + { "-nokerninfo",0, NOKERNINFO }, + { "kerninfo", 0, NOKERNINFO }, + { "-kerninfo", NOKERNINFO, 0 }, + { NULL }, +}; + +struct modes omodes[] = { + { "opost", OPOST, 0 }, + { "-opost", 0, OPOST }, + { "litout", 0, OPOST }, + { "-litout", OPOST, 0 }, + { "onlcr", ONLCR, 0 }, + { "-onlcr", 0, ONLCR }, + { "tabs", 0, OXTABS }, /* "preserve" tabs */ + { "-tabs", OXTABS, 0 }, + { "oxtabs", OXTABS, 0 }, + { "-oxtabs", 0, OXTABS }, + { NULL }, +}; + +#define CHK(s) (*name == s[0] && !strcmp(name, s)) + +int +msearch(argvp, ip) + char ***argvp; + struct info *ip; +{ + struct modes *mp; + char *name; + + name = **argvp; + + for (mp = cmodes; mp->name; ++mp) + if (CHK(mp->name)) { + ip->t.c_cflag &= ~mp->unset; + ip->t.c_cflag |= mp->set; + ip->set = 1; + return (1); + } + for (mp = imodes; mp->name; ++mp) + if (CHK(mp->name)) { + ip->t.c_iflag &= ~mp->unset; + ip->t.c_iflag |= mp->set; + ip->set = 1; + return (1); + } + for (mp = lmodes; mp->name; ++mp) + if (CHK(mp->name)) { + ip->t.c_lflag &= ~mp->unset; + ip->t.c_lflag |= mp->set; + ip->set = 1; + return (1); + } + for (mp = omodes; mp->name; ++mp) + if (CHK(mp->name)) { + ip->t.c_oflag &= ~mp->unset; + ip->t.c_oflag |= mp->set; + ip->set = 1; + return (1); + } + return (0); +} diff --git a/bin/stty/print.c b/bin/stty/print.c new file mode 100644 index 0000000..21f5379 --- /dev/null +++ b/bin/stty/print.c @@ -0,0 +1,266 @@ +/*- + * 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[] = "@(#)print.c 8.6 (Berkeley) 4/16/94"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <stddef.h> +#include <stdio.h> +#include <string.h> + +#include "stty.h" +#include "extern.h" + +static void binit __P((char *)); +static void bput __P((char *)); +static char *ccval __P((struct cchar *, int)); + +void +print(tp, wp, ldisc, fmt) + struct termios *tp; + struct winsize *wp; + int ldisc; + enum FMT fmt; +{ + struct cchar *p; + long tmp; + u_char *cc; + int cnt, ispeed, ospeed; + char buf1[100], buf2[100]; + + cnt = 0; + + /* Line discipline. */ + if (ldisc != TTYDISC) { + switch(ldisc) { + case TABLDISC: + cnt += printf("tablet disc; "); + break; + case SLIPDISC: + cnt += printf("slip disc; "); + break; + default: + cnt += printf("#%d disc; ", ldisc); + break; + } + } + + /* Line speed. */ + ispeed = cfgetispeed(tp); + ospeed = cfgetospeed(tp); + if (ispeed != ospeed) + cnt += + printf("ispeed %d baud; ospeed %d baud;", ispeed, ospeed); + else + cnt += printf("speed %d baud;", ispeed); + if (fmt >= BSD) + cnt += printf(" %d rows; %d columns;", wp->ws_row, wp->ws_col); + if (cnt) + (void)printf("\n"); + +#define on(f) ((tmp&f) != 0) +#define put(n, f, d) \ + if (fmt >= BSD || on(f) != d) \ + bput(n + on(f)); + + /* "local" flags */ + tmp = tp->c_lflag; + binit("lflags"); + put("-icanon", ICANON, 1); + put("-isig", ISIG, 1); + put("-iexten", IEXTEN, 1); + put("-echo", ECHO, 1); + put("-echoe", ECHOE, 0); + put("-echok", ECHOK, 0); + put("-echoke", ECHOKE, 0); + put("-echonl", ECHONL, 0); + put("-echoctl", ECHOCTL, 0); + put("-echoprt", ECHOPRT, 0); + put("-altwerase", ALTWERASE, 0); + put("-noflsh", NOFLSH, 0); + put("-tostop", TOSTOP, 0); + put("-flusho", FLUSHO, 0); + put("-pendin", PENDIN, 0); + put("-nokerninfo", NOKERNINFO, 0); + put("-extproc", EXTPROC, 0); + + /* input flags */ + tmp = tp->c_iflag; + binit("iflags"); + put("-istrip", ISTRIP, 0); + put("-icrnl", ICRNL, 1); + put("-inlcr", INLCR, 0); + put("-igncr", IGNCR, 0); + put("-ixon", IXON, 1); + put("-ixoff", IXOFF, 0); + put("-ixany", IXANY, 1); + put("-imaxbel", IMAXBEL, 1); + put("-ignbrk", IGNBRK, 0); + put("-brkint", BRKINT, 1); + put("-inpck", INPCK, 0); + put("-ignpar", IGNPAR, 0); + put("-parmrk", PARMRK, 0); + + /* output flags */ + tmp = tp->c_oflag; + binit("oflags"); + put("-opost", OPOST, 1); + put("-onlcr", ONLCR, 1); + put("-oxtabs", OXTABS, 1); + + /* control flags (hardware state) */ + tmp = tp->c_cflag; + binit("cflags"); + put("-cread", CREAD, 1); + switch(tmp&CSIZE) { + case CS5: + bput("cs5"); + break; + case CS6: + bput("cs6"); + break; + case CS7: + bput("cs7"); + break; + case CS8: + bput("cs8"); + break; + } + bput("-parenb" + on(PARENB)); + put("-parodd", PARODD, 0); + put("-hupcl", HUPCL, 1); + put("-clocal", CLOCAL, 0); + put("-cstopb", CSTOPB, 0); + put("-crtscts", CRTSCTS, 0); + put("-mdmbuf", MDMBUF, 0); + + /* special control characters */ + cc = tp->c_cc; + if (fmt == POSIX) { + binit("cchars"); + for (p = cchars1; p->name; ++p) { + (void)snprintf(buf1, sizeof(buf1), "%s = %s;", + p->name, ccval(p, cc[p->sub])); + bput(buf1); + } + binit(NULL); + } else { + binit(NULL); + for (p = cchars1, cnt = 0; p->name; ++p) { + if (fmt != BSD && cc[p->sub] == p->def) + continue; +#define WD "%-8s" + (void)sprintf(buf1 + cnt * 8, WD, p->name); + (void)sprintf(buf2 + cnt * 8, WD, ccval(p, cc[p->sub])); + if (++cnt == LINELENGTH / 8) { + cnt = 0; + (void)printf("%s\n", buf1); + (void)printf("%s\n", buf2); + } + } + if (cnt) { + (void)printf("%s\n", buf1); + (void)printf("%s\n", buf2); + } + } +} + +static int col; +static char *label; + +static void +binit(lb) + char *lb; +{ + + if (col) { + (void)printf("\n"); + col = 0; + } + label = lb; +} + +static void +bput(s) + char *s; +{ + + if (col == 0) { + col = printf("%s: %s", label, s); + return; + } + if ((col + strlen(s)) > LINELENGTH) { + (void)printf("\n\t"); + col = printf("%s", s) + 8; + return; + } + col += printf(" %s", s); +} + +static char * +ccval(p, c) + struct cchar *p; + int c; +{ + static char buf[5]; + char *bp; + + if (c == _POSIX_VDISABLE) + return ("<undef>"); + + if (p->sub == VMIN || p->sub == VTIME) { + (void)snprintf(buf, sizeof(buf), "%d", c); + return (buf); + } + bp = buf; + if (c & 0200) { + *bp++ = 'M'; + *bp++ = '-'; + c &= 0177; + } + if (c == 0177) { + *bp++ = '^'; + *bp++ = '?'; + } + else if (c < 040) { + *bp++ = '^'; + *bp++ = c + '@'; + } + else + *bp++ = c; + *bp = '\0'; + return (buf); +} diff --git a/bin/stty/stty.1 b/bin/stty/stty.1 new file mode 100644 index 0000000..e9665d7 --- /dev/null +++ b/bin/stty/stty.1 @@ -0,0 +1,580 @@ +.\" Copyright (c) 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. +.\" +.\" @(#)stty.1 8.4 (Berkeley) 4/18/94 +.\" +.Dd April 18, 1994 +.Dt STTY 1 +.Os +.Sh NAME +.Nm stty +.Nd set the options for a terminal device interface +.Sh SYNOPSIS +.Nm stty +.Op Fl a | Fl e | Fl g +.Op Fl f Ar file +.Op operands +.Sh DESCRIPTION +The +.Nm stty +utility sets or reports on terminal +characteristics for the device that is its standard input. +If no options or operands are specified, it reports the settings of a subset +of characteristics as well as additional ones if they differ from their +default values. +Otherwise it modifies +the terminal state according to the specified arguments. +Some combinations of arguments are mutually +exclusive on some terminal types. +.Pp +The following options are available: +.Bl -tag -width Ds +.It Fl a +Display all the current settings for the terminal to standard output +as per +.St -p1003.2 . +.It Fl e +Display all the current settings for the terminal to standard output +in the traditional +.Tn BSD +``all'' and ``everything'' formats. +.It Fl f +Open and use the terminal named by +.Ar file +rather than using standard input. The file is opened +using the +.Dv O_NONBLOCK +flag of +.Fn open , +making it possible to +set or display settings on a terminal that might otherwise +block on the open. +.It Fl g +Display all the current settings for the terminal to standard output +in a form that may be used as an argument to a subsequent invocation of +.Nm stty +to restore the current terminal state as per +.St -p1003.2 . +.El +.Pp +The following arguments are available to set the terminal +characteristics: +.Ss Control Modes: +.Pp +Control mode flags affect hardware characteristics associated with the +terminal. This corresponds to the c_cflag in the termios structure. +.Bl -tag -width Fl +.It Cm parenb Pq Fl parenb +Enable (disable) parity generation +and detection. +.It Cm parodd Pq Fl parodd +Select odd (even) parity. +.It Cm cs5 cs6 cs7 cs8 +Select character size, if possible. +.It Ar number +Set terminal baud rate to the +number given, if possible. +If the +baud rate is set to zero, modem +control is no longer +asserted. +.It Cm ispeed Ar number +Set terminal input baud rate to the +number given, if possible. +If the +input baud rate is set to zero, the +input baud rate is set to the +value of the output baud +rate. +.It Cm ospeed Ar number +Set terminal output baud rate to +the number given, if possible. +If +the output baud rate is set to +zero, modem control is +no longer asserted. +.It Cm speed Ar number +This sets both +.Cm ispeed +and +.Cm ospeed +to +.Ar number . +.It Cm hupcl Pq Fl hupcl +Stop asserting modem control +(do not stop asserting modem control) on last close. +.It Cm hup Pq Fl hup +Same as hupcl +.Pq Fl hupcl . +.It Cm cstopb Pq Fl cstopb +Use two (one) stop bits per character. +.It Cm cread Pq Fl cread +Enable (disable) the receiver. +.It Cm clocal Pq Fl clocal +Assume a line without (with) modem +control. +.It Cm crtscts Pq Fl crtscts +Enable RTS/CTS flow control. +.El +.Ss Input Modes: +This corresponds to the c_iflag in the termios structure. +.Bl -tag -width Fl +.It Cm ignbrk Pq Fl ignbrk +Ignore (do not ignore) break on +input. +.It Cm brkint Pq Fl brkint +Signal (do not signal) +.Dv INTR +on +break. +.It Cm ignpar Pq Fl ignpar +Ignore (do not ignore) parity +errors. +.It Cm parmrk Pq Fl parmrk +Mark (do not mark) parity errors. +.It Cm inpck Pq Fl inpck +Enable (disable) input parity +checking. +.It Cm istrip Pq Fl istrip +Strip (do not strip) input characters +to seven bits. +.It Cm inlcr Pq Fl inlcr +Map (do not map) +.Dv NL +to +.Dv CR +on input. +.It Cm igncr Pq Fl igncr +Ignore (do not ignore) +.Dv CR +on input. +.It Cm icrnl Pq Fl icrnl +Map (do not map) +.Dv CR +to +.Dv NL +on input. +.It Cm ixon Pq Fl ixon +Enable (disable) +.Dv START/STOP +output +control. +Output from the system is +stopped when the system receives +.Dv STOP +and started when the system +receives +.Dv START , +or if +.Cm ixany +is set, any character restarts output. +.It Cm ixoff Pq Fl ixoff +Request that the system send (not +send) +.Dv START/STOP +characters when +the input queue is nearly +empty/full. +.It Cm ixany Pq Fl ixany +Allow any character (allow only +.Dv START ) +to restart output. +.It Cm imaxbel Pq Fl imaxbel +The system imposes a limit of +.Dv MAX_INPUT +(currently 255) characters in the input queue. If +.Cm imaxbel +is set and the input queue limit has been reached, +subsequent input causes the system to send an ASCII BEL +character to the output queue (the terminal beeps at you). Otherwise, +if +.Cm imaxbel +is unset and the input queue is full, the next input character causes +the entire input and output queues to be discarded. +.El +.Ss Output Modes: +This corresponds to the c_oflag of the termios structure. +.Bl -tag -width Fl +.It Cm opost Pq Fl opost +Post-process output (do not +post-process output; ignore all other +output modes). +.It Cm onlcr Pq Fl onlcr +Map (do not map) +.Dv NL +to +.DV CR-NL +on output. +.It Cm oxtabs Pq Fl oxtabs +Expand (do not expand) tabs to spaces on output. +.El +.Ss Local Modes: +.Pp +Local mode flags (lflags) affect various and sundry characteristics of terminal +processing. +Historically the term "local" pertained to new job control features +implemented by Jim Kulp on a +.Tn Pdp 11/70 +at +.Tn IIASA . +Later the driver ran on the first +.Tn VAX +at Evans Hall, UC Berkeley, where the job control details +were greatly modified but the structure definitions and names +remained essentially unchanged. +The second interpretation of the 'l' in lflag +is ``line discipline flag'' which corresponds to the +.Ar c_lflag +of the +.Ar termios +structure. +.Bl -tag -width Fl +.It Cm isig Pq Fl isig +Enable (disable) the checking of +characters against the special control +characters +.Dv INTR , QUIT , +and +.Dv SUSP . +.It Cm icanon Pq Fl icanon +Enable (disable) canonical input +.Pf ( Dv ERASE +and +.Dv KILL +processing). +.It Cm iexten Pq Fl iexten +Enable (disable) any implementation +defined special control characters +not currently controlled by icanon, +isig, or ixon. +.It Cm echo Pq Fl echo +Echo back (do not echo back) every +character typed. +.It Cm echoe Pq Fl echoe +The +.Dv ERASE +character shall (shall +not) visually erase the last character +in the current line from the +display, if possible. +.It Cm echok Pq Fl echok +Echo (do not echo) +.Dv NL +after +.Dv KILL +character. +.It Cm echoke Pq Fl echoke +The +.Dv KILL +character shall (shall +not) visually erase the +the current line from the +display, if possible. +.It Cm echonl Pq Fl echonl +Echo (do not echo) +.Dv NL , +even if echo +is disabled. +.It Cm echoctl Pq Fl echoctl +If +.Cm echoctl +is set, echo control characters as ^X. Otherwise control characters +echo as themselves. +.It Cm echoprt Pq Fl echoprt +For printing terminals. If set, echo erased characters backwards within ``\\'' +and ``/''. Otherwise, disable this feature. +.It Cm noflsh Pq Fl noflsh +Disable (enable) flush after +.Dv INTR , QUIT , SUSP . +.It Cm tostop Pq Fl tostop +Send (do not send) +.Dv SIGTTOU +for background output. This causes background jobs to stop if they attempt +terminal output. +.It Cm altwerase Pq Fl altwerase +Use (do not use) an alternate word erase algorithm when processing +.Dv WERASE +characters. +This alternate algorithm considers sequences of +alphanumeric/underscores as words. +It also skips the first preceding character in its classification +(as a convenience since the one preceding character could have been +erased with simply an +.Dv ERASE +character.) +.It Cm mdmbuf Pq Fl mdmbuf +If set, flow control output based on condition of Carrier Detect. Otherwise +writes return an error if Carrier Detect is low (and Carrier is not being +ignored with the +.Dv CLOCAL +flag.) +.It Cm flusho Pq Fl flusho +Indicates output is (is not) being discarded. +.It Cm pendin Pq Fl pendin +Indicates input is (is not) pending after a switch from non-canonical +to canonical mode and will be re-input when a read becomes pending +or more input arrives. +.El +.Ss Control Characters: +.Bl -tag -width Fl +.It Ar control-character Ar string +Set +.Ar control-character +to +.Ar string . +If string is a single character, +the control character is set to +that character. +If string is the +two character sequence "^-" or the +string "undef" the control character +is disabled (i.e. set to +.Pf { Dv _POSIX_VDISABLE Ns } . ) +.Pp +Recognized control-characters: +.Bd -ragged -offset indent +.Bl -column character Subscript +.It control- +.It character Subscript Description +.It _________ _________ _______________ +.It eof Ta Tn VEOF EOF No character +.It eol Ta Tn VEOL EOL No character +.It eol2 Ta Tn VEOL2 EOL2 No character +.It erase Ta Tn VERASE ERASE No character +.It werase Ta Tn VWERASE WERASE No character +.It intr Ta Tn VINTR INTR No character +.It kill Ta Tn VKILL KILL No character +.It quit Ta Tn VQUIT QUIT No character +.It susp Ta Tn VSUSP SUSP No character +.It start Ta Tn VSTART START No character +.It stop Ta Tn VSTOP STOP No character +.It dsusp Ta Tn VDSUSP DSUSP No character +.It lnext Ta Tn VLNEXT LNEXT No character +.It reprint Ta Tn VREPRINT REPRINT No character +.It status Ta Tn VSTATUS STATUS No character +.El +.Ed +.It Cm min Ar number +.It Cm time Ar number +Set the value of min or time to +number. +.Dv MIN +and +.Dv TIME +are used in +Non-Canonical mode input processing +(-icanon). +.El +.Ss Combination Modes: +.Pp +.Bl -tag -width Fl +.It Ar saved settings +Set the current terminal +characteristics to the saved settings +produced by the +.Fl g +option. +.It Cm evenp No or Cm parity +Enable parenb and cs7; disable +parodd. +.It Cm oddp +Enable parenb, cs7, and parodd. +.It Fl parity , evenp , oddp +Disable parenb, and set cs8. +.It Cm \&nl Pq Fl \&nl +Enable (disable) icrnl. +In addition +-nl unsets inlcr and igncr. +.It Cm ek +Reset +.Dv ERASE +and +.Dv KILL +characters +back to system defaults. +.It Cm sane +Resets all modes to reasonable values for interactive terminal use. +.It Cm tty +Set the line discipline to the standard terminal line discipline +.Dv TTYDISC . +.It Cm crt Pq Fl crt +Set (disable) all modes suitable for a CRT display device. +.It Cm kerninfo Pq Fl kerninfo +Enable (disable) the system generated status line associated with +processing a +.Dv STATUS +character (usually set to ^T). The status line consists of the +system load average, the current command name, its process ID, the +event the process is waiting on (or the status of the process), the user +and system times, percent cpu, and current memory usage. +.It Cm columns Ar number +The terminal size is recorded as having +.Ar number +columns. +.It Cm cols Ar number +is an alias for +.Cm columns. +.It Cm rows Ar number +The terminal size is recorded as having +.Ar number +rows. +.It Cm dec +Set modes suitable for users of Digital Equipment Corporation systems ( +.Dv ERASE , +.Dv KILL , +and +.Dv INTR +characters are set to ^?, ^U, and ^C; +.Dv ixany +is disabled, and +.Dv crt +is enabled.) +.It Cm extproc Pq Fl extproc +If set, this flag indicates that some amount of terminal processing is being +performed by either the terminal hardware or by the remote side connected +to a pty. +.It Cm raw Pq Fl raw +If set, change the modes of the terminal so that no input or output processing +is performed. If unset, change the modes of the terminal to some reasonable +state that performs input and output processing. Note that since the +terminal driver no longer has a single +.Dv RAW +bit, it is not possible to intuit what flags were set prior to setting +.Cm raw . +This means that unsetting +.Cm raw +may not put back all the setting that were previously in effect. +To set the terminal into a raw state and then accurately restore it, the following +shell code is recommended: +.nf + +save_state=$(stty -g) +stty raw +\&... +stty "$save_state" + +.fi +.It Cm size +The size of the terminal is printed as two numbers on a single line, +first rows, then columns. +.El +.Ss Compatibility Modes: +.Pp +These modes remain for compatibility with the previous version of +the stty command. +.Bl -tag -width Fl +.It Cm all +Reports all the terminal modes as with +.Cm stty Fl a +except that the control characters are printed in a columnar format. +.It Cm everything +Same as +.Cm all . +.It Cm cooked +Same as +.Cm sane . +.It Cm cbreak +If set, enables +.Cm brkint , ixon , imaxbel , opost , +.Cm isig , iexten , +and +.Cm Fl icanon . +If unset, same as +.Cm sane . +.It Cm new +Same as +.Cm tty . +.It Cm old +Same as +.Cm tty . +.It Cm newcrt Pq Fl newcrt +Same as +.Cm crt . +.It Cm pass8 +The converse of +.Cm parity . +.It Cm tandem Pq Fl tandem +Same as +.Cm ixoff . +.It Cm decctlq Pq Fl decctlq +The converse of +.Cm ixany . +.It Cm crterase Pq Fl crterase +Same as +.Cm echoe . +.It Cm crtbs Pq Fl crtbs +Same as +.Cm echoe . +.It Cm crtkill Pq Fl crtkill +Same as +.Cm echoke . +.It Cm ctlecho Pq Fl ctlecho +Same as +.Cm echoctl . +.It Cm prterase Pq Fl prterase +Same as +.Cm echoprt . +.It Cm litout Pq Fl litout +The converse of +.Cm opost . +.It Cm tabs Pq Fl tabs +The converse of +.Cm tabs . +.It Cm brk Ar value +Same as the control character +.Cm eol . +.It Cm flush Ar value +Same as the control character +.Cm discard . +.It Cm rprnt Ar value +Same as the control character +.Cm reprint . +.El +.Pp +The +.Nm stty +utility exits with a value of 0 if successful, and >0 if an error occurs. +.Sh SEE ALSO +.Xr termios 4 +.Sh STANDARDS +The +.Nm stty +function is expected to be +.St -p1003.2 +compatible. The flags +.Fl e +and +.Fl f +are +extensions to the standard. diff --git a/bin/stty/stty.c b/bin/stty/stty.c new file mode 100644 index 0000000..4a840c2 --- /dev/null +++ b/bin/stty/stty.c @@ -0,0 +1,161 @@ +/*- + * Copyright (c) 1989, 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 copyright[] = +"@(#) Copyright (c) 1989, 1991, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)stty.c 8.3 (Berkeley) 4/2/94"; +#endif /* not lint */ + +#include <sys/types.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> + +#include "stty.h" +#include "extern.h" + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct info i; + enum FMT fmt; + int ch; + + fmt = NOTSET; + i.fd = STDIN_FILENO; + + opterr = 0; + while (optind < argc && + strspn(argv[optind], "-aefg") == strlen(argv[optind]) && + (ch = getopt(argc, argv, "aef:g")) != EOF) + switch(ch) { + case 'a': /* undocumented: POSIX compatibility */ + fmt = POSIX; + break; + case 'e': + fmt = BSD; + break; + case 'f': + if ((i.fd = open(optarg, O_RDONLY | O_NONBLOCK)) < 0) + err(1, "%s", optarg); + break; + case 'g': + fmt = GFLAG; + break; + case '?': + default: + goto args; + } + +args: argc -= optind; + argv += optind; + + if (ioctl(i.fd, TIOCGETD, &i.ldisc) < 0) + err(1, "TIOCGETD"); + if (tcgetattr(i.fd, &i.t) < 0) + err(1, "tcgetattr"); + if (ioctl(i.fd, TIOCGWINSZ, &i.win) < 0) + warn("TIOCGWINSZ: %s\n", strerror(errno)); + + checkredirect(); /* conversion aid */ + + switch(fmt) { + case NOTSET: + if (*argv) + break; + /* FALLTHROUGH */ + case BSD: + case POSIX: + print(&i.t, &i.win, i.ldisc, fmt); + break; + case GFLAG: + gprint(&i.t, &i.win, i.ldisc); + break; + } + + for (i.set = i.wset = 0; *argv; ++argv) { + if (ksearch(&argv, &i)) + continue; + + if (csearch(&argv, &i)) + continue; + + if (msearch(&argv, &i)) + continue; + + if (isdigit(**argv)) { + int speed; + + speed = atoi(*argv); + cfsetospeed(&i.t, speed); + cfsetispeed(&i.t, speed); + i.set = 1; + continue; + } + + if (!strncmp(*argv, "gfmt1", sizeof("gfmt1") - 1)) { + gread(&i.t, *argv + sizeof("gfmt1") - 1); + continue; + } + + warnx("illegal option -- %s", *argv); + usage(); + } + + if (i.set && tcsetattr(i.fd, 0, &i.t) < 0) + err(1, "tcsetattr"); + if (i.wset && ioctl(i.fd, TIOCSWINSZ, &i.win) < 0) + warn("TIOCSWINSZ"); + exit(0); +} + +void +usage() +{ + + (void)fprintf(stderr, "usage: stty: [-a|-e|-g] [-f file] [options]\n"); + exit (1); +} diff --git a/bin/stty/stty.h b/bin/stty/stty.h new file mode 100644 index 0000000..92cffcc --- /dev/null +++ b/bin/stty/stty.h @@ -0,0 +1,58 @@ +/*- + * 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. + * + * @(#)stty.h 8.1 (Berkeley) 5/31/93 + */ + +#include <sys/ioctl.h> +#include <termios.h> + +struct info { + int fd; /* file descriptor */ + int ldisc; /* line discipline */ + int off; /* turn off */ + int set; /* need set */ + int wset; /* need window set */ + char *arg; /* argument */ + struct termios t; /* terminal info */ + struct winsize win; /* window info */ +}; + +struct cchar { + char *name; + int sub; + u_char def; +}; + +enum FMT { NOTSET, GFLAG, BSD, POSIX }; + +#define LINELENGTH 72 diff --git a/bin/stty/util.c b/bin/stty/util.c new file mode 100644 index 0000000..95bea15 --- /dev/null +++ b/bin/stty/util.c @@ -0,0 +1,64 @@ +/*- + * 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[] = "@(#)util.c 8.3 (Berkeley) 4/2/94"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "stty.h" +#include "extern.h" + +/* + * Gross, but since we're changing the control descriptor from 1 to 0, most + * users will be probably be doing "stty > /dev/sometty" by accident. If 1 + * and 2 are both ttys, but not the same, assume that 1 was incorrectly + * redirected. + */ +void +checkredirect() +{ + struct stat sb1, sb2; + + if (isatty(STDOUT_FILENO) && isatty(STDERR_FILENO) && + !fstat(STDOUT_FILENO, &sb1) && !fstat(STDERR_FILENO, &sb2) && + (sb1.st_rdev != sb2.st_rdev)) +warn("stdout appears redirected, but stdin is the control descriptor"); +} diff --git a/bin/sync/Makefile b/bin/sync/Makefile new file mode 100644 index 0000000..b3f08be --- /dev/null +++ b/bin/sync/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 + +PROG= sync +MAN8= sync.0 + +.include <bsd.prog.mk> diff --git a/bin/sync/sync.8 b/bin/sync/sync.8 new file mode 100644 index 0000000..049007e --- /dev/null +++ b/bin/sync/sync.8 @@ -0,0 +1,73 @@ +.\" 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. +.\" +.\" @(#)sync.8 8.1 (Berkeley) 5/31/93 +.\" +.Dd May 31, 1993 +.Dt SYNC 8 +.Os BSD 4 +.Sh NAME +.Nm sync +.Nd force completion of pending disk writes (flush cache) +.Sh SYNOPSIS +.Nm sync +.Sh DESCRIPTION +.Nm Sync +can be called to insure that all disk writes have been completed before the +processor is halted in a way not suitably done by +.Xr reboot 8 +or +.Xr halt 8 . +Generally, it is preferable to use +.Xr reboot +or +.Xr halt +to shut down the system, +as they may perform additional actions +such as resynchronizing the hardware clock +and flushing internal caches before performing a final +.Nm sync . +.Pp +.Nm Sync +utilizes the +.Xr sync 2 +function call. +.Sh SEE ALSO +.Xr sync 2 , +.Xr fsync 2 , +.Xr halt 8 , +.Xr reboot 8 , +.Xr update 8 +.Sh HISTORY +A +.Nm sync +command appeared in +.At v6 . diff --git a/bin/sync/sync.c b/bin/sync/sync.c new file mode 100644 index 0000000..dd58649 --- /dev/null +++ b/bin/sync/sync.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 1987, 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) 1987, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)sync.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +main() +{ + sync(); + exit(0); +} diff --git a/bin/test/Makefile b/bin/test/Makefile new file mode 100644 index 0000000..ccd6089 --- /dev/null +++ b/bin/test/Makefile @@ -0,0 +1,8 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 + +PROG= test +SRCS= test.c operators.c +LINKS= ${BINDIR}/test ${BINDIR}/[ +MLINKS= test.1 '[.1' + +.include <bsd.prog.mk> diff --git a/bin/test/TEST.csh b/bin/test/TEST.csh new file mode 100644 index 0000000..e5b9652 --- /dev/null +++ b/bin/test/TEST.csh @@ -0,0 +1,137 @@ +# @(#)TEST.csh 5.2 (Berkeley) 4/30/93 + +#alias t '/usr/src/bin/test/obj/test \!*; echo $status' +alias t '/bin/test \!*; echo $status' + +echo 't -b /dev/ttyp2' +t -b /dev/ttyp2 +echo 't -b /dev/jb1a' +t -b /dev/jb1a + +echo 't -c test.c' +t -c test.c +echo 't -c /dev/tty' +t -c /dev/tty + +echo 't -d test.c' +t -d test.c +echo 't -d /etc' +t -d /etc + +echo 't -e noexist' +t -e noexist +echo 't -e test.c' +t -e test.c + +echo 't -f noexist' +t -f noexist +echo 't -f /dev/tty' +t -f /dev/tty +echo 't -f test.c' +t -f test.c + +echo 't -g test.c' +t -g test.c +echo 't -g /bin/ps' +t -g /bin/ps + +echo 't -n ""' +t -n "" +echo 't -n "hello"' +t -n "hello" + +echo 't -p test.c' +t -p test.c + +echo 't -r noexist' +t -r noexist +echo 't -r /etc/master.passwd' +t -r /etc/master.passwd +echo 't -r test.c' +t -r test.c + +echo 't -s noexist' +t -s noexist +echo 't -s /dev/null' +t -s /dev/null +echo 't -s test.c' +t -s test.c + +echo 't -t 20' +t -t 20 +echo 't -t 0' +t -t 0 + +echo 't -u test.c' +t -u test.c +echo 't -u /bin/rcp' +t -u /bin/rcp + +echo 't -w noexist' +t -w noexist +echo 't -w /etc/master.passwd' +t -w /etc/master.passwd +echo 't -w /dev/null' +t -w /dev/null + +echo 't -x noexist' +t -x noexist +echo 't -x /bin/ps' +t -x /bin/ps +echo 't -x /etc/motd' +t -x /etc/motd + +echo 't -z ""' +t -z "" +echo 't -z "foo"' +t -z "foo" + +echo 't "foo"' +t "foo" +echo 't ""' +t "" + +echo 't "hello" = "hello"' +t "hello" = "hello" +echo 't "hello" = "goodbye"' +t "hello" = "goodbye" + +echo 't "hello" != "hello"' +t "hello" != "hello" +echo 't "hello" != "goodbye"' +t "hello" != "goodbye" + +echo 't 200 -eq 200' +t 200 -eq 200 +echo 't 34 -eq 222' +t 34 -eq 222 + +echo 't 200 -ne 200' +t 200 -ne 200 +echo 't 34 -ne 222' +t 34 -ne 222 + +echo 't 200 -gt 200' +t 200 -gt 200 +echo 't 340 -gt 222' +t 340 -gt 222 + +echo 't 200 -ge 200' +t 200 -ge 200 +echo 't 34 -ge 222' +t 34 -ge 222 + +echo 't 200 -lt 200' +t 200 -lt 200 +echo 't 34 -lt 222' +t 34 -lt 222 + +echo 't 200 -le 200' +t 200 -le 200 +echo 't 340 -le 222' +t 340 -le 222 + +echo 't 700 -le 1000 -a -n "1" -a "20" = "20"' +t 700 -le 1000 -a -n "1" -a "20" = "20" +echo 't ! \( 700 -le 1000 -a -n "1" -a "20" = "20" \)' +t ! \( 700 -le 1000 -a -n "1" -a "20" = "20" \) diff --git a/bin/test/operators.c b/bin/test/operators.c new file mode 100644 index 0000000..335223b --- /dev/null +++ b/bin/test/operators.c @@ -0,0 +1,148 @@ +/*- + * Copyright (c) 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[] = "@(#)operators.c 8.3 (Berkeley) 4/2/94"; +#endif /* not lint */ + +/* + * Operators used in the test command. + */ + +#include <stdio.h> + +#include "operators.h" + +const char *const unary_op[] = { + "!", + "-b", + "-c", + "-d", + "-e", + "-f", + "-g", + "-h", + "-k", + "-n", + "-p", + "-r", + "-s", + "-t", + "-u", + "-w", + "-x", + "-z", + NULL +}; + +const char *const binary_op[] = { + "-o", + "|", + "-a", + "&", + "=", + "!=", + "-eq", + "-ne", + "-gt", + "-lt", + "-le", + "-ge", + NULL +}; + +const char op_priority[] = { + 3, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 1, + 1, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, +}; + +const char op_argflag[] = { + 0, + OP_FILE, + OP_FILE, + OP_FILE, + OP_FILE, + OP_FILE, + OP_FILE, + OP_FILE, + OP_FILE, + OP_STRING, + OP_FILE, + OP_FILE, + OP_FILE, + OP_INT, + OP_FILE, + OP_FILE, + OP_FILE, + OP_STRING, + 0, + 0, + 0, + 0, + OP_STRING, + OP_STRING, + OP_INT, + OP_INT, + OP_INT, + OP_INT, + OP_INT, + OP_INT, +}; diff --git a/bin/test/operators.h b/bin/test/operators.h new file mode 100644 index 0000000..f8746df --- /dev/null +++ b/bin/test/operators.h @@ -0,0 +1,77 @@ +/*- + * Copyright (c) 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. + * + * @(#)operators.h 8.3 (Berkeley) 4/2/94 + */ + +#define NOT 0 +#define ISBLOCK 1 +#define ISCHAR 2 +#define ISDIR 3 +#define ISEXIST 4 +#define ISFILE 5 +#define ISSETGID 6 +#define ISSYMLINK 7 +#define ISSTICKY 8 +#define STRLEN 9 +#define ISFIFO 10 +#define ISREAD 11 +#define ISSIZE 12 +#define ISTTY 13 +#define ISSETUID 14 +#define ISWRITE 15 +#define ISEXEC 16 +#define NULSTR 17 + +#define FIRST_BINARY_OP 18 +#define OR1 18 +#define OR2 19 +#define AND1 20 +#define AND2 21 +#define STREQ 22 +#define STRNE 23 +#define EQ 24 +#define NE 25 +#define GT 26 +#define LT 27 +#define LE 28 +#define GE 29 + + +#define OP_INT 1 /* arguments to operator are integer */ +#define OP_STRING 2 /* arguments to operator are string */ +#define OP_FILE 3 /* argument is a file name */ + +extern const char *const unary_op[]; +extern const char *const binary_op[]; +extern const char op_priority[]; +extern const char op_argflag[]; diff --git a/bin/test/test.1 b/bin/test/test.1 new file mode 100644 index 0000000..b4eaf0d --- /dev/null +++ b/bin/test/test.1 @@ -0,0 +1,255 @@ +.\" Copyright (c) 1991, 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. +.\" +.\" @(#)test.1 8.1 (Berkeley) 5/31/93 +.\" +.Dd May 31, 1993 +.Dt TEST 1 +.Os +.Sh NAME +.Nm test +.Nd condition evaluation utility +.Sh SYNOPSIS +.Nm test +.Ar expression +.Sh DESCRIPTION +The +.Nm test +utility evaluates the expression and, if it evaluates +to true, returns a zero (true) exit status; otherwise +it returns 1 (false). +If there is no expression, test also +returns 1 (false). +.Pp +All operators and flags are separate arguments to the +.Nm test +utility. +.Pp +The following primaries are used to construct expression: +.Bl -tag -width Ar +.It Fl b Ar file +True if +.Ar file +exists and is a block special +file. +.It Fl c Ar file +True if +.Ar file +exists and is a character +special file. +.It Fl d Ar file +True if +.Ar file +exists and is a directory. +.It Fl e Ar file +True if +.Ar file +exists (regardless of type). +.It Fl f Ar file +True if +.Ar file +exists and is a regular file. +.It Fl g Ar file +True if +.Ar file +exists and its set group ID flag +is set. +.It Fl h Ar file +True if +.Ar file +exists and is a symbolic link. +.It Fl n Ar string +True if the length of +.Ar string +is nonzero. +.It Fl p Ar file +True if +.Ar file +is a named pipe +.Po Tn FIFO Pc . +.It Fl r Ar file +True if +.Ar file exists and is readable. +.It Fl s Ar file +True if +.Ar file +exists and has a size greater +than zero. +.It Fl t Ar [file_descriptor] +True if the file whose file descriptor number +is +.Ar file_descriptor +(default 1) is open and is +associated with a terminal. +.It Fl u Ar file +True if +.Ar file +exists and its set user ID flag +is set. +.It Fl w Ar file +True if +.Ar file +exists and is writable. +True +indicates only that the write flag is on. +The file is not writable on a read-only file +system even if this test indicates true. +.It Fl x Ar file +True if +.Ar file +exists and is executable. +True +indicates only that the execute flag is on. +If +.Ar file +is a directory, true indicates that +.Ar file +can be searched. +.It Fl z Ar string +True if the length of +.Ar string +is zero. +.It Ar string +True if +.Ar string +is not the null +string. +.It Ar \&s\&1 Cm \&= Ar \&s\&2 +True if the strings +.Ar \&s\&1 +and +.Ar \&s\&2 +are identical. +.It Ar \&s\&1 Cm \&!= Ar \&s\&2 +True if the strings +.Ar \&s\&1 +and +.Ar \&s\&2 +are not identical. +.It Ar \&n\&1 Fl \&eq Ar \&n\&2 +True if the integers +.Ar \&n\&1 +and +.Ar \&n\&2 +are algebraically +equal. +.It Ar \&n\&1 Fl \&ne Ar \&n\&2 +True if the integers +.Ar \&n\&1 +and +.Ar \&n\&2 +are not +algebraically equal. +.It Ar \&n\&1 Fl \> Ar \&n\&2 +True if the integer +.Ar \&n\&1 +is algebraically +greater than the integer +.Ar \&n\&2 . +.It Ar \&n\&1 Fl \&ge Ar \&n\&2 +True if the integer +.Ar \&n\&1 +is algebraically +greater than or equal to the integer +.Ar \&n\&2 . +.It Ar \&n\&1 Fl \< Ar \&n\&2 +True if the integer +.Ar \&n\&1 +is algebraically less +than the integer +.Ar \&n\&2 . +.It Ar \&n\&1 Fl \&le Ar \&n\&2 +True if the integer +.Ar \&n\&1 +is algebraically less +than or equal to the integer +.Ar \&n\&2 . +.El +.Pp +These primaries can be combined with the following operators: +.Bl -tag -width Ar +.It Cm \&! Ar expression +True if +.Ar expression +is false. +.It Ar expression1 Fl a Ar expression2 +True if both +.Ar expression1 +and +.Ar expression2 +are true. +.It Ar expression1 Fl o Ar expression2 +True if either +.Ar expression1 +or +.Ar expression2 +are true. +.It Cm \&( Ns Ar expression Ns Cm \&) +True if expression is true. +.El +.Pp +The +.Fl a +operator has higher precedence than the +.Fl o +operator. +.Sh GRAMMAR AMBIGUITY +The +.Nm test +grammar is inherently ambiguous. In order to assure a degree of consistency, +the cases described in the +.St -p1003.2 , +section D11.2/4.62.4, standard +are evaluated consistently according to the rules specified in the +standards document. All other cases are subject to the ambiguity in the +command semantics. +.Sh RETURN VALUES +The +.Nm test +utility exits with one of the following values: +.Bl -tag -width Ds +.It 0 +expression evaluated to true. +.It 1 +expression evaluated to false or expression was +missing. +.It >1 +An error occurred. +.El +.Sh STANDARDS +The +.Nm test +function is expected to be +.St -p1003.2 +compatible. diff --git a/bin/test/test.c b/bin/test/test.c new file mode 100644 index 0000000..b9a8ebf --- /dev/null +++ b/bin/test/test.c @@ -0,0 +1,560 @@ +/*- + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)test.c 8.3 (Berkeley) 4/2/94"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "operators.h" + +#define STACKSIZE 12 +#define NESTINCR 16 + +/* data types */ +#define STRING 0 +#define INTEGER 1 +#define BOOLEAN 2 + +#define IS_BANG(s) (s[0] == '!' && s[1] == '\0') + +/* + * This structure hold a value. The type keyword specifies the type of + * the value, and the union u holds the value. The value of a boolean + * is stored in u.num (1 = TRUE, 0 = FALSE). + */ +struct value { + int type; + union { + char *string; + long num; + } u; +}; + +struct operator { + short op; /* Which operator. */ + short pri; /* Priority of operator. */ +}; + +struct filestat { + char *name; /* Name of file. */ + int rcode; /* Return code from stat. */ + struct stat stat; /* Status info on file. */ +}; + +static int expr_is_false __P((struct value *)); +static void expr_operator __P((int, struct value *, struct filestat *)); +static void get_int __P((char *, long *)); +static int lookup_op __P((char *, const char *const *)); +static void overflow __P((void)); +static int posix_binary_op __P((char **)); +static int posix_unary_op __P((char **)); +static void syntax __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct operator opstack[STACKSIZE]; + struct operator *opsp; + struct value valstack[STACKSIZE + 1]; + struct value *valsp; + struct filestat fs; + char c, **ap, *opname, *p; + int binary, nest, op, pri, ret_val, skipping; + + if ((p = argv[0]) == NULL) + errx(2, "test: argc is zero"); + + if (*p != '\0' && p[strlen(p) - 1] == '[') { + if (strcmp(argv[--argc], "]")) + errx(2, "missing ]"); + argv[argc] = NULL; + } + ap = argv + 1; + fs.name = NULL; + + /* + * Test(1) implements an inherently ambiguous grammer. In order to + * assure some degree of consistency, we special case the POSIX 1003.2 + * requirements to assure correct evaluation for POSIX scripts. The + * following special cases comply with POSIX P1003.2/D11.2 Section + * 4.62.4. + */ + switch(argc - 1) { + case 0: /* % test */ + return (1); + break; + case 1: /* % test arg */ + return (argv[1] == NULL || *argv[1] == '\0') ? 1 : 0; + break; + case 2: /* % test op arg */ + opname = argv[1]; + if (IS_BANG(opname)) + return (*argv[2] == '\0') ? 0 : 1; + else { + ret_val = posix_unary_op(&argv[1]); + if (ret_val >= 0) + return (ret_val); + } + break; + case 3: /* % test arg1 op arg2 */ + if (IS_BANG(argv[1])) { + ret_val = posix_unary_op(&argv[1]); + if (ret_val >= 0) + return (!ret_val); + } else { + ret_val = posix_binary_op(&argv[1]); + if (ret_val >= 0) + return (ret_val); + } + break; + case 4: /* % test ! arg1 op arg2 */ + if (IS_BANG(argv[1])) { + ret_val = posix_binary_op(&argv[2]); + if (ret_val >= 0) + return (!ret_val); + } + break; + default: + break; + } + + /* + * We use operator precedence parsing, evaluating the expression as + * we parse it. Parentheses are handled by bumping up the priority + * of operators using the variable "nest." We use the variable + * "skipping" to turn off evaluation temporarily for the short + * circuit boolean operators. (It is important do the short circuit + * evaluation because under NFS a stat operation can take infinitely + * long.) + */ + opsp = opstack + STACKSIZE; + valsp = valstack; + nest = skipping = 0; + if (*ap == NULL) { + valstack[0].type = BOOLEAN; + valstack[0].u.num = 0; + goto done; + } + for (;;) { + opname = *ap++; + if (opname == NULL) + syntax(); + if (opname[0] == '(' && opname[1] == '\0') { + nest += NESTINCR; + continue; + } else if (*ap && (op = lookup_op(opname, unary_op)) >= 0) { + if (opsp == &opstack[0]) + overflow(); + --opsp; + opsp->op = op; + opsp->pri = op_priority[op] + nest; + continue; + } else { + valsp->type = STRING; + valsp->u.string = opname; + valsp++; + } + for (;;) { + opname = *ap++; + if (opname == NULL) { + if (nest != 0) + syntax(); + pri = 0; + break; + } + if (opname[0] != ')' || opname[1] != '\0') { + if ((op = lookup_op(opname, binary_op)) < 0) + syntax(); + op += FIRST_BINARY_OP; + pri = op_priority[op] + nest; + break; + } + if ((nest -= NESTINCR) < 0) + syntax(); + } + while (opsp < &opstack[STACKSIZE] && opsp->pri >= pri) { + binary = opsp->op; + for (;;) { + valsp--; + c = op_argflag[opsp->op]; + if (c == OP_INT) { + if (valsp->type == STRING) + get_int(valsp->u.string, + &valsp->u.num); + valsp->type = INTEGER; + } else if (c >= OP_STRING) { + /* OP_STRING or OP_FILE */ + if (valsp->type == INTEGER) { + if ((p = malloc(32)) == NULL) + err(2, NULL); +#ifdef SHELL + fmtstr(p, 32, "%d", + valsp->u.num); +#else + (void)sprintf(p, + "%d", valsp->u.num); +#endif + valsp->u.string = p; + } else if (valsp->type == BOOLEAN) { + if (valsp->u.num) + valsp->u.string = + "true"; + else + valsp->u.string = ""; + } + valsp->type = STRING; + if (c == OP_FILE && (fs.name == NULL || + strcmp(fs.name, valsp->u.string))) { + fs.name = valsp->u.string; + fs.rcode = + stat(valsp->u.string, + &fs.stat); + } + } + if (binary < FIRST_BINARY_OP) + break; + binary = 0; + } + if (!skipping) + expr_operator(opsp->op, valsp, &fs); + else if (opsp->op == AND1 || opsp->op == OR1) + skipping--; + valsp++; /* push value */ + opsp++; /* pop operator */ + } + if (opname == NULL) + break; + if (opsp == &opstack[0]) + overflow(); + if (op == AND1 || op == AND2) { + op = AND1; + if (skipping || expr_is_false(valsp - 1)) + skipping++; + } + if (op == OR1 || op == OR2) { + op = OR1; + if (skipping || !expr_is_false(valsp - 1)) + skipping++; + } + opsp--; + opsp->op = op; + opsp->pri = pri; + } +done: return (expr_is_false(&valstack[0])); +} + +static int +expr_is_false(val) + struct value *val; +{ + + if (val->type == STRING) { + if (val->u.string[0] == '\0') + return (1); + } else { /* INTEGER or BOOLEAN */ + if (val->u.num == 0) + return (1); + } + return (0); +} + + +/* + * Execute an operator. Op is the operator. Sp is the stack pointer; + * sp[0] refers to the first operand, sp[1] refers to the second operand + * (if any), and the result is placed in sp[0]. The operands are converted + * to the type expected by the operator before expr_operator is called. + * Fs is a pointer to a structure which holds the value of the last call + * to stat, to avoid repeated stat calls on the same file. + */ +static void +expr_operator(op, sp, fs) + int op; + struct value *sp; + struct filestat *fs; +{ + int i; + + switch (op) { + case NOT: + sp->u.num = expr_is_false(sp); + sp->type = BOOLEAN; + break; + case ISEXIST: + if (fs == NULL || fs->rcode == -1) + goto false; + else + goto true; + case ISREAD: + i = S_IROTH; + goto permission; + case ISWRITE: + i = S_IWOTH; + goto permission; + case ISEXEC: + i = S_IXOTH; +permission: if (fs->stat.st_uid == geteuid()) + i <<= 6; + else if (fs->stat.st_gid == getegid()) + i <<= 3; + goto filebit; /* true if (stat.st_mode & i) != 0 */ + case ISFILE: + i = S_IFREG; + goto filetype; + case ISDIR: + i = S_IFDIR; + goto filetype; + case ISCHAR: + i = S_IFCHR; + goto filetype; + case ISBLOCK: + i = S_IFBLK; + goto filetype; + case ISSYMLINK: + i = S_IFLNK; + (void)lstat(sp->u.string, &fs->stat); + goto filetype; + case ISFIFO: + i = S_IFIFO; + goto filetype; +filetype: if ((fs->stat.st_mode & S_IFMT) == i && fs->rcode >= 0) +true: sp->u.num = 1; + else +false: sp->u.num = 0; + sp->type = BOOLEAN; + break; + case ISSETUID: + i = S_ISUID; + goto filebit; + case ISSETGID: + i = S_ISGID; + goto filebit; + case ISSTICKY: + i = S_ISVTX; +filebit: if (fs->stat.st_mode & i && fs->rcode >= 0) + goto true; + goto false; + case ISSIZE: + sp->u.num = fs->rcode >= 0 ? fs->stat.st_size : 0L; + sp->type = INTEGER; + break; + case ISTTY: + sp->u.num = isatty(sp->u.num); + sp->type = BOOLEAN; + break; + case NULSTR: + if (sp->u.string[0] == '\0') + goto true; + goto false; + case STRLEN: + sp->u.num = strlen(sp->u.string); + sp->type = INTEGER; + break; + case OR1: + case AND1: + /* + * These operators are mostly handled by the parser. If we + * get here it means that both operands were evaluated, so + * the value is the value of the second operand. + */ + *sp = *(sp + 1); + break; + case STREQ: + case STRNE: + i = 0; + if (!strcmp(sp->u.string, (sp + 1)->u.string)) + i++; + if (op == STRNE) + i = 1 - i; + sp->u.num = i; + sp->type = BOOLEAN; + break; + case EQ: + if (sp->u.num == (sp + 1)->u.num) + goto true; + goto false; + case NE: + if (sp->u.num != (sp + 1)->u.num) + goto true; + goto false; + case GT: + if (sp->u.num > (sp + 1)->u.num) + goto true; + goto false; + case LT: + if (sp->u.num < (sp + 1)->u.num) + goto true; + goto false; + case LE: + if (sp->u.num <= (sp + 1)->u.num) + goto true; + goto false; + case GE: + if (sp->u.num >= (sp + 1)->u.num) + goto true; + goto false; + + } +} + +static int +lookup_op(name, table) + char *name; + const char *const * table; +{ + const char *const * tp; + const char *p; + char c; + + c = name[1]; + for (tp = table; (p = *tp) != NULL; tp++) + if (p[1] == c && !strcmp(p, name)) + return (tp - table); + return (-1); +} + +static int +posix_unary_op(argv) + char **argv; +{ + struct filestat fs; + struct value valp; + int op, c; + char *opname; + + opname = *argv; + if ((op = lookup_op(opname, unary_op)) < 0) + return (-1); + c = op_argflag[op]; + opname = argv[1]; + valp.u.string = opname; + if (c == OP_FILE) { + fs.name = opname; + fs.rcode = stat(opname, &fs.stat); + } else if (c != OP_STRING) + return (-1); + + expr_operator(op, &valp, &fs); + return (valp.u.num == 0); +} + +static int +posix_binary_op(argv) + char **argv; +{ + struct value v[2]; + int op, c; + char *opname; + + opname = argv[1]; + if ((op = lookup_op(opname, binary_op)) < 0) + return (-1); + op += FIRST_BINARY_OP; + c = op_argflag[op]; + + if (c == OP_INT) { + get_int(argv[0], &v[0].u.num); + get_int(argv[2], &v[1].u.num); + } else { + v[0].u.string = argv[0]; + v[1].u.string = argv[2]; + } + expr_operator(op, v, NULL); + return (v[0].u.num == 0); +} + +/* + * Integer type checking. + */ +static void +get_int(v, lp) + char *v; + long *lp; +{ + long val; + char *ep; + + for (; *v && isspace(*v); ++v); + if (isdigit(*v)) { + errno = 0; + val = strtol(v, &ep, 10); + if (*ep != '\0') + errx(2, "%s: trailing non-numeric characters", v); + if (errno == ERANGE) { + if (val == LONG_MIN) + errx(2, "%s: underflow", v); + if (val == LONG_MAX) + errx(2, "%s: overflow", v); + } + *lp = val; + return; + } + errx(2, "%s: expected integer", v); +} + +static void +syntax() +{ + + err(2, "syntax error"); +} + +static void +overflow() +{ + + err(2, "expression is too complex"); +} |