diff options
Diffstat (limited to 'bin')
420 files changed, 73028 insertions, 0 deletions
diff --git a/bin/Makefile b/bin/Makefile new file mode 100644 index 0000000..a5e0c6f --- /dev/null +++ b/bin/Makefile @@ -0,0 +1,41 @@ +# From: @(#)Makefile 8.1 (Berkeley) 5/31/93 +# $FreeBSD$ + +SUBDIR= cat \ + chio \ + chmod \ + cp \ + date \ + dd \ + df \ + domainname \ + echo \ + ed \ + expr \ + hostname \ + kill \ + ln \ + ls \ + mkdir \ + mv \ + pax \ + ps \ + pwd \ + rcp \ + rm \ + rmdir \ + sh \ + sleep \ + stty \ + sync \ + test + +.if !defined(NO_TCSH) +SUBDIR+=csh +.endif + +.if !defined(NO_SENDMAIL) +SUBDIR+=rmail +.endif + +.include <bsd.subdir.mk> diff --git a/bin/Makefile.inc b/bin/Makefile.inc new file mode 100644 index 0000000..d29f43a --- /dev/null +++ b/bin/Makefile.inc @@ -0,0 +1,16 @@ +# @(#)Makefile.inc 8.1 (Berkeley) 5/31/93 +# $FreeBSD$ + +BINDIR?= /bin +CFLAGS+= -Wall -Wformat +NOSHARED?= YES + +.if exists (${.CURDIR}/../../secure) + +.if exists(${.CURDIR}/../../secure/lib/libcipher/obj) +CIPHEROBJDIR= ${.CURDIR}/../../secure/lib/libcipher/obj +.else +CIPHEROBJDIR= ${.CURDIR}/../../secure/lib/libcipher +.endif + +.endif diff --git a/bin/cat/Makefile b/bin/cat/Makefile new file mode 100644 index 0000000..672a4ee --- /dev/null +++ b/bin/cat/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 +# $FreeBSD$ + +PROG= cat + +.include <bsd.prog.mk> diff --git a/bin/cat/cat.1 b/bin/cat/cat.1 new file mode 100644 index 0000000..f0e6b09 --- /dev/null +++ b/bin/cat/cat.1 @@ -0,0 +1,190 @@ +.\" 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.3 (Berkeley) 5/2/95 +.\" $FreeBSD$ +.\" +.Dd May 2, 1995 +.Dt CAT 1 +.Os +.Sh NAME +.Nm cat +.Nd concatenate and print files +.Sh SYNOPSIS +.Nm cat +.Op Fl benstuv +.Op Ar +.Sh DESCRIPTION +The +.Nm +utility reads files sequentially, writing them to the standard output. +The +.Ar file +operands are processed in command-line order. +If +.Ar file +is a single dash +.Pq Sq \&- +or absent, +.Nm +reads from the standard input. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl b +Number the non-blank output lines, starting at 1. +.It Fl e +Display non-printing characters (see the +.Fl v +option), and display a dollar sign +.Pq Ql \&$ +at the end of each line. +.It Fl n +Number the output lines, starting at 1. +.It Fl s +Squeeze multiple adjacent empty lines, causing the output to be +single spaced. +.It Fl t +Display non-printing characters (see the +.Fl v +option), and display tab characters as +.Ql ^I . +.It Fl u +The +.Fl u +option guarantees that the output is unbuffered. +.It Fl v +Display non-printing characters so they are visible. +Control characters print as +.Ql ^X +for control-X; the delete +character (octal 0177) prints as +.Ql ^? +.Pf Non- Tn ASCII +characters (with the high bit set) are printed as +.Ql M- +(for meta) followed by the character for the low 7 bits. +.El +.Sh DIAGNOSTICS +The +.Nm +utility exits 0 on success or >0 if an error occurred. +.Sh EXAMPLES +The command: +.Bd -literal -offset indent +.Ic cat file1 +.Ed +.Pp +will print the contents of +.Ar file1 +to the standard output. +.Pp +The command: +.Bd -literal -offset indent +.Ic cat file1 file2 > file3 +.Ed +.Pp +will sequentially print the contents of +.Ar file1 +and +.Ar file2 +to the file +.Ar file3 , +truncating +.Ar file3 +if it already exists. +See the manual page for your shell (i.e., +.Xr sh 1 ) +for more information on redirection. +.Pp +The command: +.Bd -literal -offset indent +.Ic cat file1 - file2 - file3 +.Ed +.Pp +will print the contents of +.Ar file1 , +print data it receives from the standard input until it receives an +.Dv EOF +.Pq Sq ^D +character, print the contents of +.Ar file2 , +read and output contents of the standard input again, then finally output +the contents of +.Ar file3 . +Note that if the standard input referred to a file, the second dash +on the command-line would have no effect, since the entire contents of the file +would have already been read and printed by +.Nm +when it encountered the first +.Ql \&- +operand. +.Sh SEE ALSO +.Xr head 1 , +.Xr more 1 , +.Xr pr 1 , +.Xr sh 1 , +.Xr tail 1 , +.Xr vis 1 , +.Xr setbuf 3 +.Rs +.%A Rob Pike +.%T "UNIX Style, or cat -v Considered Harmful" +.%J "USENIX Summer Conference Proceedings" +.%D 1983 +.Re +.Sh STANDARDS +The +.Nm +utility is compliant with the +.St -p1003.2-92 +specification. +.Pp +The flags +.Op Fl benstv +are extensions to the specification. +.Sh HISTORY +A +.Nm +utility appeared in +.At v1 . +.An Dennis Ritchie +designed and wrote the first man page. +It appears to have been +.Xr cat 1 . +.Sh BUGS +Because of the shell language mechanism used to perform output +redirection, the command +.Dq Li cat file1 file2 > file1 +will cause the original data in file1 to be destroyed! diff --git a/bin/cat/cat.c b/bin/cat/cat.c new file mode 100644 index 0000000..c2f11dd --- /dev/null +++ b/bin/cat/cat.c @@ -0,0 +1,262 @@ +/* + * 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 const copyright[] = +"@(#) Copyright (c) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)cat.c 8.2 (Berkeley) 4/27/95"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <err.h> +#include <fcntl.h> +#include <locale.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +int bflag, eflag, nflag, sflag, tflag, vflag; +int rval; +const char *filename; + +void cook_args __P((char *argv[])); +void cook_buf __P((FILE *)); +int main __P((int argc, char *argv[])); +void raw_args __P((char *argv[])); +void raw_cat __P((int)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch; + + setlocale(LC_CTYPE, ""); + + while ((ch = getopt(argc, argv, "benstuv")) != -1) + 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, NULL); + break; + case 'v': + vflag = 1; + break; + default: + (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); + rval = 1; + ++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) && !isprint(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); + rval = 1; + 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); + rval = 1; + ++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 off, wfd; + ssize_t nr, nw; + static size_t 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(bsize)) == NULL) + err(1, "buffer"); + } + while ((nr = read(rfd, buf, bsize)) > 0) + for (off = 0; nr; nr -= nw, off += nw) + if ((nw = write(wfd, buf + off, (size_t)nr)) < 0) + err(1, "stdout"); + if (nr < 0) { + warn("%s", filename); + rval = 1; + } +} diff --git a/bin/chflags/Makefile b/bin/chflags/Makefile new file mode 100644 index 0000000..ebc9c0f --- /dev/null +++ b/bin/chflags/Makefile @@ -0,0 +1,10 @@ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 +# $FreeBSD$ + +NOSHARED?=yes + +PROG= chflags +CFLAGS+=-Wall +SRCS= chflags.c + +.include <bsd.prog.mk> diff --git a/bin/chflags/chflags.1 b/bin/chflags/chflags.1 new file mode 100644 index 0000000..4f3f708 --- /dev/null +++ b/bin/chflags/chflags.1 @@ -0,0 +1,139 @@ +.\" 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. +.\" +.\" @(#)chflags.1 8.4 (Berkeley) 5/2/95 +.\" $FreeBSD$ +.\" +.Dd May 2, 1995 +.Dt CHFLAGS 1 +.Os +.Sh NAME +.Nm chflags +.Nd change file flags +.Sh SYNOPSIS +.Nm chflags +.Oo +.Fl R +.Op Fl H | Fl L | Fl P +.Oc +.Ar flags +.Ar +.Sh DESCRIPTION +The +.Nm +utility modifies the file flags of the listed files +as specified by the +.Ar flags +operand. +.Pp +The options are as follows: +.Bl -tag -width indent +.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 file flags for the file hierarchies rooted +in the files instead of just the files themselves. +.El +.Pp +The flags are specified as an octal number or a comma separated list +of keywords. +The following keywords are currently defined: +.Bd -literal -offset indent compact +arch set the archived flag (super-user only) +opaque set the opaque flag (owner or super-user only) +nodump set the nodump flag (owner or super-user only) +sappnd set the system append-only flag (super-user only) +schg set the system immutable flag (super-user only) +sunlnk set the system undeletable flag (super-user only) +uappnd set the user append-only flag (owner or super-user only) +uchg set the user immutable flag (owner or super-user only) +uunlnk set the user undeletable flag (owner or super-user only) +archived, sappend, schange, simmutable, uappend, uchange, uimmutable, +sunlink, uunlink + aliases for the above +.Ed +.Pp +Putting the letters +.Dq no +before an option causes the flag to be turned off. +For example: +.Bd -literal -offset indent compact +nouchg the immutable bit should be cleared +.Ed +.Pp +Symbolic links do not have flags, so unless the +.Fl H +or +.Fl L +option is set, +.Nm +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 +You can use "ls -lo" to see the flags of existing files. +.Sh DIAGNOSTICS +The +.Nm +utility exits 0 on success, and >0 if an error occurs. +.Sh SEE ALSO +.Xr ls 1 , +.Xr chflags 2 , +.Xr stat 2 , +.Xr fts 3 , +.Xr symlink 7 +.Sh HISTORY +The +.Nm +command first appeared in +.Bx 4.4 . diff --git a/bin/chflags/chflags.c b/bin/chflags/chflags.c new file mode 100644 index 0000000..3ff36ef --- /dev/null +++ b/bin/chflags/chflags.c @@ -0,0 +1,183 @@ +/* + * 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 const char copyright[] = +"@(#) Copyright (c) 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)chflags.c 8.5 (Berkeley) 4/1/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#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; + u_long clear, set; + long val; + int Hflag, Lflag, Pflag, Rflag, ch, fts_options, oct, rval; + char *flags, *ep; + + Hflag = Lflag = Pflag = Rflag = 0; + while ((ch = getopt(argc, argv, "HLPR")) != -1) + 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 '?': + default: + usage(); + } + argv += optind; + argc -= optind; + + if (argc < 2) + usage(); + + fts_options = FTS_PHYSICAL; + if (Rflag) { + if (Hflag) + fts_options |= FTS_COMFOLLOW; + if (Lflag) { + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + } + } + + flags = *argv; + if (*flags >= '0' && *flags <= '7') { + errno = 0; + val = strtol(flags, &ep, 8); + if (val < 0) + errno = ERANGE; + if (errno) + err(1, "invalid flags: %s", flags); + if (*ep) + errx(1, "invalid flags: %s", flags); + set = val; + oct = 1; + } else { + if (strtofflags(&flags, &set, &clear)) + errx(1, "invalid flag: %s", flags); + clear = ~clear; + 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, chflag, 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 (oct) { + if (!chflags(p->fts_accpath, set)) + continue; + } else { + p->fts_statp->st_flags |= set; + p->fts_statp->st_flags &= clear; + if (!chflags(p->fts_accpath, p->fts_statp->st_flags)) + continue; + } + warn("%s", p->fts_path); + rval = 1; + } + if (errno) + err(1, "fts_read"); + exit(rval); +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: chflags [-R [-H | -L | -P]] flags file ...\n"); + exit(1); +} diff --git a/bin/chio/Makefile b/bin/chio/Makefile new file mode 100644 index 0000000..d4b7f56 --- /dev/null +++ b/bin/chio/Makefile @@ -0,0 +1,9 @@ +# $FreeBSD$ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 + +CFLAGS+=-I${.CURDIR}/../../sys + +PROG= chio +SRCS= chio.c + +.include <bsd.prog.mk> diff --git a/bin/chio/chio.1 b/bin/chio/chio.1 new file mode 100644 index 0000000..2e32b96 --- /dev/null +++ b/bin/chio/chio.1 @@ -0,0 +1,294 @@ +.\" $NetBSD: chio.1,v 1.4 1997/10/02 00:41:25 hubertf Exp $ +.\" +.\" Copyright (c) 1996 Jason R. Thorpe <thorpej@and.com> +.\" 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 acknowledgements: +.\" This product includes software developed by Jason R. Thorpe +.\" for And Communications, http://www.and.com/ +.\" 4. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +.\" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd May 14, 1998 +.Dt CHIO 1 +.Os +.Sh NAME +.Nm chio +.Nd medium changer control utility +.Sh SYNOPSIS +.Nm +.Op Fl f Ar changer +.Ar command +.Op Fl <flags> +.Ar arg1 +.Ar arg2 +.Oo +.Ar arg3 Oo ... +.Oc +.Oc +.Sh DESCRIPTION +.Nm +is used to control the operation of medium changers, such as those found +in tape and optical disk jukeboxes. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl f Ar changer +Use the device +.Ar changer +rather than the default device +.Pa /dev/ch0 . +.El +.Pp +The default changer may be overridden by setting the environment variable +.Ev CHANGER +to the desired changer device. +.Pp +A medium changer apparatus is made up of +.Em elements . +There are five element types: +.Em picker +(medium transport), +.Em slot +(storage), +.Em portal +(import/export), +.Em drive +(data transfer), and +.Em voltag +(select by volume identifier). The +.Em voltag +pseudo-element type allows the selection of tapes by their volume tag +(typically a barcode on the tape). +.Pp +In this command description, the shorthand +.Em ET +will be used to represent an element type, and +.Em EU +will be used to represent an element unit. +For example, to represent the first robotic arm in the changer, the +.Em ET +would be +.Dq picker +and the +.Em EU +would be +.Dq 0 . +.Pp +.Sh SUPPORTED COMMANDS +.Bl -tag -width indent +.It Ic move Xo +.Ar <from ET> <from EU> <to ET> <to EU> +.Op Cm inv +.Xc +Move the media unit from +.Ar <from ET/EU> +to +.Ar <to ET/EU> . +If the optional modifier +.Cm inv +is specified, the media unit will be inverted before insertion. +.It Ic exchange Xo +.Ar <src ET> <src EU> <dst1 ET> <dst1 EU> +.Op Ar <dst2 ET> <dst2 ET> +.Op Cm inv1 +.Op Cm inv2 +.Xc +Perform a media unit exchange operation. The media unit in +.Ar <src ET/EU> +is moved to +.Ar <dst1 ET/EU> +and the media unit previously in +.Ar <dst1 ET/EU> +is moved to +.Ar <dst2 ET/EU> . +In the case of a simple exchange, +.Ar <dst2 ET/EU> +is omitted and the values +.Ar <src ET/EU> +are used in their place. +The optional modifiers +.Cm inv1 +and +.Cm inv2 +specify whether the media units are to be inverted before insertion into +.Ar <dst1 ET/EU> +and +.Ar <dst2 ET/EU> +respectively. +.Pp +Note that not all medium changers support the +.Ic exchange +operation; the changer must have multiple free pickers or emulate +multiple free pickers with transient storage. +.It Ic return Xo +.Ar <from ET> <from EU> +.Xc +Return the media unit to its source element. +This command will query the status of the specified media unit, and +will move it to the element specified in its source attribute. +This is a convenient way to return media from a drive or portal +to its previous element in the changer. +.Pp +.It Ic position Xo +.Ar <to ET> <to EU> +.Op Cm inv +.Xc +Position the picker in front of the element described by +.Ar <to ET/EU> . +If the optional modifier +.Cm inv +is specified, the media unit will be inverted before insertion. +.Pp +Note that not all changers behave as expected when issued this command. +.It Ic params +Report the number of slots, drives, pickers, and portals in the changer, +and which picker unit the changer is currently configured to use. +.It Ic getpicker +Report which picker unit the changer is currently configured to use. +.It Ic setpicker Xo +.Ar <unit> +.Xc +Configure the changer to use picker +.Ar <unit> . +.Pp +.It Ic ielem Xo +.Op Ar <timeout> +.Xc +Perform an +.Em INITIALIZE ELEMENT STATUS +operation on the changer. The optional +.Ar <timeout> +parameter may be given to specify a timeout in seconds for the +operations. This may be used if the operation takes unusually long +because of buggy firmware or the like. +.It Ic voltag Xo +.Op Fl fca +.Ar <ET> +.Ar <EU> +.Op Ar <label> +.Op Ar <serial> +.Xc +Change volume tag for an element in the media changer. This command +is only supported by few media changers. If it is not supported by a +device, using this command will usually result in a "Invalid Field in +CDB" error message on the console. +.Pp +If the +.Fl c +flag is specified, the volume tag of the specified element is +cleared. If the +.Fl f +flag is specified, the volume tag is superseded with the specified +volume tag even if a volume tag is already defined for the element. +It is an error to not specify the +.Fl f +flag when trying to set a label for an element which already has +volume tag information defined. +.Pp +The command works with the primary volume tag or, if the +.Fl a +flag is given, with the alternate volume tag. +.It Ic status Xo +.Op Fl vVsSbIa +.Op Ar <type> +.Xc +Report the status of all elements in the changer. If +.Ar <type> +is specified, report the status of all elements of type +.Ar <type> . +.It Fl v +Print the primary volume tag for each loaded medium, if any. The volume +tag is printed as +.Dq <LABEL:SERIAL> . +.It Fl V +Print the alternate volume tag for each loaded medium, if any. +.It Fl s +Print the additional sense code and additional sense code qualifier for +each element. +.It Fl S +Print the element source address for each element. +.It Fl b +Print SCSI bus information for each element. Note that this information +is valid only for drives. +.It Fl I +Print the internal element addresses for each element. The internal +element address is not normally used with this driver. It is reported +for diagnostic purposes only. +.It Fl a +Print all additional information (as in +.Fl vVsSba +). +.El +.Pp +The status bits are defined as follows: +.Bl -tag -width indent +.It FULL +Element contains a media unit. +.It IMPEXP +Media was deposited into element by an outside human operator. +.It EXCEPT +Element is in an abnormal state. +.It ACCESS +Media in this element is accessible by a picker. +.It EXENAB +Element supports passing media (exporting) to an outside human operator. +.It INENAB +Element supports receiving media (importing) from an outside human operator. +.El +.Pp +.Sh EXAMPLES +.Bl -tag -width indent +.It Li chio move slot 3 drive 0 +Move the media in slot 3 (fourth slot) to drive 0 (first drive). +.It Li chio move voltag VOLUME01 drive 0 +Move the media with the barcode VOLUME01 to drive 0 (first drive). +.It Li chio return drive 0 +Remove the tape from drive 0 (first drive) and return it to its original +location in the rack. +.It Li chio setpicker 2 +Configure the changer to use picker 2 (third picker) for operations. +.El +.Sh FILES +.Bl -tag -width /dev/ch0 -compact +.It Pa /dev/ch0 +default changer device +.El +.Sh SEE ALSO +.Xr mt 1 , +.Xr mount 8 . +.Sh AUTHORS +The +.Nm +program and SCSI changer driver were written by +.An Jason R. Thorpe Aq thorpej@and.com +for And Communications, http://www.and.com/. +.br +Additional work by +.An Hans Huebner +.Aq hans@artcom.de +and Steve Gunn +.Aq csg@waterspout.com diff --git a/bin/chio/chio.c b/bin/chio/chio.c new file mode 100644 index 0000000..31b69d4 --- /dev/null +++ b/bin/chio/chio.c @@ -0,0 +1,1216 @@ +/* $NetBSD: chio.c,v 1.6 1998/01/04 23:53:58 thorpej Exp $ */ +/* + * Copyright (c) 1996 Jason R. Thorpe <thorpej@and.com> + * 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 acknowledgements: + * This product includes software developed by Jason R. Thorpe + * for And Communications, http://www.and.com/ + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * Additional Copyright (c) 1997, by Matthew Jacob, for NASA/Ames Research Ctr. + * Addidional Copyright (c) 2000, by C. Stephen Gunn, Waterspout Communications + */ + +#ifndef lint +static const char copyright[] = + "@(#) Copyright (c) 1996 Jason R. Thorpe. All rights reserved."; +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/chio.h> +#include <err.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "defs.h" +#include "pathnames.h" + +extern char *__progname; /* from crt0.o */ + +static void usage __P((void)); +static void cleanup __P((void)); +static int parse_element_type __P((char *)); +static int parse_element_unit __P((char *)); +static const char * element_type_name __P((int et)); +static int parse_special __P((char *)); +static int is_special __P((char *)); +static const char *bits_to_string __P((int, const char *)); + +static void find_element __P((char *, u_int16_t *, u_int16_t *)); +static struct changer_element_status *get_element_status __P((u_int16_t, u_int16_t)); + +static int do_move __P((char *, int, char **)); +static int do_exchange __P((char *, int, char **)); +static int do_position __P((char *, int, char **)); +static int do_params __P((char *, int, char **)); +static int do_getpicker __P((char *, int, char **)); +static int do_setpicker __P((char *, int, char **)); +static int do_status __P((char *, int, char **)); +static int do_ielem __P((char *, int, char **)); +static int do_return __P((char *, int, char **)); +static int do_voltag __P((char *, int, char **)); + +#ifndef CHET_VT +#define CHET_VT 10 /* Completely Arbitrary */ +#endif + +/* Valid changer element types. */ +const struct element_type elements[] = { + { "drive", CHET_DT }, + { "picker", CHET_MT }, + { "portal", CHET_IE }, + { "slot", CHET_ST }, + { "voltag", CHET_VT }, /* Select tapes by barcode */ + { NULL, 0 }, +}; + +/* Valid commands. */ +const struct changer_command commands[] = { + { "exchange", do_exchange }, + { "getpicker", do_getpicker }, + { "ielem", do_ielem }, + { "move", do_move }, + { "params", do_params }, + { "position", do_position }, + { "setpicker", do_setpicker }, + { "status", do_status }, + { "return", do_return }, + { "voltag", do_voltag }, + { NULL, 0 }, +}; + +/* Valid special words. */ +const struct special_word specials[] = { + { "inv", SW_INVERT }, + { "inv1", SW_INVERT1 }, + { "inv2", SW_INVERT2 }, + { NULL, 0 }, +}; + +static int changer_fd; +static const char *changer_name; + +int +main(argc, argv) + int argc; + char **argv; +{ + int ch, i; + + while ((ch = getopt(argc, argv, "f:")) != -1) { + switch (ch) { + case 'f': + changer_name = optarg; + break; + + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc == 0) + usage(); + + /* Get the default changer if not already specified. */ + if (changer_name == NULL) + if ((changer_name = getenv(CHANGER_ENV_VAR)) == NULL) + changer_name = _PATH_CH; + + /* Open the changer device. */ + if ((changer_fd = open(changer_name, O_RDWR, 0600)) == -1) + err(1, "%s: open", changer_name); + + /* Register cleanup function. */ + if (atexit(cleanup)) + err(1, "can't register cleanup function"); + + /* Find the specified command. */ + for (i = 0; commands[i].cc_name != NULL; ++i) + if (strcmp(*argv, commands[i].cc_name) == 0) + break; + if (commands[i].cc_name == NULL) { + /* look for abbreviation */ + for (i = 0; commands[i].cc_name != NULL; ++i) + if (strncmp(*argv, commands[i].cc_name, + strlen(*argv)) == 0) + break; + } + + if (commands[i].cc_name == NULL) + errx(1, "unknown command: %s", *argv); + + exit ((*commands[i].cc_handler)(commands[i].cc_name, argc, argv)); + /* NOTREACHED */ +} + +static int +do_move(cname, argc, argv) + char *cname; + int argc; + char **argv; +{ + struct changer_move cmd; + int val; + + /* + * On a move command, we expect the following: + * + * <from ET> <from EU> <to ET> <to EU> [inv] + * + * where ET == element type and EU == element unit. + */ + + ++argv; --argc; + + if (argc < 4) { + warnx("%s: too few arguments", cname); + goto usage; + } else if (argc > 5) { + warnx("%s: too many arguments", cname); + goto usage; + } + (void) memset(&cmd, 0, sizeof(cmd)); + + /* <from ET> */ + cmd.cm_fromtype = parse_element_type(*argv); + ++argv; --argc; + + /* Check for voltag virtual type */ + if (CHET_VT == cmd.cm_fromtype) { + find_element(*argv, &cmd.cm_fromtype, &cmd.cm_fromunit); + } else { + /* <from EU> */ + cmd.cm_fromunit = parse_element_unit(*argv); + } + ++argv; --argc; + + /* <to ET> */ + cmd.cm_totype = parse_element_type(*argv); + ++argv; --argc; + + /* Check for voltag virtual type, and report error */ + if (CHET_VT == cmd.cm_totype) + errx(1,"%s: voltag only makes sense as an element source", + cname); + + /* <to EU> */ + cmd.cm_tounit = parse_element_unit(*argv); + ++argv; --argc; + + /* Deal with optional command modifier. */ + if (argc) { + val = parse_special(*argv); + switch (val) { + case SW_INVERT: + cmd.cm_flags |= CM_INVERT; + break; + + default: + errx(1, "%s: inappropriate modifier `%s'", + cname, *argv); + /* NOTREACHED */ + } + } + + /* Send command to changer. */ + if (ioctl(changer_fd, CHIOMOVE, &cmd)) + err(1, "%s: CHIOMOVE", changer_name); + + return (0); + + usage: + (void) fprintf(stderr, "usage: %s %s " + "<from ET> <from EU> <to ET> <to EU> [inv]\n", __progname, cname); + return (1); +} + +static int +do_exchange(cname, argc, argv) + char *cname; + int argc; + char **argv; +{ + struct changer_exchange cmd; + int val; + + /* + * On an exchange command, we expect the following: + * + * <src ET> <src EU> <dst1 ET> <dst1 EU> [<dst2 ET> <dst2 EU>] [inv1] [inv2] + * + * where ET == element type and EU == element unit. + */ + + ++argv; --argc; + + if (argc < 4) { + warnx("%s: too few arguments", cname); + goto usage; + } else if (argc > 8) { + warnx("%s: too many arguments", cname); + goto usage; + } + (void) memset(&cmd, 0, sizeof(cmd)); + + /* <src ET> */ + cmd.ce_srctype = parse_element_type(*argv); + ++argv; --argc; + + /* Check for voltag virtual type */ + if (CHET_VT == cmd.ce_srctype) { + find_element(*argv, &cmd.ce_srctype, &cmd.ce_srcunit); + } else { + /* <from EU> */ + cmd.ce_srcunit = parse_element_unit(*argv); + } + ++argv; --argc; + + /* <dst1 ET> */ + cmd.ce_fdsttype = parse_element_type(*argv); + ++argv; --argc; + + /* Check for voltag virtual type */ + if (CHET_VT == cmd.ce_fdsttype) { + find_element(*argv, &cmd.ce_fdsttype, &cmd.ce_fdstunit); + } else { + /* <from EU> */ + cmd.ce_fdstunit = parse_element_unit(*argv); + } + ++argv; --argc; + + /* + * If the next token is a special word or there are no more + * arguments, then this is a case of simple exchange. + * dst2 == src. + */ + if ((argc == 0) || is_special(*argv)) { + cmd.ce_sdsttype = cmd.ce_srctype; + cmd.ce_sdstunit = cmd.ce_srcunit; + goto do_special; + } + + /* <dst2 ET> */ + cmd.ce_sdsttype = parse_element_type(*argv); + ++argv; --argc; + + if (CHET_VT == cmd.ce_sdsttype) + errx(1,"%s %s: voltag only makes sense as an element source", + cname, *argv); + + /* <dst2 EU> */ + cmd.ce_sdstunit = parse_element_unit(*argv); + ++argv; --argc; + + do_special: + /* Deal with optional command modifiers. */ + while (argc) { + val = parse_special(*argv); + ++argv; --argc; + switch (val) { + case SW_INVERT1: + cmd.ce_flags |= CE_INVERT1; + break; + + case SW_INVERT2: + cmd.ce_flags |= CE_INVERT2; + break; + + default: + errx(1, "%s: inappropriate modifier `%s'", + cname, *argv); + /* NOTREACHED */ + } + } + + /* Send command to changer. */ + if (ioctl(changer_fd, CHIOEXCHANGE, &cmd)) + err(1, "%s: CHIOEXCHANGE", changer_name); + + return (0); + + usage: + (void) fprintf(stderr, + "usage: %s %s <src ET> <src EU> <dst1 ET> <dst1 EU>\n" + " [<dst2 ET> <dst2 EU>] [inv1] [inv2]\n", + __progname, cname); + return (1); +} + +static int +do_position(cname, argc, argv) + char *cname; + int argc; + char **argv; +{ + struct changer_position cmd; + int val; + + /* + * On a position command, we expect the following: + * + * <to ET> <to EU> [inv] + * + * where ET == element type and EU == element unit. + */ + + ++argv; --argc; + + if (argc < 2) { + warnx("%s: too few arguments", cname); + goto usage; + } else if (argc > 3) { + warnx("%s: too many arguments", cname); + goto usage; + } + (void) memset(&cmd, 0, sizeof(cmd)); + + /* <to ET> */ + cmd.cp_type = parse_element_type(*argv); + ++argv; --argc; + + /* <to EU> */ + cmd.cp_unit = parse_element_unit(*argv); + ++argv; --argc; + + /* Deal with optional command modifier. */ + if (argc) { + val = parse_special(*argv); + switch (val) { + case SW_INVERT: + cmd.cp_flags |= CP_INVERT; + break; + + default: + errx(1, "%s: inappropriate modifier `%s'", + cname, *argv); + /* NOTREACHED */ + } + } + + /* Send command to changer. */ + if (ioctl(changer_fd, CHIOPOSITION, &cmd)) + err(1, "%s: CHIOPOSITION", changer_name); + + return (0); + + usage: + (void) fprintf(stderr, "usage: %s %s <to ET> <to EU> [inv]\n", + __progname, cname); + return (1); +} + +/* ARGSUSED */ +static int +do_params(cname, argc, argv) + char *cname; + int argc; + char **argv; +{ + struct changer_params data; + int picker; + + /* No arguments to this command. */ + + ++argv; --argc; + + if (argc) { + warnx("%s: no arguments expected", cname); + goto usage; + } + + /* Get params from changer and display them. */ + (void) memset(&data, 0, sizeof(data)); + if (ioctl(changer_fd, CHIOGPARAMS, &data)) + err(1, "%s: CHIOGPARAMS", changer_name); + + (void) printf("%s: %d slot%s, %d drive%s, %d picker%s", + changer_name, + data.cp_nslots, (data.cp_nslots > 1) ? "s" : "", + data.cp_ndrives, (data.cp_ndrives > 1) ? "s" : "", + data.cp_npickers, (data.cp_npickers > 1) ? "s" : ""); + if (data.cp_nportals) + (void) printf(", %d portal%s", data.cp_nportals, + (data.cp_nportals > 1) ? "s" : ""); + + /* Get current picker from changer and display it. */ + if (ioctl(changer_fd, CHIOGPICKER, &picker)) + err(1, "%s: CHIOGPICKER", changer_name); + + (void) printf("\n%s: current picker: %d\n", changer_name, picker); + + return (0); + + usage: + (void) fprintf(stderr, "usage: %s %s\n", __progname, cname); + return (1); +} + +/* ARGSUSED */ +static int +do_getpicker(cname, argc, argv) + char *cname; + int argc; + char **argv; +{ + int picker; + + /* No arguments to this command. */ + + ++argv; --argc; + + if (argc) { + warnx("%s: no arguments expected", cname); + goto usage; + } + + /* Get current picker from changer and display it. */ + if (ioctl(changer_fd, CHIOGPICKER, &picker)) + err(1, "%s: CHIOGPICKER", changer_name); + + (void) printf("%s: current picker: %d\n", changer_name, picker); + + return (0); + + usage: + (void) fprintf(stderr, "usage: %s %s\n", __progname, cname); + return (1); +} + +static int +do_setpicker(cname, argc, argv) + char *cname; + int argc; + char **argv; +{ + int picker; + + ++argv; --argc; + + if (argc < 1) { + warnx("%s: too few arguments", cname); + goto usage; + } else if (argc > 1) { + warnx("%s: too many arguments", cname); + goto usage; + } + + picker = parse_element_unit(*argv); + + /* Set the changer picker. */ + if (ioctl(changer_fd, CHIOSPICKER, &picker)) + err(1, "%s: CHIOSPICKER", changer_name); + + return (0); + + usage: + (void) fprintf(stderr, "usage: %s %s <picker>\n", __progname, cname); + return (1); +} + +static int +do_status(cname, argc, argv) + char *cname; + int argc; + char **argv; +{ + struct changer_params cp; + struct changer_element_status_request cesr; + int i, count, base, chet, schet, echet; + char *description; + int pvoltag = 0; + int avoltag = 0; + int sense = 0; + int scsi = 0; + int source = 0; + int intaddr = 0; + int c; + + count = 0; + base = 0; + description = NULL; + + optind = optreset = 1; + while ((c = getopt(argc, argv, "vVsSbaI")) != -1) { + switch (c) { + case 'v': + pvoltag = 1; + break; + case 'V': + avoltag = 1; + break; + case 's': + sense = 1; + break; + case 'S': + source = 1; + break; + case 'b': + scsi = 1; + break; + case 'I': + intaddr = 1; + break; + case 'a': + pvoltag = avoltag = source = sense = scsi = intaddr = 1; + break; + default: + warnx("%s: bad option", cname); + goto usage; + } + } + + argc -= optind; + argv += optind; + + /* + * On a status command, we expect the following: + * + * [<ET> [<start> [<end>] ] ] + * + * where ET == element type, start == first element to report, + * end == number of elements to report + * + * If we get no arguments, we get the status of all + * known element types. + */ + if (argc > 3) { + warnx("%s: too many arguments", cname); + goto usage; + } + + /* + * Get params from changer. Specifically, we need the element + * counts. + */ + if (ioctl(changer_fd, CHIOGPARAMS, (char *)&cp)) + err(1, "%s: CHIOGPARAMS", changer_name); + + if (argc > 0) + schet = echet = parse_element_type(argv[0]); + else { + schet = CHET_MT; + echet = CHET_DT; + } + if (argc > 1) { + base = atol(argv[1]); + count = 1; + } + if (argc > 2) + count = atol(argv[2]) - base + 1; + + if (base < 0 || count < 0) + errx(1, "bad arguments"); + + for (chet = schet; chet <= echet; ++chet) { + switch (chet) { + case CHET_MT: + if (count == 0) + count = cp.cp_npickers; + else if (count > cp.cp_npickers) + errx(1, "not that many pickers in device"); + description = "picker"; + break; + + case CHET_ST: + if (count == 0) + count = cp.cp_nslots; + else if (count > cp.cp_nslots) + errx(1, "not that many slots in device"); + description = "slot"; + break; + + case CHET_IE: + if (count == 0) + count = cp.cp_nportals; + else if (count > cp.cp_nportals) + errx(1, "not that many portals in device"); + description = "portal"; + break; + + case CHET_DT: + if (count == 0) + count = cp.cp_ndrives; + else if (count > cp.cp_ndrives) + errx(1, "not that many drives in device"); + description = "drive"; + break; + + default: + /* To appease gcc -Wuninitialized. */ + count = 0; + description = NULL; + } + + if (count == 0) { + if (argc == 0) + continue; + else { + printf("%s: no %s elements\n", + changer_name, description); + return (0); + } + } + + bzero(&cesr, sizeof(cesr)); + cesr.cesr_element_type = chet; + cesr.cesr_element_base = base; + cesr.cesr_element_count = count; + /* Allocate storage for the status structures. */ + cesr.cesr_element_status = + (struct changer_element_status *) + calloc(count, sizeof(struct changer_element_status)); + + if (!cesr.cesr_element_status) + errx(1, "can't allocate status storage"); + + if (avoltag || pvoltag) + cesr.cesr_flags |= CESR_VOLTAGS; + + if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr)) { + free(cesr.cesr_element_status); + err(1, "%s: CHIOGSTATUS", changer_name); + } + + /* Dump the status for each reported element. */ + for (i = 0; i < count; ++i) { + struct changer_element_status *ces = + &(cesr.cesr_element_status[i]); + printf("%s %d: %s", description, ces->ces_addr, + bits_to_string(ces->ces_flags, + CESTATUS_BITS)); + if (sense) + printf(" sense: <0x%02x/0x%02x>", + ces->ces_sensecode, + ces->ces_sensequal); + if (pvoltag) + printf(" voltag: <%s:%d>", + ces->ces_pvoltag.cv_volid, + ces->ces_pvoltag.cv_serial); + if (avoltag) + printf(" avoltag: <%s:%d>", + ces->ces_avoltag.cv_volid, + ces->ces_avoltag.cv_serial); + if (source) { + if (ces->ces_flags & CES_SOURCE_VALID) + printf(" source: <%s %d>", + element_type_name( + ces->ces_source_type), + ces->ces_source_addr); + else + printf(" source: <>"); + } + if (intaddr) + printf(" intaddr: <%d>", ces->ces_int_addr); + if (scsi) { + printf(" scsi: <"); + if (ces->ces_flags & CES_SCSIID_VALID) + printf("%d", ces->ces_scsi_id); + else + putchar('?'); + putchar(':'); + if (ces->ces_flags & CES_LUN_VALID) + printf("%d", ces->ces_scsi_lun); + else + putchar('?'); + putchar('>'); + } + putchar('\n'); + } + + free(cesr.cesr_element_status); + count = 0; + } + + return (0); + + usage: + (void) fprintf(stderr, "usage: %s %s [-vVsSbaA] [<element type> [<start-addr> [<end-addr>] ] ]\n", + __progname, cname); + return (1); +} + +static int +do_ielem(cname, argc, argv) + char *cname; + int argc; + char **argv; +{ + int timeout = 0; + + if (argc == 2) { + timeout = atol(argv[1]); + } else if (argc > 1) { + warnx("%s: too many arguments", cname); + goto usage; + } + + if (ioctl(changer_fd, CHIOIELEM, &timeout)) + err(1, "%s: CHIOIELEM", changer_name); + + return (0); + + usage: + (void) fprintf(stderr, "usage: %s %s [<timeout>]\n", + __progname, cname); + return (1); +} + +static int +do_voltag(cname, argc, argv) + char *cname; + int argc; + char **argv; +{ + int force = 0; + int clear = 0; + int alternate = 0; + int c; + struct changer_set_voltag_request csvr; + + bzero(&csvr, sizeof(csvr)); + + optind = optreset = 1; + while ((c = getopt(argc, argv, "fca")) != -1) { + switch (c) { + case 'f': + force = 1; + break; + case 'c': + clear = 1; + break; + case 'a': + alternate = 1; + break; + default: + warnx("%s: bad option", cname); + goto usage; + } + } + + argc -= optind; + argv += optind; + + if (argc < 2) { + warnx("%s: missing element specification", cname); + goto usage; + } + + csvr.csvr_type = parse_element_type(argv[0]); + csvr.csvr_addr = atol(argv[1]); + + if (!clear) { + if (argc < 3 || argc > 4) { + warnx("%s: missing argument", cname); + goto usage; + } + + if (force) + csvr.csvr_flags = CSVR_MODE_REPLACE; + else + csvr.csvr_flags = CSVR_MODE_SET; + + if (strlen(argv[2]) > sizeof(csvr.csvr_voltag.cv_volid)) { + warnx("%s: volume label too long", cname); + goto usage; + } + + strncpy(csvr.csvr_voltag.cv_volid, argv[2], + sizeof(csvr.csvr_voltag.cv_volid)); + + if (argc == 4) { + csvr.csvr_voltag.cv_serial = atol(argv[3]); + } + } else { + if (argc != 2) { + warnx("%s: unexpected argument", cname); + goto usage; + } + csvr.csvr_flags = CSVR_MODE_CLEAR; + } + + if (alternate) { + csvr.csvr_flags |= CSVR_ALTERNATE; + } + + if (ioctl(changer_fd, CHIOSETVOLTAG, &csvr)) + err(1, "%s: CHIOSETVOLTAG", changer_name); + + return 0; + usage: + (void) fprintf(stderr, + "usage: %s %s [-fca] <element> [<voltag> [<vsn>] ]\n", + __progname, cname); + return 1; +} + +static int +parse_element_type(cp) + char *cp; +{ + int i; + + for (i = 0; elements[i].et_name != NULL; ++i) + if (strcmp(elements[i].et_name, cp) == 0) + return (elements[i].et_type); + + errx(1, "invalid element type `%s'", cp); + /* NOTREACHED */ +} + +static const char * +element_type_name(et) + int et; +{ + int i; + + for (i = 0; elements[i].et_name != NULL; i++) + if (elements[i].et_type == et) + return elements[i].et_name; + + return "unknown"; +} + +static int +parse_element_unit(cp) + char *cp; +{ + int i; + char *p; + + i = (int)strtol(cp, &p, 10); + if ((i < 0) || (*p != '\0')) + errx(1, "invalid unit number `%s'", cp); + + return (i); +} + +static int +parse_special(cp) + char *cp; +{ + int val; + + val = is_special(cp); + if (val) + return (val); + + errx(1, "invalid modifier `%s'", cp); + /* NOTREACHED */ +} + +static int +is_special(cp) + char *cp; +{ + int i; + + for (i = 0; specials[i].sw_name != NULL; ++i) + if (strcmp(specials[i].sw_name, cp) == 0) + return (specials[i].sw_value); + + return (0); +} + +static const char * +bits_to_string(v, cp) + int v; + const char *cp; +{ + const char *np; + char f, sep, *bp; + static char buf[128]; + + bp = buf; + (void) memset(buf, 0, sizeof(buf)); + + for (sep = '<'; (f = *cp++) != 0; cp = np) { + for (np = cp; *np >= ' ';) + np++; + if ((v & (1 << (f - 1))) == 0) + continue; + (void) snprintf(bp, sizeof(buf) - (bp - &buf[0]), + "%c%.*s", sep, (int)(long)(np - cp), cp); + bp += strlen(bp); + sep = ','; + } + if (sep != '<') + *bp = '>'; + + return (buf); +} +/* + * do_return() + * + * Given an element reference, ask the changer/picker to move that + * element back to its source slot. + */ +static int +do_return(cname, argc, argv) + char *cname; + int argc; + char **argv; +{ + struct changer_element_status *ces; + struct changer_move cmd; + u_int16_t type, element; + + ++argv; --argc; + + if (argc < 2) { + warnx("%s: too few arguments", cname); + goto usage; + } else if (argc > 3) { + warnx("%s: too many arguments", cname); + goto usage; + } + + type = parse_element_type(*argv); + ++argv; --argc; + + /* Handle voltag virtual Changer Element Type */ + if (CHET_VT == type) { + find_element(*argv, &type, &element); + } else { + element = parse_element_unit(*argv); + } + ++argv; --argc; + + ces = get_element_status(type, element); /* Get the status */ + + if (NULL == ces) + errx(1, "%s: null element status pointer", cname); + + if (!(ces->ces_flags & CES_SOURCE_VALID)) + errx(1, "%s: no source information", cname); + + (void) memset(&cmd, 0, sizeof(cmd)); + + cmd.cm_fromtype = type; + cmd.cm_fromunit = element; + cmd.cm_totype = ces->ces_source_type; + cmd.cm_tounit = ces->ces_source_addr; + + if (ioctl(changer_fd, CHIOMOVE, &cmd) == -1) + err(1, "%s: CHIOMOVE", changer_name); + free(ces); + + return(0); + +usage: + (void) fprintf(stderr, "usage: %s %s " + "<from ET> <from EU>\n", __progname, cname); + return(1); +} + +/* + * get_element_status() + * + * return a *cesr for the specified changer element. This + * routing will malloc()/calloc() the memory. The caller + * should free() it when done. + */ +static struct changer_element_status * +get_element_status(type, element) + u_int16_t type; + u_int16_t element; +{ + struct changer_element_status_request cesr; + struct changer_element_status *ces; + + ces = (struct changer_element_status *) + calloc(1, sizeof(struct changer_element_status)); + + if (NULL == ces) + errx(1, "can't allocate status storage"); + + (void)memset(&cesr, 0, sizeof(cesr)); + + cesr.cesr_element_type = type; + cesr.cesr_element_base = element; + cesr.cesr_element_count = 1; /* Only this one element */ + cesr.cesr_flags |= CESR_VOLTAGS; /* Grab voltags as well */ + cesr.cesr_element_status = ces; + + if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) { + free(ces); + err(1, "%s: CHIOGSTATUS", changer_name); + /* NOTREACHED */ + } + + return ces; +} + + +/* + * find_element() + * + * Given a <voltag> find the chager element and unit, or exit + * with an error if it isn't found. We grab the changer status + * and iterate until we find a match, or crap out. + */ +static void +find_element(voltag, et, eu) + char *voltag; + u_int16_t *et; + u_int16_t *eu; +{ + struct changer_params cp; + struct changer_element_status_request cesr; + struct changer_element_status *ch_ces, *ces; + int elem, total_elem, found = 0; + + /* + * Get the changer parameters, we're interested in the counts + * for all types of elements to perform our search. + */ + if (ioctl(changer_fd, CHIOGPARAMS, (char *)&cp)) + err(1, "%s: CHIOGPARAMS", changer_name); + + /* Allocate some memory for the results */ + total_elem = (cp.cp_nslots + cp.cp_ndrives + + cp.cp_npickers + cp.cp_nportals); + + ch_ces = (struct changer_element_status *) + calloc(total_elem, sizeof(struct changer_element_status)); + + if (NULL == ch_ces) + errx(1, "can't allocate status storage"); + + ces = ch_ces; + + /* Read in the changer slots */ + if (cp.cp_nslots > 0) { + cesr.cesr_element_type = CHET_ST; + cesr.cesr_element_base = 0; + cesr.cesr_element_count = cp.cp_nslots; + cesr.cesr_flags |= CESR_VOLTAGS; + cesr.cesr_element_status = ces; + + if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) { + free(ch_ces); + err(1, "%s: CHIOGSTATUS", changer_name); + } + ces += cp.cp_nslots; + } + + /* Read in the drive information */ + if (cp.cp_ndrives > 0 ) { + + (void) memset(&cesr, 0, sizeof(cesr)); + cesr.cesr_element_type = CHET_DT; + cesr.cesr_element_base = 0; + cesr.cesr_element_count = cp.cp_ndrives; + cesr.cesr_flags |= CESR_VOLTAGS; + cesr.cesr_element_status = ces; + + if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) { + free(ch_ces); + err(1, "%s: CHIOGSTATUS", changer_name); + } + ces += cp.cp_ndrives; + } + + /* Read in the portal information */ + if (cp.cp_nportals > 0 ) { + (void) memset(&cesr, 0, sizeof(cesr)); + cesr.cesr_element_type = CHET_IE; + cesr.cesr_element_base = 0; + cesr.cesr_element_count = cp.cp_nportals; + cesr.cesr_flags |= CESR_VOLTAGS; + cesr.cesr_element_status = ces; + + if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) { + free(ch_ces); + err(1, "%s: CHIOGSTATUS", changer_name); + } + ces += cp.cp_nportals; + } + + /* Read in the picker information */ + if (cp.cp_npickers > 0) { + (void) memset(&cesr, 0, sizeof(cesr)); + cesr.cesr_element_type = CHET_MT; + cesr.cesr_element_base = 0; + cesr.cesr_element_count = cp.cp_npickers; + cesr.cesr_flags |= CESR_VOLTAGS; + cesr.cesr_element_status = ces; + + if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) { + free(ch_ces); + err(1, "%s: CHIOGSTATUS", changer_name); + } + } + + /* + * Now search the list the specified <voltag> + */ + for (elem = 0; elem <= total_elem; ++elem) { + + ces = &ch_ces[elem]; + + /* Make sure we have a tape in this element */ + if ((ces->ces_flags & (CES_STATUS_ACCESS|CES_STATUS_FULL)) + != (CES_STATUS_ACCESS|CES_STATUS_FULL)) + continue; + + /* Check to see if it is our target */ + if (strcasecmp(voltag, ces->ces_pvoltag.cv_volid) == 0) { + *et = ces->ces_type; + *eu = ces->ces_addr; + ++found; + break; + } + } + if (!found) { + errx(1, "%s: unable to locate voltag: %s", changer_name, + voltag); + } + free(ch_ces); + return; +} + +static void +cleanup() +{ + /* Simple enough... */ + (void)close(changer_fd); +} + +static void +usage() +{ + (void) fprintf(stderr, "usage: %s [-f changer] command [-<flags>] " + "arg1 arg2 [arg3 [...]]\n", __progname); + exit(1); +} diff --git a/bin/chio/defs.h b/bin/chio/defs.h new file mode 100644 index 0000000..f4580a5 --- /dev/null +++ b/bin/chio/defs.h @@ -0,0 +1,57 @@ +/* $FreeBSD$ */ + +/* + * Copyright (c) 1996 Jason R. Thorpe <thorpej@and.com> + * 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 acknowledgements: + * This product includes software developed by Jason R. Thorpe + * for And Communications, http://www.and.com/ + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +struct element_type { + char *et_name; /* name; i.e. "picker, "slot", etc. */ + int et_type; /* type number */ +}; + +struct changer_command { + char *cc_name; /* command name */ + /* command handler */ + int (*cc_handler) __P((char *, int, char **)); +}; + +struct special_word { + char *sw_name; /* special word */ + int sw_value; /* token value */ +}; + +/* sw_value */ +#define SW_INVERT 1 /* set "invert media" flag */ +#define SW_INVERT1 2 /* set "invert media 1" flag */ +#define SW_INVERT2 3 /* set "invert media 2" flag */ + +/* Environment variable to check for default changer. */ +#define CHANGER_ENV_VAR "CHANGER" diff --git a/bin/chio/pathnames.h b/bin/chio/pathnames.h new file mode 100644 index 0000000..9a9b74a --- /dev/null +++ b/bin/chio/pathnames.h @@ -0,0 +1,35 @@ +/* $FreeBSD$ */ + +/* + * Copyright (c) 1996 Jason R. Thorpe <thorpej@and.com> + * 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 acknowledgements: + * This product includes software developed by Jason R. Thorpe + * for And Communications, http://www.and.com/ + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#define _PATH_CH "/dev/ch0" diff --git a/bin/chmod/Makefile b/bin/chmod/Makefile new file mode 100644 index 0000000..71006d0 --- /dev/null +++ b/bin/chmod/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 +# $FreeBSD$ + +PROG= chmod + +.include <bsd.prog.mk> diff --git a/bin/chmod/chmod.1 b/bin/chmod/chmod.1 new file mode 100644 index 0000000..847e190 --- /dev/null +++ b/bin/chmod/chmod.1 @@ -0,0 +1,347 @@ +.\" 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 +.\" $FreeBSD$ +.\" +.Dd March 31, 1994 +.Dt CHMOD 1 +.Os +.Sh NAME +.Nm chmod +.Nd change file modes +.Sh SYNOPSIS +.Nm chmod +.Oo +.Fl f +.Fl R +.Op Fl H | Fl L | Fl P +.Fl v +.Oc +.Ar mode +.Ar file ... +.Sh DESCRIPTION +The +.Nm +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 by +default.) +.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. +.It Fl f +Do not display a diagnostic message if +.Nm +could not modify the mode for +.Va file . +.It Fl v +Cause +.Nm +to be verbose, showing files as the mode is modified. +.El +.Pp +Symbolic links do not have modes, so unless the +.Fl H +or +.Fl L +option is set, +.Nm +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. +.Sh DIAGNOSTICS +The +.Nm +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 from the sum of +one or more of the following values: +.Pp +.Bl -tag -width 6n -compact -offset indent +.It Li 4000 +(the set-user-ID-on-execution bit) Executable files with this bit set +will run with effective uid set to the uid of the file owner. +Directories with the set-user-id bit set will force all files and +sub-directories created in them to be owned by the directory owner +and not by the uid of the creating process, if the underlying file +system supports this feature: see +.Xr chmod 2 +and the +.Ar suiddir +option to +.Xr mount 8 . +.It Li 2000 +(the set-group-ID-on-execution bit) Executable files with this bit set +will run with effective gid set to the gid of the file owner. +.It Li 1000 +(the sticky bit) +When set on a directory, unprivileged users can delete and rename +only those files in the directory that are owned by them, regardless of +the permissions on the directory. Under FreeBSD, the sticky bit is +ignored for executable files and may only be set for directories (see +.Xr sticky 8 ). +.It Li 0400 +Allow read by owner. +.It Li 0200 +Allow write by owner. +.It Li 0100 +For files, allow execution by owner. For directories, allow the owner to +search in the directory. +.It Li 0040 +Allow read by group members. +.It Li 0020 +Allow write by group members. +.It Li 0010 +For files, allow execution by group members. For directories, allow +group members to search in the directory. +.It Li 0004 +Allow read by others. +.It Li 0002 +Allow write by others. +.It Li 0001 +For files, allow execution by others. For directories allow others to +search in the directory. +.El +.Pp +For example, the absolute mode that permits read, write and execute by +the owner, read and execute by group members, read and execute by +others, and no set-uid or set-gid behaviour is 755 +(400+200+100+040+010+004+001). +.Pp +The symbolic mode is described by the following grammar: +.Bd -literal -offset indent +mode ::= clause [, clause ...] +clause ::= [who ...] [action ...] action +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 COMPATIBILITY +The +.Fl v +option is non-standard and its use in scripts is not recommended. +.Sh SEE ALSO +.Xr chflags 1 , +.Xr install 1 , +.Xr chmod 2 , +.Xr stat 2 , +.Xr umask 2 , +.Xr fts 3 , +.Xr setmode 3 , +.Xr symlink 7 , +.Xr chown 8 , +.Xr mount 8 , +.Xr sticky 8 +.Sh STANDARDS +The +.Nm +utility is expected to be +.St -p1003.2 +compatible with the exception of the +.Ar perm +symbols +.Dq t +and +.Dq X +which are not included in that standard. +.Sh HISTORY +A +.Nm +command appeared in +.At v1 . diff --git a/bin/chmod/chmod.c b/bin/chmod/chmod.c new file mode 100644 index 0000000..395bee1 --- /dev/null +++ b/bin/chmod/chmod.c @@ -0,0 +1,218 @@ +/* + * 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 const copyright[] = +"@(#) Copyright (c) 1989, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)chmod.c 8.8 (Berkeley) 4/1/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <err.h> +#include <errno.h> +#include <fts.h> +#include <limits.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; + int vflag; + char *ep, *mode; + int newmode; + + set = NULL; + omode = 0; + Hflag = Lflag = Pflag = Rflag = fflag = hflag = vflag = 0; + while ((ch = getopt(argc, argv, "HLPRXfgorstuvwx")) != -1) + 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': + 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 'v': + vflag = 1; + break; + 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: /* Change it at FTS_DP. */ + if (!Rflag) + fts_set(ftsp, p, FTS_SKIP); + continue; + 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; + } + newmode = oct ? omode : getmode(set, p->fts_statp->st_mode); + if ((newmode & ALLPERMS) == (p->fts_statp->st_mode & ALLPERMS)) + continue; + if (chmod(p->fts_accpath, newmode) && !fflag) { + warn("%s", p->fts_path); + rval = 1; + } else { + if (vflag) + (void)printf("%s\n", p->fts_accpath); + } + } + if (errno) + err(1, "fts_read"); + free(set); + exit(rval); +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: chmod [-fv] [-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..1b0037f --- /dev/null +++ b/bin/cp/Makefile @@ -0,0 +1,8 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 +# $FreeBSD$ + +PROG= cp +SRCS= cp.c utils.c +CFLAGS+= -W -DVM_AND_BUFFER_CACHE_SYNCHRONIZED + +.include <bsd.prog.mk> diff --git a/bin/cp/cp.1 b/bin/cp/cp.1 new file mode 100644 index 0000000..54e050a --- /dev/null +++ b/bin/cp/cp.1 @@ -0,0 +1,237 @@ +.\" 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 +.\" $FreeBSD$ +.\" +.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 f | i +.Op Fl pv +.Ar source_file target_file +.Nm cp +.Oo +.Fl R +.Op Fl H | Fl L | Fl P +.Oc +.Op Fl f | i +.Op Fl pv +.Ar source_file ... target_directory +.Sh DESCRIPTION +In the first synopsis form, the +.Nm +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 +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 +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 +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 f +option overrides any previous +.Fl i +options.) +.It Fl i +Cause +.Nm +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 +or +.Sq Li Y , +the file copy is attempted. +(The +.Fl i +option overrides any previous +.Fl f +options.) +.It Fl p +Cause +.Nm +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 nor set group ID bits are preserved in the copy's +permissions. +.It Fl v +Cause +.Nm +to be verbose, showing files as they are copied. +.El +.Pp +For each destination file that already exists, its contents are +overwritten if permissions allow. Its mode, user ID, and group +ID are unchanged unless the +.Fl p +option was specified. +.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. +.Sh DIAGNOSTICS +The +.Nm +utility exits 0 on success, and >0 if an error occurs. +.Sh COMPATIBILITY +Historic versions of the +.Nm +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. +.Pp +The +.Fl v +option is non-standard and its use in scripts is not recommended. +.Sh SEE ALSO +.Xr mv 1 , +.Xr rcp 1 , +.Xr umask 2 , +.Xr fts 3 , +.Xr symlink 7 +.Sh STANDARDS +The +.Nm +command is expected to be +.St -p1003.2 +compatible. +.Sh HISTORY +A +.Nm +command appeared in +.At v1 . diff --git a/bin/cp/cp.c b/bin/cp/cp.c new file mode 100644 index 0000000..e528aea --- /dev/null +++ b/bin/cp/cp.c @@ -0,0 +1,454 @@ +/* + * 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 const copyright[] = +"@(#) Copyright (c) 1988, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)cp.c 8.2 (Berkeley) 4/1/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#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-relative. + * + * 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 <err.h> +#include <errno.h> +#include <fts.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "extern.h" + +#define STRIP_TRAILING_SLASH(p) { \ + while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/') \ + *--(p).p_end = 0; \ +} + +PATH_T to = { to.p_path, "", "" }; + +uid_t myuid; +int Rflag, iflag, pflag, rflag, fflag, vflag; +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 = 0; + while ((ch = getopt(argc, argv, "HLPRfiprv")) != -1) + 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': + fflag = 1; + iflag = 0; + break; + case 'i': + iflag = 1; + fflag = 0; + break; + case 'p': + pflag = 1; + break; + case 'r': + rflag = 1; + break; + case 'v': + vflag = 1; + break; + 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 = 0, dne, badcp, nlen, rval; + char *p, *target_mid; + + if ((ftsp = fts_open(argv, fts_options, mastercmp)) == NULL) + err(1, NULL); + for (badcp = rval = 0; (curr = fts_read(ftsp)) != NULL; badcp = 0) { + switch (curr->fts_info) { + case FTS_NS: + case FTS_DNR: + case FTS_ERR: + warnx("%s: %s", + curr->fts_path, strerror(curr->fts_errno)); + badcp = rval = 1; + continue; + case FTS_DC: /* Warn, continue. */ + warnx("%s: directory causes a cycle", curr->fts_path); + badcp = 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) { + /* + * 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 + * concatenation 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; + } + + p = &curr->fts_path[base]; + nlen = curr->fts_pathlen - base; + target_mid = to.target_end; + if (*p != '/' && target_mid[-1] != '/') + *target_mid++ = '/'; + *target_mid = 0; + if (target_mid - to.p_path + nlen > MAXPATHLEN) { + warnx("%s%s: name too long (not copied)", + to.p_path, p); + badcp = rval = 1; + continue; + } + (void)strncat(target_mid, p, nlen); + to.p_end = target_mid + nlen; + *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); + badcp = rval = 1; + if (S_ISDIR(curr->fts_statp->st_mode)) + (void)fts_set(ftsp, curr, FTS_SKIP); + continue; + } + if (!S_ISDIR(curr->fts_statp->st_mode) && + S_ISDIR(to_stat.st_mode)) { + warnx("cannot overwrite directory %s with non-directory %s", + to.p_path, curr->fts_path); + badcp = rval = 1; + continue; + } + dne = 0; + } + + switch (curr->fts_statp->st_mode & S_IFMT) { + case S_IFLNK: + if (copy_link(curr, !dne)) + badcp = 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); + badcp = 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", 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)) + badcp = 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)) + badcp = rval = 1; + } else { + if (copy_file(curr, dne)) + badcp = rval = 1; + } + break; + case S_IFIFO: + if (Rflag) { + if (copy_fifo(curr->fts_statp, !dne)) + badcp = rval = 1; + } else { + if (copy_file(curr, dne)) + badcp = rval = 1; + } + break; + default: + if (copy_file(curr, dne)) + badcp = rval = 1; + break; + } + if (vflag && !badcp) + (void)printf("%s -> %s\n", curr->fts_path, to.p_path); + } + 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..272d632 --- /dev/null +++ b/bin/cp/extern.h @@ -0,0 +1,56 @@ +/*- + * 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 + * $FreeBSD$ + */ + +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, fflag, vflag, 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..34b0225 --- /dev/null +++ b/bin/cp/utils.c @@ -0,0 +1,330 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)utils.c 8.3 (Berkeley) 4/1/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/time.h> +#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED +#include <sys/mman.h> +#endif + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <fts.h> +#include <stdio.h> +#include <sysexits.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, wresid; + char *bufp; +#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) { +#define YESNO "(y/n [n]) " + if (iflag) { + (void)fprintf(stderr, "overwrite %s? %s", + to.p_path, YESNO); + checkch = ch = getchar(); + while (ch != '\n' && ch != EOF) + ch = getchar(); + if (checkch != 'y' && checkch != 'Y') { + (void)close(from_fd); + (void)fprintf(stderr, "not overwritten\n"); + return (1); + } + } + + if (fflag) { + /* remove existing destination file name, + * create a new file */ + (void)unlink(to.p_path); + to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, + fs->st_mode & ~(S_ISUID | S_ISGID)); + } else + /* overwrite existing destination file name */ + 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 (S_ISREG(fs->st_mode) && fs->st_size <= 8 * 1048576) { + if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ, + MAP_SHARED, from_fd, (off_t)0)) == MAP_FAILED) { + warn("%s", entp->fts_path); + rval = 1; + } else { + for (bufp = p, wresid = fs->st_size; ; + bufp += wcount, wresid -= wcount) { + wcount = write(to_fd, bufp, wresid); + if (wcount >= wresid || wcount <= 0) + break; + } + if (wcount != wresid) { + 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) { + for (bufp = buf, wresid = rcount; ; + bufp += wcount, wresid -= wcount) { + wcount = write(to_fd, bufp, wresid); + if (wcount >= wresid || wcount <= 0) + break; + } + if (wcount != wresid) { + warn("%s", to.p_path); + rval = 1; + break; + } + } + if (rcount < 0) { + warn("%s", entp->fts_path); + rval = 1; + } + } + + /* + * Don't remove the target even after an error. The target might + * not be a regular file, or its attributes might be important, + * or its contents might be irreplaceable. It would only be safe + * to remove it if we created it and its length is 0. + */ + + 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)) == -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]; + struct stat ts; + int rval; + int gotstat; + + rval = 0; + fs->st_mode &= S_ISUID | S_ISGID | S_ISVTX | + 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; + } + if (fd ? fstat(fd, &ts) : stat(to.p_path, &ts)) + gotstat = 0; + else { + gotstat = 1; + ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX | + S_IRWXU | S_IRWXG | S_IRWXO; + } + /* + * 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 (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid) + 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 (!gotstat || fs->st_mode != ts.st_mode) + if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) { + warn("chown: %s", to.p_path); + rval = 1; + } + + if (!gotstat || fs->st_flags != ts.st_flags) + 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]] [-f | -i] [-pv] src target", +" cp [-R [-H | -L | -P]] [-f | -i] [-pv] src1 ... srcN directory"); + exit(EX_USAGE); +} diff --git a/bin/csh/Makefile b/bin/csh/Makefile new file mode 100644 index 0000000..50113e0 --- /dev/null +++ b/bin/csh/Makefile @@ -0,0 +1,75 @@ +# $FreeBSD$ +# @(#)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. + +TCSHDIR= ${.CURDIR}/../../contrib/tcsh +.PATH: ${TCSHDIR} + +PROG= csh +SUBDIR= nls +DFLAGS= -D_PATH_TCSHELL='"${DESTDIR}/bin/${PROG}"' +CFLAGS+= -I${TCSHDIR} -I${.CURDIR} -I. ${DFLAGS} +SRCS= sh.c sh.dir.c sh.dol.c sh.err.c sh.exec.c sh.char.c \ + sh.exp.c sh.file.c sh.func.c sh.glob.c sh.hist.c sh.init.c \ + sh.lex.c sh.misc.c sh.parse.c sh.print.c sh.proc.c sh.sem.c \ + sh.set.c sh.time.c sh.h sh.char.h sh.dir.h sh.proc.h +SRCS+= sh.decls.h glob.c glob.h mi.termios.c mi.wait.h mi.varargs.h +SRCS+= tw.decls.h tw.h tw.help.c tw.init.c tw.parse.c tw.spell.c \ + tw.comp.c tw.color.c +SRCS+= ed.chared.c ed.decls.h ed.defns.c ed.h ed.init.c ed.inputl.c \ + ed.refresh.c ed.screen.c ed.xmap.c ed.term.c ed.term.h +SRCS+= tc.alloc.c tc.bind.c tc.const.c tc.decls.h tc.disc.c \ + tc.func.c tc.os.c tc.os.h tc.printf.c tc.prompt.c \ + tc.sched.c tc.sig.c tc.sig.h tc.str.c sh.types.h tc.vers.c tc.wait.h \ + tc.who.c tc.h +SRCS+= tc.defs.c +GENHDRS= ed.defns.h sh.err.h tc.const.h +SRCS+= ${GENHDRS} + +MAN1= csh.1 +MLINKS= csh.1 tcsh.1 +# MLINKS for Shell built in commands for which there are no userland +# utilities of the same name are handled with the associated manpage, +# builtin.1 in share/man/man1/. + +DPADD= ${LIBTERMCAP} ${LIBCRYPT} +LDADD= -ltermcap -lcrypt + +LINKS= ${BINDIR}/csh ${BINDIR}/tcsh + +CLEANFILES= ${GENHDRS} csh.1 + +csh.1: tcsh.man + ln -sf ${.ALLSRC} ${.TARGET} + +ed.defns.h: ed.defns.c + @rm -f ${.TARGET} + @echo '/* Do not edit this file, make creates it. */' > ${.TARGET} + @echo '#ifndef _h_ed_defns' >> ${.TARGET} + @echo '#define _h_ed_defns' >> ${.TARGET} + grep '[FV]_' ${TCSHDIR}/ed.defns.c | grep '^#define' >> ${.TARGET} + @echo '#endif /* _h_ed_defns */' >> ${.TARGET} + +sh.err.h: sh.err.c + @rm -f ${.TARGET} + @echo '/* Do not edit this file, make creates it. */' > ${.TARGET} + @echo '#ifndef _h_sh_err' >> ${.TARGET} + @echo '#define _h_sh_err' >> ${.TARGET} + egrep 'ERR_' ${.ALLSRC} | egrep '^#define' >> ${.TARGET} + @echo '#endif /* _h_sh_err */' >> ${.TARGET} + +tc.const.h: tc.const.c sh.char.h config.h config_f.h sh.types.h sh.err.h + @rm -f ${.TARGET} + @echo '/* Do not edit this file, make creates it. */' > ${.TARGET} + @echo '#ifndef _h_tc_const' >> ${.TARGET} + @echo '#define _h_tc_const' >> ${.TARGET} + ${CC} -E ${CFLAGS} ${.ALLSRC} -D_h_tc_const | egrep 'Char STR' | \ + sed -e 's/Char \([a-zA-Z0-9_]*\)\(.*\)/extern Char \1[];/' | \ + sort >> ${.TARGET} + @echo '#endif /* _h_tc_const */' >> ${.TARGET} + +.include <bsd.prog.mk> diff --git a/bin/csh/USD.doc/Makefile b/bin/csh/USD.doc/Makefile new file mode 100644 index 0000000..e067417 --- /dev/null +++ b/bin/csh/USD.doc/Makefile @@ -0,0 +1,8 @@ +# @(#)Makefile 8.1 (Berkeley) 8/14/93 +# $FreeBSD$ + +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..db6d468 --- /dev/null +++ b/bin/csh/USD.doc/csh.1 @@ -0,0 +1,1015 @@ +.\" 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 +.\" $FreeBSD$ +.\" +.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 an 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..043b73f --- /dev/null +++ b/bin/csh/USD.doc/csh.2 @@ -0,0 +1,1307 @@ +.\" 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 +.\" $FreeBSD$ +.\" +.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 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..083fbcc --- /dev/null +++ b/bin/csh/USD.doc/csh.3 @@ -0,0 +1,652 @@ +.\" 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 +.\" $FreeBSD$ +.\" +.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..8a2bcbd --- /dev/null +++ b/bin/csh/USD.doc/csh.4 @@ -0,0 +1,179 @@ +.\" 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 +.\" $FreeBSD$ +.\" +.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..176534f --- /dev/null +++ b/bin/csh/USD.doc/csh.a @@ -0,0 +1,96 @@ +.\" 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 +.\" $FreeBSD$ +.\" +.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..4115173 --- /dev/null +++ b/bin/csh/USD.doc/csh.g @@ -0,0 +1,1722 @@ +.\" 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 +.\" $FreeBSD$ +.\" +.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..7066e07 --- /dev/null +++ b/bin/csh/USD.doc/tabs @@ -0,0 +1,35 @@ +.\" 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 +.\" $FreeBSD$ +.\" +.ta 5n 10n 15n 20n 25n 30n 35n 40n 45n 50n 55n 60n 65n 70n 75n 80n diff --git a/bin/csh/config.h b/bin/csh/config.h new file mode 100644 index 0000000..836e5d4 --- /dev/null +++ b/bin/csh/config.h @@ -0,0 +1,160 @@ +/* config.h. Generated automatically by configure. */ +/* + * config.h -- configure various defines for tcsh + * + * All source files should #include this FIRST. + * + * Edit this to match your system type. + */ + +/* $FreeBSD$ */ + +#ifndef _h_config +#define _h_config +/****************** System dependant compilation flags ****************/ +/* + * POSIX This system supports IEEE Std 1003.1-1988 (POSIX). + */ +#define POSIX + +/* + * POSIXJOBS This system supports the optional IEEE Std 1003.1-1988 (POSIX) + * job control facilities. + */ +#define POSIXJOBS + +/* + * POSIXSIGS Use the POSIX signal facilities to emulate BSD signals. + */ +/* #undef POSIXSIGS */ + +/* + * VFORK This machine has a vfork(). + * It used to be that for job control to work, this define + * was mandatory. This is not the case any more. + * If you think you still need it, but you don't have vfork, + * define this anyway and then do #define vfork fork. + * I do this anyway on a Sun because of yellow pages brain damage, + * [should not be needed under 4.1] + * and on the iris4d cause SGI's fork is sufficiently "virtual" + * that vfork isn't necessary. (Besides, SGI's vfork is weird). + * Note that some machines eg. rs6000 have a vfork, but not + * with the berkeley semantics, so we cannot use it there either. + */ +#define VFORK + +/* + * BSDJOBS You have BSD-style job control (both process groups and + * a tty that deals correctly + */ +#define BSDJOBS + +/* + * BSDSIGS You have 4.2-style signals, rather than USG style. + * Note: POSIX systems should not define this unless they + * have sigvec() and friends (ie: 4.3BSD-RENO, HP-UX). + */ +#define BSDSIGS + +/* + * BSDTIMES You have BSD-style process time stuff (like rusage) + * This may or may not be true. For example, Apple Unix + * (OREO) has BSDJOBS and BSDSIGS but not BSDTIMES. + */ +#define BSDTIMES + +/* + * BSDLIMIT You have BSD-style resource limit stuff (getrlimit/setrlimit) + */ +#define BSDLIMIT + +/* + * BSDNICE Your system uses setpriority() instead of nice, to + * change a processes scheduling priority + */ +#define BSDNICE + +/* + * TERMIO You have struct termio instead of struct sgttyb. + * This is usually the case for SYSV systems, where + * BSD uses sgttyb. POSIX systems should define this + * anyway, even though they use struct termios. + */ +#define TERMIO + +/* + * SYSVREL Your machine is SYSV based (HPUX, A/UX) + * NOTE: don't do this if you are on a Pyramid -- tcsh is + * built in a BSD universe. + * Set SYSVREL to 1, 2, 3, or 4, depending the version of System V + * you are running. Or set it to 0 if you are not SYSV based + */ +#define SYSVREL 0 + +/* + * YPBUGS Work around Sun YP bugs that cause expansion of ~username + * to send command output to /dev/null + */ +/* #undef YPBUGS */ + +/* + * SIGVOID Define this if your signal handlers return void. On older + * systems, signal returns int, but on newer ones, it returns void. + */ +#define SIGVOID + +/* + * HAVEDUP2 Define this if your system supports dup2(). + */ +#define HAVEDUP2 + +/* + * UTHOST Does the utmp file have a host field? + */ +#define UTHOST + +/* + * DIRENT Your system has <dirent.h> instead of <sys/dir.h> + */ +#define DIRENT +/****************** local defines *********************/ +/****************** configurable hacks ****************/ + +#include <stdlib.h> + +/* have been moved to config_f.h */ +#include "config_f.h" + +#if defined(__FreeBSD__) +#define NLS_BUGS +/* we want to use the system malloc when we install as /bin/csh */ +#define SYSMALLOC +#endif + +#if defined(__bsdi__) +/* + * _PATH_TCSHELL if you've change the installation location (vix) + */ +# ifdef _BSDI_VERSION >= 199701 +# define _PATH_TCSHELL "/bin/tcsh" +/* # undef SYSMALLOC */ +# define SYSMALLOC +# else +# define _PATH_TCSHELL "/usr/contrib/bin/tcsh" +# endif + +# undef NLS +# undef NLS_CATALOGS + +#elif defined(__APPLE__) +# define SYSMALLOC + +#else +# define NLS_CATALOGS +#endif + +#define KANJI +#define DSPMBYTE + +#endif /* _h_config */ +/* config.h.in. Generated automatically from configure.in by autoheader. */ diff --git a/bin/csh/config_p.h b/bin/csh/config_p.h new file mode 100644 index 0000000..836e5d4 --- /dev/null +++ b/bin/csh/config_p.h @@ -0,0 +1,160 @@ +/* config.h. Generated automatically by configure. */ +/* + * config.h -- configure various defines for tcsh + * + * All source files should #include this FIRST. + * + * Edit this to match your system type. + */ + +/* $FreeBSD$ */ + +#ifndef _h_config +#define _h_config +/****************** System dependant compilation flags ****************/ +/* + * POSIX This system supports IEEE Std 1003.1-1988 (POSIX). + */ +#define POSIX + +/* + * POSIXJOBS This system supports the optional IEEE Std 1003.1-1988 (POSIX) + * job control facilities. + */ +#define POSIXJOBS + +/* + * POSIXSIGS Use the POSIX signal facilities to emulate BSD signals. + */ +/* #undef POSIXSIGS */ + +/* + * VFORK This machine has a vfork(). + * It used to be that for job control to work, this define + * was mandatory. This is not the case any more. + * If you think you still need it, but you don't have vfork, + * define this anyway and then do #define vfork fork. + * I do this anyway on a Sun because of yellow pages brain damage, + * [should not be needed under 4.1] + * and on the iris4d cause SGI's fork is sufficiently "virtual" + * that vfork isn't necessary. (Besides, SGI's vfork is weird). + * Note that some machines eg. rs6000 have a vfork, but not + * with the berkeley semantics, so we cannot use it there either. + */ +#define VFORK + +/* + * BSDJOBS You have BSD-style job control (both process groups and + * a tty that deals correctly + */ +#define BSDJOBS + +/* + * BSDSIGS You have 4.2-style signals, rather than USG style. + * Note: POSIX systems should not define this unless they + * have sigvec() and friends (ie: 4.3BSD-RENO, HP-UX). + */ +#define BSDSIGS + +/* + * BSDTIMES You have BSD-style process time stuff (like rusage) + * This may or may not be true. For example, Apple Unix + * (OREO) has BSDJOBS and BSDSIGS but not BSDTIMES. + */ +#define BSDTIMES + +/* + * BSDLIMIT You have BSD-style resource limit stuff (getrlimit/setrlimit) + */ +#define BSDLIMIT + +/* + * BSDNICE Your system uses setpriority() instead of nice, to + * change a processes scheduling priority + */ +#define BSDNICE + +/* + * TERMIO You have struct termio instead of struct sgttyb. + * This is usually the case for SYSV systems, where + * BSD uses sgttyb. POSIX systems should define this + * anyway, even though they use struct termios. + */ +#define TERMIO + +/* + * SYSVREL Your machine is SYSV based (HPUX, A/UX) + * NOTE: don't do this if you are on a Pyramid -- tcsh is + * built in a BSD universe. + * Set SYSVREL to 1, 2, 3, or 4, depending the version of System V + * you are running. Or set it to 0 if you are not SYSV based + */ +#define SYSVREL 0 + +/* + * YPBUGS Work around Sun YP bugs that cause expansion of ~username + * to send command output to /dev/null + */ +/* #undef YPBUGS */ + +/* + * SIGVOID Define this if your signal handlers return void. On older + * systems, signal returns int, but on newer ones, it returns void. + */ +#define SIGVOID + +/* + * HAVEDUP2 Define this if your system supports dup2(). + */ +#define HAVEDUP2 + +/* + * UTHOST Does the utmp file have a host field? + */ +#define UTHOST + +/* + * DIRENT Your system has <dirent.h> instead of <sys/dir.h> + */ +#define DIRENT +/****************** local defines *********************/ +/****************** configurable hacks ****************/ + +#include <stdlib.h> + +/* have been moved to config_f.h */ +#include "config_f.h" + +#if defined(__FreeBSD__) +#define NLS_BUGS +/* we want to use the system malloc when we install as /bin/csh */ +#define SYSMALLOC +#endif + +#if defined(__bsdi__) +/* + * _PATH_TCSHELL if you've change the installation location (vix) + */ +# ifdef _BSDI_VERSION >= 199701 +# define _PATH_TCSHELL "/bin/tcsh" +/* # undef SYSMALLOC */ +# define SYSMALLOC +# else +# define _PATH_TCSHELL "/usr/contrib/bin/tcsh" +# endif + +# undef NLS +# undef NLS_CATALOGS + +#elif defined(__APPLE__) +# define SYSMALLOC + +#else +# define NLS_CATALOGS +#endif + +#define KANJI +#define DSPMBYTE + +#endif /* _h_config */ +/* config.h.in. Generated automatically from configure.in by autoheader. */ diff --git a/bin/csh/nls/Makefile b/bin/csh/nls/Makefile new file mode 100644 index 0000000..9f1e4c53 --- /dev/null +++ b/bin/csh/nls/Makefile @@ -0,0 +1,8 @@ +# $FreeBSD$ + +SUBDIR= finnish french ja german italian spanish + +# We are not ready for greek yet +#SUBDIR+= greek + +.include <bsd.subdir.mk> diff --git a/bin/csh/nls/Makefile.inc b/bin/csh/nls/Makefile.inc new file mode 100644 index 0000000..b9396f8 --- /dev/null +++ b/bin/csh/nls/Makefile.inc @@ -0,0 +1,17 @@ +# $FreeBSD$ + +GENCAT= gencat -new +BASESRC= ${.CURDIR}/../../../../contrib/tcsh/nls + +.SUFFIXES: .msg .cat + +.msg.cat: + ${GENCAT} ${.TARGET} ${.IMPSRC} + +CLEANFILES+= tcsh.cat tcsh.msg + +all: tcsh.cat + +tcsh.cat: tcsh.msg + +distribute: diff --git a/bin/csh/nls/finnish/Makefile b/bin/csh/nls/finnish/Makefile new file mode 100644 index 0000000..5c7c3fd --- /dev/null +++ b/bin/csh/nls/finnish/Makefile @@ -0,0 +1,20 @@ +# $FreeBSD$ + +NL= finnish +DL= fi_FI.ISO_8859-1 +LL= fi_FI.DIS_8859-15 + +.PATH: ${BASESRC}/${NL} + +tcsh.msg: set[0-9]* + cat ${BASESRC}/${NL}/set[0-9] \ + ${BASESRC}/${NL}/set[0-9][0-9] > ${.TARGET} + +install: + ${INSTALL} ${COPY} -o ${BINOWN} -g ${BINGRP} -m ${NOBINMODE} \ + tcsh.cat ${DESTDIR}${NLSDIR}/${DL}/tcsh.cat +.for i in ${LL} + ln -fs ../${DL}/tcsh.cat ${DESTDIR}${NLSDIR}/$i/tcsh.cat +.endfor + +.include <bsd.prog.mk> diff --git a/bin/csh/nls/french/Makefile b/bin/csh/nls/french/Makefile new file mode 100644 index 0000000..9a3305f --- /dev/null +++ b/bin/csh/nls/french/Makefile @@ -0,0 +1,22 @@ +# $FreeBSD$ + +NL= french +DL= fr_FR.ISO_8859-1 +LL= fr_BE.ISO_8859-1 fr_CA.ISO_8859-1 fr_CH.ISO_8859-1 \ + fr_BE.DIS_8859-15 fr_CA.DIS_8859-15 fr_CH.DIS_8859-15 \ + fr_FR.DIS_8859-15 + +.PATH: ${BASESRC}/${NL} + +tcsh.msg: set[0-9]* + cat ${BASESRC}/${NL}/set[0-9] \ + ${BASESRC}/${NL}/set[0-9][0-9] > ${.TARGET} + +install: + ${INSTALL} ${COPY} -o ${BINOWN} -g ${BINGRP} -m ${NOBINMODE} \ + tcsh.cat ${DESTDIR}${NLSDIR}/${DL}/tcsh.cat +.for i in ${LL} + ln -fs ../${DL}/tcsh.cat ${DESTDIR}${NLSDIR}/$i/tcsh.cat +.endfor + +.include <bsd.prog.mk> diff --git a/bin/csh/nls/german/Makefile b/bin/csh/nls/german/Makefile new file mode 100644 index 0000000..5e126da --- /dev/null +++ b/bin/csh/nls/german/Makefile @@ -0,0 +1,21 @@ +# $FreeBSD$ + +NL= german +DL= de_DE.ISO_8859-1 +LL= de_AT.ISO_8859-1 de_CH.ISO_8859-1 de_AT.DIS_8859-15 \ + de_CH.DIS_8859-15 de_DE.DIS_8859-15 + +.PATH: ${BASESRC}/${NL} + +tcsh.msg: set[0-9]* + cat ${BASESRC}/${NL}/set[0-9] \ + ${BASESRC}/${NL}/set[0-9][0-9] > ${.TARGET} + +install: + ${INSTALL} ${COPY} -o ${BINOWN} -g ${BINGRP} -m ${NOBINMODE} \ + tcsh.cat ${DESTDIR}${NLSDIR}/${DL}/tcsh.cat +.for i in ${LL} + ln -fs ../${DL}/tcsh.cat ${DESTDIR}${NLSDIR}/$i/tcsh.cat +.endfor + +.include <bsd.prog.mk> diff --git a/bin/csh/nls/greek/Makefile b/bin/csh/nls/greek/Makefile new file mode 100644 index 0000000..9826f03 --- /dev/null +++ b/bin/csh/nls/greek/Makefile @@ -0,0 +1,16 @@ +# $FreeBSD$ + +NL= greek +DL= el_GR.ISO_8859-7 + +.PATH: ${BASESRC}/${NL} + +tcsh.msg: set[0-9]* + cat ${BASESRC}/${NL}/set[0-9] \ + ${BASESRC}/${NL}/set[0-9][0-9] > ${.TARGET} + +install: + ${INSTALL} ${COPY} -o ${BINOWN} -g ${BINGRP} -m ${NOBINMODE} \ + tcsh.cat ${DESTDIR}${NLSDIR}/${DL}/tcsh.cat + +.include <bsd.prog.mk> diff --git a/bin/csh/nls/italian/Makefile b/bin/csh/nls/italian/Makefile new file mode 100644 index 0000000..bed7d8a --- /dev/null +++ b/bin/csh/nls/italian/Makefile @@ -0,0 +1,20 @@ +# $FreeBSD$ + +NL= italian +DL= it_IT.ISO_8859-1 +LL= it_CH.ISO_8859-1 it_IT.DIS_8859-15 it_CH.DIS_8859-15 + +.PATH: ${BASESRC}/${NL} + +tcsh.msg: set[0-9]* + cat ${BASESRC}/${NL}/set[0-9] \ + ${BASESRC}/${NL}/set[0-9][0-9] > ${.TARGET} + +install: + ${INSTALL} ${COPY} -o ${BINOWN} -g ${BINGRP} -m ${NOBINMODE} \ + tcsh.cat ${DESTDIR}${NLSDIR}/${DL}/tcsh.cat +.for i in ${LL} + ln -fs ../${DL}/tcsh.cat ${DESTDIR}${NLSDIR}/$i/tcsh.cat +.endfor + +.include <bsd.prog.mk> diff --git a/bin/csh/nls/ja/Makefile b/bin/csh/nls/ja/Makefile new file mode 100644 index 0000000..ed72335 --- /dev/null +++ b/bin/csh/nls/ja/Makefile @@ -0,0 +1,16 @@ +# $FreeBSD$ + +NL= ja +DL= ja_JP.EUC + +.PATH: ${BASESRC}/${NL} + +tcsh.msg: set[0-9]* + cat ${BASESRC}/${NL}/set[0-9] \ + ${BASESRC}/${NL}/set[0-9][0-9] > ${.TARGET} + +install: + ${INSTALL} ${COPY} -o ${BINOWN} -g ${BINGRP} -m ${NOBINMODE} \ + tcsh.cat ${DESTDIR}${NLSDIR}/${DL}/tcsh.cat + +.include <bsd.prog.mk> diff --git a/bin/csh/nls/spanish/Makefile b/bin/csh/nls/spanish/Makefile new file mode 100644 index 0000000..f6125f4 --- /dev/null +++ b/bin/csh/nls/spanish/Makefile @@ -0,0 +1,20 @@ +# $FreeBSD$ + +NL= spanish +DL= es_ES.ISO_8859-1 +LL= es_ES.DIS_8859-15 + +.PATH: ${BASESRC}/${NL} + +tcsh.msg: set[0-9]* + cat ${BASESRC}/${NL}/set[0-9] \ + ${BASESRC}/${NL}/set[0-9][0-9] > ${.TARGET} + +install: + ${INSTALL} ${COPY} -o ${BINOWN} -g ${BINGRP} -m ${NOBINMODE} \ + tcsh.cat ${DESTDIR}${NLSDIR}/${DL}/tcsh.cat +.for i in ${LL} + ln -fs ../${DL}/tcsh.cat ${DESTDIR}${NLSDIR}/$i/tcsh.cat +.endfor + +.include <bsd.prog.mk> diff --git a/bin/csh/tc.defs.c b/bin/csh/tc.defs.c new file mode 100644 index 0000000..484e72b --- /dev/null +++ b/bin/csh/tc.defs.c @@ -0,0 +1,2709 @@ +/* $FreeBSD$ */ + +/* Do not edit this file, make creates it */ +/* $Header: /src/pub/tcsh/host.defs,v 1.25 1999/04/20 07:48:41 christos Exp $ */ +/* + * host.defs: Hosttype/Machtype etc. + */ +/*- + * Copyright (c) 1980, 1991 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. + */ +#include "sh.h" + +RCSID("$Id: host.defs,v 1.25 1999/04/20 07:48:41 christos Exp $") + + + +#if (defined(mips) || defined(__mips)) && (defined(MIPSEL) || defined(__MIPSEL)) +# define M_mipsel +#endif + + +#if (defined(mips) || defined(__mips)) && (defined(MIPSEB) || defined(__MIPSEB)) +# define M_mipseb +#endif + + +#if (defined(i386) || defined(__i386__)) +# define M_i386 +#endif + + +#if (defined(i486) || defined(__i486__)) +# define M_i486 +#endif + + +#if (defined(i586) || defined(__i586__)) +# define M_i586 +#endif + + +#if (defined(M_i386) || defined(M_i486) || defined(M_i586)) +# define M_intel +#endif + + + + +#if defined(ns32000) +static char * +isamultimax(flag) + int flag; +{ + if (access("/Umax.image", F_OK) == 0) + return "multimax"; + else + return flag ? "mach" : "ns32000"; +} +#endif + + + + +#if defined(cray) +/* + * On crays, find the current machine type via the target() syscall + * We need ctype.h to convert the name returned to lower case + */ +# include <sys/target.h> +# include <ctype.h> +# include <string.h> + +/* From: hpa@hook.eecs.nwu.edu (H. Peter Anvin) */ +static char * +getcray() +{ +# ifdef MC_GET_SYSTEM /* If we have target() */ + struct target data; + + if (target(MC_GET_SYSTEM, &data) != -1) { + static char hosttype_buf[sizeof(data.mc_pmt)+1]; + char *p = (char *) &(data.mc_pmt); + char *q = hosttype_buf; + int n; + + /* + * Copy to buffer and convert to lower case + * String may not be null-terminated, so keep a counter + */ + for (n = 0; *p && n < sizeof(data.mc_pmt); n++) + *q++ = tolower(p[n]); + + *q = '\0'; + + /* replace dashes with underscores if present */ + while ((q = strchr(hosttype_buf, '-')) != NULL) + *q = '_'; + return hosttype_buf; /* Return in static buffer */ + } + else +# endif /* MC_GET_SYSTEM */ + return "cray"; /* target() failed */ +} +#endif + + + + +#if defined(convex) +/* + * On convex, find the current machine type via the getsysinfo() syscall + */ +#include <sys/sysinfo.h> + +/* From: fox@convex.com (David DeSimone) */ +static char * +getconvex() +{ + struct system_information sysinfo; + static char result[8]; + + if (getsysinfo(SYSINFO_SIZE, &sysinfo) == -1) + return "convex"; + + switch(sysinfo.cpu_type) { +#ifdef SI_CPUTYPE_C1 + case SI_CPUTYPE_C1: + return "c1"; +#endif + +#ifdef SI_CPUTYPE_C2 + case SI_CPUTYPE_C2: + return "c2"; +#endif + +#ifdef SI_CPUTYPE_C2MP + case SI_CPUTYPE_C2MP: + (void) strcpy(result, "c2X0"); + result[2] = sysinfo.cpu_count + '0'; + return result; +#endif + +#ifdef SI_CPUTYPE_C34 + case SI_CPUTYPE_C34: + (void) strcpy(result, "c34X0"); + result[3] = sysinfo.cpu_count + '0'; + return result; +#endif + +#ifdef SI_CPUTYPE_C38 + case SI_CPUTYPE_C38: + (void) strcpy(result, "c38X0"); + result[3] = sysinfo.cpu_count + '0'; + return result; +#endif + +#ifdef SI_CPUTYPE_C46 + case SI_CPUTYPE_C46: + (void) strcpy(result, "c46X0"); + result[3] = sysinfo.cpu_count + '0'; + return result; +#endif + + default: + return "convex"; + } +} +#endif + + +void +getmachine() +{ + char *hosttype; + char *ostype; + char *vendor; + char *machtype; + + + + + +#if defined(__PARAGON__) + /* Intel Paragon running OSF/1 */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "intel"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "paragon"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "osf1"; +# endif +# if (defined(M_i386)) && !defined(_machtype_) +# define _machtype_ + machtype = "i386"; +# endif +#endif + + + + +#if defined(AMIX) + /* Amiga running Amix 2.02 */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "commodore"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "amiga"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "Amix"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "m68k"; +# endif +#endif + + + + +#if defined(accel) + /* celerity Accel */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "celerity"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "celerityACCEL"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "unix"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "accel"; +# endif +#endif + + + + +#if defined(_VMS_POSIX) + /* digital vax or alpha running vms posix */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "dec"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "VMS-POSIX"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "vms"; +# endif +# if (defined(__alpha)) && !defined(_machtype_) +# define _machtype_ + machtype = "alpha"; +# endif +# if (defined(__vax) || defined(vax)) && !defined(_machtype_) +# define _machtype_ + machtype = "vax"; +# endif +# if (defined(__vax__)) && !defined(_machtype_) +# define _machtype_ + machtype = "vax"; +# endif +#endif + + + + +#if defined(__hp_osf) + /* Hewlett Packard running OSF/1 */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "hp"; +# endif +# if (defined(__pa_risc)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "hp9000s700-osf1"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "hp-osf1"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "osf1"; +# endif +# if (defined(__pa_risc)) && !defined(_machtype_) +# define _machtype_ + machtype = "pa_risc"; +# endif +#endif + + + + +#if defined(hp9000) + /* Hewlett Packard running MORE/bsd */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "hp"; +# endif +# if (defined(hp300)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "hp300"; +# endif +# if (defined(hp800)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "hp800"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "hp9000"; +# endif +# if (defined(BSD4_4)) && !defined(_ostype_) +# define _ostype_ + ostype = "bsd44"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "mtXinu"; +# endif +# if (defined(hp300)) && !defined(_machtype_) +# define _machtype_ + machtype = "m68k"; +# endif +# if (defined(hp800)) && !defined(_machtype_) +# define _machtype_ + machtype = "pa_risc"; +# endif +#endif + + + + +#if defined(hpux) || defined(__hpux) + /* Hewlett Packard running HP/UX */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "hp"; +# endif +# if (defined(__hp9000s700)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "hp9000s700"; +# endif +# if (defined(__hp9000s800) || defined(hp9000s800)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "hp9000s800"; +# endif +# if (defined(hp9000s500)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "hp9000s500"; +# endif +# if (defined(__hp9000s300) || defined(hp9000s300)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "hp9000s300"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "hp"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "hpux"; +# endif +# if (defined(__hp9000s700)) && !defined(_machtype_) +# define _machtype_ + machtype = "pa_risc"; +# endif +# if (defined(__hp9000s800) || defined(hp9000s800)) && !defined(_machtype_) +# define _machtype_ + machtype = "pa_risc"; +# endif +# if (defined(hp9000s500)) && !defined(_machtype_) +# define _machtype_ + machtype = "m68k"; +# endif +# if (defined(__hp9000s300) || defined(hp9000s300)) && !defined(_machtype_) +# define _machtype_ + machtype = "m68k"; +# endif +#endif + + + + +#if defined(apollo) + /* Hewlett Packard apollo running Domain/OS */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "hp"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "apollo"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "DomainOS"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "m68k"; +# endif +#endif + + + + +#if defined(sun) || defined(__sun__) + /* Sun Microsystems series 2 workstation (68010 based) */ + /* Sun Microsystems series 3 workstation (68020 based) */ + /* Sun Microsystems 386i workstation (386 based) */ + /* Sun Microsystems series 4 workstation (SPARC based) */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "sun"; +# endif +# if (defined(M_i386) && !defined(__SVR4)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "sun386i"; +# endif +# if (defined(M_i386) && defined(__SVR4)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "i86pc"; +# endif +# if (defined(mc68010) || defined(__mc68010__)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "sun2"; +# endif +# if (defined(mc68020) || defined(__mc68020__)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "sun3"; +# endif +# if (defined(sparc) || defined(__sparc__)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "sun4"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "sun"; +# endif +# if (defined(SUNOS3)) && !defined(_ostype_) +# define _ostype_ + ostype = "sunos3"; +# endif +# if (defined(SUNOS4)) && !defined(_ostype_) +# define _ostype_ + ostype = "sunos4"; +# endif +# if (defined(SOLARIS2)) && !defined(_ostype_) +# define _ostype_ + ostype = "solaris"; +# endif +# if (defined(mc68010) || defined(__mc68010__)) && !defined(_machtype_) +# define _machtype_ + machtype = "m68k"; +# endif +# if (defined(mc68020) || defined(__mc68020__)) && !defined(_machtype_) +# define _machtype_ + machtype = "m68k"; +# endif +# if (defined(sparc) || defined(__sparc__)) && !defined(_machtype_) +# define _machtype_ + machtype = "sparc"; +# endif +# if (defined(M_i386)) && !defined(_machtype_) +# define _machtype_ + machtype = "i386"; +# endif +#endif + + + + +#if defined(pyr) + /* Pyramid Technology */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "pyramid"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "pyramid"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "pyramid"; +# endif +#endif + + + + +#if defined(hcx) || defined(_CX_UX) + /* Harris Tahoe running CX/UX */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "harris"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "hcx"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "hcx"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "tahoe"; +# endif +#endif + + + + +#if defined(tahoe) + /* Harris Tahoe */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "harris"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "tahoe"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "tahoe"; +# endif +#endif + + + + +#if defined(ibm032) + /* RT running IBM AOS4.3 or MACH */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "ibm"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "rt"; +# endif +# if (defined(MACH)) && !defined(_ostype_) +# define _ostype_ + ostype = "mach"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "aos"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "ibm032"; +# endif +#endif + + + + +#if defined(aiws) + /* RT running IBM aix2.x */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "ibm"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "rtpc"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "aix"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "ibm032"; +# endif +#endif + + + + +#if defined(_AIX370) + /* IBM/370 running aix */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "ibm"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "aix370"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "aix"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "ibm370"; +# endif +#endif + + + + +#if defined(_IBMESA) + /* IBM/ESA running aix */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "ibm"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "aixESA"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "aix"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "esa"; +# endif +#endif + + + + +#if defined(_IBMR2) + /* IBM/RS6000 running aix */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "ibm"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "rs6000"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "aix"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "rs6000"; +# endif +#endif + + + + +#if defined(_AIXPS2) + /* IBM/PS2 running aix */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "ibm"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "ps2"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "aix"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "i386"; +# endif +#endif + + + + +#if defined(OREO) + /* Macintosh running AU/X */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "apple"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "mac2"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "aux"; +# endif +# if (defined(mc68020)) && !defined(_machtype_) +# define _machtype_ + machtype = "m68k"; +# endif +#endif + + + + +#if defined(u3b20d) + /* AT&T 3B/20 series running SVR2/3 */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "att"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "att3b20"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "u3b20"; +# endif +#endif + + + + +#if defined(u3b15) + /* AT&T 3B/15 series running SVR2/3 */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "att"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "att3b15"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "u3b15"; +# endif +#endif + + + + +#if defined(u3b5) + /* AT&T 3B/5 series running SVR2/3 */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "att"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "att3b5"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "u3b5"; +# endif +#endif + + + + +#if defined(u3b2) + /* AT&T 3B/2 series running SVR2/3 */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "att"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "att3b2"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "u3b2"; +# endif +#endif + + + + +#if defined(UNIXPC) + /* AT&T UnixPC att3b1/att7300 */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "att"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "unixpc"; +# endif +# if (defined(u3b1)) && !defined(_machtype_) +# define _machtype_ + machtype = "u3b1"; +# endif +# if (defined(att7300)) && !defined(_machtype_) +# define _machtype_ + machtype = "att7300"; +# endif +#endif + + + + +#if defined(_MINIX) + /* Andy Tanenbaum's minix */ +# if (defined(M_i386)) && !defined(_vendor_) +# define _vendor_ + vendor = "intel"; +# endif +# if (defined(M_i386)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "minix386"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "minix"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "minix"; +# endif +# if (defined(M_i386)) && !defined(_machtype_) +# define _machtype_ + machtype = "i386"; +# endif +#endif + + + + +#if defined(linux) + /* Linus Torvalds's linux */ +# if (defined(M_intel)) && !defined(_vendor_) +# define _vendor_ + vendor = "intel"; +# endif +# if (defined(M_i586)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "i586-linux"; +# endif +# if (defined(M_i486)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "i486-linux"; +# endif +# if (defined(M_i386)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "i386-linux"; +# endif +# if (!defined(PPC)) && !defined(_ostype_) +# define _ostype_ + ostype = "linux"; +# endif +# if (defined(PPC)) && !defined(_ostype_) +# define _ostype_ + ostype = "mklinux"; +# endif +# if (defined(M_i586)) && !defined(_machtype_) +# define _machtype_ + machtype = "i586"; +# endif +# if (defined(M_i486)) && !defined(_machtype_) +# define _machtype_ + machtype = "i486"; +# endif +# if (defined(M_i386)) && !defined(_machtype_) +# define _machtype_ + machtype = "i386"; +# endif +# if (defined(__alpha)) && !defined(_vendor_) +# define _vendor_ + vendor = "dec"; +# endif +# if (defined(PPC)) && !defined(_vendor_) +# define _vendor_ + vendor = "apple"; +# endif +# if (defined(__alpha)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "alpha"; +# endif +# if (defined(PPC)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "powerpc"; +# endif +# if (defined(__alpha)) && !defined(_machtype_) +# define _machtype_ + machtype = "alpha"; +# endif +# if (defined(PPC)) && !defined(_machtype_) +# define _machtype_ + machtype = "powerpc"; +# endif +#endif + + + + +#if defined(__EMX__) + /* OS/2 EMX [unix emulation under OS/2] */ +# if (defined(M_intel)) && !defined(_vendor_) +# define _vendor_ + vendor = "intel"; +# endif +# if (defined(M_i386)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "i386-emx"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "os2"; +# endif +# if (defined(M_i386)) && !defined(_machtype_) +# define _machtype_ + machtype = "i386"; +# endif +#endif + + + + +#if defined(__NetBSD__) + /* NetBSD */ +# if (defined(arm32)) && !defined(_vendor_) +# define _vendor_ + vendor = "acorn"; +# endif +# if (defined(alpha)) && !defined(_vendor_) +# define _vendor_ + vendor = "digital"; +# endif +# if (defined(amiga)) && !defined(_vendor_) +# define _vendor_ + vendor = "commodore"; +# endif +# if (defined(atari)) && !defined(_vendor_) +# define _vendor_ + vendor = "atari"; +# endif +# if (defined(hp300)) && !defined(_vendor_) +# define _vendor_ + vendor = "hp"; +# endif +# if (defined(M_intel)) && !defined(_vendor_) +# define _vendor_ + vendor = "intel"; +# endif +# if (defined(m68k)) && !defined(_vendor_) +# define _vendor_ + vendor = "motorola"; +# endif +# if (defined(mac68k)) && !defined(_vendor_) +# define _vendor_ + vendor = "apple"; +# endif +# if (defined(pc532)) && !defined(_vendor_) +# define _vendor_ + vendor = "national-semi"; +# endif +# if (defined(pmax) || defined(mips)) && !defined(_vendor_) +# define _vendor_ + vendor = "dec"; +# endif +# if (defined(M_mipsel)) && !defined(_vendor_) +# define _vendor_ + vendor = "dec"; +# endif +# if (defined(sparc)) && !defined(_vendor_) +# define _vendor_ + vendor = "sun"; +# endif +# if (defined(sun3)) && !defined(_vendor_) +# define _vendor_ + vendor = "sun"; +# endif +# if (defined(vax)) && !defined(_vendor_) +# define _vendor_ + vendor = "digital"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "NetBSD"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "NetBSD"; +# endif +# if (defined(arm32)) && !defined(_machtype_) +# define _machtype_ + machtype = "arm32"; +# endif +# if (defined(sparc)) && !defined(_machtype_) +# define _machtype_ + machtype = "sparc"; +# endif +# if (defined(mc68020)) && !defined(_machtype_) +# define _machtype_ + machtype = "m68k"; +# endif +# if (defined(M_i386)) && !defined(_machtype_) +# define _machtype_ + machtype = "i386"; +# endif +# if (defined(M_mipsel)) && !defined(_machtype_) +# define _machtype_ + machtype = "mipsel"; +# endif +# if (defined(M_mipseb)) && !defined(_machtype_) +# define _machtype_ + machtype = "mipseb"; +# endif +# if (defined(mips)) && !defined(_machtype_) +# define _machtype_ + machtype = "mips"; +# endif +# if (defined(pc532)) && !defined(_machtype_) +# define _machtype_ + machtype = "pc532"; +# endif +# if (defined(vax)) && !defined(_machtype_) +# define _machtype_ + machtype = "vax"; +# endif +# if (defined(alpha)) && !defined(_machtype_) +# define _machtype_ + machtype = "alpha"; +# endif +#endif + + + + +#if defined(__FreeBSD__) + /* FreeBSD */ +# if (defined(__alpha)) && !defined(_vendor_) +# define _vendor_ + vendor = "digital"; +# endif +# if (defined(M_intel)) && !defined(_vendor_) +# define _vendor_ + vendor = "intel"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "FreeBSD"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "FreeBSD"; +# endif +# if (defined(__alpha)) && !defined(_machtype_) +# define _machtype_ + machtype = "alpha"; +# endif +# if (defined(M_i386)) && !defined(_machtype_) +# define _machtype_ + machtype = "i386"; +# endif +#endif + + + + +#if defined(__386BSD__) + /* Bill Jolitz's 386BSD */ +# if (defined(M_intel)) && !defined(_vendor_) +# define _vendor_ + vendor = "intel"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "386BSD"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "386BSD"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "i386"; +# endif +#endif + + + + +#if defined(bsdi) + /* BSDI's unix */ +# if (defined(M_intel)) && !defined(_vendor_) +# define _vendor_ + vendor = "intel"; +# endif +# if (defined(sparc)) && !defined(_vendor_) +# define _vendor_ + vendor = "sun"; +# endif +# if (defined(__powerpc__)) && !defined(_vendor_) +# define _vendor_ + vendor = "motorola"; +# endif +# if (defined(M_intel)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "bsd386"; +# endif +# if (defined(sparc)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "bsd-sparc"; +# endif +# if (defined(__powerpc__)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "bsd-powerpc"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "bsdi"; +# endif +# if (defined(M_i386)) && !defined(_machtype_) +# define _machtype_ + machtype = "i386"; +# endif +# if (defined(sparc)) && !defined(_machtype_) +# define _machtype_ + machtype = "sparc"; +# endif +# if (defined(__powerpc__)) && !defined(_machtype_) +# define _machtype_ + machtype = "powerpc"; +# endif +#endif + + + + +#if defined(COHERENT) + /* COHERENT's unix */ +# if (defined(_I386)) && !defined(_vendor_) +# define _vendor_ + vendor = "intel"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "coh386"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "coherent"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "coherent"; +# endif +# if (defined(_I386)) && !defined(_machtype_) +# define _machtype_ + machtype = "i386"; +# endif +#endif + + + +#if defined(SCO) + /* SCO UNIX System V/386 Release 3.2 */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "sco"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "sco386"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "sco_unix"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "i386"; +# endif +#endif + + + +#if defined(M_XENIX) && !defined(M_UNIX) + /* SCO XENIX */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "sco"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "sco_xenix"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "sco_xenix"; +# endif +# if (defined(M_I386)) && !defined(_machtype_) +# define _machtype_ + machtype = "i386"; +# endif +# if (defined(M_I286)) && !defined(_machtype_) +# define _machtype_ + machtype = "i286"; +# endif +#endif + + + + +#if defined(ISC) || defined(ISC202) + /* Interactive Unix */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "isc"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "isc386"; +# endif +# if (defined(POSIX)) && !defined(_ostype_) +# define _ostype_ + ostype = "POSIX"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "SVR3"; +# endif +# if (defined(M_i386)) && !defined(_machtype_) +# define _machtype_ + machtype = "i386"; +# endif +#endif + + + + +#if defined(INTEL) + /* Intel Unix */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "intel"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "intel386"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "intel_unix"; +# endif +# if (defined(M_i386)) && !defined(_machtype_) +# define _machtype_ + machtype = "i386"; +# endif +#endif + + + + +#if defined(MACH) + /* cmu's mach */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "cmu"; +# endif +# if (defined(M_i386)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "i386-mach"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "mach"; +# endif +# if (defined(M_i386)) && !defined(_machtype_) +# define _machtype_ + machtype = "i386"; +# endif +#endif + + + + +#if defined(alliant) + /* Alliants FSX */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "alliant"; +# endif +# if (defined(mc68000)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "alliant-fx80"; +# endif +# if (defined(i860)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "alliant-fx2800"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "alliant"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "fsx"; +# endif +# if (defined(mc68000)) && !defined(_machtype_) +# define _machtype_ + machtype = "mc68000"; +# endif +# if (defined(i860)) && !defined(_machtype_) +# define _machtype_ + machtype = "i860"; +# endif +#endif + + + + +#if defined(_FTX) + /* Stratus Computer, Inc FTX2 (i860 based) */ + /* Stratus Computer, Inc FTX3 (HPPA based) */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "stratus"; +# endif +# if (defined(i860) && defined(_FTX)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "atlantic"; +# endif +# if (defined(__hppa) && defined(_FTX)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "continuum"; +# endif +# if (defined(i860) && defined(_FTX)) && !defined(_ostype_) +# define _ostype_ + ostype = "ftx2"; +# endif +# if (defined(__hppa) && defined(_FTX)) && !defined(_ostype_) +# define _ostype_ + ostype = "ftx3"; +# endif +# if (defined(i860)) && !defined(_machtype_) +# define _machtype_ + machtype = "i860"; +# endif +# if (defined(__hppa)) && !defined(_machtype_) +# define _machtype_ + machtype = "hppa"; +# endif +#endif + + + + +#if defined(sequent) || defined(_SEQUENT_) + /* Sequent Balance (32000 based) */ + /* Sequent Symmetry running DYNIX/ptx (386/486 based) */ + /* Sequent Symmetry running DYNIX 3 (386/486 based) */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "sequent"; +# endif +# if (defined(M_i386) && defined(sequent)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "symmetry"; +# endif +# if (defined(M_i386)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "ptx"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "balance"; +# endif +# if (defined(M_i386) && !defined(sequent)) && !defined(_ostype_) +# define _ostype_ + ostype = "ptx"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "dynix3"; +# endif +# if (defined(M_i386)) && !defined(_machtype_) +# define _machtype_ + machtype = "i386"; +# endif +# if (defined(ns32000)) && !defined(_machtype_) +# define _machtype_ + machtype = "ns32000"; +# endif +#endif + + + + +#if defined(ns32000) + /* Encore Computer Corp. Multimax (32000 based) */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "encore"; +# endif +# if (defined(CMUCS)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "multimax"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = isamultimax(0); +# endif +# if (defined(CMUCS)) && !defined(_ostype_) +# define _ostype_ + ostype = "mach"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = isamultimax(1); +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "ns32000"; +# endif +#endif + + + + +#if defined(iconuxv) + /* Icon 88k running Unix */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "icon"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "icon"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "iconuxv"; +# endif +# if (defined(m88k) || defined(__m88k__)) && !defined(_machtype_) +# define _machtype_ + machtype = "m88k"; +# endif +#endif + + + + +#if defined(_CRAY) && defined(_CRAYCOM) + /* Cray Computer Corp. running CSOS */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "ccc"; +# endif +# if (defined(_CRAY2)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "cray"; +# endif +# if (defined(_CRAY3)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "cray"; +# endif +# if (defined(_CRAY4)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "cray"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "CSOS"; +# endif +# if (defined(_CRAY2)) && !defined(_machtype_) +# define _machtype_ + machtype = "cray2"; +# endif +# if (defined(_CRAY3)) && !defined(_machtype_) +# define _machtype_ + machtype = "cray3"; +# endif +# if (defined(_CRAY4)) && !defined(_machtype_) +# define _machtype_ + machtype = "cray4"; +# endif +#endif + + + + +#if defined(cray) && !defined(_CRAYMPP) + /* Cray Research Inc. PVP running UNICOS */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "cri"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = getcray(); +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "unicos"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = getcray(); +# endif +#endif + + + + +#if defined(cray) && defined(_CRAYT3D) + /* Cray Research Inc. running UNICOS MAX */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "cri"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = getcray(); +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "unicosmax"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = getcray(); +# endif +#endif + + + + +#if defined(cray) && defined(_CRAYT3E) + /* Cray Research Inc. running UNICOS/mk */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "cri"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = getcray(); +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "unicosmk"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = getcray(); +# endif +#endif + + + + +#if defined(convex) + /* Convex */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "convex"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "convex"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "convexos"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = getconvex(); +# endif +#endif + + + + +#if defined(butterfly) + /* BBN Butterfly 1000 */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "bbn"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "butterfly"; +# endif +# if (defined(mc68020) || defined(__mc68020__)) && !defined(_machtype_) +# define _machtype_ + machtype = "m68k"; +# endif +#endif + + + + +#if defined(NeXT) + /* NeXTStep */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "next"; +# endif +# if (defined(mc68020) || defined(__mc68020__)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "next"; +# endif +# if (defined(M_i386) || defined(__i386__)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "intel-pc"; +# endif +# if (defined(hppa) || defined(__hppa__)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "hp"; +# endif +# if (defined(sparc) || defined(__sparc__)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "sun"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "nextstep"; +# endif +# if (defined(mc68020) || defined(__mc68020__)) && !defined(_machtype_) +# define _machtype_ + machtype = "m68k"; +# endif +# if (defined(M_i386) || defined(__i386__)) && !defined(_machtype_) +# define _machtype_ + machtype = "i386"; +# endif +# if (defined(hppa) || defined(__hppa__)) && !defined(_machtype_) +# define _machtype_ + machtype = "hppa"; +# endif +# if (defined(sparc) || defined(__sparc__)) && !defined(_machtype_) +# define _machtype_ + machtype = "sparc"; +# endif +#endif + + + + +#if defined(__APPLE__) + /* Rhapsody */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "apple"; +# endif +# if (defined(__i386__)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "intel-pc"; +# endif +# if (defined(__ppc__)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "macintosh"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "rhapsody"; +# endif +# if (defined(__i386__)) && !defined(_machtype_) +# define _machtype_ + machtype = "i386"; +# endif +# if (defined(__ppc__)) && !defined(_machtype_) +# define _machtype_ + machtype = "powerpc"; +# endif +#endif + + + + +#if defined(sony_news) + /* Sony NEWS 800 or 1700 workstation */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "sony"; +# endif +# if (defined(mips)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "news_mips"; +# endif +# if (defined(mc68020) || defined(__mc68020__)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "news_m68k"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "News"; +# endif +# if (defined(mc68020) || defined(__mc68020__)) && !defined(_machtype_) +# define _machtype_ + machtype = "m68k"; +# endif +# if (defined(M_mipsel)) && !defined(_machtype_) +# define _machtype_ + machtype = "mipsel"; +# endif +# if (defined(M_mipseb)) && !defined(_machtype_) +# define _machtype_ + machtype = "mipseb"; +# endif +#endif + + + + +#if defined(sgi) + /* Silicon Graphics */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "sgi"; +# endif +# if (defined(M_mipsel)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "iris4d"; +# endif +# if (defined(M_mipseb)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "iris4d"; +# endif +# if (defined(mc68000)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "iris3d"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "irix"; +# endif +# if (defined(M_mipsel)) && !defined(_machtype_) +# define _machtype_ + machtype = "mipsel"; +# endif +# if (defined(M_mipseb)) && !defined(_machtype_) +# define _machtype_ + machtype = "mipseb"; +# endif +# if (defined(mc68000)) && !defined(_machtype_) +# define _machtype_ + machtype = "mc68000"; +# endif +#endif + + + + +#if defined(ultrix) || defined(__ultrix) + /* Digital's Ultrix */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "dec"; +# endif +# if (defined(M_mipsel)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "decstation"; +# endif +# if (defined(M_mipseb)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "decmips"; +# endif +# if (defined(vax) || defined(__vax)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "vax"; +# endif +# if (defined(__vax__)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "vax"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "ultrix"; +# endif +# if (defined(M_mipsel)) && !defined(_machtype_) +# define _machtype_ + machtype = "mipsel"; +# endif +# if (defined(M_mipseb)) && !defined(_machtype_) +# define _machtype_ + machtype = "mipseb"; +# endif +# if (defined(vax) || defined (__vax)) && !defined(_machtype_) +# define _machtype_ + machtype = "vax"; +# endif +# if (defined(__vax__)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "vax"; +# endif +#endif + + + + +#if defined(MIPS) + /* Mips OS */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "mips"; +# endif +# if (defined(M_mipsel)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "mips"; +# endif +# if (defined(M_mipseb)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "mips"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "mips"; +# endif +# if (defined(M_mipsel)) && !defined(_machtype_) +# define _machtype_ + machtype = "mipsel"; +# endif +# if (defined(M_mipseb)) && !defined(_machtype_) +# define _machtype_ + machtype = "mipseb"; +# endif +#endif + + + + +#if defined(DECOSF1) + /* Digital's alpha running osf1 */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "dec"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "osf1"; +# endif +# if (defined(__alpha)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "alpha"; +# endif +# if (defined(__alpha)) && !defined(_machtype_) +# define _machtype_ + machtype = "alpha"; +# endif +#endif + + + + +#if defined(Lynx) + /* Lynx OS 2.1 */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "Lynx"; +# endif +# if (defined(M_mipsel)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "lynxos-mips"; +# endif +# if (defined(M_mipseb)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "lynxos-mips"; +# endif +# if (defined(M_i386)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "lynxos-i386"; +# endif +# if (defined(i860) || defined(__i860__)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "lynxos-i860"; +# endif +# if (defined(m68k)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "lynxos-m68k"; +# endif +# if (defined(m88k)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "lynxos-m88k"; +# endif +# if (defined(sparc)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "lynxos-sparc"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "lynxos-unknown"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "LynxOS"; +# endif +# if (defined(M_mipsel)) && !defined(_machtype_) +# define _machtype_ + machtype = "mipsel"; +# endif +# if (defined(M_mipseb)) && !defined(_machtype_) +# define _machtype_ + machtype = "mipseb"; +# endif +# if (defined(M_i386)) && !defined(_machtype_) +# define _machtype_ + machtype = "i386"; +# endif +# if (defined(i860) || defined(__i860__)) && !defined(_machtype_) +# define _machtype_ + machtype = "i860"; +# endif +# if (defined(m68k)) && !defined(_machtype_) +# define _machtype_ + machtype = "m68k"; +# endif +# if (defined(m88k)) && !defined(_machtype_) +# define _machtype_ + machtype = "m88k"; +# endif +# if (defined(sparc)) && !defined(_machtype_) +# define _machtype_ + machtype = "sparc"; +# endif +#endif + + + + +#if defined(masscomp) + /* Masscomp */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "masscomp"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "masscomp"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "masscomp"; +# endif +#endif + + + +#if defined(__MACHTEN__) + /* Machintosh */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "Tenon"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "Macintosh"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "MachTen"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "Macintosh"; +# endif +#endif + + + + + +#if defined(GOULD_NP1) + /* Gould */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "gould"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "gould_np1"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "gould"; +# endif +#endif + + + + +#if defined(MULTIFLOW) + /* Multiflow running 4.3BSD */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "multiflow"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "multiflow"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "multiflow"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "bsd43"; +# endif +#endif + + + + +#if defined(SXA) + /* PFU/Fujitsu A-xx computer */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "sxa"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "pfa50"; +# endif +# if (defined(_BSDX_)) && !defined(_ostype_) +# define _ostype_ + ostype = "e60-bsdx"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "e60"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "pfa50"; +# endif +#endif + + + + +#if defined(titan) + /* (St)Ardent Titan */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "ardent"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "titan"; +# endif +#endif + + + + +#if defined(stellar) + /* Stellar */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "stellar"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "stellar"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "stellix"; +# endif +#endif + + + + +#if defined(atari) + /* Atari TT running SVR4. This machine was never */ + /* commercially available. */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "atari"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "atari"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "asv"; +# endif +#endif + + + + +#if defined(OPUS) + /* ??? */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "opus"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "opus"; +# endif +#endif + + + + +#if defined(eta10) + /* ETA running SVR3 */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "eta"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "eta10"; +# endif +#endif + + + + +#if defined(hk68) + /* Heurikon HK68 running Uniplus+ 5.0 */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "heurikon"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "hk68"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "uniplus"; +# endif +#endif + + + + +#if defined(NDIX) + /* Norsk Data ND 500/5000 running Ndix */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "norsk"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "nd500"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "ndix"; +# endif +#endif + + + + +#if defined(AMIGA) + /* Amiga running AmigaOS+GG */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "commodore"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "amiga"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "AmigaOS"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "m68k"; +# endif +#endif + + + + +#if defined(uts) + /* Amdahl running uts 2.1 */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "amdahl"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "amdahl"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "uts"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "amdahl"; +# endif +#endif + + + + +#if defined(UTek) + /* Tektronix 4300 running UTek (BSD 4.2 / 68020 based) */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "tektronix"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "tek4300"; +# endif +#endif + + + + +#if defined(UTekV) + /* Tektronix XD88/10 running UTekV 3.2e (SVR3/88100 based) */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "tektronix"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "tekXD88"; +# endif +#endif + + + + +#if defined(__DGUX__) + /* Data-General AViiON running DGUX */ +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "aviion"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "dgux"; +# endif +# if !defined(_vendor_) +# define _vendor_ + vendor = "dg"; +# endif +# if (defined(__m88k__)) && !defined(_machtype_) +# define _machtype_ + machtype = "m88k"; +# endif +# if (defined(__i386__)) && !defined(_machtype_) +# define _machtype_ + machtype = "pentium"; +# endif +#endif + + + + +#if defined(sysV68) + /* Motorola MPC running System V/68 R32V2 (SVR3/68020 based) */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "motorola"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "sysV68"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "m68k"; +# endif +#endif + + + + +#if defined(supermax) + /* DDE Supermax running System V/68 R3 (SVR3/68020 based) */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "supermax"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "supermax"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "m68k"; +# endif +#endif + + + + +#if defined(sysV88) + /* Motorola MPC running System V/88 R32V2 (SVR3/88100 based) */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "motorola"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "sysV88"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "m88k"; +# endif +#endif + + + + +#if defined(__clipper__) + /* Clipper Chipset (Intergraph) */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "intergraph"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "clipper"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "clipper"; +# endif +#endif + + + + +#if defined(SNI) || defined(sinix) + /* Siemens Nixdorf Informationssysteme SINIX */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "sni"; +# endif +# if (defined(M_intel)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "wx200i"; +# endif +# if (defined(MIPSEB)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "rm400"; +# endif +# if (defined(sinix)) && !defined(_ostype_) +# define _ostype_ + ostype = "sinix"; +# endif +# if (defined(M_i586)) && !defined(_machtype_) +# define _machtype_ + machtype = "i586"; +# endif +# if (defined(M_i486)) && !defined(_machtype_) +# define _machtype_ + machtype = "i486"; +# endif +# if (defined(M_i386)) && !defined(_machtype_) +# define _machtype_ + machtype = "i386"; +# endif +# if (defined(M_mipsel)) && !defined(_machtype_) +# define _machtype_ + machtype = "mipsel"; +# endif +# if (defined(M_mipseb)) && !defined(_machtype_) +# define _machtype_ + machtype = "mipseb"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "mips"; +# endif +#endif + + + +#if defined(_OSD_POSIX) + /* Siemens Nixdorf Informationssysteme BS2000 POSIX (mainframe, EBCDIC) */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "sni"; +# endif +# if (defined(M_intel)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "bs2000"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "posix"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "bs2000"; +# endif +#endif + + + +#if defined(_SX) + /* NEC Corporation (SX-4) */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "nec"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "superux"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "sx4"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "sx4"; +# endif +#endif + + + +#if !defined(SOLARIS2) && (SYSVREL == 4) + /* Unix System V Release 4.0 */ +# if (defined(DELL)) && !defined(_vendor_) +# define _vendor_ + vendor = "dell"; +# endif +# if (defined(M_i386)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "i386"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "svr4"; +# endif +# if (defined(M_i386)) && !defined(_machtype_) +# define _machtype_ + machtype = "i386"; +# endif +#endif + + + +#if defined(__uxp__) || defined(__uxps__) + /* FUJITSU DS/90 7000 */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "fujitsu"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "ds90"; +# endif +# if !defined(_ostype_) +# define _ostype_ + ostype = "sysv4"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "sparc"; +# endif +#endif + + + +#if defined(_UWIN) + /* AT&T Research Unix for Windows */ +# if !defined(_vendor_) +# define _vendor_ + vendor = "att"; +# endif +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "win32.i386"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "i386"; +# endif +#endif + + + + +#if defined(mc68000) || defined(__mc68000__) || defined(mc68k32) || defined(m68k) || defined(mc68010) || defined(mc68020) +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "m68k"; +# endif +# if (defined(m68k)) && !defined(_vendor_) +# define _vendor_ + vendor = "motorola"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "m68k"; +# endif +#endif + + + + +#if defined(m88k) || defined(__m88k__) +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "m88k"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "m88k"; +# endif +#endif + + + + +#if defined(M_intel) +# if (defined(M_i586)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "i586"; +# endif +# if (defined(M_i486)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "i486"; +# endif +# if (defined(M_i386)) && !defined(_hosttype_) +# define _hosttype_ + hosttype = "i386"; +# endif +# if !defined(_vendor_) +# define _vendor_ + vendor = "intel"; +# endif +# if (defined(M_i586)) && !defined(_machtype_) +# define _machtype_ + machtype = "i586"; +# endif +# if (defined(M_i486)) && !defined(_machtype_) +# define _machtype_ + machtype = "i486"; +# endif +# if (defined(M_i386)) && !defined(_machtype_) +# define _machtype_ + machtype = "i386"; +# endif +#endif + + + + +#if defined(sparc) || defined(__sparc__) +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "sparc"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "sparc"; +# endif +#endif + + + + +#if defined(i860) || defined(__i860__) +# if !defined(_hosttype_) +# define _hosttype_ + hosttype = "i860"; +# endif +# if !defined(_machtype_) +# define _machtype_ + machtype = "i860"; +# endif +#endif + + + + +#if defined(osf1) +# if !defined(_ostype_) +# define _ostype_ + ostype = "osf1"; +# endif +#endif + + + + +#if SYSVREL == 0 +# if (defined(BSD4_4)) && !defined(_ostype_) +# define _ostype_ + ostype = "bsd44"; +# endif +# if (defined(BSD)) && !defined(_ostype_) +# define _ostype_ + ostype = "bsd"; +# endif +# if (defined(POSIX)) && !defined(_ostype_) +# define _ostype_ + ostype = "posix"; +# endif +# if (defined(unix) || defined(__unix__)) && !defined(_ostype_) +# define _ostype_ + ostype = "unix"; +# endif +#endif + + + + +#if SYSVREL == 1 +# if !defined(_ostype_) +# define _ostype_ + ostype = "svr1"; +# endif +#endif + + + + +#if SYSVREL == 2 +# if !defined(_ostype_) +# define _ostype_ + ostype = "svr2"; +# endif +#endif + + + + +#if SYSVREL == 3 +# if !defined(_ostype_) +# define _ostype_ + ostype = "svr3"; +# endif +#endif + + + + +#if SYSVREL == 4 +# if !defined(_ostype_) +# define _ostype_ + ostype = "svr4"; +# endif +#endif + + +#ifndef _hosttype_ + hosttype = "unknown"; +#endif +#ifndef _ostype_ + ostype = "unknown"; +#endif +#ifndef _vendor_ + vendor = "unknown"; +#endif +#ifndef _machtype_ + machtype = "unknown"; +#endif + tsetenv(STRHOSTTYPE, str2short(hosttype)); + tsetenv(STRVENDOR, str2short(vendor)); + tsetenv(STROSTYPE, str2short(ostype)); + tsetenv(STRMACHTYPE, str2short(machtype)); +} /* end setmachine */ diff --git a/bin/date/Makefile b/bin/date/Makefile new file mode 100644 index 0000000..8a1dc85 --- /dev/null +++ b/bin/date/Makefile @@ -0,0 +1,9 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 +# $FreeBSD$ + +PROG= date +SRCS= date.c netdate.c vary.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..f473122 --- /dev/null +++ b/bin/date/date.1 @@ -0,0 +1,380 @@ +.\" 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.3 (Berkeley) 4/28/95 +.\" $FreeBSD$ +.\" +.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 jnu +.Op Fl d Ar dst +.Op Fl r Ar seconds +.Op Fl t Ar minutes_west +.Oo +.Fl v +.Op +|- Ns +.No val Ns Op ymwdHMS +.Oc Ns ... +.Oo Fl f No " " +.Ar fmt date No | +.Oo Oo Oo Oo Oo "\&cc" Ns +.Oc "\&yy" Oc "\&mm" Oc "\&dd" Oc "\&HH" Ns +.Oc +.No "\&MM" Ns Op "\&.ss" +.Oc +.Op Cm + Ns Ar format +.Sh DESCRIPTION +When invoked without arguments, the +.Nm +utility displays the current date and time. +Otherwise, depending on the options specified, +.Nm +will set the date and time or print it in a user-defined way. +.Pp +Only the superuser may set the date. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl d Ar dst +Set the kernel's value for daylight saving time. +If +.Ar dst +is non-zero, future calls +to +.Xr gettimeofday 2 +will return a non-zero for +.Fa tz_dsttime . +.It Fl f +Use +.Ar fmt +as the format string to parse the date provided rather than using +the default +.\" .Ar [[[[[cc]yy]mm]dd]HH]MM[.ss] +.Xo +.Oo Oo Oo Oo Oo "\&cc" Ns +.Oc "\&yy" Oc "\&mm" Oc "\&dd" Oc "\&HH" Ns +.Oc +.No "\&MM" Ns Op "\&.ss" Xc +format. +Parsing is done using +.Xr strptime 3 . +.It Fl j +Do not try to set the date. +This allows you to use the +.Fl f +flag in addition to the +.Cm + +option to convert one date format to another. +.It Fl n +By default, if the +.Xr timed 8 +daemon is running, +.Nm +sets the time on all of the machines in the local group. +The +.Fl n +option suppresses this behavior and causes the time to be set only on the +current machine. +.It Fl r Ar seconds +Print the date and time represented by +.Ar seconds , +where +.Ar seconds +is the number of seconds since the Epoch +(00:00:00 UTC, January 1, 1970; +see +.Xr time 3 ) , +and can be specified in decimal, octal, or hex. +.It Fl t Ar minutes_west +Set the system's value for minutes west of +.Tn GMT . +.Ar minutes_west +specifies the number of minutes returned in +.Fa tz_minuteswest +by future calls to +.Xr gettimeofday 2 . +.It Fl u +Display or set the date in +.Tn UTC +(Coordinated Universal) time. +.It Fl v +Adjust the second, minute, hour, month day, week day, +month or year according to +.Ar val . +If +.Ar val +is preceded with a plus or minus sign, +the date is adjusted forwards or backwards according to the remaining string, +otherwise the relevant part of the date is set. +The date can be adjusted as many times as required using these flags. +Flags are processed in the order given. +.Pp +When setting values +.Pq rather than adjusting them , +seconds are in the range 0-59, minutes are in the range 0-59, hours are +in the range 1-12, month days are in the range 1-31, week days are in the +range 0-6 +.Pq Sun-Sat , +months are in the range 1-12 +.Pq Jan-Dec +and years are in the range 80-38 or 1980-2038. +.Pp +If +.Ar val +is numeric, one of either +.Ar y , +.Ar m , +.Ar w , +.Ar d , +.Ar H , +.Ar M +or +.Ar S +must be used to specify which part of the date is to be adjusted. +.Pp +The week day or month may be specified using a name rather than a +number. +If a name is used with the plus +.Pq or minus +sign, the date will be put forwards +.Pq or backwards +to the next +.Pq previous +date that matches the given week day or month. +This will not adjust the date, +if the given week day or month is the same as the current one. +.Pp +When a date is adjusted to a specific value or in units greater than hours, +daylight savings time considerations are ignored. +Adjustments in units of hours or less honor daylight saving time. +So, assuming the current date is March 26, 0:30 and that the DST adjustment +means that the clock goes forward at 01:00 to 02:00, using +.Fl v No +1H +will adjust the date to March 26, 2:30. +Likewise, if the date is October 29, 0:30 and the DST adjustment means that +the clock goes back at 02:00 to 01:00, using +.Fl v No +3H +will be necessary to reach October 29, 2:30. +.Pp +When the date is adjusted to a specific value that doesn't actually exist +.Po +for example March 26, 1:30 BST 2000 in the Europe/London timezone +.Pc , +the date will be silently adjusted forwards in units of one hour until it +reaches a valid time. +When the date is adjusted to a specific value that occurs twice +.Pq for example October 29, 1:30 2000 , +the resulting timezone will be set so that the date matches the earlier of +the two times. +.Pp +Refer to the examples below for further details. +.El +.Pp +An operand with a leading plus +.Pq Sq + +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. +A newline +.Pq Ql \en +character is always output after the characters specified by +the format string. +The format string for the default display is +.Dq +%+ . +.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 cc +Century +.Pq either 19 or 20 +prepended to the abbreviated year. +.It Ar yy +Year in abbreviated form +.Pq e.g. 89 for 1989, 06 for 2006 . +.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 +.Pq 59 plus a maximum of two leap seconds . +.El +.Pp +Everything but the minutes is optional. +.Pp +Time changes for Daylight Saving Time, standard time, leap seconds, +and leap years are handled automatically. +.Sh EXAMPLES +The command: +.Bd -literal -offset indent +date "+DATE: %Y-%m-%d%nTIME: %H:%M:%S" +.Ed +.Pp +will display: +.Bd -literal -offset indent +DATE: 1987-11-21 +TIME: 13:36:16 +.Ed +.Pp +In the Europe/London timezone, the command: +.Bd -literal -offset indent +date -v1m -v+1y +.Ed +.Pp +will display: +.Bd -literal -offset indent +Sun Jan 4 04:15:24 GMT 1998 +.Ed +.Pp +where it is currently Mon Aug 4 04:15:24 BST 1997. +.Pp +The command: +.Bd -literal -offset indent +date -v1d -v3m -v0y -v-1d +.Ed +.Pp +will display the last day of February in the year 2000: +.Bd -literal -offset indent +Tue Feb 29 03:18:00 GMT 2000 +.Ed +.Pp +The command: +.Bd -literal -offset indent +date -v1d -v+1m -v-1d -v-fri +.Ed +.Pp +will display the last Friday of the month: +.Bd -literal -offset indent +Fri Aug 29 04:31:11 BST 1997 +.Ed +.Pp +where it is currently Mon Aug 4 04:31:11 BST 1997. +.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 ENVIRONMENT +The following environment variables affect the execution of +.Nm Ns : +.Bl -tag -width Ds +.It Ev TZ +The timezone to use when displaying dates. +The normal format is a pathname relative to +.Pa /usr/share/zoneinfo . +For example, the command +.Dq TZ=America/Los_Angeles date +displays the current time in California. +See +.Xr environ 7 +for more information. +.El +.Sh FILES +.Bl -tag -width /var/log/messages -compact +.It Pa /var/log/wtmp +record of date resets and time changes +.It Pa /var/log/messages +record of the user setting the time +.El +.Sh SEE ALSO +.Xr gettimeofday 2 , +.Xr strftime 3 , +.Xr strptime 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 +The +.Nm +utility exits 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 8 +synchronizes the time on many hosts, the setting of a new time value may +require more than a few seconds. +On these occasions, +.Nm +prints: +.Ql Network time being set . +The message +.Ql Communication error with timed +occurs when the communication +between +.Nm +and +.Xr timed 8 +fails. +.Sh STANDARDS +The +.Nm +utility is expected to be compatible with +.St -p1003.2 . +.Sh HISTORY +A +.Nm +command appeared in +.At v1 . diff --git a/bin/date/date.c b/bin/date/date.c new file mode 100644 index 0000000..99ebfe6 --- /dev/null +++ b/bin/date/date.c @@ -0,0 +1,308 @@ +/* + * 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 const copyright[] = +"@(#) Copyright (c) 1985, 1987, 1988, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)date.c 8.2 (Berkeley) 4/28/95"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> + +#include <ctype.h> +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> +#include <locale.h> + +#include "extern.h" +#include "vary.h" + +#ifndef TM_YEAR_BASE +#define TM_YEAR_BASE 1900 +#endif + +time_t tval; +int retval; + +static void setthetime __P((const char *, const char *, int, int)); +static void badformat __P((void)); +static void usage __P((void)); + +int logwtmp __P((char *, char *, char *)); + +int +main(argc, argv) + int argc; + char **argv; +{ + struct timezone tz; + int ch, rflag; + int jflag, nflag; + char *format, buf[1024]; + char *endptr, *fmt; + int set_timezone; + struct vary *v; + const struct vary *badv; + struct tm lt; + + v = NULL; + fmt = NULL; + (void) setlocale(LC_TIME, ""); + tz.tz_dsttime = tz.tz_minuteswest = 0; + rflag = 0; + jflag = nflag = 0; + set_timezone = 0; + while ((ch = getopt(argc, argv, "d:f:jnr:t:uv:")) != -1) + switch((char)ch) { + case 'd': /* daylight savings time */ + tz.tz_dsttime = strtol(optarg, &endptr, 10) ? 1 : 0; + if (endptr == optarg || *endptr != '\0') + usage(); + set_timezone = 1; + break; + case 'f': + fmt = optarg; + break; + case 'j': + jflag = 1; /* don't set time */ + break; + case 'n': /* don't set network */ + nflag = 1; + break; + case 'r': /* user specified seconds */ + rflag = 1; + if (sscanf(optarg,"%li",&tval) != 1) + usage(); + break; + case 't': /* minutes west of UTC */ + /* error check; don't allow "PST" */ + tz.tz_minuteswest = strtol(optarg, &endptr, 10); + if (endptr == optarg || *endptr != '\0') + usage(); + set_timezone = 1; + break; + case 'u': /* do everything in UTC */ + (void)setenv("TZ", "UTC0", 1); + break; + case 'v': + v = vary_append(v, optarg); + break; + default: + usage(); + } + argc -= optind; + argv += optind; + + /* + * If -d or -t, set the timezone or daylight savings time; this + * doesn't belong here; the kernel should not know about either. + */ + if (set_timezone && settimeofday((struct timeval *)NULL, &tz)) + err(1, "settimeofday (timezone)"); + + if (!rflag && time(&tval) == -1) + err(1, "time"); + + format = "%+"; + + /* allow the operands in any order */ + if (*argv && **argv == '+') { + format = *argv + 1; + ++argv; + } + + if (*argv) { + setthetime(fmt, *argv, jflag, nflag); + ++argv; + } else if (fmt != NULL) + usage(); + + if (*argv && **argv == '+') + format = *argv + 1; + + lt = *localtime(&tval); + badv = vary_apply(v, <); + if (badv) { + fprintf(stderr, "%s: Cannot apply date adjustment\n", + badv->arg); + vary_destroy(v); + usage(); + } + vary_destroy(v); + (void)strftime(buf, sizeof(buf), format, <); + (void)printf("%s\n", buf); + exit(retval); +} + +#define ATOI2(s) ((s) += 2, ((s)[-2] - '0') * 10 + ((s)[-1] - '0')) + +void +setthetime(fmt, p, jflag, nflag) + const char *fmt; + register const char *p; + int jflag, nflag; +{ + register struct tm *lt; + struct timeval tv; + const char *dot, *t; + int century; + + if (fmt != NULL) { + lt = localtime(&tval); + t = strptime(p, fmt, lt); + if (t == NULL) { + fprintf(stderr, "Failed conversion of ``%s''" + " using format ``%s''\n", p, fmt); + badformat(); + } else if (*t != '\0') + fprintf(stderr, "Warning: Ignoring %ld extraneous" + " characters in date string (%s)\n", + (long) strlen(t), t); + } else { + 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++; /* *dot++ = '\0'; */ + if (strlen(dot) != 2) + badformat(); + lt->tm_sec = ATOI2(dot); + if (lt->tm_sec > 61) + badformat(); + } else + lt->tm_sec = 0; + + century = 0; + /* if p has a ".ss" field then let's pretend it's not there */ + switch (strlen(p) - ((dot != NULL) ? 3 : 0)) { + case 12: /* cc */ + lt->tm_year = ATOI2(p) * 100 - TM_YEAR_BASE; + century = 1; + /* FALLTHROUGH */ + case 10: /* yy */ + if (century) + lt->tm_year += ATOI2(p); + else { /* hack for 2000 ;-} */ + lt->tm_year = ATOI2(p); + if (lt->tm_year < 69) + lt->tm_year += 2000 - TM_YEAR_BASE; + else + lt->tm_year += 1900 - TM_YEAR_BASE; + } + /* 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) + errx(1, "nonexistent time"); + + if (!jflag) { + /* set the time */ + if (nflag || netsettime(tval)) { + logwtmp("|", "date", ""); + tv.tv_sec = tval; + tv.tv_usec = 0; + if (settimeofday(&tv, (struct timezone *)NULL)) + err(1, "settimeofday (timeval)"); + 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, "%s\n%s\n", + "usage: date [-nu] [-d dst] [-r seconds] [-t west] " + "[-v[+|-]val[ymwdHMS]] ... ", + " " + "[-f fmt date | [[[[[cc]yy]mm]dd]HH]MM[.ss]] [+format]"); + exit(1); +} diff --git a/bin/date/extern.h b/bin/date/extern.h new file mode 100644 index 0000000..4726eb6a --- /dev/null +++ b/bin/date/extern.h @@ -0,0 +1,37 @@ +/*- + * 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 + * $FreeBSD$ + */ + +int netsettime __P((time_t)); diff --git a/bin/date/netdate.c b/bin/date/netdate.c new file mode 100644 index 0000000..3ec041a --- /dev/null +++ b/bin/date/netdate.c @@ -0,0 +1,186 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)netdate.c 8.1 (Berkeley) 5/31/93"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#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 <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(msg.tsp_name) - 1); + msg.tsp_name[sizeof(msg.tsp_name) - 1] = '\0'; + 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/date/vary.c b/bin/date/vary.c new file mode 100644 index 0000000..7fba422 --- /dev/null +++ b/bin/date/vary.c @@ -0,0 +1,499 @@ +/*- + * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org> + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <time.h> +#include <string.h> +#include <stdlib.h> +#include "vary.h" + +struct trans { + int val; + char *str; +}; + +static struct trans trans_mon[] = { + { 1, "january" }, { 2, "february" }, { 3, "march" }, { 4, "april" }, + { 5, "may"}, { 6, "june" }, { 7, "july" }, { 8, "august" }, + { 9, "september" }, { 10, "october" }, { 11, "november" }, { 12, "december" }, + { -1, NULL } +}; + +static struct trans trans_wday[] = { + { 0, "sunday" }, { 1, "monday" }, { 2, "tuesday" }, { 3, "wednesday" }, + { 4, "thursday" }, { 5, "friday" }, { 6, "saturday" }, + { -1, NULL } +}; + +static char digits[] = "0123456789"; +static int adjhour(struct tm *, char, int, int); + +static int +domktime(struct tm *t, char type) +{ + time_t ret; + + while ((ret = mktime(t)) == -1 && t->tm_year > 68 && t->tm_year < 138) + /* While mktime() fails, adjust by an hour */ + adjhour(t, type == '-' ? type : '+', 1, 0); + + return ret; +} + +static int +trans(const struct trans t[], const char *arg) +{ + int f; + + for (f = 0; t[f].val != -1; f++) + if (!strncasecmp(t[f].str, arg, 3) || + !strncasecmp(t[f].str, arg, strlen(t[f].str))) + return t[f].val; + + return -1; +} + +struct vary * +vary_append(struct vary *v, char *arg) +{ + struct vary *result, **nextp; + + if (v) { + result = v; + while (v->next) + v = v->next; + nextp = &v->next; + } else + nextp = &result; + + *nextp = (struct vary *)malloc(sizeof(struct vary)); + (*nextp)->arg = arg; + (*nextp)->next = NULL; + return result; +} + +static int mdays[12] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + +static int +daysinmonth(const struct tm *t) +{ + int year; + + year = t->tm_year + 1900; + + if (t->tm_mon == 1) + if (!(year % 400)) + return 29; + else if (!(year % 100)) + return 28; + else if (!(year % 4)) + return 29; + else + return 28; + else if (t->tm_mon >= 0 && t->tm_mon < 12) + return mdays[t->tm_mon]; + + return 0; +} + + +static int +adjyear(struct tm *t, char type, int val, int mk) +{ + switch (type) { + case '+': + t->tm_year += val; + break; + case '-': + t->tm_year -= val; + break; + default: + t->tm_year = val; + if (t->tm_year < 69) + t->tm_year += 100; /* as per date.c */ + else if (t->tm_year > 1900) + t->tm_year -= 1900; /* struct tm holds years since 1900 */ + break; + } + return !mk || domktime(t, type) != -1; +} + +static int +adjmon(struct tm *t, char type, int val, int istext, int mk) +{ + if (val < 0) + return 0; + + switch (type) { + case '+': + if (istext) { + if (val <= t->tm_mon) + val += 11 - t->tm_mon; /* early next year */ + else + val -= t->tm_mon + 1; /* later this year */ + } + if (val) { + if (!adjyear(t, '+', (t->tm_mon + val) / 12, 0)) + return 0; + val %= 12; + t->tm_mon += val; + if (t->tm_mon > 11) + t->tm_mon -= 12; + } + break; + + case '-': + if (istext) { + if (val-1 > t->tm_mon) + val = 13 - val + t->tm_mon; /* later last year */ + else + val = t->tm_mon - val + 1; /* early this year */ + } + if (val) { + if (!adjyear(t, '-', val / 12, 0)) + return 0; + val %= 12; + if (val > t->tm_mon) { + if (!adjyear(t, '-', 1, 0)) + return 0; + val -= 12; + } + t->tm_mon -= val; + } + break; + + default: + if (val > 12 || val < 1) + return 0; + t->tm_mon = --val; + } + + return !mk || domktime(t, type) != -1; +} + +static int +adjday(struct tm *t, char type, int val, int mk) +{ + int mdays; + + switch (type) { + case '+': + while (val) { + mdays = daysinmonth(t); + if (val > mdays - t->tm_mday) { + val -= mdays - t->tm_mday + 1; + t->tm_mday = 1; + if (!adjmon(t, '+', 1, 0, 0)) + return 0; + } else { + t->tm_mday += val; + val = 0; + } + } + break; + case '-': + while (val) + if (val >= t->tm_mday) { + val -= t->tm_mday; + t->tm_mday = 1; + if (!adjmon(t, '-', 1, 0, 0)) + return 0; + t->tm_mday = daysinmonth(t); + } else { + t->tm_mday -= val; + val = 0; + } + break; + default: + if (val > 0 && val <= daysinmonth(t)) + t->tm_mday = val; + else + return 0; + break; + } + + return !mk || domktime(t, type) != -1; +} + +static int +adjwday(struct tm *t, char type, int val, int istext, int mk) +{ + if (val < 0) + return 0; + + switch (type) { + case '+': + if (istext) + if (val < t->tm_wday) + val = 7 - t->tm_wday + val; /* early next week */ + else + val -= t->tm_wday; /* later this week */ + else + val *= 7; /* "-v+5w" == "5 weeks in the future" */ + return !val || adjday(t, '+', val, mk); + case '-': + if (istext) { + if (val > t->tm_wday) + val = 7 - val + t->tm_wday; /* later last week */ + else + val = t->tm_wday - val; /* early this week */ + } else + val *= 7; /* "-v-5w" == "5 weeks ago" */ + return !val || adjday(t, '-', val, mk); + default: + if (val < t->tm_wday) + return adjday(t, '-', t->tm_wday - val, mk); + else if (val > 6) + return 0; + else if (val > t->tm_wday) + return adjday(t, '+', val - t->tm_wday, mk); + } + return 1; +} + +static int +adjhour(struct tm *t, char type, int val, int mk) +{ + if (val < 0) + return 0; + + switch (type) { + case '+': + if (val) { + int days; + + days = (t->tm_hour + val) / 24; + val %= 24; + t->tm_hour += val; + t->tm_hour %= 24; + if (!adjday(t, '+', days, 0)) + return 0; + } + break; + + case '-': + if (val) { + int days; + + days = val / 24; + val %= 24; + if (val > t->tm_hour) { + days++; + val -= 24; + } + t->tm_hour -= val; + if (!adjday(t, '-', days, 0)) + return 0; + } + break; + + default: + if (val > 23) + return 0; + t->tm_hour = val; + } + + return !mk || domktime(t, type) != -1; +} + +static int +adjmin(struct tm *t, char type, int val, int mk) +{ + if (val < 0) + return 0; + + switch (type) { + case '+': + if (val) { + if (!adjhour(t, '+', (t->tm_min + val) / 60, 0)) + return 0; + val %= 60; + t->tm_min += val; + if (t->tm_min > 59) + t->tm_min -= 60; + } + break; + + case '-': + if (val) { + if (!adjhour(t, '-', val / 60, 0)) + return 0; + val %= 60; + if (val > t->tm_min) { + if (!adjhour(t, '-', 1, 0)) + return 0; + val -= 60; + } + t->tm_min -= val; + } + break; + + default: + if (val > 59) + return 0; + t->tm_min = val; + } + + return !mk || domktime(t, type) != -1; +} + +static int +adjsec(struct tm *t, char type, int val, int mk) +{ + if (val < 0) + return 0; + + switch (type) { + case '+': + if (val) { + if (!adjmin(t, '+', (t->tm_sec + val) / 60, 0)) + return 0; + val %= 60; + t->tm_sec += val; + if (t->tm_sec > 59) + t->tm_sec -= 60; + } + break; + + case '-': + if (val) { + if (!adjmin(t, '-', val / 60, 0)) + return 0; + val %= 60; + if (val > t->tm_sec) { + if (!adjmin(t, '-', 1, 0)) + return 0; + val -= 60; + } + t->tm_sec -= val; + } + break; + + default: + if (val > 59) + return 0; + t->tm_sec = val; + } + + return !mk || domktime(t, type) != -1; +} + +const struct vary * +vary_apply(const struct vary *v, struct tm *t) +{ + char type; + char which; + char *arg; + int len; + int val; + + for (; v; v = v->next) { + type = *v->arg; + arg = v->arg; + if (type == '+' || type == '-') + arg++; + else + type = '\0'; + len = strlen(arg); + if (len < 2) + return v; + + if (type == '\0') + t->tm_isdst = -1; + + if (strspn(arg, digits) != len-1) { + val = trans(trans_wday, arg); + if (val != -1) { + if (!adjwday(t, type, val, 1, 1)) + return v; + } else { + val = trans(trans_mon, arg); + if (val != -1) { + if (!adjmon(t, type, val, 1, 1)) + return v; + } else + return v; + } + } else { + val = atoi(arg); + which = arg[len-1]; + + switch (which) { + case 'S': + if (!adjsec(t, type, val, 1)) + return v; + break; + case 'M': + if (!adjmin(t, type, val, 1)) + return v; + break; + case 'H': + if (!adjhour(t, type, val, 1)) + return v; + break; + case 'd': + t->tm_isdst = -1; + if (!adjday(t, type, val, 1)) + return v; + break; + case 'w': + t->tm_isdst = -1; + if (!adjwday(t, type, val, 0, 1)) + return v; + break; + case 'm': + t->tm_isdst = -1; + if (!adjmon(t, type, val, 0, 1)) + return v; + break; + case 'y': + t->tm_isdst = -1; + if (!adjyear(t, type, val, 1)) + return v; + break; + default: + return v; + } + } + } + return 0; +} + +void +vary_destroy(struct vary *v) +{ + struct vary *n; + + while (v) { + n = v->next; + free(v); + v = n; + } +} diff --git a/bin/date/vary.h b/bin/date/vary.h new file mode 100644 index 0000000..b39306a --- /dev/null +++ b/bin/date/vary.h @@ -0,0 +1,36 @@ +/*- + * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org> + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +struct vary { + char *arg; + struct vary *next; +}; + +extern struct vary *vary_append(struct vary *v, char *arg); +extern const struct vary *vary_apply(const struct vary *v, struct tm *t); +extern void vary_destroy(struct vary *v); diff --git a/bin/dd/Makefile b/bin/dd/Makefile new file mode 100644 index 0000000..0cbd238 --- /dev/null +++ b/bin/dd/Makefile @@ -0,0 +1,9 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 +# $FreeBSD$ + +PROG= dd +SRCS= args.c conv.c conv_tab.c dd.c misc.c position.c + +MAINTAINER= green@FreeBSD.org + +.include <bsd.prog.mk> diff --git a/bin/dd/args.c b/bin/dd/args.c new file mode 100644 index 0000000..e118712 --- /dev/null +++ b/bin/dd/args.c @@ -0,0 +1,434 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)args.c 8.3 (Berkeley) 4/2/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <err.h> +#include <errno.h> +#include <limits.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 quad_t get_num __P((char *)); +static off_t get_offset __P((char *)); + +static const struct arg { + const 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 }, + { "iseek", f_skip, C_SKIP, C_SKIP }, + { "obs", f_obs, C_OBS, C_BS|C_OBS }, + { "of", f_of, C_OF, C_OF }, + { "oseek", f_seek, C_SEEK, C_SEEK }, + { "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) != NULL) { + if ((oper = strdup(oper)) == NULL) + errx(1, "unable to allocate space for the argument \"%s\"", *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"); + } else + cfunc = def; + + /* + * Bail out if the calculation of a file offset would overflow. + */ + if (in.offset > QUAD_MAX / in.dbsz || out.offset > QUAD_MAX / out.dbsz) + errx(1, "seek offsets cannot be larger than %qd", QUAD_MAX); +} + +static int +c_arg(a, b) + const void *a, *b; +{ + + return (strcmp(((const struct arg *)a)->name, + ((const struct arg *)b)->name)); +} + +static void +f_bs(arg) + char *arg; +{ + quad_t res; + + res = get_num(arg); + if (res < 1 || res > SSIZE_MAX) + errx(1, "bs must be between 1 and %d", SSIZE_MAX); + in.dbsz = out.dbsz = (size_t)res; +} + +static void +f_cbs(arg) + char *arg; +{ + quad_t res; + + res = get_num(arg); + if (res < 1 || res > SSIZE_MAX) + errx(1, "cbs must be between 1 and %d", SSIZE_MAX); + cbsz = (size_t)res; +} + +static void +f_count(arg) + char *arg; +{ + + cpy_cnt = get_num(arg); + if (cpy_cnt < 0) + errx(1, "count cannot be negative"); + if (cpy_cnt == 0) + cpy_cnt = -1; +} + +static void +f_files(arg) + char *arg; +{ + + files_cnt = get_num(arg); + if (files_cnt < 1) + errx(1, "files must be between 1 and %qd", QUAD_MAX); +} + +static void +f_ibs(arg) + char *arg; +{ + quad_t res; + + if (!(ddflags & C_BS)) { + res = get_num(arg); + if (res < 1 || res > SSIZE_MAX) + errx(1, "ibs must be between 1 and %d", SSIZE_MAX); + in.dbsz = (size_t)res; + } +} + +static void +f_if(arg) + char *arg; +{ + + in.name = arg; +} + +static void +f_obs(arg) + char *arg; +{ + quad_t res; + + if (!(ddflags & C_BS)) { + res = get_num(arg); + if (res < 1 || res > SSIZE_MAX) + errx(1, "obs must be between 1 and %d", SSIZE_MAX); + out.dbsz = (size_t)res; + } +} + +static void +f_of(arg) + char *arg; +{ + + out.name = arg; +} + +static void +f_seek(arg) + char *arg; +{ + + out.offset = get_offset(arg); +} + +static void +f_skip(arg) + char *arg; +{ + + in.offset = get_offset(arg); +} + +static const struct conv { + const char *name; + u_int set, noset; + const 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 }, + { "sparse", C_SPARSE, 0, 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, ","); + cp = bsearch(&tmp, clist, sizeof(clist) / sizeof(struct conv), + sizeof(struct conv), c_conv); + if (cp == NULL) + 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(((const struct conv *)a)->name, + ((const struct conv *)b)->name)); +} + +/* + * Convert an expression of the following forms to a quad_t. + * 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 1 << 10.) + * 4) A positive decimal number followed by a m (mult by 1 << 20.) + * 5) A positive decimal number followed by a g (mult by 1 << 30.) + * 5) A positive decimal number followed by a w (mult by sizeof int.) + * 6) Two or more positive decimal numbers (with/without [bkmgw]) + * separated by x (also * for backwards compatibility), specifying + * the product of the indicated values. + */ +static quad_t +get_num(val) + char *val; +{ + quad_t num, t; + char *expr; + + errno = 0; + num = strtoq(val, &expr, 0); + if (errno != 0) /* Overflow or underflow. */ + err(1, "%s", oper); + + if (expr == val) /* No valid 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 *= 1 << 10; + if (t > num) + goto erange; + ++expr; + break; + case 'm': + t = num; + num *= 1 << 20; + if (t > num) + goto erange; + ++expr; + break; + case 'g': + t = num; + num *= 1 << 30; + 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_num(expr + 1); + if (t <= num) + break; +erange: + errx(1, "%s: %s", oper, strerror(ERANGE)); + default: + errx(1, "%s: illegal numeric value", oper); + } + return (num); +} + +static off_t +get_offset(val) + char *val; +{ + quad_t num; + + num = get_num(val); + if (num > QUAD_MAX || num < 0) /* XXX quad_t != off_t */ + errx(1, "%s: illegal offset", oper); /* Too big/negative. */ + return ((off_t)num); +} diff --git a/bin/dd/conv.c b/bin/dd/conv.c new file mode 100644 index 0000000..0987a67 --- /dev/null +++ b/bin/dd/conv.c @@ -0,0 +1,270 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)conv.c 8.3 (Berkeley) 4/2/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#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() +{ + u_char *inp; + const u_char *t; + size_t cnt; + + if ((t = ctab) != NULL) + 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() +{ + u_char *inp, *outp; + const u_char *t; + size_t cnt, maxlen; + static int intrunc; + int ch; + + /* + * 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. + */ + ch = 0; + for (inp = in.dbp - in.dbcnt, outp = out.dbp; in.dbcnt;) { + maxlen = MIN(cbsz, in.dbcnt); + if ((t = ctab) != NULL) + 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) { + (void)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; + (void)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() +{ + u_char *inp; + const u_char *t; + size_t cnt; + + /* Translation and case conversion. */ + if ((t = ctab) != NULL) + 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; + (void)memmove(out.dbp, inp, cnt); + out.dbp += cnt; + out.dbcnt += cnt; + } + *out.dbp++ = '\n'; + if (++out.dbcnt >= out.dbsz) + dd_out(0); + } + if (in.dbcnt) + (void)memmove(in.db, in.dbp - in.dbcnt, in.dbcnt); + in.dbp = in.db + in.dbcnt; +} + +void +unblock_close() +{ + u_char *t; + size_t cnt; + + 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; + (void)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..bd951a7 --- /dev/null +++ b/bin/dd/conv_tab.c @@ -0,0 +1,288 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)conv_tab.c 8.1 (Berkeley) 5/31/93"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> + +/* + * There are currently six tables: + * + * 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. + */ + +u_char casetab[256]; + +/* EBCDIC to ASCII -- 32V compatible. */ +const 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. */ +const 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. */ +const 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. */ +const 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. */ +const 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. */ +const 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..24149f4 --- /dev/null +++ b/bin/dd/dd.1 @@ -0,0 +1,371 @@ +.\" 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 +.\" $FreeBSD$ +.\" +.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 +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 to +.Va n +bytes, 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 iseek= Ns Ar n +Seek on the input file +.Va n +blocks. +This is synonymous with +.Cm skip= Ns Ar n . +.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 seeked past (see the +.Cm oseek +operand), +the output file is truncated at that point. +.It Cm oseek= Ns Ar n +Seek on the output file +.Va n +blocks. +This is synonymous with +.Cm seek= Ns Ar n . +.It Cm seek= Ns Ar n +Seek +.Va n +blocks from the beginning of the output before copying. +On non-tape devices, an +.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, an +.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 EBCDIC +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 Ns . +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 sparse +If one or more output blocks would consist solely of +.Tn NUL +bytes, try to seek the output file by the required space instead of +filling them with +.Tn NULs , +resulting in a sparse file. +.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, octal, or hexadecimal number of +bytes is expected. +If the number ends with a ``b'', ``k'', ``m'', ``g'', or ``w'', the +number is multiplied by 512, 1024 (1K), 1048576 (1M), 1073741824 (1G) +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 +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 +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 +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 +will exit. +.Sh DIAGNOSTICS +The +.Nm +utility exits 0 on success or >0 if an error occurs. +.Sh SEE ALSO +.Xr cp 1 , +.Xr mt 1 , +.Xr tr 1 +.Sh STANDARDS +The +.Nm +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..a5bb032 --- /dev/null +++ b/bin/dd/dd.c @@ -0,0 +1,470 @@ +/*- + * 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 const copyright[] = +"@(#) Copyright (c) 1991, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)dd.c 8.5 (Berkeley) 4/2/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/conf.h> +#include <sys/disklabel.h> +#include <sys/filio.h> +#include <sys/time.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <locale.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)); +int main __P((int, char *[])); +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 */ +quad_t cpy_cnt; /* # of blocks to copy */ +off_t pending = 0; /* pending seek if sparse */ +u_int ddflags; /* conversion options */ +size_t cbsz; /* conversion block size */ +quad_t files_cnt = 1; /* # of files to copy */ +const u_char *ctab; /* conversion table */ + +int +main(argc, argv) + int argc; + char *argv[]; +{ + (void)setlocale(LC_CTYPE, ""); + 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; + struct timeval tv; + + if (in.name == NULL) { + in.name = "stdin"; + in.fd = STDIN_FILENO; + } else { + in.fd = open(in.name, O_RDONLY, 0); + if (in.fd == -1) + 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 == -1) { + out.fd = open(out.name, O_WRONLY | OFLAGS, DEFFILEMODE); + out.flags |= NOREAD; + } + if (out.fd == -1) + 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, "input buffer"); + out.db = in.db; + } else if ((in.db = malloc(MAX(in.dbsz, cbsz) + cbsz)) == NULL || + (out.db = malloc(out.dbsz + cbsz)) == NULL) + err(1, "output buffer"); + 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. If it fails on a type of output file + * that it should _not_ fail on, error out. + */ + if ((ddflags & (C_OF | C_SEEK | C_NOTRUNC)) == (C_OF | C_SEEK) && + out.flags & ISTRUNC) + if (ftruncate(out.fd, out.offset * out.dbsz) == -1) + err(1, "truncating %s", out.name); + + /* + * 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 | C_EBCDIC)) { + if (ddflags & C_LCASE) { + for (cnt = 0; cnt <= 0377; ++cnt) + casetab[cnt] = tolower(ctab[cnt]); + } else { + for (cnt = 0; cnt <= 0377; ++cnt) + casetab[cnt] = toupper(ctab[cnt]); + } + } else { + if (ddflags & C_LCASE) { + for (cnt = 0; cnt <= 0377; ++cnt) + casetab[cnt] = tolower((int)cnt); + } else { + for (cnt = 0; cnt <= 0377; ++cnt) + casetab[cnt] = toupper((int)cnt); + } + } + ctab = casetab; + } + + (void)gettimeofday(&tv, (struct timezone *)NULL); + st.start = tv.tv_sec + tv.tv_usec * 1e-6; +} + +static void +getfdtype(io) + IO *io; +{ + struct stat sb; + int type; + + if (fstat(io->fd, &sb) == -1) + err(1, "%s", io->name); + if (S_ISREG(sb.st_mode)) + io->flags |= ISTRUNC; + if (S_ISCHR(sb.st_mode) || S_ISBLK(sb.st_mode)) { + if (ioctl(io->fd, FIODTYPE, &type) == -1) { + err(1, "%s", io->name); + } else { + if (type & D_TAPE) + io->flags |= ISTAPE; + else if (type & (D_DISK | D_MEM)) { + if (type & D_DISK) { + const int one = 1; + + (void)ioctl(io->fd, DIOCWLABEL, &one); + } + io->flags |= ISSEEK; + } + if (S_ISCHR(sb.st_mode) && (type & D_TAPE) == 0) + io->flags |= ISCHR; + } + return; + } + errno = 0; + if (lseek(io->fd, (off_t)0, SEEK_CUR) == -1 && errno == ESPIPE) + io->flags |= ISPIPE; + else + io->flags |= ISSEEK; +} + +static void +dd_in() +{ + ssize_t n; + + for (;;) { + switch (cpy_cnt) { + case -1: /* count=0 was specified */ + return; + case 0: + break; + default: + if (st.in_full + st.in_part >= (u_quad_t)cpy_cnt) + return; + break; + } + + /* + * Zero the buffer first if sync; if doing block operations, + * use spaces. + */ + if (ddflags & C_SYNC) { + if (ddflags & (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 == -1) { + /* + * If noerror not specified, die. POSIX requires that + * the warning message be followed by an I/O display. + */ + if (!(ddflags & C_NOERROR)) + err(1, "%s", in.name); + warn("%s", in.name); + summary(); + + /* + * If it's a seekable file descriptor, 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 & ISSEEK && + 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 ((size_t)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.dbrcnt) & 1) { + ++st.swab; + --n; + } + swab(in.dbp, in.dbp, (size_t)n); + } + + in.dbp += in.dbrcnt; + (*cfunc)(); + } +} + +/* + * Clean up any remaining I/O and flush output. If necessary, the 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.dbcnt < out.dbsz) { + if (ddflags & (C_BLOCK | C_UNBLOCK)) + memset(out.dbp, ' ', out.dbsz - out.dbcnt); + else + memset(out.dbp, 0, out.dbsz - out.dbcnt); + out.dbcnt = out.dbsz; + } + if (out.dbcnt || pending) + dd_out(1); +} + +void +dd_out(force) + int force; +{ + u_char *outp; + size_t cnt, i, n; + ssize_t nw; + static int warned; + int sparse; + + /* + * 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) { + sparse = 0; + if (ddflags & C_SPARSE) { + sparse = 1; /* Is buffer sparse? */ + for (i = 0; i < cnt; i++) + if (outp[i] != 0) { + sparse = 0; + break; + } + } + if (sparse && !force) { + pending += cnt; + nw = cnt; + } else { + if (pending != 0) { + if (force) + pending--; + if (lseek(out.fd, pending, SEEK_CUR) == + -1) + err(2, "%s: seek error creating sparse file", + out.name); + if (force) + write(out.fd, outp, 1); + pending = 0; + } + if (cnt) + nw = write(out.fd, outp, cnt); + else + return; + } + + 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 ((size_t)nw == n) { + if (n != out.dbsz) + ++st.out_part; + else + ++st.out_full; + break; + } + ++st.out_part; + if ((size_t)nw == cnt) + break; + if (out.flags & ISTAPE) + errx(1, "%s: short write on tape device", + out.name); + if (out.flags & ISCHR && !warned) { + warned = 1; + warnx("%s: short write on character device", + out.name); + } + } + if ((out.dbcnt -= n) < out.dbsz) + break; + } + + /* Reassemble the output block. */ + if (out.dbcnt) + (void)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..183176b --- /dev/null +++ b/bin/dd/dd.h @@ -0,0 +1,101 @@ +/*- + * 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 + * $FreeBSD$ + */ + +/* Input/output stream state. */ +typedef struct { + u_char *db; /* buffer address */ + u_char *dbp; /* current buffer I/O address */ + /* XXX ssize_t? */ + size_t dbcnt; /* current buffer byte count */ + size_t dbrcnt; /* last read byte count */ + size_t dbsz; /* buffer size */ + +#define ISCHR 0x01 /* character device (warn on short) */ +#define ISPIPE 0x02 /* pipe-like (see position.c) */ +#define ISTAPE 0x04 /* tape */ +#define ISSEEK 0x08 /* valid to seek on */ +#define NOREAD 0x10 /* not readable */ +#define ISTRUNC 0x20 /* valid to ftruncate() */ + u_int flags; + + const char *name; /* name */ + int fd; /* file descriptor */ + off_t offset; /* # of blocks to skip */ + + u_quad_t f_stats; /* # of full blocks processed */ + u_quad_t p_stats; /* # of partial blocks processed */ + u_quad_t s_stats; /* # of odd swab blocks */ + u_quad_t t_stats; /* # of truncations */ +} IO; + +typedef struct { + u_quad_t in_full; /* # of full input blocks */ + u_quad_t in_part; /* # of partial input blocks */ + u_quad_t out_full; /* # of full output blocks */ + u_quad_t out_part; /* # of partial output blocks */ + u_quad_t trunc; /* # of truncated records */ + u_quad_t swab; /* # of odd-length swab blocks */ + u_quad_t bytes; /* # of bytes written */ + double 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 +#define C_SPARSE 0x200000 diff --git a/bin/dd/extern.h b/bin/dd/extern.h new file mode 100644 index 0000000..bdfeeb7 --- /dev/null +++ b/bin/dd/extern.h @@ -0,0 +1,69 @@ +/*- + * 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 + * $FreeBSD$ + */ + +#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)); +void bitswab __P((void *, size_t)); + +extern IO in, out; +extern STAT st; +extern void (*cfunc) __P((void)); +extern quad_t cpy_cnt; +extern size_t cbsz; +extern u_int ddflags; +extern quad_t files_cnt; +extern const u_char *ctab; +extern const u_char a2e_32V[], a2e_POSIX[]; +extern const u_char e2a_32V[], e2a_POSIX[]; +extern const u_char a2ibm_32V[], a2ibm_POSIX[]; +extern u_char casetab[]; diff --git a/bin/dd/misc.c b/bin/dd/misc.c new file mode 100644 index 0000000..8d75668 --- /dev/null +++ b/bin/dd/misc.c @@ -0,0 +1,107 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)misc.c 8.3 (Berkeley) 4/2/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/time.h> + +#include <errno.h> +#include <stdio.h> +#include <unistd.h> + +#include "dd.h" +#include "extern.h" + +void +summary() +{ + struct timeval tv; + double secs; + char buf[100]; + + (void)gettimeofday(&tv, (struct timezone *)NULL); + secs = tv.tv_sec + tv.tv_usec * 1e-6 - st.start; + if (secs < 1e-6) + secs = 1e-6; + /* Use snprintf(3) so that we don't reenter stdio(3). */ + (void)snprintf(buf, sizeof(buf), + "%qu+%qu records in\n%qu+%qu 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), "%qu 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), "%qu truncated %s\n", + st.trunc, (st.trunc == 1) ? "block" : "blocks"); + (void)write(STDERR_FILENO, buf, strlen(buf)); + } + (void)snprintf(buf, sizeof(buf), + "%qu bytes transferred in %.6f secs (%.0f bytes/sec)\n", + st.bytes, secs, st.bytes / secs); + (void)write(STDERR_FILENO, buf, strlen(buf)); +} + +/* ARGSUSED */ +void +summaryx(notused) + int notused; +{ + int save_errno = errno; + + summary(); + errno = save_errno; +} + +/* ARGSUSED */ +void +terminate(sig) + int sig; +{ + + /* XXX exit() shouldn't call exit() from a signal handler. */ + exit(sig == 0 ? 0 : 1); +} diff --git a/bin/dd/position.c b/bin/dd/position.c new file mode 100644 index 0000000..10020d0 --- /dev/null +++ b/bin/dd/position.c @@ -0,0 +1,178 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)position.c 8.3 (Berkeley) 4/2/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/mtio.h> + +#include <err.h> +#include <errno.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() +{ + off_t cnt; + int warned; + ssize_t nr; + size_t bcnt; + + /* If known to be seekable, try to seek on it. */ + if (in.flags & ISSEEK) { + errno = 0; + if (lseek(in.fd, in.offset * in.dbsz, SEEK_CUR) == -1 && + errno != 0) + 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; + off_t cnt; + ssize_t 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 & (ISSEEK | ISPIPE)) { + errno = 0; + if (lseek(out.fd, out.offset * out.dbsz, SEEK_CUR) == -1 && + errno != 0) + 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) == -1) + 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 == -1) + 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) { + n = write(out.fd, out.db, out.dbsz); + if (n == -1) + err(1, "%s", out.name); + if ((size_t)n != out.dbsz) + errx(1, "%s: write failure", out.name); + } + break; + } +} diff --git a/bin/df/Makefile b/bin/df/Makefile new file mode 100644 index 0000000..d9a27cf --- /dev/null +++ b/bin/df/Makefile @@ -0,0 +1,16 @@ +# @(#)Makefile 8.3 (Berkeley) 5/8/95 +# $FreeBSD$ + +PROG= df +SRCS= df.c vfslist.c +BINGRP= operator +BINMODE=2555 + +MOUNT= ${.CURDIR}/../../sbin/mount +CFLAGS+= -I${MOUNT} +.PATH: ${MOUNT} + +DPADD= ${LIBM} +LDADD= -lm + +.include <bsd.prog.mk> diff --git a/bin/df/df.1 b/bin/df/df.1 new file mode 100644 index 0000000..9660183 --- /dev/null +++ b/bin/df/df.1 @@ -0,0 +1,164 @@ +.\" 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.3 (Berkeley) 5/8/95 +.\" $FreeBSD$ +.\" +.Dd May 8, 1995 +.Dt DF 1 +.Os BSD 4 +.Sh NAME +.Nm df +.Nd display free disk space +.Sh SYNOPSIS +.Nm df +.Oo +.Fl b | h | H | k | +.Fl m | P +.Oc +.Op Fl ain +.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 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 a +Show all mount points, including those that were mounted with the MNT_IGNORE +flag. +.It Fl b +Use 512-byte blocks rather than the default. Note that +this overrides the +.Ev BLOCKSIZE +specification from the environment. +.It Fl g +Use 1073741824-byte (1-Gbyte) blocks rather than the default. Note that +this overrides the +.Ev BLOCKSIZE +specification from the environment. +.It Fl H +"Human-readable" output. Use unit suffixes: Byte, Kilobyte, Megabyte, +Gigabyte, Terabyte and Petabyte in order to reduce the number of +digits to three or less using base 10 for sizes. +.It Fl h +"Human-readable" output. Use unit suffixes: Byte, Kilobyte, Megabyte, +Gigabyte, Terabyte and Petabyte in order to reduce the number of +digits to three or less using base 2 for sizes. +.It Fl i +Include statistics on the number of free inodes. +.It Fl k +Use 1024-byte (1-Kbyte) blocks rather than the default. Note that +this overrides the +.Ev BLOCKSIZE +specification from the environment. +.It Fl m +Use 1048576-byte (1-Mbyte) blocks rather than the default. Note that +this overrides the +.Ev BLOCKSIZE +specification from the environment. +.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 +will not request new statistics from the filesystems, but will respond +with the possibly stale statistics that were previously obtained. +.It Fl P +Use POSIX compliant output of 512-byte blocks rather than the default. +Note that this overrides the +.Ev BLOCKSIZE +specification from the environment. +.It Fl t +Only print out statistics for filesystems of the specified types. +More than one type may be specified in a comma separated list. +The list of filesystem types can be prefixed with +.Dq no +to specify the filesystem types for which action should +.Em not +be taken. +For example, the +.Nm +command: +.Bd -literal -offset indent +df -t nonfs,mfs +.Ed +.Pp +lists all filesystems except those of type +.Tn NFS +and +.Tn MFS . +The +.Xr lsvfs 1 +command can be used to find out the types of filesystems +that are available on the system. +.El +.Sh ENVIRONMENT +.Bl -tag -width BLOCKSIZE +.It Ev BLOCKSIZE +If the environment 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 lsvfs 1 , +.Xr quota 1 , +.Xr fstatfs 2 , +.Xr getfsstat 2 , +.Xr statfs 2 , +.Xr getmntinfo 3 , +.Xr fstab 5 , +.Xr mount 8 , +.Xr quot 8 +.Sh HISTORY +A +.Nm +command appeared in +.At v1 . diff --git a/bin/df/df.c b/bin/df/df.c new file mode 100644 index 0000000..fc8cb34 --- /dev/null +++ b/bin/df/df.c @@ -0,0 +1,511 @@ +/* + * 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 const 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 +#if 0 +static char sccsid[] = "@(#)df.c 8.9 (Berkeley) 5/8/95"; +#else +static const char rcsid[] = + "$FreeBSD$"; +#endif +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/mount.h> +#include <ufs/ufs/ufsmount.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <math.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> + +#define UNITS_SI 1 +#define UNITS_2 2 + +#define KILO_SZ(n) (n) +#define MEGA_SZ(n) ((n) * (n)) +#define GIGA_SZ(n) ((n) * (n) * (n)) +#define TERA_SZ(n) ((n) * (n) * (n) * (n)) +#define PETA_SZ(n) ((n) * (n) * (n) * (n) * (n)) + +#define KILO_2_SZ (KILO_SZ(1024ULL)) +#define MEGA_2_SZ (MEGA_SZ(1024ULL)) +#define GIGA_2_SZ (GIGA_SZ(1024ULL)) +#define TERA_2_SZ (TERA_SZ(1024ULL)) +#define PETA_2_SZ (PETA_SZ(1024ULL)) + +#define KILO_SI_SZ (KILO_SZ(1000ULL)) +#define MEGA_SI_SZ (MEGA_SZ(1000ULL)) +#define GIGA_SI_SZ (GIGA_SZ(1000ULL)) +#define TERA_SI_SZ (TERA_SZ(1000ULL)) +#define PETA_SI_SZ (PETA_SZ(1000ULL)) + +unsigned long long vals_si [] = {1, KILO_SI_SZ, MEGA_SI_SZ, GIGA_SI_SZ, TERA_SI_SZ, PETA_SI_SZ}; +unsigned long long vals_base2[] = {1, KILO_2_SZ, MEGA_2_SZ, GIGA_2_SZ, TERA_2_SZ, PETA_2_SZ}; +unsigned long long *valp; + +typedef enum { NONE, KILO, MEGA, GIGA, TERA, PETA, UNIT_MAX } unit_t; + +int unitp [] = { NONE, KILO, MEGA, GIGA, TERA, PETA }; + +int checkvfsname __P((const char *, char **)); +char **makevfslist __P((char *)); +long regetmntinfo __P((struct statfs **, long, char **)); +int bread __P((off_t, void *, int)); +char *getmntpt __P((char *)); +void prthuman __P((struct statfs *, long)); +void prthumanval __P((double)); +void prtstat __P((struct statfs *, int)); +int ufs_df __P((char *, int)); +unit_t unit_adjust __P((double *)); +void usage __P((void)); + +int aflag = 0, hflag, iflag, nflag; +struct ufs_args mdev; + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct stat stbuf; + struct statfs statfsbuf, *mntbuf; + long mntsize; + int ch, err, i, maxwidth, rv, width; + char *mntpt, *mntpath, **vfslist; + + vfslist = NULL; + while ((ch = getopt(argc, argv, "abgHhikmnPt:")) != -1) + switch (ch) { + case 'a': + aflag = 1; + break; + case 'b': + /* FALLTHROUGH */ + case 'P': + putenv("BLOCKSIZE=512"); + hflag = 0; + break; + case 'g': + putenv("BLOCKSIZE=1g"); + hflag = 0; + break; + case 'H': + hflag = UNITS_SI; + valp = vals_si; + break; + case 'h': + hflag = UNITS_2; + valp = vals_base2; + break; + case 'i': + iflag = 1; + break; + case 'k': + putenv("BLOCKSIZE=1k"); + hflag = 0; + break; + case 'm': + putenv("BLOCKSIZE=1m"); + hflag = 0; + break; + case 'n': + nflag = 1; + break; + case 't': + if (vfslist != NULL) + errx(1, "only one -t option may be specified."); + vfslist = makevfslist(optarg); + 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; + } + + rv = 0; + if (!*argv) { + mntsize = regetmntinfo(&mntbuf, mntsize, vfslist); + if (vfslist != NULL) { + 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++) { + if (aflag || (mntbuf[i].f_flags & MNT_IGNORE) == 0) + prtstat(&mntbuf[i], maxwidth); + } + exit(rv); + } + + for (; *argv; argv++) { + if (stat(*argv, &stbuf) < 0) { + err = errno; + if ((mntpt = getmntpt(*argv)) == 0) { + warn("%s", *argv); + rv = 1; + continue; + } + } else if ((stbuf.st_mode & S_IFMT) == S_IFCHR) { + rv = ufs_df(*argv, maxwidth) || rv; + continue; + } else if ((stbuf.st_mode & S_IFMT) == S_IFBLK) { + if ((mntpt = getmntpt(*argv)) == 0) { + mdev.fspec = *argv; + mntpath = strdup("/tmp/df.XXXXXX"); + if (mntpath == NULL) { + warn("strdup failed"); + rv = 1; + continue; + } + mntpt = mkdtemp(mntpath); + if (mntpt == NULL) { + warn("mkdtemp(\"%s\") failed", mntpath); + rv = 1; + free(mntpath); + continue; + } + if (mount("ufs", mntpt, MNT_RDONLY, + &mdev) != 0) { + rv = ufs_df(*argv, maxwidth) || rv; + (void)rmdir(mntpt); + free(mntpath); + continue; + } else if (statfs(mntpt, &statfsbuf) == 0) { + statfsbuf.f_mntonname[0] = '\0'; + prtstat(&statfsbuf, maxwidth); + } else { + warn("%s", *argv); + rv = 1; + } + (void)unmount(mntpt, 0); + (void)rmdir(mntpt); + free(mntpath); + 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); + rv = 1; + continue; + } + if (argc == 1) + maxwidth = strlen(statfsbuf.f_mntfromname) + 1; + prtstat(&statfsbuf, maxwidth); + } + return (rv); +} + +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); +} + +/* + * Make a pass over the filesystem info in ``mntbuf'' filtering out + * filesystem types not in vfslist and possibly re-stating to get + * current (not cached) info. Returns the new count of valid statfs bufs. + */ +long +regetmntinfo(mntbufp, mntsize, vfslist) + struct statfs **mntbufp; + long mntsize; + char **vfslist; +{ + int i, j; + struct statfs *mntbuf; + + if (vfslist == NULL) + return (nflag ? mntsize : getmntinfo(mntbufp, MNT_WAIT)); + + mntbuf = *mntbufp; + for (j = 0, i = 0; i < mntsize; i++) { + if (checkvfsname(mntbuf[i].f_fstypename, vfslist)) + continue; + if (!nflag) + (void)statfs(mntbuf[i].f_mntonname,&mntbuf[j]); + else if (i != j) + mntbuf[j] = mntbuf[i]; + j++; + } + return (j); +} + +/* + * Output in "human-readable" format. Uses 3 digits max and puts + * unit suffixes at the end. Makes output compact and easy to read, + * especially on huge disks. + * + */ +unit_t +unit_adjust(val) + double *val; +{ + double abval; + unit_t unit; + unsigned int unit_sz; + + abval = fabs(*val); + + unit_sz = abval ? ilogb(abval) / 10 : 0; + + if (unit_sz >= UNIT_MAX) { + unit = NONE; + } else { + unit = unitp[unit_sz]; + *val /= (double)valp[unit_sz]; + } + + return (unit); +} + +void +prthuman(sfsp, used) + struct statfs *sfsp; + long used; +{ + + prthumanval((double)sfsp->f_blocks * (double)sfsp->f_bsize); + prthumanval((double)used * (double)sfsp->f_bsize); + prthumanval((double)sfsp->f_bavail * (double)sfsp->f_bsize); +} + +void +prthumanval(bytes) + double bytes; +{ + + unit_t unit; + unit = unit_adjust(&bytes); + + if (bytes == 0) + (void)printf(" 0B"); + else if (bytes > 10) + (void)printf(" %5.0f%c", bytes, "BKMGTPE"[unit]); + else + (void)printf(" %5.1f%c", bytes, "BKMGTPE"[unit]); +} + +/* + * 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) { + if (hflag) { + header = " Size"; + headerlen = strlen(header); + (void)printf("%-*.*s %-s Used Avail Capacity", + maxwidth, maxwidth, "Filesystem", header); + } else { + 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; + if (hflag) { + prthuman(sfsp, used); + } else { + (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/ufs/dinode.h> +#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; + +int +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 (1); + } + if (bread((off_t)SBOFF, &sblock, SBSIZE) == 0) { + (void)close(rfd); + return (1); + } + sfsp = &statfsbuf; + sfsp->f_type = 1; + strcpy(sfsp->f_fstypename, "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 = freespace(&sblock, sblock.fs_minfree); + 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); + return (0); +} + +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 [-b | -H | -h | -k | -m | -P] [-ain] [-t type] [file | filesystem ...]\n"); + exit(EX_USAGE); +} diff --git a/bin/domainname/Makefile b/bin/domainname/Makefile new file mode 100644 index 0000000..724bd06 --- /dev/null +++ b/bin/domainname/Makefile @@ -0,0 +1,5 @@ +# $FreeBSD$ + +PROG= domainname + +.include <bsd.prog.mk> diff --git a/bin/domainname/domainname.1 b/bin/domainname/domainname.1 new file mode 100644 index 0000000..96e4f07 --- /dev/null +++ b/bin/domainname/domainname.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. +.\" +.\" From: @(#)hostname.1 8.1 (Berkeley) 5/31/93 +.\" $FreeBSD$ +.\" +.Dd September 18, 1994 +.Dt DOMAINNAME 1 +.Os FreeBSD 2.0 +.Sh NAME +.Nm domainname +.Nd set or print name of current YP/NIS domain +.Sh SYNOPSIS +.Nm domainname +.Op Ar ypdomain +.Sh DESCRIPTION +.Nm Domainname +prints the name of the current YP/NIS domain. The super-user can +set the domain name by supplying an argument; this is usually done in the +network initialization script +.Pa /etc/rc.network , +normally run at boot +time. +.Sh NOTA BENE +The YP/NIS (formerly ``Yellow Pages'' but renamed for legal reasons) +domain name does not necessarily have anything to do with the Domain +Name System domain name, although they are often set equal for administrative +convenience. +.Sh SEE ALSO +.Xr getdomainname 3 +.Sh HISTORY +The +.Nm +command appeared in +.Tn FreeBSD +1.1, based on a similar command in +.Tn SunOS . diff --git a/bin/domainname/domainname.c b/bin/domainname/domainname.c new file mode 100644 index 0000000..d0c989c --- /dev/null +++ b/bin/domainname/domainname.c @@ -0,0 +1,93 @@ +/* + * 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 const copyright[] = +"@(#) Copyright (c) 1988, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char const sccsid[] = "From: @(#)hostname.c 8.1 (Berkeley) 5/31/93"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/param.h> + +#include <err.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; + char domainname[MAXHOSTNAMELEN]; + + while ((ch = getopt(argc, argv, "")) != -1) + switch (ch) { + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc > 1) + usage(); + + if (*argv) { + if (setdomainname(*argv, strlen(*argv))) + err(1, "setdomainname"); + } else { + if (getdomainname(domainname, sizeof(domainname))) + err(1, "getdomainname"); + (void)printf("%s\n", domainname); + } + exit(0); +} + +void +usage() +{ + (void)fprintf(stderr, "usage: domainname [ypdomain]\n"); + exit(1); +} diff --git a/bin/echo/Makefile b/bin/echo/Makefile new file mode 100644 index 0000000..d00d467 --- /dev/null +++ b/bin/echo/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 +# $FreeBSD$ + +PROG= echo + +.include <bsd.prog.mk> diff --git a/bin/echo/echo.1 b/bin/echo/echo.1 new file mode 100644 index 0000000..0f43f99 --- /dev/null +++ b/bin/echo/echo.1 @@ -0,0 +1,83 @@ +.\" 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 +.\" $FreeBSD$ +.\" +.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 +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. This may also be +achieved by appending `\ec' to the end of the string, as is done +by iBCS2 compatible systems. +.El +.Pp +Some shells may provide a builtin +.Nm +command which is similar or identical to this utility. +Consult the +.Xr builtin 1 +manual page. +.Sh DIAGNOSTICS +The +.Nm +utility exits 0 on success, and >0 if an error occurs. +.Sh SEE ALSO +.Xr builtin 1 , +.Xr csh 1 , +.Xr printf 1 , +.Xr sh 1 +.Sh STANDARDS +The +.Nm +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..edab483 --- /dev/null +++ b/bin/echo/echo.c @@ -0,0 +1,81 @@ +/* + * 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 const copyright[] = +"@(#) Copyright (c) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)echo.c 8.1 (Berkeley) 5/31/93"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#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[0]) { + int len = strlen(argv[0]); + + if (len >= 2 && !argv[1] && argv[0][len - 2] == '\\' && argv[0][len - 1] == 'c') { + argv[0][len - 2] = '\0'; + nflag = 1; + } + (void)printf("%s", argv[0]); + if (*++argv) + putchar(' '); + } + if (!nflag) + putchar('\n'); + exit(0); +} diff --git a/bin/ed/Makefile b/bin/ed/Makefile new file mode 100644 index 0000000..95901a6 --- /dev/null +++ b/bin/ed/Makefile @@ -0,0 +1,15 @@ +# $FreeBSD$ + +PROG= ed +SRCS= buf.c cbc.c glbl.c io.c main.c re.c sub.c undo.c +LINKS= ${BINDIR}/ed ${BINDIR}/red +MLINKS= ed.1 red.1 + +.if exists(${.CURDIR}/../../secure) && !defined(NOCRYPT) && !defined(NOSECURE) +DISTRIBUTION=crypto +CFLAGS+=-DDES +LDADD+= -L${CIPHEROBJDIR} -lcipher +DPADD+= ${CIPHEROBJDIR}/libcipher.a +.endif + +.include <bsd.prog.mk> diff --git a/bin/ed/POSIX b/bin/ed/POSIX new file mode 100644 index 0000000..f81bb0c --- /dev/null +++ b/bin/ed/POSIX @@ -0,0 +1,101 @@ +$FreeBSD$ + +This version of ed(1) is not strictly POSIX compliant, as described in +the POSIX 1003.2 document. The following is a summary of the omissions, +extensions and possible deviations from POSIX 1003.2. + +OMISSIONS +--------- +1) For backwards compatibility, the POSIX rule that says a range of + addresses cannot be used where only a single address is expected has + been relaxed. + +2) To support the BSD `s' command (see extension [1] below), + substitution patterns cannot be delimited by numbers or the characters + `r', `g' and `p'. In contrast, POSIX specifies any character expect + space or newline can used as a delimiter. + +EXTENSIONS +---------- +1) BSD commands have been implemented wherever they do not conflict with + the POSIX standard. The BSD-ism's included are: + i) `s' (i.e., s[n][rgp]*) to repeat a previous substitution, + ii) `W' for appending text to an existing file, + iii) `wq' for exiting after a write, + iv) `z' for scrolling through the buffer, and + v) BSD line addressing syntax (i.e., `^' and `%') is recognized. + +2) If crypt(3) is available, files can be read and written using DES + encryption. The `x' command prompts the user to enter a key used for + encrypting/ decrypting subsequent reads and writes. If only a newline + is entered as the key, then encryption is disabled. Otherwise, a key + is read in the same manner as a password entry. The key remains in + effect until encryption is disabled. For more information on the + encryption algorithm, see the bdes(1) man page. Encryption/decryption + should be fully compatible with SunOS des(1). + +3) The POSIX interactive global commands `G' and `V' are extended to + support multiple commands, including `a', `i' and `c'. The command + format is the same as for the global commands `g' and `v', i.e., one + command per line with each line, except for the last, ending in a + backslash (\). + +4) An extension to the POSIX file commands `E', `e', `r', `W' and `w' is + that <file> arguments are processed for backslash escapes, i.e., any + character preceded by a backslash is interpreted literally. If the + first unescaped character of a <file> argument is a bang (!), then the + rest of the line is interpreted as a shell command, and no escape + processing is performed by ed. + +5) For SunOS ed(1) compatibility, ed runs in restricted mode if invoked + as red. This limits editing of files in the local directory only and + prohibits shell commands. + +DEVIATIONS +---------- +1) Though ed is not a stream editor, it can be used to edit binary files. + To assist in binary editing, when a file containing at least one ASCII + NUL character is written, a newline is not appended if it did not + already contain one upon reading. In particular, reading /dev/null + prior to writing prevents appending a newline to a binary file. + + For example, to create a file with ed containing a single NUL character: + $ ed file + a + ^@ + . + r /dev/null + wq + + Similarly, to remove a newline from the end of binary `file': + $ ed file + r /dev/null + wq + +2) Since the behavior of `u' (undo) within a `g' (global) command list is + not specified by POSIX, it follows the behavior of the SunOS ed: + undo forces a global command list to be executed only once, rather than + for each line matching a global pattern. In addtion, each instance of + `u' within a global command undoes all previous commands (including + undo's) in the command list. This seems the best way, since the + alternatives are either too complicated to implement or too confusing + to use. + + The global/undo combination is useful for masking errors that + would otherwise cause a script to fail. For instance, an ed script + to remove any occurences of either `censor1' or `censor2' might be + written as: + ed - file <<EOF + 1g/.*/u\ + ,s/censor1//g\ + ,s/censor2//g + ... + +3) The `m' (move) command within a `g' command list also follows the SunOS + ed implementation: any moved lines are removed from the global command's + `active' list. + +4) If ed is invoked with a name argument prefixed by a bang (!), then the + remainder of the argument is interpreted as a shell command. To invoke + ed on a file whose name starts with bang, prefix the name with a + backslash. diff --git a/bin/ed/README b/bin/ed/README new file mode 100644 index 0000000..478e7af --- /dev/null +++ b/bin/ed/README @@ -0,0 +1,24 @@ +$FreeBSD$ + +ed is an 8-bit-clean, POSIX-compliant line editor. It should work with +any regular expression package that conforms to the POSIX interface +standard, such as GNU regex(3). + +If reliable signals are supported (e.g., POSIX sigaction(2)), it should +compile with little trouble. Otherwise, the macros SPL1() and SPL0() +should be redefined to disable interrupts. + +The following compiler directives are recognized: +DES - to add encryption support (requires crypt(3)) +NO_REALLOC_NULL - if realloc(3) does not accept a NULL pointer +BACKWARDS - for backwards compatibility +NEED_INSQUE - if insque(3) is missing + +The file `POSIX' describes extensions to and deviations from the POSIX +standard. + +The ./test directory contains regression tests for ed. The README +file in that directory explains how to run these. + +For a description of the ed algorithm, see Kernighan and Plauger's book +"Software Tools in Pascal," Addison-Wesley, 1981. diff --git a/bin/ed/buf.c b/bin/ed/buf.c new file mode 100644 index 0000000..4e24796 --- /dev/null +++ b/bin/ed/buf.c @@ -0,0 +1,300 @@ +/* buf.c: This file contains the scratch-file buffer routines for the + ed line editor. */ +/*- + * Copyright (c) 1993 Andrew Moore, Talke Studio. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 +#if 0 +static char * const rcsid = "@(#)buf.c,v 1.4 1994/02/01 00:34:35 alm Exp"; +#else +static char * const rcsid = + "$FreeBSD$"; +#endif +#endif /* not lint */ + +#include <sys/file.h> +#include <sys/stat.h> + +#include "ed.h" + + +FILE *sfp; /* scratch file pointer */ +off_t sfseek; /* scratch file position */ +int seek_write; /* seek before writing */ +line_t buffer_head; /* incore buffer */ + +/* get_sbuf_line: get a line of text from the scratch file; return pointer + to the text */ +char * +get_sbuf_line(lp) + line_t *lp; +{ + static char *sfbuf = NULL; /* buffer */ + static int sfbufsz = 0; /* buffer size */ + + int len, ct; + + if (lp == &buffer_head) + return NULL; + seek_write = 1; /* force seek on write */ + /* out of position */ + if (sfseek != lp->seek) { + sfseek = lp->seek; + if (fseek(sfp, sfseek, SEEK_SET) < 0) { + fprintf(stderr, "%s\n", strerror(errno)); + sprintf(errmsg, "cannot seek temp file"); + return NULL; + } + } + len = lp->len; + REALLOC(sfbuf, sfbufsz, len + 1, NULL); + if ((ct = fread(sfbuf, sizeof(char), len, sfp)) < 0 || ct != len) { + fprintf(stderr, "%s\n", strerror(errno)); + sprintf(errmsg, "cannot read temp file"); + return NULL; + } + sfseek += len; /* update file position */ + sfbuf[len] = '\0'; + return sfbuf; +} + + +/* put_sbuf_line: write a line of text to the scratch file and add a line node + to the editor buffer; return a pointer to the end of the text */ +char * +put_sbuf_line(cs) + char *cs; +{ + line_t *lp; + int len, ct; + char *s; + + if ((lp = (line_t *) malloc(sizeof(line_t))) == NULL) { + fprintf(stderr, "%s\n", strerror(errno)); + sprintf(errmsg, "out of memory"); + return NULL; + } + /* assert: cs is '\n' terminated */ + for (s = cs; *s != '\n'; s++) + ; + if (s - cs >= LINECHARS) { + sprintf(errmsg, "line too long"); + return NULL; + } + len = s - cs; + /* out of position */ + if (seek_write) { + if (fseek(sfp, 0L, SEEK_END) < 0) { + fprintf(stderr, "%s\n", strerror(errno)); + sprintf(errmsg, "cannot seek temp file"); + return NULL; + } + sfseek = ftell(sfp); + seek_write = 0; + } + /* assert: SPL1() */ + if ((ct = fwrite(cs, sizeof(char), len, sfp)) < 0 || ct != len) { + sfseek = -1; + fprintf(stderr, "%s\n", strerror(errno)); + sprintf(errmsg, "cannot write temp file"); + return NULL; + } + lp->len = len; + lp->seek = sfseek; + add_line_node(lp); + sfseek += len; /* update file position */ + return ++s; +} + + +/* add_line_node: add a line node in the editor buffer after the current line */ +void +add_line_node(lp) + line_t *lp; +{ + line_t *cp; + + cp = get_addressed_line_node(current_addr); /* this get_addressed_line_node last! */ + INSQUE(lp, cp); + addr_last++; + current_addr++; +} + + +/* get_line_node_addr: return line number of pointer */ +long +get_line_node_addr(lp) + line_t *lp; +{ + line_t *cp = &buffer_head; + long n = 0; + + while (cp != lp && (cp = cp->q_forw) != &buffer_head) + n++; + if (n && cp == &buffer_head) { + sprintf(errmsg, "invalid address"); + return ERR; + } + return n; +} + + +/* get_addressed_line_node: return pointer to a line node in the editor buffer */ +line_t * +get_addressed_line_node(n) + long n; +{ + static line_t *lp = &buffer_head; + static long on = 0; + + SPL1(); + if (n > on) + if (n <= (on + addr_last) >> 1) + for (; on < n; on++) + lp = lp->q_forw; + else { + lp = buffer_head.q_back; + for (on = addr_last; on > n; on--) + lp = lp->q_back; + } + else + if (n >= on >> 1) + for (; on > n; on--) + lp = lp->q_back; + else { + lp = &buffer_head; + for (on = 0; on < n; on++) + lp = lp->q_forw; + } + SPL0(); + return lp; +} + + +extern int newline_added; + +char sfn[15] = ""; /* scratch file name */ + +/* open_sbuf: open scratch file */ +int +open_sbuf() +{ + int fd = -1; + int u; + + isbinary = newline_added = 0; + u = umask(077); + strcpy(sfn, "/tmp/ed.XXXXXX"); + if ((fd = mkstemp(sfn)) == -1 || + (sfp = fdopen(fd, "w+")) == NULL) { + if (fd != -1) + close(fd); + perror(sfn); + strcpy(errmsg, "cannot open temp file"); + umask(u); + return ERR; + } + umask(u); + return 0; +} + + +/* close_sbuf: close scratch file */ +int +close_sbuf() +{ + if (sfp) { + if (fclose(sfp) < 0) { + fprintf(stderr, "%s: %s\n", sfn, strerror(errno)); + sprintf(errmsg, "cannot close temp file"); + return ERR; + } + sfp = NULL; + unlink(sfn); + } + sfseek = seek_write = 0; + return 0; +} + + +/* quit: remove_lines scratch file and exit */ +void +quit(n) + int n; +{ + if (sfp) { + fclose(sfp); + unlink(sfn); + } + exit(n); +} + + +unsigned char ctab[256]; /* character translation table */ + +/* init_buffers: open scratch buffer; initialize line queue */ +void +init_buffers() +{ + int i = 0; + + /* Read stdin one character at a time to avoid i/o contention + with shell escapes invoked by nonterminal input, e.g., + ed - <<EOF + !cat + hello, world + EOF */ + setbuffer(stdin, stdinbuf, 1); + + /* Ensure stdout is line buffered. This avoids bogus delays + of output if stdout is piped through utilities to a terminal. */ + setvbuf(stdout, NULL, _IOLBF, 0); + if (open_sbuf() < 0) + quit(2); + REQUE(&buffer_head, &buffer_head); + for (i = 0; i < 256; i++) + ctab[i] = i; +} + + +/* translit_text: translate characters in a string */ +char * +translit_text(s, len, from, to) + char *s; + int len; + int from; + int to; +{ + static int i = 0; + + unsigned char *us; + + ctab[i] = i; /* restore table to initial state */ + ctab[i = from] = to; + for (us = (unsigned char *) s; len-- > 0; us++) + *us = ctab[*us]; + return s; +} diff --git a/bin/ed/cbc.c b/bin/ed/cbc.c new file mode 100644 index 0000000..f77ce0d --- /dev/null +++ b/bin/ed/cbc.c @@ -0,0 +1,434 @@ +/* cbc.c: This file contains the encryption routines for the ed line editor */ +/*- + * Copyright (c) 1993 The Regents of the University of California. + * All rights reserved. + * + * Copyright (c) 1993 Andrew Moore, Talke Studio. + * 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. + * + * from: @(#)bdes.c 5.5 (Berkeley) 6/27/91 + */ + +#ifndef lint +#if 0 +static char * const rcsid = "@(#)cbc.c,v 1.2 1994/02/01 00:34:36 alm Exp"; +#else +static char * const rcsid = + "$FreeBSD$"; +#endif +#endif /* not lint */ + +#include <sys/types.h> +#include <errno.h> +#include <pwd.h> +#ifdef DES +#include <time.h> +#endif + +#include "ed.h" + + +/* + * Define a divisor for rand() that yields a uniform distribution in the + * range 0-255. + */ +#define RAND_DIV (((unsigned) RAND_MAX + 1) >> 8) + +/* + * BSD and System V systems offer special library calls that do + * block move_liness and fills, so if possible we take advantage of them + */ +#define MEMCPY(dest,src,len) memcpy((dest),(src),(len)) +#define MEMZERO(dest,len) memset((dest), 0, (len)) + +/* Hide the calls to the primitive encryption routines. */ +#define DES_KEY(buf) \ + if (des_setkey(buf)) \ + des_error("des_setkey"); +#define DES_XFORM(buf) \ + if (des_cipher(buf, buf, 0L, (inverse ? -1 : 1))) \ + des_error("des_cipher"); + +/* + * read/write - no error checking + */ +#define READ(buf, n, fp) fread(buf, sizeof(char), n, fp) +#define WRITE(buf, n, fp) fwrite(buf, sizeof(char), n, fp) + +/* + * some things to make references easier + */ +typedef char Desbuf[8]; +#define CHAR(x,i) (x[i]) +#define UCHAR(x,i) (x[i]) +#define BUFFER(x) (x) +#define UBUFFER(x) (x) + +/* + * global variables and related macros + */ + +enum { /* encrypt, decrypt, authenticate */ + MODE_ENCRYPT, MODE_DECRYPT, MODE_AUTHENTICATE +} mode = MODE_ENCRYPT; + +Desbuf ivec; /* initialization vector */ +Desbuf pvec; /* padding vector */ +char bits[] = { /* used to extract bits from a char */ + '\200', '\100', '\040', '\020', '\010', '\004', '\002', '\001' +}; +int pflag; /* 1 to preserve parity bits */ + +unsigned char des_buf[8]; /* shared buffer for get_des_char/put_des_char */ +int des_ct = 0; /* count for get_des_char/put_des_char */ +int des_n = 0; /* index for put_des_char/get_des_char */ + + +/* init_des_cipher: initialize DES */ +void +init_des_cipher() +{ +#ifdef DES + int i; + + des_ct = des_n = 0; + + /* initialize the initialization vector */ + MEMZERO(ivec, 8); + + /* initialize the padding vector */ + srand((unsigned) time((time_t *) 0)); + for (i = 0; i < 8; i++) + CHAR(pvec, i) = (char) (rand()/RAND_DIV); +#endif +} + + +/* get_des_char: return next char in an encrypted file */ +int +get_des_char(fp) + FILE *fp; +{ +#ifdef DES + if (des_n >= des_ct) { + des_n = 0; + des_ct = cbc_decode(des_buf, fp); + } + return (des_ct > 0) ? des_buf[des_n++] : EOF; +#else + return (getc(fp)); +#endif +} + + +/* put_des_char: write a char to an encrypted file; return char written */ +int +put_des_char(c, fp) + int c; + FILE *fp; +{ +#ifdef DES + if (des_n == sizeof des_buf) { + des_ct = cbc_encode(des_buf, des_n, fp); + des_n = 0; + } + return (des_ct >= 0) ? (des_buf[des_n++] = c) : EOF; +#else + return (fputc(c, fp)); +#endif +} + + +/* flush_des_file: flush an encrypted file's output; return status */ +int +flush_des_file(fp) + FILE *fp; +{ +#ifdef DES + if (des_n == sizeof des_buf) { + des_ct = cbc_encode(des_buf, des_n, fp); + des_n = 0; + } + return (des_ct >= 0 && cbc_encode(des_buf, des_n, fp) >= 0) ? 0 : EOF; +#else + return (fflush(fp)); +#endif +} + +#ifdef DES +/* + * get keyword from tty or stdin + */ +int +get_keyword() +{ + register char *p; /* used to obtain the key */ + Desbuf msgbuf; /* I/O buffer */ + + /* + * get the key + */ + if (*(p = getpass("Enter key: "))) { + + /* + * copy it, nul-padded, into the key area + */ + expand_des_key(BUFFER(msgbuf), p); + MEMZERO(p, _PASSWORD_LEN); + set_des_key(msgbuf); + MEMZERO(msgbuf, sizeof msgbuf); + return 1; + } + return 0; +} + + +/* + * print a warning message and, possibly, terminate + */ +void +des_error(s) + char *s; /* the message */ +{ + (void)sprintf(errmsg, "%s", s ? s : strerror(errno)); +} + +/* + * map a hex character to an integer + */ +int +hex_to_binary(c, radix) + int c; /* char to be converted */ + int radix; /* base (2 to 16) */ +{ + switch(c) { + case '0': return(0x0); + case '1': return(0x1); + case '2': return(radix > 2 ? 0x2 : -1); + case '3': return(radix > 3 ? 0x3 : -1); + case '4': return(radix > 4 ? 0x4 : -1); + case '5': return(radix > 5 ? 0x5 : -1); + case '6': return(radix > 6 ? 0x6 : -1); + case '7': return(radix > 7 ? 0x7 : -1); + case '8': return(radix > 8 ? 0x8 : -1); + case '9': return(radix > 9 ? 0x9 : -1); + case 'A': case 'a': return(radix > 10 ? 0xa : -1); + case 'B': case 'b': return(radix > 11 ? 0xb : -1); + case 'C': case 'c': return(radix > 12 ? 0xc : -1); + case 'D': case 'd': return(radix > 13 ? 0xd : -1); + case 'E': case 'e': return(radix > 14 ? 0xe : -1); + case 'F': case 'f': return(radix > 15 ? 0xf : -1); + } + /* + * invalid character + */ + return(-1); +} + +/* + * convert the key to a bit pattern + */ +void +expand_des_key(obuf, ibuf) + char *obuf; /* bit pattern */ + char *ibuf; /* the key itself */ +{ + register int i, j; /* counter in a for loop */ + int nbuf[64]; /* used for hex/key translation */ + + /* + * leading '0x' or '0X' == hex key + */ + if (ibuf[0] == '0' && (ibuf[1] == 'x' || ibuf[1] == 'X')) { + ibuf = &ibuf[2]; + /* + * now translate it, bombing on any illegal hex digit + */ + for (i = 0; ibuf[i] && i < 16; i++) + if ((nbuf[i] = hex_to_binary((int) ibuf[i], 16)) == -1) + des_error("bad hex digit in key"); + while (i < 16) + nbuf[i++] = 0; + for (i = 0; i < 8; i++) + obuf[i] = + ((nbuf[2*i]&0xf)<<4) | (nbuf[2*i+1]&0xf); + /* preserve parity bits */ + pflag = 1; + return; + } + /* + * leading '0b' or '0B' == binary key + */ + if (ibuf[0] == '0' && (ibuf[1] == 'b' || ibuf[1] == 'B')) { + ibuf = &ibuf[2]; + /* + * now translate it, bombing on any illegal binary digit + */ + for (i = 0; ibuf[i] && i < 16; i++) + if ((nbuf[i] = hex_to_binary((int) ibuf[i], 2)) == -1) + des_error("bad binary digit in key"); + while (i < 64) + nbuf[i++] = 0; + for (i = 0; i < 8; i++) + for (j = 0; j < 8; j++) + obuf[i] = (obuf[i]<<1)|nbuf[8*i+j]; + /* preserve parity bits */ + pflag = 1; + return; + } + /* + * no special leader -- ASCII + */ + (void)strncpy(obuf, ibuf, 8); +} + +/***************** + * DES FUNCTIONS * + *****************/ +/* + * This sets the DES key and (if you're using the deszip version) + * the direction of the transformation. This uses the Sun + * to map the 64-bit key onto the 56 bits that the key schedule + * generation routines use: the old way, which just uses the user- + * supplied 64 bits as is, and the new way, which resets the parity + * bit to be the same as the low-order bit in each character. The + * new way generates a greater variety of key schedules, since many + * systems set the parity (high) bit of each character to 0, and the + * DES ignores the low order bit of each character. + */ +void +set_des_key(buf) + Desbuf buf; /* key block */ +{ + register int i, j; /* counter in a for loop */ + register int par; /* parity counter */ + + /* + * if the parity is not preserved, flip it + */ + if (!pflag) { + for (i = 0; i < 8; i++) { + par = 0; + for (j = 1; j < 8; j++) + if ((bits[j]&UCHAR(buf, i)) != 0) + par++; + if ((par&01) == 01) + UCHAR(buf, i) = UCHAR(buf, i)&0177; + else + UCHAR(buf, i) = (UCHAR(buf, i)&0177)|0200; + } + } + + DES_KEY(UBUFFER(buf)); +} + + +/* + * This encrypts using the Cipher Block Chaining mode of DES + */ +int +cbc_encode(msgbuf, n, fp) + char *msgbuf; + int n; + FILE *fp; +{ + int inverse = 0; /* 0 to encrypt, 1 to decrypt */ + + /* + * do the transformation + */ + if (n == 8) { + for (n = 0; n < 8; n++) + CHAR(msgbuf, n) ^= CHAR(ivec, n); + DES_XFORM(UBUFFER(msgbuf)); + MEMCPY(BUFFER(ivec), BUFFER(msgbuf), 8); + return WRITE(BUFFER(msgbuf), 8, fp); + } + /* + * at EOF or last block -- in either case, the last byte contains + * the character representation of the number of bytes in it + */ +/* + MEMZERO(msgbuf + n, 8 - n); +*/ + /* + * Pad the last block randomly + */ + (void)MEMCPY(BUFFER(msgbuf + n), BUFFER(pvec), 8 - n); + CHAR(msgbuf, 7) = n; + for (n = 0; n < 8; n++) + CHAR(msgbuf, n) ^= CHAR(ivec, n); + DES_XFORM(UBUFFER(msgbuf)); + return WRITE(BUFFER(msgbuf), 8, fp); +} + +/* + * This decrypts using the Cipher Block Chaining mode of DES + */ +int +cbc_decode(msgbuf, fp) + char *msgbuf; /* I/O buffer */ + FILE *fp; /* input file descriptor */ +{ + Desbuf ibuf; /* temp buffer for initialization vector */ + register int n; /* number of bytes actually read */ + register int c; /* used to test for EOF */ + int inverse = 1; /* 0 to encrypt, 1 to decrypt */ + + if ((n = READ(BUFFER(msgbuf), 8, fp)) == 8) { + /* + * do the transformation + */ + MEMCPY(BUFFER(ibuf), BUFFER(msgbuf), 8); + DES_XFORM(UBUFFER(msgbuf)); + for (c = 0; c < 8; c++) + UCHAR(msgbuf, c) ^= UCHAR(ivec, c); + MEMCPY(BUFFER(ivec), BUFFER(ibuf), 8); + /* + * if the last one, handle it specially + */ + if ((c = fgetc(fp)) == EOF) { + n = CHAR(msgbuf, 7); + if (n < 0 || n > 7) { + des_error("decryption failed (block corrupted)"); + return EOF; + } + } else + (void)ungetc(c, fp); + return n; + } + if (n > 0) + des_error("decryption failed (incomplete block)"); + else if (n < 0) + des_error("cannot read file"); + return EOF; +} +#endif /* DES */ diff --git a/bin/ed/ed.1 b/bin/ed/ed.1 new file mode 100644 index 0000000..8f71b36 --- /dev/null +++ b/bin/ed/ed.1 @@ -0,0 +1,904 @@ +.\" $FreeBSD$ +.Dd May 21, 1993 +.Dt ED 1 +.Os +.Sh NAME +.\" ed, red \- text editor +.Nm ed , +.Nm red +.Nd text editor +.Sh SYNOPSIS +.Nm ed +.Op Fl +.Op Fl sx +.Op Fl p Ar string +.Op Ar file +.\" .LP +.\" red [-] [-sx] [-p \fIstring\fR] [\fIfile\fR] +.Sh DESCRIPTION +.Nm Ed +is a line-oriented text editor. +It is used to create, display, modify and otherwise manipulate text +files. +.\" .B red +.\" is a restricted +.\" .BR ed : +.\" it can only edit files in the current +.\" directory and cannot execute shell commands. + +If invoked with a +.Ar file +argument, then a copy of +.Ar file +is read into the editor's buffer. +Changes are made to this copy and not directly to +.Ar file +itself. +Upon quitting +.Nm Ns , +any changes not explicitly saved with a +.Em w +command are lost. + +Editing is done in two distinct modes: +.Em command +and +.Em input . +When first invoked, +.Nm +is in command mode. +In this mode commands are read from the standard input and +executed to manipulate the contents of the editor buffer. +A typical command might look like: +.Pp +.Sm off +.Cm ,s No / Em old Xo +.No / Em new +.No / Cm g +.Xc +.Sm on +.Pp +which replaces all occurrences of the string +.Em old +with +.Em new . +.Pp +When an input command, such as +.Em a +(append), +.Em i +(insert) or +.Em c +(change), is given, +.Nm +enters input mode. This is the primary means +of adding text to a file. +In this mode, no commands are available; +instead, the standard input is written +directly to the editor buffer. Lines consist of text up to and +including a +.Em newline +character. +Input mode is terminated by +entering a single period +.Pf ( Em . Ns ) +on a line. +.Pp +All +.Nm +commands operate on whole lines or ranges of lines; e.g., +the +.Em d +command deletes lines; the +.Em m +command moves lines, and so on. +It is possible to modify only a portion of a line by means of replacement, +as in the example above. However even here, the +.Em s +command is applied to whole lines at a time. +.Pp +In general, +.Nm +commands consist of zero or more line addresses, followed by a single +character command and possibly additional parameters; i.e., +commands have the structure: +.Pp +.Sm off +.Xo Op "address\ " Op ,address +.No command Op parameters +.Xc +.Sm on +.Pp +The address(es) indicate the line or range of lines to be affected by the +command. If fewer addresses are given than the command accepts, then +default addresses are supplied. +.Sh OPTIONS +The following options are available: +.Bl -tag -width indent +.It Fl s +Suppress diagnostics. +This should be used if +.Nm Ns 's +standard input is from a script. +.It Fl x +Prompt for an encryption key to be used in subsequent reads and writes +(see the +.Em x +command). +.It Fl p Ar string +Specify a command prompt. This may be toggled on and off with the +.Em P +command. +.It Ar file +Specify the name of a file to read. If +.Ar file +is prefixed with a +bang (!), then it is interpreted as a shell command. In this case, +what is read is +the standard output of +.Ar file +executed via +.Xr sh 1 . +To read a file whose name begins with a bang, prefix the +name with a backslash (\\). +The default filename is set to +.Ar file +only if it is not prefixed with a bang. +.El +.Sh LINE ADDRESSING +An address represents the number of a line in the buffer. +.Nm Ed +maintains a +.Em current address +which is +typically supplied to commands as the default address when none is specified. +When a file is first read, the current address is set to the last line +of the file. In general, the current address is set to the last line +affected by a command. +.Pp +A line address is +constructed from one of the bases in the list below, optionally followed +by a numeric offset. The offset may include any combination +of digits, operators (i.e., +.Em + , +.Em - +and +.Em ^ ) +and whitespace. +Addresses are read from left to right, and their values are computed +relative to the current address. +.Pp +One exception to the rule that addresses represent line numbers is the +address +.Em 0 +(zero). +This means "before the first line," +and is legal wherever it makes sense. +.Pp +An address range is two addresses separated either by a comma or +semi-colon. +The value of the first address in a range cannot exceed the +value of the second. If only one address is given in a range, then +the second address is set to the given address. If an +.Em n Ns -tuple +of addresses is given where +.Em "n\ >\ 2" , +then the corresponding range is determined by the last two addresses in +the +.Em n Ns -tuple. +If only one address is expected, then the last address is used. +.Pp +Each address in a comma-delimited range is interpreted relative to the +current address. In a semi-colon-delimited range, the first address is +used to set the current address, and the second address is interpreted +relative to the first. +.Pp +The following address symbols are recognized: +.Bl -tag -width indent +.It . +The current line (address) in the buffer. +.It $ +The last line in the buffer. +.It n +The +.Em n Ns th, +line in the buffer +where +.Em n +is a number in the range +.Em [0,$] . +.It - or ^ +The previous line. +This is equivalent to +.Em -1 +and may be repeated with cumulative effect. +.It -n or ^n +The +.Em n Ns th +previous line, where +.Em n +is a non-negative number. +.It + +The next line. +This is equivalent to +.Em +1 +and may be repeated with cumulative effect. +.It +n +The +.Em n Ns th +next line, where +.Em n +is a non-negative number. +.It , or % +The first through last lines in the buffer. This is equivalent to +the address range +.Em 1,$ . +.It ; +The current through last lines in the buffer. This is equivalent to +the address range +.Em .,$ . +.It /re/ +The next line containing the regular expression +.Em re . +The search wraps to the beginning of the buffer and continues down to the +current line, if necessary. +// repeats the last search. +.It ?re? +The +previous line containing the regular expression +.Em re . +The search wraps to the end of the buffer and continues up to the +current line, if necessary. +?? repeats the last search. +.It 'lc +The +line previously marked by a +.Em k +(mark) command, where +.Em lc +is a lower case letter. +.El +.Sh REGULAR EXPRESSIONS +Regular expressions are patterns used in selecting text. +For example, the command: +.Pp +.Sm off +.Cm g No / Em string Xo +.No / +.Xc +.Sm on +.Pp +prints all lines containing +.Em string . +Regular expressions are also +used by the +.Em s +command for selecting old text to be replaced with new. +.Pp +In addition to a specifying string literals, regular expressions can +represent +classes of strings. Strings thus represented are said to be matched +by the corresponding regular expression. +If it is possible for a regular expression +to match several strings in a line, then the left-most longest match is +the one selected. +.Pp +The following symbols are used in constructing regular expressions: +.Bl -tag -width indent +.It c +Any character +.Em c +not listed below, including `{', '}', `(', `)', `<' and `>', +matches itself. +.It Pf \e c +Any backslash-escaped character +.Em c , +except for `{', '}', `(', `)', `<' and `>', +matches itself. +.It . +Match any single character. +.It Op char-class +Match any single character in +.Em char-class . +To include a `]' +in +.Em char-class , +it must be the first character. +A range of characters may be specified by separating the end characters +of the range with a `-', e.g., `a-z' specifies the lower case characters. +The following literal expressions can also be used in +.Em char-class +to specify sets of characters: +.Pp +\ \ [:alnum:]\ \ [:cntrl:]\ \ [:lower:]\ \ [:space:] +.PD 0 +\ \ [:alpha:]\ \ [:digit:]\ \ [:print:]\ \ [:upper:] +.PD 0 +\ \ [:blank:]\ \ [:graph:]\ \ [:punct:]\ \ [:xdigit:] +.Pp +If `-' appears as the first or last +character of +.Em char-class , +then it matches itself. +All other characters in +.Em char-class +match themselves. +.Pp +Patterns in +.Em char-class +of the form: +.Pp +\ \ [.\fIcol-elm\fR.] or, +.PD 0 +\ \ [=\fIcol-elm\fR=] +.Pp +where +.Em col-elm +is a +.Em collating element +are interpreted according to +.Xr locale 5 +(not currently supported). +See +.Xr regex 3 +for an explanation of these constructs. +.It Op ^char-class +Match any single character, other than newline, not in +.Em char-class . +.Em Char-class +is defined +as above. +.It ^ +If +.Em ^ +is the first character of a regular expression, then it +anchors the regular expression to the beginning of a line. +Otherwise, it matches itself. +.It $ +If +.Em $ +is the last character of a regular expression, it +anchors the regular expression to the end of a line. +Otherwise, it matches itself. +.It Pf \e < +Anchor the single character regular expression or subexpression +immediately following it to the beginning of a word. +(This may not be available) +.It Pf \e > +Anchor the single character regular expression or subexpression +immediately following it to the end of a word. +(This may not be available) +.It Pf \e (re\e) +Define a subexpression +.Em re . +Subexpressions may be nested. +A subsequent backreference of the form +.Em Pf \e n , +where +.Em n +is a number in the range [1,9], expands to the text matched by the +.Em n Ns th +subexpression. +For example, the regular expression `\e(.*\e)\e1' matches any string +consisting of identical adjacent substrings. +Subexpressions are ordered relative to +their left delimiter. +.It * +Match the single character regular expression or subexpression +immediately preceding it zero or more times. If +.Em * +is the first +character of a regular expression or subexpression, then it matches +itself. The +.Em * +operator sometimes yields unexpected results. +For example, the regular expression `b*' matches the beginning of +the string `abbb' (as opposed to the substring `bbb'), since a null match +is the only left-most match. +.It \e{n,m\e} or \e{n,\e} or \e{n\e} +Match the single character regular expression or subexpression +immediately preceding it at least +.Em n +and at most +.Em m +times. +If +.Em m +is omitted, then it matches at least +.Em n +times. +If the comma is also omitted, then it matches exactly +.Em n +times. +.El +.Pp +Additional regular expression operators may be defined depending on the +particular +.Xr regex 3 +implementation. +.Sh COMMANDS +All +.Nm +commands are single characters, though some require additional parameters. +If a command's parameters extend over several lines, then +each line except for the last +must be terminated with a backslash (\\). + +In general, at most one command is allowed per line. +However, most commands accept a print suffix, which is any of +.Em p +(print), +.Em l +(list) , +or +.Em n +(enumerate), +to print the last line affected by the command. + +An interrupt (typically ^C) has the effect of aborting the current command +and returning the editor to command mode. + +.Nm Ed +recognizes the following commands. The commands are shown together with +the default address or address range supplied if none is +specified (in parenthesis). +.Bl -tag -width indent +.It (.)a +Append text to the buffer after the addressed line. +Text is entered in input mode. +The current address is set to last line entered. +.It (.,.)c +Change lines in the buffer. The addressed lines are deleted +from the buffer, and text is appended in their place. +Text is entered in input mode. +The current address is set to last line entered. +.It (.,.)d +Delete the addressed lines from the buffer. +If there is a line after the deleted range, then the current address is set +to this line. +Otherwise the current address is set to the line +before the deleted range. +.It e Ar file +Edit +.Ar file , +and sets the default filename. +If +.Ar file +is not specified, then the default filename is used. +Any lines in the buffer are deleted before +the new file is read. +The current address is set to the last line read. +.It e Ar !command +Edit the standard output of +.Ar !command , +(see +.Ar !command +below). +The default filename is unchanged. +Any lines in the buffer are deleted before the output of +.Ar command +is read. +The current address is set to the last line read. +.It E Ar file +Edit +.Ar file +unconditionally. +This is similar to the +.Em e +command, +except that unwritten changes are discarded without warning. +The current address is set to the last line read. +.It f Ar file +Set the default filename to +.Ar file . +If +.Ar file +is not specified, then the default unescaped filename is printed. +.It (1,$)g/re/command-list +Apply +.Ar command-list +to each of the addressed lines matching a regular expression +.Ar re . +The current address is set to the +line currently matched before +.Ar command-list +is executed. +At the end of the +.Em g +command, the current address is set to the last line affected by +.Ar command-list . +.Pp +Each command in +.Ar command-list +must be on a separate line, +and every line except for the last must be terminated by a backslash +(\\). +Any commands are allowed, except for +.Em g , +.Em G , +.Em v , +and +.Em V . +A newline alone in +.Ar command-list +is equivalent to a +.Em p +command. +.It (1,$)G/re/ +Interactively edit the addressed lines matching a regular expression +.Ar re . +For each matching line, +the line is printed, +the current address is set, +and the user is prompted to enter a +.Ar command-list . +At the end of the +.Em G +command, the current address +is set to the last line affected by (the last) +.Ar command-list . +.Pp +The format of +.Ar command-list +is the same as that of the +.Em g +command. A newline alone acts as a null command list. +A single `&' repeats the last non-null command list. +.It H +Toggle the printing of error explanations. +By default, explanations are not printed. +It is recommended that ed scripts begin with this command to +aid in debugging. +.It h +Print an explanation of the last error. +.It (.)i +Insert text in the buffer before the current line. +Text is entered in input mode. +The current address is set to the last line entered. +.It (.,.+1)j +Join the addressed lines. The addressed lines are +deleted from the buffer and replaced by a single +line containing their joined text. +The current address is set to the resultant line. +.It (.)klc +Mark a line with a lower case letter +.Em lc . +The line can then be addressed as +.Em 'lc +(i.e., a single quote followed by +.Em lc +) in subsequent commands. The mark is not cleared until the line is +deleted or otherwise modified. +.It (.,.)l +Print the addressed lines unambiguously. +If a single line fills for than one screen (as might be the case +when viewing a binary file, for instance), a `--More--' +prompt is printed on the last line. +.Nm Ed +waits until the RETURN key is pressed +before displaying the next screen. +The current address is set to the last line +printed. +.It (.,.)m(.) +Move lines in the buffer. The addressed lines are moved to after the +right-hand destination address, which may be the address +.Em 0 +(zero). +The current address is set to the +last line moved. +.It (.,.)n +Print the addressed lines along with +their line numbers. The current address is set to the last line +printed. +.It (.,.)p +Print the addressed lines. +The current address is set to the last line +printed. +.It P +Toggle the command prompt on and off. +Unless a prompt was specified by with command-line option +.Fl p Ar string , +the command prompt is by default turned off. +.It q +Quit +.Nm Ns . +.It Q +Quit +.Nm +unconditionally. +This is similar to the +.Em q +command, +except that unwritten changes are discarded without warning. +.It ($)r Ar file +Read +.Ar file +to after the addressed line. If +.Ar file +is not specified, then the default +filename is used. If there was no default filename prior to the command, +then the default filename is set to +.Ar file . +Otherwise, the default filename is unchanged. +The current address is set to the last line read. +.It ($)r Ar !command +Read +to after the addressed line +the standard output of +.Ar !command , +(see the +.Ar !command +below). +The default filename is unchanged. +The current address is set to the last line read. +.It (.,.)s/re/replacement/ +.It (.,.)s/re/replacement/g +.It (.,.)s/re/replacement/n +Replace text in the addressed lines +matching a regular expression +.Ar re +with +.Ar replacement . +By default, only the first match in each line is replaced. +If the +.Em g +(global) suffix is given, then every match to be replaced. +The +.Em n +suffix, where +.Em n +is a positive number, causes only the +.Em n Ns th +match to be replaced. +It is an error if no substitutions are performed on any of the addressed +lines. +The current address is set the last line affected. +.Pp +.Ar Re +and +.Ar replacement +may be delimited by any character other than space and newline +(see the +.Em s +command below). +If one or two of the last delimiters is omitted, then the last line +affected is printed as though the print suffix +.Em p +were specified. +.Pp +An unescaped `&' in +.Ar replacement +is replaced by the currently matched text. +The character sequence +.Em \em , +where +.Em m +is a number in the range [1,9], is replaced by the +.Em m th +backreference expression of the matched text. +If +.Ar replacement +consists of a single `%', then +.Ar replacement +from the last substitution is used. +Newlines may be embedded in +.Ar replacement +if they are escaped with a backslash (\\). +.It (.,.)s +Repeat the last substitution. +This form of the +.Em s +command accepts a count suffix +.Em n , +or any combination of the characters +.Em r , +.Em g , +and +.Em p . +If a count suffix +.Em n +is given, then only the +.Em n Ns th +match is replaced. +The +.Em r +suffix causes +the regular expression of the last search to be used instead of the +that of the last substitution. +The +.Em g +suffix toggles the global suffix of the last substitution. +The +.Em p +suffix toggles the print suffix of the last substitution +The current address is set to the last line affected. +.It (.,.)t(.) +Copy (i.e., transfer) the addressed lines to after the right-hand +destination address, which may be the address +.Em 0 +(zero). +The current address is set to the last line +copied. +.It u +Undo the last command and restores the current address +to what it was before the command. +The global commands +.Em g , +.Em G , +.Em v , +and +.Em V . +are treated as a single command by undo. +.Em u +is its own inverse. +.It (1,$)v/re/command-list +Apply +.Ar command-list +to each of the addressed lines not matching a regular expression +.Ar re . +This is similar to the +.Em g +command. +.It (1,$)V/re/ +Interactively edit the addressed lines not matching a regular expression +.Ar re . +This is similar to the +.Em G +command. +.It (1,$)w Ar file +Write the addressed lines to +.Ar file . +Any previous contents of +.Ar file +is lost without warning. +If there is no default filename, then the default filename is set to +.Ar file , +otherwise it is unchanged. If no filename is specified, then the default +filename is used. +The current address is unchanged. +.It (1,$)wq Ar file +Write the addressed lines to +.Ar file , +and then executes a +.Em q +command. +.It (1,$)w Ar !command +Write the addressed lines to the standard input of +.Ar !command , +(see the +.Em !command +below). +The default filename and current address are unchanged. +.It (1,$)W Ar file +Append the addressed lines to the end of +.Ar file . +This is similar to the +.Em w +command, expect that the previous contents of file is not clobbered. +The current address is unchanged. +.It x +Prompt for an encryption key which is used in subsequent reads and +writes. If a newline alone is entered as the key, then encryption is +turned off. Otherwise, echoing is disabled while a key is read. +Encryption/decryption is done using the +.Xr bdes 1 +algorithm. +.It Pf (.+1)z n +Scroll +.Ar n +lines at a time starting at addressed line. If +.Ar n +is not specified, then the current window size is used. +The current address is set to the last line printed. +.It !command +Execute +.Ar command +via +.Xr sh 1 . +If the first character of +.Ar command +is `!', then it is replaced by text of the +previous +.Ar !command . +.Nm Ed +does not process +.Ar command +for backslash (\\) escapes. +However, an unescaped +.Em % +is replaced by the default filename. +When the shell returns from execution, a `!' +is printed to the standard output. +The current line is unchanged. +.It ($)= +Print the line number of the addressed line. +.It (.+1)newline +Print the addressed line, and sets the current address to +that line. +.El +.Sh FILES +.Bl -tag -width /tmp/ed.* -compact +.It /tmp/ed.* +buffer file +.It ed.hup +the file to which +.Nm +attempts to write the buffer if the terminal hangs up +.El +.Sh SEE ALSO +.Xr bdes 1 , +.Xr sed 1 , +.Xr sh 1 , +.Xr vi 1 , +.Xr regex 3 + +USD:12-13 + +.Rs +.%A B. W. Kernighan +.%A P. J. Plauger +.%B Software Tools in Pascal +.%O Addison-Wesley +.%D 1981 +.Re + +.Sh LIMITATIONS +.Nm Ed +processes +.Ar file +arguments for backslash escapes, i.e., in a filename, +any characters preceded by a backslash (\\) are +interpreted literally. + +If a text (non-binary) file is not terminated by a newline character, +then +.Nm +appends one on reading/writing it. In the case of a binary file, +.Nm +does not append a newline on reading/writing. + +per line overhead: 4 ints + +.Sh DIAGNOSTICS +When an error occurs, +.Nm +prints a `?' and either returns to command mode +or exits if its input is from a script. +An explanation of the last error can be +printed with the +.Em h +(help) command. + +Since the +.Em g +(global) command masks any errors from failed searches and substitutions, +it can be used to perform conditional operations in scripts; e.g., +.Pp +.Sm off +.Cm g No / Em old Xo +.No / Cm s +.No // Em new +.No / +.Xc +.Sm on +.Pp +replaces any occurrences of +.Em old +with +.Em new . +If the +.Em u +(undo) command occurs in a global command list, then +the command list is executed only once. + +If diagnostics are not disabled, attempting to quit +.Nm +or edit another file before writing a modified buffer +results in an error. +If the command is entered a second time, it succeeds, +but any changes to the buffer are lost. +.Sh HISTORY +A +.Nm +command appeared in +Version 1 AT&T UNIX. diff --git a/bin/ed/ed.h b/bin/ed/ed.h new file mode 100644 index 0000000..d313a84 --- /dev/null +++ b/bin/ed/ed.h @@ -0,0 +1,293 @@ +/* ed.h: type and constant definitions for the ed editor. */ +/* + * Copyright (c) 1993 Andrew Moore + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ed.h,v 1.5 1994/02/01 00:34:39 alm Exp + * $FreeBSD$ + */ + +#include <sys/param.h> /* for MAXPATHLEN */ +#include <errno.h> +#if defined(sun) || defined(__NetBSD__) +# include <limits.h> +#endif +#include <regex.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define ERR (-2) +#define EMOD (-3) +#define FATAL (-4) + +#ifndef MAXPATHLEN +# define MAXPATHLEN 255 /* _POSIX_PATH_MAX */ +#endif + +#define MINBUFSZ 512 /* minimum buffer size - must be > 0 */ +#define SE_MAX 30 /* max subexpressions in a regular expression */ +#ifdef INT_MAX +# define LINECHARS INT_MAX /* max chars per line */ +#else +# define LINECHARS MAXINT /* max chars per line */ +#endif + +/* gflags */ +#define GLB 001 /* global command */ +#define GPR 002 /* print after command */ +#define GLS 004 /* list after command */ +#define GNP 010 /* enumerate after command */ +#define GSG 020 /* global substitute */ + +typedef regex_t pattern_t; + +/* Line node */ +typedef struct line { + struct line *q_forw; + struct line *q_back; + off_t seek; /* address of line in scratch buffer */ + int len; /* length of line */ +} line_t; + + +typedef struct undo { + +/* type of undo nodes */ +#define UADD 0 +#define UDEL 1 +#define UMOV 2 +#define VMOV 3 + + int type; /* command type */ + line_t *h; /* head of list */ + line_t *t; /* tail of list */ +} undo_t; + +#ifndef max +# define max(a,b) ((a) > (b) ? (a) : (b)) +#endif +#ifndef min +# define min(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#define INC_MOD(l, k) ((l) + 1 > (k) ? 0 : (l) + 1) +#define DEC_MOD(l, k) ((l) - 1 < 0 ? (k) : (l) - 1) + +/* SPL1: disable some interrupts (requires reliable signals) */ +#define SPL1() mutex++ + +/* SPL0: enable all interrupts; check sigflags (requires reliable signals) */ +#define SPL0() \ +if (--mutex == 0) { \ + if (sigflags & (1 << (SIGHUP - 1))) handle_hup(SIGHUP); \ + if (sigflags & (1 << (SIGINT - 1))) handle_int(SIGINT); \ +} + +/* STRTOL: convert a string to long */ +#define STRTOL(i, p) { \ + if (((i = strtol(p, &p, 10)) == LONG_MIN || i == LONG_MAX) && \ + errno == ERANGE) { \ + sprintf(errmsg, "number out of range"); \ + i = 0; \ + return ERR; \ + } \ +} + +#if defined(sun) || defined(NO_REALLOC_NULL) +/* REALLOC: assure at least a minimum size for buffer b */ +#define REALLOC(b,n,i,err) \ +if ((i) > (n)) { \ + int ti = (n); \ + char *ts; \ + SPL1(); \ + if ((b) != NULL) { \ + if ((ts = (char *) realloc((b), ti += max((i), MINBUFSZ))) == NULL) { \ + fprintf(stderr, "%s\n", strerror(errno)); \ + sprintf(errmsg, "out of memory"); \ + SPL0(); \ + return err; \ + } \ + } else { \ + if ((ts = (char *) malloc(ti += max((i), MINBUFSZ))) == NULL) { \ + fprintf(stderr, "%s\n", strerror(errno)); \ + sprintf(errmsg, "out of memory"); \ + SPL0(); \ + return err; \ + } \ + } \ + (n) = ti; \ + (b) = ts; \ + SPL0(); \ +} +#else /* NO_REALLOC_NULL */ +/* REALLOC: assure at least a minimum size for buffer b */ +#define REALLOC(b,n,i,err) \ +if ((i) > (n)) { \ + int ti = (n); \ + char *ts; \ + SPL1(); \ + if ((ts = (char *) realloc((b), ti += max((i), MINBUFSZ))) == NULL) { \ + fprintf(stderr, "%s\n", strerror(errno)); \ + sprintf(errmsg, "out of memory"); \ + SPL0(); \ + return err; \ + } \ + (n) = ti; \ + (b) = ts; \ + SPL0(); \ +} +#endif /* NO_REALLOC_NULL */ + +/* REQUE: link pred before succ */ +#define REQUE(pred, succ) (pred)->q_forw = (succ), (succ)->q_back = (pred) + +/* INSQUE: insert elem in circular queue after pred */ +#define INSQUE(elem, pred) \ +{ \ + REQUE((elem), (pred)->q_forw); \ + REQUE((pred), elem); \ +} + +/* REMQUE: remove_lines elem from circular queue */ +#define REMQUE(elem) REQUE((elem)->q_back, (elem)->q_forw); + +/* NUL_TO_NEWLINE: overwrite ASCII NULs with newlines */ +#define NUL_TO_NEWLINE(s, l) translit_text(s, l, '\0', '\n') + +/* NEWLINE_TO_NUL: overwrite newlines with ASCII NULs */ +#define NEWLINE_TO_NUL(s, l) translit_text(s, l, '\n', '\0') + +#ifdef sun +# define strerror(n) sys_errlist[n] +#endif + +#ifndef __P +# ifndef __STDC__ +# define __P(proto) () +# else +# define __P(proto) proto +# endif +#endif + +/* Local Function Declarations */ +void add_line_node __P((line_t *)); +int append_lines __P((long)); +int apply_subst_template __P((char *, regmatch_t *, int, int)); +int build_active_list __P((int)); +int cbc_decode __P((char *, FILE *)); +int cbc_encode __P((char *, int, FILE *)); +int check_addr_range __P((long, long)); +void clear_active_list __P((void)); +void clear_undo_stack __P((void)); +int close_sbuf __P((void)); +int copy_lines __P((long)); +int delete_lines __P((long, long)); +void des_error __P((char *)); +int display_lines __P((long, long, int)); +line_t *dup_line_node __P((line_t *)); +int exec_command __P((void)); +long exec_global __P((int, int)); +void expand_des_key __P((char *, char *)); +int extract_addr_range __P((void)); +char *extract_pattern __P((int)); +int extract_subst_tail __P((int *, long *)); +char *extract_subst_template __P((void)); +int filter_lines __P((long, long, char *)); +int flush_des_file __P((FILE *)); +line_t *get_addressed_line_node __P((long)); +pattern_t *get_compiled_pattern __P((void)); +int get_des_char __P((FILE *)); +char *get_extended_line __P((int *, int)); +char *get_filename __P((void)); +int get_keyword __P((void)); +long get_line_node_addr __P((line_t *)); +long get_matching_node_addr __P((pattern_t *, int)); +long get_marked_node_addr __P((int)); +char *get_sbuf_line __P((line_t *)); +int get_shell_command __P((void)); +int get_stream_line __P((FILE *)); +int get_tty_line __P((void)); +void handle_hup __P((int)); +void handle_int __P((int)); +void handle_winch __P((int)); +int has_trailing_escape __P((char *, char *)); +int hex_to_binary __P((int, int)); +void init_buffers __P((void)); +void init_des_cipher __P((void)); +int is_legal_filename __P((char *)); +int join_lines __P((long, long)); +int mark_line_node __P((line_t *, int)); +int move_lines __P((long)); +line_t *next_active_node __P(()); +long next_addr __P((void)); +int open_sbuf __P((void)); +char *parse_char_class __P((char *)); +int pop_undo_stack __P((void)); +undo_t *push_undo_stack __P((int, long, long)); +int put_des_char __P((int, FILE *)); +char *put_sbuf_line __P((char *)); +int put_stream_line __P((FILE *, char *, int)); +int put_tty_line __P((char *, int, long, int)); +void quit __P((int)); +long read_file __P((char *, long)); +long read_stream __P((FILE *, long)); +int search_and_replace __P((pattern_t *, int, int)); +int set_active_node __P((line_t *)); +void set_des_key __P((char *)); +void signal_hup __P((int)); +void signal_int __P((int)); +char *strip_escapes __P((char *)); +int substitute_matching_text __P((pattern_t *, line_t *, int, int)); +char *translit_text __P((char *, int, int, int)); +void unmark_line_node __P((line_t *)); +void unset_active_nodes __P((line_t *, line_t *)); +long write_file __P((char *, char *, long, long)); +long write_stream __P((FILE *, long, long)); + +/* global buffers */ +extern char stdinbuf[]; +extern char *ibuf; +extern char *ibufp; +extern int ibufsz; + +/* global flags */ +extern int isbinary; +extern int isglobal; +extern int modified; +extern int mutex; +extern int sigflags; + +/* global vars */ +extern long addr_last; +extern long current_addr; +extern char errmsg[]; +extern long first_addr; +extern int lineno; +extern long second_addr; +#ifdef sun +extern char *sys_errlist[]; +#endif diff --git a/bin/ed/glbl.c b/bin/ed/glbl.c new file mode 100644 index 0000000..3eae4f2 --- /dev/null +++ b/bin/ed/glbl.c @@ -0,0 +1,228 @@ +/* glob.c: This file contains the global command routines for the ed line + editor */ +/*- + * Copyright (c) 1993 Andrew Moore, Talke Studio. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 +#if 0 +static char * const rcsid = "@(#)glob.c,v 1.1 1994/02/01 00:34:40 alm Exp"; +#else +static char * const rcsid = + "$FreeBSD$"; +#endif +#endif /* not lint */ + +#include <sys/ioctl.h> +#include <sys/wait.h> + +#include "ed.h" + + +/* build_active_list: add line matching a pattern to the global-active list */ +int +build_active_list(isgcmd) + int isgcmd; +{ + pattern_t *pat; + line_t *lp; + long n; + char *s; + char delimiter; + + if ((delimiter = *ibufp) == ' ' || delimiter == '\n') { + sprintf(errmsg, "invalid pattern delimiter"); + return ERR; + } else if ((pat = get_compiled_pattern()) == NULL) + return ERR; + else if (*ibufp == delimiter) + ibufp++; + clear_active_list(); + lp = get_addressed_line_node(first_addr); + for (n = first_addr; n <= second_addr; n++, lp = lp->q_forw) { + if ((s = get_sbuf_line(lp)) == NULL) + return ERR; + if (isbinary) + NUL_TO_NEWLINE(s, lp->len); + if (!regexec(pat, s, 0, NULL, 0) == isgcmd && + set_active_node(lp) < 0) + return ERR; + } + return 0; +} + + +/* exec_global: apply command list in the command buffer to the active + lines in a range; return command status */ +long +exec_global(interact, gflag) + int interact; + int gflag; +{ + static char *ocmd = NULL; + static int ocmdsz = 0; + + line_t *lp = NULL; + int status; + int n; + char *cmd = NULL; + +#ifdef BACKWARDS + if (!interact) + if (!strcmp(ibufp, "\n")) + cmd = "p\n"; /* null cmd-list == `p' */ + else if ((cmd = get_extended_line(&n, 0)) == NULL) + return ERR; +#else + if (!interact && (cmd = get_extended_line(&n, 0)) == NULL) + return ERR; +#endif + clear_undo_stack(); + while ((lp = next_active_node()) != NULL) { + if ((current_addr = get_line_node_addr(lp)) < 0) + return ERR; + if (interact) { + /* print current_addr; get a command in global syntax */ + if (display_lines(current_addr, current_addr, gflag) < 0) + return ERR; + while ((n = get_tty_line()) > 0 && + ibuf[n - 1] != '\n') + clearerr(stdin); + if (n < 0) + return ERR; + else if (n == 0) { + sprintf(errmsg, "unexpected end-of-file"); + return ERR; + } else if (n == 1 && !strcmp(ibuf, "\n")) + continue; + else if (n == 2 && !strcmp(ibuf, "&\n")) { + if (cmd == NULL) { + sprintf(errmsg, "no previous command"); + return ERR; + } else cmd = ocmd; + } else if ((cmd = get_extended_line(&n, 0)) == NULL) + return ERR; + else { + REALLOC(ocmd, ocmdsz, n + 1, ERR); + memcpy(ocmd, cmd, n + 1); + cmd = ocmd; + } + + } + ibufp = cmd; + for (; *ibufp;) + if ((status = extract_addr_range()) < 0 || + (status = exec_command()) < 0 || + (status > 0 && (status = display_lines( + current_addr, current_addr, status)) < 0)) + return status; + } + return 0; +} + + +line_t **active_list; /* list of lines active in a global command */ +long active_last; /* index of last active line in active_list */ +long active_size; /* size of active_list */ +long active_ptr; /* active_list index (non-decreasing) */ +long active_ndx; /* active_list index (modulo active_last) */ + +/* set_active_node: add a line node to the global-active list */ +int +set_active_node(lp) + line_t *lp; +{ + if (active_last + 1 > active_size) { + int ti = active_size; + line_t **ts; + SPL1(); +#if defined(sun) || defined(NO_REALLOC_NULL) + if (active_list != NULL) { +#endif + if ((ts = (line_t **) realloc(active_list, + (ti += MINBUFSZ) * sizeof(line_t **))) == NULL) { + fprintf(stderr, "%s\n", strerror(errno)); + sprintf(errmsg, "out of memory"); + SPL0(); + return ERR; + } +#if defined(sun) || defined(NO_REALLOC_NULL) + } else { + if ((ts = (line_t **) malloc((ti += MINBUFSZ) * + sizeof(line_t **))) == NULL) { + fprintf(stderr, "%s\n", strerror(errno)); + sprintf(errmsg, "out of memory"); + SPL0(); + return ERR; + } + } +#endif + active_size = ti; + active_list = ts; + SPL0(); + } + active_list[active_last++] = lp; + return 0; +} + + +/* unset_active_nodes: remove a range of lines from the global-active list */ +void +unset_active_nodes(np, mp) + line_t *np, *mp; +{ + line_t *lp; + long i; + + for (lp = np; lp != mp; lp = lp->q_forw) + for (i = 0; i < active_last; i++) + if (active_list[active_ndx] == lp) { + active_list[active_ndx] = NULL; + active_ndx = INC_MOD(active_ndx, active_last - 1); + break; + } else active_ndx = INC_MOD(active_ndx, active_last - 1); +} + + +/* next_active_node: return the next global-active line node */ +line_t * +next_active_node() +{ + while (active_ptr < active_last && active_list[active_ptr] == NULL) + active_ptr++; + return (active_ptr < active_last) ? active_list[active_ptr++] : NULL; +} + + +/* clear_active_list: clear the global-active list */ +void +clear_active_list() +{ + SPL1(); + active_size = active_last = active_ptr = active_ndx = 0; + free(active_list); + active_list = NULL; + SPL0(); +} diff --git a/bin/ed/io.c b/bin/ed/io.c new file mode 100644 index 0000000..2030798 --- /dev/null +++ b/bin/ed/io.c @@ -0,0 +1,380 @@ +/* io.c: This file contains the i/o routines for the ed line editor */ +/*- + * Copyright (c) 1993 Andrew Moore, Talke Studio. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 +#if 0 +static char * const rcsid = "@(#)io.c,v 1.1 1994/02/01 00:34:41 alm Exp"; +#else +static char * const rcsid = + "$FreeBSD$"; +#endif +#endif /* not lint */ + +#include "ed.h" + + +extern int scripted; + +/* read_file: read a named file/pipe into the buffer; return line count */ +long +read_file(fn, n) + char *fn; + long n; +{ + FILE *fp; + long size; + + + fp = (*fn == '!') ? popen(fn + 1, "r") : fopen(strip_escapes(fn), "r"); + if (fp == NULL) { + fprintf(stderr, "%s: %s\n", fn, strerror(errno)); + sprintf(errmsg, "cannot open input file"); + return ERR; + } else if ((size = read_stream(fp, n)) < 0) + return ERR; + else if (((*fn == '!') ? pclose(fp) : fclose(fp)) < 0) { + fprintf(stderr, "%s: %s\n", fn, strerror(errno)); + sprintf(errmsg, "cannot close input file"); + return ERR; + } + fprintf(stdout, !scripted ? "%lu\n" : "", size); + return current_addr - n; +} + + +extern int des; + +char *sbuf; /* file i/o buffer */ +int sbufsz; /* file i/o buffer size */ +int newline_added; /* if set, newline appended to input file */ + +/* read_stream: read a stream into the editor buffer; return status */ +long +read_stream(fp, n) + FILE *fp; + long n; +{ + line_t *lp = get_addressed_line_node(n); + undo_t *up = NULL; + unsigned long size = 0; + int o_newline_added = newline_added; + int o_isbinary = isbinary; + int appended = (n == addr_last); + int len; + + isbinary = newline_added = 0; + if (des) + init_des_cipher(); + for (current_addr = n; (len = get_stream_line(fp)) > 0; size += len) { + SPL1(); + if (put_sbuf_line(sbuf) == NULL) { + SPL0(); + return ERR; + } + lp = lp->q_forw; + if (up) + up->t = lp; + else if ((up = push_undo_stack(UADD, current_addr, + current_addr)) == NULL) { + SPL0(); + return ERR; + } + SPL0(); + } + if (len < 0) + return ERR; + if (appended && size && o_isbinary && o_newline_added) + fputs("newline inserted\n", stderr); + else if (newline_added && (!appended || (!isbinary && !o_isbinary))) + fputs("newline appended\n", stderr); + if (isbinary && newline_added && !appended) + size += 1; + if (!size) + newline_added = 1; + newline_added = appended ? newline_added : o_newline_added; + isbinary = isbinary | o_isbinary; + if (des) + size += 8 - size % 8; /* adjust DES size */ + return size; +} + + +/* get_stream_line: read a line of text from a stream; return line length */ +int +get_stream_line(fp) + FILE *fp; +{ + register int c; + register int i = 0; + + while (((c = des ? get_des_char(fp) : getc(fp)) != EOF || (!feof(fp) && + !ferror(fp))) && c != '\n') { + REALLOC(sbuf, sbufsz, i + 1, ERR); + if (!(sbuf[i++] = c)) + isbinary = 1; + } + REALLOC(sbuf, sbufsz, i + 2, ERR); + if (c == '\n') + sbuf[i++] = c; + else if (ferror(fp)) { + fprintf(stderr, "%s\n", strerror(errno)); + sprintf(errmsg, "cannot read input file"); + return ERR; + } else if (i) { + sbuf[i++] = '\n'; + newline_added = 1; + } + sbuf[i] = '\0'; + return (isbinary && newline_added && i) ? --i : i; +} + + +/* write_file: write a range of lines to a named file/pipe; return line count */ +long +write_file(fn, mode, n, m) + char *fn; + char *mode; + long n; + long m; +{ + FILE *fp; + long size; + + fp = (*fn == '!') ? popen(fn+1, "w") : fopen(strip_escapes(fn), mode); + if (fp == NULL) { + fprintf(stderr, "%s: %s\n", fn, strerror(errno)); + sprintf(errmsg, "cannot open output file"); + return ERR; + } else if ((size = write_stream(fp, n, m)) < 0) + return ERR; + else if (((*fn == '!') ? pclose(fp) : fclose(fp)) < 0) { + fprintf(stderr, "%s: %s\n", fn, strerror(errno)); + sprintf(errmsg, "cannot close output file"); + return ERR; + } + fprintf(stdout, !scripted ? "%lu\n" : "", size); + return n ? m - n + 1 : 0; +} + + +/* write_stream: write a range of lines to a stream; return status */ +long +write_stream(fp, n, m) + FILE *fp; + long n; + long m; +{ + line_t *lp = get_addressed_line_node(n); + unsigned long size = 0; + char *s; + int len; + + if (des) + init_des_cipher(); + for (; n && n <= m; n++, lp = lp->q_forw) { + if ((s = get_sbuf_line(lp)) == NULL) + return ERR; + len = lp->len; + if (n != addr_last || !isbinary || !newline_added) + s[len++] = '\n'; + if (put_stream_line(fp, s, len) < 0) + return ERR; + size += len; + } + if (des) { + flush_des_file(fp); /* flush buffer */ + size += 8 - size % 8; /* adjust DES size */ + } + return size; +} + + +/* put_stream_line: write a line of text to a stream; return status */ +int +put_stream_line(fp, s, len) + FILE *fp; + char *s; + int len; +{ + while (len--) + if ((des ? put_des_char(*s++, fp) : fputc(*s++, fp)) < 0) { + fprintf(stderr, "%s\n", strerror(errno)); + sprintf(errmsg, "cannot write file"); + return ERR; + } + return 0; +} + +/* get_extended_line: get a an extended line from stdin */ +char * +get_extended_line(sizep, nonl) + int *sizep; + int nonl; +{ + static char *cvbuf = NULL; /* buffer */ + static int cvbufsz = 0; /* buffer size */ + + int l, n; + char *t = ibufp; + + while (*t++ != '\n') + ; + if ((l = t - ibufp) < 2 || !has_trailing_escape(ibufp, ibufp + l - 1)) { + *sizep = l; + return ibufp; + } + *sizep = -1; + REALLOC(cvbuf, cvbufsz, l, NULL); + memcpy(cvbuf, ibufp, l); + *(cvbuf + --l - 1) = '\n'; /* strip trailing esc */ + if (nonl) l--; /* strip newline */ + for (;;) { + if ((n = get_tty_line()) < 0) + return NULL; + else if (n == 0 || ibuf[n - 1] != '\n') { + sprintf(errmsg, "unexpected end-of-file"); + return NULL; + } + REALLOC(cvbuf, cvbufsz, l + n, NULL); + memcpy(cvbuf + l, ibuf, n); + l += n; + if (n < 2 || !has_trailing_escape(cvbuf, cvbuf + l - 1)) + break; + *(cvbuf + --l - 1) = '\n'; /* strip trailing esc */ + if (nonl) l--; /* strip newline */ + } + REALLOC(cvbuf, cvbufsz, l + 1, NULL); + cvbuf[l] = '\0'; + *sizep = l; + return cvbuf; +} + + +/* get_tty_line: read a line of text from stdin; return line length */ +int +get_tty_line() +{ + register int oi = 0; + register int i = 0; + int c; + + for (;;) + switch (c = getchar()) { + default: + oi = 0; + REALLOC(ibuf, ibufsz, i + 2, ERR); + if (!(ibuf[i++] = c)) isbinary = 1; + if (c != '\n') + continue; + lineno++; + ibuf[i] = '\0'; + ibufp = ibuf; + return i; + case EOF: + if (ferror(stdin)) { + fprintf(stderr, "stdin: %s\n", strerror(errno)); + sprintf(errmsg, "cannot read stdin"); + clearerr(stdin); + ibufp = NULL; + return ERR; + } else { + clearerr(stdin); + if (i != oi) { + oi = i; + continue; + } else if (i) + ibuf[i] = '\0'; + ibufp = ibuf; + return i; + } + } +} + + + +#define ESCAPES "\a\b\f\n\r\t\v\\" +#define ESCCHARS "abfnrtv\\" + +extern int rows; +extern int cols; + +/* put_tty_line: print text to stdout */ +int +put_tty_line(s, l, n, gflag) + char *s; + int l; + long n; + int gflag; +{ + int col = 0; + int lc = 0; + char *cp; + + if (gflag & GNP) { + printf("%ld\t", n); + col = 8; + } + for (; l--; s++) { + if ((gflag & GLS) && ++col > cols) { + fputs("\\\n", stdout); + col = 1; +#ifndef BACKWARDS + if (!scripted && !isglobal && ++lc > rows) { + lc = 0; + fputs("Press <RETURN> to continue... ", stdout); + fflush(stdout); + if (get_tty_line() < 0) + return ERR; + } +#endif + } + if (gflag & GLS) { + if (31 < *s && *s < 127 && *s != '\\') + putchar(*s); + else { + putchar('\\'); + col++; + if (*s && (cp = strchr(ESCAPES, *s)) != NULL) + putchar(ESCCHARS[cp - ESCAPES]); + else { + putchar((((unsigned char) *s & 0300) >> 6) + '0'); + putchar((((unsigned char) *s & 070) >> 3) + '0'); + putchar(((unsigned char) *s & 07) + '0'); + col += 2; + } + } + + } else + putchar(*s); + } +#ifndef BACKWARDS + if (gflag & GLS) + putchar('$'); +#endif + putchar('\n'); + return 0; +} diff --git a/bin/ed/main.c b/bin/ed/main.c new file mode 100644 index 0000000..b73738d --- /dev/null +++ b/bin/ed/main.c @@ -0,0 +1,1448 @@ +/* main.c: This file contains the main control and user-interface routines + for the ed line editor. */ +/*- + * Copyright (c) 1993 Andrew Moore, Talke Studio. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 * const copyright = +"@(#) Copyright (c) 1993 Andrew Moore, Talke Studio. \n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char * const rcsid = "@(#)main.c,v 1.1 1994/02/01 00:34:42 alm Exp"; +#else +static char * const rcsid = + "$FreeBSD$"; +#endif +#endif /* not lint */ + +/* + * CREDITS + * + * This program is based on the editor algorithm described in + * Brian W. Kernighan and P. J. Plauger's book "Software Tools + * in Pascal," Addison-Wesley, 1981. + * + * The buffering algorithm is attributed to Rodney Ruddock of + * the University of Guelph, Guelph, Ontario. + * + * The cbc.c encryption code is adapted from + * the bdes program by Matt Bishop of Dartmouth College, + * Hanover, NH. + * + */ + +#include <sys/ioctl.h> +#include <sys/wait.h> +#include <ctype.h> +#include <locale.h> +#include <pwd.h> +#include <setjmp.h> + +#include "ed.h" + + +#ifdef _POSIX_SOURCE +sigjmp_buf env; +#else +jmp_buf env; +#endif + +/* static buffers */ +char stdinbuf[1]; /* stdin buffer */ +char *shcmd; /* shell command buffer */ +int shcmdsz; /* shell command buffer size */ +int shcmdi; /* shell command buffer index */ +char *ibuf; /* ed command-line buffer */ +int ibufsz; /* ed command-line buffer size */ +char *ibufp; /* pointer to ed command-line buffer */ + +/* global flags */ +int des = 0; /* if set, use crypt(3) for i/o */ +int garrulous = 0; /* if set, print all error messages */ +int isbinary; /* if set, buffer contains ASCII NULs */ +int isglobal; /* if set, doing a global command */ +int modified; /* if set, buffer modified since last write */ +int mutex = 0; /* if set, signals set "sigflags" */ +int red = 0; /* if set, restrict shell/directory access */ +int scripted = 0; /* if set, suppress diagnostics */ +int sigflags = 0; /* if set, signals received while mutex set */ +int sigactive = 0; /* if set, signal handlers are enabled */ + +char old_filename[MAXPATHLEN + 1] = ""; /* default filename */ +long current_addr; /* current address in editor buffer */ +long addr_last; /* last address in editor buffer */ +int lineno; /* script line number */ +char *prompt; /* command-line prompt */ +char *dps = "*"; /* default command-line prompt */ + +char *usage = "usage: %s [-] [-sx] [-p string] [name]\n"; + +/* ed: line editor */ +int +main(argc, argv) + int argc; + char **argv; +{ + int c, n; + long status = 0; +#if __GNUC__ + /* Avoid longjmp clobbering */ + (void) &argc; + (void) &argv; +#endif + + (void)setlocale(LC_ALL, ""); + + red = (n = strlen(argv[0])) > 2 && argv[0][n - 3] == 'r'; +top: + while ((c = getopt(argc, argv, "p:sx")) != -1) + switch(c) { + case 'p': /* set prompt */ + prompt = optarg; + break; + case 's': /* run script */ + scripted = 1; + break; + case 'x': /* use crypt */ +#ifdef DES + des = get_keyword(); +#else + fprintf(stderr, "crypt unavailable\n?\n"); +#endif + break; + + default: + fprintf(stderr, usage, argv[0]); + exit(1); + } + argv += optind; + argc -= optind; + if (argc && **argv == '-') { + scripted = 1; + if (argc > 1) { + optind = 1; + goto top; + } + argv++; + argc--; + } + /* assert: reliable signals! */ +#ifdef SIGWINCH + handle_winch(SIGWINCH); + if (isatty(0)) signal(SIGWINCH, handle_winch); +#endif + signal(SIGHUP, signal_hup); + signal(SIGQUIT, SIG_IGN); + signal(SIGINT, signal_int); +#ifdef _POSIX_SOURCE + if ((status = sigsetjmp(env, 1))) +#else + if ((status = setjmp(env))) +#endif + { + fputs("\n?\n", stderr); + sprintf(errmsg, "interrupt"); + } else { + init_buffers(); + sigactive = 1; /* enable signal handlers */ + if (argc && **argv && is_legal_filename(*argv)) { + if (read_file(*argv, 0) < 0 && !isatty(0)) + quit(2); + else if (**argv != '!') + if (strlcpy(old_filename, *argv, sizeof(old_filename)) + >= sizeof(old_filename)) + quit(2); + } else if (argc) { + fputs("?\n", stderr); + if (**argv == '\0') + sprintf(errmsg, "invalid filename"); + if (!isatty(0)) + quit(2); + } + } + for (;;) { + if (status < 0 && garrulous) + fprintf(stderr, "%s\n", errmsg); + if (prompt) { + printf("%s", prompt); + fflush(stdout); + } + if ((n = get_tty_line()) < 0) { + status = ERR; + continue; + } else if (n == 0) { + if (modified && !scripted) { + fputs("?\n", stderr); + sprintf(errmsg, "warning: file modified"); + if (!isatty(0)) { + fprintf(stderr, garrulous ? + "script, line %d: %s\n" : + "", lineno, errmsg); + quit(2); + } + clearerr(stdin); + modified = 0; + status = EMOD; + continue; + } else + quit(0); + } else if (ibuf[n - 1] != '\n') { + /* discard line */ + sprintf(errmsg, "unexpected end-of-file"); + clearerr(stdin); + status = ERR; + continue; + } + isglobal = 0; + if ((status = extract_addr_range()) >= 0 && + (status = exec_command()) >= 0) + if (!status || + (status = display_lines(current_addr, current_addr, + status)) >= 0) + continue; + switch (status) { + case EOF: + quit(0); + case EMOD: + modified = 0; + fputs("?\n", stderr); /* give warning */ + sprintf(errmsg, "warning: file modified"); + if (!isatty(0)) { + fprintf(stderr, garrulous ? + "script, line %d: %s\n" : + "", lineno, errmsg); + quit(2); + } + break; + case FATAL: + if (!isatty(0)) + fprintf(stderr, garrulous ? + "script, line %d: %s\n" : "", + lineno, errmsg); + else + fprintf(stderr, garrulous ? "%s\n" : "", + errmsg); + quit(3); + default: + fputs("?\n", stderr); + if (!isatty(0)) { + fprintf(stderr, garrulous ? + "script, line %d: %s\n" : "", + lineno, errmsg); + quit(2); + } + break; + } + } + /*NOTREACHED*/ +} + +long first_addr, second_addr, addr_cnt; + +/* extract_addr_range: get line addresses from the command buffer until an + illegal address is seen; return status */ +int +extract_addr_range() +{ + long addr; + + addr_cnt = 0; + first_addr = second_addr = current_addr; + while ((addr = next_addr()) >= 0) { + addr_cnt++; + first_addr = second_addr; + second_addr = addr; + if (*ibufp != ',' && *ibufp != ';') + break; + else if (*ibufp++ == ';') + current_addr = addr; + } + if ((addr_cnt = min(addr_cnt, 2)) == 1 || second_addr != addr) + first_addr = second_addr; + return (addr == ERR) ? ERR : 0; +} + + +#define SKIP_BLANKS() while (isspace((unsigned char)*ibufp) && *ibufp != '\n') ibufp++ + +#define MUST_BE_FIRST() \ + if (!first) { sprintf(errmsg, "invalid address"); return ERR; } + +/* next_addr: return the next line address in the command buffer */ +long +next_addr() +{ + char *hd; + long addr = current_addr; + long n; + int first = 1; + int c; + + SKIP_BLANKS(); + for (hd = ibufp;; first = 0) + switch (c = *ibufp) { + case '+': + case '\t': + case ' ': + case '-': + case '^': + ibufp++; + SKIP_BLANKS(); + if (isdigit((unsigned char)*ibufp)) { + STRTOL(n, ibufp); + addr += (c == '-' || c == '^') ? -n : n; + } else if (!isspace((unsigned char)c)) + addr += (c == '-' || c == '^') ? -1 : 1; + break; + case '0': case '1': case '2': + case '3': case '4': case '5': + case '6': case '7': case '8': case '9': + MUST_BE_FIRST(); + STRTOL(addr, ibufp); + break; + case '.': + case '$': + MUST_BE_FIRST(); + ibufp++; + addr = (c == '.') ? current_addr : addr_last; + break; + case '/': + case '?': + MUST_BE_FIRST(); + if ((addr = get_matching_node_addr( + get_compiled_pattern(), c == '/')) < 0) + return ERR; + else if (c == *ibufp) + ibufp++; + break; + case '\'': + MUST_BE_FIRST(); + ibufp++; + if ((addr = get_marked_node_addr(*ibufp++)) < 0) + return ERR; + break; + case '%': + case ',': + case ';': + if (first) { + ibufp++; + addr_cnt++; + second_addr = (c == ';') ? current_addr : 1; + addr = addr_last; + break; + } + /* FALL THROUGH */ + default: + if (ibufp == hd) + return EOF; + else if (addr < 0 || addr_last < addr) { + sprintf(errmsg, "invalid address"); + return ERR; + } else + return addr; + } + /* NOTREACHED */ +} + + +#ifdef BACKWARDS +/* GET_THIRD_ADDR: get a legal address from the command buffer */ +#define GET_THIRD_ADDR(addr) \ +{ \ + long ol1, ol2; \ +\ + ol1 = first_addr, ol2 = second_addr; \ + if (extract_addr_range() < 0) \ + return ERR; \ + else if (addr_cnt == 0) { \ + sprintf(errmsg, "destination expected"); \ + return ERR; \ + } else if (second_addr < 0 || addr_last < second_addr) { \ + sprintf(errmsg, "invalid address"); \ + return ERR; \ + } \ + addr = second_addr; \ + first_addr = ol1, second_addr = ol2; \ +} +#else /* BACKWARDS */ +/* GET_THIRD_ADDR: get a legal address from the command buffer */ +#define GET_THIRD_ADDR(addr) \ +{ \ + long ol1, ol2; \ +\ + ol1 = first_addr, ol2 = second_addr; \ + if (extract_addr_range() < 0) \ + return ERR; \ + if (second_addr < 0 || addr_last < second_addr) { \ + sprintf(errmsg, "invalid address"); \ + return ERR; \ + } \ + addr = second_addr; \ + first_addr = ol1, second_addr = ol2; \ +} +#endif + + +/* GET_COMMAND_SUFFIX: verify the command suffix in the command buffer */ +#define GET_COMMAND_SUFFIX() { \ + int done = 0; \ + do { \ + switch(*ibufp) { \ + case 'p': \ + gflag |= GPR, ibufp++; \ + break; \ + case 'l': \ + gflag |= GLS, ibufp++; \ + break; \ + case 'n': \ + gflag |= GNP, ibufp++; \ + break; \ + default: \ + done++; \ + } \ + } while (!done); \ + if (*ibufp++ != '\n') { \ + sprintf(errmsg, "invalid command suffix"); \ + return ERR; \ + } \ +} + + +/* sflags */ +#define SGG 001 /* complement previous global substitute suffix */ +#define SGP 002 /* complement previous print suffix */ +#define SGR 004 /* use last regex instead of last pat */ +#define SGF 010 /* repeat last substitution */ + +int patlock = 0; /* if set, pattern not freed by get_compiled_pattern() */ + +long rows = 22; /* scroll length: ws_row - 2 */ + +/* exec_command: execute the next command in command buffer; return print + request, if any */ +int +exec_command() +{ + extern long u_current_addr; + extern long u_addr_last; + + static pattern_t *pat = NULL; + static int sgflag = 0; + static long sgnum = 0; + + pattern_t *tpat; + char *fnp; + int gflag = 0; + int sflags = 0; + long addr = 0; + int n = 0; + int c; + + SKIP_BLANKS(); + switch(c = *ibufp++) { + case 'a': + GET_COMMAND_SUFFIX(); + if (!isglobal) clear_undo_stack(); + if (append_lines(second_addr) < 0) + return ERR; + break; + case 'c': + if (check_addr_range(current_addr, current_addr) < 0) + return ERR; + GET_COMMAND_SUFFIX(); + if (!isglobal) clear_undo_stack(); + if (delete_lines(first_addr, second_addr) < 0 || + append_lines(current_addr) < 0) + return ERR; + break; + case 'd': + if (check_addr_range(current_addr, current_addr) < 0) + return ERR; + GET_COMMAND_SUFFIX(); + if (!isglobal) clear_undo_stack(); + if (delete_lines(first_addr, second_addr) < 0) + return ERR; + else if ((addr = INC_MOD(current_addr, addr_last)) != 0) + current_addr = addr; + break; + case 'e': + if (modified && !scripted) + return EMOD; + /* fall through */ + case 'E': + if (addr_cnt > 0) { + sprintf(errmsg, "unexpected address"); + return ERR; + } else if (!isspace((unsigned char)*ibufp)) { + sprintf(errmsg, "unexpected command suffix"); + return ERR; + } else if ((fnp = get_filename()) == NULL) + return ERR; + GET_COMMAND_SUFFIX(); + if (delete_lines(1, addr_last) < 0) + return ERR; + clear_undo_stack(); + if (close_sbuf() < 0) + return ERR; + else if (open_sbuf() < 0) + return FATAL; + if (*fnp && *fnp != '!') strcpy(old_filename, fnp); +#ifdef BACKWARDS + if (*fnp == '\0' && *old_filename == '\0') { + sprintf(errmsg, "no current filename"); + return ERR; + } +#endif + if (read_file(*fnp ? fnp : old_filename, 0) < 0) + return ERR; + clear_undo_stack(); + modified = 0; + u_current_addr = u_addr_last = -1; + break; + case 'f': + if (addr_cnt > 0) { + sprintf(errmsg, "unexpected address"); + return ERR; + } else if (!isspace((unsigned char)*ibufp)) { + sprintf(errmsg, "unexpected command suffix"); + return ERR; + } else if ((fnp = get_filename()) == NULL) + return ERR; + else if (*fnp == '!') { + sprintf(errmsg, "invalid redirection"); + return ERR; + } + GET_COMMAND_SUFFIX(); + if (*fnp) strcpy(old_filename, fnp); + printf("%s\n", strip_escapes(old_filename)); + break; + case 'g': + case 'v': + case 'G': + case 'V': + if (isglobal) { + sprintf(errmsg, "cannot nest global commands"); + return ERR; + } else if (check_addr_range(1, addr_last) < 0) + return ERR; + else if (build_active_list(c == 'g' || c == 'G') < 0) + return ERR; + else if ((n = (c == 'G' || c == 'V'))) + GET_COMMAND_SUFFIX(); + isglobal++; + if (exec_global(n, gflag) < 0) + return ERR; + break; + case 'h': + if (addr_cnt > 0) { + sprintf(errmsg, "unexpected address"); + return ERR; + } + GET_COMMAND_SUFFIX(); + if (*errmsg) fprintf(stderr, "%s\n", errmsg); + break; + case 'H': + if (addr_cnt > 0) { + sprintf(errmsg, "unexpected address"); + return ERR; + } + GET_COMMAND_SUFFIX(); + if ((garrulous = 1 - garrulous) && *errmsg) + fprintf(stderr, "%s\n", errmsg); + break; + case 'i': + if (second_addr == 0) { + sprintf(errmsg, "invalid address"); + return ERR; + } + GET_COMMAND_SUFFIX(); + if (!isglobal) clear_undo_stack(); + if (append_lines(second_addr - 1) < 0) + return ERR; + break; + case 'j': + if (check_addr_range(current_addr, current_addr + 1) < 0) + return ERR; + GET_COMMAND_SUFFIX(); + if (!isglobal) clear_undo_stack(); + if (first_addr != second_addr && + join_lines(first_addr, second_addr) < 0) + return ERR; + break; + case 'k': + c = *ibufp++; + if (second_addr == 0) { + sprintf(errmsg, "invalid address"); + return ERR; + } + GET_COMMAND_SUFFIX(); + if (mark_line_node(get_addressed_line_node(second_addr), c) < 0) + return ERR; + break; + case 'l': + if (check_addr_range(current_addr, current_addr) < 0) + return ERR; + GET_COMMAND_SUFFIX(); + if (display_lines(first_addr, second_addr, gflag | GLS) < 0) + return ERR; + gflag = 0; + break; + case 'm': + if (check_addr_range(current_addr, current_addr) < 0) + return ERR; + GET_THIRD_ADDR(addr); + if (first_addr <= addr && addr < second_addr) { + sprintf(errmsg, "invalid destination"); + return ERR; + } + GET_COMMAND_SUFFIX(); + if (!isglobal) clear_undo_stack(); + if (move_lines(addr) < 0) + return ERR; + break; + case 'n': + if (check_addr_range(current_addr, current_addr) < 0) + return ERR; + GET_COMMAND_SUFFIX(); + if (display_lines(first_addr, second_addr, gflag | GNP) < 0) + return ERR; + gflag = 0; + break; + case 'p': + if (check_addr_range(current_addr, current_addr) < 0) + return ERR; + GET_COMMAND_SUFFIX(); + if (display_lines(first_addr, second_addr, gflag | GPR) < 0) + return ERR; + gflag = 0; + break; + case 'P': + if (addr_cnt > 0) { + sprintf(errmsg, "unexpected address"); + return ERR; + } + GET_COMMAND_SUFFIX(); + prompt = prompt ? NULL : optarg ? optarg : dps; + break; + case 'q': + case 'Q': + if (addr_cnt > 0) { + sprintf(errmsg, "unexpected address"); + return ERR; + } + GET_COMMAND_SUFFIX(); + gflag = (modified && !scripted && c == 'q') ? EMOD : EOF; + break; + case 'r': + if (!isspace((unsigned char)*ibufp)) { + sprintf(errmsg, "unexpected command suffix"); + return ERR; + } else if (addr_cnt == 0) + second_addr = addr_last; + if ((fnp = get_filename()) == NULL) + return ERR; + GET_COMMAND_SUFFIX(); + if (!isglobal) clear_undo_stack(); + if (*old_filename == '\0' && *fnp != '!') + strcpy(old_filename, fnp); +#ifdef BACKWARDS + if (*fnp == '\0' && *old_filename == '\0') { + sprintf(errmsg, "no current filename"); + return ERR; + } +#endif + if ((addr = read_file(*fnp ? fnp : old_filename, second_addr)) < 0) + return ERR; + else if (addr && addr != addr_last) + modified = 1; + break; + case 's': + do { + switch(*ibufp) { + case '\n': + sflags |=SGF; + break; + case 'g': + sflags |= SGG; + ibufp++; + break; + case 'p': + sflags |= SGP; + ibufp++; + break; + case 'r': + sflags |= SGR; + ibufp++; + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + STRTOL(sgnum, ibufp); + sflags |= SGF; + sgflag &= ~GSG; /* override GSG */ + break; + default: + if (sflags) { + sprintf(errmsg, "invalid command suffix"); + return ERR; + } + } + } while (sflags && *ibufp != '\n'); + if (sflags && !pat) { + sprintf(errmsg, "no previous substitution"); + return ERR; + } else if (sflags & SGG) + sgnum = 0; /* override numeric arg */ + if (*ibufp != '\n' && *(ibufp + 1) == '\n') { + sprintf(errmsg, "invalid pattern delimiter"); + return ERR; + } + tpat = pat; + SPL1(); + if ((!sflags || (sflags & SGR)) && + (tpat = get_compiled_pattern()) == NULL) { + SPL0(); + return ERR; + } else if (tpat != pat) { + if (pat) { + regfree(pat); + free(pat); + } + pat = tpat; + patlock = 1; /* reserve pattern */ + } + SPL0(); + if (!sflags && extract_subst_tail(&sgflag, &sgnum) < 0) + return ERR; + else if (isglobal) + sgflag |= GLB; + else + sgflag &= ~GLB; + if (sflags & SGG) + sgflag ^= GSG; + if (sflags & SGP) + sgflag ^= GPR, sgflag &= ~(GLS | GNP); + do { + switch(*ibufp) { + case 'p': + sgflag |= GPR, ibufp++; + break; + case 'l': + sgflag |= GLS, ibufp++; + break; + case 'n': + sgflag |= GNP, ibufp++; + break; + default: + n++; + } + } while (!n); + if (check_addr_range(current_addr, current_addr) < 0) + return ERR; + GET_COMMAND_SUFFIX(); + if (!isglobal) clear_undo_stack(); + if (search_and_replace(pat, sgflag, sgnum) < 0) + return ERR; + break; + case 't': + if (check_addr_range(current_addr, current_addr) < 0) + return ERR; + GET_THIRD_ADDR(addr); + GET_COMMAND_SUFFIX(); + if (!isglobal) clear_undo_stack(); + if (copy_lines(addr) < 0) + return ERR; + break; + case 'u': + if (addr_cnt > 0) { + sprintf(errmsg, "unexpected address"); + return ERR; + } + GET_COMMAND_SUFFIX(); + if (pop_undo_stack() < 0) + return ERR; + break; + case 'w': + case 'W': + if ((n = *ibufp) == 'q' || n == 'Q') { + gflag = EOF; + ibufp++; + } + if (!isspace((unsigned char)*ibufp)) { + sprintf(errmsg, "unexpected command suffix"); + return ERR; + } else if ((fnp = get_filename()) == NULL) + return ERR; + if (addr_cnt == 0 && !addr_last) + first_addr = second_addr = 0; + else if (check_addr_range(1, addr_last) < 0) + return ERR; + GET_COMMAND_SUFFIX(); + if (*old_filename == '\0' && *fnp != '!') + strcpy(old_filename, fnp); +#ifdef BACKWARDS + if (*fnp == '\0' && *old_filename == '\0') { + sprintf(errmsg, "no current filename"); + return ERR; + } +#endif + if ((addr = write_file(*fnp ? fnp : old_filename, + (c == 'W') ? "a" : "w", first_addr, second_addr)) < 0) + return ERR; + else if (addr == addr_last) + modified = 0; + else if (modified && !scripted && n == 'q') + gflag = EMOD; + break; + case 'x': + if (addr_cnt > 0) { + sprintf(errmsg, "unexpected address"); + return ERR; + } + GET_COMMAND_SUFFIX(); +#ifdef DES + des = get_keyword(); +#else + sprintf(errmsg, "crypt unavailable"); + return ERR; +#endif + break; + case 'z': +#ifdef BACKWARDS + if (check_addr_range(first_addr = 1, current_addr + 1) < 0) +#else + if (check_addr_range(first_addr = 1, current_addr + !isglobal) < 0) +#endif + return ERR; + else if ('0' < *ibufp && *ibufp <= '9') + STRTOL(rows, ibufp); + GET_COMMAND_SUFFIX(); + if (display_lines(second_addr, min(addr_last, + second_addr + rows), gflag) < 0) + return ERR; + gflag = 0; + break; + case '=': + GET_COMMAND_SUFFIX(); + printf("%ld\n", addr_cnt ? second_addr : addr_last); + break; + case '!': + if (addr_cnt > 0) { + sprintf(errmsg, "unexpected address"); + return ERR; + } else if ((sflags = get_shell_command()) < 0) + return ERR; + GET_COMMAND_SUFFIX(); + if (sflags) printf("%s\n", shcmd + 1); + system(shcmd + 1); + if (!scripted) printf("!\n"); + break; + case '\n': +#ifdef BACKWARDS + if (check_addr_range(first_addr = 1, current_addr + 1) < 0 +#else + if (check_addr_range(first_addr = 1, current_addr + !isglobal) < 0 +#endif + || display_lines(second_addr, second_addr, 0) < 0) + return ERR; + break; + default: + sprintf(errmsg, "unknown command"); + return ERR; + } + return gflag; +} + + +/* check_addr_range: return status of address range check */ +int +check_addr_range(n, m) + long n, m; +{ + if (addr_cnt == 0) { + first_addr = n; + second_addr = m; + } + if (first_addr > second_addr || 1 > first_addr || + second_addr > addr_last) { + sprintf(errmsg, "invalid address"); + return ERR; + } + return 0; +} + + +/* get_matching_node_addr: return the address of the next line matching a + pattern in a given direction. wrap around begin/end of editor buffer if + necessary */ +long +get_matching_node_addr(pat, dir) + pattern_t *pat; + int dir; +{ + char *s; + long n = current_addr; + line_t *lp; + + if (!pat) return ERR; + do { + if ((n = dir ? INC_MOD(n, addr_last) : DEC_MOD(n, addr_last))) { + lp = get_addressed_line_node(n); + if ((s = get_sbuf_line(lp)) == NULL) + return ERR; + if (isbinary) + NUL_TO_NEWLINE(s, lp->len); + if (!regexec(pat, s, 0, NULL, 0)) + return n; + } + } while (n != current_addr); + sprintf(errmsg, "no match"); + return ERR; +} + + +/* get_filename: return pointer to copy of filename in the command buffer */ +char * +get_filename() +{ + static char *file = NULL; + static int filesz = 0; + + int n; + + if (*ibufp != '\n') { + SKIP_BLANKS(); + if (*ibufp == '\n') { + sprintf(errmsg, "invalid filename"); + return NULL; + } else if ((ibufp = get_extended_line(&n, 1)) == NULL) + return NULL; + else if (*ibufp == '!') { + ibufp++; + if ((n = get_shell_command()) < 0) + return NULL; + if (n) printf("%s\n", shcmd + 1); + return shcmd; + } else if (n - 1 > MAXPATHLEN) { + sprintf(errmsg, "filename too long"); + return NULL; + } + } +#ifndef BACKWARDS + else if (*old_filename == '\0') { + sprintf(errmsg, "no current filename"); + return NULL; + } +#endif + REALLOC(file, filesz, MAXPATHLEN + 1, NULL); + for (n = 0; *ibufp != '\n';) + file[n++] = *ibufp++; + file[n] = '\0'; + return is_legal_filename(file) ? file : NULL; +} + + +/* get_shell_command: read a shell command from stdin; return substitution + status */ +int +get_shell_command() +{ + static char *buf = NULL; + static int n = 0; + + char *s; /* substitution char pointer */ + int i = 0; + int j = 0; + + if (red) { + sprintf(errmsg, "shell access restricted"); + return ERR; + } else if ((s = ibufp = get_extended_line(&j, 1)) == NULL) + return ERR; + REALLOC(buf, n, j + 1, ERR); + buf[i++] = '!'; /* prefix command w/ bang */ + while (*ibufp != '\n') + switch (*ibufp) { + default: + REALLOC(buf, n, i + 2, ERR); + buf[i++] = *ibufp; + if (*ibufp++ == '\\') + buf[i++] = *ibufp++; + break; + case '!': + if (s != ibufp) { + REALLOC(buf, n, i + 1, ERR); + buf[i++] = *ibufp++; + } +#ifdef BACKWARDS + else if (shcmd == NULL || *(shcmd + 1) == '\0') +#else + else if (shcmd == NULL) +#endif + { + sprintf(errmsg, "no previous command"); + return ERR; + } else { + REALLOC(buf, n, i + shcmdi, ERR); + for (s = shcmd + 1; s < shcmd + shcmdi;) + buf[i++] = *s++; + s = ibufp++; + } + break; + case '%': + if (*old_filename == '\0') { + sprintf(errmsg, "no current filename"); + return ERR; + } + j = strlen(s = strip_escapes(old_filename)); + REALLOC(buf, n, i + j, ERR); + while (j--) + buf[i++] = *s++; + s = ibufp++; + break; + } + REALLOC(shcmd, shcmdsz, i + 1, ERR); + memcpy(shcmd, buf, i); + shcmd[shcmdi = i] = '\0'; + return *s == '!' || *s == '%'; +} + + +/* append_lines: insert text from stdin to after line n; stop when either a + single period is read or EOF; return status */ +int +append_lines(n) + long n; +{ + int l; + char *lp = ibuf; + char *eot; + undo_t *up = NULL; + + for (current_addr = n;;) { + if (!isglobal) { + if ((l = get_tty_line()) < 0) + return ERR; + else if (l == 0 || ibuf[l - 1] != '\n') { + clearerr(stdin); + return l ? EOF : 0; + } + lp = ibuf; + } else if (*(lp = ibufp) == '\0') + return 0; + else { + while (*ibufp++ != '\n') + ; + l = ibufp - lp; + } + if (l == 2 && lp[0] == '.' && lp[1] == '\n') { + return 0; + } + eot = lp + l; + SPL1(); + do { + if ((lp = put_sbuf_line(lp)) == NULL) { + SPL0(); + return ERR; + } else if (up) + up->t = get_addressed_line_node(current_addr); + else if ((up = push_undo_stack(UADD, current_addr, + current_addr)) == NULL) { + SPL0(); + return ERR; + } + } while (lp != eot); + modified = 1; + SPL0(); + } + /* NOTREACHED */ +} + + +/* join_lines: replace a range of lines with the joined text of those lines */ +int +join_lines(from, to) + long from; + long to; +{ + static char *buf = NULL; + static int n; + + char *s; + int size = 0; + line_t *bp, *ep; + + ep = get_addressed_line_node(INC_MOD(to, addr_last)); + bp = get_addressed_line_node(from); + for (; bp != ep; bp = bp->q_forw) { + if ((s = get_sbuf_line(bp)) == NULL) + return ERR; + REALLOC(buf, n, size + bp->len, ERR); + memcpy(buf + size, s, bp->len); + size += bp->len; + } + REALLOC(buf, n, size + 2, ERR); + memcpy(buf + size, "\n", 2); + if (delete_lines(from, to) < 0) + return ERR; + current_addr = from - 1; + SPL1(); + if (put_sbuf_line(buf) == NULL || + push_undo_stack(UADD, current_addr, current_addr) == NULL) { + SPL0(); + return ERR; + } + modified = 1; + SPL0(); + return 0; +} + + +/* move_lines: move a range of lines */ +int +move_lines(addr) + long addr; +{ + line_t *b1, *a1, *b2, *a2; + long n = INC_MOD(second_addr, addr_last); + long p = first_addr - 1; + int done = (addr == first_addr - 1 || addr == second_addr); + + SPL1(); + if (done) { + a2 = get_addressed_line_node(n); + b2 = get_addressed_line_node(p); + current_addr = second_addr; + } else if (push_undo_stack(UMOV, p, n) == NULL || + push_undo_stack(UMOV, addr, INC_MOD(addr, addr_last)) == NULL) { + SPL0(); + return ERR; + } else { + a1 = get_addressed_line_node(n); + if (addr < first_addr) { + b1 = get_addressed_line_node(p); + b2 = get_addressed_line_node(addr); + /* this get_addressed_line_node last! */ + } else { + b2 = get_addressed_line_node(addr); + b1 = get_addressed_line_node(p); + /* this get_addressed_line_node last! */ + } + a2 = b2->q_forw; + REQUE(b2, b1->q_forw); + REQUE(a1->q_back, a2); + REQUE(b1, a1); + current_addr = addr + ((addr < first_addr) ? + second_addr - first_addr + 1 : 0); + } + if (isglobal) + unset_active_nodes(b2->q_forw, a2); + modified = 1; + SPL0(); + return 0; +} + + +/* copy_lines: copy a range of lines; return status */ +int +copy_lines(addr) + long addr; +{ + line_t *lp, *np = get_addressed_line_node(first_addr); + undo_t *up = NULL; + long n = second_addr - first_addr + 1; + long m = 0; + + current_addr = addr; + if (first_addr <= addr && addr < second_addr) { + n = addr - first_addr + 1; + m = second_addr - addr; + } + for (; n > 0; n=m, m=0, np = get_addressed_line_node(current_addr + 1)) + for (; n-- > 0; np = np->q_forw) { + SPL1(); + if ((lp = dup_line_node(np)) == NULL) { + SPL0(); + return ERR; + } + add_line_node(lp); + if (up) + up->t = lp; + else if ((up = push_undo_stack(UADD, current_addr, + current_addr)) == NULL) { + SPL0(); + return ERR; + } + modified = 1; + SPL0(); + } + return 0; +} + + +/* delete_lines: delete a range of lines */ +int +delete_lines(from, to) + long from, to; +{ + line_t *n, *p; + + SPL1(); + if (push_undo_stack(UDEL, from, to) == NULL) { + SPL0(); + return ERR; + } + n = get_addressed_line_node(INC_MOD(to, addr_last)); + p = get_addressed_line_node(from - 1); + /* this get_addressed_line_node last! */ + if (isglobal) + unset_active_nodes(p->q_forw, n); + REQUE(p, n); + addr_last -= to - from + 1; + current_addr = from - 1; + modified = 1; + SPL0(); + return 0; +} + + +/* display_lines: print a range of lines to stdout */ +int +display_lines(from, to, gflag) + long from; + long to; + int gflag; +{ + line_t *bp; + line_t *ep; + char *s; + + if (!from) { + sprintf(errmsg, "invalid address"); + return ERR; + } + ep = get_addressed_line_node(INC_MOD(to, addr_last)); + bp = get_addressed_line_node(from); + for (; bp != ep; bp = bp->q_forw) { + if ((s = get_sbuf_line(bp)) == NULL) + return ERR; + if (put_tty_line(s, bp->len, current_addr = from++, gflag) < 0) + return ERR; + } + return 0; +} + + +#define MAXMARK 26 /* max number of marks */ + +line_t *mark[MAXMARK]; /* line markers */ +int markno; /* line marker count */ + +/* mark_line_node: set a line node mark */ +int +mark_line_node(lp, n) + line_t *lp; + int n; +{ + if (!islower((unsigned char)n)) { + sprintf(errmsg, "invalid mark character"); + return ERR; + } else if (mark[n - 'a'] == NULL) + markno++; + mark[n - 'a'] = lp; + return 0; +} + + +/* get_marked_node_addr: return address of a marked line */ +long +get_marked_node_addr(n) + int n; +{ + if (!islower((unsigned char)n)) { + sprintf(errmsg, "invalid mark character"); + return ERR; + } + return get_line_node_addr(mark[n - 'a']); +} + + +/* unmark_line_node: clear line node mark */ +void +unmark_line_node(lp) + line_t *lp; +{ + int i; + + for (i = 0; markno && i < MAXMARK; i++) + if (mark[i] == lp) { + mark[i] = NULL; + markno--; + } +} + + +/* dup_line_node: return a pointer to a copy of a line node */ +line_t * +dup_line_node(lp) + line_t *lp; +{ + line_t *np; + + if ((np = (line_t *) malloc(sizeof(line_t))) == NULL) { + fprintf(stderr, "%s\n", strerror(errno)); + sprintf(errmsg, "out of memory"); + return NULL; + } + np->seek = lp->seek; + np->len = lp->len; + return np; +} + + +/* has_trailing_escape: return the parity of escapes preceding a character + in a string */ +int +has_trailing_escape(s, t) + char *s; + char *t; +{ + return (s == t || *(t - 1) != '\\') ? 0 : !has_trailing_escape(s, t - 1); +} + + +/* strip_escapes: return copy of escaped string of at most length MAXPATHLEN */ +char * +strip_escapes(s) + char *s; +{ + static char *file = NULL; + static int filesz = 0; + + int i = 0; + + REALLOC(file, filesz, MAXPATHLEN + 1, NULL); + while (i < filesz - 1 /* Worry about a possible trailing escape */ + && (file[i++] = (*s == '\\') ? *++s : *s)) + s++; + return file; +} + + +void +signal_hup(signo) + int signo; +{ + if (mutex) + sigflags |= (1 << (signo - 1)); + else + handle_hup(signo); +} + + +void +signal_int(signo) + int signo; +{ + if (mutex) + sigflags |= (1 << (signo - 1)); + else + handle_int(signo); +} + + +void +handle_hup(signo) + int signo; +{ + char *hup = NULL; /* hup filename */ + char *s; + int n; + + if (!sigactive) + quit(1); + sigflags &= ~(1 << (signo - 1)); + if (addr_last && write_file("ed.hup", "w", 1, addr_last) < 0 && + (s = getenv("HOME")) != NULL && + (n = strlen(s)) + 8 <= MAXPATHLEN && /* "ed.hup" + '/' */ + (hup = (char *) malloc(n + 10)) != NULL) { + strcpy(hup, s); + if (hup[n - 1] != '/') + hup[n] = '/', hup[n+1] = '\0'; + strcat(hup, "ed.hup"); + write_file(hup, "w", 1, addr_last); + } + quit(2); +} + + +void +handle_int(signo) + int signo; +{ + if (!sigactive) + quit(1); + sigflags &= ~(1 << (signo - 1)); +#ifdef _POSIX_SOURCE + siglongjmp(env, -1); +#else + longjmp(env, -1); +#endif +} + + +int cols = 72; /* wrap column */ + +void +handle_winch(signo) + int signo; +{ + int save_errno = errno; + + struct winsize ws; /* window size structure */ + + sigflags &= ~(1 << (signo - 1)); + if (ioctl(0, TIOCGWINSZ, (char *) &ws) >= 0) { + if (ws.ws_row > 2) rows = ws.ws_row - 2; + if (ws.ws_col > 8) cols = ws.ws_col - 8; + } + errno = save_errno; +} + + +/* is_legal_filename: return a legal filename */ +int +is_legal_filename(s) + char *s; +{ + if (red && (*s == '!' || !strcmp(s, "..") || strchr(s, '/'))) { + sprintf(errmsg, "shell access restricted"); + return 0; + } + return 1; +} diff --git a/bin/ed/re.c b/bin/ed/re.c new file mode 100644 index 0000000..d81d0d7 --- /dev/null +++ b/bin/ed/re.c @@ -0,0 +1,137 @@ +/* re.c: This file contains the regular expression interface routines for + the ed line editor. */ +/*- + * Copyright (c) 1993 Andrew Moore, Talke Studio. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 +#if 0 +static char * const rcsid = "@(#)re.c,v 1.6 1994/02/01 00:34:43 alm Exp"; +#else +static char * const rcsid = + "$FreeBSD$"; +#endif +#endif /* not lint */ + +#include "ed.h" + + +extern int patlock; + +char errmsg[MAXPATHLEN + 40] = ""; + +/* get_compiled_pattern: return pointer to compiled pattern from command + buffer */ +pattern_t * +get_compiled_pattern() +{ + static pattern_t *exp = NULL; + + char *exps; + char delimiter; + int n; + + if ((delimiter = *ibufp) == ' ') { + sprintf(errmsg, "invalid pattern delimiter"); + return NULL; + } else if (delimiter == '\n' || *++ibufp == '\n' || *ibufp == delimiter) { + if (!exp) sprintf(errmsg, "no previous pattern"); + return exp; + } else if ((exps = extract_pattern(delimiter)) == NULL) + return NULL; + /* buffer alloc'd && not reserved */ + if (exp && !patlock) + regfree(exp); + else if ((exp = (pattern_t *) malloc(sizeof(pattern_t))) == NULL) { + fprintf(stderr, "%s\n", strerror(errno)); + sprintf(errmsg, "out of memory"); + return NULL; + } + patlock = 0; + if ((n = regcomp(exp, exps, 0))) { + regerror(n, exp, errmsg, sizeof errmsg); + free(exp); + return exp = NULL; + } + return exp; +} + + +/* extract_pattern: copy a pattern string from the command buffer; return + pointer to the copy */ +char * +extract_pattern(delimiter) + int delimiter; +{ + static char *lhbuf = NULL; /* buffer */ + static int lhbufsz = 0; /* buffer size */ + + char *nd; + int len; + + for (nd = ibufp; *nd != delimiter && *nd != '\n'; nd++) + switch (*nd) { + default: + break; + case '[': + if ((nd = parse_char_class(++nd)) == NULL) { + sprintf(errmsg, "unbalanced brackets ([])"); + return NULL; + } + break; + case '\\': + if (*++nd == '\n') { + sprintf(errmsg, "trailing backslash (\\)"); + return NULL; + } + break; + } + len = nd - ibufp; + REALLOC(lhbuf, lhbufsz, len + 1, NULL); + memcpy(lhbuf, ibufp, len); + lhbuf[len] = '\0'; + ibufp = nd; + return (isbinary) ? NUL_TO_NEWLINE(lhbuf, len) : lhbuf; +} + + +/* parse_char_class: expand a POSIX character class */ +char * +parse_char_class(s) + char *s; +{ + int c, d; + + if (*s == '^') + s++; + if (*s == ']') + s++; + for (; *s != ']' && *s != '\n'; s++) + if (*s == '[' && ((d = *(s+1)) == '.' || d == ':' || d == '=')) + for (s++, c = *++s; *s != ']' || c != d; s++) + if ((c = *s) == '\n') + return NULL; + return (*s == ']') ? s : NULL; +} diff --git a/bin/ed/sub.c b/bin/ed/sub.c new file mode 100644 index 0000000..3221373 --- /dev/null +++ b/bin/ed/sub.c @@ -0,0 +1,272 @@ +/* sub.c: This file contains the substitution routines for the ed + line editor */ +/*- + * Copyright (c) 1993 Andrew Moore, Talke Studio. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 +#if 0 +static char * const rcsid = "@(#)sub.c,v 1.1 1994/02/01 00:34:44 alm Exp"; +#else +static char * const rcsid = + "$FreeBSD$"; +#endif +#endif /* not lint */ + +#include "ed.h" + + +char *rhbuf; /* rhs substitution buffer */ +int rhbufsz; /* rhs substitution buffer size */ +int rhbufi; /* rhs substitution buffer index */ + +/* extract_subst_tail: extract substitution tail from the command buffer */ +int +extract_subst_tail(flagp, np) + int *flagp; + long *np; +{ + char delimiter; + + *flagp = *np = 0; + if ((delimiter = *ibufp) == '\n') { + rhbufi = 0; + *flagp = GPR; + return 0; + } else if (extract_subst_template() == NULL) + return ERR; + else if (*ibufp == '\n') { + *flagp = GPR; + return 0; + } else if (*ibufp == delimiter) + ibufp++; + if ('1' <= *ibufp && *ibufp <= '9') { + STRTOL(*np, ibufp); + return 0; + } else if (*ibufp == 'g') { + ibufp++; + *flagp = GSG; + return 0; + } + return 0; +} + + +/* extract_subst_template: return pointer to copy of substitution template + in the command buffer */ +char * +extract_subst_template() +{ + int n = 0; + int i = 0; + char c; + char delimiter = *ibufp++; + + if (*ibufp == '%' && *(ibufp + 1) == delimiter) { + ibufp++; + if (!rhbuf) sprintf(errmsg, "no previous substitution"); + return rhbuf; + } + while (*ibufp != delimiter) { + REALLOC(rhbuf, rhbufsz, i + 2, NULL); + if ((c = rhbuf[i++] = *ibufp++) == '\n' && *ibufp == '\0') { + i--, ibufp--; + break; + } else if (c != '\\') + ; + else if ((rhbuf[i++] = *ibufp++) != '\n') + ; + else if (!isglobal) { + while ((n = get_tty_line()) == 0 || + (n > 0 && ibuf[n - 1] != '\n')) + clearerr(stdin); + if (n < 0) + return NULL; + } + } + REALLOC(rhbuf, rhbufsz, i + 1, NULL); + rhbuf[rhbufi = i] = '\0'; + return rhbuf; +} + + +char *rbuf; /* substitute_matching_text buffer */ +int rbufsz; /* substitute_matching_text buffer size */ + +/* search_and_replace: for each line in a range, change text matching a pattern + according to a substitution template; return status */ +int +search_and_replace(pat, gflag, kth) + pattern_t *pat; + int gflag; + int kth; +{ + undo_t *up; + char *txt; + char *eot; + long lc; + long xa = current_addr; + int nsubs = 0; + line_t *lp; + int len; + + current_addr = first_addr - 1; + for (lc = 0; lc <= second_addr - first_addr; lc++) { + lp = get_addressed_line_node(++current_addr); + if ((len = substitute_matching_text(pat, lp, gflag, kth)) < 0) + return ERR; + else if (len) { + up = NULL; + if (delete_lines(current_addr, current_addr) < 0) + return ERR; + txt = rbuf; + eot = rbuf + len; + SPL1(); + do { + if ((txt = put_sbuf_line(txt)) == NULL) { + SPL0(); + return ERR; + } else if (up) + up->t = get_addressed_line_node(current_addr); + else if ((up = push_undo_stack(UADD, + current_addr, current_addr)) == NULL) { + SPL0(); + return ERR; + } + } while (txt != eot); + SPL0(); + nsubs++; + xa = current_addr; + } + } + current_addr = xa; + if (nsubs == 0 && !(gflag & GLB)) { + sprintf(errmsg, "no match"); + return ERR; + } else if ((gflag & (GPR | GLS | GNP)) && + display_lines(current_addr, current_addr, gflag) < 0) + return ERR; + return 0; +} + + +/* substitute_matching_text: replace text matched by a pattern according to + a substitution template; return pointer to the modified text */ +int +substitute_matching_text(pat, lp, gflag, kth) + pattern_t *pat; + line_t *lp; + int gflag; + int kth; +{ + int off = 0; + int changed = 0; + int matchno = 0; + int i = 0; + regmatch_t rm[SE_MAX]; + char *txt; + char *eot; + + if ((txt = get_sbuf_line(lp)) == NULL) + return ERR; + if (isbinary) + NUL_TO_NEWLINE(txt, lp->len); + eot = txt + lp->len; + if (!regexec(pat, txt, SE_MAX, rm, 0)) { + do { + if (!kth || kth == ++matchno) { + changed++; + i = rm[0].rm_so; + REALLOC(rbuf, rbufsz, off + i, ERR); + if (isbinary) + NEWLINE_TO_NUL(txt, rm[0].rm_eo); + memcpy(rbuf + off, txt, i); + off += i; + if ((off = apply_subst_template(txt, rm, off, + pat->re_nsub)) < 0) + return ERR; + } else { + i = rm[0].rm_eo; + REALLOC(rbuf, rbufsz, off + i, ERR); + if (isbinary) + NEWLINE_TO_NUL(txt, i); + memcpy(rbuf + off, txt, i); + off += i; + } + txt += rm[0].rm_eo; + } while (*txt && + (!changed || ((gflag & GSG) && rm[0].rm_eo)) && + !regexec(pat, txt, SE_MAX, rm, REG_NOTBOL)); + i = eot - txt; + REALLOC(rbuf, rbufsz, off + i + 2, ERR); + if (i > 0 && !rm[0].rm_eo && (gflag & GSG)) { + sprintf(errmsg, "infinite substitution loop"); + return ERR; + } + if (isbinary) + NEWLINE_TO_NUL(txt, i); + memcpy(rbuf + off, txt, i); + memcpy(rbuf + off + i, "\n", 2); + } + return changed ? off + i + 1 : 0; +} + + +/* apply_subst_template: modify text according to a substitution template; + return offset to end of modified text */ +int +apply_subst_template(boln, rm, off, re_nsub) + char *boln; + regmatch_t *rm; + int off; + int re_nsub; +{ + int j = 0; + int k = 0; + int n; + char *sub = rhbuf; + + for (; sub - rhbuf < rhbufi; sub++) + if (*sub == '&') { + j = rm[0].rm_so; + k = rm[0].rm_eo; + REALLOC(rbuf, rbufsz, off + k - j, ERR); + while (j < k) + rbuf[off++] = boln[j++]; + } else if (*sub == '\\' && '1' <= *++sub && *sub <= '9' && + (n = *sub - '0') <= re_nsub) { + j = rm[n].rm_so; + k = rm[n].rm_eo; + REALLOC(rbuf, rbufsz, off + k - j, ERR); + while (j < k) + rbuf[off++] = boln[j++]; + } else { + REALLOC(rbuf, rbufsz, off + 1, ERR); + rbuf[off++] = *sub; + } + REALLOC(rbuf, rbufsz, off + 1, ERR); + rbuf[off] = '\0'; + return off; +} diff --git a/bin/ed/test/=.err b/bin/ed/test/=.err new file mode 100644 index 0000000..6a60559 --- /dev/null +++ b/bin/ed/test/=.err @@ -0,0 +1 @@ +1,$= diff --git a/bin/ed/test/Makefile b/bin/ed/test/Makefile new file mode 100644 index 0000000..aedfb69 --- /dev/null +++ b/bin/ed/test/Makefile @@ -0,0 +1,27 @@ +# $FreeBSD$ + +SHELL= /bin/sh +ED= ${.OBJDIR}/ed + +all: check + @: + +check: build test + @if grep -h '\*\*\*' errs.o scripts.o; then :; else \ + echo "tests completed successfully."; \ + fi + +build: mkscripts.sh + @if [ -f errs.o ]; then :; else \ + uudecode < ascii.d.uu ; \ + uudecode < ascii.r.uu ; \ + echo "building test scripts for $(ED) ..."; \ + $(SHELL) mkscripts.sh $(ED); \ + fi + +test: build ckscripts.sh + @echo testing $(ED) ... + @$(SHELL) ckscripts.sh $(ED) + +clean: + rm -f *.ed *.red *.[oz] *~ ascii.d ascii.r diff --git a/bin/ed/test/README b/bin/ed/test/README new file mode 100644 index 0000000..74c4826 --- /dev/null +++ b/bin/ed/test/README @@ -0,0 +1,32 @@ +# $FreeBSD$ + +The files in this directory with suffixes `.t', `.d', `.r' and `.err' are +used for testing ed. To run the tests, set the ED variable in the Makefile +for the path name of the program to be tested (e.g., /bin/ed), and type +`make'. The tests do not exhaustively verify POSIX compliance nor do +they verify correct 8-bit or long line support. + +The test file suffixes have the following meanings: +.t Template - a list of ed commands from which an ed script is + constructed +.d Data - read by an ed script +.r Result - the expected output after processing data via an ed + script. +.err Error - invalid ed commands that should generate an error + +The output of the tests is written to the two files err.o and scripts.o. +At the end of the tests, these files are grep'ed for error messages, +which look like: + *** The script u.ed exited abnormally *** +or: + *** Output u.o of script u.ed is incorrect *** + +The POSIX requirement that an address range not be used where at most +a single address is expected has been relaxed in this version of ed. +Therefore, the following scripts which test for compliance with this +POSIX rule exit abnormally: +=-err.ed +a1-err.ed +i1-err.ed +k1-err.ed +r1-err.ed diff --git a/bin/ed/test/TODO b/bin/ed/test/TODO new file mode 100644 index 0000000..7a4b74f --- /dev/null +++ b/bin/ed/test/TODO @@ -0,0 +1,15 @@ +Some missing tests: +0) g/./s^@^@ - okay: NULs in commands +1) g/./s/^@/ - okay: NULs in patterns +2) a + hello^V^Jworld + . - okay: embedded newlines in insert mode +3) ed "" - error: invalid filename +4) red .. - error: restricted +5) red / - error: restricted +5) red !xx - error: restricted +6) ed -x - verify: 8-bit clean +7) ed - verify: long-line support +8) ed - verify: interactive/help mode +9) G/pat/ - verify: global interactive command +10) V/pat/ - verify: global interactive command diff --git a/bin/ed/test/a.d b/bin/ed/test/a.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/bin/ed/test/a.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/a.r b/bin/ed/test/a.r new file mode 100644 index 0000000..26257bd --- /dev/null +++ b/bin/ed/test/a.r @@ -0,0 +1,8 @@ +hello world +line 1 +hello world! +line 2 +line 3 +line 4 +line5 +hello world!! diff --git a/bin/ed/test/a.t b/bin/ed/test/a.t new file mode 100644 index 0000000..ac98c40 --- /dev/null +++ b/bin/ed/test/a.t @@ -0,0 +1,9 @@ +0a +hello world +. +2a +hello world! +. +$a +hello world!! +. diff --git a/bin/ed/test/a1.err b/bin/ed/test/a1.err new file mode 100644 index 0000000..e80815f --- /dev/null +++ b/bin/ed/test/a1.err @@ -0,0 +1,3 @@ +1,$a +hello world +. diff --git a/bin/ed/test/a2.err b/bin/ed/test/a2.err new file mode 100644 index 0000000..ec4b00b --- /dev/null +++ b/bin/ed/test/a2.err @@ -0,0 +1,3 @@ +aa +hello world +. diff --git a/bin/ed/test/addr.d b/bin/ed/test/addr.d new file mode 100644 index 0000000..8f7ba1b --- /dev/null +++ b/bin/ed/test/addr.d @@ -0,0 +1,9 @@ +line 1 +line 2 +line 3 +line 4 +line5 +1ine6 +line7 +line8 +line9 diff --git a/bin/ed/test/addr.r b/bin/ed/test/addr.r new file mode 100644 index 0000000..04caf17 --- /dev/null +++ b/bin/ed/test/addr.r @@ -0,0 +1,2 @@ +line 2 +line9 diff --git a/bin/ed/test/addr.t b/bin/ed/test/addr.t new file mode 100644 index 0000000..750b224 --- /dev/null +++ b/bin/ed/test/addr.t @@ -0,0 +1,5 @@ +1 d +1 1 d +1,2,d +1;+ + ,d +1,2;., + 2d diff --git a/bin/ed/test/addr1.err b/bin/ed/test/addr1.err new file mode 100644 index 0000000..29d6383 --- /dev/null +++ b/bin/ed/test/addr1.err @@ -0,0 +1 @@ +100 diff --git a/bin/ed/test/addr2.err b/bin/ed/test/addr2.err new file mode 100644 index 0000000..e96acb9 --- /dev/null +++ b/bin/ed/test/addr2.err @@ -0,0 +1 @@ +-100 diff --git a/bin/ed/test/ascii.d.uu b/bin/ed/test/ascii.d.uu new file mode 100644 index 0000000..0b0a73c --- /dev/null +++ b/bin/ed/test/ascii.d.uu @@ -0,0 +1,9 @@ +begin 644 ascii.d +M``$"`P0%!@<("0H+#`T.#Q`1$A,4%187&!D:&QP='A\@(2(C)"4F)R@I*BLL +M+2XO,#$R,S0U-C<X.3H[/#T^/T!!0D-$149'2$E*2TQ-3D]045)35%565UA9 +M6EM<75Y?8&%B8V1E9F=H:6IK;&UN;W!Q<G-T=79W>'EZ>WQ]?G^`@8*#A(6& +MAXB)BHN,C8Z/D)&2DY25EI>8F9J;G)V>GZ"AHJ.DI::GJ*FJJZRMKJ^PL;*S +MM+6VM[BYNKN\O;Z_P,'"P\3%QL?(R<K+S,W.S]#1TM/4U=;7V-G:V]S=WM_@ +?X>+CY.7FY^CIZNOL[>[O\/'R\_3U]O?X^?K[_/W^_]/4 +` +end diff --git a/bin/ed/test/ascii.r.uu b/bin/ed/test/ascii.r.uu new file mode 100644 index 0000000..9ca88b4 --- /dev/null +++ b/bin/ed/test/ascii.r.uu @@ -0,0 +1,9 @@ +begin 644 ascii.r +M``$"`P0%!@<("0H+#`T.#Q`1$A,4%187&!D:&QP='A\@(2(C)"4F)R@I*BLL +M+2XO,#$R,S0U-C<X.3H[/#T^/T!!0D-$149'2$E*2TQ-3D]045)35%565UA9 +M6EM<75Y?8&%B8V1E9F=H:6IK;&UN;W!Q<G-T=79W>'EZ>WQ]?G^`@8*#A(6& +MAXB)BHN,C8Z/D)&2DY25EI>8F9J;G)V>GZ"AHJ.DI::GJ*FJJZRMKJ^PL;*S +MM+6VM[BYNKN\O;Z_P,'"P\3%QL?(R<K+S,W.S]#1TM/4U=;7V-G:V]S=WM_@ +?X>+CY.7FY^CIZNOL[>[O\/'R\_3U]O?X^?K[_/W^_]/4 +` +end diff --git a/bin/ed/test/ascii.t b/bin/ed/test/ascii.t new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bin/ed/test/ascii.t diff --git a/bin/ed/test/bang1.d b/bin/ed/test/bang1.d new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bin/ed/test/bang1.d diff --git a/bin/ed/test/bang1.err b/bin/ed/test/bang1.err new file mode 100644 index 0000000..630af90 --- /dev/null +++ b/bin/ed/test/bang1.err @@ -0,0 +1 @@ +.!date diff --git a/bin/ed/test/bang1.r b/bin/ed/test/bang1.r new file mode 100644 index 0000000..dcf02b2 --- /dev/null +++ b/bin/ed/test/bang1.r @@ -0,0 +1 @@ +okay diff --git a/bin/ed/test/bang1.t b/bin/ed/test/bang1.t new file mode 100644 index 0000000..d7b1fea --- /dev/null +++ b/bin/ed/test/bang1.t @@ -0,0 +1,5 @@ +!read one +hello, world +a +okay +. diff --git a/bin/ed/test/bang2.err b/bin/ed/test/bang2.err new file mode 100644 index 0000000..79d8956 --- /dev/null +++ b/bin/ed/test/bang2.err @@ -0,0 +1 @@ +!! diff --git a/bin/ed/test/c.d b/bin/ed/test/c.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/bin/ed/test/c.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/c.r b/bin/ed/test/c.r new file mode 100644 index 0000000..0fb3e4f --- /dev/null +++ b/bin/ed/test/c.r @@ -0,0 +1,4 @@ +at the top +between top/middle +in the middle +at the bottom diff --git a/bin/ed/test/c.t b/bin/ed/test/c.t new file mode 100644 index 0000000..ebdd536 --- /dev/null +++ b/bin/ed/test/c.t @@ -0,0 +1,12 @@ +1c +at the top +. +4c +in the middle +. +$c +at the bottom +. +2,3c +between top/middle +. diff --git a/bin/ed/test/c1.err b/bin/ed/test/c1.err new file mode 100644 index 0000000..658ec38 --- /dev/null +++ b/bin/ed/test/c1.err @@ -0,0 +1,3 @@ +cc +hello world +. diff --git a/bin/ed/test/c2.err b/bin/ed/test/c2.err new file mode 100644 index 0000000..24b3227 --- /dev/null +++ b/bin/ed/test/c2.err @@ -0,0 +1,3 @@ +0c +hello world +. diff --git a/bin/ed/test/ckscripts.sh b/bin/ed/test/ckscripts.sh new file mode 100644 index 0000000..deab475 --- /dev/null +++ b/bin/ed/test/ckscripts.sh @@ -0,0 +1,37 @@ +#!/bin/sh - +# This script runs the .ed scripts generated by mkscripts.sh +# and compares their output against the .r files, which contain +# the correct output +# +# $FreeBSD$ + +PATH="/bin:/usr/bin:/usr/local/bin/:." +ED=$1 +[ ! -x $ED ] && { echo "$ED: cannot execute"; exit 1; } + +# Run the *.red scripts first, since these don't generate output; +# they exit with non-zero status +for i in *.red; do + echo $i + if $i; then + echo "*** The script $i exited abnormally ***" + fi +done >errs.o 2>&1 + +# Run the remainding scripts; they exit with zero status +for i in *.ed; do +# base=`expr $i : '\([^.]*\)'` +# base=`echo $i | sed 's/\..*//'` + base=`$ED - \!"echo $i" <<-EOF + s/\..* + EOF` + if $base.ed; then + if cmp -s $base.o $base.r; then :; else + echo "*** Output $base.o of script $i is incorrect ***" + fi + else + echo "*** The script $i exited abnormally ***" + fi +done >scripts.o 2>&1 + +grep -h '\*\*\*' errs.o scripts.o diff --git a/bin/ed/test/d.d b/bin/ed/test/d.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/bin/ed/test/d.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/d.err b/bin/ed/test/d.err new file mode 100644 index 0000000..f03f694 --- /dev/null +++ b/bin/ed/test/d.err @@ -0,0 +1 @@ +dd diff --git a/bin/ed/test/d.r b/bin/ed/test/d.r new file mode 100644 index 0000000..b7e242c --- /dev/null +++ b/bin/ed/test/d.r @@ -0,0 +1 @@ +line 2 diff --git a/bin/ed/test/d.t b/bin/ed/test/d.t new file mode 100644 index 0000000..c7c473f --- /dev/null +++ b/bin/ed/test/d.t @@ -0,0 +1,3 @@ +1d +2;+1d +$d diff --git a/bin/ed/test/e1.d b/bin/ed/test/e1.d new file mode 100644 index 0000000..3b18e51 --- /dev/null +++ b/bin/ed/test/e1.d @@ -0,0 +1 @@ +hello world diff --git a/bin/ed/test/e1.err b/bin/ed/test/e1.err new file mode 100644 index 0000000..827cc29 --- /dev/null +++ b/bin/ed/test/e1.err @@ -0,0 +1 @@ +ee e1.err diff --git a/bin/ed/test/e1.r b/bin/ed/test/e1.r new file mode 100644 index 0000000..e656728 --- /dev/null +++ b/bin/ed/test/e1.r @@ -0,0 +1 @@ +E e1.t diff --git a/bin/ed/test/e1.t b/bin/ed/test/e1.t new file mode 100644 index 0000000..e656728 --- /dev/null +++ b/bin/ed/test/e1.t @@ -0,0 +1 @@ +E e1.t diff --git a/bin/ed/test/e2.d b/bin/ed/test/e2.d new file mode 100644 index 0000000..aa44630 --- /dev/null +++ b/bin/ed/test/e2.d @@ -0,0 +1 @@ +E !echo hello world- diff --git a/bin/ed/test/e2.err b/bin/ed/test/e2.err new file mode 100644 index 0000000..779a64b --- /dev/null +++ b/bin/ed/test/e2.err @@ -0,0 +1 @@ +.e e2.err diff --git a/bin/ed/test/e2.r b/bin/ed/test/e2.r new file mode 100644 index 0000000..59ebf11 --- /dev/null +++ b/bin/ed/test/e2.r @@ -0,0 +1 @@ +hello world- diff --git a/bin/ed/test/e2.t b/bin/ed/test/e2.t new file mode 100644 index 0000000..aa44630 --- /dev/null +++ b/bin/ed/test/e2.t @@ -0,0 +1 @@ +E !echo hello world- diff --git a/bin/ed/test/e3.d b/bin/ed/test/e3.d new file mode 100644 index 0000000..aa44630 --- /dev/null +++ b/bin/ed/test/e3.d @@ -0,0 +1 @@ +E !echo hello world- diff --git a/bin/ed/test/e3.err b/bin/ed/test/e3.err new file mode 100644 index 0000000..80a7fdc --- /dev/null +++ b/bin/ed/test/e3.err @@ -0,0 +1 @@ +ee.err diff --git a/bin/ed/test/e3.r b/bin/ed/test/e3.r new file mode 100644 index 0000000..aa44630 --- /dev/null +++ b/bin/ed/test/e3.r @@ -0,0 +1 @@ +E !echo hello world- diff --git a/bin/ed/test/e3.t b/bin/ed/test/e3.t new file mode 100644 index 0000000..1c50726 --- /dev/null +++ b/bin/ed/test/e3.t @@ -0,0 +1 @@ +E diff --git a/bin/ed/test/e4.d b/bin/ed/test/e4.d new file mode 100644 index 0000000..aa44630 --- /dev/null +++ b/bin/ed/test/e4.d @@ -0,0 +1 @@ +E !echo hello world- diff --git a/bin/ed/test/e4.r b/bin/ed/test/e4.r new file mode 100644 index 0000000..aa44630 --- /dev/null +++ b/bin/ed/test/e4.r @@ -0,0 +1 @@ +E !echo hello world- diff --git a/bin/ed/test/e4.t b/bin/ed/test/e4.t new file mode 100644 index 0000000..d905d9d --- /dev/null +++ b/bin/ed/test/e4.t @@ -0,0 +1 @@ +e diff --git a/bin/ed/test/f1.err b/bin/ed/test/f1.err new file mode 100644 index 0000000..e60975a --- /dev/null +++ b/bin/ed/test/f1.err @@ -0,0 +1 @@ +.f f1.err diff --git a/bin/ed/test/f2.err b/bin/ed/test/f2.err new file mode 100644 index 0000000..26d1c5e --- /dev/null +++ b/bin/ed/test/f2.err @@ -0,0 +1 @@ +ff1.err diff --git a/bin/ed/test/g1.d b/bin/ed/test/g1.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/bin/ed/test/g1.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/g1.err b/bin/ed/test/g1.err new file mode 100644 index 0000000..f95ea22 --- /dev/null +++ b/bin/ed/test/g1.err @@ -0,0 +1 @@ +g/./s //x/ diff --git a/bin/ed/test/g1.r b/bin/ed/test/g1.r new file mode 100644 index 0000000..578a44b --- /dev/null +++ b/bin/ed/test/g1.r @@ -0,0 +1,15 @@ +line5 +help! world +order +line 4 +help! world +order +line 3 +help! world +order +line 2 +help! world +order +line 1 +help! world +order diff --git a/bin/ed/test/g1.t b/bin/ed/test/g1.t new file mode 100644 index 0000000..2d0b54f --- /dev/null +++ b/bin/ed/test/g1.t @@ -0,0 +1,6 @@ +g/./m0 +g/./s/$/\ +hello world +g/hello /s/lo/p!/\ +a\ +order diff --git a/bin/ed/test/g2.d b/bin/ed/test/g2.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/bin/ed/test/g2.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/g2.err b/bin/ed/test/g2.err new file mode 100644 index 0000000..0ff6a5a --- /dev/null +++ b/bin/ed/test/g2.err @@ -0,0 +1 @@ +g//s/./x/ diff --git a/bin/ed/test/g2.r b/bin/ed/test/g2.r new file mode 100644 index 0000000..3b18e51 --- /dev/null +++ b/bin/ed/test/g2.r @@ -0,0 +1 @@ +hello world diff --git a/bin/ed/test/g2.t b/bin/ed/test/g2.t new file mode 100644 index 0000000..831ee83 --- /dev/null +++ b/bin/ed/test/g2.t @@ -0,0 +1,2 @@ +g/[2-4]/-1,+1c\ +hello world diff --git a/bin/ed/test/g3.d b/bin/ed/test/g3.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/bin/ed/test/g3.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/g3.err b/bin/ed/test/g3.err new file mode 100644 index 0000000..01058d8 --- /dev/null +++ b/bin/ed/test/g3.err @@ -0,0 +1 @@ +g diff --git a/bin/ed/test/g3.r b/bin/ed/test/g3.r new file mode 100644 index 0000000..cc6fbdd --- /dev/null +++ b/bin/ed/test/g3.r @@ -0,0 +1,5 @@ +linc 3 +xine 1 +xine 2 +xinc 4 +xinc5 diff --git a/bin/ed/test/g3.t b/bin/ed/test/g3.t new file mode 100644 index 0000000..2d052a6 --- /dev/null +++ b/bin/ed/test/g3.t @@ -0,0 +1,4 @@ +g/./s//x/\ +3m0 +g/./s/e/c/\ +2,3m1 diff --git a/bin/ed/test/g4.d b/bin/ed/test/g4.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/bin/ed/test/g4.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/g4.r b/bin/ed/test/g4.r new file mode 100644 index 0000000..350882d --- /dev/null +++ b/bin/ed/test/g4.r @@ -0,0 +1,7 @@ +hello +zine 1 +line 2 +line 3 +line 4 +line5 +world diff --git a/bin/ed/test/g4.t b/bin/ed/test/g4.t new file mode 100644 index 0000000..ec61816 --- /dev/null +++ b/bin/ed/test/g4.t @@ -0,0 +1,13 @@ +g/./s/./x/\ +u\ +s/./y/\ +u\ +s/./z/\ +u +u +0a +hello +. +$a +world +. diff --git a/bin/ed/test/g5.d b/bin/ed/test/g5.d new file mode 100644 index 0000000..a92d664 --- /dev/null +++ b/bin/ed/test/g5.d @@ -0,0 +1,3 @@ +line 1 +line 2 +line 3 diff --git a/bin/ed/test/g5.r b/bin/ed/test/g5.r new file mode 100644 index 0000000..15a2675 --- /dev/null +++ b/bin/ed/test/g5.r @@ -0,0 +1,9 @@ +line 1 +line 2 +line 3 +line 2 +line 3 +line 1 +line 3 +line 1 +line 2 diff --git a/bin/ed/test/g5.t b/bin/ed/test/g5.t new file mode 100644 index 0000000..e213481 --- /dev/null +++ b/bin/ed/test/g5.t @@ -0,0 +1,2 @@ +g/./1,3t$\ +1d diff --git a/bin/ed/test/h.err b/bin/ed/test/h.err new file mode 100644 index 0000000..a71e506 --- /dev/null +++ b/bin/ed/test/h.err @@ -0,0 +1 @@ +.h diff --git a/bin/ed/test/i.d b/bin/ed/test/i.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/bin/ed/test/i.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/i.r b/bin/ed/test/i.r new file mode 100644 index 0000000..5f27af0 --- /dev/null +++ b/bin/ed/test/i.r @@ -0,0 +1,8 @@ +hello world +hello world! +line 1 +line 2 +line 3 +line 4 +hello world!! +line5 diff --git a/bin/ed/test/i.t b/bin/ed/test/i.t new file mode 100644 index 0000000..d1d9805 --- /dev/null +++ b/bin/ed/test/i.t @@ -0,0 +1,9 @@ +1i +hello world +. +2i +hello world! +. +$i +hello world!! +. diff --git a/bin/ed/test/i1.err b/bin/ed/test/i1.err new file mode 100644 index 0000000..aaddede --- /dev/null +++ b/bin/ed/test/i1.err @@ -0,0 +1,3 @@ +1,$i +hello world +. diff --git a/bin/ed/test/i2.err b/bin/ed/test/i2.err new file mode 100644 index 0000000..b63f5ac --- /dev/null +++ b/bin/ed/test/i2.err @@ -0,0 +1,3 @@ +ii +hello world +. diff --git a/bin/ed/test/i3.err b/bin/ed/test/i3.err new file mode 100644 index 0000000..6d200c8 --- /dev/null +++ b/bin/ed/test/i3.err @@ -0,0 +1,3 @@ +0i +hello world +. diff --git a/bin/ed/test/j.d b/bin/ed/test/j.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/bin/ed/test/j.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/j.r b/bin/ed/test/j.r new file mode 100644 index 0000000..66f36a8 --- /dev/null +++ b/bin/ed/test/j.r @@ -0,0 +1,4 @@ +line 1 +line 2line 3 +line 4 +line5 diff --git a/bin/ed/test/j.t b/bin/ed/test/j.t new file mode 100644 index 0000000..9b5d28d --- /dev/null +++ b/bin/ed/test/j.t @@ -0,0 +1,2 @@ +1,1j +2,3j diff --git a/bin/ed/test/k.d b/bin/ed/test/k.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/bin/ed/test/k.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/k.r b/bin/ed/test/k.r new file mode 100644 index 0000000..eeb38db --- /dev/null +++ b/bin/ed/test/k.r @@ -0,0 +1,5 @@ +line 3 +hello world +line 4 +line5 +line 2 diff --git a/bin/ed/test/k.t b/bin/ed/test/k.t new file mode 100644 index 0000000..53d588d --- /dev/null +++ b/bin/ed/test/k.t @@ -0,0 +1,10 @@ +2ka +1d +'am$ +1ka +0a +hello world +. +'ad +u +'am0 diff --git a/bin/ed/test/k1.err b/bin/ed/test/k1.err new file mode 100644 index 0000000..eba1f3d --- /dev/null +++ b/bin/ed/test/k1.err @@ -0,0 +1 @@ +1,$ka diff --git a/bin/ed/test/k2.err b/bin/ed/test/k2.err new file mode 100644 index 0000000..b34a18d --- /dev/null +++ b/bin/ed/test/k2.err @@ -0,0 +1 @@ +kA diff --git a/bin/ed/test/k3.err b/bin/ed/test/k3.err new file mode 100644 index 0000000..70190c4 --- /dev/null +++ b/bin/ed/test/k3.err @@ -0,0 +1 @@ +0ka diff --git a/bin/ed/test/k4.err b/bin/ed/test/k4.err new file mode 100644 index 0000000..3457642 --- /dev/null +++ b/bin/ed/test/k4.err @@ -0,0 +1,6 @@ +a +hello +. +.ka +'ad +'ap diff --git a/bin/ed/test/l.d b/bin/ed/test/l.d new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bin/ed/test/l.d diff --git a/bin/ed/test/l.r b/bin/ed/test/l.r new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bin/ed/test/l.r diff --git a/bin/ed/test/l.t b/bin/ed/test/l.t new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bin/ed/test/l.t diff --git a/bin/ed/test/m.d b/bin/ed/test/m.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/bin/ed/test/m.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/m.err b/bin/ed/test/m.err new file mode 100644 index 0000000..3aec4c3 --- /dev/null +++ b/bin/ed/test/m.err @@ -0,0 +1,4 @@ +a +hello world +. +1,$m1 diff --git a/bin/ed/test/m.r b/bin/ed/test/m.r new file mode 100644 index 0000000..186cf54 --- /dev/null +++ b/bin/ed/test/m.r @@ -0,0 +1,5 @@ +line5 +line 1 +line 2 +line 3 +line 4 diff --git a/bin/ed/test/m.t b/bin/ed/test/m.t new file mode 100644 index 0000000..c39c088 --- /dev/null +++ b/bin/ed/test/m.t @@ -0,0 +1,7 @@ +1,2m$ +1,2m$ +1,2m$ +$m0 +$m0 +2,3m1 +2,3m3 diff --git a/bin/ed/test/mkscripts.sh b/bin/ed/test/mkscripts.sh new file mode 100644 index 0000000..1b8b3ee --- /dev/null +++ b/bin/ed/test/mkscripts.sh @@ -0,0 +1,75 @@ +#!/bin/sh - +# This script generates ed test scripts (.ed) from .t files +# +# $FreeBSD$ + +PATH="/bin:/usr/bin:/usr/local/bin/:." +ED=$1 +[ ! -x $ED ] && { echo "$ED: cannot execute"; exit 1; } + +for i in *.t; do +# base=${i%.*} +# base=`echo $i | sed 's/\..*//'` +# base=`expr $i : '\([^.]*\)'` +# ( +# echo "#!/bin/sh -" +# echo "$ED - <<\EOT" +# echo "r $base.d" +# cat $i +# echo "w $base.o" +# echo EOT +# ) >$base.ed +# chmod +x $base.ed +# The following is pretty ugly way of doing the above, and not appropriate +# use of ed but the point is that it can be done... + base=`$ED - \!"echo $i" <<-EOF + s/\..* + EOF` + $ED - <<-EOF + a + #!/bin/sh - + $ED - <<\EOT + H + r $base.d + w $base.o + EOT + . + -2r $i + w $base.ed + !chmod +x $base.ed + EOF +done + +for i in *.err; do +# base=${i%.*} +# base=`echo $i | sed 's/\..*//'` +# base=`expr $i : '\([^.]*\)'` +# ( +# echo "#!/bin/sh -" +# echo "$ED - <<\EOT" +# echo H +# echo "r $base.err" +# cat $i +# echo "w $base.o" +# echo EOT +# ) >$base-err.ed +# chmod +x $base-err.ed +# The following is pretty ugly way of doing the above, and not appropriate +# use of ed but the point is that it can be done... + base=`$ED - \!"echo $i" <<-EOF + s/\..* + EOF` + $ED - <<-EOF + a + #!/bin/sh - + $ED - <<\EOT + H + r $base.err + w $base.o + EOT + . + -2r $i + w ${base}.red + !chmod +x ${base}.red + EOF +done diff --git a/bin/ed/test/n.d b/bin/ed/test/n.d new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bin/ed/test/n.d diff --git a/bin/ed/test/n.r b/bin/ed/test/n.r new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bin/ed/test/n.r diff --git a/bin/ed/test/n.t b/bin/ed/test/n.t new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bin/ed/test/n.t diff --git a/bin/ed/test/nl.err b/bin/ed/test/nl.err new file mode 100644 index 0000000..8949a85 --- /dev/null +++ b/bin/ed/test/nl.err @@ -0,0 +1 @@ +,1 diff --git a/bin/ed/test/nl1.d b/bin/ed/test/nl1.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/bin/ed/test/nl1.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/nl1.r b/bin/ed/test/nl1.r new file mode 100644 index 0000000..9d8854c --- /dev/null +++ b/bin/ed/test/nl1.r @@ -0,0 +1,8 @@ + + +hello world +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/nl1.t b/bin/ed/test/nl1.t new file mode 100644 index 0000000..ea192e9 --- /dev/null +++ b/bin/ed/test/nl1.t @@ -0,0 +1,8 @@ +1 + + +0a + + +hello world +. diff --git a/bin/ed/test/nl2.d b/bin/ed/test/nl2.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/bin/ed/test/nl2.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/nl2.r b/bin/ed/test/nl2.r new file mode 100644 index 0000000..fe99e41 --- /dev/null +++ b/bin/ed/test/nl2.r @@ -0,0 +1,6 @@ +line 1 +line 2 +line 3 +line 4 +line5 +hello world diff --git a/bin/ed/test/nl2.t b/bin/ed/test/nl2.t new file mode 100644 index 0000000..73fd27b --- /dev/null +++ b/bin/ed/test/nl2.t @@ -0,0 +1,4 @@ +a +hello world +. +0;/./ diff --git a/bin/ed/test/p.d b/bin/ed/test/p.d new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bin/ed/test/p.d diff --git a/bin/ed/test/p.r b/bin/ed/test/p.r new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bin/ed/test/p.r diff --git a/bin/ed/test/p.t b/bin/ed/test/p.t new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bin/ed/test/p.t diff --git a/bin/ed/test/q.d b/bin/ed/test/q.d new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bin/ed/test/q.d diff --git a/bin/ed/test/q.r b/bin/ed/test/q.r new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bin/ed/test/q.r diff --git a/bin/ed/test/q.t b/bin/ed/test/q.t new file mode 100644 index 0000000..123a2c8 --- /dev/null +++ b/bin/ed/test/q.t @@ -0,0 +1,5 @@ +w q.o +a +hello +. +q diff --git a/bin/ed/test/q1.err b/bin/ed/test/q1.err new file mode 100644 index 0000000..0a7e178 --- /dev/null +++ b/bin/ed/test/q1.err @@ -0,0 +1 @@ +.q diff --git a/bin/ed/test/r1.d b/bin/ed/test/r1.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/bin/ed/test/r1.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/r1.err b/bin/ed/test/r1.err new file mode 100644 index 0000000..269aa7c --- /dev/null +++ b/bin/ed/test/r1.err @@ -0,0 +1 @@ +1,$r r1.err diff --git a/bin/ed/test/r1.r b/bin/ed/test/r1.r new file mode 100644 index 0000000..a3ff506 --- /dev/null +++ b/bin/ed/test/r1.r @@ -0,0 +1,7 @@ +line 1 +hello world +line 2 +line 3 +line 4 +line5 +hello world diff --git a/bin/ed/test/r1.t b/bin/ed/test/r1.t new file mode 100644 index 0000000..d787a92 --- /dev/null +++ b/bin/ed/test/r1.t @@ -0,0 +1,3 @@ +1;r !echo hello world +1 +r !echo hello world diff --git a/bin/ed/test/r2.d b/bin/ed/test/r2.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/bin/ed/test/r2.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/r2.err b/bin/ed/test/r2.err new file mode 100644 index 0000000..1c44fa3 --- /dev/null +++ b/bin/ed/test/r2.err @@ -0,0 +1 @@ +r a-good-book diff --git a/bin/ed/test/r2.r b/bin/ed/test/r2.r new file mode 100644 index 0000000..ac152ba --- /dev/null +++ b/bin/ed/test/r2.r @@ -0,0 +1,10 @@ +line 1 +line 2 +line 3 +line 4 +line5 +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/r2.t b/bin/ed/test/r2.t new file mode 100644 index 0000000..4286f42 --- /dev/null +++ b/bin/ed/test/r2.t @@ -0,0 +1 @@ +r diff --git a/bin/ed/test/r3.d b/bin/ed/test/r3.d new file mode 100644 index 0000000..593eec6 --- /dev/null +++ b/bin/ed/test/r3.d @@ -0,0 +1 @@ +r r3.t diff --git a/bin/ed/test/r3.r b/bin/ed/test/r3.r new file mode 100644 index 0000000..86d5f90 --- /dev/null +++ b/bin/ed/test/r3.r @@ -0,0 +1,2 @@ +r r3.t +r r3.t diff --git a/bin/ed/test/r3.t b/bin/ed/test/r3.t new file mode 100644 index 0000000..593eec6 --- /dev/null +++ b/bin/ed/test/r3.t @@ -0,0 +1 @@ +r r3.t diff --git a/bin/ed/test/s1.d b/bin/ed/test/s1.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/bin/ed/test/s1.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/s1.err b/bin/ed/test/s1.err new file mode 100644 index 0000000..d7ca0cf --- /dev/null +++ b/bin/ed/test/s1.err @@ -0,0 +1 @@ +s . x diff --git a/bin/ed/test/s1.r b/bin/ed/test/s1.r new file mode 100644 index 0000000..4eb0980 --- /dev/null +++ b/bin/ed/test/s1.r @@ -0,0 +1,5 @@ +liene 1 +(liene) (2) +(liene) (3) +liene (4) +(()liene5) diff --git a/bin/ed/test/s1.t b/bin/ed/test/s1.t new file mode 100644 index 0000000..b0028bb --- /dev/null +++ b/bin/ed/test/s1.t @@ -0,0 +1,6 @@ +s/\([^ ][^ ]*\)/(\1)/g +2s +/3/s +/\(4\)/sr +/\(.\)/srg +%s/i/&e/ diff --git a/bin/ed/test/s10.err b/bin/ed/test/s10.err new file mode 100644 index 0000000..0d8d83d --- /dev/null +++ b/bin/ed/test/s10.err @@ -0,0 +1,4 @@ +a +hello +. +s/[h[.]/x/ diff --git a/bin/ed/test/s2.d b/bin/ed/test/s2.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/bin/ed/test/s2.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/s2.err b/bin/ed/test/s2.err new file mode 100644 index 0000000..b5c851d --- /dev/null +++ b/bin/ed/test/s2.err @@ -0,0 +1,4 @@ +a +a +. +s/x*/a/g diff --git a/bin/ed/test/s2.r b/bin/ed/test/s2.r new file mode 100644 index 0000000..ca305c8 --- /dev/null +++ b/bin/ed/test/s2.r @@ -0,0 +1,5 @@ +li(n)e 1 +i(n)e 200 +li(n)e 3 +li(n)e 4 +li(n)e500 diff --git a/bin/ed/test/s2.t b/bin/ed/test/s2.t new file mode 100644 index 0000000..f365849 --- /dev/null +++ b/bin/ed/test/s2.t @@ -0,0 +1,4 @@ +,s/./(&)/3 +s/$/00 +2s//%/g +s/^l diff --git a/bin/ed/test/s3.d b/bin/ed/test/s3.d new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bin/ed/test/s3.d diff --git a/bin/ed/test/s3.err b/bin/ed/test/s3.err new file mode 100644 index 0000000..d68c7d0 --- /dev/null +++ b/bin/ed/test/s3.err @@ -0,0 +1 @@ +s/[xyx/a/ diff --git a/bin/ed/test/s3.r b/bin/ed/test/s3.r new file mode 100644 index 0000000..d6cada2 --- /dev/null +++ b/bin/ed/test/s3.r @@ -0,0 +1 @@ +hello world diff --git a/bin/ed/test/s3.t b/bin/ed/test/s3.t new file mode 100644 index 0000000..fbf8803 --- /dev/null +++ b/bin/ed/test/s3.t @@ -0,0 +1,6 @@ +a +hello/[]world +. +s/[/]/ / +s/[[:digit:][]/ / +s/[]]/ / diff --git a/bin/ed/test/s4.err b/bin/ed/test/s4.err new file mode 100644 index 0000000..35b609f --- /dev/null +++ b/bin/ed/test/s4.err @@ -0,0 +1 @@ +s/\a\b\c/xyz/ diff --git a/bin/ed/test/s5.err b/bin/ed/test/s5.err new file mode 100644 index 0000000..89104c5 --- /dev/null +++ b/bin/ed/test/s5.err @@ -0,0 +1 @@ +s//xyz/ diff --git a/bin/ed/test/s6.err b/bin/ed/test/s6.err new file mode 100644 index 0000000..b478595 --- /dev/null +++ b/bin/ed/test/s6.err @@ -0,0 +1 @@ +s diff --git a/bin/ed/test/s7.err b/bin/ed/test/s7.err new file mode 100644 index 0000000..30ba4fd --- /dev/null +++ b/bin/ed/test/s7.err @@ -0,0 +1,5 @@ +a +hello world +. +/./ +sr diff --git a/bin/ed/test/s8.err b/bin/ed/test/s8.err new file mode 100644 index 0000000..5665767 --- /dev/null +++ b/bin/ed/test/s8.err @@ -0,0 +1,4 @@ +a +hello +. +s/[h[=]/x/ diff --git a/bin/ed/test/s9.err b/bin/ed/test/s9.err new file mode 100644 index 0000000..1ff16dd --- /dev/null +++ b/bin/ed/test/s9.err @@ -0,0 +1,4 @@ +a +hello +. +s/[h[:]/x/ diff --git a/bin/ed/test/t.d b/bin/ed/test/t.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/bin/ed/test/t.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/t.r b/bin/ed/test/t.r new file mode 100644 index 0000000..2b28547 --- /dev/null +++ b/bin/ed/test/t.r @@ -0,0 +1,16 @@ +line 1 +line 1 +line 1 +line 2 +line 2 +line 3 +line 4 +line5 +line 1 +line 1 +line 1 +line 2 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/t1.d b/bin/ed/test/t1.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/bin/ed/test/t1.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/t1.err b/bin/ed/test/t1.err new file mode 100644 index 0000000..c49c556 --- /dev/null +++ b/bin/ed/test/t1.err @@ -0,0 +1 @@ +tt diff --git a/bin/ed/test/t1.r b/bin/ed/test/t1.r new file mode 100644 index 0000000..2b28547 --- /dev/null +++ b/bin/ed/test/t1.r @@ -0,0 +1,16 @@ +line 1 +line 1 +line 1 +line 2 +line 2 +line 3 +line 4 +line5 +line 1 +line 1 +line 1 +line 2 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/t1.t b/bin/ed/test/t1.t new file mode 100644 index 0000000..6b66163 --- /dev/null +++ b/bin/ed/test/t1.t @@ -0,0 +1,3 @@ +1t0 +2,3t2 +,t$ diff --git a/bin/ed/test/t2.d b/bin/ed/test/t2.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/bin/ed/test/t2.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/t2.err b/bin/ed/test/t2.err new file mode 100644 index 0000000..c202051 --- /dev/null +++ b/bin/ed/test/t2.err @@ -0,0 +1 @@ +t0;-1 diff --git a/bin/ed/test/t2.r b/bin/ed/test/t2.r new file mode 100644 index 0000000..0c75ff5 --- /dev/null +++ b/bin/ed/test/t2.r @@ -0,0 +1,6 @@ +line 1 +line5 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/t2.t b/bin/ed/test/t2.t new file mode 100644 index 0000000..5175abd --- /dev/null +++ b/bin/ed/test/t2.t @@ -0,0 +1 @@ +t0;/./ diff --git a/bin/ed/test/u.d b/bin/ed/test/u.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/bin/ed/test/u.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/u.err b/bin/ed/test/u.err new file mode 100644 index 0000000..caa1ba1 --- /dev/null +++ b/bin/ed/test/u.err @@ -0,0 +1 @@ +.u diff --git a/bin/ed/test/u.r b/bin/ed/test/u.r new file mode 100644 index 0000000..ad558d8 --- /dev/null +++ b/bin/ed/test/u.r @@ -0,0 +1,9 @@ +line 1 +hello +hello world!! +line 2 +line 3 +line 4 +line5 +hello +hello world!! diff --git a/bin/ed/test/u.t b/bin/ed/test/u.t new file mode 100644 index 0000000..131cb6e --- /dev/null +++ b/bin/ed/test/u.t @@ -0,0 +1,31 @@ +1;r u.t +u +a +hello +world +. +g/./s//x/\ +a\ +hello\ +world +u +u +u +a +hello world! +. +u +1,$d +u +2,3d +u +c +hello world!! +. +u +u +-1;.,+1j +u +u +u +.,+1t$ diff --git a/bin/ed/test/v.d b/bin/ed/test/v.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/bin/ed/test/v.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/v.r b/bin/ed/test/v.r new file mode 100644 index 0000000..714db63 --- /dev/null +++ b/bin/ed/test/v.r @@ -0,0 +1,11 @@ +line5 +order +hello world +line 1 +order +line 2 +order +line 3 +order +line 4 +order diff --git a/bin/ed/test/v.t b/bin/ed/test/v.t new file mode 100644 index 0000000..608a77f --- /dev/null +++ b/bin/ed/test/v.t @@ -0,0 +1,6 @@ +v/[ ]/m0 +v/[ ]/s/$/\ +hello world +v/hello /s/lo/p!/\ +a\ +order diff --git a/bin/ed/test/w.d b/bin/ed/test/w.d new file mode 100644 index 0000000..92f337e --- /dev/null +++ b/bin/ed/test/w.d @@ -0,0 +1,5 @@ +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/w.r b/bin/ed/test/w.r new file mode 100644 index 0000000..ac152ba --- /dev/null +++ b/bin/ed/test/w.r @@ -0,0 +1,10 @@ +line 1 +line 2 +line 3 +line 4 +line5 +line 1 +line 2 +line 3 +line 4 +line5 diff --git a/bin/ed/test/w.t b/bin/ed/test/w.t new file mode 100644 index 0000000..c2e18bd --- /dev/null +++ b/bin/ed/test/w.t @@ -0,0 +1,2 @@ +w !cat >\!.z +r \!.z diff --git a/bin/ed/test/w1.err b/bin/ed/test/w1.err new file mode 100644 index 0000000..e2c8a60 --- /dev/null +++ b/bin/ed/test/w1.err @@ -0,0 +1 @@ +w /to/some/far-away/place diff --git a/bin/ed/test/w2.err b/bin/ed/test/w2.err new file mode 100644 index 0000000..9daf89c --- /dev/null +++ b/bin/ed/test/w2.err @@ -0,0 +1 @@ +ww.o diff --git a/bin/ed/test/w3.err b/bin/ed/test/w3.err new file mode 100644 index 0000000..39bbf4c --- /dev/null +++ b/bin/ed/test/w3.err @@ -0,0 +1 @@ +wqp w.o diff --git a/bin/ed/test/x.err b/bin/ed/test/x.err new file mode 100644 index 0000000..0953f01 --- /dev/null +++ b/bin/ed/test/x.err @@ -0,0 +1 @@ +.x diff --git a/bin/ed/test/z.err b/bin/ed/test/z.err new file mode 100644 index 0000000..6a51a2d --- /dev/null +++ b/bin/ed/test/z.err @@ -0,0 +1,2 @@ +z +z diff --git a/bin/ed/undo.c b/bin/ed/undo.c new file mode 100644 index 0000000..b2063c7 --- /dev/null +++ b/bin/ed/undo.c @@ -0,0 +1,159 @@ +/* undo.c: This file contains the undo routines for the ed line editor */ +/*- + * Copyright (c) 1993 Andrew Moore, Talke Studio. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 +#if 0 +static char * const rcsid = "@(#)undo.c,v 1.1 1994/02/01 00:34:44 alm Exp"; +#else +static char * const rcsid = + "$FreeBSD$"; +#endif +#endif /* not lint */ + +#include "ed.h" + + +#define USIZE 100 /* undo stack size */ +undo_t *ustack = NULL; /* undo stack */ +long usize = 0; /* stack size variable */ +long u_p = 0; /* undo stack pointer */ + +/* push_undo_stack: return pointer to initialized undo node */ +undo_t * +push_undo_stack(type, from, to) + int type; + long from; + long to; +{ + undo_t *t; + +#if defined(sun) || defined(NO_REALLOC_NULL) + if (ustack == NULL && + (ustack = (undo_t *) malloc((usize = USIZE) * sizeof(undo_t))) == NULL) { + fprintf(stderr, "%s\n", strerror(errno)); + sprintf(errmsg, "out of memory"); + return NULL; + } +#endif + t = ustack; + if (u_p < usize || + (t = (undo_t *) realloc(ustack, (usize += USIZE) * sizeof(undo_t))) != NULL) { + ustack = t; + ustack[u_p].type = type; + ustack[u_p].t = get_addressed_line_node(to); + ustack[u_p].h = get_addressed_line_node(from); + return ustack + u_p++; + } + /* out of memory - release undo stack */ + fprintf(stderr, "%s\n", strerror(errno)); + sprintf(errmsg, "out of memory"); + clear_undo_stack(); + free(ustack); + ustack = NULL; + usize = 0; + return NULL; +} + + +/* USWAP: swap undo nodes */ +#define USWAP(x,y) { \ + undo_t utmp; \ + utmp = x, x = y, y = utmp; \ +} + + +long u_current_addr = -1; /* if >= 0, undo enabled */ +long u_addr_last = -1; /* if >= 0, undo enabled */ + +/* pop_undo_stack: undo last change to the editor buffer */ +int +pop_undo_stack() +{ + long n; + long o_current_addr = current_addr; + long o_addr_last = addr_last; + + if (u_current_addr == -1 || u_addr_last == -1) { + sprintf(errmsg, "nothing to undo"); + return ERR; + } else if (u_p) + modified = 1; + get_addressed_line_node(0); /* this get_addressed_line_node last! */ + SPL1(); + for (n = u_p; n-- > 0;) { + switch(ustack[n].type) { + case UADD: + REQUE(ustack[n].h->q_back, ustack[n].t->q_forw); + break; + case UDEL: + REQUE(ustack[n].h->q_back, ustack[n].h); + REQUE(ustack[n].t, ustack[n].t->q_forw); + break; + case UMOV: + case VMOV: + REQUE(ustack[n - 1].h, ustack[n].h->q_forw); + REQUE(ustack[n].t->q_back, ustack[n - 1].t); + REQUE(ustack[n].h, ustack[n].t); + n--; + break; + default: + /*NOTREACHED*/ + ; + } + ustack[n].type ^= 1; + } + /* reverse undo stack order */ + for (n = u_p; n-- > (u_p + 1)/ 2;) + USWAP(ustack[n], ustack[u_p - 1 - n]); + if (isglobal) + clear_active_list(); + current_addr = u_current_addr, u_current_addr = o_current_addr; + addr_last = u_addr_last, u_addr_last = o_addr_last; + SPL0(); + return 0; +} + + +/* clear_undo_stack: clear the undo stack */ +void +clear_undo_stack() +{ + line_t *lp, *ep, *tl; + + while (u_p--) + if (ustack[u_p].type == UDEL) { + ep = ustack[u_p].t->q_forw; + for (lp = ustack[u_p].h; lp != ep; lp = tl) { + unmark_line_node(lp); + tl = lp->q_forw; + free(lp); + } + } + u_p = 0; + u_current_addr = current_addr; + u_addr_last = addr_last; +} diff --git a/bin/expr/Makefile b/bin/expr/Makefile new file mode 100644 index 0000000..544853e --- /dev/null +++ b/bin/expr/Makefile @@ -0,0 +1,7 @@ +# $FreeBSD$ + +PROG= expr +SRCS= expr.y +YFLAGS= + +.include <bsd.prog.mk> diff --git a/bin/expr/expr.1 b/bin/expr/expr.1 new file mode 100644 index 0000000..85a0c8e --- /dev/null +++ b/bin/expr/expr.1 @@ -0,0 +1,135 @@ +.\" -*- nroff -*- +.\" +.\" Copyright (c) 1993 Winning Strategies, Inc. +.\" 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 Winning Strategies, Inc. +.\" 4. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd July 3, 1993 +.Dt EXPR 1 +.Os +.Sh NAME +.Nm expr +.Nd evaluate expression +.Sh SYNOPSIS +.Nm expr +.Ar expression +.Sh DESCRIPTION +The +.Nm +utility evaluates +.Ar expression +and writes the result on standard output. +.Pp +All operators are separate arguments to the +.Nm +utility. +Characters special to the command interpreter must be escaped. +.Pp +Operators are listed below in order of increasing precedence. +Operators with equal precedence are grouped within { } symbols. +.Bl -tag -width indent +.It Ar expr1 Li | Ar expr2 +Return the evaluation of +.Ar expr1 +if it is neither an empty string nor zero; +otherwise, returns the evaluation of +.Ar expr2 . +.It Ar expr1 Li & Ar expr2 +Return the evaluation of +.Ar expr1 +if neither expression evaluates to an empty string or zero; +otherwise, returns zero. +.It Ar expr1 Li "{=, >, >=, <, <=, !=}" Ar expr2 +Return the results of integer comparison if both arguments are integers; +otherwise, returns the results of string comparison using the locale-specific +collation sequence. +The result of each comparison is 1 if the specified relation is true, +or 0 if the relation is false. +.It Ar expr1 Li "{+, -}" Ar expr2 +Return the results of addition or subtraction of integer-valued arguments. +.It Ar expr1 Li "{*, /, %}" Ar expr2 +Return the results of multiplication, integer division, or remainder of integer-valued arguments. +.It Ar expr1 Li : Ar expr2 +The +.Dq \: +operator matches +.Ar expr1 +against +.Ar expr2 , +which must be a regular expression. The regular expression is anchored +to the beginning of the string with an implicit +.Dq ^ . +.Pp +If the match succeeds and the pattern contains at least one regular +expression subexpression +.Dq "\e(...\e)" , +the string corresponding to +.Dq "\e1" +is returned; +otherwise the matching operator returns the number of characters matched. +If the match fails and the pattern contains a regular expression subexpression +the null string is returned; +otherwise 0. +.El +.Pp +Parentheses are used for grouping in the usual manner. +.Sh EXAMPLES +.Bl -enum +.It +The following example adds one to the variable a. +.Dl a=`expr $a + 1` +.It +The following example returns the filename portion of a pathname stored +in variable a. The // characters act to eliminate ambiguity with the +division operator. +.Dl expr "//$a" Li : '.*/\e(.*\e)' +.It +The following example returns the number of characters in variable a. +.Dl expr $a Li : '.*' +.El +.Sh DIAGNOSTICS +The +.Nm +utility exits with one of the following values: +.Bl -tag -width indent -compact +.It 0 +the expression is neither an empty string nor 0. +.It 1 +the expression is an empty string or 0. +.It 2 +the expression is invalid. +.El +.Sh SEE ALSO +.Xr sh 1 , +.Xr test 1 +.Sh STANDARDS +The +.Nm +utility conforms to +.St -p1003.2 . diff --git a/bin/expr/expr.y b/bin/expr/expr.y new file mode 100644 index 0000000..835d2ee --- /dev/null +++ b/bin/expr/expr.y @@ -0,0 +1,617 @@ +%{ +/* Written by Pace Willisson (pace@blitz.com) + * and placed in the public domain. + * + * Largely rewritten by J.T. Conklin (jtc@wimsey.com) + * + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <locale.h> +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <regex.h> +#include <limits.h> + +enum valtype { + integer, numeric_string, string +} ; + +struct val { + enum valtype type; + union { + char *s; + quad_t i; + } u; +} ; + +struct val *result; +struct val *op_or (); +struct val *op_and (); +struct val *op_eq (); +struct val *op_gt (); +struct val *op_lt (); +struct val *op_ge (); +struct val *op_le (); +struct val *op_ne (); +struct val *op_plus (); +struct val *op_minus (); +struct val *op_times (); +struct val *op_div (); +struct val *op_rem (); +struct val *op_colon (); + +char **av; +%} + +%union +{ + struct val *val; +} + +%left <val> '|' +%left <val> '&' +%left <val> '=' '>' '<' GE LE NE +%left <val> '+' '-' +%left <val> '*' '/' '%' +%left <val> ':' + +%token <val> TOKEN +%type <val> start expr + +%% + +start: expr { result = $$; } + +expr: TOKEN + | '(' expr ')' { $$ = $2; } + | expr '|' expr { $$ = op_or ($1, $3); } + | expr '&' expr { $$ = op_and ($1, $3); } + | expr '=' expr { $$ = op_eq ($1, $3); } + | expr '>' expr { $$ = op_gt ($1, $3); } + | expr '<' expr { $$ = op_lt ($1, $3); } + | expr GE expr { $$ = op_ge ($1, $3); } + | expr LE expr { $$ = op_le ($1, $3); } + | expr NE expr { $$ = op_ne ($1, $3); } + | expr '+' expr { $$ = op_plus ($1, $3); } + | expr '-' expr { $$ = op_minus ($1, $3); } + | expr '*' expr { $$ = op_times ($1, $3); } + | expr '/' expr { $$ = op_div ($1, $3); } + | expr '%' expr { $$ = op_rem ($1, $3); } + | expr ':' expr { $$ = op_colon ($1, $3); } + ; + + +%% + +struct val * +make_integer (i) +quad_t i; +{ + struct val *vp; + + vp = (struct val *) malloc (sizeof (*vp)); + if (vp == NULL) { + errx (2, "malloc() failed"); + } + + vp->type = integer; + vp->u.i = i; + return vp; +} + +struct val * +make_str (s) +char *s; +{ + struct val *vp; + int i, isint; + + vp = (struct val *) malloc (sizeof (*vp)); + if (vp == NULL || ((vp->u.s = strdup (s)) == NULL)) { + errx (2, "malloc() failed"); + } + + for(i = 1, isint = isdigit(s[0]) || s[0] == '-'; + isint && i < strlen(s); + i++) + { + if(!isdigit(s[i])) + isint = 0; + } + + if (isint) + vp->type = numeric_string; + else + vp->type = string; + + return vp; +} + + +void +free_value (vp) +struct val *vp; +{ + if (vp->type == string || vp->type == numeric_string) + free (vp->u.s); +} + + +quad_t +to_integer (vp) +struct val *vp; +{ + quad_t i; + + if (vp->type == integer) + return 1; + + if (vp->type == string) + return 0; + + /* vp->type == numeric_string, make it numeric */ + errno = 0; + i = strtoq(vp->u.s, (char**)NULL, 10); + if (errno != 0) { + errx (2, "overflow"); + } + free (vp->u.s); + vp->u.i = i; + vp->type = integer; + return 1; +} + +void +to_string (vp) +struct val *vp; +{ + char *tmp; + + if (vp->type == string || vp->type == numeric_string) + return; + + tmp = malloc (25); + if (tmp == NULL) { + errx (2, "malloc() failed"); + } + + sprintf (tmp, "%qd", vp->u.i); + vp->type = string; + vp->u.s = tmp; +} + + +int +isstring (vp) +struct val *vp; +{ + /* only TRUE if this string is not a valid integer */ + return (vp->type == string); +} + + +int +yylex () +{ + char *p; + + if (*av == NULL) + return (0); + + p = *av++; + + if (strlen (p) == 1) { + if (strchr ("|&=<>+-*/%:()", *p)) + return (*p); + } else if (strlen (p) == 2 && p[1] == '=') { + switch (*p) { + case '>': return (GE); + case '<': return (LE); + case '!': return (NE); + } + } + + yylval.val = make_str (p); + return (TOKEN); +} + +int +is_zero_or_null (vp) +struct val *vp; +{ + if (vp->type == integer) { + return (vp->u.i == 0); + } else { + return (*vp->u.s == 0 || (to_integer (vp) && vp->u.i == 0)); + } + /* NOTREACHED */ +} + +int yyparse (); + +int +main (argc, argv) +int argc; +char **argv; +{ + setlocale (LC_ALL, ""); + + av = argv + 1; + + yyparse (); + + if (result->type == integer) + printf ("%qd\n", result->u.i); + else + printf ("%s\n", result->u.s); + + return (is_zero_or_null (result)); +} + +int +yyerror (s) +char *s; +{ + errx (2, "syntax error"); +} + + +struct val * +op_or (a, b) +struct val *a, *b; +{ + if (is_zero_or_null (a)) { + free_value (a); + return (b); + } else { + free_value (b); + return (a); + } +} + +struct val * +op_and (a, b) +struct val *a, *b; +{ + if (is_zero_or_null (a) || is_zero_or_null (b)) { + free_value (a); + free_value (b); + return (make_integer ((quad_t)0)); + } else { + free_value (b); + return (a); + } +} + +struct val * +op_eq (a, b) +struct val *a, *b; +{ + struct val *r; + + if (isstring (a) || isstring (b)) { + to_string (a); + to_string (b); + r = make_integer ((quad_t)(strcoll (a->u.s, b->u.s) == 0)); + } else { + (void)to_integer(a); + (void)to_integer(b); + r = make_integer ((quad_t)(a->u.i == b->u.i)); + } + + free_value (a); + free_value (b); + return r; +} + +struct val * +op_gt (a, b) +struct val *a, *b; +{ + struct val *r; + + if (isstring (a) || isstring (b)) { + to_string (a); + to_string (b); + r = make_integer ((quad_t)(strcoll (a->u.s, b->u.s) > 0)); + } else { + (void)to_integer(a); + (void)to_integer(b); + r = make_integer ((quad_t)(a->u.i > b->u.i)); + } + + free_value (a); + free_value (b); + return r; +} + +struct val * +op_lt (a, b) +struct val *a, *b; +{ + struct val *r; + + if (isstring (a) || isstring (b)) { + to_string (a); + to_string (b); + r = make_integer ((quad_t)(strcoll (a->u.s, b->u.s) < 0)); + } else { + (void)to_integer(a); + (void)to_integer(b); + r = make_integer ((quad_t)(a->u.i < b->u.i)); + } + + free_value (a); + free_value (b); + return r; +} + +struct val * +op_ge (a, b) +struct val *a, *b; +{ + struct val *r; + + if (isstring (a) || isstring (b)) { + to_string (a); + to_string (b); + r = make_integer ((quad_t)(strcoll (a->u.s, b->u.s) >= 0)); + } else { + (void)to_integer(a); + (void)to_integer(b); + r = make_integer ((quad_t)(a->u.i >= b->u.i)); + } + + free_value (a); + free_value (b); + return r; +} + +struct val * +op_le (a, b) +struct val *a, *b; +{ + struct val *r; + + if (isstring (a) || isstring (b)) { + to_string (a); + to_string (b); + r = make_integer ((quad_t)(strcoll (a->u.s, b->u.s) <= 0)); + } else { + (void)to_integer(a); + (void)to_integer(b); + r = make_integer ((quad_t)(a->u.i <= b->u.i)); + } + + free_value (a); + free_value (b); + return r; +} + +struct val * +op_ne (a, b) +struct val *a, *b; +{ + struct val *r; + + if (isstring (a) || isstring (b)) { + to_string (a); + to_string (b); + r = make_integer ((quad_t)(strcoll (a->u.s, b->u.s) != 0)); + } else { + (void)to_integer(a); + (void)to_integer(b); + r = make_integer ((quad_t)(a->u.i != b->u.i)); + } + + free_value (a); + free_value (b); + return r; +} + +int +chk_plus (a, b, r) +quad_t a, b, r; +{ + /* sum of two positive numbers must be positive */ + if (a > 0 && b > 0 && r <= 0) + return 1; + /* sum of two negative numbers must be negative */ + if (a < 0 && b < 0 && r >= 0) + return 1; + /* all other cases are OK */ + return 0; +} + +struct val * +op_plus (a, b) +struct val *a, *b; +{ + struct val *r; + + if (!to_integer (a) || !to_integer (b)) { + errx (2, "non-numeric argument"); + } + + r = make_integer (/*(quad_t)*/(a->u.i + b->u.i)); + if (chk_plus (a->u.i, b->u.i, r->u.i)) { + errx (2, "overflow"); + } + free_value (a); + free_value (b); + return r; +} + +int +chk_minus (a, b, r) +quad_t a, b, r; +{ + /* special case subtraction of QUAD_MIN */ + if (b == QUAD_MIN) { + if (a >= 0) + return 1; + else + return 0; + } + /* this is allowed for b != QUAD_MIN */ + return chk_plus (a, -b, r); +} + +struct val * +op_minus (a, b) +struct val *a, *b; +{ + struct val *r; + + if (!to_integer (a) || !to_integer (b)) { + errx (2, "non-numeric argument"); + } + + r = make_integer (/*(quad_t)*/(a->u.i - b->u.i)); + if (chk_minus (a->u.i, b->u.i, r->u.i)) { + errx (2, "overflow"); + } + free_value (a); + free_value (b); + return r; +} + +int +chk_times (a, b, r) +quad_t a, b, r; +{ + /* special case: first operand is 0, no overflow possible */ + if (a == 0) + return 0; + /* cerify that result of division matches second operand */ + if (r / a != b) + return 1; + return 0; +} + +struct val * +op_times (a, b) +struct val *a, *b; +{ + struct val *r; + + if (!to_integer (a) || !to_integer (b)) { + errx (2, "non-numeric argument"); + } + + r = make_integer (/*(quad_t)*/(a->u.i * b->u.i)); + if (chk_times (a->u.i, b->u.i, r->u.i)) { + errx (2, "overflow"); + } + free_value (a); + free_value (b); + return (r); +} + +int +chk_div (a, b, r) +quad_t a, b, r; +{ + /* div by zero has been taken care of before */ + /* only QUAD_MIN / -1 causes overflow */ + if (a == QUAD_MIN && b == -1) + return 1; + /* everything else is OK */ + return 0; +} + +struct val * +op_div (a, b) +struct val *a, *b; +{ + struct val *r; + + if (!to_integer (a) || !to_integer (b)) { + errx (2, "non-numeric argument"); + } + + if (b->u.i == 0) { + errx (2, "division by zero"); + } + + r = make_integer (/*(quad_t)*/(a->u.i / b->u.i)); + if (chk_div (a->u.i, b->u.i, r->u.i)) { + errx (2, "overflow"); + } + free_value (a); + free_value (b); + return r; +} + +struct val * +op_rem (a, b) +struct val *a, *b; +{ + struct val *r; + + if (!to_integer (a) || !to_integer (b)) { + errx (2, "non-numeric argument"); + } + + if (b->u.i == 0) { + errx (2, "division by zero"); + } + + r = make_integer (/*(quad_t)*/(a->u.i % b->u.i)); + /* chk_rem necessary ??? */ + free_value (a); + free_value (b); + return r; +} + +struct val * +op_colon (a, b) +struct val *a, *b; +{ + regex_t rp; + regmatch_t rm[2]; + char errbuf[256]; + int eval; + struct val *v; + + /* coerce to both arguments to strings */ + to_string(a); + to_string(b); + + /* compile regular expression */ + if ((eval = regcomp (&rp, b->u.s, 0)) != 0) { + regerror (eval, &rp, errbuf, sizeof(errbuf)); + errx (2, "%s", errbuf); + } + + /* compare string against pattern */ + /* remember that patterns are anchored to the beginning of the line */ + if (regexec(&rp, a->u.s, 2, rm, 0) == 0 && rm[0].rm_so == 0) { + if (rm[1].rm_so >= 0) { + *(a->u.s + rm[1].rm_eo) = '\0'; + v = make_str (a->u.s + rm[1].rm_so); + + } else { + v = make_integer ((quad_t)(rm[0].rm_eo - rm[0].rm_so)); + } + } else { + if (rp.re_nsub == 0) { + v = make_integer ((quad_t)0); + } else { + v = make_str (""); + } + } + + /* free arguments and pattern buffer */ + free_value (a); + free_value (b); + regfree (&rp); + + return v; +} diff --git a/bin/hostname/Makefile b/bin/hostname/Makefile new file mode 100644 index 0000000..3c7997f --- /dev/null +++ b/bin/hostname/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 +# $FreeBSD$ + +PROG= hostname + +.include <bsd.prog.mk> diff --git a/bin/hostname/hostname.1 b/bin/hostname/hostname.1 new file mode 100644 index 0000000..ebadd2e --- /dev/null +++ b/bin/hostname/hostname.1 @@ -0,0 +1,71 @@ +.\" 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.2 (Berkeley) 4/28/95 +.\" $FreeBSD$ +.\" +.Dd April 28, 1995 +.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 name-of-host +.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/rc.network , +normally run at boot +time. +This script uses the +.Va hostname +variable in +.Pa /etc/rc.conf . +.Pp +Options: +.Bl -tag -width flag +.It Fl s +Trim off any domain information from the printed +name. +.El +.Sh SEE ALSO +.Xr gethostname 3 , +.Xr rc.conf 5 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/bin/hostname/hostname.c b/bin/hostname/hostname.c new file mode 100644 index 0000000..94e5d4a --- /dev/null +++ b/bin/hostname/hostname.c @@ -0,0 +1,101 @@ +/* + * 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 const copyright[] = +"@(#) Copyright (c) 1988, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)hostname.c 8.1 (Berkeley) 5/31/93"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/param.h> + +#include <err.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, sflag; + char *p, hostname[MAXHOSTNAMELEN]; + + sflag = 0; + while ((ch = getopt(argc, argv, "s")) != -1) + switch (ch) { + case 's': + sflag = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc > 1) + usage(); + + 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); +} + +void +usage() +{ + + (void)fprintf(stderr, "usage: hostname [-s] [name-of-host]\n"); + exit(1); +} diff --git a/bin/kenv/Makefile b/bin/kenv/Makefile new file mode 100644 index 0000000..33a6dfc --- /dev/null +++ b/bin/kenv/Makefile @@ -0,0 +1,5 @@ +# $FreeBSD$ + +PROG= kenv + +.include <bsd.prog.mk> diff --git a/bin/kenv/kenv.1 b/bin/kenv/kenv.1 new file mode 100644 index 0000000..e80b944 --- /dev/null +++ b/bin/kenv/kenv.1 @@ -0,0 +1,50 @@ +.\" Copyright (c) 2000 Peter Wemm <peter@freebsd.org> +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd June 6, 1993 +.Dt KENV 1 +.Os BSD 4 +.Sh NAME +.Nm kenv +.Nd dump the kernel environment +.Sh SYNOPSIS +.Nm kenv +.Op Fl h +.Op variable +.Sh DESCRIPTION +.Nm kenv +will dump the kernel environment. +If the +.Fl h +flag is specified, it will limit the report to kernel probe hints. +If an optional variable name is specified, +.Nm +will only report that value. +.Sh SEE ALSO +.Xr loader 8 +.Sh HISTORY +.Nm kenv +appeared in +.Fx 5.0 . diff --git a/bin/kenv/kenv.c b/bin/kenv/kenv.c new file mode 100644 index 0000000..4e5970a --- /dev/null +++ b/bin/kenv/kenv.c @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2000 Peter Wemm <peter@freebsd.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +#include <sys/types.h> +#include <sys/sysctl.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <err.h> +#include <unistd.h> + +static char sbuf[1024]; + +static void +usage(void) +{ + errx(1, "usage: [-h] [variable]"); +} + +int +main(int argc, char **argv) +{ + int name2oid_oid[2]; + int real_oid[CTL_MAXNAME+4]; + size_t oidlen; + int ch, error, hflag, i, slen; + char *env, *eq, *name, *var, *val; + + hflag = 0; + env = NULL; + while ((ch = getopt(argc, argv, "h")) != -1) { + switch (ch) { + case 'h': + hflag++; + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + if (argc > 0) { + env = argv[0]; + argv++; + argc--; + } + if (argc > 0) + usage(); + name2oid_oid[0] = 0; /* This is magic & undocumented! */ + name2oid_oid[1] = 3; + oidlen = sizeof(real_oid); + name = "kern.environment"; + error = sysctl(name2oid_oid, 2, real_oid, &oidlen, name, strlen(name)); + if (error < 0) + err(1, "cannot find kern.environment base sysctl OID"); + oidlen /= sizeof (int); + if (oidlen >= CTL_MAXNAME) + errx(1, "kern.environment OID is too large!"); + real_oid[oidlen] = 0; + for (i = 0; ; i++) { + real_oid[oidlen + 1] = i; + slen = sizeof(sbuf) - 1; + error = sysctl(real_oid, oidlen + 2, sbuf, &slen, NULL, 0); + if (error < 0) { + if (errno != ENOENT) + err(1, "sysctl kern.environment.%d\n", i); + break; + } + sbuf[sizeof(sbuf) - 1] = '\0'; + eq = strchr(sbuf, '='); + if (eq == NULL) + err(1, "malformed environment string: %s\n", sbuf); + var = sbuf; + *eq = '\0'; + val = eq + 1; + if (env) { + if (strcmp(var, env) != 0) + continue; + printf("%s\n", val); + break; + } + if (hflag) { + if (strncmp(var, "hint.", 5) != 0) + continue; + /* FALLTHROUGH */ + } + printf("%s=\"", var); + while (*val) { + switch (*val) { + case '"': + putchar('\\'); + putchar('"'); + break; + case '\\': + putchar('\\'); + putchar('\\'); + break; + default: + putchar(*val); + break; + } + val++; + } + printf("\"\n"); + } + exit(0); +} diff --git a/bin/kill/Makefile b/bin/kill/Makefile new file mode 100644 index 0000000..55ef16d --- /dev/null +++ b/bin/kill/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 +# $FreeBSD$ + +PROG= kill + +.include <bsd.prog.mk> diff --git a/bin/kill/kill.1 b/bin/kill/kill.1 new file mode 100644 index 0000000..1a6d0f7 --- /dev/null +++ b/bin/kill/kill.1 @@ -0,0 +1,142 @@ +.\" 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.2 (Berkeley) 4/28/95 +.\" $FreeBSD$ +.\" +.Dd April 28, 1995 +.Dt KILL 1 +.Os +.Sh NAME +.Nm kill +.Nd terminate or signal a process +.Sh SYNOPSIS +.Nm kill +.Op Fl s Ar signal_name +.Ar pid +\&... +.Nm kill +.Fl l +.Op Ar exit_status +.Nm kill +.Fl signal_name +.Ar pid +\&... +.Nm kill +.Fl signal_number +.Ar pid +\&... +.Sh DESCRIPTION +The +.Nm +utility sends a 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 s Ar signal_name +A symbolic signal name specifying the signal to be sent instead of the +default +.Dv TERM . +.It Fl l Op Ar exit_status +If no operand is given, list the signal names; otherwise, write +the signal name corresponding to +.Ar exit_status . +.It Fl signal_name +A symbolic signal name specifying the signal to be sent instead of the +default +.Dv TERM . +.It Fl signal_number +A non-negative decimal integer, specifying the signal to be sent instead +of the default +.Dv TERM . +.El +.Pp +The following pids have special meanings: +.Bl -tag -width Ds -compact +.It -1 +If superuser, broadcast the signal to all processes; otherwise broadcast +to all processes belonging to the user. +.El +.Pp +Some of the more commonly used signals: +.Bl -tag -width Ds -compact +.It 1 +HUP (hang up) +.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 +.Pp +Some shells may provide a builtin +.Nm +command which is similar or identical to this utility. +Consult the +.Xr builtin 1 +manual page. +.Sh SEE ALSO +.Xr builtin 1 , +.Xr csh 1 , +.Xr killall 1 , +.Xr ps 1 , +.Xr kill 2 , +.Xr sigaction 2 +.Sh STANDARDS +The +.Nm +function is expected to be +.St -p1003.2 +compatible. +.Sh HISTORY +A +.Nm +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..9de184f --- /dev/null +++ b/bin/kill/kill.c @@ -0,0 +1,191 @@ +/* + * 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 const copyright[] = +"@(#) Copyright (c) 1988, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)kill.c 8.4 (Berkeley) 4/28/95"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#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 printsignals __P((FILE *)); +int signame_to_signum __P((char *)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int errors, numsig, pid; + char *ep; + + if (argc < 2) + usage(); + + numsig = SIGTERM; + + argc--, argv++; + if (!strcmp(*argv, "-l")) { + argc--, argv++; + if (argc > 1) + usage(); + if (argc == 1) { + if (!isdigit(**argv)) + usage(); + numsig = strtol(*argv, &ep, 10); + if (!**argv || *ep) + errx(1, "illegal signal number: %s", *argv); + if (numsig >= 128) + numsig -= 128; + if (numsig <= 0 || numsig >= NSIG) + nosig(*argv); + printf("%s\n", sys_signame[numsig]); + exit(0); + } + printsignals(stdout); + exit(0); + } + + if (!strcmp(*argv, "-s")) { + argc--, argv++; + if (argc < 1) { + warnx("option requires an argument -- s"); + usage(); + } + if (strcmp(*argv, "0")) { + if ((numsig = signame_to_signum(*argv)) < 0) + nosig(*argv); + } else + numsig = 0; + argc--, argv++; + } else if (**argv == '-') { + ++*argv; + if (isalpha(**argv)) { + if ((numsig = signame_to_signum(*argv)) < 0) + 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); + argc--, argv++; + } + + if (argc == 0) + usage(); + + for (errors = 0; argc; argc--, 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); +} + +int +signame_to_signum(sig) + char *sig; +{ + int n; + + if (!strncasecmp(sig, "sig", 3)) + sig += 3; + for (n = 1; n < NSIG; n++) { + if (!strcasecmp(sys_signame[n], sig)) + return (n); + } + return (-1); +} + +void +nosig(name) + char *name; +{ + + warnx("unknown signal %s; valid signals:", name); + printsignals(stderr); + exit(1); +} + +void +printsignals(fp) + FILE *fp; +{ + int n; + + for (n = 1; n < NSIG; n++) { + (void)fprintf(fp, "%s", sys_signame[n]); + if (n == (NSIG / 2) || n == (NSIG - 1)) + (void)fprintf(fp, "\n"); + else + (void)fprintf(fp, " "); + } +} + +void +usage() +{ + + (void)fprintf(stderr, "%s\n%s\n%s\n%s\n", + "usage: kill [-s signal_name] pid ...", + " kill -l [exit_status]", + " kill -signal_name pid ...", + " kill -signal_number pid ..."); + exit(1); +} diff --git a/bin/ln/Makefile b/bin/ln/Makefile new file mode 100644 index 0000000..bfbcef8 --- /dev/null +++ b/bin/ln/Makefile @@ -0,0 +1,11 @@ +# @(#)Makefile 8.2 (Berkeley) 5/31/93 +# $FreeBSD$ + +PROG= ln +MAN1= ln.1 +MAN7= symlink.7 + +LINKS= ${BINDIR}/ln ${BINDIR}/link +MLINKS= ln.1 link.1 + +.include <bsd.prog.mk> diff --git a/bin/ln/ln.1 b/bin/ln/ln.1 new file mode 100644 index 0000000..51d9892 --- /dev/null +++ b/bin/ln/ln.1 @@ -0,0 +1,179 @@ +.\" 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 +.\" $FreeBSD$ +.\" +.Dd December 30, 1993 +.Dt LN 1 +.Os BSD 4 +.Sh NAME +.Nm ln , +.Nm link +.Nd make links +.Sh SYNOPSIS +.Nm ln +.Op Fl fisv +.Ar source_file +.Op target_file +.Nm ln +.Op Fl fisv +.Ar source_file ... +.Op target_dir +.Nm link +.Ar source_file Ar target_file +.Sh DESCRIPTION +The +.Nm +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 and symbolic link. +.Pp +The options are as follows: +.Bl -tag -width flag +.It Fl f +If the target file already exists, +then unlink it so that the link may occur. +(The +.Fl f +option overrides any previous +.Fl i +options.) +.It Fl i +Cause +.Nm +to write a prompt to standard error if the target file exists. +If the response from the standard input begins with the character +.Sq Li y +or +.Sq Li Y , +then unlink the target file so that the link may occur. +Otherwise, do not attempt the link. +(The +.Fl i +option overrides any previous +.Fl f +options.) +.It Fl s +Create a symbolic link. +.It Fl v +Cause +.Nm +to be verbose, showing files as they are processed. +.El +.Pp +By default, +.Nm +makes +.Em hard +links. +A hard link to a file is indistinguishable from the original directory entry; +any changes to a file are effectively 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 +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 +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. +.Pp +When the utility is called as +.Nm link , +exactly two arguments must be supplied, +neither of which may specify a directory. +No options may be supplied in this simple mode of operation, +which performs a +.Xr link 2 +operation using the two passed arguments. +.Sh SEE ALSO +.Xr link 2 , +.Xr lstat 2 , +.Xr readlink 2 , +.Xr stat 2 , +.Xr symlink 2 , +.Xr symlink 7 +.Sh COMPATIBILITY +The +.Fl i +and +.Fl v +options are non-standard and their use in scripts is not recommended. +.Sh HISTORY +An +.Nm +command appeared in +.At v1 . +The simplified +.Nm link +command conforms to +.St -susv2 . diff --git a/bin/ln/ln.c b/bin/ln/ln.c new file mode 100644 index 0000000..27c6a82 --- /dev/null +++ b/bin/ln/ln.c @@ -0,0 +1,221 @@ +/* + * 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 const copyright[] = +"@(#) Copyright (c) 1987, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)ln.c 8.2 (Berkeley) 3/31/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#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 fflag; /* Unlink existing files. */ +int iflag; /* Interactive mode. */ +int sflag; /* Symbolic, not hard, link. */ +int vflag; /* Verbose output. */ + /* System link call. */ +int (*linkf) __P((const char *, const char *)); +char linkch; + +int linkit __P((char *, char *, int)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct stat sb; + int ch, exitval; + char *p, *sourcedir; + + /* + * Test for the special case where the utility is called as + * "link", for which the functionality provided is greatly + * simplified. + */ + if ((p = rindex(argv[0], '/')) == NULL) + p = argv[0]; + else + ++p; + if (strcmp(p, "link") == 0) { + if (argc == 3) { + linkf = link; + exit(linkit(argv[1], argv[2], 0)); + } else + usage(); + } + + while ((ch = getopt(argc, argv, "fisv")) != -1) + switch (ch) { + case 'f': + fflag = 1; + iflag = 0; + break; + case 'i': + iflag = 1; + fflag = 0; + break; + case 's': + sflag = 1; + break; + case 'v': + vflag = 1; + break; + case '?': + default: + usage(); + } + + argv += optind; + argc -= optind; + + linkf = sflag ? symlink : link; + linkch = sflag ? '-' : '='; + + 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 ch, exists, first; + 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. */ + if (S_ISDIR(sb.st_mode)) { + errno = EISDIR; + warn("%s", 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 = !lstat(source, &sb); + } else + exists = !lstat(source, &sb); + + /* + * If the file exists, then unlink it forcibly if -f was specified + * and interactively if -i was specified. + */ + if (fflag && exists) { + if (unlink(source)) { + warn("%s", source); + return (1); + } + } else if (iflag && exists) { + fflush(stdout); + fprintf(stderr, "replace %s? ", source); + + first = ch = getchar(); + while(ch != '\n' && ch != EOF) + ch = getchar(); + if (first != 'y' && first != 'Y') { + fprintf(stderr, "not replaced\n"); + return (1); + } + + if (unlink(source)) { + warn("%s", source); + return (1); + } + } + + /* Attempt the link. */ + if ((*linkf)(target, source)) { + warn("%s", source); + return (1); + } + if (vflag) + (void)printf("%s %c> %s\n", source, linkch, target); + return (0); +} + +void +usage() +{ + (void)fprintf(stderr, "%s\n%s\n%s\n", + "usage: ln [-fisv] file1 file2", + " ln [-fisv] file ... directory", + " link file1 file2"); + exit(1); +} diff --git a/bin/ln/symlink.7 b/bin/ln/symlink.7 new file mode 100644 index 0000000..3e474ba --- /dev/null +++ b/bin/ln/symlink.7 @@ -0,0 +1,443 @@ +.\" 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 +.\" $FreeBSD$ +.\" +.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 five system calls that do not follow links, and which operate +on the symbolic link itself. +They are: +.Xr lchown 2 , +.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 +The owner and group of an existing symbolic link can be changed by +means of the +.Xr lchown 2 +system call. +The other file attributes, such as the modification time and access +permissions, are not used by the system and cannot be changed. +.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. +The +.Xr lchown 2 +system call was added later when the limitations of the new +.Xr chown 2 +became apparent. +.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 "chown slink" +change the ownership of the 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 "chown root slink" +would change the ownership of the file referenced by +.Dq Li slink , +while +.Dq Li "chown -h root slink" +would change the ownership of +.Dq Li slink +itself. +.Pp +There are four 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 H +or +.Fl L +option is specified, +or if the +.Fl F , +.Fl d +or +.Fl l +options are not specified. (The +.Nm ls +command is the only command where the +.Fl H +and +.Fl L +options affect its behavior even though it is not doing a walk of +a file tree.) +.Pp +The +.Xr file 1 +command is also an exception to this rule. +The +.Xr file 1 +command does not follow symbolic links named as argument by default. +The +.Xr file 1 +command does follow symbolic links named as argument if +.Fl L +option is specified. +.Pp +The +.Bx 4.4 +system differs from historical 4BSD systems in that the +.Nm chown +and +.Nm chgrp +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 acts a little differently. If you do not specify the +.Fl F , +.Fl d +or +.Fl l +options, +.Nm ls +will follow symbolic links specified on the command line. 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. +.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 lchown 2 , +.Xr lstat 2 , +.Xr readlink 2 , +.Xr rename 2 , +.Xr symlink 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..96dffe1 --- /dev/null +++ b/bin/ls/Makefile @@ -0,0 +1,14 @@ +# @(#)Makefile 8.1 (Berkeley) 6/2/93 +# $FreeBSD$ + + +PROG= ls +SRCS= cmp.c ls.c print.c util.c + +.if !defined(RELEASE_BUILD_FIXIT) +CFLAGS+= -DCOLORLS +LDADD+= -ltermcap +DPADD+= ${LIBTERMCAP} +.endif + +.include <bsd.prog.mk> diff --git a/bin/ls/cmp.c b/bin/ls/cmp.c new file mode 100644 index 0000000..91852ba --- /dev/null +++ b/bin/ls/cmp.c @@ -0,0 +1,109 @@ +/* + * 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 +#if 0 +static char sccsid[] = "@(#)cmp.c 8.1 (Berkeley) 5/31/93"; +#else +static const char rcsid[] = + "$FreeBSD$"; +#endif +#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 (strcoll(a->fts_name, b->fts_name)); +} + +int +revnamecmp(a, b) + const FTSENT *a, *b; +{ + return (strcoll(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..31e2ced --- /dev/null +++ b/bin/ls/extern.h @@ -0,0 +1,60 @@ +/*- + * 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. + * + * from: @(#)extern.h 8.1 (Berkeley) 5/31/93 + * $FreeBSD$ + */ + +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 *)); + +void printcol __P((DISPLAY *)); +void printlong __P((DISPLAY *)); +void printscol __P((DISPLAY *)); +void usage __P((void)); +int len_octal __P((const char *, int)); +int prn_octal __P((const char *)); +int prn_printable __P((const char *)); +#ifdef COLORLS +void parsecolors __P((char *cs)); +void colorquit __P((int)); + +extern char *ansi_fgcol; +extern char *ansi_bgcol; +extern char *ansi_coloff; +#endif diff --git a/bin/ls/ls.1 b/bin/ls/ls.1 new file mode 100644 index 0000000..29a303f --- /dev/null +++ b/bin/ls/ls.1 @@ -0,0 +1,545 @@ +.\" 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 acknowledgment: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (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.7 (Berkeley) 7/29/94 +.\" $FreeBSD$ +.\" +.Dd July 29, 1994 +.Dt LS 1 +.Os +.Sh NAME +.Nm ls +.Nd list directory contents +.Sh SYNOPSIS +.Nm ls +.Op Fl ABCFGHLPRTWabcdfgiklnoqrstu1 +.Op Ar file ... +.Sh DESCRIPTION +For each operand that names a +.Ar file +of a type other than +directory, +.Nm +displays its name as well as any requested, +associated information. +For each operand that names a +.Ar file +of type directory, +.Nm +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 B +Force printing of non-graphic characters in file names as \\xxx, +where xxx is the numeric value of the character in octal. +.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, +an at sign (@) after each symbolic link, +an equals sign (=) after each socket, +a percent sign (%) after each whiteout, +and a vertical bar (|) after each that is a +.Tn FIFO . +.It Fl G +Enable colorized output. This option is equivalent to defining +.Ev CLICOLOR +in the environment. (See below). +.It Fl H +Symbolic links on the command line are followed. This option is assumed if +none of the +.Fl F , +.Fl d , +or +.Fl l +options are specified. +.It Fl L +If argument is a symbolic link, list the file or directory the link references +rather than the link itself. +This option cancels the +.Fl P +option. +.It Fl P +If argument is a symbolic link, list the link itself rather than the +object the link references. This option cancels the +.Fl H +and +.Fl L +options. +.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 W +Display whiteouts when scanning directories. +.It Fl a +Include directory entries whose names begin with a +dot (.). +.It Fl b +As +.Fl B , +but use C escape codes whenever possible. +.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). +.It Fl f +Output is not sorted. +.It Fl g +This option is deprecated and is only available for compatibility +with +.Bx 4.3 ; +it was used to display the group name in the long +.Pq Fl l +format output. +.It Fl i +For each file, print the file's file serial number (inode number). +.It Fl k +If the +.Fl s +option is specified, print the file size allocation in kilobytes, +not blocks. This option overrides the environment variable BLOCKSIZE. +.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 n +Display user and group IDs numerically rather than converting to a user +or group name in a long +.Pq Fl l +output. +.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. The environment variable +BLOCKSIZE overrides the unit size of 512 bytes. +.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 +The +.Fl B , +.Fl b +and +.Fl q +options all override each other; the last one specified determines +the format used for non-printable characters. +.Pp +The +.Fl H, +.Fl L +and +.Fl P +options all override each other (either partially or fully); they +are applied in the order specified. +.Pp +By default, +.Nm +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 modification time of the file is more than 6 months +in the past or future, then the year of the last modification +is displayed in place of the hour and minute fields. +.Pp +If the owner or group names are not a known user or group name, +or the +.Fl n +option is given, +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 +.Fl 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, writable, 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 +.Sh DIAGNOSTICS +The +.Nm +utility exits 0 on success, and >0 if an error occurs. +.Sh ENVIRONMENT +The following environment variables affect the execution of +.Nm Ns : +.Bl -tag -width BLOCKSIZE +.It Ev BLOCKSIZE +If the environment variable +.Ev BLOCKSIZE +is set, the block counts +(see +.Fl s ) +will be displayed in units of that size block. +.It Ev CLICOLOR +Use +.Tn ANSI +color sequences to distinguish file types. +See +.Ev LSCOLORS +below. +In addition to the file types mentioned in the +.Fl F +option some extra attributes (setuid bit set, etc.) are also displayed. +The colorization is dependent on a terminal type with the proper +.Xr termcap 5 +capabilities. +The default +.Dq cons25 +console has the proper capabilities, +however if you want to display the colors in an +.Xr xterm 1 +for example, +you need to set your +.Ev TERM +variable to +.Dq xterm-color . +Other terminal types may require similar adjustments. Colorization +is silently disabled if the output isn't directed to a terminal +unless the +.Ev CLICOLOR_FORCE +variable is defined. +.It Ev CLICOLOR_FORCE +Color sequences are normally disabled if the output isn't directed to +a terminal. This can be overridden by setting this flag. +The +.Ev TERM +variable still needs to reference a color capable terminal however +otherwise it is not possible to determine which color sequences to +use. +.It Ev 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 +utility calculates how +many pathname text columns to display +based on the width provided. +(See +.Fl C . ) +.It Ev LANG +The locale to use when determining the order of day and month in the long +.Fl l +format output. +See +.Xr environ 7 +for more information. +.It Ev LSCOLORS +The value of this variable describes what color to use for which +attribute when colors are enabled with +.Ev CLICOLOR. +This string is a concatenation of pairs of the format +.Sy fb , +where +.Sy f +is the foreground color and +.Sy b +is the background color. +.Pp +The color designators are as follows: +.Pp +.Bl -tag -width 4n -offset indent -compact +.It Sy 0 +black +.It Sy 1 +red +.It Sy 2 +green +.It Sy 3 +brown +.It Sy 4 +blue +.It Sy 5 +magenta +.It Sy 6 +cyan +.It Sy 7 +light grey +.It Sy x +default foreground or background +.El +.Pp +Note that the above are standard +.Tn ANSI +colors. +The actual display may differ +depending on the color capabilities of your terminal. +.Pp +The order of the attributes are as follows: +.Pp +.Bl -enum -offset indent -compact +.It +directory +.It +symbolic link +.It +socket +.It +pipe +.It +executable +.It +block special +.It +character special +.It +executable with setuid bit set +.It +executable with setgid bit set +.It +directory writable to others, with sticky bit +.It +directory writable to others, without sticky bit +.El +.Pp +The default is "4x5x2x3x1x464301060203", i.e. blue foreground and +default background for regular directories, black foreground and red +background for setuid executables, etc. +.It Ev LS_COLWIDTHS +If this variable is set, it is considered to be a +colon-delimited list of minimum column widths. Unreasonable +and insufficient widths are ignored (thus zero signifies +a dynamically sized column). Not all +columns have changeable widths. The fields are, +in order: inode, block count, number of links, user name, +group name, flags, file size, file name. +.It Ev TERM +The +.Ev CLICOLOR +functionality depends on a terminal type with color capabilities. +.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 chflags 1 , +.Xr chmod 1 , +.Xr xterm 1 , +.Xr termcap 5 , +.Xr symlink 7 , +.Xr sticky 8 +.Sh HISTORY +An +.Nm +command appeared in +.At v1 . +.Sh STANDARDS +The +.Nm +function is expected to be a superset of the +.St -p1003.2 +specification. +.Sh BUGS +To maintain backward compatibility, the relationships between the many +options is quite complex. diff --git a/bin/ls/ls.c b/bin/ls/ls.c new file mode 100644 index 0000000..9070205 --- /dev/null +++ b/bin/ls/ls.c @@ -0,0 +1,724 @@ +/* + * 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 const char copyright[] = +"@(#) Copyright (c) 1989, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)ls.c 8.5 (Berkeley) 4/2/94"; +#else +static const char rcsid[] = + "$FreeBSD$"; +#endif +#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 <limits.h> +#include <locale.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#ifdef COLORLS +#include <termcap.h> +#include <signal.h> +#endif + +#include "ls.h" +#include "extern.h" + +/* + * Upward approximation of the maximum number of characters needed to + * represent a value of integral type t as a string, excluding the + * NUL terminator, with provision for a sign. + */ +#define STRBUF_SIZEOF(t) (1 + CHAR_BIT * sizeof(t) / 3 + 1) + +static void display __P((FTSENT *, FTSENT *)); +static u_quad_t makenines __P((u_long)); +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_kblocks; /* print size in kilobytes */ +int f_listdir; /* list actual directory, not contents */ +int f_listdot; /* list files beginning with . */ +int f_longform; /* long listing format */ +int f_nonprint; /* show unprintables as ? */ +int f_nosort; /* don't sort output */ +int f_notabs; /* don't use tab-separated multi-col output */ +int f_numericonly; /* don't convert uid/gid to name */ +int f_octal; /* show unprintables as \xxx */ +int f_octal_escape; /* like f_octal but use C escapes if possible */ +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_timesort; /* sort by time vice name */ +int f_type; /* add type character for non-regular files */ +int f_whiteout; /* show whiteout entries */ +#ifdef COLORLS +int f_color; /* add type in color for non-regular files */ + +char *ansi_bgcol; /* ANSI sequence to set background colour */ +char *ansi_fgcol; /* ANSI sequence to set foreground colour */ +char *ansi_coloff; /* ANSI sequence to reset colours */ +#endif + +int rval; + +int +main(argc, argv) + int argc; + char *argv[]; +{ + static char dot[] = ".", *dotav[] = { dot, NULL }; + struct winsize win; + int ch, fts_options, notused; + char *p; + +#ifdef COLORLS + char termcapbuf[1024]; /* termcap definition buffer */ + char tcapbuf[512]; /* capability buffer */ + char *bp = tcapbuf; +#endif + + (void) setlocale(LC_ALL, ""); + + /* 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; + /* retrieve environment variable, in case of explicit -C */ + if ((p = getenv("COLUMNS"))) + termwidth = atoi(p); + } + + /* Root is -A automatically. */ + if (!getuid()) + f_listdot = 1; + + fts_options = FTS_PHYSICAL; + while ((ch = getopt(argc, argv, "1ABCFGHLPRTWabcdfgiklnoqrstu")) != -1) { + 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 'B': + f_nonprint = 0; + f_octal = 1; + f_octal_escape = 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 'H': + fts_options |= FTS_COMFOLLOW; + break; + case 'G': + setenv("CLICOLOR", "", 1); + break; + case 'L': + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + break; + case 'P': + fts_options &= ~FTS_COMFOLLOW; + fts_options &= ~FTS_LOGICAL; + fts_options |= FTS_PHYSICAL; + 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 'k': + f_kblocks = 1; + break; + case 'n': + f_numericonly = 1; + break; + case 'o': + f_flags = 1; + break; + case 'q': + f_nonprint = 1; + f_octal = 0; + f_octal_escape = 0; + 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; + case 'W': + f_whiteout = 1; + break; + case 'b': + f_nonprint = 0; + f_octal = 0; + f_octal_escape = 1; + break; + default: + case '?': + usage(); + } + } + argc -= optind; + argv += optind; + + /* Enabling of colours is conditional on the environment. */ + if (getenv("CLICOLOR") && + (isatty(STDOUT_FILENO) || getenv("CLICOLOR_FORCE"))) +#ifdef COLORLS + if (tgetent(termcapbuf, getenv("TERM")) == 1) { + ansi_fgcol = tgetstr("AF", &bp); + ansi_bgcol = tgetstr("AB", &bp); + + /* To switch colours off use 'op' if + * available, otherwise use 'oc', or + * don't do colours at all. */ + ansi_coloff = tgetstr("op", &bp); + if (!ansi_coloff) + ansi_coloff = tgetstr("oc", &bp); + if (ansi_fgcol && ansi_bgcol && ansi_coloff) + f_color = 1; + } +#else + (void)fprintf(stderr, "Color support not compiled in.\n"); +#endif /*COLORLS*/ + +#ifdef COLORLS + if (f_color) { + /* + * We can't put tabs and color sequences together: + * column number will be incremented incorrectly + * for "stty oxtabs" mode. + */ + f_notabs = 1; + (void) signal(SIGINT, colorquit); + (void) signal(SIGQUIT, colorquit); + parsecolors(getenv("LSCOLORS")); + } +#endif + + /* + * If not -F, -i, -l, -s or -t options, don't require stat + * information, unless in color mode in which case we do + * need this to determine which colors to display. + */ + if (!f_inode && !f_longform && !f_size && !f_timesort && !f_type +#ifdef COLORLS + && !f_color +#endif + ) + 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 -W, show whiteout entries + */ +#ifdef FTS_WHITEOUT + if (f_whiteout) + fts_options |= FTS_WHITEOUT; +#endif + + /* If -l or -s, figure out block size. */ + if (f_longform || f_size) { + if (f_kblocks) + blocksize = 2; + else { + (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(rval); +} + +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)); + rval = 1; + 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; + char *initmax; + int entries, needstats; + char *user, *group, *flags; + char buf[STRBUF_SIZEOF(u_quad_t) + 1]; + char ngroup[STRBUF_SIZEOF(uid_t) + 1]; + char nuser[STRBUF_SIZEOF(gid_t) + 1]; + + /* + * 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 signaled in traverse(). + */ + if (list == NULL) + return; + + needstats = f_inode || f_longform || f_size; + flen = 0; + btotal = 0; + initmax = getenv("LS_COLWIDTHS"); + /* Fields match -lios order. New ones should be added at the end. */ + if (initmax != NULL && *initmax != '\0') { + char *initmax2, *jinitmax; + int ninitmax; + + /* Fill-in "::" as "0:0:0" for the sake of scanf. */ + jinitmax = initmax2 = malloc(strlen(initmax) * 2 + 2); + if (jinitmax == NULL) + err(1, NULL); + if (*initmax == ':') + strcpy(initmax2, "0:"), initmax2 += 2; + else + *initmax2++ = *initmax, *initmax2 = '\0'; + for (initmax++; *initmax != '\0'; initmax++) { + if (initmax[-1] == ':' && initmax[0] == ':') { + *initmax2++ = '0'; + *initmax2++ = initmax[0]; + initmax2[1] = '\0'; + } else { + *initmax2++ = initmax[0]; + initmax2[1] = '\0'; + } + } + if (initmax2[-1] == ':') strcpy(initmax2, "0"); + + ninitmax = sscanf(jinitmax, + " %lu : %lu : %lu : %i : %i : %i : %qu : %lu ", + &maxinode, &maxblock, &maxnlink, &maxuser, + &maxgroup, &maxflags, &maxsize, &maxlen); + f_notabs = 1; + switch (ninitmax) { + case 0: maxinode = 0; + case 1: maxblock = 0; + case 2: maxnlink = 0; + case 3: maxuser = 0; + case 4: maxgroup = 0; + case 5: maxflags = 0; + case 6: maxsize = 0; + case 7: maxlen = 0; +#ifdef COLORLS + if (!f_color) +#endif + f_notabs = 0; + } + maxinode = makenines(maxinode); + maxblock = makenines(maxblock); + maxnlink = makenines(maxnlink); + maxsize = makenines(maxsize); + } else if (initmax == NULL || *initmax == '\0') + maxblock = maxinode = maxlen = maxnlink = + maxuser = maxgroup = maxflags = maxsize = 0; + bcfile = 0; + flags = NULL; + 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; + rval = 1; + 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 (cur->fts_namelen > maxlen) + maxlen = cur->fts_namelen; + if (f_octal || f_octal_escape) { + int t = len_octal(cur->fts_name, cur->fts_namelen); + if (t > maxlen) maxlen = t; + } + 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) { + if (f_numericonly) { + (void)snprintf(nuser, sizeof(nuser), + "%u", sp->st_uid); + (void)snprintf(ngroup, sizeof(ngroup), + "%u", sp->st_gid); + user = nuser; + group = ngroup; + } else { + user = user_from_uid(sp->st_uid, 0); + group = group_from_gid(sp->st_gid, 0); + } + if ((ulen = strlen(user)) > maxuser) + maxuser = ulen; + if ((glen = strlen(group)) > maxgroup) + maxgroup = glen; + if (f_flags) { + flags = fflagstostr(sp->st_flags); + if (flags != NULL && *flags == '\0') { + free(flags); + flags = strdup("-"); + } + if (flags == NULL) + err(1, NULL); + 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); + free(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 && + (*a)->fts_level == FTS_ROOTLEVEL && !f_listdir) { + if (a_info == FTS_D) + return (1); + if (b_info == FTS_D) + return (-1); + } + return (sortfcn(*a, *b)); +} + +/* + * Makenines() returns (10**n)-1. This is useful for converting a width + * into a number that wide in decimal. + */ +static u_quad_t +makenines(n) + u_long n; +{ + u_long i; + u_quad_t reg; + + reg = 1; + /* Use a loop instead of pow(), since all values of n are small. */ + for (i = 0; i < n; i++) + reg *= 10; + reg--; + + return reg; +} diff --git a/bin/ls/ls.h b/bin/ls/ls.h new file mode 100644 index 0000000..3f929e7 --- /dev/null +++ b/bin/ls/ls.h @@ -0,0 +1,81 @@ + +/* + * 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. + * + * from: @(#)ls.h 8.1 (Berkeley) 5/31/93 + * $FreeBSD$ + */ + +#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_octal; /* print unprintables in octal */ +extern int f_octal_escape; /* like f_octal but use C escapes if possible */ +extern int f_nonprint; /* show unprintables as ? */ +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_notabs; /* don't use tab-separated multi-col output */ +extern int f_type; /* add type character for non-regular files */ +#ifdef COLORLS +extern int f_color; /* add type in color for non-regular files */ +#endif + +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..a407283 --- /dev/null +++ b/bin/ls/print.c @@ -0,0 +1,512 @@ +/* + * 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 +#if 0 +static char sccsid[] = "@(#)print.c 8.4 (Berkeley) 4/17/94"; +#else +static const char rcsid[] = + "$FreeBSD$"; +#endif +#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 <unistd.h> +#ifdef COLORLS +#include <ctype.h> +#include <termcap.h> +#include <signal.h> +#endif + +#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)); +#ifdef COLORLS +static void endcolor __P((int)); +static int colortype __P((mode_t)); +#endif + +#define IS_NOPRINT(p) ((p)->fts_number == NO_PRINT) + +#ifdef COLORLS +/* Most of these are taken from <sys/stat.h> */ +typedef enum Colors { + C_DIR, /* directory */ + C_LNK, /* symbolic link */ + C_SOCK, /* socket */ + C_FIFO, /* pipe */ + C_EXEC, /* executable */ + C_BLK, /* block special */ + C_CHR, /* character special */ + C_SUID, /* setuid executable */ + C_SGID, /* setgid executable */ + C_WSDIR, /* directory writeble to others, with sticky bit */ + C_WDIR, /* directory writeble to others, without sticky bit */ + C_NUMCOLORS /* just a place-holder */ +} Colors ; + +char *defcolors = "4x5x2x3x1x464301060203"; + +static int colors[C_NUMCOLORS][2]; +#endif + +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'); + } +} + +/* + * print name in current style + */ +static int +printname(name) + const char *name; +{ + if (f_octal || f_octal_escape) + return prn_octal(name); + else if (f_nonprint) + return prn_printable(name); + else + return printf("%s", name); +} + +void +printlong(dp) + DISPLAY *dp; +{ + struct stat *sp; + FTSENT *p; + NAMES *np; + char buf[20]; +#ifdef COLORLS + int color_printed = 0; +#endif + + 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, (u_long)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)) + if (minor(sp->st_rdev) > 255 || minor(sp->st_rdev) < 0) + (void)printf("%3d, 0x%08x ", + major(sp->st_rdev), + (u_int)minor(sp->st_rdev)); + else + (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); +#ifdef COLORLS + if (f_color) + color_printed = colortype(sp->st_mode); +#endif + (void)printname(p->fts_name); +#ifdef COLORLS + if (f_color && color_printed) + endcolor(0); +#endif + if (f_type) + (void)printtype(sp->st_mode); + if (S_ISLNK(sp->st_mode)) + printlink(p); + (void)putchar('\n'); + } +} + +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; + int tabwidth; + + if (f_notabs) + tabwidth = 1; + else + tabwidth = 8; + + /* + * 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 + tabwidth) & ~(tabwidth - 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 + tabwidth) & ~(tabwidth - 1))) + <= endcol){ + (void)putchar(f_notabs ? ' ' : '\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; +#ifdef COLORLS + int color_printed = 0; +#endif + + sp = p->fts_statp; + chcnt = 0; + if (f_inode) + chcnt += printf("%*lu ", (int)inodefield, (u_long)sp->st_ino); + if (f_size) + chcnt += printf("%*qd ", + (int)sizefield, howmany(sp->st_blocks, blocksize)); +#ifdef COLORLS + if (f_color) + color_printed = colortype(sp->st_mode); +#endif + chcnt += printname(p->fts_name); +#ifdef COLORLS + if (f_color && color_printed) + endcolor(0); +#endif + if (f_type) + chcnt += printtype(sp->st_mode); + return (chcnt); +} + +static void +printtime(ftime) + time_t ftime; +{ + char longstring[80]; + static time_t now; + const char *format; + + if (now == 0) + now = time(NULL); + +#define SIXMONTHS ((365 / 2) * 86400) + /* "%Ef" is a FreeBSD strftime definition for "%e %b" or "%b %e". + * Actually format is locale sensitive. + */ + if (f_sectime) + /* mmm dd hh:mm:ss yyyy || dd mmm hh:mm:ss yyyy */ + format = "%Ef %T %Y "; + else if (ftime + SIXMONTHS > now && ftime < now + SIXMONTHS) + /* mmm dd hh:mm || dd mmm hh:mm */ + format = "%Ef %R "; + else + /* mmm dd yyyy || dd mmm yyyy */ + format = "%Ef %Y "; + strftime(longstring, sizeof(longstring), format, localtime(&ftime)); + fputs(longstring, stdout); +} + +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); + case S_IFWHT: + (void)putchar('%'); + return (1); + } + if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { + (void)putchar('*'); + return (1); + } + return (0); +} + +#ifdef COLORLS +static int +putch(c) + int c; +{ + (void) putchar(c); + return 0; +} + +static int +writech(c) + int c; +{ + char tmp = c; + + (void) write(STDOUT_FILENO, &tmp, 1); + return 0; +} + +static void +printcolor(c) + Colors c; +{ + char *ansiseq; + + if (colors[c][0] != -1) { + ansiseq = tgoto(ansi_fgcol, 0, colors[c][0]); + if (ansiseq) + tputs(ansiseq, 1, putch); + } + + if (colors[c][1] != -1) { + ansiseq = tgoto(ansi_bgcol, 0, colors[c][1]); + if (ansiseq) + tputs(ansiseq, 1, putch); + } +} + +static void +endcolor(sig) + int sig; +{ + tputs(ansi_coloff, 1, sig ? writech : putch); +} + +static int +colortype(mode) + mode_t mode; +{ + switch(mode & S_IFMT) { + case S_IFDIR: + if (mode & S_IWOTH) + if (mode & S_ISTXT) + printcolor(C_WSDIR); + else + printcolor(C_WDIR); + else + printcolor(C_DIR); + return(1); + case S_IFLNK: + printcolor(C_LNK); + return(1); + case S_IFSOCK: + printcolor(C_SOCK); + return(1); + case S_IFIFO: + printcolor(C_FIFO); + return(1); + case S_IFBLK: + printcolor(C_BLK); + return(1); + case S_IFCHR: + printcolor(C_CHR); + return(1); + } + if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { + if (mode & S_ISUID) + printcolor(C_SUID); + else if (mode & S_ISGID) + printcolor(C_SGID); + else + printcolor(C_EXEC); + return(1); + } + return(0); +} + +void +parsecolors(cs) +char *cs; +{ + int i, j, len; + char c[2]; + + if (cs == NULL) cs = ""; /* LSCOLORS not set */ + len = strlen(cs); + for (i = 0 ; i < C_NUMCOLORS ; i++) { + if (len <= 2*i) { + c[0] = defcolors[2*i]; + c[1] = defcolors[2*i+1]; + } + else { + c[0] = cs[2*i]; + c[1] = cs[2*i+1]; + } + for (j = 0 ; j < 2 ; j++) { + if ((c[j] < '0' || c[j] > '7') && + tolower((unsigned char)c[j]) != 'x') { + fprintf(stderr, + "error: invalid character '%c' in LSCOLORS env var\n", + c[j]); + c[j] = defcolors[2*i+j]; + } + if (tolower((unsigned char)c[j]) == 'x') + colors[i][j] = -1; + else + colors[i][j] = c[j]-'0'; + } + } +} + +void +colorquit(sig) + int sig; +{ + endcolor(sig); + + (void) signal(sig, SIG_DFL); + (void) kill(getpid(), sig); +} +#endif /*COLORLS*/ + +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(" -> "); + printname(path); +} diff --git a/bin/ls/util.c b/bin/ls/util.c new file mode 100644 index 0000000..56339c8 --- /dev/null +++ b/bin/ls/util.c @@ -0,0 +1,171 @@ +/* + * 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 +#if 0 +static char sccsid[] = "@(#)util.c 8.3 (Berkeley) 4/2/94"; +#else +static const char rcsid[] = + "$FreeBSD$"; +#endif +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <err.h> +#include <fts.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "ls.h" +#include "extern.h" + +int +prn_printable(s) + const char *s; +{ + unsigned char c; + int n; + + for (n = 0; (c = *s) != '\0'; ++s, ++n) + if (isprint(c)) + putchar(c); + else + putchar('?'); + return n; +} + +/* + * The fts system makes it difficult to replace fts_name with a different- + * sized string, so we just calculate the real length here and do the + * conversion in prn_octal() + * + * XXX when using f_octal_escape (-b) rather than f_octal (-B), the + * length computed by len_octal may be too big. I just can't be buggered + * to fix this as an efficient fix would involve a lookup table. Same goes + * for the rather inelegant code in prn_octal. + * + * DES 1998/04/23 + */ + +int +len_octal(s, len) + const char *s; + int len; +{ + int r = 0; + + while (len--) + if (isprint((unsigned char)*s++)) r++; else r += 4; + return r; +} + +int +prn_octal(s) + const char *s; +{ + unsigned char ch; + int len = 0; + + while ((ch = *s++)) + { + if (isprint(ch) && (ch != '\"') && (ch != '\\')) + putchar(ch), len++; + else if (f_octal_escape) { + putchar('\\'); + switch (ch) { + case '\\': + putchar('\\'); + break; + case '\"': + putchar('"'); + break; + case '\a': + putchar('a'); + break; + case '\b': + putchar('b'); + break; + case '\f': + putchar('f'); + break; + case '\n': + putchar('n'); + break; + case '\r': + putchar('r'); + break; + case '\t': + putchar('t'); + break; + case '\v': + putchar('v'); + break; + default: + putchar('0' + (ch >> 6)); + putchar('0' + ((ch >> 3) & 7)); + putchar('0' + (ch & 7)); + len += 2; + break; + } + len += 2; + } + else { + putchar('\\'); + putchar('0' + (ch >> 6)); + putchar('0' + ((ch >> 3) & 7)); + putchar('0' + (ch & 7)); + len += 4; + } + } + return len; +} + +void +usage() +{ + (void)fprintf(stderr, +#ifdef COLORLS + "usage: ls [-ABCFGHLPRTWabcdfgiklnoqrstu1]" +#else + "usage: ls [-ABCFHLPRTWabcdfgiklnoqrstu1]" +#endif + " [file ...]\n"); + exit(1); +} diff --git a/bin/mkdir/Makefile b/bin/mkdir/Makefile new file mode 100644 index 0000000..fadc6b2 --- /dev/null +++ b/bin/mkdir/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 +# $FreeBSD$ + +PROG= mkdir + +.include <bsd.prog.mk> diff --git a/bin/mkdir/mkdir.1 b/bin/mkdir/mkdir.1 new file mode 100644 index 0000000..759382e --- /dev/null +++ b/bin/mkdir/mkdir.1 @@ -0,0 +1,105 @@ +.\" 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 +.\" $FreeBSD$ +.\" +.Dd January 25, 1994 +.Dt MKDIR 1 +.Os +.Sh NAME +.Nm mkdir +.Nd make directories +.Sh SYNOPSIS +.Nm mkdir +.Op Fl pv +.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. +.It Fl v +Be verbose when creating directories, listing them as they are created. +.El +.Pp +The user must have write permission in the parent directory. +.Sh DIAGNOSTICS +The +.Nm +utility exits 0 on success, and >0 if an error occurs. +.Sh SEE ALSO +.Xr rmdir 1 +.Sh COMPATIBILITY +The +.Fl v +option is non-standard and its use in scripts is not recommended. +.Sh STANDARDS +The +.Nm +utility is expected to be +.St -p1003.2 +compatible. +.Sh HISTORY +A +.Nm +command appeared in +.At v1 . diff --git a/bin/mkdir/mkdir.c b/bin/mkdir/mkdir.c new file mode 100644 index 0000000..6c61abf --- /dev/null +++ b/bin/mkdir/mkdir.c @@ -0,0 +1,209 @@ +/* + * 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 const copyright[] = +"@(#) Copyright (c) 1983, 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)mkdir.c 8.2 (Berkeley) 1/25/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#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 <sysexits.h> +#include <unistd.h> + +int build __P((char *, mode_t)); +void usage __P((void)); + +int vflag; + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch, exitval, success, omode, pflag; + mode_t *set = (mode_t *)NULL; + char *mode; + + omode = pflag = 0; + mode = NULL; + while ((ch = getopt(argc, argv, "m:pv")) != -1) + switch(ch) { + case 'm': + mode = optarg; + break; + case 'p': + pflag = 1; + break; + case 'v': + vflag = 1; + break; + case '?': + default: + usage(); + } + + argc -= optind; + argv += optind; + if (argv[0] == NULL) + usage(); + + if (mode == NULL) { + omode = S_IRWXU | S_IRWXG | S_IRWXO; + } else { + if ((set = setmode(mode)) == NULL) + errx(1, "invalid file mode: %s", mode); + omode = getmode(set, S_IRWXU | S_IRWXG | S_IRWXO); + free(set); + } + + for (exitval = 0; *argv != NULL; ++argv) { + success = 1; + if (pflag) { + if (build(*argv, omode)) + success = 0; + } else if (mkdir(*argv, omode) < 0) { + warn("%s", *argv); + success = 0; + } else if (vflag) + (void)printf("%s\n", *argv); + + if (!success) + exitval = 1; + /* + * The mkdir() and umask() calls both honor only the low + * nine bits, so if you try to set a mode including the + * sticky, setuid, setgid bits you lose them. Don't do + * this unless the user has specifically requested a mode, + * as chmod will (obviously) ignore the umask. + */ + if (success && mode != NULL && chmod(*argv, omode) == -1) { + warn("%s", *argv); + exitval = 1; + } + } + exit(exitval); +} + +int +build(path, omode) + char *path; + mode_t omode; +{ + struct stat sb; + mode_t numask, oumask; + int first, last, retval; + char *p; + + p = path; + oumask = 0; + retval = 0; + if (p[0] == '/') /* Skip leading '/'. */ + ++p; + for (first = 1, last = 0; !last ; ++p) { + if (p[0] == '\0') + last = 1; + else if (p[0] != '/') + continue; + *p = '\0'; + if (p[1] == '\0') + last = 1; + 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 (last) + (void)umask(oumask); + if (stat(path, &sb)) { + if (errno != ENOENT || + mkdir(path, last ? omode : + S_IRWXU | S_IRWXG | S_IRWXO) < 0) { + warn("%s", path); + retval = 1; + break; + } else if (vflag) + printf("%s\n", path); + } + else if ((sb.st_mode & S_IFMT) != S_IFDIR) { + if (last) + errno = EEXIST; + else + errno = ENOTDIR; + warn("%s", path); + retval = 1; + break; + } + if (!last) + *p = '/'; + } + if (!first && !last) + (void)umask(oumask); + return (retval); +} + +void +usage() +{ + + (void)fprintf(stderr, "usage: mkdir [-pv] [-m mode] directory ...\n"); + exit (EX_USAGE); +} diff --git a/bin/mv/Makefile b/bin/mv/Makefile new file mode 100644 index 0000000..8405782 --- /dev/null +++ b/bin/mv/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.2 (Berkeley) 4/2/94 +# $FreeBSD$ + +PROG= mv + +.include <bsd.prog.mk> diff --git a/bin/mv/mv.1 b/bin/mv/mv.1 new file mode 100644 index 0000000..528eeaa --- /dev/null +++ b/bin/mv/mv.1 @@ -0,0 +1,153 @@ +.\" 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 +.\" $FreeBSD$ +.\" +.Dd May 31, 1993 +.Dt MV 1 +.Os +.Sh NAME +.Nm mv +.Nd move files +.Sh SYNOPSIS +.Nm mv +.Op Fl f | Fl i +.Op Fl v +.Ar source target +.Nm mv +.Op Fl f | Fl i +.Op Fl v +.Ar source ... directory +.Sh DESCRIPTION +In its first form, the +.Nm +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 +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 f +option overrides any previous +.Fl i +options.) +.It Fl i +Cause +.Nm +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 +.Sq Li y +or +.Sq Li Y , +the move is attempted. +(The +.Fl i +option overrides any previous +.Fl f +options.) +.It Fl v +Cause +.Nm +to be verbose, showing files after they are moved. +.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 +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 +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 -pRP source_file destination && \e +\trm -rf source_file +.Ed +.Sh DIAGNOSTICS +The +.Nm +utility exits 0 on success, and >0 if an error occurs. +.Sh SEE ALSO +.Xr cp 1 , +.Xr rm 1 , +.Xr symlink 7 +.Sh COMPATIBILITY +The +.Fl v +option is non-standard and its use in scripts is not recommended. +.Sh STANDARDS +The +.Nm +utility is expected to be +.St -p1003.2 +compatible. +.Sh HISTORY +A +.Nm +command appeared in +.At v1 . diff --git a/bin/mv/mv.c b/bin/mv/mv.c new file mode 100644 index 0000000..fe7a2ed --- /dev/null +++ b/bin/mv/mv.c @@ -0,0 +1,378 @@ +/* + * 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 const copyright[] = +"@(#) Copyright (c) 1989, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)mv.c 8.2 (Berkeley) 4/2/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/wait.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 <sysexits.h> +#include <unistd.h> + +#include "pathnames.h" + +int fflg, iflg, vflg; + +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]; + + while ((ch = getopt(argc, argv, "fiv")) != -1) + switch (ch) { + case 'i': + iflg = 1; + fflg = 0; + break; + case 'f': + fflg = 1; + iflg = 0; + break; + case 'v': + vflg = 1; + break; + default: + usage(); + } + 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. */ + if (strlen(argv[argc - 1]) > sizeof(path) - 1) + errx(1, "%s: destination pathname too long", *argv); + (void)strcpy(path, argv[argc - 1]); + baselen = strlen(path); + endp = &path[baselen]; + if (!baselen || *(endp - 1) != '/') { + *endp++ = '/'; + ++baselen; + } + for (rval = 0; --argc; ++argv) { + /* + * Find the last component of the source pathname. It + * may have trailing slashes. + */ + p = *argv + strlen(*argv); + while (p != *argv && p[-1] == '/') + --p; + while (p != *argv && p[-1] != '/') + --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, first; + 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)) { + + /* prompt only if source exist */ + if (lstat(from, &sb) == -1) { + warn("%s", from); + return (1); + } + +#define YESNO "(y/n [n]) " + ask = 0; + if (iflg) { + (void)fprintf(stderr, "overwrite %s? %s", to, YESNO); + 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? %s", + modep + 1, modep[9] == ' ' ? "" : " ", + user_from_uid(sb.st_uid, 0), + group_from_gid(sb.st_gid, 0), to, YESNO); + ask = 1; + } + if (ask) { + first = ch = getchar(); + while (ch != '\n' && ch != EOF) + ch = getchar(); + if (first != 'y' && first != 'Y') { + (void)fprintf(stderr, "not overwritten\n"); + return (0); + } + } + } + if (!rename(from, to)) { + if (vflg) + printf("%s -> %s\n", from, to); + return (0); + } + + if (errno == EXDEV) { + struct statfs sfs; + char path[MAXPATHLEN]; + + /* Can't mv(1) a mount point. */ + if (realpath(from, path) == NULL) { + warnx("cannot resolve %s: %s", from, path); + return (1); + } + if (!statfs(path, &sfs) && !strcmp(path, sfs.f_mntonname)) { + warnx("cannot rename a mount point"); + return (1); + } + } else { + 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 (lstat(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; + mode_t oldmode; + register int nread, from_fd, to_fd; + + if ((from_fd = open(from, O_RDONLY, 0)) < 0) { + warn("%s", from); + return (1); + } + if (blen < sbp->st_blksize) { + if (bp != NULL) + free(bp); + if ((bp = malloc(sbp->st_blksize)) == NULL) { + blen = 0; + warnx("malloc failed"); + return (1); + } + blen = sbp->st_blksize; + } + while ((to_fd = + open(to, O_CREAT | O_EXCL | O_TRUNC | O_WRONLY, 0)) < 0) { + if (errno == EEXIST && unlink(to) == 0) + continue; + warn("%s", to); + (void)close(from_fd); + 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); + + oldmode = sbp->st_mode & ALLPERMS; + if (fchown(to_fd, sbp->st_uid, sbp->st_gid)) { + warn("%s: set owner/group (was: %lu/%lu)", to, + (u_long)sbp->st_uid, (u_long)sbp->st_gid); + if (oldmode & (S_ISUID | S_ISGID)) { + warnx( +"%s: owner/group changed; clearing suid/sgid (mode was 0%03o)", + to, oldmode); + sbp->st_mode &= ~(S_ISUID | S_ISGID); + } + } + if (fchmod(to_fd, sbp->st_mode)) + warn("%s: set mode (was: 0%03o)", to, oldmode); + /* + * XXX + * NFS doesn't support chflags; ignore errors unless there's reason + * to believe we're losing bits. (Note, this still won't be right + * if the server supports flags and we were trying to *remove* flags + * on a file that we copied, i.e., that we didn't create.) + */ + errno = 0; + if (fchflags(to_fd, sbp->st_flags)) + if (errno != EOPNOTSUPP || sbp->st_flags != 0) + warn("%s: set flags (was: 0%07o)", to, sbp->st_flags); + + 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); + } + if (vflg) + printf("%s -> %s\n", from, to); + return (0); +} + +int +copy(from, to) + char *from, *to; +{ + int pid, status; + + if ((pid = fork()) == 0) { + execl(_PATH_CP, "mv", vflg ? "-PRpv" : "-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, "%s\n%s\n", + "usage: mv [-f | -i] [-v] source target", + " mv [-f | -i] [-v] source ... directory"); + exit(EX_USAGE); +} diff --git a/bin/mv/pathnames.h b/bin/mv/pathnames.h new file mode 100644 index 0000000..b9cd27f --- /dev/null +++ b/bin/mv/pathnames.h @@ -0,0 +1,38 @@ +/* + * 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 + * $FreeBSD$ + */ + +#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..b1a4dcf --- /dev/null +++ b/bin/pax/Makefile @@ -0,0 +1,33 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 +# $FreeBSD$ + +# 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..b286aa6 --- /dev/null +++ b/bin/pax/ar_io.c @@ -0,0 +1,1289 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)ar_io.c 8.2 (Berkeley) 4/18/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/mtio.h> +#include <signal.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdio.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>" /* pseudo name for stdout */ +#define STDN "<STDIN>" /* pseudo 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) + sys_warn(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) + sys_warn(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) + sys_warn(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) { + sys_warn(0, errno, "Failed stat on %s", arcname); + (void)close(arfd); + arfd = -1; + can_unlnk = 0; + return(-1); + } + if (S_ISDIR(arsb.st_mode)) { + pax_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 cannot 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)) { + sys_warn(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) { + pax_warn(1, "Cannot append to an archive obtained from a pipe."); + return(-1); + } + + if (!invld_rec) + return(0); + pax_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) + sys_warn(1, errno, "Failed read on archive volume %d", arvol); + else + pax_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) { + pax_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 boundary. 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)) { + pax_warn(1,"Unable to append, trailer re-write failed. Quitting."); + return(res); + } + + if (res == 0) + pax_warn(0, "End of archive volume %d reached", arvol); + else if (res < 0) + sys_warn(1, errno, "Failed write to archive volume: %d", arvol); + else if (!frmt->blkalgn || ((res % frmt->blkalgn) == 0)) + pax_warn(0,"WARNING: partial archive write. Archive MAY BE FLAWED"); + else + pax_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)) { + pax_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 forward 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) { + pax_warn(1, "Unable to recover from an archive read failure."); + return(-1); + } + pax_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); + } + sys_warn(1, errno, "Forward 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 + */ + pax_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) { + sys_warn(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 + */ + pax_warn(1,"Reverse position on previous volume."); + lstrval = -1; + return(-1); + } + cpos = (off_t)0L; + } + if (lseek(arfd, cpos, SEEK_SET) < 0) { + sys_warn(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) { + pax_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) { + sys_warn(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) { + sys_warn(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) { + sys_warn(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) { + sys_warn(1, errno, "Unable to backspace over last tape block."); + return(-1); + } + if ((phyblk = read(arfd, scbuf, sizeof(scbuf))) <= 0) { + sys_warn(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) { + sys_warn(1, errno, "Unable to locate tape filemark."); + return(-1); + } + mb.mt_op = MTBSF; + mb.mt_count = 1; + if (ioctl(arfd, MTIOCTOP, &mb) < 0) { + sys_warn(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) { + pax_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) { + sys_warn(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) + sys_warn(0, errno, "Unable to set signal mask"); + ar_close(); + if (sigprocmask(SIG_SETMASK, &o_mask, (sigset_t *)NULL) < 0) + sys_warn(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; + pax_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..151fe2a --- /dev/null +++ b/bin/pax/ar_subs.c @@ -0,0 +1,1240 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)ar_subs.c 8.2 (Berkeley) 4/18/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <signal.h> +#include <string.h> +#include <stdio.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 existing 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) { + sys_warn(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 boundary; 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 format. + */ + if (get_arc() < 0) + return; + if ((orgfrmt != NULL) && (orgfrmt != frmt)) { + pax_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 position 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, sizeof(dirbuf) - 1); + dest_pt = dirbuf + dlen; + if (*(dest_pt-1) != '/') { + *dest_pt++ = '/'; + ++dlen; + } + *dest_pt = '\0'; + drem = PAXPATHLEN - dlen; + + if (stat(dirptr, &sb) < 0) { + sys_warn(1, errno, "Cannot access destination directory %s", + dirptr); + return; + } + if (!S_ISDIR(sb.st_mode)) { + pax_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) { + pax_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) { + sys_warn(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)) { + pax_warn(1,"Premature end of file on archive read"); + return(-1); + } + if (!in_resync) { + if (act == APPND) { + pax_warn(1, + "Archive I/O error, cannot continue"); + return(-1); + } + pax_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) { + pax_warn(1,"Unable to append, archive header flaw"); + return(-1); + } + pax_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); + pax_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); + pax_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 + */ + pax_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..50c3076 --- /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 +#if 0 +static char sccsid[] = "@(#)buf_subs.c 8.2 (Berkeley) 4/18/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.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) { + pax_warn(1, "Write block size of %d too large, maximum is: %d", + wrblksz, MAXBLK); + return(-1); + } + if (wrblksz % BLKMULT) { + pax_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) { + pax_warn(1,"Write block size %d too large, maximum is: %d", + wrblksz, MAXBLK); + return(-1); + } + if (wrblksz % BLKMULT) { + pax_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) { + pax_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: + pax_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) { + pax_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)) + pax_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 forward 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 pax_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) + sys_warn(1, errno, "Read fault on %s", arcn->org_name); + else if (size != 0L) + pax_warn(1, "File changed size during read %s", arcn->org_name); + else if (fstat(ifd, &sb) < 0) + sys_warn(1, errno, "Failed stat on %s", arcn->org_name); + else if (arcn->sb.st_mtime != sb.st_mtime) + pax_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 + sys_warn(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 forward 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)) + pax_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 + sys_warn(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) + sys_warn(1, errno, "Failed write during copy of %s to %s", + arcn->org_name, arcn->name); + else if (cpcnt != arcn->sb.st_size) + pax_warn(1, "File %s changed size during copy to %s", + arcn->org_name, arcn->name); + else if (fstat(fd1, &sb) < 0) + sys_warn(1, errno, "Failed stat of %s", arcn->org_name); + else if (arcn->sb.st_mtime != sb.st_mtime) + pax_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 forward 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)) { + pax_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 pax_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..7f3b37f --- /dev/null +++ b/bin/pax/cache.c @@ -0,0 +1,480 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)cache.c 8.1 (Berkeley) 5/31/93"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <string.h> +#include <stdio.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; + pax_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; + pax_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; + pax_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; + pax_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 numeric 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", (u_long)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 - 1); + 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 numeric 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", (u_long)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 - 1); + 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 - 1); + 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 - 1); + 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..6420588 --- /dev/null +++ b/bin/pax/cache.h @@ -0,0 +1,75 @@ +/*- + * 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 + * $FreeBSD$ + */ + +/* + * 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 address 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..626a77d --- /dev/null +++ b/bin/pax/cpio.c @@ -0,0 +1,1280 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)cpio.c 8.1 (Berkeley) 5/31/93"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <string.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))) { + pax_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')) { + pax_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 + pax_warn(1, "Cpio link name length is invalid: %lu", + arcn->sb.st_size); +# else + pax_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) { + pax_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') { + pax_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 + pax_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)) { + pax_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) { + pax_warn(1,"Unable to write cpio link name for %s",arcn->org_name); + return(-1); + } + return(1); + + out: + /* + * header field is out of range + */ + pax_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 + pax_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)) { + pax_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)) { + pax_warn(1,"Could not write sv4cpio link name for %s", + arcn->org_name); + return(-1); + } + return(1); + + out: + /* + * header field is out of range + */ + pax_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 boundaries + */ + 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) { + pax_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)) { + pax_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)) { + pax_warn(1,"Could not write bcpio link name for %s",arcn->org_name); + return(-1); + } + return(1); + + out: + /* + * header field is out of range + */ + pax_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..2b72f53 --- /dev/null +++ b/bin/pax/cpio.h @@ -0,0 +1,152 @@ +/*- + * 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 + * $FreeBSD$ + */ + +/* + * 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..2b062cb --- /dev/null +++ b/bin/pax/extern.h @@ -0,0 +1,286 @@ +/*- + * 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 + * $FreeBSD$ + */ + +/* + * 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 pax_warn __P((int, char *, ...)); +void sys_warn __P((int, int, char *, ...)); diff --git a/bin/pax/file_subs.c b/bin/pax/file_subs.c new file mode 100644 index 0000000..b8f2a49 --- /dev/null +++ b/bin/pax/file_subs.c @@ -0,0 +1,1057 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)file_subs.c 8.1 (Berkeley) 5/31/93"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <stdio.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 effort 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) { + sys_warn(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) + sys_warn(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) { + sys_warn(1,errno,"Unable to link to %s from %s", arcn->ln_name, + arcn->name); + return(-1); + } + + if (S_ISDIR(sb.st_mode)) { + pax_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 pax_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 original 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)) { + pax_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)) { + pax_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) { + sys_warn(1, errno, "Unable to remove %s", from); + return(-1); + } + } else if (unlink(from) < 0) { + if (!ign) { + sys_warn(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) { + sys_warn(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 + */ + pax_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 + */ + pax_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) { + sys_warn(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) { + sys_warn(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); + sys_warn(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) { + sys_warn(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 + sys_warn(0,errno,"Unable to obtain file stats %s", fnm); + } + + /* + * set the times + */ + if (utimes(fnm, tv) < 0) + sys_warn(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) { + sys_warn(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) + sys_warn(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) { + sys_warn(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) { + sys_warn(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) { + sys_warn(1, errno, "Failed seek on file %s", fname); + return; + } + + if (write(fd, blnk, 1) < 0) + sys_warn(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) + pax_warn(1, "File changed size %s", arcn->org_name); + else if (fstat(fd, &sb) < 0) + sys_warn(1, errno, "Failed stat on %s", arcn->org_name); + else if (arcn->sb.st_mtime != sb.st_mtime) + pax_warn(1, "File %s was modified during read", arcn->org_name); + else if (lseek(fd, (off_t)0L, SEEK_SET) < 0) + sys_warn(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..ba1e489 --- /dev/null +++ b/bin/pax/ftree.c @@ -0,0 +1,545 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)ftree.c 8.2 (Berkeley) 4/18/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <unistd.h> +#include <string.h> +#include <stdio.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; /* current 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 + pax_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)) { + pax_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')) { + pax_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) { + pax_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) { + pax_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 cannot 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 + */ + pax_warn(1,"File system cycle found at %s",ftent->fts_path); + continue; + case FTS_DNR: +# ifdef NET2_FTS + sys_warn(1, errno, +# else + sys_warn(1, ftent->fts_errno, +# endif + "Unable to read directory %s", ftent->fts_path); + continue; + case FTS_ERR: +# ifdef NET2_FTS + sys_warn(1, errno, +# else + sys_warn(1, ftent->fts_errno, +# endif + "File system traversal error"); + continue; + case FTS_NS: + case FTS_NSOK: +# ifdef NET2_FTS + sys_warn(1, errno, +# else + sys_warn(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 - 1)) < 0) { + sys_warn(1, errno, "Unable to read symlink %s", + ftent->fts_path); + continue; + } + /* + * set link name length, watch out readlink does not + * always NUL 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..e5c9749 --- /dev/null +++ b/bin/pax/ftree.h @@ -0,0 +1,51 @@ +/*- + * 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 + * $FreeBSD$ + */ + +/* + * 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..83cfdc5 --- /dev/null +++ b/bin/pax/gen_subs.c @@ -0,0 +1,489 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)gen_subs.c 8.1 (Berkeley) 5/31/93"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <stdio.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 ((365 / 2) * 86400) +#define CURFRMT "%Ef %H:%M" +#define OLDFRMT "%Ef %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), + MINOR(sbp->st_rdev)); +# else + (void)printf("%4lu,%4lu ", (unsigned long)MAJOR(sbp->st_rdev), + (unsigned long)MINOR(sbp->st_rdev)); +# endif + 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..ec274d1 --- /dev/null +++ b/bin/pax/options.c @@ -0,0 +1,1139 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)options.c 8.2 (Berkeley) 4/18/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mtio.h> +#include <stdio.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; + + /* + * process option flags + */ + while ((c=getopt(argc,argv,"ab:cdf:iklno:p:rs:tuvwx:B:DE:G:HLPT:U:XYZ")) + != -1) { + switch (c) { + case 'a': + /* + * append + */ + flg |= AF; + break; + case 'b': + /* + * specify blocksize + */ + flg |= BF; + if ((wrblksz = (int)str_offt(optarg)) <= 0) { + pax_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: + pax_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; + } + pax_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) { + pax_warn(1, "Invalid write limit %s", optarg); + pax_usage(); + } + if (wrlimit % BLKMULT) { + pax_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) { + pax_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) { + pax_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) { + pax_warn(1,"blocksize must be specified with 'b'"); + tar_usage(); + } + if ((wrblksz = (int)str_offt(*argv)) <= 0) { + pax_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) { + pax_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 + */ + pax_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 + * multiple -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')) { + pax_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) { + pax_warn(0, "Invalid options format"); + return(-1); + } + if ((opt = (OPLIST *)malloc(sizeof(OPLIST))) == NULL) { + pax_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..b1de2b6d --- /dev/null +++ b/bin/pax/options.h @@ -0,0 +1,114 @@ +/*- + * 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 + * $FreeBSD$ + */ + +/* + * 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..e68a0eb --- /dev/null +++ b/bin/pax/pat_rep.c @@ -0,0 +1,1199 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)pat_rep.c 8.2 (Berkeley) 4/18/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.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')) { + pax_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) { + pax_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) { + pax_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)); + pax_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); + pax_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; + pax_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')) { + pax_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) { + pax_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) { + pax_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) { + pax_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.... + */ + pax_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); + arcn->name[PAXPATHLEN] = '\0'; + 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) { + pax_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) + pax_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 - pm[0].rm_so; +# 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) + pax_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); + name[PAXPATHLEN] = '\0'; + } + 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..e0d812b --- /dev/null +++ b/bin/pax/pat_rep.h @@ -0,0 +1,55 @@ +/*- + * 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 + * $FreeBSD$ + */ + +/* + * 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..43311b9 --- /dev/null +++ b/bin/pax/pax.1 @@ -0,0 +1,1173 @@ +.\" 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 +.\" $FreeBSD$ +.\" +.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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +to be the same as they were before being read or accessed by +.Nm Ns . +.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 +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 +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 +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 +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 +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 +to stop operation after the first read error is detected on an archive volume. +A +.Ar limit +of +.Li NONE +will cause +.Nm +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 +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 +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 +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 -v -f filename +gives the verbose table of contents for an archive stored in +.Pa filename . +.Pp +The following commands: +.Dl mkdir /tmp/foo +.Dl cd /tmp/bar +.Dl pax -rw .\ /tmp/foo +will copy the entire +.Pa /tmp/bar +directory hierarchy to +.Pa /tmp/foo . +.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 +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 AUTHORS +.An Keith Muller +at the University of California, San Diego +.Sh DIAGNOSTICS +.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 +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 +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 +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 +may have only partially created the archive which may violate the specific +archive format specification. +.Pp +If while doing a +.Em copy , +.Nm +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 +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..7bcc81e --- /dev/null +++ b/bin/pax/pax.c @@ -0,0 +1,410 @@ +/*- + * 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 const copyright[] = +"@(#) Copyright (c) 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)pax.c 8.2 (Berkeley) 4/18/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <errno.h> +#include <locale.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.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 existing 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 written by pax are forced to strictly conform to both the + * archive and pax the specific 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 existing file before + * name modification (-D) + * 3.10 File inode change time can be checked against existing file after + * name modification (-Y) + * 3.11 File modification time can be checked against existing 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 +{ + (void) setlocale(LC_ALL, ""); + /* + * 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) + pax_warn(0, "Cpu time limit reached, cleaning up."); + else + pax_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)) { + pax_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: + sys_warn(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..a87b359 --- /dev/null +++ b/bin/pax/pax.h @@ -0,0 +1,239 @@ +/*- + * 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 + * $FreeBSD$ + */ + +/* + * 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 /* maximum 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 specific 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 boundary */ + 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 end 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) major(x) +#define MINOR(x) minor(x) +#define TODEV(x, y) makedev((x), (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..ba7b71e --- /dev/null +++ b/bin/pax/sel_subs.c @@ -0,0 +1,659 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)sel_subs.c 8.1 (Berkeley) 5/31/93"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <pwd.h> +#include <grp.h> +#include <stdio.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 specified 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 escape 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)) { + pax_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) { + pax_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); + } + pax_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)) { + pax_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) { + pax_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); + } + pax_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')) { + pax_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; + } + pax_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) { + pax_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: + pax_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) { + pax_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) { + pax_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) { + pax_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: + pax_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..ab24ab0 --- /dev/null +++ b/bin/pax/sel_subs.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. + * + * @(#)sel_subs.h 8.1 (Berkeley) 5/31/93 + * $FreeBSD$ + */ + +/* + * 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..e6c106b --- /dev/null +++ b/bin/pax/tables.c @@ -0,0 +1,1430 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)tables.c 8.1 (Berkeley) 5/31/93"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.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) { + pax_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); + arcn->ln_name[PAXPATHLEN] = '\0'; + 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); + } + + pax_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) { + pax_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) { + sys_warn(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) { + sys_warn(1, errno, + "Failed ftime table seek"); + return(-1); + } + if (read(ffd, ckname, namelen) != namelen) { + sys_warn(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); + } + sys_warn(1, errno, "Failed write to file time table"); + } else + sys_warn(1, errno, "Failed seek on file time table"); + } else + pax_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 + * assigns 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) { + pax_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 + */ + pax_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) { + pax_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); + } + pax_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); + oname[PAXPATHLEN] = '\0'; + 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) { + pax_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 already 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) { + pax_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: + pax_warn(1, "Unable to fix truncated inode/device field when storing %s", + arcn->name); + pax_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) { + pax_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); + } + + pax_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); + } + pax_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) { + pax_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; + } + + pax_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) + pax_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..74c91f7 --- /dev/null +++ b/bin/pax/tables.h @@ -0,0 +1,173 @@ +/*- + * 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 + * $FreeBSD$ + */ + +/* + * 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; /* location 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..f5415f4 --- /dev/null +++ b/bin/pax/tar.c @@ -0,0 +1,1193 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)tar.c 8.2 (Berkeley) 4/18/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <string.h> +#include <stdio.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 sum 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; /* initial 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)) { + pax_warn(1, "Unknown tar format -o option/value pair %s=%s", + opt->name, opt->value); + pax_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)) { + pax_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 calculate 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: + pax_warn(1, "Tar cannot archive a character device %s", + arcn->org_name); + return(1); + case PAX_BLK: + pax_warn(1, "Tar cannot archive a block device %s", arcn->org_name); + return(1); + case PAX_SCK: + pax_warn(1, "Tar cannot archive a socket %s", arcn->org_name); + return(1); + case PAX_FIF: + pax_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)) { + pax_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)) { + pax_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 + pax_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 + */ + pax_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) { + pax_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))){ + pax_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) { + pax_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 + pax_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 + */ + pax_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)) + return(NULL); + + /* + * we start looking at the biggest sized piece that fits in the name + * field. We walk forward 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) + */ + start = name + len - TNMSZ; + 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..83df0da --- /dev/null +++ b/bin/pax/tar.h @@ -0,0 +1,149 @@ +/*- + * 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 + * $FreeBSD$ + */ + +/* + * 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 checksum 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..d70bffa --- /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 +#if 0 +static char sccsid[] = "@(#)tty_subs.c 8.2 (Berkeley) 4/18/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdio.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 terminal (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) { + pax_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); +} + +/* + * pax_warn() + * write a pax_warning message to stderr. if "set" the exit value of pax + * will be non-zero. + */ + +#if __STDC__ +void +pax_warn(int set, char *fmt, ...) +#else +void +pax_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); +} + +/* + * sys_warn() + * write a pax_warning message to stderr. if "set" the exit value of pax + * will be non-zero. + */ + +#if __STDC__ +void +sys_warn(int set, int errnum, char *fmt, ...) +#else +void +sys_warn(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..d5c791c --- /dev/null +++ b/bin/ps/Makefile @@ -0,0 +1,18 @@ +# $FreeBSD$ +# @(#)Makefile 8.1 (Berkeley) 6/2/93 + +PROG= ps +SRCS= fmt.c keyword.c nlist.c print.c ps.c +# +# To support "lazy" ps for non root/wheel users +# add -DLAZY_PS to the cflags. This helps +# keep ps from being an unnecessary load +# on large systems. +# +CFLAGS+=-I${.CURDIR}/../../sys -DLAZY_PS +DPADD= ${LIBM} ${LIBKVM} +LDADD= -lm -lkvm +#BINGRP= kmem +#BINMODE=2555 + +.include <bsd.prog.mk> diff --git a/bin/ps/extern.h b/bin/ps/extern.h new file mode 100644 index 0000000..a14390d --- /dev/null +++ b/bin/ps/extern.h @@ -0,0 +1,86 @@ +/*- + * 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 + * $FreeBSD$ + */ + +struct kinfo; +struct nlist; +struct var; +struct varent; + +extern fixpt_t ccpu; +extern int eval, fscale, mempages, nlistread, rawcpu, cflag; +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 rtprior __P((KINFO *, VARENT *)); +void printheader __P((void)); +void pvar __P((KINFO *, VARENT *)); +void rssize __P((KINFO *, VARENT *)); +void runame __P((KINFO *, VARENT *)); +int s_runame __P((KINFO *)); +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 *)); +void tsize __P((KINFO *, VARENT *)); +void ucomm __P((KINFO *, VARENT *)); +void uname __P((KINFO *, VARENT *)); +int s_uname __P((KINFO *)); +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..9c0f8d3 --- /dev/null +++ b/bin/ps/fmt.c @@ -0,0 +1,131 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)fmt.c 8.4 (Berkeley) 4/15/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.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; +{ + long arg_max; + char **p, *dst, *src; + static char *buf = NULL; + + if (buf == NULL) { + if ((arg_max = sysconf(_SC_ARG_MAX)) == -1) + errx(1, "sysconf _SC_ARG_MAX failed"); + if ((buf = malloc((4 * arg_max) + 1)) == NULL) + errx(1, "malloc failed"); + } + + 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++ = ' '; + } + /* Chop off trailing space */ + if (dst != buf) + 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..cbabd97 --- /dev/null +++ b/bin/ps/keyword.c @@ -0,0 +1,283 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)keyword.c 8.5 (Berkeley) 4/2/94"; +#else +static const char rcsid[] = + "$FreeBSD$"; +#endif +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/proc.h> +#include <sys/sysctl.h> +#include <sys/ucred.h> +#include <sys/user.h> + +#include <err.h> +#include <errno.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <utmp.h> + +#include "ps.h" + +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, NULL, 4}, + {{"stime"}, "STIME", USER, stime, NULL, 4}, + {{"ixrss"}, "IXRSS", USER, ixrss, NULL, 4}, + {{"idrss"}, "IDRSS", USER, idrss, NULL, 4}, + {{"isrss"}, "ISRSS", USER, isrss, NULL, 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 UT_NAMESIZE + +VAR var[] = { + {"%cpu", "%CPU", NULL, 0, pcpu, NULL, 4}, + {"%mem", "%MEM", NULL, 0, pmem, NULL, 4}, + {"acflag", "ACFLG", + NULL, 0, pvar, NULL, 3, POFF(p_acflag), USHORT, "x"}, + {"acflg", "", "acflag"}, + {"blocked", "", "sigmask"}, + {"caught", "", "sigcatch"}, + {"command", "COMMAND", NULL, COMM|LJUST|USER, command, NULL, 16}, + {"cpu", "CPU", NULL, 0, pvar, NULL, 3, POFF(p_estcpu), UINT, "d"}, + {"cputime", "", "time"}, + {"f", "F", NULL, 0, pvar, NULL, 7, POFF(p_flag), INT, "x"}, + {"flags", "", "f"}, + {"ignored", "", "sigignore"}, + {"inblk", "INBLK", + NULL, USER, rvar, NULL, 4, ROFF(ru_inblock), LONG, "ld"}, + {"inblock", "", "inblk"}, + {"jobc", "JOBC", NULL, 0, evar, NULL, 4, EOFF(e_jobc), SHORT, "d"}, + {"ktrace", "KTRACE", + NULL, 0, pvar, NULL, 8, POFF(p_traceflag), INT, "x"}, + {"ktracep", "KTRACEP", + NULL, 0, pvar, NULL, 8, POFF(p_tracep), LONG, "lx"}, + {"lim", "LIM", NULL, 0, maxrss, NULL, 5}, + {"login", "LOGIN", NULL, LJUST, logname, NULL, MAXLOGNAME-1}, + {"logname", "", "login"}, + {"lstart", "STARTED", NULL, LJUST|USER, lstarted, NULL, 28}, + {"majflt", "MAJFLT", + NULL, USER, rvar, NULL, 4, ROFF(ru_majflt), LONG, "ld"}, + {"minflt", "MINFLT", + NULL, USER, rvar, NULL, 4, ROFF(ru_minflt), LONG, "ld"}, + {"msgrcv", "MSGRCV", + NULL, USER, rvar, NULL, 4, ROFF(ru_msgrcv), LONG, "ld"}, + {"msgsnd", "MSGSND", + NULL, USER, rvar, NULL, 4, ROFF(ru_msgsnd), LONG, "ld"}, + {"ni", "", "nice"}, + {"nice", "NI", NULL, 0, pvar, NULL, 2, POFF(p_nice), CHAR, "d"}, + {"nivcsw", "NIVCSW", + NULL, USER, rvar, NULL, 5, ROFF(ru_nivcsw), LONG, "ld"}, + {"nsignals", "", "nsigs"}, + {"nsigs", "NSIGS", + NULL, USER, rvar, NULL, 4, ROFF(ru_nsignals), LONG, "ld"}, + {"nswap", "NSWAP", + NULL, USER, rvar, NULL, 4, ROFF(ru_nswap), LONG, "ld"}, + {"nvcsw", "NVCSW", + NULL, USER, rvar, NULL, 5, ROFF(ru_nvcsw), LONG, "ld"}, + {"nwchan", "WCHAN", NULL, 0, pvar, NULL, 6, POFF(p_wchan), KPTR, "lx"}, + {"oublk", "OUBLK", + NULL, USER, rvar, NULL, 4, ROFF(ru_oublock), LONG, "ld"}, + {"oublock", "", "oublk"}, + {"p_ru", "P_RU", NULL, 0, pvar, NULL, 6, POFF(p_ru), KPTR, "lx"}, + {"paddr", "PADDR", NULL, 0, evar, NULL, 6, EOFF(e_paddr), KPTR, "lx"}, + {"pagein", "PAGEIN", NULL, USER, pagein, NULL, 6}, + {"pcpu", "", "%cpu"}, + {"pending", "", "sig"}, + {"pgid", "PGID", + NULL, 0, evar, NULL, PIDLEN, EOFF(e_pgid), UINT, PIDFMT}, + {"pid", "PID", NULL, 0, pvar, NULL, PIDLEN, POFF(p_pid), UINT, PIDFMT}, + {"pmem", "", "%mem"}, + {"ppid", "PPID", + NULL, 0, evar, NULL, PIDLEN, EOFF(e_ppid), UINT, PIDFMT}, + {"pri", "PRI", NULL, 0, pri, NULL, 3}, + {"re", "RE", NULL, 0, pvar, NULL, 3, POFF(p_swtime), UINT, "d"}, + {"rgid", "RGID", NULL, 0, evar, NULL, UIDLEN, EOFF(e_pcred.p_rgid), + UINT, UIDFMT}, + {"rlink", "RLINK", + NULL, 0, pvar, NULL, 8, POFF(p_procq.tqe_prev), KPTR, "lx"}, + {"rss", "RSS", NULL, 0, p_rssize, NULL, 4}, + {"rssize", "", "rsz"}, + {"rsz", "RSZ", NULL, 0, rssize, NULL, 4}, + {"rtprio", "RTPRIO", NULL, 0, rtprior, NULL, 7, POFF(p_rtprio)}, + {"ruid", "RUID", NULL, 0, evar, NULL, UIDLEN, EOFF(e_pcred.p_ruid), + UINT, UIDFMT}, + {"ruser", "RUSER", NULL, LJUST|DSIZ, runame, s_runame, USERLEN}, + {"sess", "SESS", NULL, 0, evar, NULL, 6, EOFF(e_sess), KPTR, "lx"}, + {"sig", "PENDING", NULL, 0, pvar, NULL, 8, POFF(p_siglist), INT, "x"}, + {"sigcatch", "CAUGHT", + NULL, 0, evar, NULL, 8, EOFF(e_procsig.ps_sigcatch), UINT, "x"}, + {"sigignore", "IGNORED", + NULL, 0, evar, NULL, 8, EOFF(e_procsig.ps_sigignore), UINT, "x"}, + {"sigmask", "BLOCKED", + NULL, 0, pvar, NULL, 8, POFF(p_sigmask), UINT, "x"}, + {"sl", "SL", NULL, 0, pvar, NULL, 3, POFF(p_slptime), UINT, "d"}, + {"start", "STARTED", NULL, LJUST|USER, started, NULL, 7}, + {"stat", "", "state"}, + {"state", "STAT", NULL, 0, state, NULL, 4}, + {"svgid", "SVGID", NULL, 0, + evar, NULL, UIDLEN, EOFF(e_pcred.p_svgid), UINT, UIDFMT}, + {"svuid", "SVUID", NULL, 0, + evar, NULL, UIDLEN, EOFF(e_pcred.p_svuid), UINT, UIDFMT}, + {"tdev", "TDEV", NULL, 0, tdev, NULL, 4}, + {"time", "TIME", NULL, USER, cputime, NULL, 9}, + {"tpgid", "TPGID", + NULL, 0, evar, NULL, 4, EOFF(e_tpgid), UINT, PIDFMT}, + {"tsess", "TSESS", NULL, 0, evar, NULL, 6, EOFF(e_tsess), KPTR, "lx"}, + {"tsiz", "TSIZ", NULL, 0, tsize, NULL, 4}, + {"tt", "TT ", NULL, 0, tname, NULL, 4}, + {"tty", "TTY", NULL, LJUST, longtname, NULL, 8}, + {"ucomm", "UCOMM", NULL, LJUST, ucomm, NULL, MAXCOMLEN}, + {"uid", "UID", NULL, 0, evar, NULL, UIDLEN, EOFF(e_ucred.cr_uid), + UINT, UIDFMT}, + {"upr", "UPR", NULL, 0, pvar, NULL, 3, POFF(p_usrpri), CHAR, "d"}, + {"user", "USER", NULL, LJUST|DSIZ, uname, s_uname, USERLEN}, + {"usrpri", "", "upr"}, + {"vsize", "", "vsz"}, + {"vsz", "VSZ", NULL, 0, vsize, NULL, 5}, + {"wchan", "WCHAN", NULL, LJUST, wchan, NULL, 6}, + {"xstat", "XSTAT", NULL, 0, pvar, NULL, 4, POFF(p_xstat), USHORT, "x"}, + {""}, +}; + +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(); + + 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; + } else 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..d950e1b --- /dev/null +++ b/bin/ps/nlist.c @@ -0,0 +1,60 @@ +/*- + * 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. + */ + +#include <sys/types.h> +#include <sys/sysctl.h> + +#include <stddef.h> + +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 */ + +int +donlist() +{ + size_t oldlen; + + oldlen = sizeof(ccpu); + if (sysctlbyname("kern.ccpu", &ccpu, &oldlen, NULL, 0) < 0) + return (1); + oldlen = sizeof(fscale); + if (sysctlbyname("kern.fscale", &fscale, &oldlen, NULL, 0) < 0) + return (1); + oldlen = sizeof(mempages); + if (sysctlbyname("hw.availpages", &mempages, &oldlen, NULL, 0) < 0) + return (1); + nlistread = 1; + return (0); +} diff --git a/bin/ps/print.c b/bin/ps/print.c new file mode 100644 index 0000000..600a639 --- /dev/null +++ b/bin/ps/print.c @@ -0,0 +1,734 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)print.c 8.6 (Berkeley) 4/16/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/proc.h> +#include <sys/stat.h> + +#include <sys/ucred.h> +#include <sys/user.h> +#include <sys/sysctl.h> +#include <vm/vm.h> + +#include <err.h> +#include <math.h> +#include <nlist.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <vis.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; + + v = ve->var; + + if (cflag) { + if (ve->next == NULL) /* last field, don't pad */ + (void)printf("%s", KI_PROC(k)->p_comm); + else + (void)printf("%-*s", v->width, KI_PROC(k)->p_comm); + return; + } + + 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; + + 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; + char *s; + + v = ve->var; + (void)printf("%-*s", v->width, (s = KI_EPROC(k)->e_login, *s) ? s : "-"); +} + +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) /* interruptable (long) */ + *cp = p->p_slptime >= MAXSLP ? 'I' : 'S'; + else + *cp = 'D'; + break; + + case SRUN: + case SIDL: + *cp = 'R'; + break; + + case SWAIT: + *cp = 'W'; + break; + + case SMTX: + *cp = 'M'; + break; + + case SZOMB: + *cp = 'Z'; + break; + + default: + *cp = '?'; + } + cp++; + if (!(flag & P_INMEM)) + *cp++ = 'W'; + if (p->p_nice < NZERO) + *cp++ = '<'; + else if (p->p_nice > NZERO) + *cp++ = 'N'; + if (flag & P_TRACED) + *cp++ = 'X'; + if (flag & P_WEXIT && p->p_stat != SZOMB) + *cp++ = 'E'; + if (flag & P_PPWAIT) + *cp++ = 'V'; + if ((flag & P_SYSTEM) || p->p_lock > 0) + *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++ = '+'; + if (flag & P_JAILED) + *cp++ = 'J'; + *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; + (void)printf("%-*s", + (int)v->width, user_from_uid(KI_EPROC(k)->e_ucred.cr_uid, 0)); +} + +int +s_uname(k) + KINFO *k; +{ + return (strlen(user_from_uid(KI_EPROC(k)->e_ucred.cr_uid, 0))); +} + +void +runame(k, ve) + KINFO *k; + VARENT *ve; +{ + VAR *v; + + v = ve->var; + (void)printf("%-*s", + (int)v->width, user_from_uid(KI_EPROC(k)->e_pcred.p_ruid, 0)); +} + +int +s_runame(k) + KINFO *k; +{ + return (strlen(user_from_uid(KI_EPROC(k)->e_pcred.p_ruid, 0))); +} + +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-1, "??"); + else { + if (strncmp(ttname, "tty", 3) == 0 || + strncmp(ttname, "cua", 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; + time_t then; + struct tm *tp; + char buf[100]; + + v = ve->var; + if (!k->ki_u.u_valid) { + (void)printf("%-*s", v->width, "-"); + return; + } + + then = k->ki_u.u_start.tv_sec; + tp = localtime(&then); + if (!now) + (void)time(&now); + if (now - k->ki_u.u_start.tv_sec < 24 * 3600) { + (void)strftime(buf, sizeof(buf) - 1, "%l:%M%p", tp); + } else if (now - k->ki_u.u_start.tv_sec < 7 * 86400) { + (void)strftime(buf, sizeof(buf) - 1, "%a%I%p", 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; + time_t then; + char buf[100]; + + v = ve->var; + if (!k->ki_u.u_valid) { + (void)printf("%-*s", v->width, "-"); + return; + } + then = k->ki_u.u_start.tv_sec; + (void)strftime(buf, sizeof(buf) -1, "%c", localtime(&then)); + (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("%-*lx", v->width, + (long)KI_PROC(k)->p_wchan &~ KERNBASE); + } else + (void)printf("%-*s", v->width, "-"); +} + +#ifndef pgtok +#define pgtok(a) (((a)*getpagesize())/1024) +#endif + +void +vsize(k, ve) + KINFO *k; + VARENT *ve; +{ + VAR *v; + + v = ve->var; + (void)printf("%*d", v->width, + (KI_EPROC(k)->e_vm.vm_map.size/1024)); +} + +void +rssize(k, ve) + KINFO *k; + VARENT *ve; +{ + VAR *v; + + v = ve->var; + /* XXX don't have info about shared */ + (void)printf("%*lu", v->width, + (u_long)pgtok(KI_EPROC(k)->e_vm.vm_rssize)); +} + +void +p_rssize(k, ve) /* doesn't account for text */ + KINFO *k; + VARENT *ve; +{ + VAR *v; + + v = ve->var; + (void)printf("%*ld", v->width, (long)pgtok(KI_EPROC(k)->e_vm.vm_rssize)); +} + +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_runtime / 1000000; + psecs = KI_PROC(k)->p_runtime % 1000000; + 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); + /* 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)/mempages; + 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("%*ld", 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; + /* XXX not yet */ + (void)printf("%*s", v->width, "-"); +} + +void +tsize(k, ve) + KINFO *k; + VARENT *ve; +{ + VAR *v; + + v = ve->var; + (void)printf("%*ld", v->width, (long)pgtok(KI_EPROC(k)->e_vm.vm_tsize)); +} + +void +rtprior(k, ve) + KINFO *k; + VARENT *ve; +{ + VAR *v; + struct rtprio *prtp; + char str[8]; + unsigned prio, type; + + v = ve->var; + prtp = (struct rtprio *) ((char *)KI_PROC(k) + v->off); + prio = prtp->prio; + type = prtp->type; + switch (type) { + case RTP_PRIO_REALTIME: + snprintf(str, sizeof(str), "real:%u", prio); + break; + case RTP_PRIO_NORMAL: + strncpy(str, "normal", sizeof(str)); + break; + case RTP_PRIO_IDLE: + snprintf(str, sizeof(str), "idle:%u", prio); + break; + default: + snprintf(str, sizeof(str), "%u:%u", type, prio); + break; + } + str[sizeof(str) - 1] = '\0'; + (void)printf("%*s", v->width, str); +} + +/* + * 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 INT: + (void)printf(ofmt, v->width, *(int *)bp); + break; + case UINT: + (void)printf(ofmt, v->width, *(u_int *)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..5510c42 --- /dev/null +++ b/bin/ps/ps.1 @@ -0,0 +1,513 @@ +.\" 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 +.\" $FreeBSD$ +.\" +.Dd April 18, 1994 +.Dt PS 1 +.Os BSD 4 +.Sh NAME +.Nm ps +.Nd process status +.Sh SYNOPSIS +.Nm ps +.Op Fl aCcefhjlmrSTuvwx +.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 +.Oo Fl U Ar username Ns +.Op , Ns Ar username Ns No ... +.Oc +.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 controlling terminal, then 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 process file system (see +.Xr procfs 5 ) +should be mounted when +.Nm +is executed, otherwise not all information will be available. +.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 ``command'' column output to just contain the executable name, +rather than the full command line. +.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 f +Show commandline and environment information about swapped out processes. +This option is honored only if the uid of the user is 0. +.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 +.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 +.Pa /kernel . +.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 the processes belonging to the specified +.Tn username Ns No (s). +.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 +.Pa /dev/drum . +.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 +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 associated with the process as in +the include file +.Aq Pa sys/proc.h : +.Bl -column P_NOCLDSTOP P_NOCLDSTOP +.It Dv "P_ADVLOCK" Ta No "0x00001 Process may hold a POSIX advisory lock" +.It Dv "P_CONTROLT" Ta No "0x00002 Has a controlling terminal" +.It Dv "P_INMEM" Ta No "0x00004 Loaded into memory" +.It Dv "P_NOCLDSTOP" Ta No "0x00008 No SIGCHLD when children stop" +.It Dv "P_PPWAIT" Ta No "0x00010 Parent is waiting for child to exec/exit" +.It Dv "P_PROFIL" Ta No "0x00020 Has started profiling" +.It Dv "P_SELECT" Ta No "0x00040 Selecting; wakeup/waiting danger" +.It Dv "P_SINTR" Ta No "0x00080 Sleep is interruptible" +.It Dv "P_SUGID" Ta No "0x00100 Had set id privileges since last exec" +.It Dv "P_SYSTEM" Ta No "0x00200 System proc: no sigs, stats or swapping" +.It Dv "P_TIMEOUT" Ta No "0x00400 Timing out during sleep" +.It Dv "P_TRACED" Ta No "0x00800 Debugged process being traced" +.It Dv "P_WAITED" Ta No "0x01000 Debugging process has waited for child" +.It Dv "P_WEXIT" Ta No "0x02000 Working on exiting" +.It Dv "P_EXEC" Ta No "0x04000 Process called exec" +.It Dv "P_OWEUPC" Ta No "0x20000 Owe process an addupc() call at next ast" +.It Dv "P_SWAPPING" Ta No "0x40000 Process is being swapped" +.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 J +Marks a process which is in +.Xr jail 2 . +The hostname of the prison can be found in +.Ql Li /proc/<pid>/status . +.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 MADV_RANDOM , +from +.Xr madvise 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 MADV_SEQUENTIAL , +from +.Xr madvise 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 three letters following +.Pa /dev/tty , +or, for the console, ``con''. +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 rtprio +realtime priority (101 = not a realtime process) +.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/db/kvm_kernel.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/db/kvm_kernel.db +system namelist database +.It Pa /kernel +default system namelist +.It Pa /proc +the mount point of +.Xr procfs 5 +.El +.Sh SEE ALSO +.Xr kill 1 , +.Xr w 1 , +.Xr kvm 3 , +.Xr strftime 3 , +.Xr procfs 5 , +.Xr pstat 8 +.Sh BUGS +Since +.Nm +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..9f513b2 --- /dev/null +++ b/bin/ps/ps.c @@ -0,0 +1,645 @@ +/*- + * 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 const copyright[] = +"@(#) Copyright (c) 1990, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)ps.c 8.4 (Berkeley) 4/2/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/user.h> +#include <sys/time.h> +#include <sys/resource.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 <limits.h> +#include <nlist.h> +#include <paths.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <locale.h> +#include <pwd.h> +#include <utmp.h> + +#include "ps.h" + +#define SEP ", \t" /* username separators */ + +KINFO *kinfo; +struct varent *vhead, *vtail; + +int eval; /* exit value */ +int cflag; /* -c */ +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; +#if defined(LAZY_PS) +static int forceuread=0; +#else +static int forceuread=1; +#endif + +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 dynsizevars __P((KINFO *)); +static void sizevars __P((void)); +static void usage __P((void)); +static uid_t *getuids(const char *, int *); + +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 *uids; + int all, ch, flag, i, fmt, lineno, nentries, dropgid; + int prtheader, wflag, what, xflg, uid, nuids; + char *nlistf, *memf, *swapf, errbuf[_POSIX2_LINE_MAX]; + + (void) setlocale(LC_ALL, ""); + + 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; + nuids = 0; + uids = NULL; + ttydev = NODEV; + dropgid = 0; + memf = nlistf = swapf = _PATH_DEVNULL; + while ((ch = getopt(argc, argv, +#if defined(LAZY_PS) + "aCcefghjLlM:mN:O:o:p:rSTt:U:uvW:wx")) != -1) +#else + "aCceghjLlM:mN:O:o:p:rSTt:U:uvW:wx")) != -1) +#endif + switch((char)ch) { + case 'a': + all = 1; + break; + case 'C': + rawcpu = 1; + break; + case 'c': + cflag = 1; + break; + case 'e': /* XXX set ufmt */ + needenv = 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; + dropgid = 1; + break; + case 'm': + sortby = SORTMEM; + break; + case 'N': + nlistf = optarg; + dropgid = 1; + break; + case 'O': + parsefmt(o1); + parsefmt(optarg); + parsefmt(o2); + o1[0] = o2[0] = '\0'; + fmt = 1; + break; + case 'o': + parsefmt(optarg); + fmt = 1; + break; +#if defined(LAZY_PS) + case 'f': + if (getuid() == 0 || getgid() == 0) + forceuread = 1; + break; +#endif + 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': + uids = getuids(optarg, &nuids); + xflg++; /* XXX: intuitive? */ + 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; + dropgid = 1; + 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 (dropgid) { + setgid(getgid()); + setuid(getuid()); + } + + kd = kvm_openfiles(nlistf, memf, swapf, O_RDONLY, errbuf); + if (kd == 0) + errx(1, "%s", errbuf); + + if (!fmt) + parsefmt(dfmt); + + /* XXX - should be cleaner */ + if (!all && ttydev == NODEV && pid == -1 && !nuids) { + if ((uids = malloc(sizeof (*uids))) == NULL) + errx(1, "malloc: %s", strerror(errno)); + nuids = 1; + *uids = getuid(); + } + + /* + * scan requested variables, noting what structures are needed, + * and adjusting header widths as appropriate. + */ + scanvars(); + /* + * get proc list + */ + if (nuids == 1) { + what = KERN_PROC_UID; + flag = *uids; + } 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]); + dynsizevars(&kinfo[i]); + } + + sizevars(); + + /* + * print header + */ + printheader(); + if (nentries == 0) + exit(1); + /* + * 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; + if (nuids > 1) { + for (uid = 0; uid < nuids; uid++) + if (KI_EPROC(&kinfo[i])->e_ucred.cr_uid == + uids[uid]) + break; + if (uid == nuids) + 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; + } + } + free(uids); + + exit(eval); +} + +uid_t * +getuids(const char *arg, int *nuids) +{ + char name[UT_NAMESIZE + 1]; + struct passwd *pwd; + uid_t *uids, *moreuids; + int l, alloc; + + + alloc = 0; + *nuids = 0; + uids = NULL; + for (; (l = strcspn(arg, SEP)) > 0; arg += l + strspn(arg + l, SEP)) { + if (l >= sizeof name) { + warnx("%.*s: name too long", l, arg); + continue; + } + strncpy(name, arg, l); + name[l] = '\0'; + if ((pwd = getpwnam(name)) == NULL) { + warnx("%s: no such user", name); + continue; + } + if (*nuids >= alloc) { + alloc = (alloc + 1) << 1; + moreuids = realloc(uids, alloc * sizeof (*uids)); + if (moreuids == NULL) { + free(uids); + errx(1, "realloc: %s", strerror(errno)); + } + uids = moreuids; + } + uids[(*nuids)++] = pwd->pw_uid; + } + endpwent(); + + if (!*nuids) + errx(1, "No users specified"); + + return uids; +} + +static void +scanvars() +{ + struct varent *vent; + VAR *v; + + for (vent = vhead; vent; vent = vent->next) { + v = vent->var; + if (v->flag & DSIZ) { + v->dwidth = v->width; + v->width = 0; + } + if (v->flag & USER) + needuser = 1; + if (v->flag & COMM) + needcomm = 1; + } +} + +static void +dynsizevars(ki) + KINFO *ki; +{ + struct varent *vent; + VAR *v; + int i; + + for (vent = vhead; vent; vent = vent->next) { + v = vent->var; + if (!(v->flag & DSIZ)) + continue; + i = (v->sproc)( ki); + if (v->width < i) + v->width = i; + if (v->width > v->dwidth) + v->width = v->dwidth; + } +} + +static void +sizevars() +{ + 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 */ + } + 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); +} + +#define UREADOK(ki) (forceuread || (KI_PROC(ki)->p_flag & P_INMEM)) + +static void +saveuser(ki) + KINFO *ki; +{ + struct usave *usp; + + usp = &ki->ki_u; + + if (KI_PROC(ki)->p_flag & P_INMEM) { + /* + * 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 = KI_EPROC(ki)->e_stats.p_start; + usp->u_ru = KI_EPROC(ki)->e_stats.p_ru; + usp->u_cru = KI_EPROC(ki)->e_stats.p_cru; + usp->u_valid = 1; + } else + usp->u_valid = 0; + /* + * save arguments if needed + */ + if (needcomm && (UREADOK(ki) || (KI_PROC(ki)->p_args != NULL))) { + ki->ki_args = fmt(kvm_getargv, ki, KI_PROC(ki)->p_comm, + MAXCOMLEN); + } else if (needcomm) { + ki->ki_args = malloc(strlen(KI_PROC(ki)->p_comm) + 3); + sprintf(ki->ki_args, "(%s)", KI_PROC(ki)->p_comm); + } else { + ki->ki_args = NULL; + } + if (needenv && UREADOK(ki)) { + ki->ki_env = fmt(kvm_getenvv, ki, (char *)NULL, 0); + } else if (needenv) { + ki->ki_env = malloc(3); + strcpy(ki->ki_env, "()"); + } else { + ki->ki_env = NULL; + } +} + +static int +pscomp(a, b) + const void *a, *b; +{ + int i; +#define VSIZE(k) (KI_EPROC(k)->e_vm.vm_dsize + KI_EPROC(k)->e_vm.vm_ssize + \ + KI_EPROC(k)->e_vm.vm_tsize) + + 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, "%s\n%s\n%s\n", + "usage: ps [-aChjlmrSTuvwx] [-O|o fmt] [-p pid] [-t tty] [-U user]", + " [-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..f8d244c --- /dev/null +++ b/bin/ps/ps.h @@ -0,0 +1,93 @@ +/*- + * 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 + * $FreeBSD$ + */ + +#define UNLIMITED 0 /* unlimited terminal width */ +enum type { CHAR, UCHAR, SHORT, USHORT, INT, UINT, 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 */ +#define DSIZ 0x08 /* field size is dynamic*/ + u_int flag; + /* output routine */ + void (*oproc) __P((struct kinfo *, struct varent *)); + /* sizing routine*/ + int (*sproc) __P((struct kinfo *)); + 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 */ + short dwidth; /* dynamic printing width */ + /* + * 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..4a8e66a --- /dev/null +++ b/bin/pwd/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 +# $FreeBSD$ + +PROG= pwd + +.include <bsd.prog.mk> diff --git a/bin/pwd/pwd.1 b/bin/pwd/pwd.1 new file mode 100644 index 0000000..455f791 --- /dev/null +++ b/bin/pwd/pwd.1 @@ -0,0 +1,81 @@ +.\" 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.2 (Berkeley) 4/28/95 +.\" $FreeBSD$ +.\" +.Dd April 28, 1995 +.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 +Some shells may provide a builtin +.Nm +command which is similar or identical to this utility. +Consult the +.Xr builtin 1 +manual page. +.Sh DIAGNOSTICS +The +.Nm +utility exits 0 on success, and >0 if an error occurs. +.Sh STANDARDS +The +.Nm +utility is expected to be +.St -p1003.2 +compatible. +.Sh SEE ALSO +.Xr builtin 1 , +.Xr cd 1 , +.Xr csh 1 , +.Xr getcwd 3 , +.Xr sh 1 +.Sh BUGS +In +.Xr csh 1 +the command +.Ic dirs +is always faster because it is built into that shell. +However, 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..1adb3c8 --- /dev/null +++ b/bin/pwd/pwd.c @@ -0,0 +1,96 @@ +/* + * 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 const copyright[] = +"@(#) Copyright (c) 1991, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)pwd.c 8.3 (Berkeley) 4/1/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#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")) != -1) + switch (ch) { + case 'P': + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 0) + usage(); + + if ((p = getcwd(NULL, 0)) == NULL) + err(1, "."); + (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..c3d2483 --- /dev/null +++ b/bin/rcp/Makefile @@ -0,0 +1,26 @@ +# @(#)Makefile 8.1 (Berkeley) 7/19/93 +# $FreeBSD$ + +PROG= rcp +SRCS= rcp.c util.c +CFLAGS+=-DBINDIR=${BINDIR} + +.if exists(${DESTDIR}${LIBDIR}/libkrb.a) && defined(MAKE_KERBEROS4) +SRCS+= krcmd.c kcmd.c rcmd_util.c +DPADD= ${LIBUTIL} ${LIBKRB} ${LIBCRYPTO} +CFLAGS+=-DKERBEROS -DCRYPT -DHAVE_CONFIG_H \ + -I${.CURDIR}/../../kerberosIV/include \ + -I${.CURDIR}/../../crypto/kerberosIV/include \ + -I${.CURDIR}/../../crypto/kerberosIV/lib/roken \ + -I${.CURDIR}/../../crypto/kerberosIV/appl/bsd \ + -I${.CURDIR} +LDADD= -lutil -lkrb -lcrypto +DISTRIBUTION= krb4 +.PATH: ${.CURDIR}/../../crypto/kerberosIV/appl/bsd +.endif + +BINOWN= root +BINMODE=4555 +INSTALLFLAGS=-fschg + +.include <bsd.prog.mk> diff --git a/bin/rcp/extern.h b/bin/rcp/extern.h new file mode 100644 index 0000000..5081c9d --- /dev/null +++ b/bin/rcp/extern.h @@ -0,0 +1,51 @@ +/*- + * 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 + * $FreeBSD$ + */ + +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..b1f0ec0 --- /dev/null +++ b/bin/rcp/pathnames.h @@ -0,0 +1,42 @@ +/* + * 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 + * $FreeBSD$ + */ + +#include <paths.h> + +#define _PATH_CP "/bin/cp" +#define _PATH_RCP "/bin/rcp" +#define _PATH_RLOGIN "/usr/bin/rlogin" +#define _PATH_RSH "/usr/bin/rsh" diff --git a/bin/rcp/rcp.1 b/bin/rcp/rcp.1 new file mode 100644 index 0000000..3034e20 --- /dev/null +++ b/bin/rcp/rcp.1 @@ -0,0 +1,163 @@ +.\" 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 +.\" $FreeBSD$ +.\" +.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 +.Dq rname@rhost:path , +or a local file name (containing no `:' characters, +or a `/' before any `:'s). +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl K +Turn off all Kerberos authentication. +.It Fl k +Request +.Nm +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 +Cause +.Nm +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 +copies each subtree rooted at that name; in this case +the destination must be a directory. +.It Fl x +Turn on +.Tn DES +encryption for all data passed by +.Nm Ns . +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 FILES +.Bl -tag -width /etc/auth.conf -compact +.It Pa /etc/auth.conf +configure authentication services +.El +.Sh SEE ALSO +.Xr cp 1 , +.Xr ftp 1 , +.Xr rlogin 1 , +.Xr rsh 1 , +.Xr auth.conf 5 , +.Xr hosts.equiv 5 . +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . +The version of +.Nm +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 +.Dq rhost.rname +when the destination machine is running the +.Bx 4.2 +version of +.Nm Ns . diff --git a/bin/rcp/rcp.c b/bin/rcp/rcp.c new file mode 100644 index 0000000..a1534ea --- /dev/null +++ b/bin/rcp/rcp.c @@ -0,0 +1,953 @@ +/* + * 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 const copyright[] = +"@(#) Copyright (c) 1983, 1990, 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)rcp.c 8.2 (Berkeley) 4/2/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#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 <libutil.h> + +#include "pathnames.h" +#include "extern.h" + +#ifdef KERBEROS +#include <openssl/des.h> +#include <krb.h> +#include "bsd_locl.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; + +static int argc_copy; +static char **argv_copy; + +#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, i, tflag; + char *targ, *shell; +#ifdef KERBEROS + char *k; +#endif + + /* + * Prepare for execing ourselves. + */ + argc_copy = argc + 1; + argv_copy = malloc((argc_copy + 1) * sizeof(*argv_copy)); + if (argv_copy == NULL) + err(1, "malloc"); + argv_copy[0] = argv[0]; + argv_copy[1] = "-K"; + for (i = 1; i < argc; ++i) { + argv_copy[i + 1] = strdup(argv[i]); + if (argv_copy[i + 1] == NULL) + errx(1, "strdup: out of memory"); + } + argv_copy[argc + 1] = NULL; + + fflag = tflag = 0; + while ((ch = getopt(argc, argv, OPTIONS)) != -1) + 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 - 1); + dst_realm_buf[REALM_SZ - 1] = '\0'; + 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 + k = auth_getval("auth_list"); + if (k && !strstr(k, "kerberos")) + use_kerberos = 0; + 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 ? " -PR" : "", 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", + (long)stb.st_mtimespec.tv_sec, + (long)stb.st_atimespec.tv_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", + (long)statp->st_mtimespec.tv_sec, + (long)statp->st_atimespec.tv_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, size; + int amt, count, exists, first, mask, mode, ofd, omode; + int setimes, targisdir, wrerrno = 0; + 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; + + cp = buf; + if (*cp == 'T') { + setimes++; + cp++; + mtime.tv_sec = strtol(cp, &cp, 10); + if (!cp || *cp++ != ' ') + SCREWUP("mtime.sec not delimited"); + mtime.tv_usec = strtol(cp, &cp, 10); + if (!cp || *cp++ != ' ') + SCREWUP("mtime.usec not delimited"); + atime.tv_sec = strtol(cp, &cp, 10); + if (!cp || *cp++ != ' ') + SCREWUP("atime.sec not delimited"); + atime.tv_usec = strtol(cp, &cp, 10); + if (!cp || *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; +{ + if (use_kerberos) { + setuid(getuid()); + 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) { + if (errno == ECONNREFUSED) + oldw("remote host doesn't support Kerberos"); + else if (errno == ENOENT) + oldw("can't provide Kerberos authentication data"); + execv(_PATH_RCP, argv_copy); + err(1, "execv: %s", _PATH_RCP); + } + } 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%s\n", + "usage: rcp [-Kpx] [-k realm] f1 f2", + " rcp [-Kprx] [-k realm] f1 ... fn directory"); +#else + (void)fprintf(stderr, "%s\n%s\n", + "usage: rcp [-Kp] [-k realm] f1 f2", + " rcp [-Kpr] [-k realm] f1 ... fn directory"); +#endif +#else + (void)fprintf(stderr, "%s\n%s\n", + "usage: rcp [-p] f1 f2", + " rcp [-pr] f1 ... fn directory"); +#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..a8ba4c0 --- /dev/null +++ b/bin/rcp/util.c @@ -0,0 +1,171 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)util.c 8.2 (Berkeley) 4/2/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#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; + 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..7b85cdf --- /dev/null +++ b/bin/rm/Makefile @@ -0,0 +1,10 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 +# $FreeBSD$ + +PROG= rm +SRCS= rm.c + +LINKS= ${BINDIR}/rm ${BINDIR}/unlink +MLINKS= rm.1 unlink.1 + +.include <bsd.prog.mk> diff --git a/bin/rm/rm.1 b/bin/rm/rm.1 new file mode 100644 index 0000000..a038392 --- /dev/null +++ b/bin/rm/rm.1 @@ -0,0 +1,218 @@ +.\" 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.5 (Berkeley) 12/5/94 +.\" $FreeBSD$ +.\" +.Dd January 28, 1999 +.Dt RM 1 +.Os +.Sh NAME +.Nm rm , +.Nm unlink +.Nd remove directory entries +.Sh SYNOPSIS +.Nm rm +.Op Fl dfiPRrvW +.Ar file ... +.Nm unlink +.Ar file +.Sh DESCRIPTION +The +.Nm +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 Fl +.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 . +.It Fl v +Be verbose when deleting files, showing them as they are removed. +.It Fl W +Attempt to undelete the named files. +Currently, this option can only be used to recover +files covered by whiteouts. +.El +.Pp +The +.Nm +utility removes symbolic links, not the files referenced by the links. +.Pp +It is an error to attempt to remove the files +.Dq \&. +or +.Dq .. . +.Pp +When the utility is called as +.Nm unlink , +only one argument, +which must not be a directory, +may be supplied. +No options may be supplied in this simple mode of operation, +which performs an +.Xr unlink 2 +operation on the passed argument. +.Pp +The +.Nm +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 +exits with a value >0. +.Sh NOTE +The +.Nm +command uses +.Xr getopt 3 +to parse its arguments, which allows it to accept +the +.Sq Li -- +option which will cause it to stop processing flag options at that +point. This will allow the removal of file names that begin +with a dash +.Pq Sq - . +For example: +.Dl rm -- -filename +The same behavior can be obtained by using an absolute or relative +path reference. For example: +.Dl rm /home/user/-filename +.Dl rm ./-filename +.Sh SEE ALSO +.Xr rmdir 1 , +.Xr undelete 2 , +.Xr unlink 2 , +.Xr fts 3 , +.Xr getopt 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 +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. +The +.Fl v +option is non-standard and its use in scripts is not recommended. +.Pp +Also, historical +.Bx +implementations prompted on the standard output, +not the standard error output. +.Sh STANDARDS +The +.Nm +command is almost +.St -p1003.2 +compatible, except that +.Tn POSIX +requires +.Nm rm +to act like +.Xr rmdir 1 +when the +.Ar file +specified is a directory. This implementation requires the +.Fl d +option if such behavior is desired. This follows the historical +behavior of +.Nm +with respect to directories. +.Pp +The simplified +.Nm unlink +command conforms to +.St -susv2 . +.Sh HISTORY +A +.Nm +command appeared in +.At v1 . diff --git a/bin/rm/rm.c b/bin/rm/rm.c new file mode 100644 index 0000000..7d01346 --- /dev/null +++ b/bin/rm/rm.c @@ -0,0 +1,494 @@ +/*- + * 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 const char copyright[] = +"@(#) Copyright (c) 1990, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)rm.c 8.5 (Berkeley) 4/18/94"; +#else +static const char rcsid[] = + "$FreeBSD$"; +#endif +#endif /* not lint */ + +#include <sys/stat.h> +#include <sys/param.h> +#include <sys/mount.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <fts.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> + +int dflag, eval, fflag, iflag, Pflag, vflag, Wflag, stdin_ok; +uid_t uid; + +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; + char *p; + + /* + * Test for the special case where the utility is called as + * "unlink", for which the functionality provided is greatly + * simplified. + */ + if ((p = rindex(argv[0], '/')) == NULL) + p = argv[0]; + else + ++p; + if (strcmp(p, "unlink") == 0) { + if (argc == 2) { + rm_file(&argv[1]); + exit(eval); + } else + usage(); + } + + Pflag = rflag = 0; + while ((ch = getopt(argc, argv, "dfiPRrvW")) != -1) + 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 'v': + vflag = 1; + break; + case 'W': + Wflag = 1; + break; + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc < 1) { + if (fflag) + return 0; + usage(); + } + + checkdot(argv); + uid = geteuid(); + + if (*argv) { + 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; + int flags; + int rval; + + /* + * Remove a file hierarchy. If forcing removal (-f), or interactive + * (-i) or can't ask anyway (stdin_ok), don't stat the file. + */ + needstat = !uid || (!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 + + flags = FTS_PHYSICAL; + if (!needstat) + flags |= FTS_NOSTAT; + if (Wflag) + flags |= FTS_WHITEOUT; + if (!(fts = fts_open(argv, flags, (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 (!fflag && !check(p->fts_path, p->fts_accpath, + p->fts_statp)) { + (void)fts_set(fts, p, FTS_SKIP); + p->fts_number = SKIPPED; + } + else if (!uid && + (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && + !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && + chflags(p->fts_accpath, + p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)) < 0) + goto err; + continue; + case FTS_DP: + /* Post-order: see if user skipped. */ + if (p->fts_number == SKIPPED) + continue; + break; + default: + if (!fflag && + !check(p->fts_path, p->fts_accpath, p->fts_statp)) + continue; + } + + rval = 0; + if (!uid && + (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && + !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE))) + rval = chflags(p->fts_accpath, + p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)); + if (rval == 0) { + /* + * 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. + */ + switch (p->fts_info) { + case FTS_DP: + case FTS_DNR: + rval = rmdir(p->fts_accpath); + if (rval == 0 || (fflag && errno == ENOENT)) { + if (rval == 0 && vflag) + (void)printf("%s\n", + p->fts_accpath); + continue; + } + break; + + case FTS_W: + rval = undelete(p->fts_accpath); + if (rval == 0 && (fflag && errno == ENOENT)) { + if (vflag) + (void)printf("%s\n", + p->fts_accpath); + continue; + } + break; + + default: + if (Pflag) + rm_overwrite(p->fts_accpath, NULL); + rval = unlink(p->fts_accpath); + if (rval == 0 || (fflag && errno == ENOENT)) { + if (rval == 0 && vflag) + (void)printf("%s\n", + p->fts_accpath); + continue; + } + } + } +err: + warn("%s", p->fts_path); + eval = 1; + } + if (errno) + err(1, "fts_read"); +} + +void +rm_file(argv) + char **argv; +{ + struct stat sb; + int rval; + char *f; + + /* + * 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 (Wflag) { + sb.st_mode = S_IFWHT|S_IWUSR|S_IRUSR; + } else { + if (!fflag || errno != ENOENT) { + warn("%s", f); + eval = 1; + } + continue; + } + } else if (Wflag) { + warnx("%s: %s", f, strerror(EEXIST)); + eval = 1; + continue; + } + + if (S_ISDIR(sb.st_mode) && !dflag) { + warnx("%s: is a directory", f); + eval = 1; + continue; + } + if (!fflag && !S_ISWHT(sb.st_mode) && !check(f, f, &sb)) + continue; + rval = 0; + if (!uid && + (sb.st_flags & (UF_APPEND|UF_IMMUTABLE)) && + !(sb.st_flags & (SF_APPEND|SF_IMMUTABLE))) + rval = chflags(f, sb.st_flags & ~(UF_APPEND|UF_IMMUTABLE)); + if (rval == 0) { + if (S_ISWHT(sb.st_mode)) + rval = undelete(f); + else 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; + } + if (vflag && rval == 0) + (void)printf("%s\n", f); + } +} + +/* + * 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; + struct statfs fsb; + off_t len; + int bsize, fd, wlen; + char *buf = NULL; + + 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; + if (fstatfs(fd, &fsb) == -1) + goto err; + bsize = MAX(fsb.f_iosize, 1024); + if ((buf = malloc(bsize)) == NULL) + err(1, "malloc"); + +#define PASS(byte) { \ + memset(buf, byte, bsize); \ + for (len = sbp->st_size; len > 0; len -= wlen) { \ + wlen = len < bsize ? len : bsize; \ + 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)) { + free(buf); + return; + } + +err: eval = 1; + if (buf) + free(buf); + warn("%s", file); +} + + +int +check(path, name, sp) + char *path, *name; + struct stat *sp; +{ + int ch, first; + char modep[15], *flagsp; + + /* 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) && + !(sp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && + (!(sp->st_flags & (UF_APPEND|UF_IMMUTABLE)) || !uid))) + return (1); + strmode(sp->st_mode, modep); + if ((flagsp = fflagstostr(sp->st_flags)) == NULL) + err(1, NULL); + (void)fprintf(stderr, "override %s%s%s/%s %s%sfor %s? ", + modep + 1, modep[9] == ' ' ? "" : " ", + user_from_uid(sp->st_uid, 0), + group_from_gid(sp->st_gid, 0), + *flagsp ? flagsp : "", *flagsp ? " " : "", + path); + free(flagsp); + } + (void)fflush(stderr); + + first = ch = getchar(); + while (ch != '\n' && ch != EOF) + ch = getchar(); + return (first == 'y' || 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) + continue; + t = save; + } else + ++t; + } +} + +void +usage() +{ + + (void)fprintf(stderr, "%s\n%s\n", + "usage: rm [-f | -i] [-dPRrvW] file ...", + " unlink file"); + exit(EX_USAGE); +} diff --git a/bin/rmail/Makefile b/bin/rmail/Makefile new file mode 100644 index 0000000..fcc11bb --- /dev/null +++ b/bin/rmail/Makefile @@ -0,0 +1,35 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 +# $FreeBSD$ + +SENDMAIL_DIR=${.CURDIR}/../../contrib/sendmail +.PATH: ${SENDMAIL_DIR}/rmail + +PROG= rmail +SRCS= rmail.c +MAN8= rmail.8 +CFLAGS+=-I${SENDMAIL_DIR}/include + +.if exists(${.OBJDIR}/../../lib/libsmutil) +LIBSMUTILDIR:= ${.OBJDIR}/../../lib/libsmutil +.else +LIBSMUTILDIR!= cd ${.CURDIR}/../../lib/libsmutil; make -V .OBJDIR +.endif +LIBSMUTIL:= ${LIBSMUTILDIR}/libsmutil.a + +DPADD= ${LIBSMUTIL} +LDADD= ${LIBSMUTIL} + +# User customizations to the sendmail build environment +CFLAGS+=${SENDMAIL_CFLAGS} +DPADD+=${SENDMAIL_DPADD} +LDADD+=${SENDMAIL_LDADD} +LDFLAGS+=${SENDMAIL_LDFLAGS} + +# If you want to have your rmail queuing the mail only, uncomment the +# following: +# CFLAGS+= -DQUEUE_ONLY + +# Not much point this being static. It calls a shared sendmail... +NOSHARED?= NO + +.include <bsd.prog.mk> diff --git a/bin/rmdir/Makefile b/bin/rmdir/Makefile new file mode 100644 index 0000000..c2c7f30 --- /dev/null +++ b/bin/rmdir/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 +# $FreeBSD$ + +PROG= rmdir + +.include <bsd.prog.mk> diff --git a/bin/rmdir/rmdir.1 b/bin/rmdir/rmdir.1 new file mode 100644 index 0000000..a6a2478 --- /dev/null +++ b/bin/rmdir/rmdir.1 @@ -0,0 +1,100 @@ +.\" 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 +.\" $FreeBSD$ +.\" +.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 +.Nm +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 +tries to remove it. +.Pp +The following option is available: +.Bl -tag -width indent +.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 +utility exits with one of the following values: +.Bl -tag -width Ds +.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 +command is expected to be +.St -p1003.2 +compatible. +.Sh HISTORY +A +.Nm +command appeared in +.At v1 . diff --git a/bin/rmdir/rmdir.c b/bin/rmdir/rmdir.c new file mode 100644 index 0000000..5c16c3d --- /dev/null +++ b/bin/rmdir/rmdir.c @@ -0,0 +1,124 @@ +/*- + * 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 const copyright[] = +"@(#) Copyright (c) 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)rmdir.c 8.3 (Berkeley) 4/2/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +int rm_path __P((char *)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch, errors; + int pflag; + + pflag = 0; + while ((ch = getopt(argc, argv, "p")) != -1) + switch(ch) { + case 'p': + pflag = 1; + break; + 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; + } else if (pflag) + errors |= rm_path(*argv); + } + + exit(errors); +} + +int +rm_path(path) + char *path; +{ + char *p; + + p = path + strlen(path); + while (--p > path && *p == '/') + ; + *++p = '\0'; + while ((p = strrchr(path, '/')) != NULL) { + /* Delete trailing slashes. */ + while (--p > path && *p == '/') + ; + *++p = '\0'; + + if (rmdir(path) < 0) { + warn("%s", path); + return (1); + } + } + + return (0); +} + +void +usage() +{ + + (void)fprintf(stderr, "usage: rmdir [-p] directory ...\n"); + exit(1); +} diff --git a/bin/sh/Makefile b/bin/sh/Makefile new file mode 100644 index 0000000..01c46f5 --- /dev/null +++ b/bin/sh/Makefile @@ -0,0 +1,61 @@ +# @(#)Makefile 8.4 (Berkeley) 5/5/95 +# $FreeBSD$ + +PROG= sh +SHSRCS= alias.c arith.y arith_lex.l 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 options.c output.c parser.c printf.c redir.c show.c \ + trap.c var.c +GENSRCS= builtins.c init.c nodes.c syntax.c +GENHDRS= builtins.h nodes.h syntax.h token.h y.tab.h +SRCS= ${SHSRCS} ${GENSRCS} ${GENHDRS} y.tab.h + +# MLINKS for Shell built in commands for which there are no userland +# utilities of the same name are handled with the associated manpage, +# builtin.1 in share/man/man1/. + +DPADD+= ${LIBL} ${LIBEDIT} ${LIBTERMCAP} +LDADD+= -ll -ledit -ltermcap + +LFLAGS= -8 # 8-bit lex scanner for arithmetic +CFLAGS+=-DSHELL -I. -I${.CURDIR} +# for debug: +# CFLAGS+= -g -DDEBUG=2 + +.PATH: ${.CURDIR}/bltin ${.CURDIR}/../../usr.bin/printf + +CLEANFILES+= mkinit mkinit.o mknodes mknodes.o \ + mksyntax mksyntax.o +CLEANFILES+= ${GENSRCS} ${GENHDRS} + +build-tools: mkinit mknodes mksyntax + +.ORDER: builtins.c builtins.h +builtins.c builtins.h: mkbuiltins builtins.def + cd ${.CURDIR}; sh mkbuiltins ${.OBJDIR} + +init.c: mkinit alias.c eval.c exec.c input.c jobs.c options.c parser.c \ + redir.c trap.c var.c + ./mkinit ${.ALLSRC:S/^mkinit$//} + +# XXX this is just to stop the default .c rule being used, so that the +# intermediate object has a fixed name. +# XXX we have a default .c rule, but no default .o rule. +.o: + ${CC} ${CFLAGS} ${LDFLAGS} ${.IMPSRC} ${LDLIBS} -o ${.TARGET} +mkinit: mkinit.o +mknodes: mknodes.o +mksyntax: mksyntax.o + +.ORDER: nodes.c nodes.h +nodes.c nodes.h: mknodes nodetypes nodes.c.pat + ./mknodes ${.CURDIR}/nodetypes ${.CURDIR}/nodes.c.pat + +.ORDER: syntax.c syntax.h +syntax.c syntax.h: mksyntax + ./mksyntax + +token.h: mktokens + sh ${.CURDIR}/mktokens + +.include <bsd.prog.mk> diff --git a/bin/sh/TOUR b/bin/sh/TOUR new file mode 100644 index 0000000..5a0b76b --- /dev/null +++ b/bin/sh/TOUR @@ -0,0 +1,357 @@ +# @(#)TOUR 8.1 (Berkeley) 5/31/93 +# $FreeBSD$ + +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.h + 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..3d6d8a1 --- /dev/null +++ b/bin/sh/alias.c @@ -0,0 +1,265 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)alias.c 8.3 (Berkeley) 5/4/95"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <stdlib.h> +#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 void setalias __P((char *, char *)); +STATIC int unalias __P((char *)); +STATIC struct alias **hashalias __P((char *)); + +STATIC +void +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 marks 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); + memcpy(ap->val, val, len); + ap->val[len] = ' '; /* fluff */ + ap->val[len+1] = '\0'; + } +#endif + ap->flag = 0; + 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; + int check; +{ + 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 + */ +int +aliascmd(argc, argv) + int argc; + 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) != NULL) { + 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); +} + +int +unaliascmd(argc, argv) + int argc __unused; + char **argv __unused; +{ + 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) + 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..a282a69 --- /dev/null +++ b/bin/sh/alias.h @@ -0,0 +1,52 @@ +/*- + * 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.2 (Berkeley) 5/4/95 + * $FreeBSD$ + */ + +#define ALIASINUSE 1 + +struct alias { + struct alias *next; + char *name; + char *val; + int flag; +}; + +struct alias *lookupalias __P((char *, int)); +int aliascmd __P((int, char **)); +int unaliascmd __P((int, char **)); +void rmaliases __P((void)); diff --git a/bin/sh/arith.h b/bin/sh/arith.h new file mode 100644 index 0000000..c58cd3c --- /dev/null +++ b/bin/sh/arith.h @@ -0,0 +1,38 @@ +/*- + * Copyright (c) 1995 + * 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. + * + * @(#)arith.h 1.1 (Berkeley) 5/4/95 + * $FreeBSD$ + */ + +int arith __P((char *)); +int expcmd __P((int , char **)); diff --git a/bin/sh/arith.y b/bin/sh/arith.y new file mode 100644 index 0000000..6d5bf95 --- /dev/null +++ b/bin/sh/arith.y @@ -0,0 +1,194 @@ +%token ARITH_NUM ARITH_LPAREN ARITH_RPAREN + +%left ARITH_OR +%left ARITH_AND +%left ARITH_BOR +%left ARITH_BXOR +%left ARITH_BAND +%left ARITH_EQ ARITH_NE +%left ARITH_LT ARITH_GT ARITH_GE ARITH_LE +%left ARITH_LSHIFT ARITH_RSHIFT +%left ARITH_ADD ARITH_SUB +%left ARITH_MUL ARITH_DIV ARITH_REM +%left ARITH_UNARYMINUS ARITH_UNARYPLUS ARITH_NOT ARITH_BNOT +%% + +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_BOR expr = { $$ = $1 | $3; } + | expr ARITH_BXOR expr = { $$ = $1 ^ $3; } + | expr ARITH_BAND expr = { $$ = $1 & $3; } + | expr ARITH_EQ expr = { $$ = $1 == $3; } + | expr ARITH_GT expr = { $$ = $1 > $3; } + | expr ARITH_GE expr = { $$ = $1 >= $3; } + | expr ARITH_LT expr = { $$ = $1 < $3; } + | expr ARITH_LE expr = { $$ = $1 <= $3; } + | expr ARITH_NE expr = { $$ = $1 != $3; } + | expr ARITH_LSHIFT expr = { $$ = $1 << $3; } + | expr ARITH_RSHIFT expr = { $$ = $1 >> $3; } + | expr ARITH_ADD expr = { $$ = $1 + $3; } + | expr ARITH_SUB expr = { $$ = $1 - $3; } + | expr ARITH_MUL expr = { $$ = $1 * $3; } + | expr ARITH_DIV expr = { + if ($3 == 0) + yyerror("division by zero"); + $$ = $1 / $3; + } + | expr ARITH_REM expr = { + if ($3 == 0) + yyerror("division by zero"); + $$ = $1 % $3; + } + | ARITH_NOT expr = { $$ = !($2); } + | ARITH_BNOT expr = { $$ = ~($2); } + | ARITH_SUB expr %prec ARITH_UNARYMINUS = { $$ = -($2); } + | ARITH_ADD expr %prec ARITH_UNARYPLUS = { $$ = $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. + * + * $FreeBSD$ + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)arith.y 8.3 (Berkeley) 5/4/95"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include "shell.h" +#include "error.h" +#include "output.h" +#include "memalloc.h" + +char *arith_buf, *arith_startbuf; +extern void arith_lex_reset(); + +int yylex __P((void)); +int yyparse __P((void)); + +int +arith(s) + char *s; +{ + long result; + + arith_buf = arith_startbuf = s; + + INTOFF; + result = yyparse(); + arith_lex_reset(); /* reprime lex */ + INTON; + + return (result); +} + +void +yyerror(s) + char *s; +{ + + yyerrok; + yyclearin; + arith_lex_reset(); /* reprime lex */ + error("arithmetic expression: %s: \"%s\"", s, arith_startbuf); +} + +/* + * The exp(1) builtin. + */ +int +expcmd(argc, argv) + int argc; + 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..82086bf --- /dev/null +++ b/bin/sh/arith_lex.l @@ -0,0 +1,90 @@ +%{ +/*- + * 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. + * + * $FreeBSD$ + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)arith_lex.l 8.3 (Berkeley) 5/4/95"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include "y.tab.h" +#include "error.h" + +extern int yylval; +extern char *arith_buf, *arith_startbuf; +#undef YY_INPUT +#define YY_INPUT(buf,result,max) \ + result = (*buf = *arith_buf++) ? 1 : YY_NULL; +#define YY_NO_UNPUT +%} + +%% +[ \t\n] { ; } +[0-9]+ { yylval = atol(yytext); return(ARITH_NUM); } +"(" { return(ARITH_LPAREN); } +")" { return(ARITH_RPAREN); } +"||" { return(ARITH_OR); } +"&&" { return(ARITH_AND); } +"|" { return(ARITH_BOR); } +"^" { return(ARITH_BXOR); } +"&" { return(ARITH_BAND); } +"==" { return(ARITH_EQ); } +"!=" { return(ARITH_NE); } +">" { return(ARITH_GT); } +">=" { return(ARITH_GE); } +"<" { return(ARITH_LT); } +"<=" { return(ARITH_LE); } +"<<" { return(ARITH_LSHIFT); } +">>" { return(ARITH_RSHIFT); } +"*" { return(ARITH_MUL); } +"/" { return(ARITH_DIV); } +"%" { return(ARITH_REM); } +"+" { return(ARITH_ADD); } +"-" { return(ARITH_SUB); } +"~" { return(ARITH_BNOT); } +"!" { return(ARITH_NOT); } +. { error("arith: syntax error: \"%s\"\n", arith_startbuf); } +%% + +void +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..a96c476 --- /dev/null +++ b/bin/sh/bltin/bltin.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. + * + * @(#)bltin.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD$ + */ + +/* + * 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" +#undef stdout +#define stdout out1 +#undef stderr +#define stderr out2 +#define printf out1fmt +#undef putc +#define putc(c, file) outc(c, file) +#undef putchar +#define putchar(c) out1c(c) +#define fprintf outfmt +#define fputs outstr +#define fflush flushout +#define INITARGS(argv) +#define warnx1(a, b, c) { \ + char buf[64]; \ + (void)snprintf(buf, sizeof(buf), a); \ + error("%s", buf); \ +} +#define warnx2(a, b, c) { \ + char buf[64]; \ + (void)snprintf(buf, sizeof(buf), a, b); \ + error("%s", buf); \ +} +#define warnx3(a, b, c) { \ + char buf[64]; \ + (void)snprintf(buf, sizeof(buf), a, b, c); \ + error("%s", buf); \ +} + +#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..a2f57f3 --- /dev/null +++ b/bin/sh/bltin/echo.1 @@ -0,0 +1,112 @@ +.\" 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.2 (Berkeley) 5/4/95 +.\" $FreeBSD$ +.\" +.Dd May 4, 1995 +.Dt ECHO 1 +.Os BSD 4.4 +.Sh NAME +.Nm echo +.Nd produce message in a shell script +.Sh SYNOPSIS +.Nm +.Op Fl n | Fl e +.Ar args... +.Sh DESCRIPTION +.Nm +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 +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 +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 +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..850a811 --- /dev/null +++ b/bin/sh/bltin/echo.c @@ -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. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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.2 (Berkeley) 5/4/95 + * $FreeBSD$ + */ + +/* + * Echo command. + */ + +#define main echocmd + +#include "bltin.h" + +/* #define eflag 1 */ + +int +main(argc, argv) + int argc; + char **argv; +{ + char **ap; + char *p; + 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 'a': c = '\a'; break; + case 'b': c = '\b'; break; + case 'c': return 0; /* exit */ + case 'e': c = '\e'; break; + 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.def b/bin/sh/builtins.def new file mode 100644 index 0000000..7cdcc38 --- /dev/null +++ b/bin/sh/builtins.def @@ -0,0 +1,92 @@ +#!/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.def 8.4 (Berkeley) 5/4/95 +# $FreeBSD$ + +# +# 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, and the -h flag, +# if present specifies that this command is to be excluded from systems +# based on the NO_HISTORY compile-time symbol. 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. +# +# 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 [ +falsecmd false +histcmd -h 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 +typecmd type +umaskcmd umask +unaliascmd unalias +unsetcmd unset +waitcmd wait +#foocmd foo +aliascmd alias +ulimitcmd ulimit diff --git a/bin/sh/cd.c b/bin/sh/cd.c new file mode 100644 index 0000000..3ec8dee --- /dev/null +++ b/bin/sh/cd.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 +#if 0 +static char sccsid[] = "@(#)cd.c 8.2 (Berkeley) 5/4/95"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> + +/* + * 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 "exec.h" +#include "redir.h" +#include "mystring.h" +#include "show.h" +#include "cd.h" + +STATIC int docd __P((char *, int)); +STATIC char *getcomponent __P((void)); +STATIC void updatepwd __P((char *)); + +char *curdir = NULL; /* current working directory */ +char *prevdir; /* previous working directory */ +STATIC char *cdcomppath; + +int +cdcmd(argc, argv) + int argc __unused; + char **argv __unused; +{ + char *dest; + char *path; + char *p; + struct stat statb; + int print = 0; + + nextopt(nullstr); + if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME", 1)) == NULL) + error("HOME not set"); + if (*dest == '\0') + dest = "."; + if (dest[0] == '-' && dest[1] == '\0') { + dest = prevdir ? prevdir : curdir; + if (dest) + print = 1; + else + dest = "."; + } + if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL) + path = nullstr; + while ((p = padvance(&path, dest)) != NULL) { + if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) { + if (!print) { + /* + * XXX - rethink + */ + if (p[0] == '.' && p[1] == '/' && p[2] != '\0') + p += 2; + print = strcmp(p, dest); + } + if (docd(p, print) >= 0) + return 0; + + } + } + error("can't cd to %s", dest); + /*NOTREACHED*/ + return 0; +} + + +/* + * Actually do the chdir. In an interactive shell, print the + * directory name if "print" is nonzero. + */ +STATIC int +docd(dest, print) + char *dest; + int print; +{ + char *p; + char *q; + char *component; + struct stat statb; + int first; + int badstat; + + TRACE(("docd(\"%s\", %d) called\n", dest, print)); + + /* + * Check each component of the path. If we find a symlink or + * something we can't stat, clear curdir to force a getcwd() + * next time we get the value of the current directory. + */ + badstat = 0; + cdcomppath = stalloc(strlen(dest) + 1); + scopy(dest, cdcomppath); + 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) + || (S_ISLNK(statb.st_mode))) { + /* print = 1; */ + badstat = 1; + break; + } + } + + INTOFF; + if (chdir(dest) < 0) { + INTON; + return -1; + } + updatepwd(badstat ? NULL : dest); + INTON; + if (print && iflag && curdir) + out1fmt("%s\n", curdir); + return 0; +} + + +/* + * Get the next component of the path name pointed to by cdcomppath. + * This routine overwrites the string pointed to by cdcomppath. + */ +STATIC char * +getcomponent() +{ + 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. + */ +STATIC void +updatepwd(dir) + char *dir; +{ + char *new; + char *p; + + hashcd(); /* update command hash table */ + + /* + * If our argument is NULL, we don't know the current directory + * any more because we traversed a symbolic link or something + * we couldn't stat(). + */ + if (dir == NULL || curdir == NULL) { + if (prevdir) + ckfree(prevdir); + INTOFF; + prevdir = curdir; + curdir = NULL; + if (getpwd() == NULL) + error("getcwd() failed: %s", strerror(errno)); + setvar("PWD", curdir, VEXPORT | VTEXTFIXED); + setvar("OLDPWD", prevdir, VEXPORT | VTEXTFIXED); + INTON; + return; + } + cdcomppath = stalloc(strlen(dir) + 1); + scopy(dir, cdcomppath); + STARTSTACKSTR(new); + if (*dir != '/') { + 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()); + setvar("PWD", curdir, VEXPORT | VTEXTFIXED); + setvar("OLDPWD", prevdir, VEXPORT | VTEXTFIXED); + INTON; +} + + +int +pwdcmd(argc, argv) + int argc __unused; + char **argv __unused; +{ + if (!getpwd()) + error("getcwd() failed: %s", strerror(errno)); + out1str(curdir); + out1c('\n'); + return 0; +} + + + + +#define MAXPWD 256 + +/* + * Find out what the current directory is. If we already know the current + * directory, this routine returns immediately. + */ +char * +getpwd() +{ + char buf[MAXPWD]; + + if (curdir) + return curdir; + /* + * Things are a bit complicated here; we could have just used + * getcwd, but traditionally getcwd is implemented using popen + * to /bin/pwd. This creates a problem for us, since we cannot + * keep track of the job if it is being ran behind our backs. + * So we re-implement getcwd(), and we suppress interrupts + * throughout the process. This is not completely safe, since + * the user can still break out of it by killing the pwd program. + * We still try to use getcwd for systems that we know have a + * c implementation of getcwd, that does not open a pipe to + * /bin/pwd. + */ +#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__SVR4) + + if (getcwd(buf, sizeof(buf)) == NULL) { + char *pwd = getenv("PWD"); + struct stat stdot, stpwd; + + if (pwd && *pwd == '/' && stat(".", &stdot) != -1 && + stat(pwd, &stpwd) != -1 && + stdot.st_dev == stpwd.st_dev && + stdot.st_ino == stpwd.st_ino) { + curdir = savestr(pwd); + return curdir; + } + return NULL; + } + curdir = savestr(buf); +#else + { + char *p; + int i; + int status; + struct job *jp; + int pip[2]; + + INTOFF; + if (pipe(pip) < 0) + error("Pipe call failed: %s", strerror(errno)); + jp = makejob((union node *)NULL, 1); + if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) { + (void) close(pip[0]); + if (pip[1] != 1) { + close(1); + copyfd(pip[1], 1); + close(pip[1]); + } + (void) execl("/bin/pwd", "pwd", (char *)0); + error("Cannot exec /bin/pwd"); + } + (void) 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; + } + (void) 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; +#endif + return curdir; +} diff --git a/bin/sh/cd.h b/bin/sh/cd.h new file mode 100644 index 0000000..078b40e --- /dev/null +++ b/bin/sh/cd.h @@ -0,0 +1,38 @@ +/*- + * Copyright (c) 1995 + * 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. + * + * $FreeBSD$ + */ + +char *getpwd __P((void)); +int cdcmd __P((int, char **)); +int pwdcmd __P((int, char **)); diff --git a/bin/sh/error.c b/bin/sh/error.c new file mode 100644 index 0000000..e079699 --- /dev/null +++ b/bin/sh/error.c @@ -0,0 +1,306 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)error.c 8.2 (Berkeley) 5/4/95"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +/* + * Errors and exceptions. + */ + +#include "shell.h" +#include "main.h" +#include "options.h" +#include "output.h" +#include "error.h" +#include "nodes.h" /* show.h needs nodes.h */ +#include "show.h" +#include "trap.h" +#include <signal.h> +#include <unistd.h> +#include <errno.h> + + +/* + * Code to handle exceptions in C. + */ + +struct jmploc *handler; +volatile sig_atomic_t exception; +volatile sig_atomic_t suppressint; +volatile sig_atomic_t intpending; +char *commandname; + + +static void exverror __P((int, char *, va_list)); + +/* + * 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) + int 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. If SIGINTs are not suppressed and + * the shell is not a root shell, then we want to be terminated if we + * get here, as if we were terminated directly by a SIGINT. Arrange for + * this here. + */ + +void +onint() { + sigset_t sigset; + + /* + * The !in_dotrap here is safe. The only way we can arrive here + * with in_dotrap set is that a trap handler set SIGINT to SIG_DFL + * and killed itself. + */ + + if (suppressint && !in_dotrap) { + intpending++; + return; + } + intpending = 0; + sigemptyset(&sigset); + sigprocmask(SIG_SETMASK, &sigset, NULL); + + /* + * This doesn't seem to be needed, since main() emits a newline. + */ +#if 0 + if (tcgetpgrp(0) == getpid()) + write(STDERR_FILENO, "\n", 1); +#endif + if (rootshell && iflag) + exraise(EXINT); + else { + signal(SIGINT, SIG_DFL); + kill(getpid(), SIGINT); + } +} + + +/* + * Exverror 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. + */ +static void +exverror(cond, msg, ap) + int cond; + char *msg; + va_list ap; +{ + CLEAR_PENDING_INT; + INTOFF; + +#ifdef DEBUG + if (msg) + TRACE(("exverror(%d, \"%s\") pid=%d\n", cond, msg, getpid())); + else + TRACE(("exverror(%d, NULL) pid=%d\n", cond, getpid())); +#endif + if (msg) { + if (commandname) + outfmt(&errout, "%s: ", commandname); + doformat(&errout, msg, ap); + out2c('\n'); + } + flushall(); + exraise(cond); +} + + +#ifdef __STDC__ +void +error(char *msg, ...) +#else +void +error(va_alist) + va_dcl +#endif +{ +#ifndef __STDC__ + char *msg; +#endif + va_list ap; +#ifdef __STDC__ + va_start(ap, msg); +#else + va_start(ap); + msg = va_arg(ap, char *); +#endif + exverror(EXERROR, msg, ap); + va_end(ap); +} + + +#ifdef __STDC__ +void +exerror(int cond, char *msg, ...) +#else +void +exerror(va_alist) + va_dcl +#endif +{ +#ifndef __STDC__ + int cond; + char *msg; +#endif + va_list ap; +#ifdef __STDC__ + va_start(ap, msg); +#else + va_start(ap); + cond = va_arg(ap, int); + msg = va_arg(ap, char *); +#endif + exverror(cond, msg, ap); + va_end(ap); +} + + + +/* + * 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" }, +#ifdef notdef + { EMFILE, ALL, "too many open files" }, +#endif + { 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) + int e; + int 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..b86e177 --- /dev/null +++ b/bin/sh/error.h @@ -0,0 +1,107 @@ +/*- + * 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.2 (Berkeley) 5/4/95 + * $FreeBSD$ + */ + +/* + * 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 exception. 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> +#include <signal.h> + +struct jmploc { + jmp_buf loc; +}; + +extern struct jmploc *handler; +extern volatile sig_atomic_t exception; + +/* exceptions */ +#define EXINT 0 /* SIGINT received */ +#define EXERROR 1 /* a generic error */ +#define EXSHELLPROC 2 /* execute a shell procedure */ +#define EXEXEC 3 /* command execution failed */ + + +/* + * 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 sig_atomic_t suppressint; +extern volatile sig_atomic_t intpending; + +#define INTOFF suppressint++ +#define INTON { if (--suppressint == 0 && intpending) onint(); } +#define FORCEINTON {suppressint = 0; if (intpending) onint();} +#define CLEAR_PENDING_INT intpending = 0 +#define int_pending() intpending + +void exraise __P((int)); +void onint __P((void)); +void error __P((char *, ...)); +void exerror __P((int, char *, ...)); +char *errmsg __P((int, int)); + + +/* + * 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..8bf2d84 --- /dev/null +++ b/bin/sh/eval.c @@ -0,0 +1,1054 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)eval.c 8.9 (Berkeley) 6/8/95"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <signal.h> +#include <unistd.h> +#include <sys/wait.h> /* For WIFSIGNALED(status) */ +#include <errno.h> + +/* + * 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 "show.h" +#include "mystring.h" +#ifndef NO_HISTORY +#include "myhistedit.h" +#endif + + +/* 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 */ + +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 */ +int oexitstatus; /* saved exit status */ + + +STATIC void evalloop __P((union node *)); +STATIC void evalfor __P((union node *)); +STATIC void evalcase __P((union node *, int)); +STATIC void evalsubshell __P((union node *, int)); +STATIC void expredir __P((union node *)); +STATIC void evalpipe __P((union node *)); +STATIC void evalcommand __P((union node *, int, struct backcmd *)); +STATIC void prehash __P((union node *)); + + +/* + * 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 command. + */ + +int +evalcmd(argc, argv) + int argc; + 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; + int flags; +{ + if (n == NULL) { + TRACE(("evaltree(NULL) called\n")); + exitstatus = 0; + goto out; + } +#ifndef NO_HISTORY + displayhist = 1; /* show history substitutions done with fc */ +#endif + TRACE(("evaltree(0x%lx: %d) called\n", (long)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) { + flags |= EV_TESTED; + 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: { + evaltree(n->nif.test, EV_TESTED); + if (evalskip) + goto out; + if (exitstatus == 0) + evaltree(n->nif.ifpart, flags); + else if (n->nif.elsepart) + evaltree(n->nif.elsepart, flags); + else + exitstatus = 0; + 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(); + /* + * XXX - Like "!(n->type == NSEMI)", more types will probably + * need to be excluded from this test. It's probably better + * to set or unset EV_TESTED in the loop above than to bloat + * the conditional here. + */ + if ((flags & EV_EXIT) || (eflag && exitstatus + && !(flags & EV_TESTED) && !(n->type == NSEMI))) + 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) { + oexitstatus = exitstatus; + 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; + int flags; +{ + union node *cp; + union node *patp; + struct arglist arglist; + struct stackmark smark; + + setstackmark(&smark); + arglist.lastp = &arglist.list; + oexitstatus = exitstatus; + 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; + int flags; +{ + 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, (int *)NULL); + INTON; + } +} + + + +/* + * Compute the names of the files in a redirection list. + */ + +STATIC void +expredir(n) + union node *n; +{ + union node *redir; + + for (redir = n ; redir ; redir = redir->nfile.next) { + struct arglist fn; + fn.lastp = &fn.list; + oexitstatus = exitstatus; + switch (redir->type) { + case NFROM: + case NTO: + case NFROMTO: + case NAPPEND: + expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR); + redir->nfile.expfname = fn.list->text; + break; + case NFROMFD: + case NTOFD: + if (redir->ndup.vname) { + expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE); + fixredir(redir, fn.list->text, 1); + } + break; + } + } +} + + + +/* + * 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%lx) called\n", (long)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: %s", strerror(errno)); + } + } + if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) { + INTON; + if (prevfd > 0) { + close(0); + copyfd(prevfd, 0); + close(prevfd); + } + if (pip[1] >= 0) { + if (!(prevfd >= 0 && pip[0] == 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, (int *)NULL); + TRACE(("evalpipe: job done exit status %d\n", exitstatus)); + INTON; + } +} + + + +/* + * Execute a command inside back quotes. If it's a builtin command, we + * want to save its output in a block obtained from malloc. Otherwise + * we fork off a subprocess and get the output of the command via a pipe. + * Should be called with interrupts off. + */ + +void +evalbackcmd(n, result) + union node *n; + struct backcmd *result; +{ + int pip[2]; + struct job *jp; + struct stackmark smark; /* unnecessary */ + + setstackmark(&smark); + result->fd = -1; + result->buf = NULL; + result->nleft = 0; + result->jp = NULL; + if (n == NULL) { + exitstatus = 0; + goto out; + } + if (n->type == NCMD) { + exitstatus = oexitstatus; + evalcommand(n, EV_BACKCMD, result); + } else { + exitstatus = 0; + if (pipe(pip) < 0) + error("Pipe call failed: %s", strerror(errno)); + 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; + int flags; + 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; + 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; + int realstatus; + int do_clearcmdentry; +#if __GNUC__ + /* Avoid longjmp clobbering */ + (void) &argv; + (void) &argc; + (void) &lastarg; + (void) &flags; + (void) &do_clearcmdentry; +#endif + + /* First expand the arguments. */ + TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); + setstackmark(&smark); + arglist.lastp = &arglist.list; + varlist.lastp = &varlist.list; + varflag = 1; + do_clearcmdentry = 0; + oexitstatus = exitstatus; + exitstatus = 0; + for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { + char *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 { + static const char PATH[] = "PATH="; + char *path = pathval(); + + /* + * Modify the command lookup path, if a PATH= assignment + * is present + */ + for (sp = varlist.list ; sp ; sp = sp->next) + if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0) { + path = sp->text + sizeof(PATH) - 1; + /* + * On `PATH=... command`, we need to make + * sure that the command isn't using the + * non-updated hash table of the outer PATH + * setting and we need to make sure that + * the hash table isn't filled with items + * from the temporary setting. + * + * It would be better to forbit using and + * updating the table while this command + * runs, by the command finding mechanism + * is heavily integrated with hash handling, + * so we just delete the hash before and after + * the command runs. Partly deleting like + * changepatch() does doesn't seem worth the + * bookinging effort, since most such runs add + * diretories in front of the new PATH. + */ + clearcmdentry(0); + do_clearcmdentry = 1; + } + + find_command(argv[0], &cmdentry, 1, path); + if (cmdentry.cmdtype == CMDUNKNOWN) { /* command not found */ + exitstatus = 127; + 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 = 127; + 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 || Tflag)) + || ((flags & EV_BACKCMD) != 0 + && (cmdentry.cmdtype != CMDBUILTIN + || cmdentry.u.index == CDCMD + || 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: %s", strerror(errno)); + } + 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) { +#ifdef DEBUG + trputs("Shell function: "); trargs(argv); +#endif + redirect(cmd->ncmd.redirect, REDIR_PUSH); + saveparam = shellparam; + shellparam.malloc = 0; + shellparam.reset = 1; + 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++; + if (flags & EV_TESTED) + evaltree(cmdentry.u.func, EV_TESTED); + else + 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) { +#ifdef DEBUG + trputs("builtin command: "); trargs(argv); +#endif + 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: + cmdenviron = NULL; + out1 = &output; + out2 = &errout; + freestdout(); + if (e != EXSHELLPROC) { + commandname = savecmdname; + if (flags & EV_EXIT) { + exitshell(exitstatus); + } + } + handler = savehandler; + if (e != -1) { + if ((e != EXERROR && e != EXEXEC) + || cmdentry.u.index == BLTINCMD + || cmdentry.u.index == DOTCMD + || cmdentry.u.index == EVALCMD +#ifndef NO_HISTORY + || cmdentry.u.index == HISTCMD +#endif + || 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 { +#ifdef DEBUG + trputs("normal command: "); trargs(argv); +#endif + clearredir(); + redirect(cmd->ncmd.redirect, 0); + for (sp = varlist.list ; sp ; sp = sp->next) + setvareq(sp->text, VEXPORT|VSTACK); + envp = environment(); + shellexec(argv, envp, pathval(), cmdentry.u.index); + /*NOTREACHED*/ + } + goto out; + +parent: /* parent process gets here (if we forked) */ + if (mode == 0) { /* argument to fork */ + INTOFF; + exitstatus = waitforjob(jp, &realstatus); + INTON; + if (iflag && loopnest > 0 && WIFSIGNALED(realstatus)) { + evalskip = SKIPBREAK; + skipcount = loopnest; + } + } else if (mode == 2) { + backcmd->fd = pip[0]; + close(pip[1]); + backcmd->jp = jp; + } + +out: + if (lastarg) + setvar("_", lastarg, 0); + if (do_clearcmdentry) + clearcmdentry(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 && n->ncmd.args) + if (goodname(n->ncmd.args->narg.text)) + find_command(n->ncmd.args->narg.text, &entry, 0, + pathval()); +} + + + +/* + * 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. + */ + +int +bltincmd(argc, argv) + int argc __unused; + char **argv __unused; +{ + listsetvar(cmdenviron); + /* + * Preserve exitstatus of a previous possible redirection + * as POSIX mandates + */ + 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. + */ + +int +breakcmd(argc, argv) + int argc; + char **argv; +{ + int n = argc > 1 ? number(argv[1]) : 1; + + if (n > loopnest) + n = loopnest; + if (n > 0) { + evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK; + skipcount = n; + } + return 0; +} + + +/* + * The return command. + */ + +int +returncmd(argc, argv) + int argc; + char **argv; +{ + int ret = argc > 1 ? number(argv[1]) : oexitstatus; + + if (funcnest) { + evalskip = SKIPFUNC; + skipcount = 1; + } else { + /* skip the rest of the file */ + evalskip = SKIPFILE; + skipcount = 1; + } + return ret; +} + + +int +falsecmd(argc, argv) + int argc __unused; + char **argv __unused; +{ + return 1; +} + + +int +truecmd(argc, argv) + int argc __unused; + char **argv __unused; +{ + return 0; +} + + +int +execcmd(argc, argv) + int argc; + char **argv; +{ + if (argc > 1) { + struct strlist *sp; + + iflag = 0; /* exit on error */ + mflag = 0; + optschanged(); + for (sp = cmdenviron; sp ; sp = sp->next) + setvareq(sp->text, VEXPORT|VSTACK); + 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..4f0b898 --- /dev/null +++ b/bin/sh/eval.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. + * + * @(#)eval.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD$ + */ + +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 */ +}; + +int evalcmd __P((int, char **)); +void evalstring __P((char *)); +union node; /* BLETCH for ansi C */ +void evaltree __P((union node *, int)); +void evalbackcmd __P((union node *, struct backcmd *)); +int bltincmd __P((int, char **)); +int breakcmd __P((int, char **)); +int returncmd __P((int, char **)); +int falsecmd __P((int, char **)); +int truecmd __P((int, char **)); +int execcmd __P((int, char **)); + +/* in_function returns nonzero if we are currently evaluating a function */ +#define in_function() funcnest +extern int funcnest; +extern int evalskip; + +/* reasons for skipping commands (see comment on breakcmd routine) */ +#define SKIPBREAK 1 +#define SKIPCONT 2 +#define SKIPFUNC 3 +#define SKIPFILE 4 diff --git a/bin/sh/exec.c b/bin/sh/exec.c new file mode 100644 index 0000000..714ac36 --- /dev/null +++ b/bin/sh/exec.c @@ -0,0 +1,926 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)exec.c 8.4 (Berkeley) 6/8/95"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <stdlib.h> + +/* + * 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 "show.h" +#include "jobs.h" +#include "alias.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 */ +int exerrno = 0; /* Last exec error */ + + +STATIC void tryexec __P((char *, char **, char **)); +#ifndef BSD +STATIC void execinterp __P((char **, char **)); +#endif +STATIC void printentry __P((struct tblentry *, int)); +STATIC struct tblentry *cmdlookup __P((char *, int)); +STATIC void delete_cmd_entry __P((void)); + + + +/* + * 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; + int index; +{ + 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); + } + } + + /* Map to POSIX errors */ + switch (e) { + case EACCES: + exerrno = 126; + break; + case ENOENT: + exerrno = 127; + break; + default: + exerrno = 2; + break; + } + exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC)); +} + + +STATIC void +tryexec(cmd, argv, envp) + char *cmd; + char **argv; + char **envp; + { + int e; +#ifndef BSD + char *p; +#endif + +#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; + { + 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) { + memcpy(q, start, 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 ***/ + + +int +hashcmd(argc, argv) + int argc __unused; + char **argv __unused; +{ + 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, pathval()); + 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, path) + char *name; + struct cmdentry *entry; + int printerr; + char *path; +{ + struct tblentry *cmdp; + int index; + int prev; + 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; + } + + 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 (!S_ISREG(statb.st_mode)) + 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 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) + const char *newval; +{ + const char *old, *new; + int index; + int firstchange; + int bltin; + + old = pathval(); + new = newval; + firstchange = 9999; /* assume no change */ + index = 0; + bltin = -1; + for (;;) { + if (*old != *new) { + firstchange = index; + if ((*old == '\0' && *new == ':') + || (*old == ':' && *new == '\0')) + firstchange++; + old = new; /* ignore subsequent differences */ + } + if (*new == '\0') + break; + if (*new == '%' && bltin < 0 && prefix("builtin", new + 1)) + bltin = index; + if (*new == ':') { + index++; + } + new++, old++; + } + if (builtinloc < 0 && bltin >= 0) + builtinloc = bltin; /* zap builtins */ + if (builtinloc >= 0 && bltin < 0) + firstchange = 0; + clearcmdentry(firstchange); + builtinloc = bltin; +} + + +/* + * Clear out command entries. The argument specifies the first entry in + * PATH which has changed. + */ + +void +clearcmdentry(firstchange) + int 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 add; +{ + int hashval; + 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); +} + +/* + * Locate and print what a word is... + */ + +int +typecmd(argc, argv) + int argc; + char **argv; +{ + struct cmdentry entry; + struct tblentry *cmdp; + char **pp; + struct alias *ap; + int i; + int error = 0; + extern char *const parsekwd[]; + + for (i = 1; i < argc; i++) { + out1str(argv[i]); + /* First look at the keywords */ + for (pp = (char **)parsekwd; *pp; pp++) + if (**pp == *argv[i] && equal(*pp, argv[i])) + break; + + if (*pp) { + out1str(" is a shell keyword\n"); + continue; + } + + /* Then look at the aliases */ + if ((ap = lookupalias(argv[i], 1)) != NULL) { + out1fmt(" is an alias for %s\n", ap->val); + continue; + } + + /* Then check if it is a tracked alias */ + if ((cmdp = cmdlookup(argv[i], 0)) != NULL) { + entry.cmdtype = cmdp->cmdtype; + entry.u = cmdp->param; + } + else { + /* Finally use brute force */ + find_command(argv[i], &entry, 0, pathval()); + } + + switch (entry.cmdtype) { + case CMDNORMAL: { + if (strchr(argv[i], '/') == NULL) { + char *path = pathval(), *name; + int j = entry.u.index; + do { + name = padvance(&path, argv[i]); + stunalloc(name); + } while (--j >= 0); + out1fmt(" is%s %s\n", + cmdp ? " a tracked alias for" : "", name); + } else { + if (access(argv[i], X_OK) == 0) + out1fmt(" is %s\n", argv[i]); + else + out1fmt(": %s\n", strerror(errno)); + } + break; + } + case CMDFUNCTION: + out1str(" is a shell function\n"); + break; + + case CMDBUILTIN: + out1str(" is a shell builtin\n"); + break; + + default: + out1str(": not found\n"); + error |= 127; + break; + } + } + return error; +} diff --git a/bin/sh/exec.h b/bin/sh/exec.h new file mode 100644 index 0000000..bb593f0 --- /dev/null +++ b/bin/sh/exec.h @@ -0,0 +1,72 @@ +/*- + * 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.3 (Berkeley) 6/8/95 + * $FreeBSD$ + */ + +/* 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 */ +extern int exerrno; /* last exec error */ + +void shellexec __P((char **, char **, char *, int)); +char *padvance __P((char **, char *)); +int hashcmd __P((int, char **)); +void find_command __P((char *, struct cmdentry *, int, char *)); +int find_builtin __P((char *)); +void hashcd __P((void)); +void changepath __P((const char *)); +void deletefuncs __P((void)); +void getcmdentry __P((char *, struct cmdentry *)); +void addcmdentry __P((char *, struct cmdentry *)); +void defun __P((char *, union node *)); +int unsetfunc __P((char *)); +int typecmd __P((int, char **)); +void clearcmdentry __P((int)); diff --git a/bin/sh/expand.c b/bin/sh/expand.c new file mode 100644 index 0000000..d82f459 --- /dev/null +++ b/bin/sh/expand.c @@ -0,0 +1,1560 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)expand.c 8.5 (Berkeley) 5/15/95"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <errno.h> +#include <dirent.h> +#include <unistd.h> +#include <pwd.h> +#include <stdlib.h> +#include <limits.h> +#include <stdio.h> + +/* + * 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 "arith.h" +#include "show.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 */ + +STATIC void argstr __P((char *, int)); +STATIC char *exptilde __P((char *, int)); +STATIC void expbackq __P((union node *, int, int)); +STATIC int subevalvar __P((char *, char *, int, int, int, int)); +STATIC char *evalvar __P((char *, int)); +STATIC int varisset __P((char *, int)); +STATIC void varvalue __P((char *, int, int)); +STATIC void recordregion __P((int, int, int)); +STATIC void removerecordregions __P((int)); +STATIC void ifsbreakup __P((char *, struct arglist *)); +STATIC void expandmeta __P((struct strlist *, int)); +STATIC void expmeta __P((char *, char *)); +STATIC void addfname __P((char *)); +STATIC struct strlist *expsort __P((struct strlist *)); +STATIC struct strlist *msort __P((struct strlist *, int)); +STATIC int pmatch __P((char *, char *, int)); +STATIC char *cvtnum __P((int, char *)); +STATIC int collate_range_cmp __P((int, int)); + +STATIC int collate_range_cmp (c1, c2) + int c1, c2; +{ + static char s1[2], s2[2]; + int ret; + + c1 &= UCHAR_MAX; + c2 &= UCHAR_MAX; + if (c1 == c2) + return (0); + s1[0] = c1; + s2[0] = c2; + if ((ret = strcoll(s1, s2)) != 0) + return (ret); + return (c1 - c2); +} + +/* + * 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; + int flag; +{ + 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) + char *p; + int flag; +{ + 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 CTLQUOTEMARK: + /* "$@" syntax adherence hack */ + if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=') + break; + if ((flag & EXP_FULL) != 0) + STPUTC(c, expdest); + break; + 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; + int flag; +{ + char c, *startp = p; + struct passwd *pw; + char *home; + int quotes = flag & (EXP_FULL | EXP_CASE); + + while ((c = *p) != '\0') { + switch(c) { + case CTLESC: + return (startp); + case CTLQUOTEMARK: + 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++) != '\0') { + if (quotes && c >= 0 && SQSYNTAX[(int)c] == CCTL) + STPUTC(CTLESC, expdest); + STPUTC(c, expdest); + } + return (p); +lose: + *p = c; + return (startp); +} + + +STATIC void +removerecordregions(endoff) + int endoff; +{ + if (ifslastp == NULL) + return; + + if (ifsfirst.endoff > endoff) { + while (ifsfirst.next != NULL) { + struct ifsregion *ifsp; + INTOFF; + ifsp = ifsfirst.next->next; + ckfree(ifsfirst.next); + ifsfirst.next = ifsp; + INTON; + } + if (ifsfirst.begoff > endoff) + ifslastp = NULL; + else { + ifslastp = &ifsfirst; + ifsfirst.endoff = endoff; + } + return; + } + + ifslastp = &ifsfirst; + while (ifslastp->next && ifslastp->next->begoff < endoff) + ifslastp=ifslastp->next; + while (ifslastp->next != NULL) { + struct ifsregion *ifsp; + INTOFF; + ifsp = ifslastp->next->next; + ckfree(ifslastp->next); + ifslastp->next = ifsp; + INTON; + } + if (ifslastp->endoff > endoff) + ifslastp->endoff = endoff; +} + +/* + * Expand arithmetic expression. Backup to start of expression, + * evaluate, place result in (backed up) result, adjust string position. + */ +void +expari(flag) + int flag; +{ + char *p, *start; + int result; + int begoff; + int quotes = flag & (EXP_FULL | EXP_CASE); + int quoted; + + + /* + * This routine is slightly over-complicated for + * efficiency. First we make sure there is + * enough space for the result, which may be bigger + * than the expression if we add exponentiation. 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. + */ +#if INT_MAX / 1000000000 >= 10 || INT_MIN / 1000000000 <= -10 +#error "integers with more than 10 digits are not supported" +#endif + CHECKSTRSPACE(12 - 2, 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 (p[1] == '"') + quoted=1; + else + quoted=0; + begoff = p - start; + removerecordregions(begoff); + if (quotes) + rmescapes(p+2); + result = arith(p+2); + fmtstr(p, 12, "%d", result); + while (*p++) + ; + if (quoted == 0) + recordregion(begoff, p - 1 - start, 0); + result = expdest - p + 1; + STADJUST(-result, expdest); +} + + +/* + * Expand stuff in backwards quotes. + */ + +STATIC void +expbackq(cmd, quoted, flag) + union node *cmd; + int quoted; + int flag; +{ + 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 && lastc >= 0 && syntax[(int)lastc] == CCTL) + STPUTC(CTLESC, dest); + STPUTC(lastc, dest); + } + } + + /* Eat all trailing newlines */ + for (p--; lastc == '\n'; lastc = *--p) + STUNPUTC(dest); + + if (in.fd >= 0) + close(in.fd); + if (in.buf) + ckfree(in.buf); + if (in.jp) + exitstatus = waitforjob(in.jp, (int *)NULL); + 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; +} + + + +STATIC int +subevalvar(p, str, strloc, subtype, startloc, varflags) + char *p; + char *str; + int strloc; + int subtype; + int startloc; + int varflags; +{ + char *startp; + char *loc = NULL; + char *q; + int c = 0; + int saveherefd = herefd; + struct nodelist *saveargbackq = argbackq; + int amount; + + herefd = -1; + argstr(p, 0); + STACKSTRNUL(expdest); + herefd = saveherefd; + argbackq = saveargbackq; + startp = stackblock() + startloc; + if (str == NULL) + str = stackblock() + strloc; + + switch (subtype) { + case VSASSIGN: + setvar(str, startp, 0); + amount = startp - expdest; + STADJUST(amount, expdest); + varflags &= ~VSNUL; + if (c != 0) + *loc = c; + return 1; + + case VSQUESTION: + if (*p != CTLENDVAR) { + outfmt(&errout, "%s\n", startp); + error((char *)NULL); + } + error("%.*s: parameter %snot set", p - str - 1, + str, (varflags & VSNUL) ? "null or " + : nullstr); + return 0; + + case VSTRIMLEFT: + for (loc = startp; loc < str; loc++) { + c = *loc; + *loc = '\0'; + if (patmatch(str, startp, varflags & VSQUOTE)) { + *loc = c; + goto recordleft; + } + *loc = c; + if ((varflags & VSQUOTE) && *loc == CTLESC) + loc++; + } + return 0; + + case VSTRIMLEFTMAX: + for (loc = str - 1; loc >= startp;) { + c = *loc; + *loc = '\0'; + if (patmatch(str, startp, varflags & VSQUOTE)) { + *loc = c; + goto recordleft; + } + *loc = c; + loc--; + if ((varflags & VSQUOTE) && loc > startp && + *(loc - 1) == CTLESC) { + for (q = startp; q < loc; q++) + if (*q == CTLESC) + q++; + if (q > loc) + loc--; + } + } + return 0; + + case VSTRIMRIGHT: + for (loc = str - 1; loc >= startp;) { + if (patmatch(str, loc, varflags & VSQUOTE)) { + amount = loc - expdest; + STADJUST(amount, expdest); + return 1; + } + loc--; + if ((varflags & VSQUOTE) && loc > startp && + *(loc - 1) == CTLESC) { + for (q = startp; q < loc; q++) + if (*q == CTLESC) + q++; + if (q > loc) + loc--; + } + } + return 0; + + case VSTRIMRIGHTMAX: + for (loc = startp; loc < str - 1; loc++) { + if (patmatch(str, loc, varflags & VSQUOTE)) { + amount = loc - expdest; + STADJUST(amount, expdest); + return 1; + } + if ((varflags & VSQUOTE) && *loc == CTLESC) + loc++; + } + return 0; + + + default: + abort(); + } + +recordleft: + amount = ((str - 1) - (loc - startp)) - expdest; + STADJUST(amount, expdest); + while (loc != str - 1) + *startp++ = *loc++; + return 1; +} + + +/* + * Expand a variable, and return a pointer to the next character in the + * input string. + */ + +STATIC char * +evalvar(p, flag) + char *p; + int flag; +{ + int subtype; + int varflags; + char *var; + char *val; + int patloc; + int c; + int set; + int special; + int startloc; + int varlen; + int easy; + 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, varflags & VSNUL); + val = NULL; + } else { + val = bltinlookup(var, 1); + if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) { + val = NULL; + set = 0; + } else + set = 1; + } + varlen = 0; + startloc = expdest - stackblock(); + if (set && subtype != VSPLUS) { + /* insert the value of the variable */ + if (special) { + varvalue(var, varflags & VSQUOTE, flag & EXP_FULL); + if (subtype == VSLENGTH) { + varlen = expdest - stackblock() - startloc; + STADJUST(-varlen, expdest); + } + } else { + char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX + : BASESYNTAX; + + if (subtype == VSLENGTH) { + for (;*val; val++) + varlen++; + } + else { + while (*val) { + if (quotes && *val >= 0 && + syntax[(int)*val] == CCTL) + STPUTC(CTLESC, expdest); + STPUTC(*val++, expdest); + } + + } + } + } + + if (subtype == VSPLUS) + set = ! set; + + easy = ((varflags & VSQUOTE) == 0 || + (*var == '@' && shellparam.nparam != 1)); + + + switch (subtype) { + case VSLENGTH: + expdest = cvtnum(varlen, expdest); + goto record; + + case VSNORMAL: + if (!easy) + break; +record: + recordregion(startloc, expdest - stackblock(), + varflags & VSQUOTE); + break; + + case VSPLUS: + case VSMINUS: + if (!set) { + argstr(p, flag); + break; + } + if (easy) + goto record; + break; + + case VSTRIMLEFT: + case VSTRIMLEFTMAX: + case VSTRIMRIGHT: + case VSTRIMRIGHTMAX: + if (!set) + break; + /* + * Terminate the string and start recording the pattern + * right after it + */ + STPUTC('\0', expdest); + patloc = expdest - stackblock(); + if (subevalvar(p, NULL, patloc, subtype, + startloc, varflags) == 0) { + int amount = (expdest - stackblock() - patloc) + 1; + STADJUST(-amount, expdest); + } + /* Remove any recorded regions beyond start of variable */ + removerecordregions(startloc); + goto record; + + case VSASSIGN: + case VSQUESTION: + if (!set) { + if (subevalvar(p, var, 0, subtype, startloc, varflags)) { + varflags &= ~VSNUL; + /* + * Remove any recorded regions beyond + * start of variable + */ + removerecordregions(startloc); + goto again; + } + break; + } + if (easy) + goto record; + break; + + default: + abort(); + } + + 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, nulok) + char *name; + int nulok; +{ + + if (*name == '!') + return backgndpid != -1; + else if (*name == '@' || *name == '*') { + if (*shellparam.p == NULL) + return 0; + + if (nulok) { + char **av; + + for (av = shellparam.p; *av; av++) + if (**av != '\0') + return 1; + return 0; + } + } else if (is_digit(*name)) { + char *ap; + int num = atoi(name); + + if (num > shellparam.nparam) + return 0; + + if (num == 0) + ap = arg0; + else + ap = shellparam.p[num - 1]; + + if (nulok && (ap == NULL || *ap == '\0')) + return 0; + } + return 1; +} + + + +/* + * Add the value of a specialized variable to the stack string. + */ + +STATIC void +varvalue(name, quoted, allow_split) + char *name; + int quoted; + int allow_split; +{ + int num; + char *p; + int i; + extern int oexitstatus; + char sep; + char **ap; + char const *syntax; + +#define STRTODEST(p) \ + do {\ + if (allow_split) { \ + syntax = quoted? DQSYNTAX : BASESYNTAX; \ + while (*p) { \ + if (*p >= 0 && syntax[(int)*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 = oexitstatus; + goto numvar; + case '#': + num = shellparam.nparam; + goto numvar; + case '!': + num = backgndpid; +numvar: + expdest = cvtnum(num, expdest); + break; + case '-': + for (i = 0 ; i < NOPTS ; i++) { + if (optlist[i].val) + STPUTC(optlist[i].letter, expdest); + } + break; + case '@': + if (allow_split && quoted) { + for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { + STRTODEST(p); + if (*ap) + STPUTC('\0', expdest); + } + break; + } + /* fall through */ + case '*': + if (ifsset() != 0) + sep = ifsval()[0]; + else + sep = ' '; + for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { + STRTODEST(p); + if (*ap && sep) + STPUTC(sep, expdest); + } + break; + case '0': + p = arg0; + STRTODEST(p); + break; + default: + if (is_digit(*name)) { + num = atoi(name); + if (num > 0 && num <= shellparam.nparam) { + p = shellparam.p[num - 1]; + 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) + int start; + int end; + int nulonly; +{ + 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; + char *p; + char *q; + char *ifs; + int ifsspc; + int nulonly; + + + start = string; + ifsspc = 0; + nulonly = 0; + if (ifslastp != NULL) { + ifsp = &ifsfirst; + do { + p = string + ifsp->begoff; + nulonly = ifsp->nulonly; + ifs = nulonly ? nullstr : + ( ifsset() ? ifsval() : " \t\n" ); + ifsspc = 0; + while (p < string + ifsp->endoff) { + q = p; + if (*p == CTLESC) + p++; + if (strchr(ifs, *p)) { + if (!nulonly) + ifsspc = (strchr(" \t\n", *p) != NULL); + /* Ignore IFS whitespace at start */ + if (q == start && ifsspc) { + p++; + start = p; + continue; + } + *q = '\0'; + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = start; + *arglist->lastp = sp; + arglist->lastp = &sp->next; + p++; + if (!nulonly) { + for (;;) { + if (p >= string + ifsp->endoff) { + break; + } + q = p; + if (*p == CTLESC) + p++; + if (strchr(ifs, *p) == NULL ) { + p = q; + break; + } else if (strchr(" \t\n",*p) == NULL) { + if (ifsspc) { + p++; + ifsspc = 0; + } else { + p = q; + break; + } + } else + p++; + } + } + start = p; + } else + p++; + } + } while ((ifsp = ifsp->next) != NULL); + if (*start || (!ifsspc && start > string && + (nulonly || 1))) { + 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; + int flag __unused; +{ + 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; + { + 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 == '^') + q++; + for (;;) { + while (*q == CTLQUOTEMARK) + q++; + 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 == CTLQUOTEMARK) + continue; + 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 == CTLQUOTEMARK) + continue; + 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) { + while (*p == CTLQUOTEMARK) + p++; + 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; + p = start; + while (*p == CTLQUOTEMARK) + p++; + if (*p == CTLESC) + p++; + if (*p == '.') + matchdot++; + while (! int_pending() && (dp = readdir(dirp)) != NULL) { + if (dp->d_name[0] == '.' && ! matchdot) + continue; + if (patmatch(start, dp->d_name, 0)) { + if (atend) { + scopy(dp->d_name, enddir); + addfname(expdir); + } else { + char *q; + for (p = enddir, q = dp->d_name; + (*p++ = *q++) != '\0';) + continue; + 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; + int len; +{ + struct strlist *p, *q = NULL; + 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, squoted) + char *pattern; + char *string; + int squoted; /* string might have quote chars */ + { +#ifdef notdef + if (pattern[0] == '!' && pattern[1] == '!') + return 1 - pmatch(pattern + 2, string); + else +#endif + return pmatch(pattern, string, squoted); +} + + +STATIC int +pmatch(pattern, string, squoted) + char *pattern; + char *string; + int squoted; + { + char *p, *q; + char c; + + p = pattern; + q = string; + for (;;) { + switch (c = *p++) { + case '\0': + goto breakloop; + case CTLESC: + if (squoted && *q == CTLESC) + q++; + if (*q++ != *p++) + return 0; + break; + case CTLQUOTEMARK: + continue; + case '?': + if (squoted && *q == CTLESC) + q++; + if (*q++ == '\0') + return 0; + break; + case '*': + c = *p; + while (c == CTLQUOTEMARK || c == '*') + c = *++p; + if (c != CTLESC && c != CTLQUOTEMARK && + c != '?' && c != '*' && c != '[') { + while (*q != c) { + if (squoted && *q == CTLESC && + q[1] == c) + break; + if (*q == '\0') + return 0; + if (squoted && *q == CTLESC) + q++; + q++; + } + } + do { + if (pmatch(p, q, squoted)) + return 1; + if (squoted && *q == CTLESC) + q++; + } while (*q++ != '\0'); + return 0; + case '[': { + char *endp; + int invert, found; + char chr; + + endp = p; + if (*endp == '!' || *endp == '^') + endp++; + for (;;) { + while (*endp == CTLQUOTEMARK) + endp++; + if (*endp == '\0') + goto dft; /* no matching ] */ + if (*endp == CTLESC) + endp++; + if (*++endp == ']') + break; + } + invert = 0; + if (*p == '!' || *p == '^') { + invert++; + p++; + } + found = 0; + chr = *q++; + if (squoted && chr == CTLESC) + chr = *q++; + if (chr == '\0') + return 0; + c = *p++; + do { + if (c == CTLQUOTEMARK) + continue; + if (c == CTLESC) + c = *p++; + if (*p == '-' && p[1] != ']') { + p++; + while (*p == CTLQUOTEMARK) + p++; + if (*p == CTLESC) + p++; + if ( collate_range_cmp(chr, c) >= 0 + && collate_range_cmp(chr, *p) <= 0 + ) + found = 1; + p++; + } else { + if (chr == c) + found = 1; + } + } while ((c = *p++) != ']'); + if (found == invert) + return 0; + break; + } +dft: default: + if (squoted && *q == CTLESC) + q++; + 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; +{ + char *p, *q; + + p = str; + while (*p != CTLESC && *p != CTLQUOTEMARK) { + if (*p++ == '\0') + return; + } + q = p; + while (*p) { + if (*p == CTLQUOTEMARK) { + p++; + continue; + } + 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, 0); + popstackmark(&smark); + return result; +} + +/* + * Our own itoa(). + */ + +STATIC char * +cvtnum(num, buf) + int num; + char *buf; + { + char temp[32]; + int neg = num < 0; + char *p = temp + 31; + + temp[31] = '\0'; + + do { + *--p = num % 10 + '0'; + } while ((num /= 10) != 0); + + if (neg) + *--p = '-'; + + while (*p) + STPUTC(*p++, buf); + return buf; +} diff --git a/bin/sh/expand.h b/bin/sh/expand.h new file mode 100644 index 0000000..955b8bf --- /dev/null +++ b/bin/sh/expand.h @@ -0,0 +1,67 @@ +/*- + * 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.2 (Berkeley) 5/4/95 + * $FreeBSD$ + */ + +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 */ + + +union node; +void expandhere __P((union node *, int)); +void expandarg __P((union node *, struct arglist *, int)); +void expari __P((int)); +int patmatch __P((char *, char *, int)); +void rmescapes __P((char *)); +int casematch __P((union node *, char *)); diff --git a/bin/sh/funcs/cmv b/bin/sh/funcs/cmv new file mode 100644 index 0000000..6e9dcff --- /dev/null +++ b/bin/sh/funcs/cmv @@ -0,0 +1,50 @@ +# 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.2 (Berkeley) 5/4/95 +# $FreeBSD$ + +# 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..a5d3c8e --- /dev/null +++ b/bin/sh/funcs/dirs @@ -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. +# +# @(#)dirs 8.2 (Berkeley) 5/4/95 +# $FreeBSD$ + +# 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..7455571 --- /dev/null +++ b/bin/sh/funcs/kill @@ -0,0 +1,50 @@ +# 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.2 (Berkeley) 5/4/95 +# $FreeBSD$ + +# 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..7a6fc85 --- /dev/null +++ b/bin/sh/funcs/login @@ -0,0 +1,39 @@ +# 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.2 (Berkeley) 5/4/95 +# $FreeBSD$ + +# 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..fd8e5d6 --- /dev/null +++ b/bin/sh/funcs/newgrp @@ -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. +# +# @(#)newgrp 8.2 (Berkeley) 5/4/95 +# $FreeBSD$ + +newgrp() exec newgrp "$@" diff --git a/bin/sh/funcs/popd b/bin/sh/funcs/popd new file mode 100644 index 0000000..e0be35a --- /dev/null +++ b/bin/sh/funcs/popd @@ -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. +# +# @(#)popd 8.2 (Berkeley) 5/4/95 +# $FreeBSD$ + +# 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..d60ede2 --- /dev/null +++ b/bin/sh/funcs/pushd @@ -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. +# +# @(#)pushd 8.2 (Berkeley) 5/4/95 +# $FreeBSD$ + +# 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..f06e12f --- /dev/null +++ b/bin/sh/funcs/suspend @@ -0,0 +1,42 @@ +# 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.2 (Berkeley) 5/4/95 +# $FreeBSD$ + +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..6ff6070 --- /dev/null +++ b/bin/sh/histedit.c @@ -0,0 +1,490 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)histedit.c 8.2 (Berkeley) 5/4/95"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/param.h> +#include <paths.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +/* + * Editline and history functions (and glue). + */ +#include "shell.h" +#include "parser.h" +#include "var.h" +#include "options.h" +#include "main.h" +#include "output.h" +#include "mystring.h" +#ifndef NO_HISTORY +#include "myhistedit.h" +#include "error.h" +#include "eval.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). + */ +void +histedit() +{ + +#define editing (Eflag || Vflag) + + if (iflag) { + if (!hist) { + /* + * turn history on + */ + INTOFF; + hist = history_init(); + INTON; + + if (hist != NULL) + sethistsize(histsizeval()); + 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; + } +} + + +void +sethistsize(hs) + const char *hs; +{ + int histsize; + + if (hist != NULL) { + if (hs == NULL || *hs == '\0' || + (histsize = atoi(hs)) < 0) + histsize = 100; + history(hist, H_EVENT, histsize); + } +} + +/* + * This command is provided since POSIX decided to standardize + * the Korn shell fc command. Oh well... + */ +int +histcmd(argc, argv) + int argc; + char **argv; +{ + 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; +#ifdef __GNUC__ + /* Avoid longjmp clobbering */ + (void) &editor; + (void) &lflg; + (void) &nflg; + (void) &rflg; + (void) &sflg; + (void) &firststr; + (void) &laststr; + (void) &pat; + (void) &repl; + (void) &efp; + (void) &argc; + (void) &argv; +#endif + + 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")) != -1) + switch ((char)ch) { + case 'e': + editor = shoptarg; + 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"); + } + } + + /* + * 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; + return 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); +} + +int +not_fcnumber(s) + char *s; +{ + if (s == NULL) + return (0); + if (*s == '-') + s++; + return (!is_number(s)); +} + +int +str_to_event(str, last) + char *str; + int last; +{ + const HistEvent *he; + char *s = str; + int relative = 0; + int i; + + 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); +} +#else +#include "error.h" + +int +histcmd(argc, argv) + int argc; + char **argv; +{ + + error("not compiled with history support"); + /*NOTREACHED*/ + return (0); +} +#endif diff --git a/bin/sh/init.h b/bin/sh/init.h new file mode 100644 index 0000000..366b8a3 --- /dev/null +++ b/bin/sh/init.h @@ -0,0 +1,42 @@ +/*- + * 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.2 (Berkeley) 5/4/95 + * $FreeBSD$ + */ + +void init __P((void)); +void reset __P((void)); +void initshellproc __P((void)); diff --git a/bin/sh/input.c b/bin/sh/input.c new file mode 100644 index 0000000..d75dfa4 --- /dev/null +++ b/bin/sh/input.c @@ -0,0 +1,511 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)input.c 8.3 (Berkeley) 6/9/95"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <stdio.h> /* defines BUFSIZ */ +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> + +/* + * This file implements the input routines used by the parser. + */ + +#include "shell.h" +#include "redir.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; + int prevlleft; + 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 this line */ + int lleft; /* number of lines left in this 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 */ +MKINIT int parselleft; /* copy of parsefile->lleft */ +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 */ +int init_editline = 0; /* editline library initialized? */ +int whichprompt; /* 1 == PS1, 2 == PS2 */ + +EditLine *el; /* cookie for editline package */ + +STATIC void pushfile __P((void)); +static int preadfd __P((void)); + +#ifdef mkinit +INCLUDE "input.h" +INCLUDE "error.h" + +INIT { + extern char basebuf[]; + + basepf.nextc = basepf.buf = basebuf; +} + +RESET { + if (exception != EXSHELLPROC) + parselleft = parsenleft = 0; /* clear input buffer */ + popallfiles(); +} + +SHELLPROC { + popallfiles(); +} +#endif + + +/* + * Read a line from the script. + */ + +char * +pfgets(line, len) + char *line; + int len; +{ + 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(); +} + + +static int +preadfd() +{ + int nr; + parsenextc = parsefile->buf; + +retry: +#ifndef NO_HISTORY + if (parsefile->fd == 0 && el) { + const char *rl_cp; + + rl_cp = el_gets(el, &nr); + if (rl_cp == NULL) + nr = 0; + else { + /* XXX - BUFSIZE should redesign so not necessary */ + (void) strcpy(parsenextc, rl_cp); + } + } else +#endif + nr = read(parsefile->fd, parsenextc, BUFSIZ - 1); + + if (nr <= 0) { + if (nr < 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; + } + } + } + } + nr = -1; + } + return nr; +} + +/* + * 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) If there is more in this buffer, use it else call read to fill it. + * 4) Process input up to the next newline, deleting nul characters. + */ + +int +preadbuffer() +{ + char *p, *q; + int more; + int something; + char savec; + + if (parsefile->strpush) { + popstring(); + if (--parsenleft >= 0) + return (*parsenextc++); + } + if (parsenleft == EOF_NLEFT || parsefile->buf == NULL) + return PEOF; + flushout(&output); + flushout(&errout); + +again: + if (parselleft <= 0) { + if ((parselleft = preadfd()) == -1) { + parselleft = parsenleft = EOF_NLEFT; + return PEOF; + } + } + + q = p = parsenextc; + + /* delete nul characters */ + something = 0; + for (more = 1; more;) { + switch (*p) { + case '\0': + p++; /* Skip nul */ + goto check; + + case '\t': + case ' ': + break; + + case '\n': + parsenleft = q - parsenextc; + more = 0; /* Stop processing here */ + break; + + default: + something = 1; + break; + } + + *q++ = *p++; +check: + if (--parselleft <= 0) { + parsenleft = q - parsenextc - 1; + if (parsenleft < 0) + goto again; + *q = '\0'; + more = 0; + } + } + + savec = *q; + *q = '\0'; + +#ifndef NO_HISTORY + if (parsefile->fd == 0 && hist && something) { + INTOFF; + history(hist, whichprompt == 1 ? H_ENTER : H_ADD, parsenextc); + INTON; + } +#endif + + if (vflag) { + out2str(parsenextc); + flushout(out2); + } + + *q = savec; + + 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->prevlleft = parselleft; + sp->ap = (struct alias *)ap; + if (ap) + ((struct alias *)ap)->flag |= ALIASINUSE; + parsenextc = s; + parsenleft = len; + INTON; +} + +void +popstring() +{ + struct strpush *sp = parsefile->strpush; + + INTOFF; + parsenextc = sp->prevstring; + parsenleft = sp->prevnleft; + parselleft = sp->prevlleft; +/*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 push; +{ + int fd; + int fd2; + + INTOFF; + if ((fd = open(fname, O_RDONLY)) < 0) + error("Can't open %s: %s", fname, strerror(errno)); + 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) + int fd, push; +{ + (void)fcntl(fd, F_SETFD, FD_CLOEXEC); + 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); + parselleft = parsenleft = 0; + plinno = 1; +} + + +/* + * Like setinputfile, but takes input from a string. + */ + +void +setinputstring(string, push) + char *string; + int push; + { + INTOFF; + if (push) + pushfile(); + parsenextc = string; + parselleft = 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->lleft = parselleft; + 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; + parselleft = parsefile->lleft; + 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..16d7244 --- /dev/null +++ b/bin/sh/input.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. + * + * @(#)input.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD$ + */ + +/* 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 */ + +char *pfgets __P((char *, int)); +int pgetc __P((void)); +int preadbuffer __P((void)); +void pungetc __P((void)); +void pushstring __P((char *, int, void *)); +void popstring __P((void)); +void setinputfile __P((char *, int)); +void setinputfd __P((int, int)); +void setinputstring __P((char *, int)); +void popfile __P((void)); +void popallfiles __P((void)); +void closescript __P((void)); + +#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..e437441 --- /dev/null +++ b/bin/sh/jobs.c @@ -0,0 +1,1156 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)jobs.c 8.5 (Berkeley) 5/4/95"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <fcntl.h> +#include <signal.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/param.h> +#ifdef BSD +#include <sys/wait.h> +#include <sys/time.h> +#include <sys/resource.h> +#endif +#include <sys/ioctl.h> + +#include "shell.h" +#if JOBS +#if OLD_TTY_DRIVER +#include "sgtty.h" +#else +#include <termios.h> +#endif +#undef CEOF /* syntax.h redefines this */ +#endif +#include "redir.h" +#include "show.h" +#include "main.h" +#include "parser.h" +#include "nodes.h" +#include "jobs.h" +#include "options.h" +#include "trap.h" +#include "syntax.h" +#include "input.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" + + +struct job *jobtab; /* array of jobs */ +int njobs; /* size of array */ +MKINIT pid_t backgndpid = -1; /* pid of last background process */ +#if JOBS +int initialpgrp; /* pgrp of shell on invocation */ +int curjob; /* current job */ +#endif +int in_waitcmd = 0; /* are we in waitcmd()? */ +int in_dowait = 0; /* are we in dowait()? */ +volatile sig_atomic_t breakwaitcmd = 0; /* should wait be terminated? */ + +#if JOBS +STATIC void restartjob __P((struct job *)); +#endif +STATIC void freejob __P((struct job *)); +STATIC struct job *getjob __P((char *)); +STATIC int dowait __P((int, struct job *)); +#if SYSV +STATIC int onsigchild __P((void)); +#endif +STATIC int waitproc __P((int, int *)); +STATIC void cmdtxt __P((union node *)); +STATIC void cmdputs __P((char *)); + + +/* + * 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; + +#if JOBS +void +setjobctl(on) + int on; +{ +#ifdef OLD_TTY_DRIVER + int ldisc; +#endif + + if (on == jobctl || rootshell == 0) + return; + if (on) { + do { /* while we are in the background */ +#ifdef OLD_TTY_DRIVER + if (ioctl(2, TIOCGPGRP, (char *)&initialpgrp) < 0) { +#else + initialpgrp = tcgetpgrp(2); + if (initialpgrp < 0) { +#endif + out2str("sh: can't access tty; job control turned off\n"); + mflag = 0; + return; + } + if (initialpgrp == -1) + initialpgrp = getpgrp(); + else if (initialpgrp != getpgrp()) { + 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); + setpgid(0, rootpid); +#ifdef OLD_TTY_DRIVER + ioctl(2, TIOCSPGRP, (char *)&rootpid); +#else + tcsetpgrp(2, rootpid); +#endif + } else { /* turning job control off */ + setpgid(0, initialpgrp); +#ifdef OLD_TTY_DRIVER + ioctl(2, TIOCSPGRP, (char *)&initialpgrp); +#else + tcsetpgrp(2, initialpgrp); +#endif + setsignal(SIGTSTP); + setsignal(SIGTTOU); + setsignal(SIGTTIN); + } + jobctl = on; +} +#endif + + +#ifdef mkinit +INCLUDE <sys/types.h> +INCLUDE <stdlib.h> + +SHELLPROC { + backgndpid = -1; +#if JOBS + jobctl = 0; +#endif +} + +#endif + + + +#if JOBS +int +fgcmd(argc, argv) + int argc __unused; + 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; +#ifdef OLD_TTY_DRIVER + ioctl(2, TIOCSPGRP, (char *)&pgrp); +#else + tcsetpgrp(2, pgrp); +#endif + restartjob(jp); + INTOFF; + status = waitforjob(jp, (int *)NULL); + INTON; + return status; +} + + +int +bgcmd(argc, argv) + int argc; + 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 (WIFSTOPPED(ps->status)) { + ps->status = -1; + jp->state = 0; + } + } + INTON; +} +#endif + + +int +jobscmd(argc, argv) + int argc __unused; + char **argv __unused; +{ + 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 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 (WIFEXITED(ps->status)) { + fmtstr(s, 64, "Exit %d", WEXITSTATUS(ps->status)); + } else { +#if JOBS + if (WIFSTOPPED(ps->status)) + i = WSTOPSIG(ps->status); + else +#endif + i = WTERMSIG(ps->status); + if ((i & 0x7F) < NSIG && sys_siglist[i & 0x7F]) + scopy(sys_siglist[i & 0x7F], s); + else + fmtstr(s, 64, "Signal %d", i & 0x7F); + if (WCOREDUMP(ps->status)) + 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) + int argc; + char **argv; +{ + struct job *job; + int status, retval; + struct job *jp; + + if (argc > 1) { + job = getjob(argv[1]); + } else { + job = NULL; + } + + /* + * Loop until a process is terminated or stopped, or a SIGINT is + * received. + */ + + in_waitcmd++; + do { + if (job != NULL) { + if (job->state) { + status = job->ps[job->nprocs - 1].status; + if (WIFEXITED(status)) + retval = WEXITSTATUS(status); +#if JOBS + else if (WIFSTOPPED(status)) + retval = WSTOPSIG(status) + 128; +#endif + else + retval = WTERMSIG(status) + 128; + if (! iflag) + freejob(job); + in_waitcmd--; + return retval; + } + } else { + for (jp = jobtab ; ; jp++) { + if (jp >= jobtab + njobs) { /* no running procs */ + in_waitcmd--; + return 0; + } + if (jp->used && jp->state == 0) + break; + } + } + } while (dowait(1, (struct job *)NULL) != -1); + in_waitcmd--; + + return 0; +} + + + +int +jobidcmd(argc, argv) + int argc __unused; + 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; + 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 { + 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); + /*NOTREACHED*/ + return NULL; +} + + + +/* + * Return a new job structure, + */ + +struct job * +makejob(node, nprocs) + union node *node __unused; + int nprocs; +{ + 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]); + memcpy(jp, jobtab, njobs * sizeof jp[0]); + /* Relocate `ps' pointers */ + for (i = 0; i < njobs; i++) + if (jp[i].ps == &jobtab[i].ps0) + jp[i].ps = &jp[i].ps0; + 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%lx, %d) returns %%%d\n", (long)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 mode; +{ + int pid; + int pgrp; + + TRACE(("forkshell(%%%d, 0x%lx, %d) called\n", jp - jobtab, (long)n, + mode)); + INTOFF; + pid = fork(); + if (pid == -1) { + TRACE(("Fork failed, errno=%d\n", errno)); + INTON; + error("Cannot fork: %s", strerror(errno)); + } + 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; + if (setpgid(0, pgrp) == 0 && mode == FORK_FG) { + /*** this causes superfluous TIOCSPGRPS ***/ +#ifdef OLD_TTY_DRIVER + if (ioctl(2, TIOCSPGRP, (char *)&pgrp) < 0) + error("TIOCSPGRP failed, errno=%d", errno); +#else + if (tcsetpgrp(2, pgrp) < 0) + error("tcsetpgrp failed, errno=%d", errno); +#endif + } + 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: %s", + strerror(errno)); + } + } +#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: %s", + strerror(errno)); + } + } +#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; + setpgid(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 + * foreground 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, origstatus) + struct job *jp; + int *origstatus; +{ +#if JOBS + int mypgrp = getpgrp(); +#endif + int status; + int st; + + INTOFF; + TRACE(("waitforjob(%%%d) called\n", jp - jobtab + 1)); + while (jp->state == 0) + if (dowait(1, jp) == -1) + dotrap(); +#if JOBS + if (jp->jobctl) { +#ifdef OLD_TTY_DRIVER + if (ioctl(2, TIOCSPGRP, (char *)&mypgrp) < 0) + error("TIOCSPGRP failed, errno=%d\n", errno); +#else + if (tcsetpgrp(2, mypgrp) < 0) + error("tcsetpgrp failed, errno=%d\n", errno); +#endif + } + if (jp->state == JOBSTOPPED) + curjob = jp - jobtab + 1; +#endif + status = jp->ps[jp->nprocs - 1].status; + if (origstatus != NULL) + *origstatus = status; + /* convert to 8 bits */ + if (WIFEXITED(status)) + st = WEXITSTATUS(status); +#if JOBS + else if (WIFSTOPPED(status)) + st = WSTOPSIG(status) + 128; +#endif + else + st = WTERMSIG(status) + 128; + if (! JOBS || jp->state == JOBDONE) + freejob(jp); + if (int_pending()) { + if (WIFSIGNALED(status) && WTERMSIG(status) == SIGINT) + kill(getpid(), SIGINT); + else + CLEAR_PENDING_INT; + } + INTON; + return st; +} + + + +/* + * Wait for a process to terminate. + */ + +STATIC int +dowait(block, job) + int block; + struct job *job; +{ + int pid; + int status; + struct procstat *sp; + struct job *jp; + struct job *thisjob; + int done; + int stopped; + int core; + int sig; + + in_dowait++; + 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 && breakwaitcmd == 0); + in_dowait--; + if (breakwaitcmd != 0) { + breakwaitcmd = 0; + return -1; + } + 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(("Changing 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 (WIFSTOPPED(sp->status)) + 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)) { + core = WCOREDUMP(status); +#if JOBS + if (WIFSTOPPED(status)) + sig = WSTOPSIG(status); + else +#endif + if (WIFEXITED(status)) + sig = 0; + else + sig = WTERMSIG(status); + + if (sig != 0 && sig != SIGINT && sig != SIGPIPE) { + if (thisjob != job) + outfmt(out2, "%d: ", pid); +#if JOBS + if (sig == SIGTSTP && rootshell && iflag) + outfmt(out2, "%%%d ", job - jobtab + 1); +#endif + if (sig < NSIG && sys_siglist[sig]) + out2str(sys_siglist[sig]); + else + outfmt(out2, "Signal %d", sig); + if (core) + out2str(" - core dumped"); + out2c('\n'); + flushout(&errout); + } else { + TRACE(("Not printing status: status=%d, sig=%d\n", + status, sig)); + } + } 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 sig_atomic_t gotsigchild; + +STATIC int onsigchild() { + gotsigchild = 1; +} +#endif + + +STATIC int +waitproc(block, status) + int block; + 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() +{ + int jobno; + 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; +#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 NFROMTO: + 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; + { + char *p, *q; + 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..e8274b0 --- /dev/null +++ b/bin/sh/jobs.h @@ -0,0 +1,100 @@ +/*- + * 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.2 (Berkeley) 5/4/95 + * $FreeBSD$ + */ + +/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ +#define FORK_FG 0 +#define FORK_BG 1 +#define FORK_NOJOB 2 + +#include <signal.h> /* for sig_atomic_t */ + +/* + * 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 { + pid_t pid; /* process id */ + int 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 pid_t backgndpid; /* pid of last background process */ +extern int job_warning; /* user was warned about stopped jobs */ +extern int in_waitcmd; /* are we in waitcmd()? */ +extern int in_dowait; /* are we in dowait()? */ +extern volatile sig_atomic_t breakwaitcmd; /* break wait to process traps? */ + +void setjobctl __P((int)); +int fgcmd __P((int, char **)); +int bgcmd __P((int, char **)); +int jobscmd __P((int, char **)); +void showjobs __P((int)); +int waitcmd __P((int, char **)); +int jobidcmd __P((int, char **)); +struct job *makejob __P((union node *, int)); +int forkshell __P((struct job *, union node *, int)); +int waitforjob __P((struct job *, int *)); +int stoppedjobs __P((void)); +char *commandtext __P((union node *)); + +#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..20aadd0 --- /dev/null +++ b/bin/sh/machdep.h @@ -0,0 +1,52 @@ +/*- + * 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.2 (Berkeley) 5/4/95 + * $FreeBSD$ + */ + +/* + * 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..826e89a --- /dev/null +++ b/bin/sh/mail.c @@ -0,0 +1,120 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)mail.c 8.2 (Berkeley) 5/4/95"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#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) + int silent; +{ + int i; + char *mpath; + char *p; + 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..319796b --- /dev/null +++ b/bin/sh/mail.h @@ -0,0 +1,40 @@ +/*- + * 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.2 (Berkeley) 5/4/95 + * $FreeBSD$ + */ + +void chkmail __P((int)); diff --git a/bin/sh/main.c b/bin/sh/main.c new file mode 100644 index 0000000..7d00586 --- /dev/null +++ b/bin/sh/main.c @@ -0,0 +1,390 @@ +/*- + * 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 const copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 5/28/95"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <stdio.h> +#include <signal.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <locale.h> +#include <errno.h> + +#include "shell.h" +#include "main.h" +#include "mail.h" +#include "options.h" +#include "output.h" +#include "parser.h" +#include "nodes.h" +#include "expand.h" +#include "eval.h" +#include "jobs.h" +#include "input.h" +#include "trap.h" +#include "var.h" +#include "show.h" +#include "memalloc.h" +#include "error.h" +#include "init.h" +#include "mystring.h" +#include "exec.h" +#include "cd.h" + +#define PROFILE 0 + +int rootpid; +int rootshell; +#if PROFILE +short profile_buf[16384]; +extern int etext(); +#endif + +STATIC void read_profile __P((char *)); +STATIC char *find_dot_file __P((char *)); + +/* + * 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. + */ + +int +main(argc, argv) + int argc; + 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 + (void) setlocale(LC_ALL, ""); + 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. + */ + switch (exception) { + case EXSHELLPROC: + rootpid = getpid(); + rootshell = 1; + minusc = NULL; + state = 3; + break; + + case EXEXEC: + exitstatus = exerrno; + break; + + case EXERROR: + exitstatus = 2; + break; + + default: + break; + } + + if (exception != EXSHELLPROC) { + if (state == 0 || iflag == 0 || ! rootshell) + exitshell(exitstatus); + } + 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 (getpwd() == NULL && iflag) + out2str("sh: cannot determine working directory\n"); + if (argv[0] && argv[0][0] == '-') { + state = 1; + read_profile("/etc/profile"); +state1: + state = 2; + if (privileged == 0) + read_profile(".profile"); + else + read_profile("/etc/suid_profile"); + } +state2: + state = 3; + if (!privileged && iflag) { + 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); + /*NOTREACHED*/ + return 0; +} + + +/* + * 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) + int 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); + setstackmark(&smark); + if (evalskip == SKIPFILE) { + evalskip = 0; + break; + } + } + popstackmark(&smark); +} + + + +/* + * 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: %s", name, strerror(errno)); + INTON; + cmdloop(0); + popfile(); +} + + + +/* + * Take commands from a file. To be compatible we should do a path + * search for the file, which is necessary to find sub-commands. + */ + + +STATIC char * +find_dot_file(basename) + char *basename; +{ + static char localname[FILENAME_MAX+1]; + char *fullname; + char *path = pathval(); + struct stat statb; + + /* don't try this for absolute or relative paths */ + if( strchr(basename, '/')) + return basename; + + while ((fullname = padvance(&path, basename)) != NULL) { + strcpy(localname, fullname); + stunalloc(fullname); + if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) + return localname; + } + return basename; +} + +int +dotcmd(argc, argv) + int argc; + char **argv; +{ + struct strlist *sp; + exitstatus = 0; + + for (sp = cmdenviron; sp ; sp = sp->next) + setvareq(savestr(sp->text), VSTRFIXED|VTEXTFIXED); + + if (argc >= 2) { /* That's what SVR2 does */ + char *fullname = find_dot_file(argv[1]); + + setinputfile(fullname, 1); + commandname = fullname; + cmdloop(0); + popfile(); + } + return exitstatus; +} + + +int +exitcmd(argc, argv) + int argc; + char **argv; +{ + extern int oexitstatus; + + if (stoppedjobs()) + return 0; + if (argc > 1) + exitstatus = number(argv[1]); + else + exitstatus = oexitstatus; + exitshell(exitstatus); + /*NOTREACHED*/ + return 0; +} + + +#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..3dc3dd7 --- /dev/null +++ b/bin/sh/main.h @@ -0,0 +1,46 @@ +/*- + * 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.2 (Berkeley) 5/4/95 + * $FreeBSD$ + */ + +extern int rootpid; /* pid of main shell */ +extern int rootshell; /* true if we aren't a child of the main shell */ + +void readcmdfile __P((char *)); +void cmdloop __P((int)); +int dotcmd __P((int, char **)); +int exitcmd __P((int, char **)); diff --git a/bin/sh/memalloc.c b/bin/sh/memalloc.c new file mode 100644 index 0000000..c106775 --- /dev/null +++ b/bin/sh/memalloc.c @@ -0,0 +1,335 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)memalloc.c 8.3 (Berkeley) 5/4/95"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include "shell.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "machdep.h" +#include "mystring.h" +#include "expand.h" +#include <stdlib.h> +#include <unistd.h> + +/* + * Like malloc, but returns an error when out of space. + */ + +pointer +ckmalloc(nbytes) + int nbytes; +{ + pointer p; + + if ((p = malloc(nbytes)) == NULL) + error("Out of space"); + return p; +} + + +/* + * Same for realloc. + */ + +pointer +ckrealloc(p, nbytes) + pointer p; + int nbytes; +{ + 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; +{ + 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; +struct stackmark *markp; +char *stacknxt = stackbase.space; +int stacknleft = MINSIZE; +int sstrnleft; +int herefd = -1; + + + +pointer +stalloc(nbytes) + int nbytes; +{ + 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; + mark->marknext = markp; + markp = mark; +} + + +void +popstackmark(mark) + struct stackmark *mark; +{ + struct stack_block *sp; + + INTOFF; + markp = mark->marknext; + 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; + char *oldspace; + int oldlen; + struct stack_block *sp; + struct stack_block *oldstackp; + + newlen = ALIGN(stacknleft * 2 + 100); + oldspace = stacknxt; + oldlen = stacknleft; + + if (stacknxt == stackp->space && stackp != &stackbase) { + INTOFF; + oldstackp = stackp; + 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; + { + /* Stack marks pointing to the start of the old block + * must be relocated to point to the new block + */ + struct stackmark *xmark; + xmark = markp; + while (xmark != NULL && xmark->stackp == oldstackp) { + xmark->stackp = stackp; + xmark->stacknxt = stacknxt; + xmark->stacknleft = stacknleft; + xmark = xmark->marknext; + } + } + INTON; + } else { + p = stalloc(newlen); + memcpy(p, oldspace, oldlen); + stacknxt = p; /* free the space */ + stacknleft += newlen; /* we just allocated */ + } +} + + + +void +grabstackblock(len) + int 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; + + 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; + + 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..e3ca8be --- /dev/null +++ b/bin/sh/memalloc.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. + * + * @(#)memalloc.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD$ + */ + +struct stackmark { + struct stack_block *stackp; + char *stacknxt; + int stacknleft; + struct stackmark *marknext; +}; + + +extern char *stacknxt; +extern int stacknleft; +extern int sstrnleft; +extern int herefd; + +pointer ckmalloc __P((int)); +pointer ckrealloc __P((pointer, int)); +char *savestr __P((char *)); +pointer stalloc __P((int)); +void stunalloc __P((pointer)); +void setstackmark __P((struct stackmark *)); +void popstackmark __P((struct stackmark *)); +void growstackblock __P((void)); +void grabstackblock __P((int)); +char *growstackstr __P((void)); +char *makestrspace __P((void)); +void ungrabstackstr __P((char *, char *)); + + + +#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(); } +#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..46d3950 --- /dev/null +++ b/bin/sh/miscbltin.c @@ -0,0 +1,464 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)miscbltin.c 8.4 (Berkeley) 5/4/95"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +/* + * Miscellaneous builtins. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <unistd.h> +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <termios.h> + +#include "shell.h" +#include "options.h" +#include "var.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" + +#undef eflag + +/* + * The read builtin. The -r option causes backslashes to be treated like + * ordinary characters. + * + * This uses unbuffered input, which may be avoidable in some cases. + */ + +int +readcmd(argc, argv) + int argc __unused; + char **argv __unused; +{ + char **ap; + int backslash; + char c; + int rflag; + char *prompt; + char *ifs; + char *p; + int startword; + int status; + int i; + struct timeval tv; + char *tvptr; + fd_set ifds; + struct termios told, tnew; + int tsaved; + + rflag = 0; + prompt = NULL; + tv.tv_sec = -1; + tv.tv_usec = 0; + while ((i = nextopt("erp:t:")) != '\0') { + switch(i) { + case 'p': + prompt = shoptarg; + break; + case 'e': + break; + case 'r': + rflag = 1; + break; + case 't': + tv.tv_sec = strtol(shoptarg, &tvptr, 0); + if (tvptr == shoptarg) + error("timeout value"); + switch(*tvptr) { + case 0: + case 's': + break; + case 'h': + tv.tv_sec *= 60; + /* FALLTHROUGH */ + case 'm': + tv.tv_sec *= 60; + break; + default: + error("timeout unit"); + } + break; + } + } + if (prompt && isatty(0)) { + out2str(prompt); + flushall(); + } + if (*(ap = argptr) == NULL) + error("arg count"); + if ((ifs = bltinlookup("IFS", 1)) == NULL) + ifs = nullstr; + + if (tv.tv_sec >= 0) { + /* + * See if we can disable input processing; this will + * not give the desired result if we are in a pipeline + * and someone upstream is still in line-by-line mode. + */ + tsaved = 0; + if (tcgetattr(0, &told) == 0) { + memcpy(&tnew, &told, sizeof(told)); + cfmakeraw(&tnew); + tcsetattr(0, TCSANOW, &tnew); + tsaved = 1; + } + /* + * Wait for something to become available. + */ + FD_ZERO(&ifds); + FD_SET(0, &ifds); + status = select(1, &ifds, NULL, NULL, &tv); + if (tsaved) + tcsetattr(0, TCSANOW, &told); + /* + * If there's nothing ready, return an error. + */ + if (status <= 0) + return(1); + } + + 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 (!rflag && 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; +} + + + +int +umaskcmd(argc, argv) + int argc __unused; + char **argv; +{ + char *ap; + int mask; + int i; + int symbolic_mode = 0; + + while ((i = nextopt("S")) != '\0') { + symbolic_mode = 1; + } + + INTOFF; + mask = umask(0); + umask(mask); + INTON; + + if ((ap = *argptr) == NULL) { + if (symbolic_mode) { + char u[4], g[4], o[4]; + + i = 0; + if ((mask & S_IRUSR) == 0) + u[i++] = 'r'; + if ((mask & S_IWUSR) == 0) + u[i++] = 'w'; + if ((mask & S_IXUSR) == 0) + u[i++] = 'x'; + u[i] = '\0'; + + i = 0; + if ((mask & S_IRGRP) == 0) + g[i++] = 'r'; + if ((mask & S_IWGRP) == 0) + g[i++] = 'w'; + if ((mask & S_IXGRP) == 0) + g[i++] = 'x'; + g[i] = '\0'; + + i = 0; + if ((mask & S_IROTH) == 0) + o[i++] = 'r'; + if ((mask & S_IWOTH) == 0) + o[i++] = 'w'; + if ((mask & S_IXOTH) == 0) + o[i++] = 'x'; + o[i] = '\0'; + + out1fmt("u=%s,g=%s,o=%s\n", u, g, o); + } else { + out1fmt("%.4o\n", mask); + } + } else { + if (isdigit(*ap)) { + mask = 0; + do { + if (*ap >= '8' || *ap < '0') + error("Illegal number: %s", argv[1]); + mask = (mask << 3) + (*ap - '0'); + } while (*++ap != '\0'); + umask(mask); + } else { + void *set; + if ((set = setmode (ap)) == 0) + error("Illegal number: %s", ap); + + mask = getmode (set, ~mask & 0777); + umask(~mask & 0777); + free(set); + } + } + return 0; +} + +/* + * ulimit builtin + * + * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and + * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with + * ash by J.T. Conklin. + * + * Public domain. + */ + +struct limits { + const char *name; + const char *units; + int cmd; + int factor; /* multiply by to get rlim_{cur,max} values */ + char option; +}; + +static const struct limits limits[] = { +#ifdef RLIMIT_CPU + { "cpu time", "seconds", RLIMIT_CPU, 1, 't' }, +#endif +#ifdef RLIMIT_FSIZE + { "file size", "512-blocks", RLIMIT_FSIZE, 512, 'f' }, +#endif +#ifdef RLIMIT_DATA + { "data seg size", "kbytes", RLIMIT_DATA, 1024, 'd' }, +#endif +#ifdef RLIMIT_STACK + { "stack size", "kbytes", RLIMIT_STACK, 1024, 's' }, +#endif +#ifdef RLIMIT_CORE + { "core file size", "512-blocks", RLIMIT_CORE, 512, 'c' }, +#endif +#ifdef RLIMIT_RSS + { "max memory size", "kbytes", RLIMIT_RSS, 1024, 'm' }, +#endif +#ifdef RLIMIT_MEMLOCK + { "locked memory", "kbytes", RLIMIT_MEMLOCK, 1024, 'l' }, +#endif +#ifdef RLIMIT_NPROC + { "max user processes", (char *)0, RLIMIT_NPROC, 1, 'u' }, +#endif +#ifdef RLIMIT_NOFILE + { "open files", (char *)0, RLIMIT_NOFILE, 1, 'n' }, +#endif +#ifdef RLIMIT_VMEM + { "virtual mem size", "kbytes", RLIMIT_VMEM, 1024, 'v' }, +#endif +#ifdef RLIMIT_SWAP + { "swap limit", "kbytes", RLIMIT_SWAP, 1024, 'w' }, +#endif +#ifdef RLIMIT_SBSIZE + { "sbsize", "bytes", RLIMIT_SBSIZE, 1, 'b' }, +#endif + { (char *) 0, (char *)0, 0, 0, '\0' } +}; + +int +ulimitcmd(argc, argv) + int argc __unused; + char **argv __unused; +{ + int c; + quad_t val = 0; + enum { SOFT = 0x1, HARD = 0x2 } + how = SOFT | HARD; + const struct limits *l; + int set, all = 0; + int optc, what; + struct rlimit limit; + + what = 'f'; + while ((optc = nextopt("HSatfdsmcnulb")) != '\0') + switch (optc) { + case 'H': + how = HARD; + break; + case 'S': + how = SOFT; + break; + case 'a': + all = 1; + break; + default: + what = optc; + } + + for (l = limits; l->name && l->option != what; l++) + ; + if (!l->name) + error("ulimit: internal error (%c)", what); + + set = *argptr ? 1 : 0; + if (set) { + char *p = *argptr; + + if (all || argptr[1]) + error("ulimit: too many arguments"); + if (strcmp(p, "unlimited") == 0) + val = RLIM_INFINITY; + else { + val = (quad_t) 0; + + while ((c = *p++) >= '0' && c <= '9') + { + val = (val * 10) + (long)(c - '0'); + if (val < (quad_t) 0) + break; + } + if (c) + error("ulimit: bad number"); + val *= l->factor; + } + } + if (all) { + for (l = limits; l->name; l++) { + char optbuf[40]; + if (getrlimit(l->cmd, &limit) < 0) + error("ulimit: can't get limit: %s", strerror(errno)); + if (how & SOFT) + val = limit.rlim_cur; + else if (how & HARD) + val = limit.rlim_max; + + if (l->units) + snprintf(optbuf, sizeof(optbuf), + "(%s, -%c) ", l->units, l->option); + else + snprintf(optbuf, sizeof(optbuf), + "(-%c) ", l->option); + out1fmt("%-18s %18s ", l->name, optbuf); + if (val == RLIM_INFINITY) + out1fmt("unlimited\n"); + else + { + val /= l->factor; + out1fmt("%qd\n", (quad_t) val); + } + } + return 0; + } + + if (getrlimit(l->cmd, &limit) < 0) + error("ulimit: can't get limit: %s", strerror(errno)); + if (set) { + if (how & SOFT) + limit.rlim_cur = val; + if (how & HARD) + limit.rlim_max = val; + if (setrlimit(l->cmd, &limit) < 0) + error("ulimit: bad limit: %s", strerror(errno)); + } else { + if (how & SOFT) + val = limit.rlim_cur; + else if (how & HARD) + val = limit.rlim_max; + + if (val == RLIM_INFINITY) + out1fmt("unlimited\n"); + else + { + val /= l->factor; + out1fmt("%qd\n", (quad_t) val); + } + } + return 0; +} diff --git a/bin/sh/mkbuiltins b/bin/sh/mkbuiltins new file mode 100755 index 0000000..89f50d2 --- /dev/null +++ b/bin/sh/mkbuiltins @@ -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. +# +# @(#)mkbuiltins 8.2 (Berkeley) 5/4/95 +# $FreeBSD$ + +temp=/tmp/ka$$ +havejobs=0 +if grep '^#define JOBS[ ]*1' shell.h > /dev/null +then havejobs=1 +fi +havehist=1 +if [ "X$1" = "X-h" ]; then + havehist=0 + shift +fi +objdir=$1 +exec > ${objdir}/builtins.c +cat <<\! +/* + * This file was generated by the mkbuiltins program. + */ + +#include "shell.h" +#include "builtins.h" + +! +awk '/^[^#]/ {if(('$havejobs' || $2 != "-j") && ('$havehist' || $2 != "-h")) \ + print $0}' builtins.def | sed 's/-j//' > $temp +awk '{ printf "int %s();\n", $1}' $temp +echo ' +int (*const builtinfunc[])() = {' +awk '/^[^#]/ { printf "\t%s,\n", $1}' $temp +echo '}; + +const struct builtincmd builtincmd[] = {' +awk '{ for (i = 2 ; i <= NF ; i++) { + printf "\t{ \"%s\", %d },\n", $i, NR-1 + }}' $temp +echo ' { NULL, 0 } +};' + +exec > ${objdir}/builtins.h +cat <<\! +/* + * This file was generated by the mkbuiltins program. + */ + +#include <sys/cdefs.h> +! +tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ < $temp | + awk '{ printf "#define %s %d\n", $1, NR-1}' +echo ' +struct builtincmd { + char *name; + int code; +}; + +extern int (*const builtinfunc[])(); +extern const struct builtincmd builtincmd[];' +rm -f $temp diff --git a/bin/sh/mkinit.c b/bin/sh/mkinit.c new file mode 100644 index 0000000..b8f8ffb --- /dev/null +++ b/bin/sh/mkinit.c @@ -0,0 +1,518 @@ +/*- + * 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 const copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)mkinit.c 8.2 (Berkeley) 5/4/95"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#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. + * + * Usage: mkinit sourcefile... + */ + + +#include <sys/cdefs.h> +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> + + +/* + * OUTFILE is the name of the output file. Output is initially written + * to the file OUTTEMP, which is then moved to OUTFILE. + */ + +#define OUTFILE "init.c" +#define OUTTEMP "init.c.new" + + +/* + * 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 __P((char *)); +int match __P((char *, char *)); +int gooddefine __P((char *)); +void doevent __P((struct event *, FILE *, char *)); +void doinclude __P((char *)); +void dodecl __P((char *, FILE *)); +void output __P((void)); +void addstr __P((char *, struct text *)); +void addchar __P((int, struct text *)); +void writetext __P((struct text *, FILE *)); +FILE *ckfopen __P((char *, char *)); +void *ckmalloc __P((int)); +char *savestr __P((char *)); +void error __P((char *)); + +#define equal(s1, s2) (strcmp(s1, s2) == 0) + +int +main(argc, argv) + int argc __unused; + char **argv; +{ + char **ap; + + header_files[0] = "\"shell.h\""; + header_files[1] = "\"mystring.h\""; + for (ap = argv + 1 ; *ap ; ap++) + readfile(*ap); + output(); + rename(OUTTEMP, OUTFILE); + exit(0); +} + + +/* + * 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)) { + char *cp; + char line2[1024]; + static const char undef[] = "#undef "; + + strcpy(line2, line); + memcpy(line2, undef, sizeof(undef) - 1); + cp = line2 + sizeof(undef) - 1; + while(*cp && (*cp == ' ' || *cp == '\t')) + cp++; + while(*cp && *cp != ' ' && *cp != '\t' && *cp != '\n') + cp++; + *cp++ = '\n'; *cp = '\0'; + addstr(line2, &defines); + addstr(line, &defines); + } + } + fclose(fp); +} + + +int +match(name, line) + char *name; + char *line; +{ + 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; +{ + 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) + 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; + { + char *p; + char *name; + 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]; + 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 && strchr("=/\n", *p) == NULL; p++) + continue; + 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); +} + + +/* + * 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) + char *s; + struct text *text; + { + while (*s) { + if (--text->nleft < 0) + addchar(*s++, text); + else + *text->nextc++ = *s++; + } +} + + +void +addchar(c, text) + int c; + 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: %s\n", file, strerror(errno)); + exit(2); + } + return fp; +} + +void * +ckmalloc(nbytes) + int nbytes; +{ + char *p; + + if ((p = malloc(nbytes)) == NULL) + error("Out of space"); + return p; +} + +char * +savestr(s) + char *s; + { + 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..007cb32 --- /dev/null +++ b/bin/sh/mknodes.c @@ -0,0 +1,480 @@ +/*- + * 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 const copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)mknodes.c 8.2 (Berkeley) 5/4/95"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#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> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + + +#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 */ +}; + + +static int ntypes; /* number of node types */ +static char *nodename[MAXTYPES]; /* names of the nodes */ +static struct str *nodestr[MAXTYPES]; /* type of structure used by the node */ +static int nstr; /* number of structures */ +static struct str str[MAXTYPES]; /* the structures */ +static struct str *curstr; /* current structure */ +static FILE *infp = stdin; +static char line[1024]; +static int linno; +static char *linep; + +static void parsenode __P((void)); +static void parsefield __P((void)); +static void output __P((char *)); +static void outsizes __P((FILE *)); +static void outfunc __P((FILE *, int)); +static void indent __P((int, FILE *)); +static int nextfield __P((char *)); +static void skipbl __P((void)); +static int readline __P((void)); +static void error __P((const char *, ...)); +static char *savestr __P((const char *)); + + +int +main(argc, argv) + int argc; + char **argv; +{ + if (argc != 3) + error("usage: mknodes file"); + if ((infp = fopen(argv[1], "r")) == NULL) + error("Can't open %s: %s", argv[1], strerror(errno)); + while (readline()) { + if (line[0] == ' ' || line[0] == '\t') + parsefield(); + else if (line[0] != '\0') + parsenode(); + } + output(argv[2]); + exit(0); +} + + + +static void +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 (strcmp(sp->tag, tag) == 0) + break; + } + if (sp >= str + nstr) { + sp->tag = savestr(tag); + sp->nfields = 0; + curstr = sp; + nstr++; + } + nodestr[ntypes] = sp; + ntypes++; +} + + +static void +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 (strcmp(type, "nodeptr") == 0) { + fp->type = T_NODE; + sprintf(decl, "union node *%s", name); + } else if (strcmp(type, "nodelist") == 0) { + fp->type = T_NODELIST; + sprintf(decl, "struct nodelist *%s", name); + } else if (strcmp(type, "string") == 0) { + fp->type = T_STRING; + sprintf(decl, "char *%s", name); + } else if (strcmp(type, "int") == 0) { + fp->type = T_INT; + sprintf(decl, "int %s", name); + } else if (strcmp(type, "other") == 0) { + fp->type = T_OTHER; + } else if (strcmp(type, "temp") == 0) { + 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"; + +static void +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: %s", file, strerror(errno)); + if ((hfile = fopen("nodes.h", "w")) == NULL) + error("Can't create nodes.h: %s", strerror(errno)); + 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 (strcmp(p, "%SIZES\n") == 0) + outsizes(cfile); + else if (strcmp(p, "%CALCSIZE\n") == 0) + outfunc(cfile, 1); + else if (strcmp(p, "%COPY\n") == 0) + outfunc(cfile, 0); + else + fputs(line, cfile); + } +} + + + +static void +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"); +} + + +static void +outfunc(cfile, calcsize) + FILE *cfile; + int calcsize; +{ + 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 = (char *)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); +} + + +static void +indent(amount, fp) + int amount; + FILE *fp; +{ + while (amount >= 8) { + putc('\t', fp); + amount -= 8; + } + while (--amount >= 0) { + putc(' ', fp); + } +} + + +static int +nextfield(buf) + char *buf; +{ + 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); +} + + +static void +skipbl() +{ + while (*linep == ' ' || *linep == '\t') + linep++; +} + + +static int +readline() +{ + 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; +} + + + +static void +#ifdef __STDC__ +error(const char *msg, ...) +#else +error(va_alist) + va_dcl +#endif +{ + va_list va; +#ifdef __STDC__ + va_start(va, msg); +#else + char *msg; + va_start(va); + msg = va_arg(va, char *); +#endif + + (void) fprintf(stderr, "line %d: ", linno); + (void) vfprintf(stderr, msg, va); + (void) fputc('\n', stderr); + + va_end(va); + + exit(2); +} + + + +static char * +savestr(s) + const char *s; +{ + char *p; + + if ((p = malloc(strlen(s) + 1)) == NULL) + error("Out of space"); + (void) strcpy(p, s); + return p; +} diff --git a/bin/sh/mksyntax.c b/bin/sh/mksyntax.c new file mode 100644 index 0000000..3f7f8cb --- /dev/null +++ b/bin/sh/mksyntax.c @@ -0,0 +1,403 @@ +/*- + * 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 const copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)mksyntax.c 8.2 (Berkeley) 5/4/95"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +/* + * This program creates syntax.h and syntax.c. + */ + +#include <stdio.h> +#include <string.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 } +}; + +static char writer[] = "\ +/*\n\ + * This file was generated by the mksyntax program.\n\ + */\n\ +\n"; + + +static FILE *cfile; +static FILE *hfile; +static char *syntax[513]; +static int base; +static int size; /* number of values which a char variable can have */ +static int nbits; /* number of bits in a character */ +static int digit_contig;/* true if digits are contiguous */ + +static void filltable __P((char *)); +static void init __P((void)); +static void add __P((char *, char *)); +static void print __P((char *)); +static void output_type_macros __P((void)); +static void digit_convert __P((void)); + +int +main(argc, argv) + int argc __unused; + char **argv __unused; +{ + 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; + } +#if 0 + printf("%s %d bit chars\n", sign? "signed" : "unsigned", nbits); +#endif + 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); + fputs("#include <ctype.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"); + /* ':/' for tilde expansion, '-' for [a\-x] pattern ranges */ + add("!*?[=~:/-", "CCTL"); + print("dqsyntax"); + init(); + fputs("\n/* syntax table used when in single quotes */\n", cfile); + add("\n", "CNL"); + add("'", "CENDQUOTE"); + /* ':/' for tilde expansion, '-' for [a\-x] pattern ranges */ + add("!*?[=~:/-", "CCTL"); + 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. + */ + +static void +filltable(dftval) + char *dftval; +{ + int i; + + for (i = 0 ; i < size ; i++) + syntax[i] = dftval; +} + + +/* + * Initialize the syntax table with default values. + */ + +static void +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"; + syntax[base + CTLQUOTEMARK] = "CCTL"; +} + + +/* + * Add entries to the syntax table. + */ + +static void +add(p, type) + char *p, *type; +{ + while (*p) + syntax[*p++ + base] = type; +} + + + +/* + * Output the syntax table. + */ + +static void +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. + */ + +static char *macro[] = { + "#define is_digit(c)\t((c >= 0 && is_type+SYNBASE)[c] & ISDIGIT)", + "#define is_alpha(c)\t((c) != PEOF && ((c) < CTLESC || (c) > CTLENDARI) && isalpha((unsigned char) (c)))", + "#define is_name(c)\t((c) != PEOF && ((c) < CTLESC || (c) > CTLENDARI) && ((c) == '_' || isalpha((unsigned char) (c))))", + "#define is_in_name(c)\t((c) != PEOF && ((c) < CTLESC || (c) > CTLENDARI) && ((c) == '_' || isalnum((unsigned char) (c))))", + "#define is_special(c)\t((is_type+SYNBASE)[c] & (ISSPECL|ISDIGIT))", + NULL +}; + +static void +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). + */ + +static void +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..172bb23 --- /dev/null +++ b/bin/sh/mktokens @@ -0,0 +1,95 @@ +#!/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 +# $FreeBSD$ + +# 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.h +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..e60bf5a --- /dev/null +++ b/bin/sh/myhistedit.h @@ -0,0 +1,48 @@ +/*- + * 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.2 (Berkeley) 5/4/95 + * $FreeBSD$ + */ + +#include <histedit.h> + +extern History *hist; +extern EditLine *el; +extern int displayhist; + +void histedit __P((void)); +void sethistsize __P((const char *)); +int histcmd __P((int, char **)); +int not_fcnumber __P((char *)); +int str_to_event __P((char *, int)); + diff --git a/bin/sh/mystring.c b/bin/sh/mystring.c new file mode 100644 index 0000000..8013f7d --- /dev/null +++ b/bin/sh/mystring.c @@ -0,0 +1,141 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)mystring.c 8.2 (Berkeley) 5/4/95"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#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 <stdlib.h> +#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) + char const *from; + char *to; + 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) + char const *pfx; + 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)) + error("Illegal number: %s", (char *)s); + return atoi(s); +} + + + +/* + * Check for a valid number. This should be elsewhere. + */ + +int +is_number(p) + 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..4a78bd9 --- /dev/null +++ b/bin/sh/mystring.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. + * + * @(#)mystring.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD$ + */ + +#include <string.h> + +void scopyn __P((const char *, char *, int)); +int prefix __P((const char *, const char *)); +int number __P((const char *)); +int is_number __P((const char *)); + +#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..526cd88 --- /dev/null +++ b/bin/sh/nodes.c.pat @@ -0,0 +1,168 @@ +/*- + * 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.2 (Berkeley) 5/4/95 + * $FreeBSD$ + */ + +#include <stdlib.h> +/* + * 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 */ +pointer funcblock; /* block to allocate function from */ +char *funcstring; /* block to allocate strings from */ + +%SIZES + + +STATIC void calcsize __P((union node *)); +STATIC void sizenodelist __P((struct nodelist *)); +STATIC union node *copynode __P((union node *)); +STATIC struct nodelist *copynodelist __P((struct nodelist *)); +STATIC char *nodesavestr __P((char *)); + + + +/* + * 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 = (char *)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 = (char *)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; +{ + char *p = s; + char *q = funcstring; + char *rtn = funcstring; + + while ((*q++ = *p++) != '\0') + continue; + 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..53dd18f --- /dev/null +++ b/bin/sh/nodetypes @@ -0,0 +1,147 @@ +# +# 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.2 (Berkeley) 5/4/95 +# $FreeBSD$ + +# 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 +NFROMTO 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 + vname nodeptr # file name if fd>&$var + + +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..36f9c5c --- /dev/null +++ b/bin/sh/options.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 +#if 0 +static char sccsid[] = "@(#)options.c 8.2 (Berkeley) 5/4/95"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <signal.h> +#include <unistd.h> +#include <stdlib.h> + +#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" +#ifndef NO_HISTORY +#include "myhistedit.h" +#endif + +char *arg0; /* value of $0 */ +struct shparam shellparam; /* current positional parameters */ +char **argptr; /* argument list for builtin commands */ +char *shoptarg; /* set by nextopt (like getopt) */ +char *optptr; /* used by nextopt */ + +char *minusc; /* argument to -c option */ + + +STATIC void options __P((int)); +STATIC void minus_o __P((char *, int)); +STATIC void setoption __P((int, int)); +STATIC int getopts __P((char *, char *, char **, char ***, char **)); + + +/* + * Process the shell command line arguments. + */ + +void +procargs(argc, argv) + int argc; + char **argv; +{ + int i; + + argptr = argv; + if (argc > 0) + argptr++; + for (i = 0; i < NOPTS; i++) + optlist[i].val = 2; + privileged = (getuid() != geteuid() || getgid() != getegid()); + 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); + } + /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */ + if (argptr && minusc && *argptr) + arg0 = *argptr++; + + shellparam.p = argptr; + shellparam.reset = 1; + /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */ + while (*argptr) { + shellparam.nparam++; + argptr++; + } + optschanged(); +} + + +void +optschanged() +{ + setinteractive(iflag); +#ifndef NO_HISTORY + histedit(); +#endif + 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) + int cmdline; +{ + 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 { + if (c == 'p' && !val && privileged) { + (void) setuid(getuid()); + (void) setgid(getgid()); + } + 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)) { + if (!val && privileged && equal(name, "privileged")) { + (void) setuid(getuid()); + (void) setgid(getgid()); + } + 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. + */ + +int +shiftcmd(argc, argv) + int argc; + 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. + */ + +int +setcmd(argc, argv) + int argc; + char **argv; +{ + if (argc == 1) + return showvarscmd(argc, argv); + INTOFF; + options(0); + optschanged(); + if (*argptr != NULL) { + setparam(argptr); + } + INTON; + return 0; +} + + +void +getoptsreset(value) + const char *value; +{ + if (number(value) == 1) { + shellparam.optnext = NULL; + shellparam.reset = 1; + } +} + +/* + * 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. + */ + +int +getoptscmd(argc, argv) + int argc; + char **argv; +{ + char **optbase = NULL; + + if (argc < 3) + error("Usage: getopts optstring var [arg]"); + else if (argc == 3) + optbase = shellparam.p; + else + optbase = &argv[3]; + + if (shellparam.reset == 1) { + shellparam.optnext = optbase; + shellparam.optptr = NULL; + shellparam.reset = 0; + } + + return getopts(argv[1], argv[2], optbase, &shellparam.optnext, + &shellparam.optptr); +} + +STATIC int +getopts(optstr, optvar, optfirst, optnext, optptr) + char *optstr; + char *optvar; + char **optfirst; + char ***optnext; + char **optptr; +{ + char *p, *q; + char c = '?'; + int done = 0; + int ind = 0; + int err = 0; + char s[10]; + + if ((p = *optptr) == NULL || *p == '\0') { + /* Current word is done, advance */ + if (*optnext == NULL) + return 1; + p = **optnext; + if (p == NULL || *p != '-' || *++p == '\0') { +atend: + ind = *optnext - optfirst + 1; + *optnext = NULL; + p = NULL; + done = 1; + goto out; + } + (*optnext)++; + if (p[0] == '-' && p[1] == '\0') /* check for "--" */ + goto atend; + } + + c = *p++; + for (q = optstr; *q != c; ) { + if (*q == '\0') { + if (optstr[0] == ':') { + s[0] = c; + s[1] = '\0'; + err |= setvarsafe("OPTARG", s, 0); + } + else { + out1fmt("Illegal option -%c\n", c); + (void) unsetvar("OPTARG"); + } + c = '?'; + goto bad; + } + if (*++q == ':') + q++; + } + + if (*++q == ':') { + if (*p == '\0' && (p = **optnext) == NULL) { + if (optstr[0] == ':') { + s[0] = c; + s[1] = '\0'; + err |= setvarsafe("OPTARG", s, 0); + c = ':'; + } + else { + out1fmt("No arg for -%c option\n", c); + (void) unsetvar("OPTARG"); + c = '?'; + } + goto bad; + } + + if (p == **optnext) + (*optnext)++; + setvarsafe("OPTARG", p, 0); + p = NULL; + } + else + setvarsafe("OPTARG", "", 0); + ind = *optnext - optfirst + 1; + goto out; + +bad: + ind = 1; + *optnext = NULL; + p = NULL; +out: + *optptr = p; + fmtstr(s, sizeof(s), "%d", ind); + err |= setvarsafe("OPTIND", s, VNOFUNC); + s[0] = c; + s[1] = '\0'; + err |= setvarsafe(optvar, s, 0); + if (err) { + *optnext = NULL; + *optptr = NULL; + flushall(); + exraise(EXERROR); + } + return done; +} + +/* + * 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; + { + 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); + shoptarg = 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..023027b --- /dev/null +++ b/bin/sh/options.h @@ -0,0 +1,117 @@ +/*- + * 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.2 (Berkeley) 5/4/95 + * $FreeBSD$ + */ + +struct shparam { + int nparam; /* # of positional parameters (without $0) */ + unsigned char malloc; /* if parameter list dynamically allocated */ + unsigned char reset; /* if getopts has been reset */ + 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 privileged optlist[15].val +#define Tflag optlist[16].val + +#define NOPTS 17 + +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 }, + { "privileged", 'p', 0 }, + { "trapsasync", 'T', 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 *shoptarg; /* set by nextopt */ +extern char *optptr; /* used by nextopt */ + +void procargs __P((int, char **)); +void optschanged __P((void)); +void setparam __P((char **)); +void freeparam __P((struct shparam *)); +int shiftcmd __P((int, char **)); +int setcmd __P((int, char **)); +int getoptscmd __P((int, char **)); +int nextopt __P((char *)); +void getoptsreset __P((const char *)); diff --git a/bin/sh/output.c b/bin/sh/output.c new file mode 100644 index 0000000..82ae380 --- /dev/null +++ b/bin/sh/output.c @@ -0,0 +1,575 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)output.c 8.2 (Berkeley) 5/4/95"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#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 <sys/types.h> /* quad_t */ +#include <sys/ioctl.h> + +#include <stdio.h> /* defines BUFSIZ */ +#include <string.h> +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> + +#include "shell.h" +#include "syntax.h" +#include "output.h" +#include "memalloc.h" +#include "error.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) + const char *p; + { + outstr(p, out1); +} + + +void +out2str(p) + const char *p; + { + outstr(p, out2); +} + + +void +outstr(p, file) + const char *p; + 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 and q modifiers are 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 + +static const char digit[] = "0123456789ABCDEF"; + + +void +doformat(dest, f, ap) + struct output *dest; + char *f; /* format string */ + va_list ap; + { + char c; + char temp[TEMPSIZE]; + int flushleft; + int sharp; + int width; + int prec; + int islong; + int isquad; + char *p; + int sign; + quad_t l; + u_quad_t 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; + isquad = 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++; + } else if (*f == 'q') { + isquad++; + f++; + } + switch (*f) { + case 'd': + if (isquad) + l = va_arg(ap, quad_t); + else 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 (isquad) + num = va_arg(ap, u_quad_t); + else 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. + * XXX unused function + */ + +int +xioctl(fd, request, arg) + int fd; + unsigned long request; + char * 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..f99db6b --- /dev/null +++ b/bin/sh/output.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. + * + * @(#)output.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD$ + */ + +#ifndef OUTPUT_INCL + +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +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; + +void open_mem __P((char *, int, struct output *)); +void out1str __P((const char *)); +void out2str __P((const char *)); +void outstr __P((const char *, struct output *)); +void emptyoutbuf __P((struct output *)); +void flushall __P((void)); +void flushout __P((struct output *)); +void freestdout __P((void)); +void outfmt __P((struct output *, char *, ...)); +void out1fmt __P((char *, ...)); +void dprintf __P((char *, ...)); +void fmtstr __P((char *, int, char *, ...)); +void doformat __P((struct output *, char *, va_list)); +int xwrite __P((int, char *, int)); +int xioctl __P((int, unsigned long, char *)); + +#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..08d2569 --- /dev/null +++ b/bin/sh/parser.c @@ -0,0 +1,1559 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)parser.c 8.7 (Berkeley) 5/16/95"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <stdlib.h> + +#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 "show.h" +#include "eval.h" +#ifndef NO_HISTORY +#include "myhistedit.h" +#endif + +/* + * Shell command parser. + */ + +#define EOFMARKLEN 79 + +/* values returned by readtoken */ +#include "token.h" + + + +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 */ + +/* XXX When 'noaliases' is set to one, no alias expansion takes place. */ +static int noaliases = 0; + +#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 union node *makename __P((void)); +STATIC void parsefname __P((void)); +STATIC void parseheredoc __P((void)); +STATIC int peektoken __P((void)); +STATIC int readtoken __P((void)); +STATIC int xxreadtoken __P((void)); +STATIC int readtoken1 __P((int, char const *, char *, int)); +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 interact; +{ + int t; + + tokpushback = 0; + 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) + int nlflag; +{ + union node *n1, *n2, *n3; + int tok; + + checkkwd = 2; + if (nlflag == 0 && tokendlist[peektoken()]) + return NULL; + n1 = NULL; + for (;;) { + n2 = andor(); + tok = readtoken(); + if (tok == TBACKGND) { + if (n2->type == NCMD || n2->type == NPIPE) { + n2->ncmd.backgnd = 1; + } else if (n2->type == NREDIR) { + n2->type = NBACKGND; + } else { + n3 = (union node *)stalloc(sizeof (struct nredir)); + n3->type = NBACKGND; + n3->nredir.n = n2; + n3->nredir.redirect = NULL; + n2 = n3; + } + } + if (n1 == NULL) { + n1 = n2; + } + else { + n3 = (union node *)stalloc(sizeof (struct nbinary)); + n3->type = NSEMI; + n3->nbinary.ch1 = n1; + n3->nbinary.ch2 = n2; + n1 = n3; + } + switch (tok) { + case TBACKGND: + case TSEMI: + tok = readtoken(); + /* fall through */ + case TNL: + if (tok == TNL) { + parseheredoc(); + if (nlflag) + return n1; + } else { + tokpushback++; + } + checkkwd = 2; + if (tokendlist[peektoken()]) + return n1; + 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 = NULL; + n1 = NULL; + 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; + noaliases = 1; /* turn off alias expansion */ + checkkwd = 2, readtoken(); + do { + *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 (checkkwd = 2, readtoken() != TPIPE) + break; + app = &ap->narg.next; + readtoken(); + } + ap->narg.next = NULL; + if (lasttoken != TRP) + noaliases = 0, synexpect(TRP); + cp->nclist.body = list(0); + + checkkwd = 2; + if ((t = readtoken()) != TESAC) { + if (t != TENDCASE) + noaliases = 0, synexpect(TENDCASE); + else + checkkwd = 2, readtoken(); + } + cpp = &cp->nclist.next; + } while(lasttoken != TESAC); + noaliases = 0; /* reset alias expansion */ + *cpp = NULL; + 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 TSEMI: + /* + * An empty command before a ; doesn't make much sense, and + * should certainly be disallowed in the case of `if ;'. + */ + if (!redir) + synexpect(-1); + case TAND: + case TOR: + case TNL: + case TEOF: + case TWORD: + case TRP: + 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 = NULL; + + /* 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 union node * +makename() { + union node *n; + + n = (union node *)stalloc(sizeof (struct narg)); + n->type = NARG; + n->narg.next = NULL; + n->narg.text = wordtext; + n->narg.backquote = backquotelist; + return n; +} + +void fixredir(n, text, err) + union node *n; + const char *text; + int err; + { + TRACE(("Fix redir %s %d\n", text, err)); + if (!err) + n->ndup.vname = NULL; + + if (is_digit(text[0]) && text[1] == '\0') + n->ndup.dupfd = digit_val(text[0]); + else if (text[0] == '-' && text[1] == '\0') + n->ndup.dupfd = -1; + else { + + if (err) + synerror("Bad fd number"); + else + n->ndup.vname = makename(); + } +} + + +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) { + fixredir(n, wordtext, 0); + } else { + n->nfile.fname = makename(); + } +} + + +/* + * 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 +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) + { + char * const *pp; + + 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 (noaliases == 0 && + (ap = lookupalias(wordtext, 1)) != NULL) { + 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() { + int 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; + { + int c = firstc; + 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 */ + int synentry; +#if __GNUC__ + /* Avoid longjmp clobbering */ + (void) &out; + (void) "ef; + (void) &dblquote; + (void) &varnest; + (void) &arinest; + (void) &parenlevel; + (void) &oldstyle; + (void) &prevsyntax; + (void) &syntax; + (void) &synentry; +#endif + + 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 */ + + synentry = syntax[c]; + + switch(synentry) { + 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 (c >= 0 && SQSYNTAX[c] == CCTL) + USTPUTC(CTLESC, out); + else if (eofmark == NULL) + USTPUTC(CTLQUOTEMARK, out); + USTPUTC(c, out); + quotef++; + } + break; + case CSQUOTE: + if (eofmark == NULL) + USTPUTC(CTLQUOTEMARK, out); + syntax = SQSYNTAX; + break; + case CDQUOTE: + if (eofmark == NULL) + USTPUTC(CTLQUOTEMARK, out); + syntax = DQSYNTAX; + dblquote = 1; + break; + case CENDQUOTE: + if (eofmark != NULL && arinest == 0 && + varnest == 0) { + USTPUTC(c, out); + } else { + if (arinest) { + syntax = ARISYNTAX; + dblquote = 0; + } else if (eofmark == NULL) { + syntax = BASESYNTAX; + dblquote = 0; + } + quotef++; + } + 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; + if (syntax == DQSYNTAX) + dblquote = 1; + else + dblquote = 0; + } 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) { + 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 if (c == '>') + np->type = NFROMTO; + 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 + int bracketed_name = 0; /* used to handle ${[0-9]*} variables */ + + 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 == '{') { + bracketed_name = 1; + c = pgetc(); + if (c == '#') { + if ((c = pgetc()) == '}') + c = '#'; + else + subtype = VSLENGTH; + } + else + subtype = 0; + } + if (is_name(c)) { + do { + STPUTC(c, out); + c = pgetc(); + } while (is_in_name(c)); + } else if (is_digit(c)) { + if (bracketed_name) { + do { + STPUTC(c, out); + c = pgetc(); + } while (is_digit(c)); + } else { + STPUTC(c, out); + c = pgetc(); + } + } else { + if (! is_special(c)) +badsub: synerror("Bad substitution"); + USTPUTC(c, out); + c = pgetc(); + } + STPUTC('=', out); + flags = 0; + if (subtype == 0) { + switch (c) { + case ':': + flags = VSNUL; + c = pgetc(); + /*FALLTHROUGH*/ + default: + p = strchr(types, c); + if (p == NULL) + goto badsub; + subtype = p - types + VSNORMAL; + break; + case '%': + case '#': + { + int cc = c; + subtype = c == '#' ? VSTRIMLEFT : + VSTRIMRIGHT; + c = pgetc(); + if (c == cc) + subtype++; + else + pungetc(); + break; + } + } + } else { + pungetc(); + } + if (subtype != VSLENGTH && (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; + int saveprompt; +#if __GNUC__ + /* Avoid longjmp clobbering */ + (void) &saveprompt; +#endif + + 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); + memcpy(str, stackblock(), 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. */ + char *out; + int c; + int savelen; + char *str; + + + STARTSTACKSTR(out); + for (;;) { + if (needprompt) { + setprompt(2); + needprompt = 0; + } + switch (c = pgetc()) { + case '`': + goto done; + + case '\\': + if ((c = pgetc()) == '\n') { + plinno++; + if (doprompt) + setprompt(2); + else + setprompt(0); + /* + * If eating a newline, avoid putting + * the newline into the new character + * stream (via the STPUTC after the + * switch). + */ + continue; + } + if (c != '\\' && c != '`' && c != '$' + && (!dblquote || c != '"')) + STPUTC('\\', out); + break; + + case '\n': + plinno++; + needprompt = doprompt; + break; + + case PEOF: + startlinno = plinno; + synerror("EOF in backquote substitution"); + break; + + default: + break; + } + STPUTC(c, out); + } +done: + STPUTC('\0', out); + savelen = out - stackblock(); + if (savelen > 0) { + str = ckmalloc(savelen); + memcpy(str, stackblock(), savelen); + setinputstring(str, 1); + } + } + nlpp = &bqlist; + while (*nlpp) + nlpp = &(*nlpp)->next; + *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist)); + (*nlpp)->next = NULL; + parsebackquote = oldstyle; + + if (oldstyle) { + saveprompt = doprompt; + doprompt = 0; + } + + n = list(0); + + if (oldstyle) + doprompt = saveprompt; + else { + if (readtoken() != TRP) + synexpect(TRP); + } + + (*nlpp)->n = n; + if (oldstyle) { + /* + * Start reading from old file again, ignoring any pushed back + * tokens left from the backquote parsing + */ + popfile(); + tokpushback = 0; + } + while (stackblocksize() <= savelen) + growstackblock(); + STARTSTACKSTR(out); + if (str) { + memcpy(out, str, 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); + if (dblquote) + USTPUTC('"',out); + else + USTPUTC(' ',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; + { + char *p; + char c; + + p = text; + while ((c = *p++) != '\0') { + if ( c == CTLQUOTEMARK) + continue; + if (c == CTLESC) + p++; + else if (c >= 0 && BASESYNTAX[(int)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; + { + 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) + int 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; + +#ifndef NO_HISTORY + if (!el) +#endif + out2str(getprompt(NULL)); +} + +/* + * called by editline -- any expansions to the prompt + * should be added here. + */ +char * +getprompt(unused) + void *unused __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..fdb2c34 --- /dev/null +++ b/bin/sh/parser.h @@ -0,0 +1,82 @@ +/*- + * 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.3 (Berkeley) 5/4/95 + * $FreeBSD$ + */ + +/* 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' +#define CTLQUOTEMARK '\210' + +/* variable substitution byte (follows CTLVAR) */ +#define VSTYPE 0x0f /* type of variable substitution */ +#define VSNUL 0x10 /* colon--treat the empty string as unset */ +#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */ + +/* values of VSTYPE field */ +#define VSNORMAL 0x1 /* normal variable: $var or ${var} */ +#define VSMINUS 0x2 /* ${var-text} */ +#define VSPLUS 0x3 /* ${var+text} */ +#define VSQUESTION 0x4 /* ${var?message} */ +#define VSASSIGN 0x5 /* ${var=text} */ +#define VSTRIMLEFT 0x6 /* ${var#pattern} */ +#define VSTRIMLEFTMAX 0x7 /* ${var##pattern} */ +#define VSTRIMRIGHT 0x8 /* ${var%pattern} */ +#define VSTRIMRIGHTMAX 0x9 /* ${var%%pattern} */ +#define VSLENGTH 0xa /* ${#var} */ + + +/* + * 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 */ + + +union node *parsecmd __P((int)); +void fixredir __P((union node *, const char *, int)); +int goodname __P((char *)); +char *getprompt __P((void *)); diff --git a/bin/sh/redir.c b/bin/sh/redir.c new file mode 100644 index 0000000..ee893f4 --- /dev/null +++ b/bin/sh/redir.c @@ -0,0 +1,393 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)redir.c 8.2 (Berkeley) 5/4/95"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> +#include <signal.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> + +/* + * 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" + + +#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; + +STATIC void openredirect __P((union node *, char[10 ])); +STATIC int openhere __P((union node *)); + + +/* + * 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 = NULL; + int i; + int fd; + int try; + 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; + try = 0; + if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) && + n->ndup.dupfd == fd) + continue; /* redirect from/to same file descriptor */ + + if ((flags & REDIR_PUSH) && sv->renamed[fd] == EMPTY) { + INTOFF; +again: + if ((i = fcntl(fd, F_DUPFD, 10)) == -1) { + switch (errno) { + case EBADF: + if (!try) { + openredirect(n, memory); + try++; + goto again; + } + /* FALLTHROUGH*/ + default: + INTON; + error("%d: %s", fd, strerror(errno)); + break; + } + } + if (!try) { + sv->renamed[fd] = i; + close(fd); + } + INTON; + } else { + close(fd); + } + if (fd == 0) + fd0_redirected++; + if (!try) + 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 NFROMTO: + fname = redir->nfile.expfname; +#ifdef O_CREAT + if ((f = open(fname, O_RDWR|O_CREAT, 0666)) < 0) + error("cannot create %s: %s", fname, errmsg(errno, E_CREAT)); +#else + if ((f = open(fname, O_RDWR, 0666)) < 0) { + if (errno != ENOENT) + 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)); + else { + close(f); + if ((f = open(fname, O_RDWR)) < 0) { + error("cannot create %s: %s", fname, errmsg(errno, E_CREAT)); + remove(fname); + } + } + } +#endif + goto movefd; + 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 = 0; + + if (pipe(pip) < 0) + error("Pipe call failed: %s", strerror(errno)); + 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() { + 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() { + 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 from; + int to; +{ + int newfd; + + newfd = fcntl(from, F_DUPFD, to); + if (newfd < 0) { + if (errno == EMFILE) + return EMPTY; + else + error("%d: %s", from, strerror(errno)); + } + return newfd; +} diff --git a/bin/sh/redir.h b/bin/sh/redir.h new file mode 100644 index 0000000..709ed12 --- /dev/null +++ b/bin/sh/redir.h @@ -0,0 +1,50 @@ +/*- + * 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.2 (Berkeley) 5/4/95 + * $FreeBSD$ + */ + +/* flags passed to redirect */ +#define REDIR_PUSH 01 /* save previous values of file descriptors */ +#define REDIR_BACKQ 02 /* save the command output in memory */ + +union node; +void redirect __P((union node *, int)); +void popredir __P((void)); +int fd0_redirected_p __P((void)); +void clearredir __P((void)); +int copyfd __P((int, int)); + diff --git a/bin/sh/sh.1 b/bin/sh/sh.1 new file mode 100644 index 0000000..8277ddb --- /dev/null +++ b/bin/sh/sh.1 @@ -0,0 +1,1944 @@ +.\" 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. +.\" +.\" from: @(#)sh.1 8.6 (Berkeley) 5/4/95 +.\" $FreeBSD$ +.\" +.Dd May 5, 1995 +.Dt SH 1 +.Os BSD 4 +.Sh NAME +.Nm sh +.Nd command interpreter (shell) +.Sh SYNOPSIS +.Nm +.Op Fl /+abCEefIimnpsTuVvx +.Op Fl /+o Ar longname +.Op Fl c Ar string +.Op Ar arg ... +.Sh DESCRIPTION +The +.Nm +utility is the standard command interpreter for the system. +The current version of +.Nm +is in the process of being changed to +conform with the +.St -p1003.2 +specification 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 like +.Xr pdksh 1 . +Only features +designated by POSIX, plus a few Berkeley extensions, are being +incorporated into this shell. +This man page is not intended to be a tutorial nor a complete +specification of the shell. +.Ss Overview +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 started when a user logs into the system, +although a user can select a different shell with the +.Xr 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 builtin 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, +which can be executed directly by the shell. +.Ss Invocation +.\" +.\" XXX This next sentence is incredibly confusing. +.\" +If no arguments are present and if the standard input of the shell +is connected to a terminal +(or if the +.Fl i +option 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 +.Pq Li - , +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 +.Pa /etc/profile +and then +.Pa .profile +if they exist. If the environment variable +.Ev ENV +is set on entry to a shell, or is set in the +.Pa .profile +of a login shell, the shell then reads commands from the file named in +.Ev ENV . +Therefore, a user should place commands that are to be executed only +at login time in the +.Pa .profile +file, and commands that are executed for every shell inside the +.Ev ENV +file. +The user can set the +.Ev ENV +variable to some file by placing the following line in the file +.Pa .profile +in the home directory, +substituting for +.Pa .shinit +the filename desired: +.Pp +.Dl ENV=$HOME/.shinit; export ENV +.Pp +The first non-option argument specified on the command line +will be treated 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. +.Pp +Unlike older versions of +.Nm +the +.Ev ENV +script is only sourced on invocation of interactive shells. This +closes a well-known, and sometimes easily exploitable security +hole related to poorly thought out +.Ev ENV +scripts. +.Ss Argument List Processing +All of the single letter options to +.Nm +have a corresponding long name, +with the exception of +.Fl c +and +.Fl /+o . +These long names are provided next to the single letter options +in the descriptions below. +The long name for an option may be specified as an argument to the +.Fl /+o +option of +.Xr sh 1 . +Once the shell is running, +the long name for an option may be specified as an argument to the +.Fl /+o +option of the +.Ic set +builtin command +(described later in the section called +.Sx Builtin Commands ) . +Introducing an option with a dash +.Pq Li - +enables the option, +while using a plus +.Pq Li + +disables the option. +A +.Dq Li -- +or plain +.Dq Li - +will stop option processing and will force the remaining +words on the command line to be treated as arguments. +The +.Fl /+o +and +.Fl c +options do not have long names. +They take arguments and are described after the single letter options. +.Bl -tag -width Ds +.It Fl a Li allexport +Flag variables for export when assignments are made to them. +.It Fl b Li notify +Enable asynchronous notification of background job +completion. +.Pq UNIMPLEMENTED +.It Fl C Li noclobber +Do not overwrite existing files with +.Dq Li > . +.Pq UNIMPLEMENTED +.It Fl E Li emacs +Enable the builtin +.Xr emacs 1 +command line editor (disables the +.Fl V +option if it has been set). +.It Fl e Li errexit +Exit immediately if any untested command fails in non-interactive mode. +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 +.Dq Li && +or +.Dq Li || +operator. +.It Fl f Li noglob +Disable pathname expansion. +.It Fl I Li ignoreeof +Ignore +.Dv EOF Ns ' Ns s +from input when in interactive mode. +.It Fl i Li interactive +Force the shell to behave interactively. +.It Fl m Li monitor +Turn on job control (set automatically when interactive). +.It Fl n Li noexec +If not interactive, read commands but do not +execute them. This is useful for checking the +syntax of shell scripts. +.It Fl p Li privileged +Turn on privileged mode. This mode is enabled on startup +if either the effective user or group id is not equal to the +real user or group id. Turning this mode off sets the +effective user and group ids to the real user and group ids. +When this mode is enabled for interactive shells, the file +.Pa /etc/suid_profile +is sourced instead of +.Pa ~/.profile +after +.Pa /etc/profile +is sourced, and the contents of the +.Ev ENV +variable are ignored. +.It Fl s Li 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. when set with the +.Ic set +command). +.It Fl T Li asynctraps +When waiting for a child, execute traps immediately. +If this option is not set, +traps are executed after the child exits, +as specified in +.St -p1003.2 +This nonstandard option is useful for putting guarding shells around +children that block signals. The surrounding shell may kill the child +or it may just return control to the tty and leave the child alone, +like this: +.Bd -literal -offset indent +sh -T -c "trap 'exit 1' 2 ; some-blocking-program" +.Ed +.Pp +.It Fl u Li 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. +.Pq UNIMPLEMENTED +.It Fl V Li vi +Enable the builtin +.Xr vi 1 +command line editor (disables +.Fl E +if it has been set). +.It Fl v Li verbose +The shell writes its input to standard error +as it is read. Useful for debugging. +.It Fl x Li xtrace +Write each command +(preceded by +.Dq Li +\ ) +to standard error before it is executed. +Useful for debugging. +.El +.Pp +The +.Fl c +option may be used to pass its string argument to the shell +to be interpreted as input. +Keep in mind that this option only accepts a single string as its +argument, hence multi-word strings must be quoted. +.Pp +The +.Fl /+o +option takes as its only argument the long name of an option +to be enabled or disabled. +For example, the following two invocations of +.Nm +both enable the builtin +.Xr emacs 1 +command line editor: +.Bd -literal -offset indent +set -E +set -o emacs +.Ed +.Ss Lexical Structure +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 called +.Dq operators , +which are special to the shell. +There are two types of operators: control operators and +redirection operators (their meaning is discussed later). +The following is a list of valid operators: +.Bl -tag -width Ds +.It Control operators: +.Bl -column "XXX" "XXX" "XXX" "XXX" "XXX" -offset center -compact +.It Xo +.Li & Ta Xo +.Li && Ta Xo +.Li ( Ta Xo +.Li ) Ta Xo +.Li \en +.Xc Xc Xc Xc Xc +.It Xo +.Li ;; Ta Xo +.Li ; Ta Xo +.Li | Ta Xo +.Li || +.Xc Xc Xc Xc +.El +.It Redirection operators: +.Bl -column "XXX" "XXX" "XXX" "XXX" "XXX" -offset center -compact +.It Xo +.Li < Ta Xo +.Li > Ta Xo +.Li << Ta Xo +.Li >> Ta Xo +.Li <> +.Xc Xc Xc Xc Xc +.It Xo +.Li <& Ta Xo +.Li >& Ta Xo +.Li <<- Ta Xo +.Li >| +.Xc Xc Xc Xc +.El +.El +.Ss Quoting +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. +.Bl -tag -width Ds +.It Single Quotes +Enclosing characters in single quotes preserves the literal +meaning of all the characters (except single quotes, making +it impossible to put single-quotes in a single-quoted string). +.It Double Quotes +Enclosing characters within double quotes preserves the literal +meaning of all characters except dollarsign +.Pq Li $ , +backquote +.Pq Li ` , +and backslash +.Po Li \e\" +.Pc . +The backslash inside double quotes is historically weird. +It remains literal unless it precedes the following characters, +which it serves to quote: +.Bl -column "XXX" "XXX" "XXX" "XXX" "XXX" -offset center -compact +.It Xo +.Li $ Ta Xo +.Li ` Ta Xo +.Li \&" Ta Xo +.Li \e\ Ta Xo +.Li \en +.Xc Xc Xc Xc Xc +.El +.It Backslash +A backslash preserves the literal meaning of the following +character, with the exception of the newline character +.Pq Li \en . +A backslash preceding a newline is treated as a line continuation. +.El +.Ss Reserved Words +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: +.Bl -column "doneXX" "elifXX" "elseXX" "untilXX" "whileX" -offset center +.It Xo +.Li ! Ta Xo +.Li { Ta Xo +.Li } Ta Xo +.Ic case Ta Xo +.Ic do +.Xc Xc Xc Xc Xc +.It Xo +.Ic done Ta Xo +.Ic elif Ta Xo +.Ic else Ta Xo +.Ic esac Ta Xo +.Ic fi +.Xc Xc Xc Xc Xc +.It Xo +.Ic for Ta Xo +.Ic if Ta Xo +.Ic then Ta Xo +.Ic until Ta Xo +.Ic while +.Xc Xc Xc Xc Xc +.El +.Ss Aliases +An alias is a name and corresponding value set using the +.Ic alias +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 +.Dq Li lf +with the value +.Dq Li ls -F , +then the input +.Bd -literal -offset indent +lf foobar +.Ed +.Pp +would become +.Bd -literal -offset indent +ls -F foobar +.Ed +.Pp +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. +.Ss Commands +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 +.St -p1003.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. +.Ss Simple Commands +If a simple command has been recognized, the shell performs +the following actions: +.Bl -enum +.It +Leading words of the form +.Dq Li 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. +.It +The remaining words are expanded as described in +the section called +.Sx Word 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 +.Dq Li name=value +variable assignments recognized in 1) affect the +current shell. +.It +Redirections are performed as described in +the next section. +.El +.Ss Redirections +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: +.Pp +.Dl [n] redir-op file +.Pp +The +.Ql redir-op +is one of the redirection operators mentioned +previously. The following gives some examples of how these +operators can be used. +Note that stdin and stdout are commonly used abbreviations +for standard input and standard output respectively. +.Bl -tag -width "1234567890XX" -offset indent +.It Li [n]> file +redirect stdout (or file descriptor n) to file +.It Li [n]>| file +same as above, but override the +.Fl C +option +.It Li [n]>> file +append stdout (or file descriptor n) to file +.It Li [n]< file +redirect stdin (or file descriptor n) from file +.It Li [n]<> file +redirect stdin (or file descriptor n) to and from file +.It Li [n1]<&n2 +duplicate stdin (or file descriptor n1) from file descriptor n2 +.It Li [n]<&- +close stdin (or file descriptor n) +.It Li [n1]>&n2 +duplicate stdout (or file descriptor n1) to file descriptor n2 +.It Li [n]>&- +close stdout (or file descriptor n) +.El +.Pp +The following redirection is often called a +.Dq here-document . +.Bd -literal -offset indent +[n]<< delimiter + here-doc-text + ... +delimiter +.Ed +.Pp +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 +.Sx Word Expansions ) . +If the operator is +.Dq Li <<- +instead of +.Dq Li << , +then leading tabs +in the here-doc-text are stripped. +.Ss Search and Execution +There are three types of commands: shell functions, +builtin commands, and normal programs. +The command is searched for (by name) in that order. +The three types of commands are all executed in a different way. +.Pp +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. +This all occurs within the current shell. +.Pp +Shell builtin commands are executed internally to the shell, without +spawning a new process. +.Pp +Otherwise, if the command name does not match a function +or builtin command, 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 not a normal executable file +(i.e. if it does not begin with the +.Qq magic number +whose +.Tn ASCII +representation is +.Qq #! , +resulting in an +.Dv ENOEXEC +return value from +.Xr execve 2 ) +the shell will interpret the program in a subshell. +The child shell will reinitialize itself in this case, +so that the effect will be +as if a new shell had been invoked to handle the ad-hoc shell script, +except that the location of hashed commands located in +the parent shell will be remembered by the child. +.Pp +Note that previous versions of this document +and the source code itself misleadingly and sporadically +refer to a shell script without a magic number +as a +.Qq shell procedure . +.Ss Path Search +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. If a builtin command is not found, +one of two things happen: +.Bl -enum +.It +Command names containing a slash are simply executed without +performing any searches. +.It +The shell searches each entry in +.Ev PATH +in turn for the command. The value of the +.Ev PATH +variable should be a series of +entries separated by colons. Each entry consists of a +directory name. +The current directory +may be indicated implicitly by an empty directory name, +or explicitly by a single period. +.El +.Ss Command Exit Status +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 shell function. +.Pp +If a command is terminated by a signal, its exit status is 128 plus +the signal number. Signal numbers are defined in the header file +.Aq Pa sys/signal.h . +.Ss Complex Commands +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: +.Bl -item -offset indent +.It +simple command +.It +pipeline +.It +list or compound-list +.It +compound command +.It +function definition +.El +.Pp +Unless otherwise stated, the exit status of a command is +that of the last simple command executed by the command. +.Ss Pipelines +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. The standard output of the last +command is inherited from the shell, as usual. +.Pp +The format for a pipeline is: +.Pp +.Dl [!] command1 [ | command2 ...] +.Pp +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. +.Pp +If the pipeline is not in the background (discussed later), +the shell waits for all commands to complete. +.Pp +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. +.Pp +Because pipeline assignment of standard input or standard +output or both takes place before redirection, it can be +modified by redirection. For example: +.Pp +.Dl $ command1 2>&1 | command2 +.Pp +sends both the standard output and standard error of +.Ql command1 +to the standard input of +.Ql command2 . +.Pp +A +.Dq Li \; +or newline terminator causes the preceding +AND-OR-list +(described below in the section called +.Sx Short-Circuit List Operators ) +to be executed sequentially; +an +.Dq Li & +causes asynchronous execution of the preceding AND-OR-list. +.Pp +Note that unlike some other shells, +.Nm +executes each process in the pipeline as a child of the +.Nm +process. +Shell builtin commands are the exception to this rule. +They are executed in the current shell, although they do not affect its +environment when used in pipelines. +.Ss Background Commands (&) +If a command is terminated by the control operator ampersand +.Pq Li & , +the shell executes the command asynchronously; +the shell does not wait for the command to finish +before executing the next command. +.Pp +The format for running a command in background is: +.Bd -literal -offset indent +command1 & [command2 & ...] +.Ed +.Pp +If the shell is not interactive, the standard input of an +asynchronous command is set to /dev/null. +.Ss Lists (Generally Speaking) +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. +.Ss Short-Circuit List Operators +.Dq Li && +and +.Dq Li || +are AND-OR list operators. +.Dq Li && +executes the first command, and then executes the second command +if the exit status of the first command is zero. +.Dq Li || +is similar, but executes the second command if the exit +status of the first command is nonzero. +.Dq Li && +and +.Dq Li || +both have the same priority. +.Ss Flow-Control Constructs (if, while, for, case) +The syntax of the +.Ic if +command is: +.\" +.\" XXX Use .Dl to work around broken handling of .Ic inside .Bd and .Ed . +.\" +.Dl Ic if Ar list +.Dl Ic then Ar list +.Dl [ Ic elif Ar list +.Dl Ic then Ar list ] ... +.Dl [ Ic else Ar list ] +.Dl Ic fi +.Pp +The syntax of the +.Ic while +command is: +.Dl Ic while Ar list +.Dl Ic do Ar list +.Dl Ic done +.Pp +The two lists are executed repeatedly while the exit status of the +first list is zero. +The +.Ic until +command is similar, but has the word +.Ic until +in place of +.Ic while , +which causes it to +repeat until the exit status of the first list is zero. +.Pp +The syntax of the +.Ic for +command is: +.Dl Ic for Ar variable Ic in Ar word ... +.Dl Ic do Ar list +.Dl Ic done +.Pp +The words are expanded, and then the list is executed +repeatedly with the variable set to each word in turn. +The +.Ic do +and +.Ic done +commands may be replaced with +.Dq Li { +and +.Dq Li } . +.Pp +The syntax of the +.Ic break +and +.Ic continue +commands is: +.Dl Ic break Op Ar num +.Dl Ic continue Op Ar num +.Pp +The +.Ic break +command terminates the +.Ar num +innermost +.Ic for +or +.Ic while +loops. +The +.Ic continue +command continues with the next iteration of the innermost loop. +These are implemented as builtin commands. +.Pp +The syntax of the +.Ic case +command is +.Dl Ic case Ar word Ic in +.Dl pattern) list ;; +.Dl ... +.Dl Ic esac +.Pp +The pattern can actually be one or more patterns +(see +.Sx Shell Patterns +described later), +separated by +.Dq Li \&| +characters. +.Ss Grouping Commands Together +Commands may be grouped by writing either +.Bd -literal -offset indent +(list) +.Ed +.Pp +or +.Bd -literal -offset indent +{ list; } +.Ed +.Pp +The first form executes the commands in a subshell. +Note that builtin commands thus executed do not affect the current shell. +The second form does not fork another shell, +so it is slightly more efficient. +Grouping commands together this way allows the user to +redirect their output as though they were one program: +.Bd -literal -offset indent +{ echo -n "hello"; echo " world"; } > greeting +.Ed +.Ss Functions +The syntax of a function definition is +.Bd -literal -offset indent +name ( ) command +.Ed +.Pp +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 +.Dq Li { +and +.Dq Li } . +.Pp +Variables may be declared to be local to a function by +using the +.Ic local +command. +This should appear as the first statement of a function, +and the syntax is: +.Bd -ragged -offset indent +.Ic local +.Op Ar variable ... +.Op Ar - +.Ed +.Pp +The +.Ic local +command is implemented as a builtin command. +.Pp +When a variable is made local, it inherits the initial +value and exported and readonly flags from the variable +with the same name in the surrounding scope, if there is +one. Otherwise, the variable is initially unset. The shell +uses dynamic scoping, so that if the variable +.Em x +is made local to function +.Em f , +which then calls function +.Em g , +references to the variable +.Em x +made inside +.Em g +will refer to the variable +.Em x +declared inside +.Em f , +not to the global variable named +.Em x . +.Pp +The only special parameter than can be made local is +.Dq Li - . +Making +.Dq Li - +local causes any shell options that are +changed via the set command inside the function to be +restored to their original values when the function +returns. +.Pp +The syntax of the +.Ic return +command is +.Bd -ragged -offset indent +.Ic return +.Op Ar exitstatus +.Ed +.Pp +It terminates the currently executing function. +The +.Ic return +command is implemented as a builtin command. +.Ss Variables and Parameters +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 +.Bd -literal -offset indent +name=value +.Ed +.Pp +Variables set by the user must have a name consisting solely +of alphabetics, numerics, and underscores. +The first letter of a variable name must not be numeric. +A parameter can also be denoted by a number +or a special character as explained below. +.Ss Positional Parameters +A positional parameter is a parameter denoted by a number greater than zero. +The shell sets these initially to the values of its command line +arguments that follow the name of the shell script. The +.Ic set +builtin command can also be used to set or reset them. +.Ss Special Parameters +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. +.Bl -hang +.It Li * +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 +.Ev IFS +variable, +or by a +.Aq space +if +.Ev IFS +is unset. +.It Li @ +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 +.Li @ +generates zero arguments, even when +.Li @ +is double-quoted. What this basically means, for example, is +if $1 is +.Dq abc +and $2 is +.Dq def ghi , +then +.Qq Li $@ +expands to +the two arguments: +.Bd -literal -offset indent +"abc" "def ghi" +.Ed +.It Li # +Expands to the number of positional parameters. +.It Li ? +Expands to the exit status of the most recent pipeline. +.It Li - +(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. +.It Li $ +Expands to the process ID of the invoked shell. A subshell +retains the same value of $ as its parent. +.It Li ! +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. +.It Li 0 +(zero) Expands to the name of the shell or shell script. +.El +.Ss Word Expansions +This clause describes the various expansions that are +performed on words. Not all expansions are performed on +every word, as explained later. +.Pp +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 +.Li @ +within double-quotes, +as was described above. +.Pp +The order of word expansion is: +.Bl -enum +.It +Tilde Expansion, Parameter Expansion, Command Substitution, +Arithmetic Expansion (these all occur at the same time). +.It +Field Splitting is performed on fields generated by step (1) +unless the +.Ev IFS +variable is null. +.It +Pathname Expansion (unless the +.Fl f +option is in effect). +.It +Quote Removal. +.El +.Pp +The +.Dq Li $ +character is used to introduce parameter expansion, command +substitution, or arithmetic evaluation. +.Ss Tilde Expansion (substituting a user's home directory) +A word beginning with an unquoted tilde character +.Pq Li ~ +is +subjected to tilde expansion. +All the characters up to a slash +.Pq Li / +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). +.Ss Parameter Expansion +The format for parameter expansion is as follows: +.Bd -literal -offset indent +${expression} +.Ed +.Pp +where expression consists of all characters until the matching +.Dq Li } . +Any +.Dq Li } +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 +.Dq Li } . +.Pp +The simplest form for parameter expansion is: +.Bd -literal -offset indent +${parameter} +.Ed +.Pp +The value, if any, of parameter is substituted. +.Pp +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: +.Bl -enum +.It +Pathname expansion is not performed on the results of the +expansion. +.It +Field splitting is not performed on the results of the +expansion, with the exception of the special parameter +.Li @ . +.El +.Pp +In addition, a parameter expansion can be modified by using one of the +following formats. +.Bl -tag -width Ds +.It Li ${parameter:-word} +Use Default Values. If parameter is unset or +null, the expansion of word is +substituted; otherwise, the value of +parameter is substituted. +.It Li ${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. +.It Li ${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. +.It Li ${parameter:+word} +Use Alternate Value. If parameter is unset +or null, null is substituted; +otherwise, the expansion of word is +substituted. +.Pp +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. +.It Li ${#parameter} +String Length. The length in characters of +the value of parameter. +.Pp +The following four varieties of parameter expansion provide for substring +processing. +In each case, pattern matching notation +(see +.Sx Shell Patterns ) , +rather than regular expression notation, +is used to evaluate the patterns. +If parameter is one of the special parameters +.Li * +or +.Li @ , +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. +.It Li ${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. +.It Li ${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. +.It Li ${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. +.It Li ${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. +.El +.Ss Command Substitution +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: +.Bd -literal -offset indent +$(command) +.Ed +.Pp +or the backquoted version: +.Bd -literal -offset indent +`command` +.Ed +.Pp +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 newlines at the end of the substitution. +Embedded newlines before the end of the output are not removed; +however, during field splitting, they may be translated into spaces +depending on the value of +.Ev IFS +and the quoting that is in effect. +.Ss Arithmetic Expansion +Arithmetic expansion provides a mechanism for evaluating an arithmetic +expression and substituting its value. +The format for arithmetic expansion is as follows: +.Bd -literal -offset indent +$((expression)) +.Ed +.Pp +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. +.Pp +Next, the shell treats this as an arithmetic expression and +substitutes the value of the expression. +.Ss White Space Splitting (Field Splitting) +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. +.Pp +The shell treats each character of the +.Ev IFS +as a delimiter and uses +the delimiters to split the results of parameter expansion and command +substitution into fields. +.Ss Pathname Expansion (File Name Generation) +Unless the +.Fl f +option 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 +.Ic case +command. +.Ss Shell Patterns +A pattern consists of normal characters, which match themselves, +and meta-characters. +The meta-characters are +.Dq Li ! , +.Dq Li * , +.Dq Li ? , +and +.Dq Li [ . +These characters lose their special meanings if they are quoted. +When command or variable substitution is performed and the dollar sign +or back quotes are not double-quoted, the value of the +variable or the output of the command is scanned for these +characters and they are turned into meta-characters. +.Pp +An asterisk +.Pq Li * +matches any string of characters. +A question mark +.Pq Li ? +matches any single character. +A left bracket +.Pq Li [ +introduces a character class. +The end of the character class is indicated by a +.Dq Li \&] ; +if the +.Dq Li \&] +is missing then the +.Dq Li [ +matches a +.Dq Li [ +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 +.Pq Li ! +the first character of the character class. +.Pp +To include a +.Dq Li \] +in a character class, make it the first character listed +(after the +.Dq Li ! , +if any). +To include a +.Dq Li - , +make it the first or last character listed. +.Ss Builtin Commands +This section lists the commands which +are builtin because they need to perform some operation +that can not be performed by a separate process. In addition to +these, a builtin version of the +.Xr printf 1 +command is provided for efficiency. +.Bl -tag -width Ds +.It Ic : +A null command that returns a 0 (true) exit value. +.It Ic . Ar file +The commands in the specified file are read and executed by the shell. +If +.Ar file +contains any +.Dq / +characters, it is used as is. Otherwise, the shell searches the +.Ev PATH +for the file. If it is not found in the +.Ev PATH , +it is sought in the current working directory. +.It Ic alias Op Ar name ... +.It Ic alias Op Ar name=string ... +If +.Ar name=string +is specified, the shell defines the alias +.Ar name +with value +.Ar string . +If just +.Ar name +is specified, the value of the alias +.Ar name +is printed. +With no arguments, the +.Ic alias +builtin command prints the names and values of all defined aliases +(see +.Ic unalias ) . +.It Ic bg Op Ar job ... +Continue the specified jobs +(or the current job if no jobs are given) +in the background. +.It Ic command Ar cmd Op Ar arg ... +Execute the specified builtin command, +.Ar cmd . +This is useful when the user wishes to override a shell function +with the same name as a builtin command. +.It Ic cd Op Ar directory +Switch to the specified +.Ar directory , +or to the directory specified in the +.Ev HOME +environment variable if no +.Ar directory +is specified. +If +.Ar directory +is not found as a subdirectory of the current directory +(and does not begin with +.Dq Li / , +.Dq Li ./ , +or +.Dq Li ../ ) , +then the directories listed in the +.Ev CDPATH +variable will be +searched for the specified +.Ar directory . +The format of +.Ar CDPATH +is the same as that of +.Ev PATH . +In an interactive shell, +the +.Ic 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 +.Ev CDPATH +mechanism was used or because a symbolic link was crossed. +.It Xo +.Ic echo +.Op Fl en +.Ar string +.Xc +Print +.Ar string +to the standard output with a newline appended. +.Bl -tag -width Ds +.It Fl n +Suppress the output of the trailing newline. +.It Fl e +Process C-style backslash escape sequences. +.Ic echo +understands the following character escapes: +.Bl -tag -width Ds +.It \ea +Alert (ring the terminal bell) +.It \eb +Backspace +.It \ec +Suppress the trailing newline (this has the side-effect of truncating the +line if it is not the last character) +.It \ee +The ESC character (ASCII 0x1b) +.It \ef +Formfeed +.It \en +Newline +.It \er +Carriage return +.It \et +Horizontal tab +.It \ev +Vertical tab +.It \e\e +Literal backslash +.It \e0nnn +(Zero) The character whose octal value is nnn +.El +.Pp +If +.Ar string +is not enclosed in quotes then the backslash itself must be escaped +with a backslash to protect it from the shell. For example +.Bd -literal -offset indent +$ echo -e "a\evb" +a + b +$ echo -e a\e\evb +a + b +$ echo -e "a\e\eb" +a\eb +$ echo -e a\e\e\e\eb +a\eb +.Ed +.El +.It Ic eval Ar string ... +Concatenate all the arguments with spaces. +Then re-parse and execute the command. +.It Ic exec Op Ar command Op arg ... +Unless +.Ar command +is omitted, +the shell process is replaced with the specified program +(which must be a real program, not a shell builtin command or function). +Any redirections on the +.Ic exec +command are marked as permanent, +so that they are not undone when the +.Ic exec +command finishes. +.It Ic exit Op Ar exitstatus +Terminate the shell process. +If +.Ar exitstatus +is given +it is used as the exit status of the shell; +otherwise the exit status of the preceding command is used. +.It Ic export Ar 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 +.Ic unset +it. +The shell allows the value of a variable to be set +at the same time as it is exported by writing +.Bd -literal -offset indent +export name=value +.Ed +.Pp +With no arguments the export command lists the names +of all exported variables. +.It Xo +.Ic fc +.Op Fl e Ar editor +.Op Ar first Op Ar last +.Xc +.It Xo +.Ic fc +.Fl l +.Op Fl nr +.Op Ar first Op Ar last +.Xc +.It Xo +.Ic fc +.Fl s +.Op Ar old=new +.Op Ar first +.Xc +The +.Ic fc +builtin command lists, or edits and re-executes, +commands previously entered to an interactive shell. +.Bl -tag -width Ds +.It Fl e Ar editor +Use the editor named by +.Ar editor +to edit the commands. +The editor string is a command name, +subject to search via the +.Ev PATH +variable. +The value in the +.Ev FCEDIT +variable is used as a default when +.Fl e +is not specified. +If +.Ev FCEDIT +is null or unset, the value of the +.Ev EDITOR +variable is used. +If +.Ev EDITOR +is null or unset, +.Xr ed 1 +is used as the editor. +.It Fl l No (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 +.Fl r , +with each command preceded by the command number. +.It Fl n +Suppress command numbers when listing with +.Fl l . +.It Fl r +Reverse the order of the commands listed +(with +.Fl l ) +or edited +(with neither +.Fl l +nor +.Fl s ) . +.It Fl s +Re-execute the command without invoking an editor. +.It Ar first +.It Ar last +Select the commands to list or edit. +The number of previous commands that can be accessed +are determined by the value of the +.Ev HISTSIZE variable. +The value of +.Ar first +or +.Ar last +or both are one of the following: +.Bl -tag -width Ds +.It Ar [+]num +A positive number representing a command number; +command numbers can be displayed with the +.Fl l +option. +.It Ar -num +A negative decimal number representing the +command that was executed +.Ar num +of +commands previously. +For example, -1 is the immediately previous command. +.It Ar string +A string indicating the most recently entered command +that begins with that string. +If the +.Ar old=new +operand is not also specified with +.Fl s , +the string form of the first operand cannot contain an embedded equal sign. +.El +.El +.Pp +The following environment variables affect the execution of +.Ic fc : +.Bl -tag -width Ds +.It Ev FCEDIT +Name of the editor to use. +.It Ev HISTSIZE +The number of previous commands that are accessible. +.El +.It Ic fg Op Ar job +Move the specified +.Ar job +or the current job to the foreground. +.It Ic getopts Ar optstring Ar var +The POSIX +.Ic getopts +command. +The +.Ic getopts +command deprecates the older +.Xr getopt 1 +command. +The first argument should be a series of letters, each possibly +followed by a colon which indicates that the option takes an argument. +The specified variable is set to the parsed option. The index of +the next argument is placed into the shell variable +.Ev OPTIND . +If an option takes an argument, it is placed into the shell variable +.Ev OPTARG . +If an invalid option is encountered, +.Ev var +is set to +.Dq Li ? . +It returns a false value (1) when it encounters the end of the options. +.It Xo +.Ic hash +.Op Fl rv +.Op Ar command ... +.Xc +The shell maintains a hash table which remembers the locations of commands. +With no arguments whatsoever, the +.Ic hash +command prints out the contents of this table. +Entries which have not been looked at since the last +.Ic cd +command are marked with an asterisk; +it is possible for these entries to be invalid. +.Pp +With arguments, the +.Ic hash +command removes each specified +.Ar command +from the hash table (unless they are functions) and then locates it. +With the +.Fl v +option, +.Ic hash +prints the locations of the commands as it finds them. +The +.Fl r +option causes the +.Ic hash +command to delete all the entries in the hash table except for functions. +.It Ic jobid Op Ar job +Print the process id's of the processes in the specified +.Ar job . +If the +.Ar job +argument is omitted, use the current job. +.It Ic jobs +This command lists out all the background processes +which are children of the current shell process. +.It Ic pwd +Print the path of 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 +.Xr pwd 1 +will continue to print the old name for the directory. +.It Xo +.Ic read +.Op Fl p Ar prompt +.Op Fl t Ar timeout +.Op Fl er +.Ar variable ... +.Xc +The +.Ar prompt +is printed if the +.Fl 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 +.Sx White Space Splitting (Field 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 +.Ev 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. +.Pp +Backslashes are treated specially, unless the +.Fl r +option is +specified. 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 +.Ev IFS , +even if it is. +.Pp +If the +.Fl t +option is specified and the +.Ar timeout +elapses before any input is supplied, +the +.Ic read +command will return without assigning any values. +The +.Ar timeout +value may optionally be followed by one of +.Dq s , +.Dq m +or +.Dq h +to explicitly specify seconds, minutes or or hours. +If none is supplied, +.Dq s +is assumed. +.Pp +The +.Fl e +option exists only for backward compatibility with older scripts. +.It Ic readonly Ar name ... +Each specified +.Ar name +is marked as read only, +so that it cannot be subsequently modified or unset. +The shell allows the value of a variable to be set +at the same time as it is marked read only +by using the following form: +.Bd -literal -offset indent +readonly name=value +.Ed +.Pp +With no arguments the +.Ic readonly +command lists the names of all read only variables. +.It Xo +.Ic set +.Op Fl /+abCEefIimnpTuVvx +.Op Fl /+o Ar longname +.Op Fl c Ar string +.Op Fl - Ar arg ... +.Xc +The +.Ic set +command performs three different functions: +.Bl -item +.It +With no arguments, it lists the values of all shell variables. +.It +If options are given, +either in short form or using the long +.Dq Fl /+o Ar longname +form, +it sets or clears the specified options as described in the section called +.Sx Argument List Processing . +.It +If the +.Dq Fl - +option is specified, +.Ic set +will replace the shell's positional parameters with the subsequent +arguments. +If no arguments follow the +.Dq Fl - +option, +all the positional parameters will be cleared, +which is equivalent to executing the command +.Dq Li shift $# . +The +.Dq Fl - +flag may be ommitted when specifying arguments to be used +as positional replacement parameters. +This is not recommended, +because the first argument may begin with a dash +.Pq Li - +or a plus +.Pq Li + , +which the +.Ic set +command will interpret as a request to enable or disable options. +.El +.It Ic setvar Ar variable Ar value +Assigns the specified +.Ar value +to the specified +.Ar variable . +.Ic Setvar +is intended to be used in functions that +assign values to variables whose names are passed as parameters. +In general it is better to write +.Bd -literal -offset indent +variable=value +.Ed +rather than using +.Ic setvar . +.It Ic shift Op Ar n +Shift the positional parameters +.Ar n +times, or once if +.Ar n +is not specified. +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 does not do anything. +.It Xo +.Ic trap +.Op Ar action +.Ar signal ... +.Xc +Cause the shell to parse and execute +.Ar action +when any specified +.Ar signal +is received. +The signals are specified by signal number. +The +.Ar 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 +.Ic trap +command has no effect on signals that were ignored on entry to the shell. +.It Ic type Op Ar name ... +Interpret each +.Ar name +as a command and print the resolution of the command search. +Possible resolutions are: +shell keyword, alias, shell builtin command, command, tracked alias +and not found. +For aliases the alias expansion is printed; +for commands and tracked aliases +the complete pathname of the command is printed. +.It Xo +.Ic ulimit +.Op Fl HSabcdflmnust +.Op Ar limit +.Xc +Set or display resource limits (see +.Xr getrlimit 2 ). +If +.Ar limit +is specified, the named resource will be set; +otherwise the current resource value will be displayed. +.Pp +If +.Fl H +is specified, the hard limits will be set or displayed. +While everybody is allowed to reduce a hard limit, +only the superuser can increase it. +The +.Fl S +option +specifies the soft limits instead. When displaying limits, +only one of +.Fl S +or +.Fl H +can be given. +The default is to display the soft limits, +and to set both the hard and the soft limits. +.Pp +Option +.Fl a +causes the +.Ic ulimit +command to display all resources. +The parameter +.Ar limit +is not acceptable in this mode. +.Pp +The remaining options specify which resource value is to be +displayed or modified. +They are mutually exclusive. +.Bl -tag -width Ds +.It Fl b Ar sbsize +The maximum size of socket buffer usage, in bytes. +.It Fl c Ar coredumpsize +The maximal size of core dump files, in 512-byte blocks. +.It Fl d Ar datasize +The maximal size of the data segment of a process, in kilobytes. +.It Fl f Ar filesize +The maximal size of a file, in 512-byte blocks. +.It Fl l Ar lockedmem +The maximal size of memory that can be locked by a process, in +kilobytes. +.It Fl m Ar memoryuse +The maximal resident set size of a process, in kilobytes. +.It Fl n Ar nofiles +The maximal number of descriptors that could be opened by a process. +.It Fl s Ar stacksize +The maximal size of the stack segment, in kilobytes. +.It Fl t Ar time +The maximal amount of CPU time to be used by each process, in seconds. +.It Fl u Ar userproc +The maximal number of simultaneous processes for this user ID. +.El +.It Ic umask Op Ar mask +Set the file creation mask (see +.Xr umask 2 ) +to the octal value specified by +.Ar mask . +If the argument is omitted, the current mask value is printed. +.It Xo +.Ic unalias +.Op Fl a +.Op Ar name +.Xc +If +.Ar name +is specified, the shell removes that alias. +If +.Fl a +is specified, all aliases are removed. +.It Ic unset Ar name ... +The specified variables and functions are unset and unexported. +If a given +.Ar name +corresponds to both a variable and a function, +both the variable and the function are unset. +.It Ic wait Op Ar job +Wait for the specified +.Ar job +to complete and return the exit status of the last process in the +.Ar job . +If the argument is omitted, wait for all jobs to complete +and return an exit status of zero. +.El +.Ss Commandline Editing +When +.Nm +is being used interactively from a terminal, the current command +and the command history +(see +.Ic fc +in +.Sx Builtin Commands ) +can be edited using vi-mode command line editing. +This mode uses commands similar +to a subset of those described in the vi man page. +The command +.Dq Li set -o vi +(or +.Dq Li set -V ) +enables vi-mode editing and places +.Nm +into vi insert mode. With vi-mode enabled, +.Nm +can be switched between insert mode and command mode by typing +.Aq ESC . +Hitting +.Aq return +while in command mode will pass the line to the shell. +.Pp +Similarly, the +.Dq Li set -o emacs +(or +.Dq Li set -E ) +command can be used to enable a subset of +emacs-style command line editing features. +.Sh SEE ALSO +.Xr builtin 1 , +.Xr echo 1 , +.Xr expr 1 , +.Xr pwd 1 , +.Xr printf 1 , +.Xr test 1 +.Sh HISTORY +A +.Nm +command appeared in +.At V.1 . diff --git a/bin/sh/shell.h b/bin/sh/shell.h new file mode 100644 index 0000000..0d00a91 --- /dev/null +++ b/bin/sh/shell.h @@ -0,0 +1,83 @@ +/*- + * 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.2 (Berkeley) 5/4/95 + * $FreeBSD$ + */ + +/* + * The follow should be set to reflect the type of system you have: + * JOBS -> 1 if you have Berkeley job control, 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 +#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 static +#define MKINIT /* empty */ + +#include <sys/cdefs.h> + +extern char nullstr[1]; /* null string */ + + +#ifdef DEBUG +#define TRACE(param) sh_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..0183e6b --- /dev/null +++ b/bin/sh/show.c @@ -0,0 +1,438 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)show.c 8.3 (Berkeley) 5/4/95"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <stdio.h> +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif +#if DEBUG == 2 +#include <errno.h> +#endif +#include <errno.h> + +#include "shell.h" +#include "parser.h" +#include "nodes.h" +#include "mystring.h" +#include "show.h" + + +#ifdef DEBUG +static void shtree __P((union node *, int, char *, FILE*)); +static void shcmd __P((union node *, FILE *)); +static void sharg __P((union node *, FILE *)); +static void indent __P((int, char *, FILE *)); +static void trstring __P((char *)); + + +void +showtree(n) + union node *n; +{ + trputs("showtree called\n"); + shtree(n, 1, NULL, stdout); +} + + +static void +shtree(n, ind, pfx, fp) + union node *n; + int ind; + char *pfx; + FILE *fp; +{ + struct nodelist *lp; + char *s; + + if (n == NULL) + return; + + 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 void +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 NFROMTO: s = "<>"; dftfd = 0; break; + case NFROMFD: s = "<&"; dftfd = 0; break; + default: s = "*error*"; 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 void +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; + if (subtype == VSLENGTH) + putc('#', fp); + + 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; + case VSTRIMLEFT: + putc('#', fp); + break; + case VSTRIMLEFTMAX: + putc('#', fp); + putc('#', fp); + break; + case VSTRIMRIGHT: + putc('%', fp); + break; + case VSTRIMRIGHTMAX: + putc('%', fp); + putc('%', fp); + break; + case VSLENGTH: + 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 void +indent(amount, pfx, fp) + int amount; + char *pfx; + FILE *fp; +{ + int i; + + for (i = 0 ; i < amount ; i++) { + if (pfx && i == amount - 1) + fputs(pfx, fp); + putc('\t', fp); + } +} + + +/* + * Debugging stuff. + */ + + +FILE *tracefile; + +#if DEBUG == 2 +int debug = 1; +#else +int debug = 0; +#endif + + +void +trputc(c) + int c; +{ + if (tracefile == NULL) + return; + putc(c, tracefile); + if (c == '\n') + fflush(tracefile); +} + + +void +#ifdef __STDC__ +sh_trace(const char *fmt, ...) +#else +sh_trace(va_alist) + va_dcl +#endif +{ + va_list va; +#ifdef __STDC__ + va_start(va, fmt); +#else + char *fmt; + va_start(va); + fmt = va_arg(va, char *); +#endif + if (tracefile != NULL) { + (void) vfprintf(tracefile, fmt, va); + if (strchr(fmt, '\n')) + (void) fflush(tracefile); + } + va_end(va); +} + + +void +trputs(s) + char *s; +{ + if (tracefile == NULL) + return; + fputs(s, tracefile); + if (strchr(s, '\n')) + fflush(tracefile); +} + + +static void +trstring(s) + char *s; +{ + char *p; + char c; + + 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); +} + + +void +trargs(ap) + char **ap; +{ + if (tracefile == NULL) + return; + while (*ap) { + trstring(*ap++); + if (*ap) + putc(' ', tracefile); + else + putc('\n', tracefile); + } + fflush(tracefile); +} + + +void +opentrace() { + char s[100]; + char *getenv(); +#ifdef O_APPEND + int flags; +#endif + + if (!debug) + return; +#ifdef not_this_way + { + char *p; + 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: %s\n", s, strerror(errno)); + 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/show.h b/bin/sh/show.h new file mode 100644 index 0000000..837d21c --- /dev/null +++ b/bin/sh/show.h @@ -0,0 +1,44 @@ +/*- + * Copyright (c) 1995 + * 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. + * + * @(#)show.h 1.1 (Berkeley) 5/4/95 + * $FreeBSD$ + */ + +void showtree __P((union node *)); +#ifdef DEBUG +void sh_trace __P((const char *, ...)); +void trargs __P((char **)); +void trputc __P((int)); +void trputs __P((char *)); +void opentrace __P((void)); +#endif diff --git a/bin/sh/trap.c b/bin/sh/trap.c new file mode 100644 index 0000000..233746e --- /dev/null +++ b/bin/sh/trap.c @@ -0,0 +1,463 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)trap.c 8.5 (Berkeley) 6/5/95"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <signal.h> +#include <unistd.h> +#include <stdlib.h> + +#include "shell.h" +#include "main.h" +#include "nodes.h" /* for other headers */ +#include "eval.h" +#include "jobs.h" +#include "show.h" +#include "options.h" +#include "syntax.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "trap.h" +#include "mystring.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 permanently */ +#define S_RESET 5 /* temporary - to reset a hard ignored sig */ + + +MKINIT char sigmode[NSIG]; /* current value of signal */ +int pendingsigs; /* indicates some signal received */ +int in_dotrap; /* do we execute in a trap handler? */ +static char *volatile trap[NSIG]; /* trap handler commands */ +static volatile sig_atomic_t gotsig[NSIG]; + /* indicates specified signal received */ +static int ignore_sigchld; /* Used while handling SIGCHLD traps. */ + +static int getsigaction __P((int, sig_t *)); + + +/* + * Map a string to a signal number. + */ +static int +sigstring_to_signum(sig) + char *sig; +{ + + if (is_number(sig)) { + int signo; + + signo = atoi(sig); + return ((signo >= 0 && signo < NSIG) ? signo : (-1)); + } else if (strcasecmp(sig, "exit") == 0) { + return (0); + } else { + int n; + + if (strncasecmp(sig, "sig", 3) == 0) + sig += 3; + for (n = 1; n < NSIG; n++) + if (strcasecmp(sys_signame[n], sig) == 0) + return (n); + } + return (-1); +} + + +/* + * Print a list of valid signal names. + */ +static void +printsignals() +{ + int n; + + for (n = 1; n < NSIG; n++) { + out1fmt("%s", sys_signame[n]); + if (n == (NSIG / 2) || n == (NSIG - 1)) + out1str("\n"); + else + out1c(' '); + } +} + + +/* + * The trap builtin. + */ +int +trapcmd(argc, argv) + int argc; + char **argv; +{ + char *action; + int signo; + + if (argc <= 1) { + for (signo = 0 ; signo < NSIG ; signo++) { + if (trap[signo] != NULL) + out1fmt("trap -- '%s' %s\n", trap[signo], + (signo) ? sys_signame[signo] : "exit"); + } + return 0; + } + action = NULL; + if (*++argv && strcmp(*argv, "--") == 0) + argv++; + if (*argv && sigstring_to_signum(*argv) == -1) { + if ((*argv)[0] != '-') { + action = *argv; + argv++; + } else if ((*argv)[1] == '\0') { + argv++; + } else if ((*argv)[1] == 'l' && (*argv)[2] == '\0') { + printsignals(); + return 0; + } else { + error("bad option %s", *argv); + } + } + while (*argv) { + if ((signo = sigstring_to_signum(*argv)) == -1) + error("bad signal %s", *argv); + INTOFF; + if (action) + action = savestr(action); + if (trap[signo]) + ckfree(trap[signo]); + trap[signo] = action; + if (signo != 0) + setsignal(signo); + INTON; + argv++; + } + return 0; +} + + +/* + * Clear traps on a fork. + */ +void +clear_traps() +{ + char *volatile *tp; + + for (tp = trap ; tp <= &trap[NSIG - 1] ; 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. + */ +void +setsignal(signo) + int signo; +{ + int action; + sig_t sig, sigact = SIG_DFL; + char *t; + + if ((t = trap[signo]) == NULL) + action = S_DFL; + else if (*t != '\0') + action = S_CATCH; + else + action = S_IGN; + if (action == S_DFL) { + switch (signo) { + case SIGINT: + action = S_CATCH; + break; + case SIGQUIT: +#ifdef DEBUG + { + extern int debug; + + if (debug) + break; + } +#endif + action = S_CATCH; + break; + case SIGTERM: + if (rootshell && iflag) + action = S_IGN; + break; +#if JOBS + case SIGTSTP: + case SIGTTOU: + if (rootshell && mflag) + action = S_IGN; + break; +#endif + } + } + + t = &sigmode[signo]; + if (*t == 0) { + /* + * current setting unknown + */ + if (!getsigaction(signo, &sigact)) { + /* + * Pretend it worked; maybe we should give a warning + * here, but other shells don't. We don't alter + * sigmode, so that we retry every time. + */ + return; + } + 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; + switch (action) { + case S_DFL: sigact = SIG_DFL; break; + case S_CATCH: sigact = onsig; break; + case S_IGN: sigact = SIG_IGN; break; + } + *t = action; + sig = signal(signo, sigact); +#ifdef BSD + if (sig != SIG_ERR && action == S_CATCH) + siginterrupt(signo, 1); +#endif +} + + +/* + * Return the current setting for sig w/o changing it. + */ +static int +getsigaction(signo, sigact) + int signo; + sig_t *sigact; +{ + struct sigaction sa; + + if (sigaction(signo, (struct sigaction *)0, &sa) == -1) + return 0; + *sigact = (sig_t) sa.sa_handler; + return 1; +} + + +/* + * Ignore a signal. + */ +void +ignoresig(signo) + int signo; +{ + + if (sigmode[signo] != S_IGN && sigmode[signo] != S_HARD_IGN) { + signal(signo, SIG_IGN); + } + sigmode[signo] = S_HARD_IGN; +} + + +#ifdef mkinit +INCLUDE <signal.h> +INCLUDE "trap.h" + +SHELLPROC { + char *sm; + + clear_traps(); + for (sm = sigmode ; sm < sigmode + NSIG ; sm++) { + if (*sm == S_IGN) + *sm = S_HARD_IGN; + } +} +#endif + + +/* + * Signal handler. + */ +void +onsig(signo) + int signo; +{ + +#ifndef BSD + signal(signo, onsig); +#endif + if (signo == SIGINT && trap[SIGINT] == NULL) { + onint(); + return; + } + + if (signo != SIGCHLD || !ignore_sigchld) + gotsig[signo] = 1; + pendingsigs++; + + /* If we are currently in a wait builtin, prepare to break it */ + if ((signo == SIGINT || signo == SIGQUIT) && in_waitcmd != 0) + breakwaitcmd = 1; + /* + * If a trap is set, not ignored and not the null command, we need + * to make sure traps are executed even when a child blocks signals. + */ + if (Tflag && + trap[signo] != NULL && + ! trap[signo][0] == '\0' && + ! (trap[signo][0] == ':' && trap[signo][1] == '\0')) + breakwaitcmd = 1; +} + + +/* + * 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; + + in_dotrap++; + for (;;) { + for (i = 1; i < NSIG; i++) { + if (gotsig[i]) { + gotsig[i] = 0; + if (trap[i]) { + /* + * Ignore SIGCHLD to avoid infinite recursion + * if the trap action does a fork. + */ + if (i == SIGCHLD) + ignore_sigchld++; + savestatus = exitstatus; + evalstring(trap[i]); + exitstatus = savestatus; + if (i == SIGCHLD) + ignore_sigchld--; + } + break; + } + } + if (i >= NSIG) + break; + } + in_dotrap--; + pendingsigs = 0; +} + + +/* + * Controls whether the shell is interactive or not. + */ +void +setinteractive(on) + int on; +{ + static int is_interactive = -1; + + if (on == is_interactive) + return; + setsignal(SIGINT); + setsignal(SIGQUIT); + setsignal(SIGTERM); + is_interactive = on; +} + + +/* + * Called to exit the shell. + */ +void +exitshell(status) + int 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..2140c14 --- /dev/null +++ b/bin/sh/trap.h @@ -0,0 +1,50 @@ +/*- + * 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.3 (Berkeley) 6/5/95 + * $FreeBSD$ + */ + +extern int pendingsigs; +extern int in_dotrap; + +int trapcmd __P((int, char **)); +void clear_traps __P((void)); +void setsignal __P((int)); +void ignoresig __P((int)); +void onsig __P((int)); +void dotrap __P((void)); +void setinteractive __P((int)); +void exitshell __P((int)); diff --git a/bin/sh/var.c b/bin/sh/var.c new file mode 100644 index 0000000..056ae6f --- /dev/null +++ b/bin/sh/var.c @@ -0,0 +1,797 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 5/4/95"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <unistd.h> +#include <stdlib.h> + +/* + * Shell variables. + */ + +#include <locale.h> + +#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" +#include "parser.h" +#ifndef NO_HISTORY +#include "myhistedit.h" +#endif + + +#define VTABSIZE 39 + + +struct varinit { + struct var *var; + int flags; + char *text; + void (*func) __P((const char *)); +}; + + +#if ATTY +struct var vatty; +#endif +#ifndef NO_HISTORY +struct var vhistsize; +#endif +struct var vifs; +struct var vmail; +struct var vmpath; +struct var vpath; +struct var vps1; +struct var vps2; +struct var vvers; +#if ATTY +struct var vterm; +#endif +struct var voptind; + +const struct varinit varinit[] = { +#if ATTY + { &vatty, VSTRFIXED|VTEXTFIXED|VUNSET, "ATTY=", + NULL }, +#endif +#ifndef NO_HISTORY + { &vhistsize, VSTRFIXED|VTEXTFIXED|VUNSET, "HISTSIZE=", + sethistsize }, +#endif + { &vifs, VSTRFIXED|VTEXTFIXED, "IFS= \t\n", + NULL }, + { &vmail, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL=", + NULL }, + { &vmpath, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH=", + NULL }, + { &vpath, VSTRFIXED|VTEXTFIXED, "PATH=/bin:/usr/bin", + changepath }, + /* + * vps1 depends on uid + */ + { &vps2, VSTRFIXED|VTEXTFIXED, "PS2=> ", + NULL }, +#if ATTY + { &vterm, VSTRFIXED|VTEXTFIXED|VUNSET, "TERM=", + NULL }, +#endif + { &voptind, VSTRFIXED|VTEXTFIXED, "OPTIND=1", + getoptsreset }, + { NULL, 0, NULL, + NULL } +}; + +struct var *vartab[VTABSIZE]; + +STATIC struct var **hashvar __P((char *)); +STATIC int varequal __P((char *, char *)); +STATIC int localevar __P((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; + vp->func = ip->func; + } + } + /* + * 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; + } +} + +/* + * Safe version of setvar, returns 1 on success 0 on failure. + */ + +int +setvarsafe(name, val, flags) + char *name, *val; + int flags; +{ + struct jmploc jmploc; + struct jmploc *volatile savehandler = handler; + int err = 0; +#if __GNUC__ + /* Avoid longjmp clobbering */ + (void) &err; +#endif + + if (setjmp(jmploc.loc)) + err = 1; + else { + handler = &jmploc; + setvar(name, val, flags); + } + handler = savehandler; + return err; +} + +/* + * Set the value of a variable. The flags argument is tored with the + * flags of the variable. If val is NULL, the variable is unset. + */ + +void +setvar(name, val, flags) + char *name, *val; + int flags; +{ + char *p, *q; + int len; + int namelen; + char *nameeq; + int isbad; + + isbad = 0; + p = name; + if (! is_name(*p)) + isbad = 1; + p++; + 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); +} + +STATIC int +localevar(s) + char *s; + { + static char *lnames[7] = { + "ALL", "COLLATE", "CTYPE", "MONETARY", + "NUMERIC", "TIME", NULL + }; + char **ss; + + if (*s != 'L') + return 0; + if (varequal(s + 1, "ANG")) + return 1; + if (strncmp(s + 1, "C_", 2) != 0) + return 0; + for (ss = lnames; *ss ; ss++) + if (varequal(s + 3, *ss)) + return 1; + return 0; +} + +/* + * 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; + int flags; +{ + struct var *vp, **vpp; + + if (aflag) + flags |= VEXPORT; + vpp = hashvar(s); + for (vp = *vpp ; vp ; vp = vp->next) { + if (varequal(s, vp->text)) { + if (vp->flags & VREADONLY) { + size_t len = strchr(s, '=') - s; + error("%.*s: is read only", len, s); + } + INTOFF; + + if (vp->func && (flags & VNOFUNC) == 0) + (*vp->func)(strchr(s, '=') + 1); + + if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) + ckfree(vp->text); + + vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET); + vp->flags |= flags; + vp->text = s; + + /* + * We could roll this to a function, to handle it as + * a regular variable function callback, but why bother? + */ + if (vp == &vmpath || (vp == &vmail && ! mpathset())) + chkmail(1); + if ((vp->flags & VEXPORT) && localevar(s)) { + putenv(s); + (void) setlocale(LC_ALL, ""); + } + INTON; + return; + } + } + /* not found */ + vp = ckmalloc(sizeof (*vp)); + vp->flags = flags; + vp->text = s; + vp->next = *vpp; + vp->func = NULL; + INTOFF; + *vpp = vp; + if ((vp->flags & VEXPORT) && localevar(s)) { + putenv(s); + (void) setlocale(LC_ALL, ""); + } + INTON; +} + + + +/* + * 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; + int doall; +{ + 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) + int argc __unused; + char **argv __unused; +{ + 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) + int argc; + 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; + if ((vp->flags & VEXPORT) && localevar(vp->text)) { + putenv(vp->text); + (void) setlocale(LC_ALL, ""); + } + 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. + */ + +int +localcmd(argc, argv) + int argc __unused; + char **argv __unused; +{ + 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); + memcpy(lvp->text, optlist, 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 */ + memcpy(optlist, lvp->text, 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); + } +} + + +int +setvarcmd(argc, argv) + int argc; + 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. + */ + +int +unsetcmd(argc, argv) + int argc __unused; + char **argv __unused; +{ + 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. + */ + +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); + if ((vp->flags & VEXPORT) && localevar(vp->text)) { + unsetenv(s); + setlocale(LC_ALL, ""); + } + 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) + char *p; + { + unsigned int hashval; + + hashval = ((unsigned char) *p) << 4; + while (*p && *p != '=') + hashval += (unsigned char) *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) + 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..1a215fc --- /dev/null +++ b/bin/sh/var.h @@ -0,0 +1,133 @@ +/*- + * 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.2 (Berkeley) 5/4/95 + * $FreeBSD$ + */ + +/* + * Shell variables. + */ + +/* flags */ +#define VEXPORT 0x01 /* variable is exported */ +#define VREADONLY 0x02 /* variable cannot be modified */ +#define VSTRFIXED 0x04 /* variable struct is staticly allocated */ +#define VTEXTFIXED 0x08 /* text is staticly allocated */ +#define VSTACK 0x10 /* text is allocated on the stack */ +#define VUNSET 0x20 /* the variable is not set */ +#define VNOFUNC 0x40 /* don't call the callback function */ + + +struct var { + struct var *next; /* next entry in hash list */ + int flags; /* flags are defined above */ + char *text; /* name=value */ + void (*func) __P((const char *)); + /* function to be called when */ + /* the variable gets set/unset */ +}; + + +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 +#ifndef NO_HISTORY +extern struct var vhistsize; +#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 ifsset() ((vifs.flags & VUNSET) == 0) +#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 +#define optindval() (voptind.text + 7) +#ifndef NO_HISTORY +#define histsizeval() (vhistsize.text + 9) +#endif + +#if ATTY +#define attyset() ((vatty.flags & VUNSET) == 0) +#endif +#define mpathset() ((vmpath.flags & VUNSET) == 0) + +void initvar __P((void)); +void setvar __P((char *, char *, int)); +void setvareq __P((char *, int)); +struct strlist; +void listsetvar __P((struct strlist *)); +char *lookupvar __P((char *)); +char *bltinlookup __P((char *, int)); +char **environment __P((void)); +void shprocvar __P((void)); +int showvarscmd __P((int, char **)); +int exportcmd __P((int, char **)); +int localcmd __P((int, char **)); +void mklocal __P((char *)); +void poplocalvars __P((void)); +int setvarcmd __P((int, char **)); +int unsetcmd __P((int, char **)); +int unsetvar __P((char *)); +int setvarsafe __P((char *, char *, int)); diff --git a/bin/sleep/Makefile b/bin/sleep/Makefile new file mode 100644 index 0000000..4ff7330 --- /dev/null +++ b/bin/sleep/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 +# $FreeBSD$ + +PROG= sleep + +.include <bsd.prog.mk> diff --git a/bin/sleep/sleep.1 b/bin/sleep/sleep.1 new file mode 100644 index 0000000..41d6664 --- /dev/null +++ b/bin/sleep/sleep.1 @@ -0,0 +1,131 @@ +.\" 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 +.\" $FreeBSD$ +.\" +.Dd April 18, 1994 +.Dt SLEEP 1 +.Os +.Sh NAME +.Nm sleep +.Nd suspend execution for an interval of time +.Sh SYNOPSIS +.Nm +.Ar seconds +.Sh DESCRIPTION +The +.Nm +command +suspends execution for a minimum of +.Ar seconds . +.Pp +If the +.Nm +command receives a signal, it takes the standard action. +.Sh IMPLEMENTATION NOTES +The +.Dv SIGALRM +signal is not handled specially by this implementation. +.Pp +The +.Nm +command will accept and honor a non-integer number of specified seconds +.Po +with a +.Ql \&. +character as a decimal point +.Pc . +.Bf Sy +This is a non-portable extension, and its use will nearly guarantee that +a shell script will not execute properly on another system. +.Ef +.Sh DIAGNOSTICS +The +.Nm +utility exits with one of the following values: +.Bl -tag -width flag +.It Li \&0 +On successful completion. +.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 nanosleep 2 , +.Xr sleep 3 +.Sh STANDARDS +The +.Nm +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..0083987 --- /dev/null +++ b/bin/sleep/sleep.c @@ -0,0 +1,136 @@ +/* + * 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 const copyright[] = +"@(#) Copyright (c) 1988, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)sleep.c 8.3 (Berkeley) 4/2/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <ctype.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct timespec time_to_sleep; + long l; + int ch, neg; + char *p; + + while ((ch = getopt(argc, argv, "")) != -1) + switch(ch) { + case '?': + default: + usage(); + /* NOTREACHED */ + } + argc -= optind; + argv += optind; + + if (argc != 1) { + usage(); + /* NOTREACHED */ + } + + p = argv[0]; + + /* Skip over leading whitespaces. */ + while (isspace((unsigned char)*p)) + ++p; + + /* Check for optional `+' or `-' sign. */ + neg = 0; + if (*p == '-') { + neg = 1; + ++p; + } + else if (*p == '+') + ++p; + + /* Calculate seconds. */ + if (isdigit((unsigned char)*p)) { + l = strtol(p, &p, 10); + if (l > INT_MAX) { + /* + * Avoid overflow when `seconds' is huge. This assumes + * that the maximum value for a time_t is >= INT_MAX. + */ + l = INT_MAX; + } + } else + l = 0; + time_to_sleep.tv_sec = (time_t)l; + + /* Calculate nanoseconds. */ + time_to_sleep.tv_nsec = 0; + + if (*p == '.') { /* Decimal point. */ + l = 100000000L; + do { + if (isdigit((unsigned char)*++p)) + time_to_sleep.tv_nsec += (*p - '0') * l; + else + break; + } while (l /= 10); + } + + if (!neg && (time_to_sleep.tv_sec > 0 || time_to_sleep.tv_nsec > 0)) + (void)nanosleep(&time_to_sleep, (struct timespec *)NULL); + + 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..82b15eb --- /dev/null +++ b/bin/stty/Makefile @@ -0,0 +1,7 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 +# $FreeBSD$ + +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..847cf74 --- /dev/null +++ b/bin/stty/cchar.c @@ -0,0 +1,144 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)cchar.c 8.5 (Berkeley) 4/2/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#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, cchars2, + sizeof(cchars2)/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 > 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..27869f8 --- /dev/null +++ b/bin/stty/extern.h @@ -0,0 +1,49 @@ +/*- + * 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 + * $FreeBSD$ + */ + +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..b34a117 --- /dev/null +++ b/bin/stty/gfmt.c @@ -0,0 +1,135 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)gfmt.c 8.6 (Berkeley) 4/2/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#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=%lx:iflag=%lx:lflag=%lx:oflag=%lx:", + (u_long)tp->c_cflag, (u_long)tp->c_iflag, (u_long)tp->c_lflag, + (u_long)tp->c_oflag); + for (cp = cchars1; cp->name; ++cp) + (void)printf("%s=%x:", cp->name, tp->c_cc[cp->sub]); + (void)printf("ispeed=%lu:ospeed=%lu\n", + (u_long)cfgetispeed(tp), (u_long)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..ba33a81 --- /dev/null +++ b/bin/stty/key.c @@ -0,0 +1,304 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)key.c 8.3 (Berkeley) 4/2/94"; +#else +static const char rcsid[] = + "$FreeBSD$"; +#endif +#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) { + warnx("illegal option -- -%s", name); + usage(); + } + if (kp->flags & F_NEEDARG && !(ip->arg = *++*argvp)) { + warnx("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->off) { + int tmp = 0; + (void)ioctl(ip->fd, TIOCEXT, &tmp); + } else { + int tmp = 1; + (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("%lu\n", (u_long)cfgetospeed(&ip->t)); +} + +void +f_tty(ip) + struct info *ip; +{ + int tmp; + + tmp = TTYDISC; + if (ioctl(ip->fd, TIOCSETD, &tmp) < 0) + err(1, "TIOCSETD"); +} diff --git a/bin/stty/modes.c b/bin/stty/modes.c new file mode 100644 index 0000000..b1f333e --- /dev/null +++ b/bin/stty/modes.c @@ -0,0 +1,242 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)modes.c 8.3 (Berkeley) 4/2/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#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 }, + { "ctsflow", CCTS_OFLOW, 0 }, + { "-ctsflow", 0, CCTS_OFLOW }, + { "dsrflow", CDSR_OFLOW, 0 }, + { "-dsrflow", 0, CDSR_OFLOW }, + { "dtrflow", CDTR_IFLOW, 0 }, + { "-dtrflow", 0, CDTR_IFLOW }, + { "rtsflow", CRTS_IFLOW, 0 }, + { "-rtsflow", 0, CRTS_IFLOW }, + { "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..a0149c2 --- /dev/null +++ b/bin/stty/print.c @@ -0,0 +1,288 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)print.c 8.6 (Berkeley) 4/16/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <stddef.h> +#include <stdio.h> +#include <string.h> + +#include "stty.h" +#include "extern.h" + +#include <sys/ioctl_compat.h> /* XXX NTTYDISC is too well hidden */ + +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 NTTYDISC: + cnt += printf("new tty disc; "); + break; + case SLIPDISC: + cnt += printf("slip disc; "); + break; + case PPPDISC: + cnt += printf("ppp 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); + switch(tmp & (CCTS_OFLOW | CRTS_IFLOW)) { + case CCTS_OFLOW: + bput("ctsflow"); + break; + case CRTS_IFLOW: + bput("rtsflow"); + break; + default: + put("-crtscts", CCTS_OFLOW | CRTS_IFLOW, 0); + break; + } + put("-dsrflow", CDSR_OFLOW, 0); + put("-dtrflow", CDTR_IFLOW, 0); + put("-mdmbuf", MDMBUF, 0); /* XXX mdmbuf == dtrflow */ + + /* 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)snprintf(buf1 + cnt * 8, sizeof(buf1) - cnt * 8, + WD, p->name); + (void)snprintf(buf2 + cnt * 8, sizeof(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 (p->sub == VMIN || p->sub == VTIME) { + (void)snprintf(buf, sizeof(buf), "%d", c); + return (buf); + } + if (c == _POSIX_VDISABLE) + return ("<undef>"); + 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..26732d2 --- /dev/null +++ b/bin/stty/stty.1 @@ -0,0 +1,585 @@ +.\" 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 +.\" $FreeBSD$ +.\" +.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 +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 indent +.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 +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 (disable) 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) characters with parity +errors. +.It Cm parmrk Pq Fl parmrk +Mark (do not mark) characters with 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 +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 +.Nm +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 oxtabs . +.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 +.Sh DIAGNOSTICS +The +.Nm +utility exits 0 on success, and >0 if an error occurs. +.Sh SEE ALSO +.Xr termios 4 +.Sh STANDARDS +The +.Nm +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..7a83c4f --- /dev/null +++ b/bin/stty/stty.c @@ -0,0 +1,166 @@ +/*- + * 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 const copyright[] = +"@(#) Copyright (c) 1989, 1991, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)stty.c 8.3 (Berkeley) 4/2/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#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")) != -1) + 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 (tcgetattr(i.fd, &i.t) < 0) + errx(1, "stdin isn't a terminal"); + if (ioctl(i.fd, TIOCGETD, &i.ldisc) < 0) + err(1, "TIOCGETD"); + if (ioctl(i.fd, TIOCGWINSZ, &i.win) < 0) + warn("TIOCGWINSZ"); + + 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); + i.set = 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..e122979 --- /dev/null +++ b/bin/stty/stty.h @@ -0,0 +1,59 @@ +/*- + * 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 + * $FreeBSD$ + */ + +#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..b99e83f --- /dev/null +++ b/bin/stty/util.c @@ -0,0 +1,68 @@ +/*- + * 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 +#if 0 +static char sccsid[] = "@(#)util.c 8.3 (Berkeley) 4/2/94"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#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)) +warnx("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..9698a64 --- /dev/null +++ b/bin/sync/Makefile @@ -0,0 +1,7 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 +# $FreeBSD$ + +PROG= sync +MAN8= sync.8 + +.include <bsd.prog.mk> diff --git a/bin/sync/sync.8 b/bin/sync/sync.8 new file mode 100644 index 0000000..ea45b3d --- /dev/null +++ b/bin/sync/sync.8 @@ -0,0 +1,74 @@ +.\" 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 +.\" $FreeBSD$ +.\" +.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 8 +or +.Xr halt 8 +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 Ns . +.Pp +.Nm Sync +utilizes the +.Xr sync 2 +function call. +.Sh SEE ALSO +.Xr fsync 2 , +.Xr sync 2 , +.Xr syncer 4 , +.Xr halt 8 , +.Xr reboot 8 +.Sh HISTORY +A +.Nm +command appeared in +.At v6 . diff --git a/bin/sync/sync.c b/bin/sync/sync.c new file mode 100644 index 0000000..6fe3160 --- /dev/null +++ b/bin/sync/sync.c @@ -0,0 +1,55 @@ +/* + * 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 const copyright[] = +"@(#) Copyright (c) 1987, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)sync.c 8.1 (Berkeley) 5/31/93"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <unistd.h> + +int +main() +{ + sync(); + exit(0); +} diff --git a/bin/test/Makefile b/bin/test/Makefile new file mode 100644 index 0000000..c1bc62c --- /dev/null +++ b/bin/test/Makefile @@ -0,0 +1,8 @@ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 +# $FreeBSD$ + +PROG= test +LINKS= ${BINDIR}/test ${BINDIR}/[ +MLINKS= test.1 '[.1' + +.include <bsd.prog.mk> diff --git a/bin/test/TEST.README b/bin/test/TEST.README new file mode 100644 index 0000000..f291953 --- /dev/null +++ b/bin/test/TEST.README @@ -0,0 +1,24 @@ +# $FreeBSD$ + +OS/shell syntax error failed +-------------------------------------------------------------------- +bash 1.14.5 3 0 +pdksh 5.2.5 7 0 +zsh 2.6-beta17 6 4 +FreeBSD 2.1 /bin/test 8 0 +IRIX 5.3 ksh 3 0 +IRIX 5.3 sh 3 0 +IRIX 5.3 /usr/bin/test 11 3 +SunOS 5.4 ksh 3 0 +SunOS 5.4 sh 3 0 +SunOS 5.4 /usr/ucb/test 3 0 +SunOS 5.5 ksh 0 0 +SunOS 5.5 sh 3 0 +SunOS 5.5 /usr/ucb/test 3 0 +SunOP 4.1.3_U1 sh 3 0 +SunOP 4.1.3_U1 /usr/bin/test 3 0 +ULTRIX 4.2 /bin/test 9 0 +ULTRIX 4.2 ksh 1 0 +ULTRIX 4.2 sh5 4 0 + +96/06/16 diff --git a/bin/test/TEST.csh b/bin/test/TEST.csh new file mode 100644 index 0000000..afa1c91 --- /dev/null +++ b/bin/test/TEST.csh @@ -0,0 +1,152 @@ +# @(#)TEST.csh 5.2 (Berkeley) 4/30/93 +# $FreeBSD$ + +#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" \) + +echo 't -5 -eq 5' +t -5 -eq 5 + + +echo 't foo -a ""' +t foo -a "" +echo 't "" -a foo' +t "" -a foo +echo 't "" -a ""' +t "" -a "" +echo 't "" -o ""' +t "" -o "" + diff --git a/bin/test/TEST.sh b/bin/test/TEST.sh new file mode 100644 index 0000000..d3aa83c --- /dev/null +++ b/bin/test/TEST.sh @@ -0,0 +1,135 @@ +#!/bin/sh +# +# Copyright (c) June 1996 Wolfram Schneider <wosch@FreeBSD.org>. Berlin. +# 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. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (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.sh - check if test(1) or builtin test works +# +# $FreeBSD$ + +# force a specified test program, e.g. `env test=/bin/test sh TEST.sh' +: ${test=test} + +ERROR=0 FAILED=0 + +t () +{ + # $1 -> exit code + # $2 -> $test expression + + echo -n "$1: $test $2 " + + # check for syntax errors + syntax="`eval $test $2 2>&1`" + if test -z "$syntax"; then + + case $1 in + 0) if eval $test $2; then echo " OK"; else failed;fi;; + 1) if eval $test $2; then failed; else echo " OK";fi;; + esac + + else + error + fi +} + +error () +{ + echo ""; echo " $syntax" + ERROR=`expr $ERROR + 1` +} + +failed () +{ + echo ""; echo " failed" + FAILED=`expr $FAILED + 1` +} + + +t 0 'b = b' +t 1 'b != b' +t 0 '\( b = b \)' +t 1 '! \( b = b \)' +t 1 '! -f /etc/passwd' + +t 0 '-h = -h' +t 0 '-o = -o' +t 1 '-f = h' +t 1 '-h = f' +t 1 '-o = f' +t 1 'f = -o' +t 0 '\( -h = -h \)' +t 1 '\( a = -h \)' +t 1 '\( -f = h \)' +t 0 '-h = -h -o a' +t 0 '\( -h = -h \) -o 1' +t 0 '-h = -h -o -h = -h' +t 0 '\( -h = -h \) -o \( -h = -h \)' +t 0 'roedelheim = roedelheim' +t 1 'potsdam = berlin-dahlem' + +t 0 '-d /' +t 0 '-d / -a a != b' +t 1 '-z "-z"' +t 0 '-n -n' + +t 0 '0' +t 0 '\( 0 \)' +t 0 '-E' +t 0 '-X -a -X' +t 0 '-XXX' +t 0 '\( -E \)' +t 0 'true -o X' +t 0 'true -o -X' +t 0 '\( \( \( a = a \) -o 1 \) -a 1 \) -a true' +t 1 '-h /' +t 0 '-r /' +t 1 '-w /' +t 0 '-x /bin/sh' +t 0 '-c /dev/null' +t 0 '-f /etc/passwd' +t 0 '-s /etc/passwd' + +t 1 '! \( 700 -le 1000 -a -n "1" -a "20" = "20" \)' +t 0 '100 -eq 100' +t 0 '100 -lt 200' +t 1 '1000 -lt 200' +t 0 '1000 -gt 200' +t 0 '1000 -ge 200' +t 0 '1000 -ge 1000' +t 1 '2 -ne 2' +t 0 '0 -eq 0' +t 1 '-5 -eq 5' +t 0 '\( 0 -eq 0 \)' +t 1 '1 -eq 0 -o a = a -a 1 -eq 0 -o a = aa' + +t 1 '"" -o ""' +t 1 '"" -a ""' +t 1 '"a" -a ""' +t 0 '"a" -a ! ""' +t 1 '""' +t 0 '! ""' + +echo "" +echo "Syntax errors: $ERROR Failed: $FAILED" diff --git a/bin/test/test.1 b/bin/test/test.1 new file mode 100644 index 0000000..da9686b --- /dev/null +++ b/bin/test/test.1 @@ -0,0 +1,320 @@ +.\" 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 +.\" $FreeBSD$ +.\" +.Dd May 31, 1993 +.Dt TEST 1 +.Os +.Sh NAME +.Nm test , +.Nm \&[ +.Nd condition evaluation utility +.Sh SYNOPSIS +.Nm test +.Ar expression +.Nm [ +.Ar expression Cm ] +.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. +This operator is retained for compatibility with previous versions of +this program. +Do not rely on its existence; use +.Fl L +instead. +.It Fl k Ar file +True if +.Ar file +exists and its sticky bit is set. +.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 Op Ar file_descriptor +True if the file whose file descriptor number +is +.Ar file_descriptor +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 Fl L Ar file +True if +.Ar file +exists and is a symbolic link. +.It Fl O Ar file +True if +.Ar file +exists and its owner matches the effective user id of this process. +.It Fl G Ar file +True if +.Ar file +exists and its group matches the effective group id of this process. +.It Fl S Ar file +True if +.Ar file +exists and is a socket. +.It Ar file1 Fl nt Ar file2 +True if +.Ar file1 +exists and is newer than +.Ar file2 . +.It Ar file1 Fl ot Ar file2 +True if +.Ar file1 +exists and is older than +.Ar file2 . +.It Ar file1 Fl ef Ar file2 +True if +.Ar file1 +and +.Ar file2 +exist and refer to the same file. +.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 \&s\&1 Cm \&< Ar \&s\&2 +True if string +.Ar \&s\&1 +comes before +.Ar \&s\&2 +based on the ASCII value of their characters. +.It Ar \&s\&1 Cm \&> Ar \&s\&2 +True if string +.Ar \&s\&1 +comes after +.Ar \&s\&2 +based on the ASCII value of their characters. +.It Ar \&s\&1 +True if +.Ar \&s\&1 +is not the null +string. +.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 SEE ALSO +.Xr expr 1 , +.Xr sh 1 +.Sh STANDARDS +The +.Nm test +utility implements a superset of the +.St -p1003.2 +specification. diff --git a/bin/test/test.c b/bin/test/test.c new file mode 100644 index 0000000..73e6ebf --- /dev/null +++ b/bin/test/test.c @@ -0,0 +1,522 @@ +/* $NetBSD: test.c,v 1.21 1999/04/05 09:48:38 kleink Exp $ */ + +/* + * test(1); version 7-like -- author Erik Baalbergen + * modified by Eric Gisin to be used as built-in. + * modified by Arnold Robbins to add SVR3 compatibility + * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket). + * modified by J.T. Conklin for NetBSD. + * + * This program is in the Public Domain. + */ + +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +/* test(1) accepts the following grammar: + oexpr ::= aexpr | aexpr "-o" oexpr ; + aexpr ::= nexpr | nexpr "-a" aexpr ; + nexpr ::= primary | "!" primary + primary ::= unary-operator operand + | operand binary-operator operand + | operand + | "(" oexpr ")" + ; + unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"| + "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S"; + + binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"| + "-nt"|"-ot"|"-ef"; + operand ::= <any legal UNIX file name> +*/ + +enum token { + EOI, + FILRD, + FILWR, + FILEX, + FILEXIST, + FILREG, + FILDIR, + FILCDEV, + FILBDEV, + FILFIFO, + FILSOCK, + FILSYM, + FILGZ, + FILTT, + FILSUID, + FILSGID, + FILSTCK, + FILNT, + FILOT, + FILEQ, + FILUID, + FILGID, + STREZ, + STRNZ, + STREQ, + STRNE, + STRLT, + STRGT, + INTEQ, + INTNE, + INTGE, + INTGT, + INTLE, + INTLT, + UNOT, + BAND, + BOR, + LPAREN, + RPAREN, + OPERAND +}; + +enum token_types { + UNOP, + BINOP, + BUNOP, + BBINOP, + PAREN +}; + +struct t_op { + const char *op_text; + short op_num, op_type; +} const ops [] = { + {"-r", FILRD, UNOP}, + {"-w", FILWR, UNOP}, + {"-x", FILEX, UNOP}, + {"-e", FILEXIST,UNOP}, + {"-f", FILREG, UNOP}, + {"-d", FILDIR, UNOP}, + {"-c", FILCDEV,UNOP}, + {"-b", FILBDEV,UNOP}, + {"-p", FILFIFO,UNOP}, + {"-u", FILSUID,UNOP}, + {"-g", FILSGID,UNOP}, + {"-k", FILSTCK,UNOP}, + {"-s", FILGZ, UNOP}, + {"-t", FILTT, UNOP}, + {"-z", STREZ, UNOP}, + {"-n", STRNZ, UNOP}, + {"-h", FILSYM, UNOP}, /* for backwards compat */ + {"-O", FILUID, UNOP}, + {"-G", FILGID, UNOP}, + {"-L", FILSYM, UNOP}, + {"-S", FILSOCK,UNOP}, + {"=", STREQ, BINOP}, + {"!=", STRNE, BINOP}, + {"<", STRLT, BINOP}, + {">", STRGT, BINOP}, + {"-eq", INTEQ, BINOP}, + {"-ne", INTNE, BINOP}, + {"-ge", INTGE, BINOP}, + {"-gt", INTGT, BINOP}, + {"-le", INTLE, BINOP}, + {"-lt", INTLT, BINOP}, + {"-nt", FILNT, BINOP}, + {"-ot", FILOT, BINOP}, + {"-ef", FILEQ, BINOP}, + {"!", UNOT, BUNOP}, + {"-a", BAND, BBINOP}, + {"-o", BOR, BBINOP}, + {"(", LPAREN, PAREN}, + {")", RPAREN, PAREN}, + {0, 0, 0} +}; + +struct t_op const *t_wp_op; +char **t_wp; + +static void syntax __P((const char *, const char *)); +static enum token t_lex __P((char *)); +static int oexpr __P((enum token)); +static int aexpr __P((enum token)); +static int nexpr __P((enum token)); +static int primary __P((enum token)); +static int binop __P((void)); +static int filstat __P((char *, enum token)); +static int isoperand __P((void)); +static int getn __P((const char *)); +static quad_t getq __P((const char *)); +static int intcmp __P((const char *, const char *)); +static int newerf __P((const char *, const char *)); +static int olderf __P((const char *, const char *)); +static int equalf __P((const char *, const char *)); + +int +main(argc, argv) + int argc; + char **argv; +{ + int res; + char *p; + + if ((p = rindex(argv[0], '/')) == NULL) + p = argv[0]; + else + p++; + if (strcmp(p, "[") == 0) { + if (strcmp(argv[--argc], "]")) + errx(2, "missing ]"); + argv[argc] = NULL; + } + + /* XXX work around the absence of an eaccess(2) syscall */ + (void)setgid(getegid()); + (void)setuid(geteuid()); + + t_wp = &argv[1]; + res = !oexpr(t_lex(*t_wp)); + + if (*t_wp != NULL && *++t_wp != NULL) + syntax(*t_wp, "unexpected operator"); + + return res; +} + +static void +syntax(op, msg) + const char *op; + const char *msg; +{ + + if (op && *op) + errx(2, "%s: %s", op, msg); + else + errx(2, "%s", msg); +} + +static int +oexpr(n) + enum token n; +{ + int res; + + res = aexpr(n); + if (t_lex(*++t_wp) == BOR) + return oexpr(t_lex(*++t_wp)) || res; + t_wp--; + return res; +} + +static int +aexpr(n) + enum token n; +{ + int res; + + res = nexpr(n); + if (t_lex(*++t_wp) == BAND) + return aexpr(t_lex(*++t_wp)) && res; + t_wp--; + return res; +} + +static int +nexpr(n) + enum token n; /* token */ +{ + if (n == UNOT) + return !nexpr(t_lex(*++t_wp)); + return primary(n); +} + +static int +primary(n) + enum token n; +{ + enum token nn; + int res; + + if (n == EOI) + return 0; /* missing expression */ + if (n == LPAREN) { + if ((nn = t_lex(*++t_wp)) == RPAREN) + return 0; /* missing expression */ + res = oexpr(nn); + if (t_lex(*++t_wp) != RPAREN) + syntax(NULL, "closing paren expected"); + return res; + } + if (t_wp_op && t_wp_op->op_type == UNOP) { + /* unary expression */ + if (*++t_wp == NULL) + syntax(t_wp_op->op_text, "argument expected"); + switch (n) { + case STREZ: + return strlen(*t_wp) == 0; + case STRNZ: + return strlen(*t_wp) != 0; + case FILTT: + return isatty(getn(*t_wp)); + default: + return filstat(*t_wp, n); + } + } + + if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) { + return binop(); + } + + return strlen(*t_wp) > 0; +} + +static int +binop() +{ + const char *opnd1, *opnd2; + struct t_op const *op; + + opnd1 = *t_wp; + (void) t_lex(*++t_wp); + op = t_wp_op; + + if ((opnd2 = *++t_wp) == NULL) + syntax(op->op_text, "argument expected"); + + switch (op->op_num) { + case STREQ: + return strcmp(opnd1, opnd2) == 0; + case STRNE: + return strcmp(opnd1, opnd2) != 0; + case STRLT: + return strcmp(opnd1, opnd2) < 0; + case STRGT: + return strcmp(opnd1, opnd2) > 0; + case INTEQ: + return intcmp(opnd1, opnd2) == 0; + case INTNE: + return intcmp(opnd1, opnd2) != 0; + case INTGE: + return intcmp(opnd1, opnd2) >= 0; + case INTGT: + return intcmp(opnd1, opnd2) > 0; + case INTLE: + return intcmp(opnd1, opnd2) <= 0; + case INTLT: + return intcmp(opnd1, opnd2) < 0; + case FILNT: + return newerf (opnd1, opnd2); + case FILOT: + return olderf (opnd1, opnd2); + case FILEQ: + return equalf (opnd1, opnd2); + default: + abort(); + /* NOTREACHED */ + } +} + +static int +filstat(nm, mode) + char *nm; + enum token mode; +{ + struct stat s; + + if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s)) + return 0; + + switch (mode) { + case FILRD: + return access(nm, R_OK) == 0; + case FILWR: + return access(nm, W_OK) == 0; + case FILEX: + /* XXX work around access(2) false positives for superuser */ + if (access(nm, X_OK) != 0) + return 0; + if (S_ISDIR(s.st_mode) || getuid() != 0) + return 1; + return (s.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0; + case FILEXIST: + return access(nm, F_OK) == 0; + case FILREG: + return S_ISREG(s.st_mode); + case FILDIR: + return S_ISDIR(s.st_mode); + case FILCDEV: + return S_ISCHR(s.st_mode); + case FILBDEV: + return S_ISBLK(s.st_mode); + case FILFIFO: + return S_ISFIFO(s.st_mode); + case FILSOCK: + return S_ISSOCK(s.st_mode); + case FILSYM: + return S_ISLNK(s.st_mode); + case FILSUID: + return (s.st_mode & S_ISUID) != 0; + case FILSGID: + return (s.st_mode & S_ISGID) != 0; + case FILSTCK: + return (s.st_mode & S_ISVTX) != 0; + case FILGZ: + return s.st_size > (off_t)0; + case FILUID: + return s.st_uid == geteuid(); + case FILGID: + return s.st_gid == getegid(); + default: + return 1; + } +} + +static enum token +t_lex(s) + char *s; +{ + struct t_op const *op = ops; + + if (s == 0) { + t_wp_op = NULL; + return EOI; + } + while (op->op_text) { + if (strcmp(s, op->op_text) == 0) { + if ((op->op_type == UNOP && isoperand()) || + (op->op_num == LPAREN && *(t_wp+1) == 0)) + break; + t_wp_op = op; + return op->op_num; + } + op++; + } + t_wp_op = NULL; + return OPERAND; +} + +static int +isoperand() +{ + struct t_op const *op = ops; + char *s; + char *t; + + if ((s = *(t_wp+1)) == 0) + return 1; + if ((t = *(t_wp+2)) == 0) + return 0; + while (op->op_text) { + if (strcmp(s, op->op_text) == 0) + return op->op_type == BINOP && + (t[0] != ')' || t[1] != '\0'); + op++; + } + return 0; +} + +/* atoi with error detection */ +static int +getn(s) + const char *s; +{ + char *p; + long r; + + errno = 0; + r = strtol(s, &p, 10); + + if (errno != 0) + errx(2, "%s: out of range", s); + + while (isspace((unsigned char)*p)) + p++; + + if (*p) + errx(2, "%s: bad number", s); + + return (int) r; +} + +/* atoi with error detection and 64 bit range */ +static quad_t +getq(s) + const char *s; +{ + char *p; + quad_t r; + + errno = 0; + r = strtoq(s, &p, 10); + + if (errno != 0) + errx(2, "%s: out of range", s); + + while (isspace((unsigned char)*p)) + p++; + + if (*p) + errx(2, "%s: bad number", s); + + return r; +} + +static int +intcmp (s1, s2) + const char *s1, *s2; +{ + quad_t q1, q2; + + + q1 = getq(s1); + q2 = getq(s2); + + if (q1 > q2) + return 1; + + if (q1 < q2) + return -1; + + return 0; +} + +static int +newerf (f1, f2) + const char *f1, *f2; +{ + struct stat b1, b2; + + return (stat (f1, &b1) == 0 && + stat (f2, &b2) == 0 && + b1.st_mtime > b2.st_mtime); +} + +static int +olderf (f1, f2) + const char *f1, *f2; +{ + struct stat b1, b2; + + return (stat (f1, &b1) == 0 && + stat (f2, &b2) == 0 && + b1.st_mtime < b2.st_mtime); +} + +static int +equalf (f1, f2) + const char *f1, *f2; +{ + struct stat b1, b2; + + return (stat (f1, &b1) == 0 && + stat (f2, &b2) == 0 && + b1.st_dev == b2.st_dev && + b1.st_ino == b2.st_ino); +} |