From c46940197cebd4c5cb0dc0f610ba16bc47721c55 Mon Sep 17 00:00:00 2001 From: Bruce Evans Date: Tue, 1 Jul 1997 22:34:08 +0000 Subject: Import Lite2's src/bin. All files in src/bin are off the vendor branch, so this doesn't change the active versions. --- bin/cat/cat.1 | 120 +++ bin/cat/cat.c | 252 ++++++ bin/cp/cp.c | 448 ++++++++++ bin/csh/Makefile | 44 + bin/csh/USD.doc/csh.1 | 1011 ++++++++++++++++++++++ bin/csh/USD.doc/csh.2 | 1304 ++++++++++++++++++++++++++++ bin/csh/USD.doc/csh.3 | 651 ++++++++++++++ bin/csh/USD.doc/csh.4 | 177 ++++ bin/csh/csh.1 | 2185 +++++++++++++++++++++++++++++++++++++++++++++++ bin/csh/csh.c | 1368 +++++++++++++++++++++++++++++ bin/csh/csh.h | 552 ++++++++++++ bin/csh/dir.c | 930 ++++++++++++++++++++ bin/csh/dol.c | 978 +++++++++++++++++++++ bin/csh/exec.c | 746 ++++++++++++++++ bin/csh/file.c | 690 +++++++++++++++ bin/csh/func.c | 1517 ++++++++++++++++++++++++++++++++ bin/csh/hist.c | 188 ++++ bin/csh/lex.c | 1639 +++++++++++++++++++++++++++++++++++ bin/csh/proc.c | 1379 ++++++++++++++++++++++++++++++ bin/csh/sem.c | 653 ++++++++++++++ bin/csh/set.c | 848 ++++++++++++++++++ bin/date/date.1 | 232 +++++ bin/date/date.c | 237 +++++ bin/date/netdate.c | 183 ++++ bin/hostname/hostname.1 | 66 ++ bin/hostname/hostname.c | 94 ++ bin/kill/kill.1 | 142 +++ bin/kill/kill.c | 187 ++++ bin/ls/ls.1 | 338 ++++++++ bin/ls/ls.c | 518 +++++++++++ bin/ls/print.c | 296 +++++++ bin/ls/stat_flags.c | 153 ++++ bin/ls/util.c | 72 ++ bin/ps/fmt.c | 119 +++ bin/pwd/pwd.1 | 68 ++ bin/rm/rm.1 | 155 ++++ bin/rm/rm.c | 403 +++++++++ bin/rmail/rmail.c | 373 ++++++++ bin/stty/key.c | 300 +++++++ bin/stty/stty.1 | 585 +++++++++++++ 40 files changed, 22201 insertions(+) create mode 100644 bin/cat/cat.1 create mode 100644 bin/cat/cat.c create mode 100644 bin/cp/cp.c create mode 100644 bin/csh/Makefile create mode 100644 bin/csh/USD.doc/csh.1 create mode 100644 bin/csh/USD.doc/csh.2 create mode 100644 bin/csh/USD.doc/csh.3 create mode 100644 bin/csh/USD.doc/csh.4 create mode 100644 bin/csh/csh.1 create mode 100644 bin/csh/csh.c create mode 100644 bin/csh/csh.h create mode 100644 bin/csh/dir.c create mode 100644 bin/csh/dol.c create mode 100644 bin/csh/exec.c create mode 100644 bin/csh/file.c create mode 100644 bin/csh/func.c create mode 100644 bin/csh/hist.c create mode 100644 bin/csh/lex.c create mode 100644 bin/csh/proc.c create mode 100644 bin/csh/sem.c create mode 100644 bin/csh/set.c create mode 100644 bin/date/date.1 create mode 100644 bin/date/date.c create mode 100644 bin/date/netdate.c create mode 100644 bin/hostname/hostname.1 create mode 100644 bin/hostname/hostname.c create mode 100644 bin/kill/kill.1 create mode 100644 bin/kill/kill.c create mode 100644 bin/ls/ls.1 create mode 100644 bin/ls/ls.c create mode 100644 bin/ls/print.c create mode 100644 bin/ls/stat_flags.c create mode 100644 bin/ls/util.c create mode 100644 bin/ps/fmt.c create mode 100644 bin/pwd/pwd.1 create mode 100644 bin/rm/rm.1 create mode 100644 bin/rm/rm.c create mode 100644 bin/rmail/rmail.c create mode 100644 bin/stty/key.c create mode 100644 bin/stty/stty.1 diff --git a/bin/cat/cat.1 b/bin/cat/cat.1 new file mode 100644 index 000000000000..91ce3fd87fd6 --- /dev/null +++ b/bin/cat/cat.1 @@ -0,0 +1,120 @@ +.\" 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 +.\" +.Dd May 2, 1995 +.Dt CAT 1 +.Os BSD 3 +.Sh NAME +.Nm cat +.Nd concatenate and print files +.Sh SYNOPSIS +.Nm cat +.Op Fl benstuv +.Op Fl +.Op Ar +.Sh DESCRIPTION +The +.Nm cat +utility reads files sequentially, writing them to the standard output. +The +.Ar file +operands are processed in command line order. +A single dash represents the standard input. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl b +Implies the +.Fl n +option but doesn't number blank lines. +.It Fl e +Implies the +.Fl v +option, and displays a dollar sign +.Pq Ql \&$ +at the end of each line +as well. +.It Fl n +Number the output lines, starting at 1. +.It Fl s +Squeeze multiple adjacent empty lines, causing the output to be +single spaced. +.It Fl t +Implies the +.Fl v +option, and displays tab characters as +.Ql ^I +as well. +.It Fl u +The +.Fl u +option guarantees that the output is unbuffered. +.It Fl v +Displays non-printing characters so they are visible. +Control characters print as +.Ql ^X +for control-X; the delete +character (octal 0177) prints as +.Ql ^? +Non-ascii characters (with the high bit set) are printed as +.Ql M- +(for meta) followed by the character for the low 7 bits. +.El +.Pp +The +.Nm cat +utility exits 0 on success, and >0 if an error occurs. +.Sh BUGS +Because of the shell language mechanism used to perform output +redirection, the command +.Dq Li cat file1 file 2 > file1 +will cause the original data in file1 to be destroyed! +.Sh SEE ALSO +.Xr head 1 , +.Xr more 1 , +.Xr pr 1 , +.Xr tail 1 , +.Xr vis 1 +.Rs +.%A Rob Pike +.%T "UNIX Style, or cat -v Considered Harmful" +.%J "USENIX Summer Conference Proceedings" +.%D 1983 +.Re +.Sh HISTORY +A +.Nm +command appeared in Version 6 AT&T UNIX. diff --git a/bin/cat/cat.c b/bin/cat/cat.c new file mode 100644 index 000000000000..0b2e1a73ae64 --- /dev/null +++ b/bin/cat/cat.c @@ -0,0 +1,252 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kevin Fall. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)cat.c 8.2 (Berkeley) 4/27/95"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +int bflag, eflag, nflag, sflag, tflag, vflag; +int rval; +char *filename; + +void cook_args __P((char *argv[])); +void cook_buf __P((FILE *)); +void raw_args __P((char *argv[])); +void raw_cat __P((int)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern int optind; + int ch; + + while ((ch = getopt(argc, argv, "benstuv")) != -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, (char *)NULL); + break; + case 'v': + vflag = 1; + break; + default: + case '?': + (void)fprintf(stderr, + "usage: cat [-benstuv] [-] [file ...]\n"); + exit(1); + } + argv += optind; + + if (bflag || eflag || nflag || sflag || tflag || vflag) + cook_args(argv); + else + raw_args(argv); + if (fclose(stdout)) + err(1, "stdout"); + exit(rval); +} + +void +cook_args(argv) + char **argv; +{ + register FILE *fp; + + fp = stdin; + filename = "stdin"; + do { + if (*argv) { + if (!strcmp(*argv, "-")) + fp = stdin; + else if ((fp = fopen(*argv, "r")) == NULL) { + warn("%s", *argv); + ++argv; + continue; + } + filename = *argv++; + } + cook_buf(fp); + if (fp != stdin) + (void)fclose(fp); + } while (*argv); +} + +void +cook_buf(fp) + register FILE *fp; +{ + register int ch, gobble, line, prev; + + line = gobble = 0; + for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) { + if (prev == '\n') { + if (ch == '\n') { + if (sflag) { + if (!gobble && putchar(ch) == EOF) + break; + gobble = 1; + continue; + } + if (nflag && !bflag) { + (void)fprintf(stdout, "%6d\t", ++line); + if (ferror(stdout)) + break; + } + } else if (nflag) { + (void)fprintf(stdout, "%6d\t", ++line); + if (ferror(stdout)) + break; + } + } + gobble = 0; + if (ch == '\n') { + if (eflag) + if (putchar('$') == EOF) + break; + } else if (ch == '\t') { + if (tflag) { + if (putchar('^') == EOF || putchar('I') == EOF) + break; + continue; + } + } else if (vflag) { + if (!isascii(ch)) { + if (putchar('M') == EOF || putchar('-') == EOF) + break; + ch = toascii(ch); + } + if (iscntrl(ch)) { + if (putchar('^') == EOF || + putchar(ch == '\177' ? '?' : + ch | 0100) == EOF) + break; + continue; + } + } + if (putchar(ch) == EOF) + break; + } + if (ferror(fp)) { + warn("%s", filename); + clearerr(fp); + } + if (ferror(stdout)) + err(1, "stdout"); +} + +void +raw_args(argv) + char **argv; +{ + register int fd; + + fd = fileno(stdin); + filename = "stdin"; + do { + if (*argv) { + if (!strcmp(*argv, "-")) + fd = fileno(stdin); + else if ((fd = open(*argv, O_RDONLY, 0)) < 0) { + warn("%s", *argv); + ++argv; + continue; + } + filename = *argv++; + } + raw_cat(fd); + if (fd != fileno(stdin)) + (void)close(fd); + } while (*argv); +} + +void +raw_cat(rfd) + register int rfd; +{ + register int nr, nw, off, wfd; + static int bsize; + static char *buf; + struct stat sbuf; + + wfd = fileno(stdout); + if (buf == NULL) { + if (fstat(wfd, &sbuf)) + err(1, "%s", filename); + bsize = MAX(sbuf.st_blksize, 1024); + if ((buf = malloc((u_int)bsize)) == NULL) + err(1, NULL); + } + while ((nr = read(rfd, buf, bsize)) > 0) + for (off = 0; nr; nr -= nw, off += nw) + if ((nw = write(wfd, buf + off, nr)) < 0) + err(1, "stdout"); + if (nr < 0) + warn("%s", filename); +} diff --git a/bin/cp/cp.c b/bin/cp/cp.c new file mode 100644 index 000000000000..61a1f795e5f2 --- /dev/null +++ b/bin/cp/cp.c @@ -0,0 +1,448 @@ +/* + * Copyright (c) 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * David Hitz of Auspex Systems Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1988, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)cp.c 8.5 (Berkeley) 4/29/95"; +#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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "extern.h" + +#define STRIP_TRAILING_SLASH(p) { \ + while ((p).p_end > (p).p_path && (p).p_end[-1] == '/') \ + *--(p).p_end = 0; \ +} + +PATH_T to = { to.p_path, "" }; + +uid_t myuid; +int Rflag, iflag, pflag, rflag; +int myumask; + +enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE }; + +int copy __P((char *[], enum op, int)); +int mastercmp __P((const FTSENT **, const FTSENT **)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct stat to_stat, tmp_stat; + enum op type; + int Hflag, Lflag, Pflag, ch, fts_options, r; + char *target; + + Hflag = Lflag = Pflag = Rflag = 0; + while ((ch = getopt(argc, argv, "HLPRfipr")) != EOF) + switch (ch) { + case 'H': + Hflag = 1; + Lflag = Pflag = 0; + break; + case 'L': + Lflag = 1; + Hflag = Pflag = 0; + break; + case 'P': + Pflag = 1; + Hflag = Lflag = 0; + break; + case 'R': + Rflag = 1; + break; + case 'f': + iflag = 0; + break; + case 'i': + iflag = isatty(fileno(stdin)); + break; + case 'p': + pflag = 1; + break; + case 'r': + rflag = 1; + break; + case '?': + default: + usage(); + break; + } + argc -= optind; + argv += optind; + + if (argc < 2) + usage(); + + fts_options = FTS_NOCHDIR | FTS_PHYSICAL; + if (rflag) { + if (Rflag) + errx(1, + "the -R and -r options may not be specified together."); + if (Hflag || Lflag || Pflag) + errx(1, + "the -H, -L, and -P options may not be specified with the -r option."); + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + } + if (Rflag) { + if (Hflag) + fts_options |= FTS_COMFOLLOW; + if (Lflag) { + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + } + } else { + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + } + + myuid = getuid(); + + /* Copy the umask for explicit mode setting. */ + myumask = umask(0); + (void)umask(myumask); + + /* Save the target base in "to". */ + target = argv[--argc]; + if (strlen(target) > MAXPATHLEN) + errx(1, "%s: name too long", target); + (void)strcpy(to.p_path, target); + to.p_end = to.p_path + strlen(to.p_path); + if (to.p_path == to.p_end) { + *to.p_end++ = '.'; + *to.p_end = 0; + } + STRIP_TRAILING_SLASH(to); + to.target_end = to.p_end; + + /* Set end of argument list for fts(3). */ + argv[argc] = NULL; + + /* + * Cp has two distinct cases: + * + * cp [-R] source target + * cp [-R] source1 ... sourceN directory + * + * In both cases, source can be either a file or a directory. + * + * In (1), the target becomes a copy of the source. That is, if the + * source is a file, the target will be a file, and likewise for + * directories. + * + * In (2), the real target is not directory, but "directory/source". + */ + r = stat(to.p_path, &to_stat); + if (r == -1 && errno != ENOENT) + err(1, "%s", to.p_path); + if (r == -1 || !S_ISDIR(to_stat.st_mode)) { + /* + * Case (1). Target is not a directory. + */ + if (argc > 1) { + usage(); + exit(1); + } + /* + * Need to detect the case: + * cp -R dir foo + * Where dir is a directory and foo does not exist, where + * we want pathname concatenations turned on but not for + * the initial mkdir(). + */ + if (r == -1) { + if (rflag || (Rflag && (Lflag || Hflag))) + stat(*argv, &tmp_stat); + else + lstat(*argv, &tmp_stat); + + if (S_ISDIR(tmp_stat.st_mode) && (Rflag || rflag)) + type = DIR_TO_DNE; + else + type = FILE_TO_FILE; + } else + type = FILE_TO_FILE; + } else + /* + * Case (2). Target is a directory. + */ + type = FILE_TO_DIR; + + exit (copy(argv, type, fts_options)); +} + +int +copy(argv, type, fts_options) + char *argv[]; + enum op type; + int fts_options; +{ + struct stat to_stat; + FTS *ftsp; + FTSENT *curr; + int base, dne, nlen, rval; + char *p; + + if ((ftsp = fts_open(argv, fts_options, mastercmp)) == NULL) + err(1, NULL); + for (rval = 0; (curr = fts_read(ftsp)) != NULL;) { + switch (curr->fts_info) { + case FTS_NS: + case FTS_ERR: + warnx("%s: %s", + curr->fts_path, strerror(curr->fts_errno)); + rval = 1; + continue; + case FTS_DC: /* Warn, continue. */ + warnx("%s: directory causes a cycle", curr->fts_path); + rval = 1; + continue; + case FTS_DP: /* Ignore, continue. */ + continue; + } + + /* + * If we are in case (2) or (3) above, we need to append the + * source name to the target name. + */ + if (type != FILE_TO_FILE) { + if ((curr->fts_namelen + + to.target_end - to.p_path + 1) > MAXPATHLEN) { + warnx("%s/%s: name too long (not copied)", + to.p_path, curr->fts_name); + rval = 1; + continue; + } + + /* + * Need to remember the roots of traversals to create + * correct pathnames. If there's a directory being + * copied to a non-existent directory, e.g. + * cp -R a/dir noexist + * the resulting path name should be noexist/foo, not + * noexist/dir/foo (where foo is a file in dir), which + * is the case where the target exists. + * + * Also, check for "..". This is for correct path + * concatentation for paths ending in "..", e.g. + * cp -R .. /tmp + * Paths ending in ".." are changed to ".". This is + * tricky, but seems the easiest way to fix the problem. + * + * XXX + * Since the first level MUST be FTS_ROOTLEVEL, base + * is always initialized. + */ + if (curr->fts_level == FTS_ROOTLEVEL) + if (type != DIR_TO_DNE) { + p = strrchr(curr->fts_path, '/'); + base = (p == NULL) ? 0 : + (int)(p - curr->fts_path + 1); + + if (!strcmp(&curr->fts_path[base], + "..")) + base += 1; + } else + base = curr->fts_pathlen; + + if (to.target_end[-1] != '/') { + *to.target_end = '/'; + *(to.target_end + 1) = 0; + } + p = &curr->fts_path[base]; + nlen = curr->fts_pathlen - base; + + (void)strncat(to.target_end + 1, p, nlen); + to.p_end = to.target_end + nlen + 1; + *to.p_end = 0; + STRIP_TRAILING_SLASH(to); + } + + /* Not an error but need to remember it happened */ + if (stat(to.p_path, &to_stat) == -1) + dne = 1; + else { + if (to_stat.st_dev == curr->fts_statp->st_dev && + to_stat.st_ino == curr->fts_statp->st_ino) { + warnx("%s and %s are identical (not copied).", + to.p_path, curr->fts_path); + rval = 1; + if (S_ISDIR(curr->fts_statp->st_mode)) + (void)fts_set(ftsp, curr, FTS_SKIP); + continue; + } + 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); + rval = 1; + continue; + } + dne = 0; + } + + switch (curr->fts_statp->st_mode & S_IFMT) { + case S_IFLNK: + if (copy_link(curr, !dne)) + rval = 1; + break; + case S_IFDIR: + if (!Rflag && !rflag) { + warnx("%s is a directory (not copied).", + curr->fts_path); + (void)fts_set(ftsp, curr, FTS_SKIP); + rval = 1; + break; + } + /* + * If the directory doesn't exist, create the new + * one with the from file mode plus owner RWX bits, + * modified by the umask. Trade-off between being + * able to write the directory (if from directory is + * 555) and not causing a permissions race. If the + * umask blocks owner writes, we fail.. + */ + if (dne) { + if (mkdir(to.p_path, + curr->fts_statp->st_mode | S_IRWXU) < 0) + err(1, "%s", to.p_path); + } else if (!S_ISDIR(to_stat.st_mode)) { + errno = ENOTDIR; + err(1, "%s", to.p_path); + } + /* + * If not -p and directory didn't exist, set it to be + * the same as the from directory, umodified by the + * umask; arguably wrong, but it's been that way + * forever. + */ + if (pflag && setfile(curr->fts_statp, 0)) + rval = 1; + else if (dne) + (void)chmod(to.p_path, + curr->fts_statp->st_mode); + break; + case S_IFBLK: + case S_IFCHR: + if (Rflag) { + if (copy_special(curr->fts_statp, !dne)) + rval = 1; + } else + if (copy_file(curr, dne)) + rval = 1; + break; + case S_IFIFO: + if (Rflag) { + if (copy_fifo(curr->fts_statp, !dne)) + rval = 1; + } else + if (copy_file(curr, dne)) + rval = 1; + break; + default: + if (copy_file(curr, dne)) + rval = 1; + break; + } + } + if (errno) + err(1, "fts_read"); + return (rval); +} + +/* + * mastercmp -- + * The comparison function for the copy order. The order is to copy + * non-directory files before directory files. The reason for this + * is because files tend to be in the same cylinder group as their + * parent directory, whereas directories tend not to be. Copying the + * files first reduces seeking. + */ +int +mastercmp(a, b) + const FTSENT **a, **b; +{ + int a_info, b_info; + + a_info = (*a)->fts_info; + if (a_info == FTS_ERR || a_info == FTS_NS || a_info == FTS_DNR) + return (0); + b_info = (*b)->fts_info; + if (b_info == FTS_ERR || b_info == FTS_NS || b_info == FTS_DNR) + return (0); + if (a_info == FTS_D) + return (-1); + if (b_info == FTS_D) + return (1); + return (0); +} diff --git a/bin/csh/Makefile b/bin/csh/Makefile new file mode 100644 index 000000000000..9b81560d1206 --- /dev/null +++ b/bin/csh/Makefile @@ -0,0 +1,44 @@ +# @(#)Makefile 8.2 (Berkeley) 3/22/95 +# +# C Shell with process control; VM/UNIX VAX Makefile +# Bill Joy UC Berkeley; Jim Kulp IIASA, Austria +# +# To profile, put -DPROF in DEFS and -pg in CFLAGS, and recompile. + +PROG= csh +DFLAGS=-DBUILTIN -DFILEC -DNLS -DSHORT_STRINGS +#CFLAGS+=-g +#CFLAGS+=-Wall +CFLAGS+=-I${.CURDIR} -I. ${DFLAGS} +SRCS= alloc.c char.c const.c csh.c dir.c dol.c err.c exec.c exp.c file.c \ + func.c glob.c hist.c init.c lex.c misc.c parse.c printf.c proc.c \ + sem.c set.c str.c time.c +.PATH: ${.CURDIR}/../../usr.bin/printf + +MAN1= csh.0 +MLINKS= csh.1 limit.1 csh.1 alias.1 csh.1 bg.1 csh.1 dirs.1 csh.1 fg.1 \ + csh.1 foreach.1 csh.1 history.1 csh.1 jobs.1 csh.1 popd.1 \ + csh.1 pushd.1 csh.1 rehash.1 csh.1 repeat.1 csh.1 suspend.1 \ + csh.1 stop.1 csh.1 source.1 +CLEANFILES+=err.h const.h + +const.h: err.h + +err.h: err.c + @rm -f $@ + @echo '/* Do not edit this file, make creates it. */' > $@ + @echo '#ifndef _h_sh_err' >> $@ + @echo '#define _h_sh_err' >> $@ + egrep 'ERR_' ${.CURDIR}/$*.c | egrep '^#define' >> $@ + @echo '#endif /* _h_sh_err */' >> $@ + +const.h: const.c + @rm -f $@ + @echo '/* Do not edit this file, make creates it. */' > $@ + ${CC} -E ${CFLAGS} ${.CURDIR}/$*.c | egrep 'Char STR' | \ + sed -e 's/Char \([a-zA-Z0-9_]*\)\(.*\)/extern Char \1[];/' | \ + sort >> $@ + +.depend: const.h err.h + +.include diff --git a/bin/csh/USD.doc/csh.1 b/bin/csh/USD.doc/csh.1 new file mode 100644 index 000000000000..199ba8fe94c1 --- /dev/null +++ b/bin/csh/USD.doc/csh.1 @@ -0,0 +1,1011 @@ +.\" 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.2 (Berkeley) 5/16/94 +.\" +.EH 'USD:4-%''An Introduction to the C shell' +.OH 'An Introduction to the C shell''USD:4-%' +.\".RP +.TL +An Introduction to the C shell +.AU +William Joy +(revised for 4.3BSD by Mark Seiden) +.AI +Computer Science Division +.br +Department of Electrical Engineering and Computer Science +.br +University of California, Berkeley +.br +Berkeley, California 94720 +.AB +.I Csh +is a new command language interpreter for +.UX +systems. +It incorporates good features of other shells and a +.I history +mechanism similar to the +.I redo +of \s-2INTERLISP\s0. +While incorporating many features of other shells which make +writing shell programs (shell scripts) easier, +most of the features unique to +.I csh +are designed more for the interactive \s-2UNIX\s0 user. +.PP +\s-2UNIX\s0 +users who have read a general introduction to the system +will find a valuable basic explanation of the shell here. +Simple terminal interaction with +.I csh +is possible after reading just the first section of this document. +The second section describes the shell's capabilities which you can +explore after you have begun to become acquainted with the shell. +Later sections introduce features which are useful, but not necessary +for all users of the shell. +.PP +Additional information includes an appendix listing special characters of the shell +and a glossary of terms and commands introduced in this manual. +.AE +.SH +.if n .ND +Introduction +.PP +A +.I shell +is a command language interpreter. +.I Csh +is the name of one particular command interpreter on +\s-2UNIX\s0. +The primary purpose of +.I csh +is to translate command lines typed at a terminal into +system actions, such as invocation of other programs. +.I Csh +is a user program just like any you might write. +Hopefully, +.I csh +will be a very useful program for you +in interacting with the \s-2UNIX\s0 system. +.PP +In addition to this document, you will want to refer to a copy +of the \s-2UNIX\s0 User Reference Manual. +The +.I csh +documentation in section 1 of the manual provides a full description of all +features of the shell and is the definitive reference for questions +about the shell. +.PP +Many words in this document are shown in +.I italics. +These are important words; +names of commands, and words which have special meaning in discussing +the shell and \s-2UNIX\s0. +Many of the words are defined in a glossary at the end of this document. +If you don't know what is meant by a word, you should look +for it in the glossary. +.SH +Acknowledgements +.PP +Numerous people have provided good input about previous versions +of +.I csh +and aided in its debugging and in the debugging of its documentation. +I would especially like to thank Michael Ubell +who made the crucial observation that history commands could be +done well over the word structure of input text, and implemented +a prototype history mechanism in an older version of the shell. +Eric Allman has also provided a large number of useful comments on the +shell, helping to unify those concepts which are present and to identify +and eliminate useless and marginally useful features. +Mike O'Brien suggested the pathname hashing +mechanism which speeds command execution. +Jim Kulp added the job control and directory stack primitives and +added their documentation to this introduction. +.br +.bp +.NH +Terminal usage of the shell +.NH 2 +The basic notion of commands +.PP +A +.I shell +in +\s-2UNIX\s0 +acts mostly as a medium through which other +.I programs +are invoked. +While it has a set of +.I builtin +functions which it performs directly, +most commands cause execution of programs that are, in fact, +external to the shell. +The shell is thus distinguished from the command interpreters of other +systems both by the fact that it is just a user program, and by the fact +that it is used almost exclusively as a mechanism for invoking other programs. +.PP +.I Commands +in the \s-2UNIX\s0 system consist of a list of strings or +.I words +interpreted as a +.I "command name" +followed by +.I arguments. +Thus the command +.DS +mail bill +.DE +consists of two words. +The first word +.I mail +names the command to be executed, in this case the +mail program which sends messages to other users. +The shell uses the name of the command in attempting to execute it for you. +It will look in a number of +.I directories +for a file with the name +.I mail +which is expected to contain the mail program. +.PP +The rest of the words of the command are given as +.I arguments +to the command itself when it is executed. +In this case we specified also the argument +.I bill +which is interpreted by the +.I mail +program to be the name of a user to whom mail is to be sent. +In normal terminal usage we might use the +.I mail +command as follows. +.DS +% mail bill +I have a question about the csh documentation. +My document seems to be missing page 5. +Does a page five exist? + Bill +EOT +% +.DE +.PP +Here we typed a message to send to +.I bill +and ended this message with a ^D which sent an end-of-file to +the mail program. +(Here and throughout this document, the notation ``^\fIx\fR'' +is to be read ``control-\fIx\fR'' and represents the striking of the \fIx\fR +key while the control key is held down.) +The mail program +then echoed the characters `EOT' and transmitted our message. +The characters `% ' were printed before and after the mail command +by the shell to indicate that input was needed. +.PP +After typing the `% ' prompt the shell was reading command input from +our terminal. +We typed a complete command `mail bill'. +The shell then executed the +.I mail +program with argument +.I bill +and went dormant waiting for it to complete. +The mail program then read input from our terminal until we signalled +an end-of-file via typing a ^D after which the shell noticed +that mail had completed +and signaled us that it was ready to read from the terminal again by +printing another `% ' prompt. +.PP +This is the essential pattern of all interaction with \s-2UNIX\s0 +through the shell. +A complete command is typed at the terminal, the shell executes +the command and when this execution completes, it prompts for a new command. +If you run the editor for an hour, the shell will patiently wait for +you to finish editing and obediently prompt you again whenever you finish +editing. +.PP +An example of a useful command you can execute now is the +.I tset +command, which sets the default +.I erase +and +.I kill +characters on your terminal \- the erase character erases the last +character you typed and the kill character erases the entire line you +have entered so far. +By default, the erase character is the delete key (equivalent to `^?') +and the kill character is `^U'. Some people prefer to make the erase character +the backspace key (equivalent to `^H'). +You can make this be true by typing +.DS +tset \-e +.DE +which tells the program +.I tset +to set the erase character to tset's default setting for this character +(a backspace). +.NH 2 +Flag arguments +.PP +A useful notion in \s-2UNIX\s0 is that of a +.I flag +argument. +While many arguments to commands specify file names or user names, +some arguments rather specify an optional capability of the command +which you wish to invoke. +By convention, such arguments begin with the character `\-' (hyphen). +Thus the command +.DS +ls +.DE +will produce a list of the files in the current +.I "working directory" . +The option +.I \-s +is the size option, and +.DS +ls \-s +.DE +causes +.I ls +to also give, for each file the size of the file in blocks of 512 +characters. +The manual section for each command in the \s-2UNIX\s0 reference manual +gives the available options for each command. +The +.I ls +command has a large number of useful and interesting options. +Most other commands have either no options or only one or two options. +It is hard to remember options of commands which are not used very +frequently, so most \s-2UNIX\s0 utilities perform only one or two functions +rather than having a large number of hard to remember options. +.NH 2 +Output to files +.PP +Commands that normally read input or write output on the terminal +can also be executed with this input and/or output done to +a file. +.PP +Thus suppose we wish to save the current date in a file called `now'. +The command +.DS +date +.DE +will print the current date on our terminal. +This is because our terminal is the default +.I "standard output" +for the date command and the date command prints the date on its +standard output. +The shell lets us +.I redirect +the +.I "standard output" +of a command through a +notation using the +.I metacharacter +`>' and the name of the file where output is to be placed. +Thus the command +.DS +date > now +.DE +runs the +.I date +command such that its standard output is +the file `now' rather than the terminal. +Thus this command places the current date and time into the file `now'. +It is important to know that the +.I date +command was unaware that its output was going to a file rather than +to the terminal. +The shell performed this +.I redirection +before the command began executing. +.PP +One other thing to note here is that the file `now' +need not have existed before the +.I date +command was executed; the shell would have created the file if it did +not exist. +And if the file did exist? +If it had existed previously these previous contents would have been discarded! +A shell option +.I noclobber +exists to prevent this from happening accidentally; +it is discussed in section 2.2. +.PP +The system normally keeps files which you create with `>' and all other files. +Thus the default is for files to be permanent. If you wish to create a file +which will be removed automatically, you can begin its name with a `#' +character, this `scratch' character denotes the fact that the file will +be a scratch file.* +.FS +*Note that if your erase character is a `#', you will have to precede the +`#' with a `\e'. The fact that the `#' character is the old (pre-\s-2CRT\s0) +standard erase character means that it seldom appears in a file name, and +allows this convention to be used for scratch files. If you are using a +\s-2CRT\s0, your erase character should be a ^H, as we demonstrated +in section 1.1 how this could be set up. +.FE +The system will remove such files after a couple of days, +or sooner if file space becomes very tight. +Thus, in running the +.I date +command above, we don't really want to save the output forever, so we +would more likely do +.DS +date > #now +.DE +.NH 2 +Metacharacters in the shell +.PP +The shell has a large number of +special characters (like `>') +which indicate special functions. +We say that these notations have +.I syntactic +and +.I semantic +meaning to the shell. +In general, most characters which are neither letters nor digits +have special meaning to the shell. +We shall shortly learn a means of +.I quotation +which allows us to use +.I metacharacters +without the shell treating them in any special way. +.PP +Metacharacters normally have effect only when the shell is reading +our input. +We need not worry about placing shell metacharacters in a letter +we are sending via +.I mail, +or when we are typing in text or data to some other program. +Note that the shell is only reading input when it has prompted with +`% ' (although we can type our input even before it prompts). +.NH 2 +Input from files; pipelines +.PP +We learned above how to +.I redirect +the +.I "standard output" +of a command +to a file. +It is also possible to redirect the +.I "standard input" +of a command from a file. +This is not often necessary since most commands will read from +a file whose name is given as an argument. +We can give the command +.DS +sort < data +.DE +to run the +.I sort +command with standard input, where the command normally +reads its input, from the file +`data'. +We would more likely say +.DS +sort data +.DE +letting the +.I sort +command open the file +`data' +for input itself since this is less to type. +.PP +We should note that if we just typed +.DS +sort +.DE +then the sort program would sort lines from its +.I "standard input." +Since we did not +.I redirect +the standard input, it would sort lines as we typed them on the terminal +until we typed a ^D to indicate an end-of-file. +.PP +A most useful capability is the ability to combine the standard output +of one command with the standard input of another, i.e. to run the +commands in a sequence known as a +.I pipeline. +For instance the command +.DS +ls \-s +.DE +normally produces a list of the files in our directory with the size +of each in blocks of 512 characters. +If we are interested in learning which of our files is largest we +may wish to have this sorted by size rather than by name, which is +the default way in which +.I ls +sorts. +We could look at the many options of +.I ls +to see if there was an option to do this but would eventually discover +that there is not. +Instead we can use a couple of simple options of the +.I sort +command, combining it with +.I ls +to get what we want. +.PP +The +.I \-n +option of sort specifies a numeric sort rather than an alphabetic sort. +Thus +.DS +ls \-s | sort \-n +.DE +specifies that the output of the +.I ls +command run with the option +.I \-s +is to be +.I piped +to the command +.I sort +run with the numeric sort option. +This would give us a sorted list of our files by size, but with the +smallest first. +We could then use the +.I \-r +reverse sort option and the +.I head +command in combination with the previous command doing +.DS +ls \-s | sort \-n \-r | head \-5 +.DE +Here we have taken a list of our files sorted alphabetically, +each with the size in blocks. +We have run this to the standard input of the +.I sort +command asking it to sort numerically in reverse order (largest first). +This output has then been run into the command +.I head +which gives us the first few lines. +In this case we have asked +.I head +for the first 5 lines. +Thus this command gives us the names and sizes of our 5 largest files. +.PP +The notation introduced above is called the +.I pipe +mechanism. +Commands separated by `\||\|' characters are connected together by the +shell and the standard output of each is run into the standard input of the +next. +The leftmost command in a pipeline will normally take its standard +input from the terminal and the rightmost will place its standard +output on the terminal. +Other examples of pipelines will be given later when we discuss the +history mechanism; +one important use of pipes which is illustrated there is in the +routing of information to the line printer. +.NH 2 +Filenames +.PP +Many commands to be executed will need the names of files as arguments. +\s-2UNIX\s0 +.I pathnames +consist of a number of +.I components +separated by `/'. +Each component except the last names a directory in which the next +component resides, in effect specifying the +.I path +of directories to follow to reach the file. +Thus the pathname +.DS +/etc/motd +.DE +specifies a file in the directory +`etc' +which is a subdirectory of the +.I root +directory `/'. +Within this directory the file named is `motd' which stands +for `message of the day'. +A +.I pathname +that begins with a slash is said to be an +.I absolute +pathname since it is specified from the absolute top of the entire +directory hierarchy of the system (the +.I root ). +.I Pathnames +which do not begin with `/' are interpreted as starting in the current +.I "working directory" , +which is, by default, your +.I home +directory and can be changed dynamically by the +.I cd +change directory command. +Such pathnames are said to be +.I relative +to the working directory since they are found by starting +in the working directory and descending to lower levels of directories +for each +.I component +of the pathname. If the pathname contains no slashes at all then the +file is contained in the working directory itself and the pathname is merely +the name of the file in this directory. +Absolute pathnames have no relation +to the working directory. +.PP +Most filenames consist of a number of alphanumeric characters and +`.'s (periods). +In fact, all printing characters except `/' (slash) may appear in filenames. +It is inconvenient to have most non-alphabetic characters in filenames +because many of these have special meaning to the shell. +The character `.' (period) is not a shell-metacharacter and is often used +to separate the +.I extension +of a file name from the base of the name. +Thus +.DS +prog.c prog.o prog.errs prog.output +.DE +are four related files. +They share a +.I base +portion of a name +(a base portion being that part of the name that is left when a trailing +`.' and following characters which are not `.' are stripped off). +The file +`prog.c' +might be the source for a C program, +the file `prog.o' the corresponding object file, +the file +`prog.errs' the errors resulting from a compilation of the program +and the file +`prog.output' the output of a run of the program. +.PP +If we wished to refer to all four of these files in a command, we could +use the notation +.DS +prog.* +.DE +This expression is expanded by the shell, before the command to which it is +an argument is executed, into a list of names which begin with `prog.'. +The character `*' here matches any sequence (including the empty sequence) +of characters in a file name. +The names which match are alphabetically sorted and placed in the +.I "argument list" +of the command. +Thus the command +.DS +echo prog.* +.DE +will echo the names +.DS +prog.c prog.errs prog.o prog.output +.DE +Note that the names are in sorted order here, and a different +order than we listed them above. +The +.I echo +command receives four words as arguments, even though we only typed +one word as as argument directly. +The four words were generated by +.I "filename expansion" +of the one input word. +.PP +Other notations for +.I "filename expansion" +are also available. +The character `?' matches any single character in a filename. +Thus +.DS +echo ? \|?? \|??? +.DE +will echo a line of filenames; first those with one character names, +then those with two character names, and finally those with three +character names. +The names of each length will be independently sorted. +.PP +Another mechanism consists of a sequence of characters between `[' and `]'. +This metasequence matches any single character from the enclosed set. +Thus +.DS +prog.[co] +.DE +will match +.DS +prog.c prog.o +.DE +in the example above. +We can also place two characters around a `\-' in this notation to denote +a range. +Thus +.DS +chap.[1\-5] +.DE +might match files +.DS +chap.1 chap.2 chap.3 chap.4 chap.5 +.DE +if they existed. +This is shorthand for +.DS +chap.[12345] +.DE +and otherwise equivalent. +.PP +An important point to note is that if a list of argument words to +a command (an +.I "argument list)" +contains filename expansion syntax, and if this filename expansion syntax +fails to match any existing file names, then the shell considers this +to be an error and prints a diagnostic +.DS +No match. +.DE +and does not execute the command. +.PP +Another very important point is that files with the character `.' at the +beginning are treated specially. +Neither `*' or `?' or the `[' `]' mechanism will match it. +This prevents accidental matching of the filenames `.' and `..' +in the working directory which have special meaning to the system, +as well as other files such as +.I \&.cshrc +which are not normally +visible. +We will discuss the special role of the file +.I \&.cshrc +later. +.PP +Another filename expansion mechanism gives access to the pathname of +the +.I home +directory of other users. +This notation consists of the character `~' (tilde) followed by another user's +login name. +For instance the word `~bill' would map to the pathname `/usr/bill' +if the home directory for `bill' was `/usr/bill'. +Since, on large systems, users may have login directories scattered over +many different disk volumes with different prefix directory names, +this notation provides a convenient way of accessing the files +of other users. +.PP +A special case of this notation consists of a `~' alone, e.g. `~/mbox'. +This notation is expanded by the shell into the file `mbox' in your +.I home +directory, i.e. into `/usr/bill/mbox' for me on Ernie Co-vax, the UCB +Computer Science Department VAX machine, where this document was prepared. +This can be very useful if you have used +.I cd +to change to another directory and have found a file you wish to +copy using +.I cp. +If I give the command +.DS +cp thatfile ~ +.DE +the shell will expand this command to +.DS +cp thatfile /usr/bill +.DE +since my home directory is /usr/bill. +.PP +There also exists a mechanism using the characters `{' and `}' for +abbreviating a set of words which have common parts but cannot +be abbreviated by the above mechanisms because they are not files, +are the names of files which do not yet exist, +are not thus conveniently described. +This mechanism will be described much later, +in section 4.2, +as it is used less frequently. +.NH 2 +Quotation +.PP +We have already seen a number of metacharacters used by the shell. +These metacharacters pose a problem in that we cannot use them directly +as parts of words. +Thus the command +.DS +echo * +.DE +will not echo the character `*'. +It will either echo an sorted list of filenames in the +current +.I "working directory," +or print the message `No match' if there are +no files in the working directory. +.PP +The recommended mechanism for placing characters which are neither numbers, +digits, `/', `.' or `\-' in an argument word to a command is to enclose +it with single quotation characters `\'', i.e. +.DS +echo \'*\' +.DE +There is one special character `!' which is used by the +.I history +mechanism of the shell and which cannot be +.I escaped +by placing it within `\'' characters. +It and the character `\'' itself can be preceded by a single `\e' +to prevent their special meaning. +Thus +.DS +echo \e\'\e! +.DE +prints +.DS +\'! +.DE +These two mechanisms suffice to place any printing character into a word +which is an argument to a shell command. They can be combined, as in +.DS +echo \e\'\'*\' +.DE +which prints +.DS +\'* +.DE +since the first `\e' escaped the first `\'' and the `*' was enclosed +between `\'' characters. +.NH 2 +Terminating commands +.PP +When you are executing a command and the shell is +waiting for it to complete there are several ways +to force it to stop. +For instance if you type the command +.DS +cat /etc/passwd +.DE +the system will print a copy of a list of all users of the system +on your terminal. +This is likely to continue for several minutes unless you stop it. +You can send an +\s-2INTERRUPT\s0 +.I signal +to the +.I cat +command by typing ^C on your terminal.* +.FS +*On some older Unix systems the \s-2DEL\s0 or \s-2RUBOUT\s0 key +has the same effect. "stty all" will tell you the INTR key value. +.FE +Since +.I cat +does not take any precautions to avoid or otherwise handle this signal +the +\s-2INTERRUPT\s0 +will cause it to terminate. +The shell notices that +.I cat +has terminated and prompts you again with `% '. +If you hit \s-2INTERRUPT\s0 again, the shell will just +repeat its prompt since it handles \s-2INTERRUPT\s0 signals +and chooses to continue to execute commands rather than terminating +like +.I cat +did, which would have the effect of logging you out. +.PP +Another way in which many programs terminate is when they get an end-of-file +from their standard input. +Thus the +.I mail +program in the first example above was terminated when we typed a ^D +which generates an end-of-file from the standard input. +The shell also terminates when it gets an end-of-file printing `logout'; +\s-2UNIX\s0 then logs you off the system. +Since this means that typing too many ^D's can accidentally log us off, +the shell has a mechanism for preventing this. +This +.I ignoreeof +option will be discussed in section 2.2. +.PP +If a command has its standard input redirected from a file, then it will +normally terminate when it reaches the end of this file. +Thus if we execute +.DS +mail bill < prepared.text +.DE +the mail command will terminate without our typing a ^D. +This is because it read to the end-of-file of our file +`prepared.text' in which we placed a message for `bill' with an editor program. +We could also have done +.DS +cat prepared.text \||\| mail bill +.DE +since the +.I cat +command would then have written the text through the pipe to the +standard input of the mail command. +When the +.I cat +command completed it would have terminated, +closing down the pipeline +and the +.I mail +command would have received an end-of-file from it and terminated. +Using a pipe here is more complicated than redirecting input +so we would more likely use the first form. +These commands could also have been stopped by sending an \s-2INTERRUPT\s0. +.PP +Another possibility for stopping a command is to suspend its execution +temporarily, with the possibility of continuing execution later. This is +done by sending a \s-2STOP\s0 signal via typing a ^Z. +This signal causes all commands running on the terminal +(usually one but more if a pipeline is executing) to become suspended. +The shell notices that the command(s) have been suspended, types +`Stopped' and then prompts for a new command. +The previously executing command has been suspended, but otherwise +unaffected by the \s-2STOP\s0 signal. Any other commands can be executed +while the original command remains suspended. The suspended command can +be continued using the +.I fg +command with no arguments. The shell will then retype the command +to remind you which command is being continued, and cause the command +to resume execution. Unless any input files in use by the suspended +command have been changed in the meantime, the suspension has no effect +whatsoever on the execution of the command. This feature can be very useful +during editing, when you need to look at another file before continuing. An +example of command suspension follows. +.DS +% mail harold +Someone just copied a big file into my directory and its name is +^Z +Stopped +% ls +funnyfile +prog.c +prog.o +% jobs +.ta 1.75i +[1] + Stopped mail harold +% fg +mail harold +funnyfile. Do you know who did it? +EOT +% +.so tabs +.DE +In this example someone was sending a message to Harold and forgot the +name of the file he wanted to mention. The mail command was suspended +by typing ^Z. When the shell noticed that the mail program was +suspended, it typed `Stopped' and prompted for a new command. Then the +.I ls +command was typed to find out the name of the file. The +.I jobs +command was run to find out which command was suspended. At this time the +.I fg +command was typed to continue execution of the mail program. Input +to the mail program was then continued and ended with a ^D +which indicated the end of the message at which time the mail +program typed EOT. The +.I jobs +command will show which commands are suspended. +The ^Z should only be typed at the beginning of a line since +everything typed on the current line is discarded when a signal is sent +from the keyboard. This also happens on \s-2INTERRUPT\s0, and \s-2QUIT\s0 +signals. More information on +suspending jobs and controlling them is given in +section 2.6. +.PP +If you write or run programs which are not fully debugged then it may +be necessary to stop them somewhat ungracefully. +This can be done by sending them a \s-2QUIT\s0 +signal, sent by typing a ^\e. +This will usually provoke the shell to produce a message like: +.DS +Quit (Core dumped) +.DE +indicating that a file +`core' has been created containing information about the running program's +state when it terminated due to the \s-2QUIT\s0 signal. +You can examine this file yourself, or forward information to the +maintainer of the program telling him/her where the +.I "core file" +is. +.PP +If you run background commands (as explained in section 2.6) then these +commands will ignore \s-2INTERRUPT\s0 and \s-2QUIT\s0 signals at the +terminal. To stop them you must use the +.I kill +command. See section 2.6 for an example. +.PP +If you want to examine the output of a command without having it move +off the screen as the output of the +.DS +cat /etc/passwd +.DE +command will, you can use the command +.DS +more /etc/passwd +.DE +The +.I more +program pauses after each complete screenful and types `\-\-More\-\-' +at which point you can hit a space to get another screenful, a return +to get another line, a `?' to get some help on other commands, or a `q' to end the +.I more +program. You can also use more as a filter, i.e. +.DS +cat /etc/passwd | more +.DE +works just like the more simple more command above. +.PP +For stopping output of commands not involving +.I more +you can use the +^S key to stop the typeout. The typeout will resume when you +hit ^Q or any other key, but ^Q is normally used because +it only restarts the output and does not become input to the program +which is running. This works well on low-speed terminals, but at 9600 +baud it is hard to type ^S and ^Q fast enough to paginate +the output nicely, and a program like +.I more +is usually used. +.PP +An additional possibility is to use the ^O flush output +character; when this character is typed, all output from the current +command is thrown away (quickly) until the next input read occurs +or until the next shell prompt. This can be used to allow a command +to complete without having to suffer through the output on a slow +terminal; ^O is a toggle, so flushing can be turned off by +typing ^O again while output is being flushed. +.NH 2 +What now? +.PP +We have so far seen a number of mechanisms of the shell and learned a lot +about the way in which it operates. +The remaining sections will go yet further into the internals of the +shell, but you will surely want to try using the +shell before you go any further. +To try it you can log in to \s-2UNIX\s0 and type the following +command to the system: +.DS +chsh myname /bin/csh +.DE +Here `myname' should be replaced by the name you typed to +the system prompt of `login:' to get onto the system. +Thus I would use `chsh bill /bin/csh'. +.B +You only have to do this once; it takes effect at next login. +.R +You are now ready to try using +.I csh. +.PP +Before you do the `chsh' command, the shell you are using when +you log into the system is `/bin/sh'. +In fact, much of the above discussion is applicable to `/bin/sh'. +The next section will introduce many features particular to +.I csh +so you should change your shell to +.I csh +before you begin reading it. diff --git a/bin/csh/USD.doc/csh.2 b/bin/csh/USD.doc/csh.2 new file mode 100644 index 000000000000..68fcbc41752d --- /dev/null +++ b/bin/csh/USD.doc/csh.2 @@ -0,0 +1,1304 @@ +.\" 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.2 (Berkeley) 5/16/94 +.\" +.nr H1 1 +.NH +Details on the shell for terminal users +.NH 2 +Shell startup and termination +.PP +When you login, the shell is started by the system in your +.I home +directory and begins by reading commands from a file +.I \&.cshrc +in this directory. +All shells which you may start during your terminal session will +read from this file. +We will later see what kinds of commands are usefully placed there. +For now we need not have this file and the shell does not complain about +its absence. +.PP +A +.I "login shell" , +executed after you login to the system, +will, after it reads commands from +.I \&.cshrc, +read commands from a file +.I \&.login +also in your home directory. +This file contains commands which you wish to do each time you login +to the \s-2UNIX\s0 system. +My +.I \&.login +file looks something like: +.DS +set ignoreeof +set mail=(/usr/spool/mail/bill) +echo "${prompt}users" ; users +alias ts \e + \'set noglob ; eval \`tset \-s \-m dialup:c100rv4pna \-m plugboard:?hp2621nl \!*\`\'; +ts; stty intr ^C kill ^U crt +set time=15 history=10 +msgs \-f +if (\-e $mail) then + echo "${prompt}mail" + mail +endif +.DE +.PP +This file contains several commands to be executed by \s-2UNIX\s0 +each time I login. +The first is a +.I set +command which is interpreted directly by the shell. It sets the shell +variable +.I ignoreeof +which causes the shell to not log me off if I hit ^D. Rather, +I use the +.I logout +command to log off of the system. +By setting the +.I mail +variable, I ask the shell to watch for incoming mail to me. Every 5 minutes +the shell looks for this file and tells me if more mail has arrived there. +An alternative to this is to put the command +.DS +biff y +.DE +in place of this +.I set; +this will cause me to be notified immediately when mail arrives, and to +be shown the first few lines of the new message. +.PP +Next I set the shell variable `time' to `15' causing the shell to automatically +print out statistics lines for commands which execute for at least 15 seconds +of \s-2CPU\s+2 time. The variable `history' is set to 10 indicating that +I want the shell to remember the last 10 commands I type in its +.I "history list" , +(described later). +.PP +I create an +.I alias +``ts'' which executes a +\fItset\fR\|(1) command setting up the modes of the terminal. +The parameters to +.I tset +indicate the kinds of terminal which I usually use when not on a hardwired +port. I then execute ``ts'' and also use the +.I stty +command to change the interrupt character to ^C and the line kill +character to ^U. +.PP +I then run the `msgs' program, which provides me with any +system messages which I have not seen before; the `\-f' option here prevents +it from telling me anything if there are no new messages. +Finally, if my mailbox file exists, then I run the `mail' program to +process my mail. +.PP +When the `mail' and `msgs' programs finish, the shell will finish +processing my +.I \&.login +file and begin reading commands from the terminal, prompting for each with +`% '. +When I log off (by giving the +.I logout +command) the shell +will print `logout' and execute commands from the file `.logout' +if it exists in my home directory. +After that the shell will terminate and \s-2UNIX\s0 will log +me off the system. +If the system is not going down, I will receive a new login message. +In any case, after the `logout' message the shell is committed to terminating +and will take no further input from my terminal. +.NH 2 +Shell variables +.PP +The shell maintains a set of +.I variables. +We saw above the variables +.I history +and +.I time +which had values `10' and `15'. +In fact, each shell variable has as value an array of +zero or more +.I strings. +Shell variables may be assigned values by the set command. It has +several forms, the most useful of which was given above and is +.DS +set name=value +.DE +.PP +Shell variables may be used to store values which are to +be used in commands later through a substitution mechanism. +The shell variables most commonly referenced are, however, those which the +shell itself refers to. +By changing the values of these variables one can directly affect the +behavior of the shell. +.PP +One of the most important variables is the variable +.I path. +This variable contains a sequence of directory names where the shell +searches for commands. +The +.I set +command with no arguments +shows the value of all variables currently defined (we usually say +.I set) +in the shell. +The default value for path will be shown by +.I set +to be +.DS +% set +.ta .75i +argv () +cwd /usr/bill +home /usr/bill +path (. /usr/ucb /bin /usr/bin) +prompt % +shell /bin/csh +status 0 +term c100rv4pna +user bill +% +.so tabs +.DE +This output indicates that the variable path points to the current +directory `.' and then `/usr/ucb', `/bin' and `/usr/bin'. +Commands which you may write might be in `.' (usually one of +your directories). +Commands developed at Berkeley, live in `/usr/ucb' +while commands developed at Bell Laboratories live in `/bin' and `/usr/bin'. +.PP +A number of locally developed programs on the system live in the directory +`/usr/local'. +If we wish that all shells which we invoke to have +access to these new programs we can place the command +.DS +set path=(. /usr/ucb /bin /usr/bin /usr/local) +.DE +in our file +.I \&.cshrc +in our home directory. +Try doing this and then logging out and back in and do +.DS +set +.DE +again to see that the value assigned to +.I path +has changed. +.FS \(dg +Another directory that might interest you is /usr/new, which contains +many useful user-contributed programs provided with Berkeley Unix. +.FE +.PP +One thing you should be aware of is that the shell examines each directory +which you insert into your path and determines which commands are contained +there. Except for the current directory `.', which the shell treats specially, +this means that if commands are added to a directory in your search path after +you have started the shell, they will not necessarily be found by the shell. +If you wish to use a command which has been added in this way, you should +give the command +.DS +rehash +.DE +to the shell, which will cause it to recompute its internal table of command +locations, so that it will find the newly added command. +Since the shell has to look in the current directory `.' on each command, +placing it at the end of the path specification usually works equivalently +and reduces overhead. +.PP +Other useful built in variables are the variable +.I home +which shows your home directory, +.I cwd +which contains your current working directory, +the variable +.I ignoreeof +which can be set in your +.I \&.login +file to tell the shell not to exit when it receives an end-of-file from +a terminal (as described above). +The variable `ignoreeof' +is one of several variables which the shell does not care about the +value of, only whether they are +.I set +or +.I unset. +Thus to set this variable you simply do +.DS +set ignoreeof +.DE +and to unset it do +.DS +unset ignoreeof +.DE +These give the variable `ignoreeof' no value, but none is desired or required. +.PP +Finally, some other built-in shell variables of use are the +variables +.I noclobber +and +.I mail. +The metasyntax +.DS +> filename +.DE +which redirects the standard output of a command +will overwrite and destroy the previous contents of the named file. +In this way you may accidentally overwrite a file which is valuable. +If you would prefer that the shell not overwrite files in this +way you can +.DS +set noclobber +.DE +in your +.I \&.login +file. +Then trying to do +.DS +date > now +.DE +would cause a diagnostic if `now' existed already. +You could type +.DS +date >! now +.DE +if you really wanted to overwrite the contents of `now'. +The `>!' is a special metasyntax indicating that clobbering the +file is ok.\(dg +.FS +\(dgThe space between the `!' and the word `now' is critical here, as `!now' +would be an invocation of the +.I history +mechanism, and have a totally different effect. +.FE +.NH 2 +The shell's history list +.PP +The shell can maintain a +.I "history list" +into which it places the words +of previous commands. +It is possible to use a notation to reuse commands or words +from commands in forming new commands. +This mechanism can be used to repeat previous commands or to +correct minor typing mistakes in commands. +.PP +The following figure gives a sample session involving typical usage of the +history mechanism of the shell. +.KF +.DS +% cat bug.c +main() + +{ + printf("hello); +} +% cc !$ +cc bug.c +"bug.c", line 4: newline in string or char constant +"bug.c", line 5: syntax error +% ed !$ +ed bug.c +29 +4s/);/"&/p + printf("hello"); +w +30 +q +% !c +cc bug.c +% a.out +hello% !e +ed bug.c +30 +4s/lo/lo\e\en/p + printf("hello\en"); +w +32 +q +% !c \-o bug +cc bug.c \-o bug +% size a.out bug +a.out: 2784+364+1028 = 4176b = 0x1050b +bug: 2784+364+1028 = 4176b = 0x1050b +% ls \-l !* +ls \-l a.out bug +\(mirwxr\(mixr\(mix 1 bill 3932 Dec 19 09:41 a.out +\(mirwxr\(mixr\(mix 1 bill 3932 Dec 19 09:42 bug +% bug +hello +% num bug.c | spp +spp: Command not found. +% ^spp^ssp +num bug.c | ssp + 1 main() + 3 { + 4 printf("hello\en"); + 5 } +% !! | lpr +num bug.c | ssp | lpr +% +.DE +.KE +In this example we have a very simple C program which has a bug (or two) +in it in the file `bug.c', which we `cat' out on our terminal. We then +try to run the C compiler on it, referring to the file again as `!$', +meaning the last argument to the previous command. Here the `!' is the +history mechanism invocation metacharacter, and the `$' stands for the last +argument, by analogy to `$' in the editor which stands for the end of the line. +The shell echoed the command, as it would have been typed without use of +the history mechanism, and then executed it. +The compilation yielded error diagnostics so we now run the editor on the +file we were trying to compile, fix the bug, and run the C compiler again, +this time referring to this command simply as `!c', which repeats the last +command which started with the letter `c'. If there were other +commands starting with `c' done recently we could have said `!cc' or even +`!cc:p' which would have printed the last command starting with `cc' +without executing it. +.PP +After this recompilation, we ran the resulting `a.out' file, and then +noting that there still was a bug, ran the editor again. After fixing +the program we ran the C compiler again, but tacked onto the command +an extra `\-o bug' telling the compiler to place the resultant binary in +the file `bug' rather than `a.out'. In general, the history mechanisms +may be used anywhere in the formation of new commands and other characters +may be placed before and after the substituted commands. +.PP +We then ran the `size' command to see how large the binary program images +we have created were, and then an `ls \-l' command with the same argument +list, denoting the argument list `\!*'. +Finally we ran the program `bug' to see that its output is indeed correct. +.PP +To make a numbered listing of the program we ran the `num' command on the file `bug.c'. +In order to compress out blank lines in the output of `num' we ran the +output through the filter `ssp', but misspelled it as spp. To correct this +we used a shell substitute, placing the old text and new text between `^' +characters. This is similar to the substitute command in the editor. +Finally, we repeated the same command with `!!', but sent its output to the +line printer. +.PP +There are other mechanisms available for repeating commands. The +.I history +command prints out a number of previous commands with numbers by which +they can be referenced. There is a way to refer to a previous command +by searching for a string which appeared in it, and there are other, +less useful, ways to select arguments to include in a new command. +A complete description of all these mechanisms +is given in the C shell manual pages in the \s-2UNIX\s0 Programmer's Manual. +.NH 2 +Aliases +.PP +The shell has an +.I alias +mechanism which can be used to make transformations on input commands. +This mechanism can be used to simplify the commands you type, +to supply default arguments to commands, +or to perform transformations on commands and their arguments. +The alias facility is similar to a macro facility. +Some of the features obtained by aliasing can be obtained also +using shell command files, but these take place in another instance +of the shell and cannot directly affect the current shells environment +or involve commands such as +.I cd +which must be done in the current shell. +.PP +As an example, suppose that there is a new version of the mail program +on the system called `newmail' +you wish to use, rather than the standard mail program which is called +`mail'. +If you place the shell command +.DS +alias mail newmail +.DE +in your +.I \&.cshrc +file, the shell will transform an input line of the form +.DS +mail bill +.DE +into a call on `newmail'. +More generally, suppose we wish the command `ls' to always show +sizes of files, that is to always do `\-s'. +We can do +.DS +alias ls ls \-s +.DE +or even +.DS +alias dir ls \-s +.DE +creating a new command syntax `dir' +which does an `ls \-s'. +If we say +.DS +dir ~bill +.DE +then the shell will translate this to +.DS +ls \-s /mnt/bill +.DE +.PP +Thus the +.I alias +mechanism can be used to provide short names for commands, +to provide default arguments, +and to define new short commands in terms of other commands. +It is also possible to define aliases which contain multiple +commands or pipelines, showing where the arguments to the original +command are to be substituted using the facilities of the +history mechanism. +Thus the definition +.DS +alias cd \'cd \e!* ; ls \' +.DE +would do an +.I ls +command after each change directory +.I cd +command. +We enclosed the entire alias definition in `\'' characters to prevent +most substitutions from occurring and the character `;' from being +recognized as a metacharacter. +The `!' here is escaped with a `\e' to prevent it from being interpreted +when the alias command is typed in. +The `\e!*' here substitutes the entire argument list to the pre-aliasing +.I cd +command, without giving an error if there were no arguments. +The `;' separating commands is used here +to indicate that one command is to be done and then the next. +Similarly the definition +.DS +alias whois \'grep \e!^ /etc/passwd\' +.DE +defines a command which looks up its first argument in the password file. +.PP +.B Warning: +The shell currently reads the +.I \&.cshrc +file each time it starts up. If you place a large number of commands +there, shells will tend to start slowly. A mechanism for saving the shell +environment after reading the \fI\&.cshrc\fR file and quickly restoring it is +under development, but for now you should try to limit the number of +aliases you have to a reasonable number... 10 or 15 is reasonable, +50 or 60 will cause a noticeable delay in starting up shells, and make +the system seem sluggish when you execute commands from within the editor +and other programs. +.NH 2 +More redirection; >> and >& +.PP +There are a few more notations useful to the terminal user +which have not been introduced yet. +.PP +In addition to the standard output, commands also have a +.I "diagnostic output" +which is normally directed to the terminal even when the standard output +is redirected to a file or a pipe. +It is occasionally desirable to direct the diagnostic output along with +the standard output. +For instance if you want to redirect the output of a long running command +into a file and wish to have a record of any error diagnostic it produces +you can do +.DS +command >& file +.DE +The `>&' here tells the shell to route both the diagnostic output and the +standard output into `file'. +Similarly you can give the command +.DS +command |\|& lpr +.DE +to route both standard and diagnostic output through the pipe +to the line printer daemon +.I lpr.\(dd +.FS +\(dd A command of the form +.br +.ti +5 +command >&! file +.br +exists, and is used when +.I noclobber +is set and +.I file +already exists. +.FE +.PP +Finally, it is possible to use the form +.DS +command >> file +.DE +to place output at the end of an existing file.\(dg +.FS +\(dg If +.I noclobber +is set, then an error will result if +.I file +does not exist, otherwise the shell will create +.I file +if it doesn't exist. +A form +.br +.ti +5 +command >>! file +.br +makes it not be an error for file to not exist when +.I noclobber +is set. +.FE +.NH 2 +Jobs; Background, Foreground, or Suspended +.PP +When one or more commands +are typed together as a pipeline or as a sequence of commands separated by +semicolons, a single +.I job +is created by the shell consisting of these commands together as a unit. +Single commands without pipes or semicolons create the simplest jobs. +Usually, every line typed to the shell creates a job. +Some lines that create jobs (one per line) are +.DS +sort < data +ls \-s | sort \-n | head \-5 +mail harold +.DE +.PP +If the metacharacter `&' is typed +at the end of the commands, then the job is started as a +.I background +job. This means that the shell does not wait for it to complete but +immediately prompts and is ready for another command. The job runs +.I "in the background" +at the same time that normal jobs, called +.I foreground +jobs, continue to be read and executed by the shell one at a time. +Thus +.DS +du > usage & +.DE +would run the +.I du +program, which reports on the disk usage of your working directory (as well as +any directories below it), put the output into the file `usage' and return +immediately with a prompt for the next command without out waiting for +.I du +to finish. The +.I du +program would continue executing in the background +until it finished, even though you can type and execute more commands in the +mean time. +When a background +job terminates, a message is typed by the shell just before the next prompt +telling you that the job has completed. +In the following example the +.I du +job finishes sometime during the +execution of the +.I mail +command and its completion is reported just before +the prompt after the +.I mail +job is finished. +.DS +% du > usage & +[1] 503 +% mail bill +How do you know when a background job is finished? +EOT +.ta 1.75i +[1] \- Done du > usage +% +.so tabs +.DE +If the job did not terminate normally the `Done' message might say +something else like `Killed'. +If you want the +terminations of background jobs to be reported at the time they occur +(possibly interrupting the output of other foreground jobs), you can set +the +.I notify +variable. In the previous example this would mean that the +`Done' message might have come right in the middle of the message to +Bill. +Background jobs are unaffected by any signals from the keyboard like +the \s-2STOP\s0, \s-2INTERRUPT\s0, or \s-2QUIT\s0 signals mentioned earlier. +.PP +Jobs are recorded in a table inside the shell until they terminate. +In this table, the shell remembers the command names, arguments and the +.I "process numbers" +of all commands in the job as well as the working directory where the job was +started. +Each job in the table is either running +.I "in the foreground" +with the shell waiting for it to terminate, running +.I "in the background," +or +.I suspended. +Only one job can be running in the foreground at one time, but several +jobs can be suspended or running in the background at once. As each job +is started, it is assigned a small identifying +number called the +.I "job number" +which can be used later to refer to the job in the commands described below. +Job numbers remain +the same until the job terminates and then are re-used. +.PP +When a job is started in the backgound using `&', its number, as well +as the process numbers of all its (top level) commands, is typed by the shell +before prompting you for another command. For example, +.DS +% ls \-s | sort \-n > usage & +[2] 2034 2035 +% +.DE +runs the `ls' program with the `\-s' options, pipes this output into +the `sort' program with the `\-n' option which puts its output into the +file `usage'. +Since the `&' was at the end of the line, these two programs were started +together as a background job. After starting the job, the shell prints +the job number in brackets (2 in this case) followed by the process number +of each program started in the job. Then the shell immediates prompts for +a new command, leaving the job running simultaneously. +.PP +As mentioned in section 1.8, foreground jobs become +.I suspended +by typing ^Z +which sends a \s-2STOP\s0 signal to the currently running +foreground job. A background job can become suspended by using the +.I stop +command described below. When jobs are suspended they merely stop +any further progress until started again, either in the foreground +or the backgound. The shell notices when a job becomes stopped and +reports this fact, much like it reports the termination of background jobs. +For foreground jobs this looks like +.DS +% du > usage +^Z +Stopped +% +.DE +`Stopped' message is typed by the shell when it notices that the +.I du +program stopped. +For background jobs, using the +.I stop +command, it is +.DS +% sort usage & +[1] 2345 +% stop %1 +.ta 1.75i +[1] + Stopped (signal) sort usage +% +.so tabs +.DE +Suspending foreground jobs can be very useful when you need to temporarily +change what you are doing (execute other commands) and then return to +the suspended job. Also, foreground jobs can be suspended and then +continued as background jobs using the +.I bg +command, allowing you to continue other work and +stop waiting for the foreground job to finish. Thus +.DS +% du > usage +^Z +Stopped +% bg +[1] du > usage & +% +.DE +starts `du' in the foreground, stops it before it finishes, then continues +it in the background allowing more foreground commands to be executed. +This is especially helpful +when a foreground job ends up taking longer than you expected and you +wish you had started it in the backgound in the beginning. +.PP +All +.I "job control" +commands can take an argument that identifies a particular +job. +All job name arguments begin with the character `%', since some of the +job control commands also accept process numbers (printed by the +.I ps +command.) +The default job (when no argument is given) is called the +.I current +job and is identified by a `+' in the output of the +.I jobs +command, which shows you which jobs you have. +When only one job is stopped or running in the background (the usual case) +it is always the current job thus no argument is needed. +If a job is stopped while running in the foreground it becomes the +.I current +job and the existing current job becomes the +.I previous +job \- identified by a `\-' in the output of +.I jobs. +When the current job terminates, the previous job becomes the current job. +When given, the argument is either `%\-' (indicating +the previous job); `%#', where # is the job number; +`%pref' where pref is some unique prefix of the command name +and arguments of one of the jobs; or `%?' followed by some string found +in only one of the jobs. +.PP +The +.I jobs +command types the table of jobs, giving the job number, +commands and status (`Stopped' or `Running') of each backgound or +suspended job. With the `\-l' option the process numbers are also +typed. +.DS +% du > usage & +[1] 3398 +% ls \-s | sort \-n > myfile & +[2] 3405 +% mail bill +^Z +Stopped +% jobs +.ta 1.75i +[1] \(mi Running du > usage +[2] Running ls \-s | sort \-n > myfile +[3] \(pl Stopped mail bill +% fg %ls +ls \-s | sort \-n > myfile +% more myfile +.so tabs +.DE +.PP +The +.I fg +command runs a suspended or background job in the foreground. It is +used to restart a previously suspended job or change a background job +to run in the foreground (allowing signals or input from the terminal). +In the above example we used +.I fg +to change the `ls' job from the +background to the foreground since we wanted to wait for it to +finish before looking at its output file. +The +.I bg +command runs a suspended job in the background. It is usually used +after stopping the currently running foreground job with the +\s-2STOP\s0 signal. The combination of the \s-2STOP\s0 signal and the +.I bg +command changes a foreground job into a background job. +The +.I stop +command suspends a background job. +.PP +The +.I kill +command terminates a background or suspended job immediately. +In addition to jobs, it may be given process numbers as arguments, +as printed by +.I ps. +Thus, in the example above, the running +.I du +command could have been terminated by the command +.DS +% kill %1 +.ta 1.75i +[1] Terminated du > usage +% +.so tabs +.DE +.PP +The +.I notify +command (not the variable mentioned earlier) indicates that the termination +of a specific job should be +reported at the time it finishes instead of waiting for the next prompt. +.PP +If a job running in the background tries to read input from the terminal +it is automatically stopped. When such a job is then run in the +foreground, input can be given to the job. If desired, the job can +be run in the background again until it requests input again. +This is illustrated in the following sequence where the `s' command in the +text editor might take a long time. +.ID +.nf +% ed bigfile +120000 +1,$s/thisword/thatword/ +^Z +Stopped +% bg +[1] ed bigfile & +% + . . . some foreground commands +.ta 1.75i +[1] Stopped (tty input) ed bigfile +% fg +ed bigfile +w +120000 +q +% +.so tabs +.DE +So after the `s' command was issued, the `ed' job was stopped with ^Z +and then put in the background using +.I bg. +Some time later when the `s' command was finished, +.I ed +tried to read another command and was stopped because jobs +in the backgound cannot read from the terminal. The +.I fg +command returned the `ed' job to the foreground where it could once again +accept commands from the terminal. +.PP +The command +.DS +stty tostop +.DE +causes all background jobs run on your terminal to stop +when they are about to +write output to the terminal. This prevents messages from background +jobs from interrupting foreground job output and allows you to run +a job in the background without losing terminal output. It also +can be used for interactive programs that sometimes have long +periods without interaction. Thus each time it outputs a prompt for more +input it will stop before the prompt. It can then be run in the +foreground using +.I fg, +more input can be given and, if necessary stopped and returned to +the background. This +.I stty +command might be a good thing to put in your +.I \&.login +file if you do not like output from background jobs interrupting +your work. It also can reduce the need for redirecting the output +of background jobs if the output is not very big: +.DS +% stty tostop +% wc hugefile & +[1] 10387 +% ed text +\&. . . some time later +q +.ta 1.75i +[1] Stopped (tty output) wc hugefile +% fg wc +wc hugefile + 13371 30123 302577 +% stty \-tostop +.so tabs +.DE +Thus after some time the `wc' command, which counts the lines, words +and characters in a file, had one line of output. When it tried to +write this to the terminal it stopped. By restarting it in the +foreground we allowed it to write on the terminal exactly when we were +ready to look at its output. +Programs which attempt to change the mode of the terminal will also +block, whether or not +.I tostop +is set, when they are not in the foreground, as +it would be very unpleasant to have a background job change the state +of the terminal. +.PP +Since the +.I jobs +command only prints jobs started in the currently executing shell, +it knows nothing about background jobs started in other login sessions +or within shell files. The +.I ps +can be used in this case to find out about background jobs not started +in the current shell. +.NH 2 +Working Directories +.PP +As mentioned in section 1.6, the shell is always in a particular +.I "working directory." +The `change directory' command +.I chdir +(its +short form +.I cd +may also be used) +changes the working directory of the shell, +that is, changes the directory you +are located in. +.PP +It is useful to make a directory for each project you wish to work on +and to place all files related to that project in that directory. +The `make directory' command, +.I mkdir, +creates a new directory. +The +.I pwd +(`print working directory') command +reports the absolute pathname of the working directory of the shell, +that is, the directory you are +located in. +Thus in the example below: +.DS +% pwd +/usr/bill +% mkdir newpaper +% chdir newpaper +% pwd +/usr/bill/newpaper +% +.DE +the user has created and moved to the +directory +.I newpaper. +where, for example, he might +place a group of related files. +.PP +No matter where you have moved to in a directory hierarchy, +you can return to your `home' login directory by doing just +.DS +cd +.DE +with no arguments. +The name `..' always means the directory above the current one in +the hierarchy, thus +.DS +cd .. +.DE +changes the shell's working directory to the one directly above the +current one. +The name `..' can be used in any +pathname, thus, +.DS +cd ../programs +.DE +means +change to the directory `programs' contained in the directory +above the current one. +If you have several directories for different +projects under, say, your home directory, +this shorthand notation +permits you to switch easily between them. +.PP +The shell always remembers the pathname of its current working directory in +the variable +.I cwd. +The shell can also be requested to remember the previous directory when +you change to a new working directory. If the `push directory' command +.I pushd +is used in place of the +.I cd +command, the shell saves the name of the current working directory +on a +.I "directory stack" +before changing to the new one. +You can see this list at any time by typing the `directories' +command +.I dirs. +.ID +.nf +% pushd newpaper/references +~/newpaper/references ~ +% pushd /usr/lib/tmac +/usr/lib/tmac ~/newpaper/references ~ +% dirs +/usr/lib/tmac ~/newpaper/references ~ +% popd +~/newpaper/references ~ +% popd +~ +% +.DE +The list is printed in a horizontal line, reading left to right, +with a tilde (~) as +shorthand for your home directory\(emin this case `/usr/bill'. +The directory stack is printed whenever there is more than one +entry on it and it changes. +It is also printed by a +.I dirs +command. +.I Dirs +is usually faster and more informative than +.I pwd +since it shows the current working directory as well as any +other directories remembered in the stack. +.PP +The +.I pushd +command with no argument +alternates the current directory with the first directory in the +list. +The `pop directory' +.I popd +command without an argument returns you to the directory you were in prior to +the current one, discarding the previous current directory from the +stack (forgetting it). +Typing +.I popd +several times in a series takes you backward through the directories +you had been in (changed to) by +.I pushd +command. +There are other options to +.I pushd +and +.I popd +to manipulate the contents of the directory stack and to change +to directories not at the top of the stack; see the +.I csh +manual page for details. +.PP +Since the shell remembers the working directory in which each job +was started, it warns you when you might be confused by restarting +a job in the foreground which has a different working directory than the +current working directory of the shell. Thus if you start a background +job, then change the shell's working directory and then cause the +background job to run in the foreground, the shell warns you that the +working directory of the currently running foreground job is different +from that of the shell. +.DS +% dirs \-l +/mnt/bill +% cd myproject +% dirs +~/myproject +% ed prog.c +1143 +^Z +Stopped +% cd .. +% ls +myproject +textfile +% fg +ed prog.c (wd: ~/myproject) +.DE +This way the shell warns you when there +is an implied change of working directory, even though no cd command was +issued. In the above example the `ed' job was still in `/mnt/bill/project' +even though the shell had changed to `/mnt/bill'. +A similar warning is given when such a foreground job +terminates or is suspended (using the \s-2STOP\s0 signal) since +the return to the shell again implies a change of working directory. +.DS +% fg +ed prog.c (wd: ~/myproject) + . . . after some editing +q +(wd now: ~) +% +.DE +These messages are sometimes confusing if you use programs that change +their own working directories, since the shell only remembers which +directory a job is started in, and assumes it stays there. +The `\-l' option of +.I jobs +will type the working directory +of suspended or background jobs when it is different +from the current working directory of the shell. +.NH 2 +Useful built-in commands +.PP +We now give a few of the useful built-in commands of the shell describing +how they are used. +.PP +The +.I alias +command described above is used to assign new aliases and to show the +existing aliases. +With no arguments it prints the current aliases. +It may also be given only one argument such as +.DS +alias ls +.DE +to show the current alias for, e.g., `ls'. +.PP +The +.I echo +command prints its arguments. +It is often used in +.I "shell scripts" +or as an interactive command +to see what filename expansions will produce. +.PP +The +.I history +command will show the contents of the history list. +The numbers given with the history events can be used to reference +previous events which are difficult to reference using the +contextual mechanisms introduced above. +There is also a shell variable called +.I prompt. +By placing a `!' character in its value the shell will there substitute +the number of the current command in the history list. +You can use this number to refer to this command in a history substitution. +Thus you could +.DS +set prompt=\'\e! % \' +.DE +Note that the `!' character had to be +.I escaped +here even within `\'' characters. +.PP +The +.I limit +command is used to restrict use of resources. +With no arguments it prints the current limitations: +.DS +.ta 1i +cputime unlimited +filesize unlimited +datasize 5616 kbytes +stacksize 512 kbytes +coredumpsize unlimited +.so tabs +.DE +Limits can be set, e.g.: +.DS +limit coredumpsize 128k +.DE +Most reasonable units abbreviations will work; see the +.I csh +manual page for more details. +.PP +The +.I logout +command can be used to terminate a login shell which has +.I ignoreeof +set. +.PP +The +.I rehash +command causes the shell to recompute a table of where commands are +located. This is necessary if you add a command to a directory +in the current shell's search path and wish the shell to find it, +since otherwise the hashing algorithm may tell the shell that the +command wasn't in that directory when the hash table was computed. +.PP +The +.I repeat +command can be used to repeat a command several times. +Thus to make 5 copies of the file +.I one +in the file +.I five +you could do +.DS +repeat 5 cat one >> five +.DE +.PP +The +.I setenv +command can be used +to set variables in the environment. +Thus +.DS +setenv TERM adm3a +.DE +will set the value of the environment variable \s-2TERM\s0 +to +`adm3a'. +A user program +.I printenv +exists which will print out the environment. +It might then show: +.DS +% printenv +HOME=/usr/bill +SHELL=/bin/csh +PATH=:/usr/ucb:/bin:/usr/bin:/usr/local +TERM=adm3a +USER=bill +% +.DE +.PP +The +.I source +command can be used to force the current shell to read commands from +a file. +Thus +.DS +source .cshrc +.DE +can be used after editing in a change to the +.I \&.cshrc +file which you wish to take effect right away. +.PP +The +.I time +command can be used to cause a command to be timed no matter how much +\s-2CPU\s0 time it takes. +Thus +.DS +% time cp /etc/rc /usr/bill/rc +0.0u 0.1s 0:01 8% 2+1k 3+2io 1pf+0w +% time wc /etc/rc /usr/bill/rc + 52 178 1347 /etc/rc + 52 178 1347 /usr/bill/rc + 104 356 2694 total +0.1u 0.1s 0:00 13% 3+3k 5+3io 7pf+0w +% +.DE +indicates that the +.I cp +command used a negligible amount of user time (u) +and about 1/10th of a system time (s); the elapsed time was 1 second (0:01), +there was an average memory usage of 2k bytes of program space and 1k +bytes of data space over the cpu time involved (2+1k); the program +did three disk reads and two disk writes (3+2io), and took one page fault +and was not swapped (1pf+0w). +The word count command +.I wc +on the other hand used 0.1 seconds of user time and 0.1 seconds of system +time in less than a second of elapsed time. +The percentage `13%' indicates that over the period when it was active +the command `wc' used an average of 13 percent of the available \s-2CPU\s0 +cycles of the machine. +.PP +The +.I unalias +and +.I unset +commands can be used +to remove aliases and variable definitions from the shell, and +.I unsetenv +removes variables from the environment. +.NH 2 +What else? +.PP +This concludes the basic discussion of the shell for terminal users. +There are more features of the shell to be discussed here, and all +features of the shell are discussed in its manual pages. +One useful feature which is discussed later is the +.I foreach +built-in command which can be used to run the same command +sequence with a number of different arguments. +.PP +If you intend to use \s-2UNIX\s0 a lot you you should look through +the rest of this document and the csh manual pages (section1) to become familiar +with the other facilities which are available to you. diff --git a/bin/csh/USD.doc/csh.3 b/bin/csh/USD.doc/csh.3 new file mode 100644 index 000000000000..1f77a07dcdba --- /dev/null +++ b/bin/csh/USD.doc/csh.3 @@ -0,0 +1,651 @@ +.\" 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.2 (Berkeley) 5/16/94 +.\" +.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 +# +.DE +.sp -1 +.DS +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. diff --git a/bin/csh/USD.doc/csh.4 b/bin/csh/USD.doc/csh.4 new file mode 100644 index 000000000000..6efb30e826f9 --- /dev/null +++ b/bin/csh/USD.doc/csh.4 @@ -0,0 +1,177 @@ +.\" 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.2 (Berkeley) 5/16/94 +.\" +.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. diff --git a/bin/csh/csh.1 b/bin/csh/csh.1 new file mode 100644 index 000000000000..1667847ced83 --- /dev/null +++ b/bin/csh/csh.1 @@ -0,0 +1,2185 @@ +.\" Copyright (c) 1980, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)csh.1 8.4 (Berkeley) 6/1/94 +.\" +.Dd June 1, 1994 +.Dt CSH 1 +.Os BSD 4 +.Sh NAME +.Nm csh +.Nd a shell (command interpreter) with C-like syntax +.Sh SYNOPSIS +.Nm csh +.Op Fl bcefinstvVxX +.Op arg ... +.Nm csh +.Op Fl l +.Sh DESCRIPTION +The +.Nm csh +is a command language interpreter +incorporating a history mechanism (see +.Nm History Substitutions ) , +job control facilities (see +.Nm Jobs ) , +interactive file name +and user name completion (see +.Nm File Name Completion ) , +and a C-like syntax. It is used both as an interactive +login shell and a shell script command processor. +.Ss Argument list processing +If the first argument (argument 0) to the shell is +.Ql Fl \& , +then this is a login shell. +A login shell also can be specified by invoking the shell with the +.Ql Fl l +flag as the only argument. +.Pp +The rest of the flag arguments are interpreted as follows: +.Bl -tag -width 5n +.It Fl b +This flag forces a ``break'' from option processing, causing any further +shell arguments to be treated as non-option arguments. +The remaining arguments will not be interpreted as shell options. +This may be used to pass options to a shell script without confusion +or possible subterfuge. +The shell will not run a set-user ID script without this option. +.It Fl c +Commands are read from the (single) following argument which must +be present. +Any remaining arguments are placed in +.Ar argv . +.It Fl e +The shell exits if any invoked command terminates abnormally +or yields a non-zero exit status. +.It Fl f +The shell will start faster, because it will neither search for nor +execute commands from the file +.Pa \&.cshrc +in the invoker's home directory. +.It Fl i +The shell is interactive and prompts for its top-level input, +even if it appears not to be a terminal. +Shells are interactive without this option if their inputs +and outputs are terminals. +.It Fl l +The shell is a login shell (only applicable if +.Fl l +is the only flag specified). +.It Fl n +Commands are parsed, but not executed. +This aids in syntactic checking of shell scripts. +.It Fl s +Command input is taken from the standard input. +.It Fl t +A single line of input is read and executed. +A +.Ql \e +may be used to escape the newline at the end of this +line and continue onto another line. +.It Fl v +Causes the +.Ar verbose +variable to be set, with the effect +that command input is echoed after history substitution. +.It Fl x +Causes the +.Ar echo +variable to be set, so that commands are echoed immediately before execution. +.It Fl V +Causes the +.Ar verbose +variable to be set even before +.Pa .cshrc +is executed. +.It Fl X +Is to +.Fl x +as +.Fl V +is to +.Fl v . +.El +.Pp +After processing of flag arguments, if arguments remain but none of the +.Fl c , +.Fl i , +.Fl s , +or +.Fl t +options were given, the first argument is taken as the name of a file of +commands to be executed. +The shell opens this file, and saves its name for possible resubstitution +by `$0'. +Since many systems use either the standard version 6 or version 7 shells +whose shell scripts are not compatible with this shell, the shell will +execute such a `standard' shell if the first character of a script +is not a `#', i.e., if the script does not start with a comment. +Remaining arguments initialize the variable +.Ar argv . +.Pp +An instance of +.Nm csh +begins by executing commands from the file +.Pa /etc/csh.cshrc +and, +if this is a login shell, +.Pa \&/etc/csh.login . +It then executes +commands from +.Pa \&.cshrc +in the +.Ar home +directory of the invoker, and, if this is a login shell, the file +.Pa \&.login +in the same location. +It is typical for users on crt's to put the command ``stty crt'' +in their +.Pa \&.login +file, and to also invoke +.Xr tset 1 +there. +.Pp +In the normal case, the shell will begin reading commands from the +terminal, prompting with `% '. +Processing of arguments and the use of the shell to process files +containing command scripts will be described later. +.Pp +The shell repeatedly performs the following actions: +a line of command input is read and broken into +.Ar words . +This sequence of words is placed on the command history list and parsed. +Finally each command in the current line is executed. +.Pp +When a login shell terminates it executes commands from the files +.Pa .logout +in the user's +.Ar home +directory and +.Pa /etc/csh.logout . +.Ss Lexical structure +The shell splits input lines into words at blanks and tabs with the +following exceptions. +The characters +`&' `\&|' `;' `<' `>' `(' `)' +form separate words. +If doubled in `&&', `\&|\&|', `<<' or `>>' these pairs form single words. +These parser metacharacters may be made part of other words, or prevented their +special meaning, by preceding them with `\e'. +A newline preceded by a `\e' is equivalent to a blank. +.Pp +Strings enclosed in matched pairs of quotations, +`'\|', `\*(ga' or `"', +form parts of a word; metacharacters in these strings, including blanks +and tabs, do not form separate words. +These quotations have semantics to be described later. +Within pairs of `\'' or `"' characters, a newline preceded by a `\e' gives +a true newline character. +.Pp +When the shell's input is not a terminal, +the character `#' introduces a comment that continues to the end of the +input line. +It is prevented this special meaning when preceded by `\e' +and in quotations using `\`', `\'', and `"'. +.Ss Commands +A simple command is a sequence of words, the first of which +specifies the command to be executed. +A simple command or +a sequence of simple commands separated by `\&|' characters +forms a pipeline. +The output of each command in a pipeline is connected to the input of the next. +Sequences of pipelines may be separated by `;', and are then executed +sequentially. +A sequence of pipelines may be executed without immediately +waiting for it to terminate by following it with an `&'. +.Pp +Any of the above may be placed in `(' `)' to form a simple command (that +may be a component of a pipeline, etc.). +It is also possible to separate pipelines with `\&|\&|' or `&&' showing, +as in the C language, +that the second is to be executed only if the first fails or succeeds +respectively. (See +.Em Expressions . ) +.Ss Jobs +The shell associates a +.Ar job +with each pipeline. It keeps +a table of current jobs, printed by the +.Ar jobs +command, and assigns them small integer numbers. When +a job is started asynchronously with `&', the shell prints a line that looks +like: +.Bd -filled -offset indent +.Op 1 +1234 +.Ed +.Pp +showing that the job which was started asynchronously was job number +1 and had one (top-level) process, whose process id was 1234. +.Pp +If you are running a job and wish to do something else you may hit the key +.Nm ^Z +(control-Z) which sends a STOP signal to the current job. +The shell will then normally show that the job has been `Stopped', +and print another prompt. You can then manipulate the state of this job, +putting it in the +.Em background +with the +.Ar bg +command, or run some other +commands and eventually bring the job back into the foreground with +the +.Em foreground +command +.Ar fg . +A +.Nm ^Z +takes effect immediately and +is like an interrupt in that pending output and unread input are discarded +when it is typed. There is another special key +.Nm ^Y +that does not generate a STOP signal until a program attempts to +.Xr read 2 +it. +This request can usefully be typed ahead when you have prepared some commands +for a job that you wish to stop after it has read them. +.Pp +A job being run in the background will stop if it tries to read +from the terminal. Background jobs are normally allowed to produce output, +but this can be disabled by giving the command ``stty tostop''. +If you set this +tty option, then background jobs will stop when they try to produce +output like they do when they try to read input. +.Pp +There are several ways to refer to jobs in the shell. The character +`%' introduces a job name. If you wish to refer to job number 1, you can +name it as `%1'. Just naming a job brings it to the foreground; thus +`%1' is a synonym for `fg %1', bringing job number 1 back into the foreground. +Similarly saying `%1 &' resumes job number 1 in the background. +Jobs can also be named by prefixes of the string typed in to start them, +if these prefixes are unambiguous, thus `%ex' would normally restart +a suspended +.Xr ex 1 +job, if there were only one suspended job whose name began with +the string `ex'. It is also possible to say `%?string' +which specifies a job whose text contains +.Ar string , +if there is only one such job. +.Pp +The shell maintains a notion of the current and previous jobs. +In output about jobs, the current job is marked with a `+' +and the previous job with a `\-'. The abbreviation `%+' refers +to the current job and `%\-' refers to the previous job. For close +analogy with the syntax of the +.Ar history +mechanism (described below), +`%%' is also a synonym for the current job. +.Pp +The job control mechanism requires that the +.Xr stty 1 +option +.Ic new +be set. It is an artifact from a +.Em new +implementation +of the +tty driver that allows generation of interrupt characters from +the keyboard to tell jobs to stop. See stty(1) for details +on setting options in the new tty driver. +.Ss Status reporting +This shell learns immediately whenever a process changes state. +It normally informs you whenever a job becomes blocked so that +no further progress is possible, but only just before it prints +a prompt. This is done so that it does not otherwise disturb your work. +If, however, you set the shell variable +.Ar notify , +the shell will notify you immediately of changes of status in background +jobs. +There is also a shell command +.Ar notify +that marks a single process so that its status changes will be immediately +reported. By default +.Ar notify +marks the current process; +simply say `notify' after starting a background job to mark it. +.Pp +When you try to leave the shell while jobs are stopped, you will +be warned that `You have stopped jobs.' You may use the +.Ar jobs +command to see what they are. If you do this or immediately try to +exit again, the shell will not warn you a second time, and the suspended +jobs will be terminated. +.Ss File Name Completion +When the file name completion feature is enabled by setting +the shell variable +.Ar filec +(see +.Ic set ) , +.Nm csh +will +interactively complete file names and user names from unique +prefixes, when they are input from the terminal followed by +the escape character (the escape key, or control-[) +For example, +if the current directory looks like +.Bd -literal -offset indent +DSC.OLD bin cmd lib xmpl.c +DSC.NEW chaosnet cmtest mail xmpl.o +bench class dev mbox xmpl.out +.Ed +.Pp +and the input is +.Pp +.Dl % vi ch +.ne 1i +.Pp +.Nm csh +will complete the prefix ``ch'' +to the only matching file name ``chaosnet'', changing the input +line to +.Pp +.Dl % vi chaosnet +.Pp +However, given +.Pp +.Dl % vi D +.Pp +.Nm csh +will only expand the input to +.Pp +.Dl % vi DSC. +.Pp +and will sound the terminal bell to indicate that the expansion is +incomplete, since there are two file names matching the prefix ``D''. +.Pp +If a partial file name is followed by the end-of-file character +(usually control-D), then, instead of completing the name, +.Nm csh +will list all file names matching the prefix. For example, +the input +.Pp +.Dl % vi D +.Pp +causes all files beginning with ``D'' to be listed: +.Pp +.Dl DSC.NEW DSC.OLD +.Pp +while the input line remains unchanged. +.Pp +The same system of escape and end-of-file can also be used to +expand partial user names, if the word to be completed +(or listed) begins with the character ``~''. For example, +typing +.Pp +.Dl cd ~ro +.Pp +may produce the expansion +.Pp +.Dl cd ~root +.Pp +The use of the terminal bell to signal errors or multiple matches +can be inhibited by setting the variable +.Ar nobeep . +.Pp +Normally, all files in the particular directory are candidates +for name completion. Files with certain suffixes can be excluded +from consideration by setting the variable +.Ar fignore +to the +list of suffixes to be ignored. Thus, if +.Ar fignore +is set by +the command +.Pp +.Dl % set fignore = (.o .out) +.Pp +then typing +.Pp +.Dl % vi x +.Pp +would result in the completion to +.Pp +.Dl % vi xmpl.c +.Pp +ignoring the files "xmpl.o" and "xmpl.out". +However, if the only completion possible requires not ignoring these +suffixes, then they are not ignored. In addition, +.Ar fignore +does not affect the listing of file names by control-D. All files +are listed regardless of their suffixes. +.Ss Substitutions +We now describe the various transformations the shell performs on the +input in the order in which they occur. +.Ss History substitutions +History substitutions place words from previous command input as portions +of new commands, making it easy to repeat commands, repeat arguments +of a previous command in the current command, or fix spelling mistakes +in the previous command with little typing and a high degree of confidence. +History substitutions begin with the character `!' and may begin +.Ar anywhere +in the input stream (with the proviso that they +.Nm "do not" +nest.) +This `!' may be preceded by a `\e' to prevent its special meaning; for +convenience, an `!' is passed unchanged when it is followed by a blank, +tab, newline, `=' or `('. +(History substitutions also occur when an input line begins with `\*(ua'. +This special abbreviation will be described later.) +Any input line that contains history substitution is echoed on the terminal +before it is executed as it could have been typed without history substitution. +.Pp +Commands input from the terminal that consist of one or more words +are saved on the history list. +The history substitutions reintroduce sequences of words from these +saved commands into the input stream. +The size of the history list is controlled by the +.Ar history +variable; the previous command is always retained, +regardless of the value of the history variable. +Commands are numbered sequentially from 1. +.Pp +For definiteness, consider the following output from the +.Ar history +command: +.Bd -literal -offset indent +\09 write michael +10 ex write.c +11 cat oldwrite.c +12 diff *write.c +.Ed +.Pp +The commands are shown with their event numbers. +It is not usually necessary to use event numbers, but the current event +number can be made part of the +.Ar prompt +by placing an `!' in the prompt string. +.Pp +With the current event 13 we can refer to previous events by event +number `!11', relatively as in `!\-2' (referring to the same event), +by a prefix of a command word +as in `!d' for event 12 or `!wri' for event 9, or by a string contained in +a word in the command as in `!?mic?' also referring to event 9. +These forms, without further change, simply reintroduce the words +of the specified events, each separated by a single blank. +As a special case, `!!' refers to the previous command; thus `!!' +alone is a +.Ar redo . +.Pp +To select words from an event we can follow the event specification by +a `:' and a designator for the desired words. +The words of an input line are numbered from 0, +the first (usually command) word being 0, the second word (first argument) +being 1, etc. +The basic word designators are: +.Pp +.Bl -tag -width Ds -compact -offset indent +.It \&0 +first (command) word +.It Ar n +.Ar n Ns 'th +argument +.It \*(ua +first argument, i.e., `1' +.It $ +last argument +.It % +word matched by (immediately preceding) +.No \&? Ns Ar s Ns \&? +search +.It Ar \&x\-y +range of words +.It Ar \&\-y +abbreviates +.Ar `\&0\-y\' +.It * +abbreviates `\*(ua\-$', or nothing if only 1 word in event +.It Ar x* +abbreviates +.Ar `x\-$\' +.It Ar x\- +like +.Ar `x*\' +but omitting word `$' +.El +.Pp +The `:' separating the event specification from the word designator +can be omitted if the argument selector begins with a `\*(ua', `$', `*' +`\-' or `%'. +After the optional word designator can be +placed a sequence of modifiers, each preceded by a `:'. +The following modifiers are defined: +.Pp +.Bl -tag -width Ds -compact -offset indent +.It h +Remove a trailing pathname component, leaving the head. +.It r +Remove a trailing `.xxx' component, leaving the root name. +.It e +Remove all but the extension `.xxx' part. +.It s Ns Ar /l/r/ +Substitute +.Ar l +for +.Ar r +.It t +Remove all leading pathname components, leaving the tail. +.It \&& +Repeat the previous substitution. +.It g +Apply the change once on each word, prefixing the above, e.g., `g&'. +.It a +Apply the change as many times as possible on a single word, prefixing +the above. It can be used together with `g' to apply a substitution +globally. +.It p +Print the new command line but do not execute it. +.It q +Quote the substituted words, preventing further substitutions. +.It x +Like q, but break into words at blanks, tabs and newlines. +.El +.Pp +Unless preceded by a `g' the change is applied only to the first +modifiable word. With substitutions, it is an error for no word to be +applicable. +.Pp +The left hand side of substitutions are not regular expressions in the sense +of the editors, but instead strings. +Any character may be used as the delimiter in place of `/'; +a `\e' quotes the delimiter into the +.Ar l " " +and +.Ar r " " +strings. +The character `&' in the right hand side is replaced by the text from +the left. +A `\e' also quotes `&'. +A null +.Ar l +(`//') +uses the previous string either from an +.Ar l +or from a +contextual scan string +.Ar s +in +.No \&`!? Ns Ar s Ns \e?' . +The trailing delimiter in the substitution may be omitted if a newline +follows immediately as may the trailing `?' in a contextual scan. +.Pp +A history reference may be given without an event specification, e.g., `!$'. +Here, the reference is to the previous command unless a previous +history reference occurred on the same line in which case this form repeats +the previous reference. +Thus `!?foo?\*(ua !$' gives the first and last arguments +from the command matching `?foo?'. +.Pp +A special abbreviation of a history reference occurs when the first +non-blank character of an input line is a `\*(ua'. +This is equivalent to `!:s\*(ua' providing a convenient shorthand for substitutions +on the text of the previous line. +Thus `\*(ualb\*(ualib' fixes the spelling of +`lib' +in the previous command. +Finally, a history substitution may be surrounded with `{' and `}' +if necessary to insulate it from the characters that follow. +Thus, after `ls \-ld ~paul' we might do `!{l}a' to do `ls \-ld ~paula', +while `!la' would look for a command starting with `la'. +.Pp +.Ss Quotations with \' and \&" +The quotation of strings by `\'' and `"' can be used +to prevent all or some of the remaining substitutions. +Strings enclosed in `\'' are prevented any further interpretation. +Strings enclosed in `"' may be expanded as described below. +.Pp +In both cases the resulting text becomes (all or part of) a single word; +only in one special case (see +.Em Command Substitution +below) does a `"' quoted string yield parts of more than one word; +`\'' quoted strings never do. +.Ss Alias substitution +The shell maintains a list of aliases that can be established, displayed +and modified by the +.Ar alias +and +.Ar unalias +commands. +After a command line is scanned, it is parsed into distinct commands and +the first word of each command, left-to-right, is checked to see if it +has an alias. +If it does, then the text that is the alias for that command is reread +with the history mechanism available +as though that command were the previous input line. +The resulting words replace the +command and argument list. +If no reference is made to the history list, then the argument list is +left unchanged. +.Pp +Thus if the alias for `ls' is `ls \-l' the command `ls /usr' would map to +`ls \-l /usr', the argument list here being undisturbed. +Similarly if the alias for `lookup' was `grep !\*(ua /etc/passwd' then +`lookup bill' would map to `grep bill /etc/passwd'. +.Pp +If an alias is found, the word transformation of the input text +is performed and the aliasing process begins again on the reformed input line. +Looping is prevented if the first word of the new text is the same as the old +by flagging it to prevent further aliasing. +Other loops are detected and cause an error. +.Pp +Note that the mechanism allows aliases to introduce parser metasyntax. +Thus, we can `alias print \'pr \e!* \&| lpr\'' to make a command that +.Ar pr Ns 's +its arguments to the line printer. +.Ss Variable substitution +The shell maintains a set of variables, each of which has as value a list +of zero or more words. +Some of these variables are set by the shell or referred to by it. +For instance, the +.Ar argv +variable is an image of the shell's argument list, and words of this +variable's value are referred to in special ways. +.Pp +The values of variables may be displayed and changed by using the +.Ar set +and +.Ar unset +commands. +Of the variables referred to by the shell a number are toggles; +the shell does not care what their value is, +only whether they are set or not. +For instance, the +.Ar verbose +variable is a toggle that causes command input to be echoed. +The setting of this variable results from the +.Fl v +command line option. +.Pp +Other operations treat variables numerically. +The `@' command permits numeric calculations to be performed and the result +assigned to a variable. +Variable values are, however, always represented as (zero or more) strings. +For the purposes of numeric operations, the null string is considered to be +zero, and the second and additional words of multiword values are ignored. +.Pp +After the input line is aliased and parsed, and before each command +is executed, variable substitution +is performed keyed by `$' characters. +This expansion can be prevented by preceding the `$' with a `\e' except +within `"'s where it +.Em always +occurs, and within `\''s where it +.Em never +occurs. +Strings quoted by `\*(ga' are interpreted later (see +.Nm "Command substitution" +below) so `$' substitution does not occur there until later, if at all. +A `$' is passed unchanged if followed by a blank, tab, or end-of-line. +.Pp +Input/output redirections are recognized before variable expansion, +and are variable expanded separately. +Otherwise, the command name and entire argument list are expanded together. +It is thus possible for the first (command) word (to this point) to generate +more than one word, the first of which becomes the command name, +and the rest of which become arguments. +.Pp +Unless enclosed in `"' or given the `:q' modifier the results of variable +substitution may eventually be command and filename substituted. +Within `"', a variable whose value consists of multiple words expands to a +(portion of) a single word, with the words of the variables value +separated by blanks. +When the `:q' modifier is applied to a substitution +the variable will expand to multiple words with each word separated +by a blank and quoted to prevent later command or filename substitution. +.Pp +The following metasequences are provided for introducing variable values into +the shell input. +Except as noted, it is an error to reference a variable that is not set. +.Pp +.Bl -tag -width Ds -compact -offset indent +.It $name +.It ${name} +Are replaced by the words of the value of variable +.Ar name , +each separated by a blank. +Braces insulate +.Ar name +from following characters that would otherwise be part of it. +Shell variables have names consisting of up to 20 letters and digits +starting with a letter. The underscore character is considered a letter. +If +.Ar name +is not a shell variable, but is set in the environment, then +that value is returned (but +.Nm : +modifiers and the other forms +given below are not available here). +.It $name Ns Op selector +.It ${name Ns [ selector ] } +May be used to select only some of the words from the value of +.Ar name . +The selector is subjected to `$' substitution and may consist of a single +number or two numbers separated by a `\-'. +The first word of a variables value is numbered `1'. +If the first number of a range is omitted it defaults to `1'. +If the last number of a range is omitted it defaults to `$#name'. +The selector `*' selects all words. +It is not an error for a range to be empty if the second argument is omitted +or in range. +.ne 1i +.It $#name +.It ${#name} +Gives the number of words in the variable. +This is useful for later use in a +`$argv[selector]'. +.It $0 +Substitutes the name of the file from which command input is being read. +An error occurs if the name is not known. +.It $number +.It ${number} +Equivalent to +`$argv[number]'. +.It $* +Equivalent to +`$argv[*]'. +The modifiers `:e', `:h', `:t', `:r', `:q' and `:x' may be applied to +the substitutions above as may `:gh', `:gt' and `:gr'. +If braces `{' '}' appear in the command form then the modifiers +must appear within the braces. +The current implementation allows only one `:' modifier on each `$' expansion. +.El +.Pp +The following substitutions may not be modified with `:' modifiers. +.Bl -tag -width Ds -compact -offset indent +.It $?name +.It ${?name} +Substitutes the string `1' if name is set, `0' if it is not. +.It $?0 +Substitutes `1' if the current input filename is known, `0' if it is not. +.It \&$\&$\& +Substitute the (decimal) process number of the (parent) shell. +.It $! +Substitute the (decimal) process number of the last background process +started by this shell. +.It $< +Substitutes a line from the standard +input, with no further interpretation. +It can be used to read from the keyboard in a shell script. +.El +.Ss Command and filename substitution +The remaining substitutions, command and filename substitution, +are applied selectively to the arguments of builtin commands. +By selectively, we mean that portions of expressions which are +not evaluated are not subjected to these expansions. +For commands that are not internal to the shell, the command +name is substituted separately from the argument list. +This occurs very late, +after input-output redirection is performed, and in a child +of the main shell. +.Ss Command substitution +Command substitution is shown by a command enclosed in `\*(ga'. +The output from such a command is normally broken into separate words +at blanks, tabs and newlines, with null words being discarded; +this text then replaces the original string. +Within `"'s, only newlines force new words; blanks and tabs are preserved. +.Pp +In any case, the single final newline does not force a new word. +Note that it is thus possible for a command substitution to yield +only part of a word, even if the command outputs a complete line. +.Ss Filename substitution +If a word contains any of the characters `*', `?', `[' or `{' +or begins with the character `~', then that word is a candidate for +filename substitution, also known as `globbing'. +This word is then regarded as a pattern, and replaced with an alphabetically +sorted list of file names that match the pattern. +In a list of words specifying filename substitution it is an error for +no pattern to match an existing file name, but it is not required +for each pattern to match. +Only the metacharacters `*', `?' and `[' imply pattern matching, +the characters `~' and `{' being more akin to abbreviations. +.Pp +In matching filenames, the character `.' at the beginning of a filename +or immediately following a `/', as well as the character `/' must +be matched explicitly. +The character `*' matches any string of characters, including the null +string. +The character `?' matches any single character. +The sequence +.Sq Op ... +matches any one of the characters enclosed. +Within +.Sq Op ... , +a pair of characters separated by `\-' matches any character lexically between +the two (inclusive). +.Pp +The character `~' at the beginning of a filename refers to home +directories. +Standing alone, i.e., `~' it expands to the invokers home directory as reflected +in the value of the variable +.Ar home . +When followed by a name consisting of letters, digits and `\-' characters, +the shell searches for a user with that name and substitutes their +home directory; thus `~ken' might expand to `/usr/ken' and `~ken/chmach' +to `/usr/ken/chmach'. +If the character `~' is followed by a character other than a letter or `/' +or does not appear at the beginning of a word, +it is left undisturbed. +.Pp +The metanotation `a{b,c,d}e' is a shorthand for `abe ace ade'. +Left to right order is preserved, with results of matches being sorted +separately at a low level to preserve this order. +This construct may be nested. +Thus, `~source/s1/{oldls,ls}.c' expands to +`/usr/source/s1/oldls.c /usr/source/s1/ls.c' +without chance of error +if the home directory for `source' is `/usr/source'. +Similarly `../{memo,*box}' might expand to `../memo ../box ../mbox'. +(Note that `memo' was not sorted with the results of the match to `*box'.) +As a special case `{', `}' and `{}' are passed undisturbed. +.Ss Input/output +The standard input and the standard output of a command may be redirected +with the following syntax: +.Pp +.Bl -tag -width Ds -compact -offset indent +.It < name +Open file +.Ar name +(which is first variable, command and filename expanded) as the standard +input. +.It << word +Read the shell input up to a line that is identical to +.Ar word . +.Ar Word +is not subjected to variable, filename or command substitution, +and each input line is compared to +.Ar word +before any substitutions are done on the input line. +Unless a quoting `\e', `"', `\*(aa' or `\*(ga' appears in +.Ar word , +variable and command substitution is performed on the intervening lines, +allowing `\e' to quote `$', `\e' and `\*(ga'. +Commands that are substituted have all blanks, tabs, and newlines +preserved, except for the final newline which is dropped. +The resultant text is placed in an anonymous temporary file that +is given to the command as its standard input. +.It > name +.It >! name +.It >& name +.It >&! name +The file +.Ar name +is used as the standard output. +If the file does not exist then it is created; +if the file exists, it is truncated; its previous contents are lost. +.Pp +If the variable +.Ar noclobber +is set, then the file must not exist or be a character special file (e.g., a +terminal or `/dev/null') or an error results. +This helps prevent accidental destruction of files. +Here, the `!' forms can be used to suppress this check. +.Pp +The forms involving `&' route the standard error output into the specified +file as well as the standard output. +.Ar Name +is expanded in the same way as `<' input filenames are. +.It >> name +.It >>& name +.It >>! name +.It >>&! name +Uses file +.Ar name +as the standard output; +like `>' but places output at the end of the file. +If the variable +.Ar noclobber +is set, then it is an error for the file not to exist unless +one of the `!' forms is given. +Otherwise similar to `>'. +.El +.Pp +A command receives the environment in which the shell was +invoked as modified by the input-output parameters and +the presence of the command in a pipeline. +Thus, unlike some previous shells, commands run from a file of shell commands +have no access to the text of the commands by default; +instead they receive the original standard input of the shell. +The `<<' mechanism should be used to present inline data. +This permits shell command scripts to function as components of pipelines +and allows the shell to block read its input. +Note that the default standard input for a command run detached is +.Ar not +modified to be the empty file +.Pa /dev/null ; +instead the standard input +remains as the original standard input of the shell. If this is a terminal +and if the process attempts to read from the terminal, then the process +will block and the user will be notified (see +.Sx Jobs +above). +.Pp +The standard error output may be directed through +a pipe with the standard output. +Simply use the form `\&|&' instead of just `\&|'. +.Ss Expressions +Several of the builtin commands (to be described later) +take expressions, in which the operators are similar to those of C, with +the same precedence. +These expressions appear in the +.Nm @, +.Ar exit , +.Ar if , +and +.Ar while +commands. +The following operators are available: +.Bd -ragged -offset indent +\&|\&| && \&| \*(ua & == != =~ !~ <= >= +< > << >> + \- * / % ! ~ ( ) +.Ed +.Pp +Here the precedence increases to the right, +`==' `!=' `=~' and `!~', `<=' `>=' `<' and `>', `<<' and `>>', `+' and `\-', +`*' `/' and `%' being, in groups, at the same level. +The `==' `!=' `=~' and `!~' operators compare their arguments as strings; +all others operate on numbers. +The operators `=~' and `!~' are like `!=' and `==' except that the right +hand side is a +.Ar pattern +(containing, e.g., `*'s, `?'s and instances of `[...]') +against which the left hand operand is matched. This reduces the +need for use of the +.Ar switch +statement in shell scripts when all that is really needed is pattern matching. +.Pp +Strings that begin with `0' are considered octal numbers. +Null or missing arguments are considered `0'. +The result of all expressions are strings, +which represent decimal numbers. +It is important to note that no two components of an expression can appear +in the same word; except when adjacent to components of expressions that +are syntactically significant to the parser (`&' `\&|' `<' `>' `(' `)'), +they should be surrounded by spaces. +.Pp +Also available in expressions as primitive operands are command executions +enclosed in `{' and `}' +and file enquiries of the form +.Fl l +.Ar name +where +.Ic l +is one of: +.Bd -literal -offset indent +r read access +w write access +x execute access +e existence +o ownership +z zero size +f plain file +d directory +.Ed +.Pp +The specified name is command and filename expanded and then tested +to see if it has the specified relationship to the real user. +If the file does not exist or is inaccessible then all enquiries return +false, i.e., `0'. +Command executions succeed, returning true, i.e., `1', +if the command exits with status 0, otherwise they fail, returning +false, i.e., `0'. +If more detailed status information is required then the command +should be executed outside an expression and the variable +.Ar status +examined. +.Ss Control flow +The shell contains several commands that can be used to regulate the +flow of control in command files (shell scripts) and +(in limited but useful ways) from terminal input. +These commands all operate by forcing the shell to reread or skip in its +input and, because of the implementation, restrict the placement of some +of the commands. +.Pp +The +.Ic foreach , +.Ic switch , +and +.Ic while +statements, as well as the +.Ic if\-then\-else +form of the +.Ic if +statement require that the major keywords appear in a single simple command +on an input line as shown below. +.pl +1 +.Pp +If the shell's input is not seekable, +the shell buffers up input whenever a loop is being read +and performs seeks in this internal buffer to accomplish the rereading +implied by the loop. +(To the extent that this allows, backward goto's will succeed on +non-seekable inputs.) +.Ss Builtin commands +Builtin commands are executed within the shell. +If a builtin command occurs as any component of a pipeline +except the last then it is executed in a subshell. +.Pp +.Bl -tag -width Ds -compact -offset indent +.It Ic alias +.It Ic alias Ar name +.It Ic alias Ar name wordlist +The first form prints all aliases. +The second form prints the alias for name. +The final form assigns the specified +.Ar wordlist +as the alias of +.Ar name ; +.Ar wordlist +is command and filename substituted. +.Ar Name +is not allowed to be +.Ar alias +or +.Ar unalias . +.Pp +.It Ic alloc +Shows the amount of dynamic memory acquired, broken down into used and +free memory. +With an argument shows the number of free and used blocks in each size +category. The categories start at size 8 and double at each step. +This command's output may vary across system types, since +systems other than the VAX may use a different memory allocator. +.Pp +.It Ic bg +.It Ic bg \&% Ns Ar job ... +Puts the current or specified jobs into the background, continuing them +if they were stopped. +.Pp +.It Ic break +Causes execution to resume after the +.Ic end +of the nearest enclosing +.Ic foreach +or +.Ic while . +The remaining commands on the current line are executed. +Multi-level breaks are thus possible by writing them all on one line. +.Pp +.It Ic breaksw +Causes a break from a +.Ic switch , +resuming after the +.Ic endsw . +.Pp +.It Ic case Ar label : +A label in a +.Ic switch +statement as discussed below. +.Pp +.It Ic cd +.It Ic cd Ar name +.It Ic chdir +.It Ic chdir Ar name +Change the shell's working directory to directory +.Ar name . +If no argument is given then change to the home directory of the user. +If +.Ar name +is not found as a subdirectory of the current directory (and does not begin +with `/', `./' or `../'), then each +component of the variable +.Ic cdpath +is checked to see if it has a subdirectory +.Ar name . +Finally, if all else fails but +.Ar name +is a shell variable whose value begins with `/', then this +is tried to see if it is a directory. +.Pp +.It Ic continue +Continue execution of the nearest enclosing +.Ic while +or +.Ic foreach . +The rest of the commands on the current line are executed. +.Pp +.It Ic default : +Labels the default case in a +.Ic switch +statement. +The default should come after all +.Ic case +labels. +.Pp +.It Ic dirs +Prints the directory stack; the top of the stack is at the left, +the first directory in the stack being the current directory. +.Pp +.It Ic echo Ar wordlist +.It Ic echo Fl n Ar wordlist +The specified words are written to the shell's standard output, separated +by spaces, and terminated with a newline unless the +.Fl n +option is specified. +.Pp +.It Ic else +.It Ic end +.It Ic endif +.It Ic endsw +See the description of the +.Ic foreach , +.Ic if , +.Ic switch , +and +.Ic while +statements below. +.Pp +.It Ic eval Ar arg ... +(As in +.Xr sh 1 . ) +The arguments are read as input to the shell and the resulting +command(s) executed in the context of the current shell. +This is usually used to execute commands +generated as the result of command or variable substitution, since +parsing occurs before these substitutions. See +.Xr tset 1 +for an example of using +.Ic eval . +.Pp +.It Ic exec Ar command +The specified command is executed in place of the current shell. +.Pp +.It Ic exit +.It Ic exit Ar (expr ) +The shell exits either with the value of the +.Ic status +variable (first form) or with the value of the specified +.Ic expr +(second form). +.Pp +.It Ic fg +.It Ic fg % Ns Ar job ... +Brings the current or specified jobs into the foreground, continuing them if +they were stopped. +.Pp +.It Ic foreach Ar name (wordlist) +.It ... +.It Ic end +The variable +.Ic name +is successively set to each member of +.Ic wordlist +and the sequence of commands between this command and the matching +.Ic end +are executed. +(Both +.Ic foreach +and +.Ic end +must appear alone on separate lines.) +The builtin command +.Ic continue +may be used to continue the loop prematurely and the builtin +command +.Ic break +to terminate it prematurely. +When this command is read from the terminal, the loop is read once +prompting with `?' before any statements in the loop are executed. +If you make a mistake typing in a loop at the terminal you can rub it out. +.Pp +.It Ic glob Ar wordlist +Like +.Ic echo +but no `\e' escapes are recognized and words are delimited +by null characters in the output. +Useful for programs that wish to use the shell to filename expand a list +of words. +.Pp +.It Ic goto Ar word +The specified +.Ic word +is filename and command expanded to yield a string of the form `label'. +The shell rewinds its input as much as possible +and searches for a line of the form `label:' +possibly preceded by blanks or tabs. +Execution continues after the specified line. +.Pp +.It Ic hashstat +Print a statistics line showing how effective the internal hash +table has been at locating commands (and avoiding +.Ic exec Ns \'s ) . +An +.Ic exec +is attempted for each component of the +.Em path +where the hash function indicates a possible hit, and in each component +that does not begin with a `/'. +.Pp +.It Ic history +.It Ic history Ar n +.It Ic history Fl r Ar n +.It Ic history Fl h Ar n +Displays the history event list; if +.Ar n +is given only the +.Ar n +most recent events are printed. +The +.Fl r +option reverses the order of printout to be most recent first +instead of oldest first. +The +.Fl h +option causes the history list to be printed without leading numbers. +This format produces files suitable for sourcing using the \-h +option to +.Ic source . +.Pp +.It Ic if ( Ar expr ) No command +If the specified expression evaluates true, then the single +.Ar command +with arguments is executed. +Variable substitution on +.Ar command +happens early, at the same +time it does for the rest of the +.Ic if +command. +.Ar Command +must be a simple command, not +a pipeline, a command list, or a parenthesized command list. +Input/output redirection occurs even if +.Ar expr +is false, i.e., when command is +.Sy not +executed (this is a bug). +.Pp +.It Ic if ( Ar expr ) Ic then +.It ... +.It Ic else if ( Ar expr2 ) Ic then +.It ... +.It Ic else +.It ... +.It Ic endif +If the specified +.Ar expr +is true then the commands up to the first +.Ic else +are executed; otherwise if +.Ar expr2 +is true then the commands up to the +second +.Ic else +are executed, etc. +Any number of +.Ic else-if +pairs are possible; only one +.Ic endif +is needed. +The +.Ic else +part is likewise optional. +(The words +.Ic else +and +.Ic endif +must appear at the beginning of input lines; +the +.Ic if +must appear alone on its input line or after an +.Ic else . ) +.Pp +.It Ic jobs +.It Ic jobs Fl l +Lists the active jobs; the +.Fl l +option lists process id's in addition to the normal information. +.Pp +.It Ic kill % Ns Ar job +.It Ic kill Ar pid +.It Ic kill Fl sig Ar pid ... +.It Ic kill Fl l +Sends either the TERM (terminate) signal or the +specified signal to the specified jobs or processes. +Signals are either given by number or by names (as given in +.Pa /usr/include/signal.h, +stripped of the prefix ``SIG''). +The signal names are listed by ``kill \-l''. +There is no default, just saying `kill' does not +send a signal to the current job. +If the signal being sent is TERM (terminate) or HUP (hangup), +then the job or process will be sent a CONT (continue) signal as well. +.Pp +.It Ic limit +.It Ic limit Ar resource +.It Ic limit Ar resource maximum-use +.It Ic limit Fl h +.It Ic limit Fl h Ar resource +.It Ic limit Fl h Ar resource maximum-use +Limits the consumption by the current process and each process +it creates to not individually exceed +.Ar maximum-use +on the +specified +.Ar resource . +If no +.Ar maximum-use +is given, then +the current limit is printed; if no +.Ar resource +is given, then +all limitations are given. If the +.Fl h +flag is given, the hard limits are used instead of the current +limits. The hard limits impose a ceiling on the values of +the current limits. Only the super-user may raise the hard limits, +but a user may lower or raise the current limits within the legal range. +.Pp +Resources controllable currently include +.Ar cputime +(the maximum +number of cpu-seconds to be used by each process), +.Ar filesize +(the largest single file that can be created), +.Ar datasize +(the maximum growth of the data+stack region via +.Xr sbrk 2 +beyond the end of the program text), +.Ar stacksize +(the maximum +size of the automatically-extended stack region), and +.Ar coredumpsize +(the size of the largest core dump that will be created). +.lp -1 +.ne 1i +.Pp +The +.Ar maximum-use +may be given as a (floating point or integer) +number followed by a scale factor. For all limits other than +.Ar cputime +the default scale is `k' or `kilobytes' (1024 bytes); +a scale factor of `m' or `megabytes' may also be used. +For +.Ar cputime +the default scale is `seconds'; +a scale factor of `m' for minutes +or `h' for hours, or a time of the form `mm:ss' giving minutes +and seconds also may be used. +.Pp +For both +.Ar resource +names and scale factors, unambiguous prefixes +of the names suffice. +.Pp +.It Ic login +Terminate a login shell, replacing it with an instance of +.Pa /bin/login. +This is one way to log off, included for compatibility with +.Xr sh 1 . +.Pp +.It Ic logout +Terminate a login shell. +Especially useful if +.Ic ignoreeof +is set. +.Pp +.It Ic nice +.It Ic nice Ar +number +.It Ic nice Ar command +.It Ic nice Ar +number command +The first form sets the +scheduling priority +for this shell to 4. +The second form sets the +priority +to the given +.Ar number . +The final two forms run command at priority 4 and +.Ar number +respectively. +The greater the number, the less cpu the process will get. +The super-user may specify negative priority by using `nice \-number ...'. +.Ar Command +is always executed in a sub-shell, and the restrictions +placed on commands in simple +.Ic if +statements apply. +.Pp +.It Ic nohup +.It Ic nohup Ar command +The first form can be used in shell scripts to cause hangups to be +ignored for the remainder of the script. +The second form causes the specified command to be run with hangups +ignored. +All processes detached with `&' are effectively +.Ic nohup Ns \'ed . +.Pp +.It Ic notify +.It Ic notify % Ns Ar job ... +Causes the shell to notify the user asynchronously when the status of the +current or specified jobs change; normally notification is presented +before a prompt. This is automatic if the shell variable +.Ic notify +is set. +.Pp +.It Ic onintr +.It Ic onintr Fl +.It Ic onintr Ar label +Control the action of the shell on interrupts. +The first form restores the default action of the shell on interrupts +which is to terminate shell scripts or to return to the terminal command +input level. +The second form `onintr \-' causes all interrupts to be ignored. +The final form causes the shell to execute a `goto label' when +an interrupt is received or a child process terminates because +it was interrupted. +.Pp +In any case, if the shell is running detached and interrupts are +being ignored, all forms of +.Ic onintr +have no meaning and interrupts +continue to be ignored by the shell and all invoked commands. +Finally +.Ic onintr +statements are ignored in the system startup files where interrupts +are disabled (/etc/csh.cshrc, /etc/csh.login). +.Pp +.It Ic popd +.It Ic popd Ar +n +Pops the directory stack, returning to the new top directory. +With an argument +.Ns \`+ Ar n Ns \' +discards the +.Ar n Ns \'th +entry in the stack. +The members of the directory stack are numbered from the top starting at 0. +.ne 1i +.Pp +.It Ic pushd +.It Ic pushd Ar name +.It Ic pushd Ar n +With no arguments, +.Ic pushd +exchanges the top two elements of the directory stack. +Given a +.Ar name +argument, +.Ic pushd +changes to the new directory (ala +.Ic cd ) +and pushes the old current working directory +(as in +.Ic csw ) +onto the directory stack. +With a numeric argument, +.Ic pushd +rotates the +.Ar n Ns \'th +argument of the directory +stack around to be the top element and changes to it. The members +of the directory stack are numbered from the top starting at 0. +.Pp +.It Ic rehash +Causes the internal hash table of the contents of the directories in +the +.Ic path +variable to be recomputed. This is needed if new commands are added +to directories in the +.Ic path +while you are logged in. This should only be necessary if you add +commands to one of your own directories, or if a systems programmer +changes the contents of a system directory. +.Pp +.It Ic repeat Ar count command +The specified +.Ar command +which is subject to the same restrictions +as the +.Ar command +in the one line +.Ic if +statement above, +is executed +.Ar count +times. +I/O redirections occur exactly once, even if +.Ar count +is 0. +.Pp +.It Ic set +.It Ic set Ar name +.It Ic set Ar name Ns =word +.It Ic set Ar name[index] Ns =word +.It Ic set Ar name Ns =(wordlist) +The first form of the command shows the value of all shell variables. +Variables that have other than a single word as their +value print as a parenthesized word list. +The second form sets +.Ar name +to the null string. +The third form sets +.Ar name +to the single +.Ar word . +The fourth form sets +the +.Ar index Ns 'th +component of +.Ar name +to +.Ar word ; +this component must already exist. +The final form sets +.Ar name +to the list of words in +.Ar wordlist . +The value is always command and filename expanded. +.Pp +These arguments may be repeated to set multiple values in a single set command. +Note however, that variable expansion happens for all arguments before any +setting occurs. +.Pp +.It Ic setenv +.It Ic setenv Ar name +.It Ic setenv Ar name value +The first form lists all current environment variables. +It is equivalent to +.Xr printenv 1 . +The last form sets the value of environment variable +.Ar name +to be +.Ar value , +a single string. The second form sets +.Ar name +to an empty string. +The most commonly used environment variables +.Ev USER , +.Ev TERM , +and +.Ev PATH +are automatically imported to and exported from the +.Nm csh +variables +.Ar user , +.Ar term , +and +.Ar path ; +there is no need to use +.Ic setenv +for these. +.Pp +.It Ic shift +.It Ic shift Ar variable +The members of +.Ic argv +are shifted to the left, discarding +.Ic argv Ns Bq 1 . +It is an error for +.Ic argv +not to be set or to have less than one word as value. +The second form performs the same function on the specified variable. +.Pp +.It Ic source Ar name +.It Ic source Fl h Ar name +.pl -1 +The shell reads commands from +.Ar name . +.Ic Source +commands may be nested; if they are nested too deeply the shell may +run out of file descriptors. +An error in a +.Ic source +at any level terminates all nested +.Ic source +commands. +Normally input during +.Ic source +commands is not placed on the history list; +the \-h option causes the commands to be placed on the +history list without being executed. +.Pp +.It Ic stop +.It Ic stop % Ns Ar job ... +Stops the current or specified jobs that are executing in the background. +.Pp +.It Ic suspend +Causes the shell to stop in its tracks, much as if it had been sent a stop +signal with +.Ic ^Z . +This is most often used to stop shells started by +.Xr su 1 . +.Pp +.It Ic switch Ar (string) +.It Ic case Ar str1 : +.It \ \ \ \ \&... +.It Ic \ \ \ \ breaksw +.It \ \ \ \ \&... +.It Ic default : +.It \ \ \ \ \&... +.It Ic \ \ \ \ breaksw +.It Ic endsw +Each case label is successively matched against the specified +.Ar string +which is first command and filename expanded. +The file metacharacters `*', `?' and `[...]' +may be used in the case labels, +which are variable expanded. +If none of the labels match before the `default' label is found, then +the execution begins after the default label. +Each case label and the default label must appear at the beginning of a line. +The command +.Ic breaksw +causes execution to continue after the +.Ic endsw . +Otherwise control may fall through case labels and the default label as in C. +If no label matches and there is no default, execution continues after +the +.Ic endsw . +.Pp +.It Ic time +.It Ic time Ar command +With no argument, a summary of time used by this shell and its children +is printed. +If arguments are given +the specified simple command is timed and a time summary +as described under the +.Ic time +variable is printed. If necessary, an extra shell is created to print the time +statistic when the command completes. +.Pp +.It Ic umask +.It Ic umask Ar value +The file creation mask is displayed (first form) or set to the specified +value (second form). The mask is given in octal. Common values for +the mask are 002 giving all access to the group and read and execute +access to others or 022 giving all access except write access for +users in the group or others. +.Pp +.It Ic unalias Ar pattern +All aliases whose names match the specified pattern are discarded. +Thus all aliases are removed by `unalias *'. +It is not an error for nothing to be +.Ic unaliased . +.Pp +.It Ic unhash +Use of the internal hash table to speed location of executed programs +is disabled. +.Pp +.It Ic unlimit +.It Ic unlimit Ar resource +.It Ic unlimit Fl h +.It Ic unlimit Fl h Ar resource +Removes the limitation on +.Ar resource . +If no +.Ar resource +is specified, then all +.Ar resource +limitations are removed. If +.Fl h +is given, the corresponding hard limits are removed. Only the +super-user may do this. +.Pp +.It Ic unset Ar pattern +All variables whose names match the specified pattern are removed. +Thus all variables are removed by `unset *'; this has noticeably +distasteful side-effects. +It is not an error for nothing to be +.Ic unset . +.Pp +.It Ic unsetenv Ar pattern +Removes all variables whose name match the specified pattern from the +environment. See also the +.Ic setenv +command above and +.Xr printenv 1 . +.Pp +.It Ic wait +Wait for all background jobs. +If the shell is interactive, then an interrupt can disrupt the wait. +After the interrupt, the shell prints names and job numbers of all jobs +known to be outstanding. +.It Ic which Ar command +Displays the resolved command that will be executed by the shell. +.Pp +.It Ic while Ar (expr) +.It \&... +.It Ic end +While the specified expression evaluates non-zero, the commands between +the +.Ic while +and the matching +.Ic end +are evaluated. +.Ic Break +and +.Ic continue +may be used to terminate or continue the loop prematurely. +(The +.Ic while +and +.Ic end +must appear alone on their input lines.) +Prompting occurs here the first time through the loop as for the +.Ic foreach +statement if the input is a terminal. +.Pp +.It Ic % Ns Ar job +Brings the specified job into the foreground. +.Pp +.It Ic % Ns Ar job Ic & +Continues the specified job in the background. +.Pp +.It Ic @ +.It Ic @ Ns Ar name Ns = expr +.It Ic @ Ns Ar name[index] Ns = expr +The first form prints the values of all the shell variables. +The second form sets the specified +.Ar name +to the value of +.Ar expr . +If the expression contains `<', `>', `&' or `|' then at least +this part of the expression must be placed within `(' `)'. +The third form assigns the value of +.Ar expr +to the +.Ar index Ns 'th +argument of +.Ar name . +Both +.Ar name +and its +.Ar index Ns 'th +component must already exist. +.El +.Pp +The operators `*=', `+=', etc are available as in C. +The space separating the name from the assignment operator is optional. +Spaces are, however, mandatory in separating components of +.Ar expr +which would otherwise be single words. +.Pp +Special postfix `+\|+' and `\-\|\-' operators increment and decrement +.Ar name +respectively, i.e., `@ i++'. +.Ss Pre-defined and environment variables +The following variables have special meaning to the shell. +Of these, +.Ar argv , +.Ar cwd, +.Ar home , +.Ar path, +.Ar prompt , +.Ar shell +and +.Ar status +are always set by the shell. +Except for +.Ar cwd +and +.Ar status , +this setting occurs only at initialization; +these variables will not then be modified unless done +explicitly by the user. +.Pp +The shell copies the environment variable +.Ev USER +into the variable +.Ar user , +.Ev TERM +into +.Ar term , +and +.Ev HOME +into +.Ar home , +and copies these back into the environment whenever the normal +shell variables are reset. +The environment variable +.Ev PATH +is likewise handled; it is not +necessary to worry about its setting other than in the file +.Ar \&.cshrc +as inferior +.Nm csh +processes will import the definition of +.Ar path +from the environment, and re-export it if you then change it. +.ne 1i +.Bl -tag -width histchars +.It Ic argv +Set to the arguments to the shell, it is from this variable that +positional parameters are substituted, i.e., `$1' is replaced by +`$argv[1]', +etc. +.It Ic cdpath +Gives a list of alternate directories searched to find subdirectories +in +.Ar chdir +commands. +.It Ic cwd +The full pathname of the current directory. +.It Ic echo +Set when the +.Fl x +command line option is given. +Causes each command and its arguments +to be echoed just before it is executed. +For non-builtin commands all expansions occur before echoing. +Builtin commands are echoed before command and filename substitution, +since these substitutions are then done selectively. +.It Ic filec +Enable file name completion. +.It Ic histchars +Can be given a string value to change the characters used in history +substitution. The first character of its value is used as the +history substitution character, replacing the default character `!'. +The second character of its value replaces the character `\(ua' in +quick substitutions. +.It Ic histfile +Can be set to the pathname where history is going to be saved/restored. +.It Ic history +Can be given a numeric value to control the size of the history list. +Any command that has been referenced in this many events will not be +discarded. +Too large values of +.Ar history +may run the shell out of memory. +The last executed command is always saved on the history list. +.It Ic home +The home directory of the invoker, initialized from the environment. +The filename expansion of +.Sq Pa ~ +refers to this variable. +.It Ic ignoreeof +If set the shell ignores +end-of-file from input devices which are terminals. +This prevents shells from accidentally being killed by control-D's. +.It Ic mail +The files where the shell checks for mail. +This checking is done after each command completion that will +result in a prompt, +if a specified interval has elapsed. +The shell says `You have new mail.' +if the file exists with an access time not greater than its modify time. +.Pp +If the first word of the value of +.Ar mail +is numeric it specifies a different mail checking interval, in seconds, +than the default, which is 10 minutes. +.Pp +If multiple mail files are specified, then the shell says +`New mail in +.Ar name Ns ' +when there is mail in the file +.Ar name . +.It Ic noclobber +As described in the section on +.Sx input/output , +restrictions are placed on output redirection to insure that +files are not accidentally destroyed, and that `>>' redirections +refer to existing files. +.It Ic noglob +If set, filename expansion is inhibited. +This inhibition is most useful in shell scripts that + are not dealing with filenames, +or after a list of filenames has been obtained and further expansions +are not desirable. +.It Ic nonomatch +If set, it is not an error for a filename expansion to not match any +existing files; instead the primitive pattern is returned. +It is still an error for the primitive pattern to be malformed, i.e., +`echo [' +still gives an error. +.It Ic notify +If set, the shell notifies asynchronously of job completions; +the default is to present job completions just before printing +a prompt. +.It Ic path +Each word of the path variable specifies a directory in which +commands are to be sought for execution. +A null word specifies the current directory. +If there is no +.Ar path +variable then only full path names will execute. +The usual search path is `.', `/bin' and `/usr/bin', but this +may vary from system to system. +For the super-user the default search path is `/etc', `/bin' and `/usr/bin'. +A shell that is given neither the +.Fl c +nor the +.Fl t +option will normally hash the contents of the directories in the +.Ar path +variable after reading +.Ar \&.cshrc , +and each time the +.Ar path +variable is reset. If new commands are added to these directories +while the shell is active, it may be necessary to do a +.Ic rehash +or the commands may not be found. +.It Ic prompt +The string that is printed before each command is read from +an interactive terminal input. +If a `!' appears in the string it will be replaced by the current event number +unless a preceding `\e' is given. +Default is `% ', or `# ' for the super-user. +.It Ic savehist +Is given a numeric value to control the number of entries of the +history list that are saved in ~/.history when the user logs out. +Any command that has been referenced in this many events will be saved. +During start up the shell sources ~/.history into the history list +enabling history to be saved across logins. +Too large values of +.Ar savehist +will slow down the shell during start up. +If +.Ar savehist +is just set, the shell will use the value of +.Ar history. +.It Ic shell +The file in which the shell resides. +This variable is used in forking shells to interpret files that have execute +bits set, but which are not executable by the system. +(See the description of +.Sx Non-builtin Command Execution +below.) +Initialized to the (system-dependent) home of the shell. +.It Ic status +The status returned by the last command. +If it terminated abnormally, then 0200 is added to the status. +Builtin commands that fail return exit status `1', +all other builtin commands set status to `0'. +.It Ic time +Controls automatic timing of commands. +If set, then any command that takes more than this many cpu seconds +will cause a line giving user, system, and real times and a utilization +percentage which is the ratio of user plus system times to real time +to be printed when it terminates. +.It Ic verbose +Set by the +.Fl v +command line option, causes the words of each command to be printed +after history substitution. +.El +.Ss Non-builtin command execution +When a command to be executed is found to not be a builtin command +the shell attempts to execute the command via +.Xr execve 2 . +Each word in the variable +.Ar path +names a directory from which the shell will attempt to execute the command. +If it is given neither a +.Fl c +nor a +.Fl t +option, the shell will hash the names in these directories into an internal +table so that it will only try an +.Ic exec +in a directory if there is a possibility that the command resides there. +This shortcut greatly speeds command location when many directories +are present in the search path. +If this mechanism has been turned off (via +.Ic unhash ) , +or if the shell was given a +.Fl c +or +.Fl t +argument, and in any case for each directory component of +.Ar path +that does not begin with a `/', +the shell concatenates with the given command name to form a path name +of a file which it then attempts to execute. +.Pp +Parenthesized commands are always executed in a subshell. +Thus +.Pp +.Dl (cd ; pwd) ; pwd +.Pp +prints the +.Ar home +directory; leaving you where you were (printing this after the home directory), +while +.Pp +.Dl cd ; pwd +.Pp +leaves you in the +.Ar home +directory. +Parenthesized commands are most often used to prevent +.Ic chdir +from affecting the current shell. +.Pp +If the file has execute permissions but is not an +executable binary to the system, then it is assumed to be a +file containing shell commands and a new shell is spawned to read it. +.pl +1 +.Pp +If there is an +.Ic alias +for +.Ic shell +then the words of the alias will be prepended to the argument list to form +the shell command. +The first word of the +.Ic alias +should be the full path name of the shell +(e.g., `$shell'). +Note that this is a special, late occurring, case of +.Ic alias +substitution, +and only allows words to be prepended to the argument list without change. +.ne 1i +.Ss Signal handling +The shell normally ignores +.Ar quit +signals. +Jobs running detached (either by +.Ic \&& +or the +.Ic bg +or +.Ic %... & +commands) are immune to signals generated from the keyboard, including +hangups. +Other signals have the values which the shell inherited from its parent. +The shell's handling of interrupts and terminate signals +in shell scripts can be controlled by +.Ic onintr . +Login shells catch the +.Ar terminate +signal; otherwise this signal is passed on to children from the state in the +shell's parent. +Interrupts are not allowed when a login shell is reading the file +.Pa \&.logout . +.Sh AUTHOR +William Joy. +Job control and directory stack features first implemented by J.E. Kulp of +IIASA, Laxenburg, Austria, +with different syntax than that used now. +File name completion code written by Ken Greer, HP Labs. +Eight-bit implementation Christos S. Zoulas, Cornell University. +.Sh FILES +.Bl -tag -width /etc/passwd -compact +.It Pa ~/.cshrc +Read at beginning of execution by each shell. +.It Pa ~/.login +Read by login shell, after `.cshrc' at login. +.It Pa ~/.logout +Read by login shell, at logout. +.It Pa /bin/sh +Standard shell, for shell scripts not starting with a `#'. +.It Pa /tmp/sh* +Temporary file for `<<'. +.It Pa /etc/passwd +Source of home directories for `~name'. +.El +.Sh LIMITATIONS +Word lengths \- +Words can be no longer than 1024 characters. +The system limits argument lists to 10240 characters. +The number of arguments to a command that involves filename expansion +is limited to 1/6'th the number of characters allowed in an argument list. +Command substitutions may substitute no more characters than are +allowed in an argument list. +To detect looping, the shell restricts the number of +.Ic alias +substitutions on a single line to 20. +.Sh SEE ALSO +.Xr sh 1 , +.Xr access 2 , +.Xr execve 2 , +.Xr fork 2 , +.Xr killpg 2 , +.Xr pipe 2 , +.Xr sigvec 2 , +.Xr umask 2 , +.Xr setrlimit 2 , +.Xr wait 2 , +.Xr tty 4 , +.Xr a.out 5 , +.Xr environ 7 , +.br +.Em An introduction to the C shell +.Sh HISTORY +.Nm Csh +appeared in +.Bx 3 . +It +was a first implementation of a command language interpreter +incorporating a history mechanism (see +.Sx History Substitutions ) , +job control facilities (see +.Sx Jobs ) , +interactive file name +and user name completion (see +.Sx File Name Completion ) , +and a C-like syntax. +There are now many shells that also have these mechanisms, plus +a few more (and maybe some bugs too), which are available through the +usenet. +.Sh BUGS +When a command is restarted from a stop, +the shell prints the directory it started in if this is different +from the current directory; this can be misleading (i.e., wrong) +as the job may have changed directories internally. +.Pp +Shell builtin functions are not stoppable/restartable. +Command sequences of the form `a ; b ; c' are also not handled gracefully +when stopping is attempted. If you suspend `b', the shell will +immediately execute `c'. This is especially noticeable if this +expansion results from an +.Ar alias . +It suffices to place the sequence of commands in ()'s to force it to +a subshell, i.e., `( a ; b ; c )'. +.Pp +Control over tty output after processes are started is primitive; +perhaps this will inspire someone to work on a good virtual +terminal interface. In a virtual terminal interface much more +interesting things could be done with output control. +.Pp +Alias substitution is most often used to clumsily simulate shell procedures; +shell procedures should be provided instead of aliases. +.Pp +Commands within loops, prompted for by `?', are not placed on the +.Ic history +list. +Control structure should be parsed instead of being recognized as built-in +commands. This would allow control commands to be placed anywhere, +to be combined with `\&|', and to be used with `&' and `;' metasyntax. +.Pp +It should be possible to use the `:' modifiers on the output of command +substitutions. +.Pp +The way the +.Ic filec +facility is implemented is ugly and expensive. diff --git a/bin/csh/csh.c b/bin/csh/csh.c new file mode 100644 index 000000000000..510c68aa9177 --- /dev/null +++ b/bin/csh/csh.c @@ -0,0 +1,1368 @@ +/*- + * Copyright (c) 1980, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1980, 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)csh.c 8.4 (Berkeley) 4/29/95"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if __STDC__ +# include +#else +# include +#endif + +#include "csh.h" +#include "proc.h" +#include "extern.h" +#include "pathnames.h" + +extern bool MapsAreInited; +extern bool NLSMapsAreInited; + +/* + * C Shell + * + * Bill Joy, UC Berkeley, California, USA + * October 1978, May 1980 + * + * Jim Kulp, IIASA, Laxenburg, Austria + * April 1980 + * + * Christos Zoulas, Cornell University + * June, 1991 + */ + +Char *dumphist[] = {STRhistory, STRmh, 0, 0}; +Char *loadhist[] = {STRsource, STRmh, STRtildothist, 0}; + +int nofile = 0; +bool reenter = 0; +bool nverbose = 0; +bool nexececho = 0; +bool quitit = 0; +bool fast = 0; +bool batch = 0; +bool mflag = 0; +bool prompt = 1; +bool enterhist = 0; +bool tellwhat = 0; + +extern char **environ; + +static int readf __P((void *, char *, int)); +static fpos_t seekf __P((void *, fpos_t, int)); +static int writef __P((void *, const char *, int)); +static int closef __P((void *)); +static int srccat __P((Char *, Char *)); +static int srcfile __P((char *, bool, bool)); +static void phup __P((int)); +static void srcunit __P((int, bool, bool)); +static void mailchk __P((void)); +static Char **defaultpath __P((void)); + +int +main(argc, argv) + int argc; + char **argv; +{ + register Char *cp; + register char *tcp; + register int f; + register char **tempv; + struct sigaction oact; + sigset_t sigset; + + cshin = stdin; + cshout = stdout; + csherr = stderr; + + settimes(); /* Immed. estab. timing base */ + + /* + * Initialize non constant strings + */ +#ifdef _PATH_BSHELL + STR_BSHELL = SAVE(_PATH_BSHELL); +#endif +#ifdef _PATH_CSHELL + STR_SHELLPATH = SAVE(_PATH_CSHELL); +#endif + STR_environ = blk2short(environ); + environ = short2blk(STR_environ); /* So that we can free it */ + STR_WORD_CHARS = SAVE(WORD_CHARS); + + HIST = '!'; + HISTSUB = '^'; + word_chars = STR_WORD_CHARS; + + tempv = argv; + if (eq(str2short(tempv[0]), STRaout)) /* A.out's are quittable */ + quitit = 1; + uid = getuid(); + gid = getgid(); + euid = geteuid(); + egid = getegid(); + /* + * We are a login shell if: 1. we were invoked as - and we had + * no arguments 2. or we were invoked only with the -l flag + */ + loginsh = (**tempv == '-' && argc == 1) || + (argc == 2 && tempv[1][0] == '-' && tempv[1][1] == 'l' && + tempv[1][2] == '\0'); + + if (loginsh && **tempv != '-') { + /* + * Mangle the argv space + */ + tempv[1][0] = '\0'; + tempv[1][1] = '\0'; + tempv[1] = NULL; + for (tcp = *tempv; *tcp++;) + continue; + for (tcp--; tcp >= *tempv; tcp--) + tcp[1] = tcp[0]; + *++tcp = '-'; + argc--; + } + if (loginsh) + (void) time(&chktim); + + AsciiOnly = 1; +#ifdef NLS + (void) setlocale(LC_ALL, ""); + { + int k; + + for (k = 0200; k <= 0377 && !Isprint(k); k++) + continue; + AsciiOnly = k > 0377; + } +#else + AsciiOnly = getenv("LANG") == NULL && getenv("LC_CTYPE") == NULL; +#endif /* NLS */ + + /* + * Move the descriptors to safe places. The variable didfds is 0 while we + * have only FSH* to work with. When didfds is true, we have 0,1,2 and + * prefer to use these. + */ + initdesc(); + /* + * XXX: This is to keep programs that use stdio happy. + * what we really want is freunopen() .... + * Closing cshin cshout and csherr (which are really stdin stdout + * and stderr at this point and then reopening them in the same order + * gives us again stdin == cshin stdout == cshout and stderr == csherr. + * If that was not the case builtins like printf that use stdio + * would break. But in any case we could fix that with memcpy and + * a bit of pointer manipulation... + * Fortunately this is not needed under the current implementation + * of stdio. + */ + (void) fclose(cshin); + (void) fclose(cshout); + (void) fclose(csherr); + if (!(cshin = funopen((void *) &SHIN, readf, writef, seekf, closef))) + exit(1); + if (!(cshout = funopen((void *) &SHOUT, readf, writef, seekf, closef))) + exit(1); + if (!(csherr = funopen((void *) &SHERR, readf, writef, seekf, closef))) + exit(1); + (void) setvbuf(cshin, NULL, _IOLBF, 0); + (void) setvbuf(cshout, NULL, _IOLBF, 0); + (void) setvbuf(csherr, NULL, _IOLBF, 0); + + /* + * Initialize the shell variables. ARGV and PROMPT are initialized later. + * STATUS is also munged in several places. CHILD is munged when + * forking/waiting + */ + set(STRstatus, Strsave(STR0)); + + if ((tcp = getenv("HOME")) != NULL) + cp = SAVE(tcp); + else + cp = NULL; + + if (cp == NULL) + fast = 1; /* No home -> can't read scripts */ + else + set(STRhome, cp); + dinit(cp); /* dinit thinks that HOME == cwd in a login + * shell */ + /* + * Grab other useful things from the environment. Should we grab + * everything?? + */ + if ((tcp = getenv("LOGNAME")) != NULL || + (tcp = getenv("USER")) != NULL) + set(STRuser, SAVE(tcp)); + if ((tcp = getenv("TERM")) != NULL) + set(STRterm, SAVE(tcp)); + + /* + * Re-initialize path if set in environment + */ + if ((tcp = getenv("PATH")) == NULL) + set1(STRpath, defaultpath(), &shvhed); + else + importpath(SAVE(tcp)); + + set(STRshell, Strsave(STR_SHELLPATH)); + + doldol = putn((int) getpid()); /* For $$ */ + shtemp = Strspl(STRtmpsh, doldol); /* For << */ + + /* + * Record the interrupt states from the parent process. If the parent is + * non-interruptible our hand must be forced or we (and our children) won't + * be either. Our children inherit termination from our parent. We catch it + * only if we are the login shell. + */ + /* parents interruptibility */ + (void) sigaction(SIGINT, NULL, &oact); + parintr = oact.sa_handler; + (void) sigaction(SIGTERM, NULL, &oact); + parterm = oact.sa_handler; + + (void) signal(SIGHUP, phup); /* exit processing on HUP */ + (void) signal(SIGXCPU, phup); /* ...and on XCPU */ + (void) signal(SIGXFSZ, phup); /* ...and on XFSZ */ + + /* + * Process the arguments. + * + * Note that processing of -v/-x is actually delayed till after script + * processing. + * + * We set the first character of our name to be '-' if we are a shell + * running interruptible commands. Many programs which examine ps'es + * use this to filter such shells out. + */ + argc--, tempv++; + while (argc > 0 && (tcp = tempv[0])[0] == '-' && *++tcp != '\0' && !batch) { + do + switch (*tcp++) { + + case 0: /* - Interruptible, no prompt */ + prompt = 0; + setintr = 1; + nofile = 1; + break; + + case 'b': /* -b Next arg is input file */ + batch = 1; + break; + + case 'c': /* -c Command input from arg */ + if (argc == 1) + xexit(0); + argc--, tempv++; + arginp = SAVE(tempv[0]); + prompt = 0; + nofile = 1; + break; + + case 'e': /* -e Exit on any error */ + exiterr = 1; + break; + + case 'f': /* -f Fast start */ + fast = 1; + break; + + case 'i': /* -i Interactive, even if !intty */ + intact = 1; + nofile = 1; + break; + + case 'm': /* -m read .cshrc (from su) */ + mflag = 1; + break; + + case 'n': /* -n Don't execute */ + noexec = 1; + break; + + case 'q': /* -q (Undoc'd) ... die on quit */ + quitit = 1; + break; + + case 's': /* -s Read from std input */ + nofile = 1; + break; + + case 't': /* -t Read one line from input */ + onelflg = 2; + prompt = 0; + nofile = 1; + break; + + case 'v': /* -v Echo hist expanded input */ + nverbose = 1; /* ... later */ + break; + + case 'x': /* -x Echo just before execution */ + nexececho = 1; /* ... later */ + break; + + case 'V': /* -V Echo hist expanded input */ + setNS(STRverbose); /* NOW! */ + break; + + case 'X': /* -X Echo just before execution */ + setNS(STRecho); /* NOW! */ + break; + + } while (*tcp); + tempv++, argc--; + } + + if (quitit) /* With all due haste, for debugging */ + (void) signal(SIGQUIT, SIG_DFL); + + /* + * Unless prevented by -, -c, -i, -s, or -t, if there are remaining + * arguments the first of them is the name of a shell file from which to + * read commands. + */ + if (nofile == 0 && argc > 0) { + nofile = open(tempv[0], O_RDONLY); + if (nofile < 0) { + child = 1; /* So this doesn't return */ + stderror(ERR_SYSTEM, tempv[0], strerror(errno)); + } + ffile = SAVE(tempv[0]); + /* + * Replace FSHIN. Handle /dev/std{in,out,err} specially + * since once they are closed we cannot open them again. + * In that case we use our own saved descriptors + */ + if ((SHIN = dmove(nofile, FSHIN)) < 0) + switch(nofile) { + case 0: + SHIN = FSHIN; + break; + case 1: + SHIN = FSHOUT; + break; + case 2: + SHIN = FSHERR; + break; + default: + stderror(ERR_SYSTEM, tempv[0], strerror(errno)); + break; + } + (void) ioctl(SHIN, FIOCLEX, NULL); + prompt = 0; + /* argc not used any more */ tempv++; + } + + intty = isatty(SHIN); + intty |= intact; + if (intty || (intact && isatty(SHOUT))) { + if (!batch && (uid != euid || gid != egid)) { + errno = EACCES; + child = 1; /* So this doesn't return */ + stderror(ERR_SYSTEM, "csh", strerror(errno)); + } + } + /* + * Decide whether we should play with signals or not. If we are explicitly + * told (via -i, or -) or we are a login shell (arg0 starts with -) or the + * input and output are both the ttys("csh", or "csh/dev/ttyx") + * Note that in only the login shell is it likely that parent may have set + * signals to be ignored + */ + if (loginsh || intact || (intty && isatty(SHOUT))) + setintr = 1; + settell(); + /* + * Save the remaining arguments in argv. + */ + setq(STRargv, blk2short(tempv), &shvhed); + + /* + * Set up the prompt. + */ + if (prompt) { + set(STRprompt, Strsave(uid == 0 ? STRsymhash : STRsymcent)); + /* that's a meta-questionmark */ + set(STRprompt2, Strsave(STRmquestion)); + } + + /* + * If we are an interactive shell, then start fiddling with the signals; + * this is a tricky game. + */ + shpgrp = getpgrp(); + opgrp = tpgrp = -1; + if (setintr) { + **argv = '-'; + if (!quitit) /* Wary! */ + (void) signal(SIGQUIT, SIG_IGN); + (void) signal(SIGINT, pintr); + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigprocmask(SIG_BLOCK, &sigset, NULL); + (void) signal(SIGTERM, SIG_IGN); + if (quitit == 0 && arginp == 0) { + (void) signal(SIGTSTP, SIG_IGN); + (void) signal(SIGTTIN, SIG_IGN); + (void) signal(SIGTTOU, SIG_IGN); + /* + * Wait till in foreground, in case someone stupidly runs csh & + * dont want to try to grab away the tty. + */ + if (isatty(FSHERR)) + f = FSHERR; + else if (isatty(FSHOUT)) + f = FSHOUT; + else if (isatty(OLDSTD)) + f = OLDSTD; + else + f = -1; + retry: + if ((tpgrp = tcgetpgrp(f)) != -1) { + if (tpgrp != shpgrp) { + sig_t old = signal(SIGTTIN, SIG_DFL); + (void) kill(0, SIGTTIN); + (void) signal(SIGTTIN, old); + goto retry; + } + opgrp = shpgrp; + shpgrp = getpid(); + tpgrp = shpgrp; + /* + * Setpgid will fail if we are a session leader and + * mypid == mypgrp (POSIX 4.3.3) + */ + if (opgrp != shpgrp) + if (setpgid(0, shpgrp) == -1) + goto notty; + /* + * We do that after we set our process group, to make sure + * that the process group belongs to a process in the same + * session as the tty (our process and our group) (POSIX 7.2.4) + */ + if (tcsetpgrp(f, shpgrp) == -1) + goto notty; + (void) ioctl(dcopy(f, FSHTTY), FIOCLEX, NULL); + } + if (tpgrp == -1) { +notty: + (void) fprintf(csherr, "Warning: no access to tty (%s).\n", + strerror(errno)); + (void) fprintf(csherr, "Thus no job control in this shell.\n"); + } + } + } + if ((setintr == 0) && (parintr == SIG_DFL)) + setintr = 1; + (void) signal(SIGCHLD, pchild); /* while signals not ready */ + + /* + * Set an exit here in case of an interrupt or error reading the shell + * start-up scripts. + */ + reenter = setexit(); /* PWP */ + haderr = 0; /* In case second time through */ + if (!fast && reenter == 0) { + /* Will have value(STRhome) here because set fast if don't */ + { + int osetintr = setintr; + sig_t oparintr = parintr; + sigset_t osigset; + + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigprocmask(SIG_BLOCK, &sigset, &osigset); + + setintr = 0; + parintr = SIG_IGN; /* Disable onintr */ +#ifdef _PATH_DOTCSHRC + (void) srcfile(_PATH_DOTCSHRC, 0, 0); +#endif + if (!fast && !arginp && !onelflg) + dohash(NULL, NULL); +#ifdef _PATH_DOTLOGIN + if (loginsh) + (void) srcfile(_PATH_DOTLOGIN, 0, 0); +#endif + sigprocmask(SIG_SETMASK, &osigset, NULL); + setintr = osetintr; + parintr = oparintr; + } + (void) srccat(value(STRhome), STRsldotcshrc); + + if (!fast && !arginp && !onelflg && !havhash) + dohash(NULL, NULL); + /* + * Source history before .login so that it is available in .login + */ + if ((cp = value(STRhistfile)) != STRNULL) + loadhist[2] = cp; + dosource(loadhist, NULL); + if (loginsh) + (void) srccat(value(STRhome), STRsldotlogin); + } + + /* + * Now are ready for the -v and -x flags + */ + if (nverbose) + setNS(STRverbose); + if (nexececho) + setNS(STRecho); + + /* + * All the rest of the world is inside this call. The argument to process + * indicates whether it should catch "error unwinds". Thus if we are a + * interactive shell our call here will never return by being blown past on + * an error. + */ + process(setintr); + + /* + * Mop-up. + */ + if (intty) { + if (loginsh) { + (void) fprintf(cshout, "logout\n"); + (void) close(SHIN); + child = 1; + goodbye(); + } + else { + (void) fprintf(cshout, "exit\n"); + } + } + rechist(); + exitstat(); + return (0); +} + +void +untty() +{ + if (tpgrp > 0) { + (void) setpgid(0, opgrp); + (void) tcsetpgrp(FSHTTY, opgrp); + } +} + +void +importpath(cp) + Char *cp; +{ + register int i = 0; + register Char *dp; + register Char **pv; + int c; + + for (dp = cp; *dp; dp++) + if (*dp == ':') + i++; + /* + * i+2 where i is the number of colons in the path. There are i+1 + * directories in the path plus we need room for a zero terminator. + */ + pv = (Char **) xcalloc((size_t) (i + 2), sizeof(Char **)); + dp = cp; + i = 0; + if (*dp) + for (;;) { + if ((c = *dp) == ':' || c == 0) { + *dp = 0; + if ((*cp != '/' || *cp == '\0') && (euid == 0 || uid == 0)) + (void) fprintf(csherr, + "Warning: imported path contains relative components\n"); + pv[i++] = Strsave(*cp ? cp : STRdot); + if (c) { + cp = dp + 1; + *dp = ':'; + } + else + break; + } + dp++; + } + pv[i] = 0; + set1(STRpath, pv, &shvhed); +} + +/* + * Source to the file which is the catenation of the argument names. + */ +static int +srccat(cp, dp) + Char *cp, *dp; +{ + register Char *ep = Strspl(cp, dp); + char *ptr = short2str(ep); + + xfree((ptr_t) ep); + return srcfile(ptr, mflag ? 0 : 1, 0); +} + +/* + * Source to a file putting the file descriptor in a safe place (> 2). + */ +static int +srcfile(f, onlyown, flag) + char *f; + bool onlyown, flag; +{ + register int unit; + + if ((unit = open(f, O_RDONLY)) == -1) + return 0; + unit = dmove(unit, -1); + + (void) ioctl(unit, FIOCLEX, NULL); + srcunit(unit, onlyown, flag); + return 1; +} + +/* + * Source to a unit. If onlyown it must be our file or our group or + * we don't chance it. This occurs on ".cshrc"s and the like. + */ +int insource; +static void +srcunit(unit, onlyown, hflg) + register int unit; + bool onlyown, hflg; +{ + /* We have to push down a lot of state here */ + /* All this could go into a structure */ + int oSHIN = -1, oldintty = intty, oinsource = insource; + struct whyle *oldwhyl = whyles; + Char *ogointr = gointr, *oarginp = arginp; + Char *oevalp = evalp, **oevalvec = evalvec; + int oonelflg = onelflg; + bool oenterhist = enterhist; + char OHIST = HIST; + bool otell = cantell; + + struct Bin saveB; + sigset_t sigset, osigset; + jmp_buf oldexit; + + /* The (few) real local variables */ + int my_reenter; + + if (unit < 0) + return; + if (didfds) + donefds(); + if (onlyown) { + struct stat stb; + + if (fstat(unit, &stb) < 0) { + (void) close(unit); + return; + } + } + + /* + * There is a critical section here while we are pushing down the input + * stream since we have stuff in different structures. If we weren't + * careful an interrupt could corrupt SHIN's Bin structure and kill the + * shell. + * + * We could avoid the critical region by grouping all the stuff in a single + * structure and pointing at it to move it all at once. This is less + * efficient globally on many variable references however. + */ + insource = 1; + getexit(oldexit); + + if (setintr) { + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigprocmask(SIG_BLOCK, &sigset, &osigset); + } + /* Setup the new values of the state stuff saved above */ + memmove(&saveB, &B, sizeof(B)); + fbuf = NULL; + fseekp = feobp = fblocks = 0; + oSHIN = SHIN, SHIN = unit, arginp = 0, onelflg = 0; + intty = isatty(SHIN), whyles = 0, gointr = 0; + evalvec = 0; + evalp = 0; + enterhist = hflg; + if (enterhist) + HIST = '\0'; + + /* + * Now if we are allowing commands to be interrupted, we let ourselves be + * interrupted. + */ + if (setintr) + sigprocmask(SIG_SETMASK, &osigset, NULL); + settell(); + + if ((my_reenter = setexit()) == 0) + process(0); /* 0 -> blow away on errors */ + + if (setintr) + sigprocmask(SIG_SETMASK, &osigset, NULL); + if (oSHIN >= 0) { + register int i; + + /* We made it to the new state... free up its storage */ + /* This code could get run twice but xfree doesn't care */ + for (i = 0; i < fblocks; i++) + xfree((ptr_t) fbuf[i]); + xfree((ptr_t) fbuf); + + /* Reset input arena */ + memmove(&B, &saveB, sizeof(B)); + + (void) close(SHIN), SHIN = oSHIN; + arginp = oarginp, onelflg = oonelflg; + evalp = oevalp, evalvec = oevalvec; + intty = oldintty, whyles = oldwhyl, gointr = ogointr; + if (enterhist) + HIST = OHIST; + enterhist = oenterhist; + cantell = otell; + } + + resexit(oldexit); + /* + * If process reset() (effectively an unwind) then we must also unwind. + */ + if (my_reenter) + stderror(ERR_SILENT); + insource = oinsource; +} + +void +rechist() +{ + Char buf[BUFSIZ], hbuf[BUFSIZ], *hfile; + int fp, ftmp, oldidfds; + struct varent *shist; + + if (!fast) { + /* + * If $savehist is just set, we use the value of $history + * else we use the value in $savehist + */ + if ((shist = adrof(STRsavehist)) != NULL) { + if (shist->vec[0][0] != '\0') + (void) Strcpy(hbuf, shist->vec[0]); + else if ((shist = adrof(STRhistory)) && shist->vec[0][0] != '\0') + (void) Strcpy(hbuf, shist->vec[0]); + else + return; + } + else + return; + + if ((hfile = value(STRhistfile)) == STRNULL) { + hfile = Strcpy(buf, value(STRhome)); + (void) Strcat(buf, STRsldthist); + } + + if ((fp = open(short2str(hfile), O_WRONLY | O_CREAT | O_TRUNC, + 0600)) == -1) + return; + + oldidfds = didfds; + didfds = 0; + ftmp = SHOUT; + SHOUT = fp; + dumphist[2] = hbuf; + dohist(dumphist, NULL); + SHOUT = ftmp; + (void) close(fp); + didfds = oldidfds; + } +} + +void +goodbye() +{ + rechist(); + + if (loginsh) { + (void) signal(SIGQUIT, SIG_IGN); + (void) signal(SIGINT, SIG_IGN); + (void) signal(SIGTERM, SIG_IGN); + setintr = 0; /* No interrupts after "logout" */ + if (!(adrof(STRlogout))) + set(STRlogout, STRnormal); +#ifdef _PATH_DOTLOGOUT + (void) srcfile(_PATH_DOTLOGOUT, 0, 0); +#endif + if (adrof(STRhome)) + (void) srccat(value(STRhome), STRsldtlogout); + } + exitstat(); +} + +void +exitstat() +{ + Char *s; +#ifdef PROF + monitor(0); +#endif + /* + * Note that if STATUS is corrupted (i.e. getn bombs) then error will exit + * directly because we poke child here. Otherwise we might continue + * unwarrantedly (sic). + */ + child = 1; + s = value(STRstatus); + xexit(s ? getn(s) : 0); +} + +/* + * in the event of a HUP we want to save the history + */ +static void +phup(sig) +int sig; +{ + rechist(); + + /* + * We kill the last foreground process group. It then becomes + * responsible to propagate the SIGHUP to its progeny. + */ + { + struct process *pp, *np; + + for (pp = proclist.p_next; pp; pp = pp->p_next) { + np = pp; + /* + * Find if this job is in the foreground. It could be that + * the process leader has exited and the foreground flag + * is cleared for it. + */ + do + /* + * If a process is in the foreground; we try to kill + * it's process group. If we succeed, then the + * whole job is gone. Otherwise we keep going... + * But avoid sending HUP to the shell again. + */ + if ((np->p_flags & PFOREGND) != 0 && np->p_jobid != shpgrp && + killpg(np->p_jobid, SIGHUP) != -1) { + /* In case the job was suspended... */ + (void) killpg(np->p_jobid, SIGCONT); + break; + } + while ((np = np->p_friends) != pp); + } + } + xexit(sig); +} + +Char *jobargv[2] = {STRjobs, 0}; + +/* + * Catch an interrupt, e.g. during lexical input. + * If we are an interactive shell, we reset the interrupt catch + * immediately. In any case we drain the shell output, + * and finally go through the normal error mechanism, which + * gets a chance to make the shell go away. + */ +/* ARGSUSED */ +void +pintr(notused) + int notused; +{ + pintr1(1); +} + +void +pintr1(wantnl) + bool wantnl; +{ + Char **v; + sigset_t sigset, osigset; + + sigemptyset(&sigset); + sigprocmask(SIG_BLOCK, &sigset, &osigset); + if (setintr) { + sigset = osigset; + sigdelset(&sigset, SIGINT); + sigprocmask(SIG_SETMASK, &sigset, NULL); + if (pjobs) { + pjobs = 0; + (void) fprintf(cshout, "\n"); + dojobs(jobargv, NULL); + stderror(ERR_NAME | ERR_INTR); + } + } + sigdelset(&osigset, SIGCHLD); + sigprocmask(SIG_SETMASK, &osigset, NULL); + (void) fpurge(cshout); + (void) endpwent(); + + /* + * If we have an active "onintr" then we search for the label. Note that if + * one does "onintr -" then we shan't be interruptible so we needn't worry + * about that here. + */ + if (gointr) { + gotolab(gointr); + timflg = 0; + if ((v = pargv) != NULL) + pargv = 0, blkfree(v); + if ((v = gargv) != NULL) + gargv = 0, blkfree(v); + reset(); + } + else if (intty && wantnl) { + (void) fputc('\r', cshout); + (void) fputc('\n', cshout); + } + stderror(ERR_SILENT); +} + +/* + * Process is the main driving routine for the shell. + * It runs all command processing, except for those within { ... } + * in expressions (which is run by a routine evalav in sh.exp.c which + * is a stripped down process), and `...` evaluation which is run + * also by a subset of this code in sh.glob.c in the routine backeval. + * + * The code here is a little strange because part of it is interruptible + * and hence freeing of structures appears to occur when none is necessary + * if this is ignored. + * + * Note that if catch is not set then we will unwind on any error. + * If an end-of-file occurs, we return. + */ +static struct command *savet = NULL; +void +process(catch) + bool catch; +{ + jmp_buf osetexit; + struct command *t = savet; + sigset_t sigset; + + savet = NULL; + getexit(osetexit); + for (;;) { + pendjob(); + paraml.next = paraml.prev = ¶ml; + paraml.word = STRNULL; + (void) setexit(); + justpr = enterhist; /* execute if not entering history */ + + /* + * Interruptible during interactive reads + */ + if (setintr) { + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigprocmask(SIG_UNBLOCK, &sigset, NULL); + } + + /* + * For the sake of reset() + */ + freelex(¶ml); + if (savet) + freesyn(savet), savet = NULL; + + if (haderr) { + if (!catch) { + /* unwind */ + doneinp = 0; + resexit(osetexit); + savet = t; + reset(); + } + haderr = 0; + /* + * Every error is eventually caught here or the shell dies. It is + * at this point that we clean up any left-over open files, by + * closing all but a fixed number of pre-defined files. Thus + * routines don't have to worry about leaving files open due to + * deeper errors... they will get closed here. + */ + closem(); + continue; + } + if (doneinp) { + doneinp = 0; + break; + } + if (chkstop) + chkstop--; + if (neednote) + pnote(); + if (intty && prompt && evalvec == 0) { + mailchk(); + /* + * If we are at the end of the input buffer then we are going to + * read fresh stuff. Otherwise, we are rereading input and don't + * need or want to prompt. + */ + if (aret == F_SEEK && fseekp == feobp) + printprompt(); + (void) fflush(cshout); + } + if (seterr) { + xfree((ptr_t) seterr); + seterr = NULL; + } + + /* + * Echo not only on VERBOSE, but also with history expansion. If there + * is a lexical error then we forego history echo. + */ + if ((lex(¶ml) && !seterr && intty) || adrof(STRverbose)) { + prlex(csherr, ¶ml); + } + + /* + * The parser may lose space if interrupted. + */ + if (setintr) + sigprocmask(SIG_BLOCK, &sigset, NULL); + + /* + * Save input text on the history list if reading in old history, or it + * is from the terminal at the top level and not in a loop. + * + * PWP: entry of items in the history list while in a while loop is done + * elsewhere... + */ + if (enterhist || (catch && intty && !whyles)) + savehist(¶ml); + + /* + * Print lexical error messages, except when sourcing history lists. + */ + if (!enterhist && seterr) + stderror(ERR_OLD); + + /* + * If had a history command :p modifier then this is as far as we + * should go + */ + if (justpr) + reset(); + + alias(¶ml); + + /* + * Parse the words of the input into a parse tree. + */ + savet = syntax(paraml.next, ¶ml, 0); + if (seterr) + stderror(ERR_OLD); + + execute(savet, (tpgrp > 0 ? tpgrp : -1), NULL, NULL); + + /* + * Made it! + */ + freelex(¶ml); + freesyn((struct command *) savet), savet = NULL; + } + resexit(osetexit); + savet = t; +} + +void +/*ARGSUSED*/ +dosource(v, t) + Char **v; + struct command *t; + +{ + register Char *f; + bool hflg = 0; + Char buf[BUFSIZ]; + + v++; + if (*v && eq(*v, STRmh)) { + if (*++v == NULL) + stderror(ERR_NAME | ERR_HFLAG); + hflg++; + } + (void) Strcpy(buf, *v); + f = globone(buf, G_ERROR); + (void) strcpy((char *) buf, short2str(f)); + xfree((ptr_t) f); + if (!srcfile((char *) buf, 0, hflg) && !hflg) + stderror(ERR_SYSTEM, (char *) buf, strerror(errno)); +} + +/* + * Check for mail. + * If we are a login shell, then we don't want to tell + * about any mail file unless its been modified + * after the time we started. + * This prevents us from telling the user things he already + * knows, since the login program insists on saying + * "You have mail." + */ +static void +mailchk() +{ + register struct varent *v; + register Char **vp; + time_t t; + int intvl, cnt; + struct stat stb; + bool new; + + v = adrof(STRmail); + if (v == 0) + return; + (void) time(&t); + vp = v->vec; + cnt = blklen(vp); + intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL; + if (intvl < 1) + intvl = 1; + if (chktim + intvl > t) + return; + for (; *vp; vp++) { + if (stat(short2str(*vp), &stb) < 0) + continue; + new = stb.st_mtime > time0.tv_sec; + if (stb.st_size == 0 || stb.st_atime > stb.st_mtime || + (stb.st_atime < chktim && stb.st_mtime < chktim) || + (loginsh && !new)) + continue; + if (cnt == 1) + (void) fprintf(cshout, "You have %smail.\n", new ? "new " : ""); + else + (void) fprintf(cshout, "%s in %s.\n", new ? "New mail" : "Mail", + vis_str(*vp)); + } + chktim = t; +} + +/* + * Extract a home directory from the password file + * The argument points to a buffer where the name of the + * user whose home directory is sought is currently. + * We write the home directory of the user back there. + */ +int +gethdir(home) + Char *home; +{ + Char *h; + struct passwd *pw; + + /* + * Is it us? + */ + if (*home == '\0') { + if ((h = value(STRhome)) != NULL) { + (void) Strcpy(home, h); + return 0; + } + else + return 1; + } + + if ((pw = getpwnam(short2str(home))) != NULL) { + (void) Strcpy(home, str2short(pw->pw_dir)); + return 0; + } + else + return 1; +} + +/* + * When didfds is set, we do I/O from 0, 1, 2 otherwise from 15, 16, 17 + * We also check if the shell has already changed the decriptor to point to + * 0, 1, 2 when didfds is set. + */ +#define DESC(a) (*((int *) (a)) - (didfds && *((int *) a) >= FSHIN ? FSHIN : 0)) + +static int +readf(oreo, buf, siz) + void *oreo; + char *buf; + int siz; +{ + return read(DESC(oreo), buf, siz); +} + + +static int +writef(oreo, buf, siz) + void *oreo; + const char *buf; + int siz; +{ + return write(DESC(oreo), buf, siz); +} + +static fpos_t +seekf(oreo, off, whence) + void *oreo; + fpos_t off; + int whence; +{ + return lseek(DESC(oreo), off, whence); +} + + +static int +closef(oreo) + void *oreo; +{ + return close(DESC(oreo)); +} + + +/* + * Print the visible version of a string. + */ +int +vis_fputc(ch, fp) + int ch; + FILE *fp; +{ + char uenc[5]; /* 4 + NULL */ + + if (ch & QUOTE) + return fputc(ch & TRIM, fp); + /* + * XXX: When we are in AsciiOnly we want all characters >= 0200 to + * be encoded, but currently there is no way in vis to do that. + */ + (void) vis(uenc, ch & TRIM, VIS_NOSLASH, 0); + return fputs(uenc, fp); +} + +/* + * Move the initial descriptors to their eventual + * resting places, closin all other units. + */ +void +initdesc() +{ + + didfds = 0; /* 0, 1, 2 aren't set up */ + (void) ioctl(SHIN = dcopy(0, FSHIN), FIOCLEX, NULL); + (void) ioctl(SHOUT = dcopy(1, FSHOUT), FIOCLEX, NULL); + (void) ioctl(SHERR = dcopy(2, FSHERR), FIOCLEX, NULL); + (void) ioctl(OLDSTD = dcopy(SHIN, FOLDSTD), FIOCLEX, NULL); + closem(); +} + + +void +#ifdef PROF +done(i) +#else +xexit(i) +#endif + int i; +{ + untty(); + _exit(i); +} + +static Char ** +defaultpath() +{ + char *ptr; + Char **blk, **blkp; + struct stat stb; + + blkp = blk = (Char **) xmalloc((size_t) sizeof(Char *) * 10); + +#define DIRAPPEND(a) \ + if (stat(ptr = a, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR) \ + *blkp++ = SAVE(ptr) + + DIRAPPEND(_PATH_BIN); + DIRAPPEND(_PATH_USRBIN); + +#undef DIRAPPEND + + if (euid != 0 && uid != 0) + *blkp++ = Strsave(STRdot); + *blkp = NULL; + return (blk); +} + +void +printprompt() +{ + register Char *cp; + + if (!whyles) { + for (cp = value(STRprompt); *cp; cp++) + if (*cp == HIST) + (void) fprintf(cshout, "%d", eventno + 1); + else { + if (*cp == '\\' && cp[1] == HIST) + cp++; + (void) vis_fputc(*cp | QUOTE, cshout); + } + } + else + /* + * Prompt for forward reading loop body content. + */ + (void) fprintf(cshout, "? "); + (void) fflush(cshout); +} diff --git a/bin/csh/csh.h b/bin/csh/csh.h new file mode 100644 index 000000000000..db8ebf949f1c --- /dev/null +++ b/bin/csh/csh.h @@ -0,0 +1,552 @@ +/*- + * Copyright (c) 1980, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)csh.h 8.2 (Berkeley) 4/29/95 + */ + +/* + * Fundamental definitions which may vary from system to system. + * + * BUFSIZ The i/o buffering size; also limits word size + * MAILINTVL How often to mailcheck; more often is more expensive + */ +#ifndef BUFSIZ +#define BUFSIZ 1024 /* default buffer size */ +#endif /* BUFSIZ */ + +#define FORKSLEEP 10 /* delay loop on non-interactive fork failure */ +#define MAILINTVL 600 /* 10 minutes */ + +/* + * The shell moves std in/out/diag and the old std input away from units + * 0, 1, and 2 so that it is easy to set up these standards for invoked + * commands. + */ +#define FSHTTY 15 /* /dev/tty when manip pgrps */ +#define FSHIN 16 /* Preferred desc for shell input */ +#define FSHOUT 17 /* ... shell output */ +#define FSHERR 18 /* ... shell diagnostics */ +#define FOLDSTD 19 /* ... old std input */ + +#ifdef PROF +#define xexit(n) done(n) +#endif + +#ifdef SHORT_STRINGS +typedef short Char; + +#define SAVE(a) (Strsave(str2short(a))) +#else +typedef char Char; + +#define SAVE(a) (strsave(a)) +#endif + +/* + * Make sure a variable is not stored in a register by taking its address + * This is used where variables might be clobbered by longjmp. + */ +#define UNREGISTER(a) (void) &a + +typedef void *ioctl_t; /* Third arg of ioctl */ + +typedef void *ptr_t; + +#include "const.h" +#include "char.h" +#include "err.h" + +#define xmalloc(i) Malloc(i) +#define xrealloc(p, i) Realloc(p, i) +#define xcalloc(n, s) Calloc(n, s) +#define xfree(p) Free(p) + +#include +FILE *cshin, *cshout, *csherr; + +#define isdir(d) ((d.st_mode & S_IFMT) == S_IFDIR) + +typedef int bool; + +#define eq(a, b) (Strcmp(a, b) == 0) + +/* globone() flags */ +#define G_ERROR 0 /* default action: error if multiple words */ +#define G_IGNORE 1 /* ignore the rest of the words */ +#define G_APPEND 2 /* make a sentence by cat'ing the words */ + +/* + * Global flags + */ +bool chkstop; /* Warned of stopped jobs... allow exit */ +bool didfds; /* Have setup i/o fd's for child */ +bool doneinp; /* EOF indicator after reset from readc */ +bool exiterr; /* Exit if error or non-zero exit status */ +bool child; /* Child shell ... errors cause exit */ +bool haderr; /* Reset was because of an error */ +bool intty; /* Input is a tty */ +bool intact; /* We are interactive... therefore prompt */ +bool justpr; /* Just print because of :p hist mod */ +bool loginsh; /* We are a loginsh -> .login/.logout */ +bool neednote; /* Need to pnotify() */ +bool noexec; /* Don't execute, just syntax check */ +bool pjobs; /* want to print jobs if interrupted */ +bool setintr; /* Set interrupts on/off -> Wait intr... */ +bool timflg; /* Time the next waited for command */ +bool havhash; /* path hashing is available */ + +#ifdef FILEC +bool filec; /* doing filename expansion */ +#endif + +/* + * Global i/o info + */ +Char *arginp; /* Argument input for sh -c and internal `xx` */ +int onelflg; /* 2 -> need line for -t, 1 -> exit on read */ +Char *ffile; /* Name of shell file for $0 */ + +char *seterr; /* Error message from scanner/parser */ +Char *shtemp; /* Temp name for << shell files in /tmp */ + +#include +#include +#include + +struct timeval time0; /* Time at which the shell started */ +struct rusage ru0; + +/* + * Miscellany + */ +Char *doldol; /* Character pid for $$ */ +int backpid; /* Pid of the last background process */ +int uid, euid; /* Invokers uid */ +int gid, egid; /* Invokers gid */ +time_t chktim; /* Time mail last checked */ +int shpgrp; /* Pgrp of shell */ +int tpgrp; /* Terminal process group */ + +/* If tpgrp is -1, leave tty alone! */ +int opgrp; /* Initial pgrp and tty pgrp */ + + +/* + * To be able to redirect i/o for builtins easily, the shell moves the i/o + * descriptors it uses away from 0,1,2. + * Ideally these should be in units which are closed across exec's + * (this saves work) but for version 6, this is not usually possible. + * The desired initial values for these descriptors are F{SHIN,...}. + */ +int SHIN; /* Current shell input (script) */ +int SHOUT; /* Shell output */ +int SHERR; /* Diagnostic output... shell errs go here */ +int OLDSTD; /* Old standard input (def for cmds) */ + +/* + * Error control + * + * Errors in scanning and parsing set up an error message to be printed + * at the end and complete. Other errors always cause a reset. + * Because of source commands and .cshrc we need nested error catches. + */ + +#include +jmp_buf reslab; + +#define setexit() (setjmp(reslab)) +#define reset() longjmp(reslab, 1) + /* Should use structure assignment here */ +#define getexit(a) memmove((a), reslab, sizeof reslab) +#define resexit(a) memmove(reslab, (a), sizeof reslab) + +Char *gointr; /* Label for an onintr transfer */ + +#include +sig_t parintr; /* Parents interrupt catch */ +sig_t parterm; /* Parents terminate catch */ + +/* + * Lexical definitions. + * + * All lexical space is allocated dynamically. + * The eighth/sixteenth bit of characters is used to prevent recognition, + * and eventually stripped. + */ +#define META 0200 +#define ASCII 0177 +#ifdef SHORT_STRINGS +#define CHAR 0377 +#define QUOTE 0100000 /* 16nth char bit used for 'ing */ +#define TRIM 0077777 /* Mask to strip quote bit */ +#else +#define CHAR 0177 +#define QUOTE 0200 /* Eighth char bit used for 'ing */ +#define TRIM 0177 /* Mask to strip quote bit */ +#endif + +int AsciiOnly; /* If set only 7 bits is expected in characters */ + +/* + * Each level of input has a buffered input structure. + * There are one or more blocks of buffered input for each level, + * exactly one if the input is seekable and tell is available. + * In other cases, the shell buffers enough blocks to keep all loops + * in the buffer. + */ +struct Bin { + off_t Bfseekp; /* Seek pointer */ + off_t Bfbobp; /* Seekp of beginning of buffers */ + off_t Bfeobp; /* Seekp of end of buffers */ + int Bfblocks; /* Number of buffer blocks */ + Char **Bfbuf; /* The array of buffer blocks */ +} B; + +/* + * This structure allows us to seek inside aliases + */ +struct Ain { + int type; +#define I_SEEK -1 /* Invalid seek */ +#define A_SEEK 0 /* Alias seek */ +#define F_SEEK 1 /* File seek */ +#define E_SEEK 2 /* Eval seek */ + union { + off_t _f_seek; + Char* _c_seek; + } fc; +#define f_seek fc._f_seek +#define c_seek fc._c_seek + Char **a_seek; +} ; +extern int aret; /* What was the last character returned */ +#define SEEKEQ(a, b) ((a)->type == (b)->type && \ + (a)->f_seek == (b)->f_seek && \ + (a)->a_seek == (b)->a_seek) + +#define fseekp B.Bfseekp +#define fbobp B.Bfbobp +#define feobp B.Bfeobp +#define fblocks B.Bfblocks +#define fbuf B.Bfbuf + +/* + * The shell finds commands in loops by reseeking the input + * For whiles, in particular, it reseeks to the beginning of the + * line the while was on; hence the while placement restrictions. + */ +struct Ain lineloc; + +bool cantell; /* Is current source tellable ? */ + +/* + * Input lines are parsed into doubly linked circular + * lists of words of the following form. + */ +struct wordent { + Char *word; + struct wordent *prev; + struct wordent *next; +}; + +/* + * During word building, both in the initial lexical phase and + * when expanding $ variable substitutions, expansion by `!' and `$' + * must be inhibited when reading ahead in routines which are themselves + * processing `!' and `$' expansion or after characters such as `\' or in + * quotations. The following flags are passed to the getC routines + * telling them which of these substitutions are appropriate for the + * next character to be returned. + */ +#define DODOL 1 +#define DOEXCL 2 +#define DOALL DODOL|DOEXCL + +/* + * Labuf implements a general buffer for lookahead during lexical operations. + * Text which is to be placed in the input stream can be stuck here. + * We stick parsed ahead $ constructs during initial input, + * process id's from `$$', and modified variable values (from qualifiers + * during expansion in sh.dol.c) here. + */ +Char *lap; + +/* + * Parser structure + * + * Each command is parsed to a tree of command structures and + * flags are set bottom up during this process, to be propagated down + * as needed during the semantics/exeuction pass (sh.sem.c). + */ +struct command { + short t_dtyp; /* Type of node */ +#define NODE_COMMAND 1 /* t_dcom t_drit */ +#define NODE_PAREN 2 /* ( t_dspr ) t_drit */ +#define NODE_PIPE 3 /* t_dlef | t_drit */ +#define NODE_LIST 4 /* t_dlef ; t_drit */ +#define NODE_OR 5 /* t_dlef || t_drit */ +#define NODE_AND 6 /* t_dlef && t_drit */ + short t_dflg; /* Flags, e.g. F_AMPERSAND|... */ +#define F_SAVE (F_NICE|F_TIME|F_NOHUP) /* save these when re-doing */ + +#define F_AMPERSAND (1<<0) /* executes in background */ +#define F_APPEND (1<<1) /* output is redirected >> */ +#define F_PIPEIN (1<<2) /* input is a pipe */ +#define F_PIPEOUT (1<<3) /* output is a pipe */ +#define F_NOFORK (1<<4) /* don't fork, last ()ized cmd */ +#define F_NOINTERRUPT (1<<5) /* should be immune from intr's */ +/* spare */ +#define F_STDERR (1<<7) /* redirect unit 2 with unit 1 */ +#define F_OVERWRITE (1<<8) /* output was ! */ +#define F_READ (1<<9) /* input redirection is << */ +#define F_REPEAT (1<<10) /* reexec aft if, repeat,... */ +#define F_NICE (1<<11) /* t_nice is meaningful */ +#define F_NOHUP (1<<12) /* nohup this command */ +#define F_TIME (1<<13) /* time this command */ + union { + Char *T_dlef; /* Input redirect word */ + struct command *T_dcar; /* Left part of list/pipe */ + } L; + union { + Char *T_drit; /* Output redirect word */ + struct command *T_dcdr; /* Right part of list/pipe */ + } R; +#define t_dlef L.T_dlef +#define t_dcar L.T_dcar +#define t_drit R.T_drit +#define t_dcdr R.T_dcdr + Char **t_dcom; /* Command/argument vector */ + struct command *t_dspr; /* Pointer to ()'d subtree */ + int t_nice; +}; + + +/* + * These are declared here because they want to be + * initialized in sh.init.c (to allow them to be made readonly) + */ + +extern struct biltins { + char *bname; + void (*bfunct) __P((Char **, struct command *)); + short minargs, maxargs; +} bfunc[]; +extern int nbfunc; + +extern struct srch { + char *s_name; + short s_value; +} srchn[]; +extern int nsrchn; + +/* + * The keywords for the parser + */ +#define T_BREAK 0 +#define T_BRKSW 1 +#define T_CASE 2 +#define T_DEFAULT 3 +#define T_ELSE 4 +#define T_END 5 +#define T_ENDIF 6 +#define T_ENDSW 7 +#define T_EXIT 8 +#define T_FOREACH 9 +#define T_GOTO 10 +#define T_IF 11 +#define T_LABEL 12 +#define T_LET 13 +#define T_SET 14 +#define T_SWITCH 15 +#define T_TEST 16 +#define T_THEN 17 +#define T_WHILE 18 + +/* + * Structure defining the existing while/foreach loops at this + * source level. Loops are implemented by seeking back in the + * input. For foreach (fe), the word list is attached here. + */ +struct whyle { + struct Ain w_start; /* Point to restart loop */ + struct Ain w_end; /* End of loop (0 if unknown) */ + Char **w_fe, **w_fe0; /* Current/initial wordlist for fe */ + Char *w_fename; /* Name for fe */ + struct whyle *w_next; /* Next (more outer) loop */ +} *whyles; + +/* + * Variable structure + * + * Aliases and variables are stored in AVL balanced binary trees. + */ +struct varent { + Char **vec; /* Array of words which is the value */ + Char *v_name; /* Name of variable/alias */ + struct varent *v_link[3]; /* The links, see below */ + int v_bal; /* Balance factor */ +} shvhed, aliases; + +#define v_left v_link[0] +#define v_right v_link[1] +#define v_parent v_link[2] + +struct varent *adrof1(); + +#define adrof(v) adrof1(v, &shvhed) +#define value(v) value1(v, &shvhed) + +/* + * The following are for interfacing redo substitution in + * aliases to the lexical routines. + */ +struct wordent *alhistp; /* Argument list (first) */ +struct wordent *alhistt; /* Node after last in arg list */ +Char **alvec, *alvecp; /* The (remnants of) alias vector */ + +/* + * Filename/command name expansion variables + */ +int gflag; /* After tglob -> is globbing needed? */ + +#define MAXVARLEN 30 /* Maximum number of char in a variable name */ + +/* + * Variables for filename expansion + */ +extern Char **gargv; /* Pointer to the (stack) arglist */ +extern long gargc; /* Number args in gargv */ + +/* + * Variables for command expansion. + */ +extern Char **pargv; /* Pointer to the argv list space */ +extern long pargc; /* Count of arguments in pargv */ +Char *pargs; /* Pointer to start current word */ +long pnleft; /* Number of chars left in pargs */ +Char *pargcp; /* Current index into pargs */ + +/* + * History list + * + * Each history list entry contains an embedded wordlist + * from the scanner, a number for the event, and a reference count + * to aid in discarding old entries. + * + * Essentially "invisible" entries are put on the history list + * when history substitution includes modifiers, and thrown away + * at the next discarding since their event numbers are very negative. + */ +struct Hist { + struct wordent Hlex; + int Hnum; + int Href; + struct Hist *Hnext; +} Histlist; + +struct wordent paraml; /* Current lexical word list */ +int eventno; /* Next events number */ +int lastev; /* Last event reference (default) */ + +Char HIST; /* history invocation character */ +Char HISTSUB; /* auto-substitute character */ + +/* + * strings.h: + */ +#ifndef SHORT_STRINGS +#define Strchr(a, b) strchr(a, b) +#define Strrchr(a, b) strrchr(a, b) +#define Strcat(a, b) strcat(a, b) +#define Strncat(a, b, c) strncat(a, b, c) +#define Strcpy(a, b) strcpy(a, b) +#define Strncpy(a, b, c) strncpy(a, b, c) +#define Strlen(a) strlen(a) +#define Strcmp(a, b) strcmp(a, b) +#define Strncmp(a, b, c) strncmp(a, b, c) + +#define Strspl(a, b) strspl(a, b) +#define Strsave(a) strsave(a) +#define Strend(a) strend(a) +#define Strstr(a, b) strstr(a, b) + +#define str2short(a) (a) +#define blk2short(a) saveblk(a) +#define short2blk(a) saveblk(a) +#define short2str(a) strip(a) +#else +#define Strchr(a, b) s_strchr(a, b) +#define Strrchr(a, b) s_strrchr(a, b) +#define Strcat(a, b) s_strcat(a, b) +#define Strncat(a, b, c) s_strncat(a, b, c) +#define Strcpy(a, b) s_strcpy(a, b) +#define Strncpy(a, b, c) s_strncpy(a, b, c) +#define Strlen(a) s_strlen(a) +#define Strcmp(a, b) s_strcmp(a, b) +#define Strncmp(a, b, c) s_strncmp(a, b, c) + +#define Strspl(a, b) s_strspl(a, b) +#define Strsave(a) s_strsave(a) +#define Strend(a) s_strend(a) +#define Strstr(a, b) s_strstr(a, b) +#endif + +/* + * setname is a macro to save space (see sh.err.c) + */ +char *bname; + +#define setname(a) (bname = (a)) + +Char *Vsav; +Char *Vdp; +Char *Vexpath; +char **Vt; + +Char **evalvec; +Char *evalp; + +/* word_chars is set by default to WORD_CHARS but can be overridden by + the worchars variable--if unset, reverts to WORD_CHARS */ + +Char *word_chars; + +#define WORD_CHARS "*?_-.[]~=" /* default chars besides alnums in words */ + +Char *STR_SHELLPATH; + +#include +#ifdef _PATH_BSHELL +Char *STR_BSHELL; +#endif +Char *STR_WORD_CHARS; +Char **STR_environ; diff --git a/bin/csh/dir.c b/bin/csh/dir.c new file mode 100644 index 000000000000..6416f1ac692a --- /dev/null +++ b/bin/csh/dir.c @@ -0,0 +1,930 @@ +/*- + * Copyright (c) 1980, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)dir.c 8.2 (Berkeley) 4/29/95"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#if __STDC__ +# include +#else +# include +#endif + +#include "csh.h" +#include "dir.h" +#include "extern.h" + +/* Directory management. */ + +static struct directory + *dfind __P((Char *)); +static Char *dfollow __P((Char *)); +static void printdirs __P((void)); +static Char *dgoto __P((Char *)); +static void dnewcwd __P((struct directory *)); +static void dset __P((Char *)); + +struct directory dhead; /* "head" of loop */ +int printd; /* force name to be printed */ + +static int dirflag = 0; + +/* + * dinit - initialize current working directory + */ +void +dinit(hp) + Char *hp; +{ + register char *tcp; + register Char *cp; + register struct directory *dp; + char path[MAXPATHLEN]; + static char *emsg = "csh: Trying to start from \"%s\"\n"; + + /* Don't believe the login shell home, because it may be a symlink */ + tcp = getcwd(path, MAXPATHLEN); + if (tcp == NULL || *tcp == '\0') { + (void) fprintf(csherr, "csh: %s\n", strerror(errno)); + if (hp && *hp) { + tcp = short2str(hp); + if (chdir(tcp) == -1) + cp = NULL; + else + cp = hp; + (void) fprintf(csherr, emsg, vis_str(hp)); + } + else + cp = NULL; + if (cp == NULL) { + (void) fprintf(csherr, emsg, "/"); + if (chdir("/") == -1) + /* I am not even try to print an error message! */ + xexit(1); + cp = SAVE("/"); + } + } + else { + struct stat swd, shp; + + /* + * See if $HOME is the working directory we got and use that + */ + if (hp && *hp && + stat(tcp, &swd) != -1 && stat(short2str(hp), &shp) != -1 && + swd.st_dev == shp.st_dev && swd.st_ino == shp.st_ino) + cp = hp; + else { + char *cwd; + + /* + * use PWD if we have it (for subshells) + */ + if ((cwd = getenv("PWD")) != NULL) { + if (stat(cwd, &shp) != -1 && swd.st_dev == shp.st_dev && + swd.st_ino == shp.st_ino) + tcp = cwd; + } + cp = dcanon(SAVE(tcp), STRNULL); + } + } + + dp = (struct directory *) xcalloc(sizeof(struct directory), 1); + dp->di_name = Strsave(cp); + dp->di_count = 0; + dhead.di_next = dhead.di_prev = dp; + dp->di_next = dp->di_prev = &dhead; + printd = 0; + dnewcwd(dp); +} + +static void +dset(dp) +Char *dp; +{ + /* + * Don't call set() directly cause if the directory contains ` or + * other junk characters glob will fail. + */ + register Char **vec = (Char **) xmalloc((size_t) (2 * sizeof(Char **))); + + vec[0] = Strsave(dp); + vec[1] = 0; + setq(STRcwd, vec, &shvhed); + Setenv(STRPWD, dp); +} + +#define DIR_LONG 1 +#define DIR_VERT 2 +#define DIR_LINE 4 + +static void +skipargs(v, str) + Char ***v; + char *str; +{ + Char **n = *v, *s; + + dirflag = 0; + for (n++; *n != NULL && (*n)[0] == '-'; n++) + for (s = &((*n)[1]); *s; s++) + switch (*s) { + case 'l': + dirflag |= DIR_LONG; + break; + case 'v': + dirflag |= DIR_VERT; + break; + case 'n': + dirflag |= DIR_LINE; + break; + default: + stderror(ERR_DIRUS, vis_str(**v), str); + break; + } + *v = n; +} + +/* + * dodirs - list all directories in directory loop + */ +void +/*ARGSUSED*/ +dodirs(v, t) + Char **v; + struct command *t; +{ + skipargs(&v, ""); + + if (*v != NULL) + stderror(ERR_DIRUS, "dirs", ""); + printdirs(); +} + +static void +printdirs() +{ + register struct directory *dp; + Char *s, *hp = value(STRhome); + int idx, len, cur; + + if (*hp == '\0') + hp = NULL; + dp = dcwd; + idx = 0; + cur = 0; + do { + if (dp == &dhead) + continue; + if (dirflag & DIR_VERT) { + (void) fprintf(cshout, "%d\t", idx++); + cur = 0; + } + if (!(dirflag & DIR_LONG) && hp != NULL && !eq(hp, STRslash) && + (len = Strlen(hp), Strncmp(hp, dp->di_name, len) == 0) && + (dp->di_name[len] == '\0' || dp->di_name[len] == '/')) + len = Strlen(s = (dp->di_name + len)) + 2; + else + len = Strlen(s = dp->di_name) + 1; + + cur += len; + if ((dirflag & DIR_LINE) && cur >= 80 - 1 && len < 80) { + (void) fprintf(cshout, "\n"); + cur = len; + } + (void) fprintf(cshout, s != dp->di_name ? "~%s%c" : "%s%c", + vis_str(s), (dirflag & DIR_VERT) ? '\n' : ' '); + } while ((dp = dp->di_prev) != dcwd); + if (!(dirflag & DIR_VERT)) + (void) fprintf(cshout, "\n"); +} + +void +dtildepr(home, dir) + register Char *home, *dir; +{ + + if (!eq(home, STRslash) && prefix(home, dir)) + (void) fprintf(cshout, "~%s", vis_str(dir + Strlen(home))); + else + (void) fprintf(cshout, "%s", vis_str(dir)); +} + +void +dtilde() +{ + struct directory *d = dcwd; + + do { + if (d == &dhead) + continue; + d->di_name = dcanon(d->di_name, STRNULL); + } while ((d = d->di_prev) != dcwd); + + dset(dcwd->di_name); +} + + +/* dnormalize(): + * If the name starts with . or .. then we might need to normalize + * it depending on the symbolic link flags + */ +Char * +dnormalize(cp) + Char *cp; +{ + +#define UC (unsigned char) +#define ISDOT(c) (UC(c)[0] == '.' && ((UC(c)[1] == '\0') || (UC(c)[1] == '/'))) +#define ISDOTDOT(c) (UC(c)[0] == '.' && ISDOT(&((c)[1]))) + + if ((unsigned char) cp[0] == '/') + return (Strsave(cp)); + + if (adrof(STRignore_symlinks)) { + int dotdot = 0; + Char *dp, *cwd; + + cwd = (Char *) xmalloc((size_t) ((Strlen(dcwd->di_name) + 3) * + sizeof(Char))); + (void) Strcpy(cwd, dcwd->di_name); + + /* + * Ignore . and count ..'s + */ + while (*cp) { + if (ISDOT(cp)) { + if (*++cp) + cp++; + } + else if (ISDOTDOT(cp)) { + dotdot++; + cp += 2; + if (*cp) + cp++; + } + else + break; + } + while (dotdot > 0) + if ((dp = Strrchr(cwd, '/'))) { + *dp = '\0'; + dotdot--; + } + else + break; + + if (*cp) { + cwd[dotdot = Strlen(cwd)] = '/'; + cwd[dotdot + 1] = '\0'; + dp = Strspl(cwd, cp); + xfree((ptr_t) cwd); + return dp; + } + else { + if (!*cwd) { + cwd[0] = '/'; + cwd[1] = '\0'; + } + return cwd; + } + } + return Strsave(cp); +} + +/* + * dochngd - implement chdir command. + */ +void +/*ARGSUSED*/ +dochngd(v, t) + Char **v; + struct command *t; +{ + register Char *cp; + register struct directory *dp; + + skipargs(&v, " []"); + printd = 0; + if (*v == NULL) { + if ((cp = value(STRhome)) == NULL || *cp == 0) + stderror(ERR_NAME | ERR_NOHOMEDIR); + if (chdir(short2str(cp)) < 0) + stderror(ERR_NAME | ERR_CANTCHANGE); + cp = Strsave(cp); + } + else if (v[1] != NULL) { + stderror(ERR_NAME | ERR_TOOMANY); + /* NOTREACHED */ + return; + } + else if ((dp = dfind(*v)) != 0) { + char *tmp; + + printd = 1; + if (chdir(tmp = short2str(dp->di_name)) < 0) + stderror(ERR_SYSTEM, tmp, strerror(errno)); + dcwd->di_prev->di_next = dcwd->di_next; + dcwd->di_next->di_prev = dcwd->di_prev; + dfree(dcwd); + dnewcwd(dp); + return; + } + else + cp = dfollow(*v); + dp = (struct directory *) xcalloc(sizeof(struct directory), 1); + dp->di_name = cp; + dp->di_count = 0; + dp->di_next = dcwd->di_next; + dp->di_prev = dcwd->di_prev; + dp->di_prev->di_next = dp; + dp->di_next->di_prev = dp; + dfree(dcwd); + dnewcwd(dp); +} + +static Char * +dgoto(cp) + Char *cp; +{ + Char *dp; + + if (*cp != '/') { + register Char *p, *q; + int cwdlen; + + for (p = dcwd->di_name; *p++;) + continue; + if ((cwdlen = p - dcwd->di_name - 1) == 1) /* root */ + cwdlen = 0; + for (p = cp; *p++;) + continue; + dp = (Char *) xmalloc((size_t)((cwdlen + (p - cp) + 1) * sizeof(Char))); + for (p = dp, q = dcwd->di_name; (*p++ = *q++) != '\0';) + continue; + if (cwdlen) + p[-1] = '/'; + else + p--; /* don't add a / after root */ + for (q = cp; (*p++ = *q++) != '\0';) + continue; + xfree((ptr_t) cp); + cp = dp; + dp += cwdlen; + } + else + dp = cp; + + cp = dcanon(cp, dp); + return cp; +} + +/* + * dfollow - change to arg directory; fall back on cdpath if not valid + */ +static Char * +dfollow(cp) + register Char *cp; +{ + register Char *dp; + struct varent *c; + char ebuf[MAXPATHLEN]; + int serrno; + + cp = globone(cp, G_ERROR); + /* + * if we are ignoring symlinks, try to fix relatives now. + */ + dp = dnormalize(cp); + if (chdir(short2str(dp)) >= 0) { + xfree((ptr_t) cp); + return dgoto(dp); + } + else { + xfree((ptr_t) dp); + if (chdir(short2str(cp)) >= 0) + return dgoto(cp); + serrno = errno; + } + + if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp) + && (c = adrof(STRcdpath))) { + Char **cdp; + register Char *p; + Char buf[MAXPATHLEN]; + + for (cdp = c->vec; *cdp; cdp++) { + for (dp = buf, p = *cdp; (*dp++ = *p++) != '\0';) + continue; + dp[-1] = '/'; + for (p = cp; (*dp++ = *p++) != '\0';) + continue; + if (chdir(short2str(buf)) >= 0) { + printd = 1; + xfree((ptr_t) cp); + cp = Strsave(buf); + return dgoto(cp); + } + } + } + dp = value(cp); + if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) { + xfree((ptr_t) cp); + cp = Strsave(dp); + printd = 1; + return dgoto(cp); + } + (void) strcpy(ebuf, short2str(cp)); + xfree((ptr_t) cp); + stderror(ERR_SYSTEM, ebuf, strerror(serrno)); + return (NULL); +} + + +/* + * dopushd - push new directory onto directory stack. + * with no arguments exchange top and second. + * with numeric argument (+n) bring it to top. + */ +void +/*ARGSUSED*/ +dopushd(v, t) + Char **v; + struct command *t; +{ + register struct directory *dp; + + skipargs(&v, " [|+]"); + printd = 1; + if (*v == NULL) { + char *tmp; + + if ((dp = dcwd->di_prev) == &dhead) + dp = dhead.di_prev; + if (dp == dcwd) + stderror(ERR_NAME | ERR_NODIR); + if (chdir(tmp = short2str(dp->di_name)) < 0) + stderror(ERR_SYSTEM, tmp, strerror(errno)); + dp->di_prev->di_next = dp->di_next; + dp->di_next->di_prev = dp->di_prev; + dp->di_next = dcwd->di_next; + dp->di_prev = dcwd; + dcwd->di_next->di_prev = dp; + dcwd->di_next = dp; + } + else if (v[1] != NULL) { + stderror(ERR_NAME | ERR_TOOMANY); + /* NOTREACHED */ + return; + } + else if ((dp = dfind(*v)) != NULL) { + char *tmp; + + if (chdir(tmp = short2str(dp->di_name)) < 0) + stderror(ERR_SYSTEM, tmp, strerror(errno)); + } + else { + register Char *ccp; + + ccp = dfollow(*v); + dp = (struct directory *) xcalloc(sizeof(struct directory), 1); + dp->di_name = ccp; + dp->di_count = 0; + dp->di_prev = dcwd; + dp->di_next = dcwd->di_next; + dcwd->di_next = dp; + dp->di_next->di_prev = dp; + } + dnewcwd(dp); +} + +/* + * dfind - find a directory if specified by numeric (+n) argument + */ +static struct directory * +dfind(cp) + register Char *cp; +{ + register struct directory *dp; + register int i; + register Char *ep; + + if (*cp++ != '+') + return (0); + for (ep = cp; Isdigit(*ep); ep++) + continue; + if (*ep) + return (0); + i = getn(cp); + if (i <= 0) + return (0); + for (dp = dcwd; i != 0; i--) { + if ((dp = dp->di_prev) == &dhead) + dp = dp->di_prev; + if (dp == dcwd) + stderror(ERR_NAME | ERR_DEEP); + } + return (dp); +} + +/* + * dopopd - pop a directory out of the directory stack + * with a numeric argument just discard it. + */ +void +/*ARGSUSED*/ +dopopd(v, t) + Char **v; + struct command *t; +{ + register struct directory *dp, *p = NULL; + + skipargs(&v, " [+]"); + printd = 1; + if (*v == NULL) + dp = dcwd; + else if (v[1] != NULL) { + stderror(ERR_NAME | ERR_TOOMANY); + /* NOTREACHED */ + return; + } + else if ((dp = dfind(*v)) == 0) + stderror(ERR_NAME | ERR_BADDIR); + if (dp->di_prev == &dhead && dp->di_next == &dhead) + stderror(ERR_NAME | ERR_EMPTY); + if (dp == dcwd) { + char *tmp; + + if ((p = dp->di_prev) == &dhead) + p = dhead.di_prev; + if (chdir(tmp = short2str(p->di_name)) < 0) + stderror(ERR_SYSTEM, tmp, strerror(errno)); + } + dp->di_prev->di_next = dp->di_next; + dp->di_next->di_prev = dp->di_prev; + if (dp == dcwd) + dnewcwd(p); + else { + printdirs(); + } + dfree(dp); +} + +/* + * dfree - free the directory (or keep it if it still has ref count) + */ +void +dfree(dp) + register struct directory *dp; +{ + + if (dp->di_count != 0) { + dp->di_next = dp->di_prev = 0; + } + else { + xfree((char *) dp->di_name); + xfree((ptr_t) dp); + } +} + +/* + * dcanon - canonicalize the pathname, removing excess ./ and ../ etc. + * we are of course assuming that the file system is standardly + * constructed (always have ..'s, directories have links) + */ +Char * +dcanon(cp, p) + register Char *cp, *p; +{ + register Char *sp; + register Char *p1, *p2; /* general purpose */ + bool slash; + + Char link[MAXPATHLEN]; + char tlink[MAXPATHLEN]; + int cc; + Char *newcp; + + /* + * christos: if the path given does not start with a slash prepend cwd. If + * cwd does not start with a path or the result would be too long abort(). + */ + if (*cp != '/') { + Char tmpdir[MAXPATHLEN]; + + p1 = value(STRcwd); + if (p1 == NULL || *p1 != '/') + abort(); + if (Strlen(p1) + Strlen(cp) + 1 >= MAXPATHLEN) + abort(); + (void) Strcpy(tmpdir, p1); + (void) Strcat(tmpdir, STRslash); + (void) Strcat(tmpdir, cp); + xfree((ptr_t) cp); + cp = p = Strsave(tmpdir); + } + + while (*p) { /* for each component */ + sp = p; /* save slash address */ + while (*++p == '/') /* flush extra slashes */ + continue; + if (p != ++sp) + for (p1 = sp, p2 = p; (*p1++ = *p2++) != '\0';) + continue; + p = sp; /* save start of component */ + slash = 0; + while (*++p) /* find next slash or end of path */ + if (*p == '/') { + slash = 1; + *p = 0; + break; + } + + if (*sp == '\0') /* if component is null */ + if (--sp == cp) /* if path is one char (i.e. /) */ + break; + else + *sp = '\0'; + else if (sp[0] == '.' && sp[1] == 0) { + if (slash) { + for (p1 = sp, p2 = p + 1; (*p1++ = *p2++) != '\0';) + continue; + p = --sp; + } + else if (--sp != cp) + *sp = '\0'; + } + else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) { + /* + * We have something like "yyy/xxx/..", where "yyy" can be null or + * a path starting at /, and "xxx" is a single component. Before + * compressing "xxx/..", we want to expand "yyy/xxx", if it is a + * symbolic link. + */ + *--sp = 0; /* form the pathname for readlink */ + if (sp != cp && !adrof(STRignore_symlinks) && + (cc = readlink(short2str(cp), tlink, + sizeof tlink)) >= 0) { + (void) Strcpy(link, str2short(tlink)); + link[cc] = '\0'; + + if (slash) + *p = '/'; + /* + * Point p to the '/' in "/..", and restore the '/'. + */ + *(p = sp) = '/'; + /* + * find length of p + */ + for (p1 = p; *p1++;) + continue; + if (*link != '/') { + /* + * Relative path, expand it between the "yyy/" and the + * "/..". First, back sp up to the character past "yyy/". + */ + while (*--sp != '/') + continue; + sp++; + *sp = 0; + /* + * New length is "yyy/" + link + "/.." and rest + */ + p1 = newcp = (Char *) xmalloc((size_t) + (((sp - cp) + cc + (p1 - p)) * + sizeof(Char))); + /* + * Copy new path into newcp + */ + for (p2 = cp; (*p1++ = *p2++) != '\0';) + continue; + for (p1--, p2 = link; (*p1++ = *p2++) != '\0';) + continue; + for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) + continue; + /* + * Restart canonicalization at expanded "/xxx". + */ + p = sp - cp - 1 + newcp; + } + else { + /* + * New length is link + "/.." and rest + */ + p1 = newcp = (Char *) xmalloc((size_t) + ((cc + (p1 - p)) * sizeof(Char))); + /* + * Copy new path into newcp + */ + for (p2 = link; (*p1++ = *p2++) != '\0';) + continue; + for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) + continue; + /* + * Restart canonicalization at beginning + */ + p = newcp; + } + xfree((ptr_t) cp); + cp = newcp; + continue; /* canonicalize the link */ + } + *sp = '/'; + if (sp != cp) + while (*--sp != '/') + continue; + if (slash) { + for (p1 = sp + 1, p2 = p + 1; (*p1++ = *p2++) != '\0';) + continue; + p = sp; + } + else if (cp == sp) + *++sp = '\0'; + else + *sp = '\0'; + } + else { /* normal dir name (not . or .. or nothing) */ + + if (sp != cp && adrof(STRchase_symlinks) && + !adrof(STRignore_symlinks) && + (cc = readlink(short2str(cp), tlink, + sizeof tlink)) >= 0) { + (void) Strcpy(link, str2short(tlink)); + link[cc] = '\0'; + + /* + * restore the '/'. + */ + if (slash) + *p = '/'; + + /* + * point sp to p (rather than backing up). + */ + sp = p; + + /* + * find length of p + */ + for (p1 = p; *p1++;) + continue; + if (*link != '/') { + /* + * Relative path, expand it between the "yyy/" and the + * remainder. First, back sp up to the character past + * "yyy/". + */ + while (*--sp != '/') + continue; + sp++; + *sp = 0; + /* + * New length is "yyy/" + link + "/.." and rest + */ + p1 = newcp = (Char *) xmalloc((size_t) + (((sp - cp) + cc + (p1 - p)) + * sizeof(Char))); + /* + * Copy new path into newcp + */ + for (p2 = cp; (*p1++ = *p2++) != '\0';) + continue; + for (p1--, p2 = link; (*p1++ = *p2++) != '\0';) + continue; + for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) + continue; + /* + * Restart canonicalization at expanded "/xxx". + */ + p = sp - cp - 1 + newcp; + } + else { + /* + * New length is link + the rest + */ + p1 = newcp = (Char *) xmalloc((size_t) + ((cc + (p1 - p)) * sizeof(Char))); + /* + * Copy new path into newcp + */ + for (p2 = link; (*p1++ = *p2++) != '\0';) + continue; + for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) + continue; + /* + * Restart canonicalization at beginning + */ + p = newcp; + } + xfree((ptr_t) cp); + cp = newcp; + continue; /* canonicalize the link */ + } + if (slash) + *p = '/'; + } + } + + /* + * fix home... + */ + p1 = value(STRhome); + cc = Strlen(p1); + /* + * See if we're not in a subdir of STRhome + */ + if (p1 && *p1 == '/' && + (Strncmp(p1, cp, cc) != 0 || (cp[cc] != '/' && cp[cc] != '\0'))) { + static ino_t home_ino = -1; + static dev_t home_dev = -1; + static Char *home_ptr = NULL; + struct stat statbuf; + + /* + * Get dev and ino of STRhome + */ + if (home_ptr != p1 && + stat(short2str(p1), &statbuf) != -1) { + home_dev = statbuf.st_dev; + home_ino = statbuf.st_ino; + home_ptr = p1; + } + /* + * Start comparing dev & ino backwards + */ + p2 = Strcpy(link, cp); + for (sp = NULL; *p2 && stat(short2str(p2), &statbuf) != -1;) { + if (statbuf.st_dev == home_dev && + statbuf.st_ino == home_ino) { + sp = (Char *) - 1; + break; + } + if ((sp = Strrchr(p2, '/')) != NULL) + *sp = '\0'; + } + /* + * See if we found it + */ + if (*p2 && sp == (Char *) -1) { + /* + * Use STRhome to make '~' work + */ + newcp = Strspl(p1, cp + Strlen(p2)); + xfree((ptr_t) cp); + cp = newcp; + } + } + return cp; +} + + +/* + * dnewcwd - make a new directory in the loop the current one + */ +static void +dnewcwd(dp) + register struct directory *dp; +{ + dcwd = dp; + dset(dcwd->di_name); + if (printd && !(adrof(STRpushdsilent))) + printdirs(); +} diff --git a/bin/csh/dol.c b/bin/csh/dol.c new file mode 100644 index 000000000000..435eb6b114e9 --- /dev/null +++ b/bin/csh/dol.c @@ -0,0 +1,978 @@ +/*- + * Copyright (c) 1980, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)dol.c 8.2 (Berkeley) 4/29/95"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#if __STDC__ +# include +#else +# include +#endif + +#include "csh.h" +#include "extern.h" + +/* + * These routines perform variable substitution and quoting via ' and ". + * To this point these constructs have been preserved in the divided + * input words. Here we expand variables and turn quoting via ' and " into + * QUOTE bits on characters (which prevent further interpretation). + * If the `:q' modifier was applied during history expansion, then + * some QUOTEing may have occurred already, so we dont "trim()" here. + */ + +static int Dpeekc, Dpeekrd; /* Peeks for DgetC and Dreadc */ +static Char *Dcp, **Dvp; /* Input vector for Dreadc */ + +#define DEOF -1 + +#define unDgetC(c) Dpeekc = c + +#define QUOTES (_QF|_QB|_ESC) /* \ ' " ` */ + +/* + * The following variables give the information about the current + * $ expansion, recording the current word position, the remaining + * words within this expansion, the count of remaining words, and the + * information about any : modifier which is being applied. + */ +#define MAXWLEN (BUFSIZ - 4) +#define MAXMOD MAXWLEN /* This cannot overflow */ +static Char *dolp; /* Remaining chars from this word */ +static Char **dolnxt; /* Further words */ +static int dolcnt; /* Count of further words */ +static Char dolmod[MAXMOD]; /* : modifier character */ +static int dolnmod; /* Number of modifiers */ +static int dolmcnt; /* :gx -> 10000, else 1 */ +static int dolwcnt; /* :wx -> 10000, else 1 */ + +static void Dfix2 __P((Char **)); +static Char *Dpack __P((Char *, Char *)); +static int Dword __P((void)); +static void dolerror __P((Char *)); +static int DgetC __P((int)); +static void Dgetdol __P((void)); +static void fixDolMod __P((void)); +static void setDolp __P((Char *)); +static void unDredc __P((int)); +static int Dredc __P((void)); +static void Dtestq __P((int)); + + +/* + * Fix up the $ expansions and quotations in the + * argument list to command t. + */ +void +Dfix(t) + register struct command *t; +{ + register Char **pp; + register Char *p; + + if (noexec) + return; + /* Note that t_dcom isn't trimmed thus !...:q's aren't lost */ + for (pp = t->t_dcom; (p = *pp++) != NULL;) + for (; *p; p++) { + if (cmap(*p, _DOL | QUOTES)) { /* $, \, ', ", ` */ + Dfix2(t->t_dcom); /* found one */ + blkfree(t->t_dcom); + t->t_dcom = gargv; + gargv = 0; + return; + } + } +} + +/* + * $ substitute one word, for i/o redirection + */ +Char * +Dfix1(cp) + register Char *cp; +{ + Char *Dv[2]; + + if (noexec) + return (0); + Dv[0] = cp; + Dv[1] = NULL; + Dfix2(Dv); + if (gargc != 1) { + setname(vis_str(cp)); + stderror(ERR_NAME | ERR_AMBIG); + } + cp = Strsave(gargv[0]); + blkfree(gargv), gargv = 0; + return (cp); +} + +/* + * Subroutine to do actual fixing after state initialization. + */ +static void +Dfix2(v) + Char **v; +{ + ginit(); /* Initialize glob's area pointers */ + Dvp = v; + Dcp = STRNULL; /* Setup input vector for Dreadc */ + unDgetC(0); + unDredc(0); /* Clear out any old peeks (at error) */ + dolp = 0; + dolcnt = 0; /* Clear out residual $ expands (...) */ + while (Dword()) + continue; +} + +/* + * Pack up more characters in this word + */ +static Char * +Dpack(wbuf, wp) + Char *wbuf, *wp; +{ + register int c; + register int i = MAXWLEN - (wp - wbuf); + + for (;;) { + c = DgetC(DODOL); + if (c == '\\') { + c = DgetC(0); + if (c == DEOF) { + unDredc(c); + *wp = 0; + Gcat(STRNULL, wbuf); + return (NULL); + } + if (c == '\n') + c = ' '; + else + c |= QUOTE; + } + if (c == DEOF) { + unDredc(c); + *wp = 0; + Gcat(STRNULL, wbuf); + return (NULL); + } + if (cmap(c, _SP | _NL | _QF | _QB)) { /* sp \t\n'"` */ + unDgetC(c); + if (cmap(c, QUOTES)) + return (wp); + *wp++ = 0; + Gcat(STRNULL, wbuf); + return (NULL); + } + if (--i <= 0) + stderror(ERR_WTOOLONG); + *wp++ = c; + } +} + +/* + * Get a word. This routine is analogous to the routine + * word() in sh.lex.c for the main lexical input. One difference + * here is that we don't get a newline to terminate our expansion. + * Rather, DgetC will return a DEOF when we hit the end-of-input. + */ +static int +Dword() +{ + register int c, c1; + Char wbuf[BUFSIZ]; + register Char *wp = wbuf; + register int i = MAXWLEN; + register bool dolflg; + bool sofar = 0, done = 0; + + while (!done) { + done = 1; + c = DgetC(DODOL); + switch (c) { + + case DEOF: + if (sofar == 0) + return (0); + /* finish this word and catch the code above the next time */ + unDredc(c); + /* fall into ... */ + + case '\n': + *wp = 0; + Gcat(STRNULL, wbuf); + return (1); + + case ' ': + case '\t': + done = 0; + break; + + case '`': + /* We preserve ` quotations which are done yet later */ + *wp++ = c, --i; + case '\'': + case '"': + /* + * Note that DgetC never returns a QUOTES character from an + * expansion, so only true input quotes will get us here or out. + */ + c1 = c; + dolflg = c1 == '"' ? DODOL : 0; + for (;;) { + c = DgetC(dolflg); + if (c == c1) + break; + if (c == '\n' || c == DEOF) + stderror(ERR_UNMATCHED, c1); + if ((c & (QUOTE | TRIM)) == ('\n' | QUOTE)) + --wp, ++i; + if (--i <= 0) + stderror(ERR_WTOOLONG); + switch (c1) { + + case '"': + /* + * Leave any `s alone for later. Other chars are all + * quoted, thus `...` can tell it was within "...". + */ + *wp++ = c == '`' ? '`' : c | QUOTE; + break; + + case '\'': + /* Prevent all further interpretation */ + *wp++ = c | QUOTE; + break; + + case '`': + /* Leave all text alone for later */ + *wp++ = c; + break; + + default: + break; + } + } + if (c1 == '`') + *wp++ = '`' /* i--; eliminated */; + sofar = 1; + if ((wp = Dpack(wbuf, wp)) == NULL) + return (1); + else { + i = MAXWLEN - (wp - wbuf); + done = 0; + } + break; + + case '\\': + c = DgetC(0); /* No $ subst! */ + if (c == '\n' || c == DEOF) { + done = 0; + break; + } + c |= QUOTE; + break; + + default: + break; + } + if (done) { + unDgetC(c); + sofar = 1; + if ((wp = Dpack(wbuf, wp)) == NULL) + return (1); + else { + i = MAXWLEN - (wp - wbuf); + done = 0; + } + } + } + /* Really NOTREACHED */ + return (0); +} + + +/* + * Get a character, performing $ substitution unless flag is 0. + * Any QUOTES character which is returned from a $ expansion is + * QUOTEd so that it will not be recognized above. + */ +static int +DgetC(flag) + register int flag; +{ + register int c; + +top: + if ((c = Dpeekc) != '\0') { + Dpeekc = 0; + return (c); + } + if (lap) { + c = *lap++ & (QUOTE | TRIM); + if (c == 0) { + lap = 0; + goto top; + } +quotspec: + if (cmap(c, QUOTES)) + return (c | QUOTE); + return (c); + } + if (dolp) { + if ((c = *dolp++ & (QUOTE | TRIM)) != '\0') + goto quotspec; + if (dolcnt > 0) { + setDolp(*dolnxt++); + --dolcnt; + return (' '); + } + dolp = 0; + } + if (dolcnt > 0) { + setDolp(*dolnxt++); + --dolcnt; + goto top; + } + c = Dredc(); + if (c == '$' && flag) { + Dgetdol(); + goto top; + } + return (c); +} + +static Char *nulvec[] = {0}; +static struct varent nulargv = {nulvec, STRargv, { NULL, NULL, NULL }, 0}; + +static void +dolerror(s) + Char *s; +{ + setname(vis_str(s)); + stderror(ERR_NAME | ERR_RANGE); +} + +/* + * Handle the multitudinous $ expansion forms. + * Ugh. + */ +static void +Dgetdol() +{ + register Char *np; + register struct varent *vp = NULL; + Char name[4 * MAXVARLEN + 1]; + int c, sc; + int subscr = 0, lwb = 1, upb = 0; + bool dimen = 0, bitset = 0; + char tnp; + Char wbuf[BUFSIZ]; + static Char *dolbang = NULL; + + dolnmod = dolmcnt = dolwcnt = 0; + c = sc = DgetC(0); + if (c == '{') + c = DgetC(0); /* sc is { to take } later */ + if ((c & TRIM) == '#') + dimen++, c = DgetC(0); /* $# takes dimension */ + else if (c == '?') + bitset++, c = DgetC(0); /* $? tests existence */ + switch (c) { + + case '!': + if (dimen || bitset) + stderror(ERR_SYNTAX); + if (backpid != 0) { + if (dolbang) + xfree((ptr_t) dolbang); + setDolp(dolbang = putn(backpid)); + } + goto eatbrac; + + case '$': + if (dimen || bitset) + stderror(ERR_SYNTAX); + setDolp(doldol); + goto eatbrac; + + case '<' | QUOTE: + if (bitset) + stderror(ERR_NOTALLOWED, "$?<"); + if (dimen) + stderror(ERR_NOTALLOWED, "$?#"); + for (np = wbuf; read(OLDSTD, &tnp, 1) == 1; np++) { + *np = (unsigned char) tnp; + if (np >= &wbuf[BUFSIZ - 1]) + stderror(ERR_LTOOLONG); + if (tnp == '\n') + break; + } + *np = 0; + /* + * KLUDGE: dolmod is set here because it will cause setDolp to call + * domod and thus to copy wbuf. Otherwise setDolp would use it + * directly. If we saved it ourselves, no one would know when to free + * it. The actual function of the 'q' causes filename expansion not to + * be done on the interpolated value. + */ + dolmod[dolnmod++] = 'q'; + dolmcnt = 10000; + setDolp(wbuf); + goto eatbrac; + + case DEOF: + case '\n': + stderror(ERR_SYNTAX); + /* NOTREACHED */ + break; + + case '*': + (void) Strcpy(name, STRargv); + vp = adrof(STRargv); + subscr = -1; /* Prevent eating [...] */ + break; + + default: + np = name; + if (Isdigit(c)) { + if (dimen) + stderror(ERR_NOTALLOWED, "$#"); + subscr = 0; + do { + subscr = subscr * 10 + c - '0'; + c = DgetC(0); + } while (Isdigit(c)); + unDredc(c); + if (subscr < 0) { + dolerror(vp->v_name); + return; + } + if (subscr == 0) { + if (bitset) { + dolp = ffile ? STR1 : STR0; + goto eatbrac; + } + if (ffile == 0) + stderror(ERR_DOLZERO); + fixDolMod(); + setDolp(ffile); + goto eatbrac; + } + if (bitset) + stderror(ERR_DOLQUEST); + vp = adrof(STRargv); + if (vp == 0) { + vp = &nulargv; + goto eatmod; + } + break; + } + if (!alnum(c)) + stderror(ERR_VARALNUM); + for (;;) { + *np++ = c; + c = DgetC(0); + if (!alnum(c)) + break; + if (np >= &name[MAXVARLEN]) + stderror(ERR_VARTOOLONG); + } + *np++ = 0; + unDredc(c); + vp = adrof(name); + } + if (bitset) { + dolp = (vp || getenv(short2str(name))) ? STR1 : STR0; + goto eatbrac; + } + if (vp == 0) { + np = str2short(getenv(short2str(name))); + if (np) { + fixDolMod(); + setDolp(np); + goto eatbrac; + } + udvar(name); + /* NOTREACHED */ + } + c = DgetC(0); + upb = blklen(vp->vec); + if (dimen == 0 && subscr == 0 && c == '[') { + np = name; + for (;;) { + c = DgetC(DODOL); /* Allow $ expand within [ ] */ + if (c == ']') + break; + if (c == '\n' || c == DEOF) + stderror(ERR_INCBR); + if (np >= &name[sizeof(name) / sizeof(Char) - 2]) + stderror(ERR_VARTOOLONG); + *np++ = c; + } + *np = 0, np = name; + if (dolp || dolcnt) /* $ exp must end before ] */ + stderror(ERR_EXPORD); + if (!*np) + stderror(ERR_SYNTAX); + if (Isdigit(*np)) { + int i; + + for (i = 0; Isdigit(*np); i = i * 10 + *np++ - '0') + continue; + if ((i < 0 || i > upb) && !any("-*", *np)) { + dolerror(vp->v_name); + return; + } + lwb = i; + if (!*np) + upb = lwb, np = STRstar; + } + if (*np == '*') + np++; + else if (*np != '-') + stderror(ERR_MISSING, '-'); + else { + register int i = upb; + + np++; + if (Isdigit(*np)) { + i = 0; + while (Isdigit(*np)) + i = i * 10 + *np++ - '0'; + if (i < 0 || i > upb) { + dolerror(vp->v_name); + return; + } + } + if (i < lwb) + upb = lwb - 1; + else + upb = i; + } + if (lwb == 0) { + if (upb != 0) { + dolerror(vp->v_name); + return; + } + upb = -1; + } + if (*np) + stderror(ERR_SYNTAX); + } + else { + if (subscr > 0) + if (subscr > upb) + lwb = 1, upb = 0; + else + lwb = upb = subscr; + unDredc(c); + } + if (dimen) { + Char *cp = putn(upb - lwb + 1); + + addla(cp); + xfree((ptr_t) cp); + } + else { +eatmod: + fixDolMod(); + dolnxt = &vp->vec[lwb - 1]; + dolcnt = upb - lwb + 1; + } +eatbrac: + if (sc == '{') { + c = Dredc(); + if (c != '}') + stderror(ERR_MISSING, '}'); + } +} + +static void +fixDolMod() +{ + register int c; + + c = DgetC(0); + if (c == ':') { + do { + c = DgetC(0), dolmcnt = 1, dolwcnt = 1; + if (c == 'g' || c == 'a') { + if (c == 'g') + dolmcnt = 10000; + else + dolwcnt = 10000; + c = DgetC(0); + } + if ((c == 'g' && dolmcnt != 10000) || + (c == 'a' && dolwcnt != 10000)) { + if (c == 'g') + dolmcnt = 10000; + else + dolwcnt = 10000; + c = DgetC(0); + } + + if (c == 's') { /* [eichin:19910926.0755EST] */ + int delimcnt = 2; + int delim = DgetC(0); + dolmod[dolnmod++] = c; + dolmod[dolnmod++] = delim; + + if (!delim || letter(delim) + || Isdigit(delim) || any(" \t\n", delim)) { + seterror(ERR_BADSUBST); + break; + } + while ((c = DgetC(0)) != (-1)) { + dolmod[dolnmod++] = c; + if(c == delim) delimcnt--; + if(!delimcnt) break; + } + if(delimcnt) { + seterror(ERR_BADSUBST); + break; + } + continue; + } + if (!any("htrqxes", c)) + stderror(ERR_BADMOD, c); + dolmod[dolnmod++] = c; + if (c == 'q') + dolmcnt = 10000; + } + while ((c = DgetC(0)) == ':'); + unDredc(c); + } + else + unDredc(c); +} + +static void +setDolp(cp) + register Char *cp; +{ + register Char *dp; + int i; + + if (dolnmod == 0 || dolmcnt == 0) { + dolp = cp; + return; + } + dp = cp = Strsave(cp); + for (i = 0; i < dolnmod; i++) { + /* handle s// [eichin:19910926.0510EST] */ + if(dolmod[i] == 's') { + int delim; + Char *lhsub, *rhsub, *np; + size_t lhlen = 0, rhlen = 0; + int didmod = 0; + + delim = dolmod[++i]; + if (!delim || letter(delim) + || Isdigit(delim) || any(" \t\n", delim)) { + seterror(ERR_BADSUBST); + break; + } + lhsub = &dolmod[++i]; + while(dolmod[i] != delim && dolmod[++i]) { + lhlen++; + } + dolmod[i] = 0; + rhsub = &dolmod[++i]; + while(dolmod[i] != delim && dolmod[++i]) { + rhlen++; + } + dolmod[i] = 0; + + do { + dp = Strstr(cp, lhsub); + if (dp) { + np = (Char *) xmalloc((size_t) + ((Strlen(cp) + 1 - lhlen + rhlen) * + sizeof(Char))); + (void) Strncpy(np, cp, dp - cp); + (void) Strcpy(np + (dp - cp), rhsub); + (void) Strcpy(np + (dp - cp) + rhlen, dp + lhlen); + + xfree((ptr_t) cp); + dp = cp = np; + didmod = 1; + } else { + /* should this do a seterror? */ + break; + } + } + while (dolwcnt == 10000); + /* + * restore dolmod for additional words + */ + dolmod[i] = rhsub[-1] = delim; + if (didmod) + dolmcnt--; + else + break; + } else { + int didmod = 0; + + do { + if ((dp = domod(cp, dolmod[i]))) { + didmod = 1; + if (Strcmp(cp, dp) == 0) { + xfree((ptr_t) cp); + cp = dp; + break; + } + else { + xfree((ptr_t) cp); + cp = dp; + } + } + else + break; + } + while (dolwcnt == 10000); + dp = cp; + if (didmod) + dolmcnt--; + else + break; + } + } + + if (dp) { + addla(dp); + xfree((ptr_t) dp); + } + else + addla(cp); + + dolp = STRNULL; + if (seterr) + stderror(ERR_OLD); +} + +static void +unDredc(c) + int c; +{ + + Dpeekrd = c; +} + +static int +Dredc() +{ + register int c; + + if ((c = Dpeekrd) != '\0') { + Dpeekrd = 0; + return (c); + } + if (Dcp && (c = *Dcp++)) + return (c & (QUOTE | TRIM)); + if (*Dvp == 0) { + Dcp = 0; + return (DEOF); + } + Dcp = *Dvp++; + return (' '); +} + +static void +Dtestq(c) + register int c; +{ + + if (cmap(c, QUOTES)) + gflag = 1; +} + +/* + * Form a shell temporary file (in unit 0) from the words + * of the shell input up to EOF or a line the same as "term". + * Unit 0 should have been closed before this call. + */ +void +/*ARGSUSED*/ +heredoc(term) + Char *term; +{ + register int c; + Char *Dv[2]; + Char obuf[BUFSIZ], lbuf[BUFSIZ], mbuf[BUFSIZ]; + int ocnt, lcnt, mcnt; + register Char *lbp, *obp, *mbp; + Char **vp; + bool quoted; + char *tmp; + + tmp = short2str(shtemp); + if (open(tmp, O_RDWR | O_CREAT | O_TRUNC, 0600) < 0) + stderror(ERR_SYSTEM, tmp, strerror(errno)); + (void) unlink(tmp); /* 0 0 inode! */ + Dv[0] = term; + Dv[1] = NULL; + gflag = 0; + trim(Dv); + rscan(Dv, Dtestq); + quoted = gflag; + ocnt = BUFSIZ; + obp = obuf; + for (;;) { + /* + * Read up a line + */ + lbp = lbuf; + lcnt = BUFSIZ - 4; + for (;;) { + c = readc(1); /* 1 -> Want EOF returns */ + if (c < 0 || c == '\n') + break; + if ((c &= TRIM) != '\0') { + *lbp++ = c; + if (--lcnt < 0) { + setname("<<"); + stderror(ERR_NAME | ERR_OVERFLOW); + } + } + } + *lbp = 0; + + /* + * Check for EOF or compare to terminator -- before expansion + */ + if (c < 0 || eq(lbuf, term)) { + (void) write(0, short2str(obuf), (size_t) (BUFSIZ - ocnt)); + (void) lseek(0, 0l, L_SET); + return; + } + + /* + * If term was quoted or -n just pass it on + */ + if (quoted || noexec) { + *lbp++ = '\n'; + *lbp = 0; + for (lbp = lbuf; (c = *lbp++) != '\0';) { + *obp++ = c; + if (--ocnt == 0) { + (void) write(0, short2str(obuf), BUFSIZ); + obp = obuf; + ocnt = BUFSIZ; + } + } + continue; + } + + /* + * Term wasn't quoted so variable and then command expand the input + * line + */ + Dcp = lbuf; + Dvp = Dv + 1; + mbp = mbuf; + mcnt = BUFSIZ - 4; + for (;;) { + c = DgetC(DODOL); + if (c == DEOF) + break; + if ((c &= TRIM) == 0) + continue; + /* \ quotes \ $ ` here */ + if (c == '\\') { + c = DgetC(0); + if (!any("$\\`", c)) + unDgetC(c | QUOTE), c = '\\'; + else + c |= QUOTE; + } + *mbp++ = c; + if (--mcnt == 0) { + setname("<<"); + stderror(ERR_NAME | ERR_OVERFLOW); + } + } + *mbp++ = 0; + + /* + * If any ` in line do command substitution + */ + mbp = mbuf; + if (any(short2str(mbp), '`')) { + /* + * 1 arg to dobackp causes substitution to be literal. Words are + * broken only at newlines so that all blanks and tabs are + * preserved. Blank lines (null words) are not discarded. + */ + vp = dobackp(mbuf, 1); + } + else + /* Setup trivial vector similar to return of dobackp */ + Dv[0] = mbp, Dv[1] = NULL, vp = Dv; + + /* + * Resurrect the words from the command substitution each separated by + * a newline. Note that the last newline of a command substitution + * will have been discarded, but we put a newline after the last word + * because this represents the newline after the last input line! + */ + for (; *vp; vp++) { + for (mbp = *vp; *mbp; mbp++) { + *obp++ = *mbp & TRIM; + if (--ocnt == 0) { + (void) write(0, short2str(obuf), BUFSIZ); + obp = obuf; + ocnt = BUFSIZ; + } + } + *obp++ = '\n'; + if (--ocnt == 0) { + (void) write(0, short2str(obuf), BUFSIZ); + obp = obuf; + ocnt = BUFSIZ; + } + } + if (pargv) + blkfree(pargv), pargv = 0; + } +} diff --git a/bin/csh/exec.c b/bin/csh/exec.c new file mode 100644 index 000000000000..49c04aeb10eb --- /dev/null +++ b/bin/csh/exec.c @@ -0,0 +1,746 @@ +/*- + * Copyright (c) 1980, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)exec.c 8.3 (Berkeley) 5/23/95"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if __STDC__ +# include +#else +# include +#endif + +#include "csh.h" +#include "extern.h" + +/* + * System level search and execute of a command. We look in each directory + * for the specified command name. If the name contains a '/' then we + * execute only the full path name. If there is no search path then we + * execute only full path names. + */ +extern char **environ; + +/* + * As we search for the command we note the first non-trivial error + * message for presentation to the user. This allows us often + * to show that a file has the wrong mode/no access when the file + * is not in the last component of the search path, so we must + * go on after first detecting the error. + */ +static char *exerr; /* Execution error message */ +static Char *expath; /* Path for exerr */ + +/* + * Xhash is an array of HSHSIZ bits (HSHSIZ / 8 chars), which are used + * to hash execs. If it is allocated (havhash true), then to tell + * whether ``name'' is (possibly) present in the i'th component + * of the variable path, you look at the bit in xhash indexed by + * hash(hashname("name"), i). This is setup automatically + * after .login is executed, and recomputed whenever ``path'' is + * changed. + * The two part hash function is designed to let texec() call the + * more expensive hashname() only once and the simple hash() several + * times (once for each path component checked). + * Byte size is assumed to be 8. + */ +#define HSHSIZ 8192 /* 1k bytes */ +#define HSHMASK (HSHSIZ - 1) +#define HSHMUL 243 +static char xhash[HSHSIZ / 8]; + +#define hash(a, b) (((a) * HSHMUL + (b)) & HSHMASK) +#define bit(h, b) ((h)[(b) >> 3] & 1 << ((b) & 7)) /* bit test */ +#define bis(h, b) ((h)[(b) >> 3] |= 1 << ((b) & 7)) /* bit set */ +static int hits, misses; + +/* Dummy search path for just absolute search when no path */ +static Char *justabs[] = {STRNULL, 0}; + +static void pexerr __P((void)); +static void texec __P((Char *, Char **)); +static int hashname __P((Char *)); +static void tellmewhat __P((struct wordent *)); +static int executable __P((Char *, Char *, bool)); +static int iscommand __P((Char *)); + + +void +/*ARGSUSED*/ +doexec(v, t) + Char **v; + struct command *t; +{ + register Char *dp, **pv, **av, *sav; + register struct varent *pathv; + register bool slash; + register int hashval = 0, hashval1, i; + Char *blk[2]; + sigset_t sigset; + + /* + * Glob the command name. We will search $path even if this does something, + * as in sh but not in csh. One special case: if there is no PATH, then we + * execute only commands which start with '/'. + */ + blk[0] = t->t_dcom[0]; + blk[1] = 0; + gflag = 0, tglob(blk); + if (gflag) { + pv = globall(blk); + if (pv == 0) { + setname(vis_str(blk[0])); + stderror(ERR_NAME | ERR_NOMATCH); + } + gargv = 0; + } + else + pv = saveblk(blk); + + trim(pv); + + exerr = 0; + expath = Strsave(pv[0]); + Vexpath = expath; + + pathv = adrof(STRpath); + if (pathv == 0 && expath[0] != '/') { + blkfree(pv); + pexerr(); + } + slash = any(short2str(expath), '/'); + + /* + * Glob the argument list, if necessary. Otherwise trim off the quote bits. + */ + gflag = 0; + av = &t->t_dcom[1]; + tglob(av); + if (gflag) { + av = globall(av); + if (av == 0) { + blkfree(pv); + setname(vis_str(expath)); + stderror(ERR_NAME | ERR_NOMATCH); + } + gargv = 0; + } + else + av = saveblk(av); + + blkfree(t->t_dcom); + t->t_dcom = blkspl(pv, av); + xfree((ptr_t) pv); + xfree((ptr_t) av); + av = t->t_dcom; + trim(av); + + if (*av == NULL || **av == '\0') + pexerr(); + + xechoit(av); /* Echo command if -x */ + /* + * Since all internal file descriptors are set to close on exec, we don't + * need to close them explicitly here. Just reorient ourselves for error + * messages. + */ + SHIN = 0; + SHOUT = 1; + SHERR = 2; + OLDSTD = 0; + /* + * We must do this AFTER any possible forking (like `foo` in glob) so that + * this shell can still do subprocesses. + */ + sigemptyset(&sigset); + sigprocmask(SIG_SETMASK, &sigset, NULL); + /* + * If no path, no words in path, or a / in the filename then restrict the + * command search. + */ + if (pathv == 0 || pathv->vec[0] == 0 || slash) + pv = justabs; + else + pv = pathv->vec; + sav = Strspl(STRslash, *av);/* / command name for postpending */ + Vsav = sav; + if (havhash) + hashval = hashname(*av); + i = 0; + hits++; + do { + /* + * Try to save time by looking at the hash table for where this command + * could be. If we are doing delayed hashing, then we put the names in + * one at a time, as the user enters them. This is kinda like Korn + * Shell's "tracked aliases". + */ + if (!slash && pv[0][0] == '/' && havhash) { + hashval1 = hash(hashval, i); + if (!bit(xhash, hashval1)) + goto cont; + } + if (pv[0][0] == 0 || eq(pv[0], STRdot)) /* don't make ./xxx */ + texec(*av, av); + else { + dp = Strspl(*pv, sav); + Vdp = dp; + texec(dp, av); + Vdp = 0; + xfree((ptr_t) dp); + } + misses++; +cont: + pv++; + i++; + } while (*pv); + hits--; + Vsav = 0; + xfree((ptr_t) sav); + pexerr(); +} + +static void +pexerr() +{ + /* Couldn't find the damn thing */ + if (expath) { + setname(vis_str(expath)); + Vexpath = 0; + xfree((ptr_t) expath); + expath = 0; + } + else + setname(""); + if (exerr) + stderror(ERR_NAME | ERR_STRING, exerr); + stderror(ERR_NAME | ERR_COMMAND); +} + +/* + * Execute command f, arg list t. + * Record error message if not found. + * Also do shell scripts here. + */ +static void +texec(sf, st) + Char *sf; + register Char **st; +{ + register char **t; + register char *f; + register struct varent *v; + register Char **vp; + Char *lastsh[2]; + int fd; + unsigned char c; + Char *st0, **ost; + + /* The order for the conversions is significant */ + t = short2blk(st); + f = short2str(sf); + Vt = t; + errno = 0; /* don't use a previous error */ + (void) execve(f, t, environ); + Vt = 0; + blkfree((Char **) t); + switch (errno) { + + case ENOEXEC: + /* + * From: casper@fwi.uva.nl (Casper H.S. Dik) If we could not execute + * it, don't feed it to the shell if it looks like a binary! + */ + if ((fd = open(f, O_RDONLY)) != -1) { + if (read(fd, (char *) &c, 1) == 1) { + if (!Isprint(c) && (c != '\n' && c != '\t')) { + (void) close(fd); + /* + * We *know* what ENOEXEC means. + */ + stderror(ERR_ARCH, f, strerror(errno)); + } + } +#ifdef _PATH_BSHELL + else + c = '#'; +#endif + (void) close(fd); + } + /* + * If there is an alias for shell, then put the words of the alias in + * front of the argument list replacing the command name. Note no + * interpretation of the words at this point. + */ + v = adrof1(STRshell, &aliases); + if (v == 0) { + vp = lastsh; + vp[0] = adrof(STRshell) ? value(STRshell) : STR_SHELLPATH; + vp[1] = NULL; +#ifdef _PATH_BSHELL + if (fd != -1 && c != '#') + vp[0] = STR_BSHELL; +#endif + } + else + vp = v->vec; + st0 = st[0]; + st[0] = sf; + ost = st; + st = blkspl(vp, st); /* Splice up the new arglst */ + ost[0] = st0; + sf = *st; + /* The order for the conversions is significant */ + t = short2blk(st); + f = short2str(sf); + xfree((ptr_t) st); + Vt = t; + (void) execve(f, t, environ); + Vt = 0; + blkfree((Char **) t); + /* The sky is falling, the sky is falling! */ + + case ENOMEM: + stderror(ERR_SYSTEM, f, strerror(errno)); + + case ENOENT: + break; + + default: + if (exerr == 0) { + exerr = strerror(errno); + if (expath) + xfree((ptr_t) expath); + expath = Strsave(sf); + Vexpath = expath; + } + } +} + +/*ARGSUSED*/ +void +execash(t, kp) + Char **t; + register struct command *kp; +{ + int saveIN, saveOUT, saveDIAG, saveSTD; + int oSHIN; + int oSHOUT; + int oSHERR; + int oOLDSTD; + jmp_buf osetexit; + int my_reenter; + int odidfds; + sig_t osigint, osigquit, osigterm; + + if (chkstop == 0 && setintr) + panystop(0); + /* + * Hmm, we don't really want to do that now because we might + * fail, but what is the choice + */ + rechist(); + + osigint = signal(SIGINT, parintr); + osigquit = signal(SIGQUIT, parintr); + osigterm = signal(SIGTERM, parterm); + + odidfds = didfds; + oSHIN = SHIN; + oSHOUT = SHOUT; + oSHERR = SHERR; + oOLDSTD = OLDSTD; + + saveIN = dcopy(SHIN, -1); + saveOUT = dcopy(SHOUT, -1); + saveDIAG = dcopy(SHERR, -1); + saveSTD = dcopy(OLDSTD, -1); + + lshift(kp->t_dcom, 1); + + getexit(osetexit); + + if ((my_reenter = setexit()) == 0) { + SHIN = dcopy(0, -1); + SHOUT = dcopy(1, -1); + SHERR = dcopy(2, -1); + didfds = 0; + doexec(t, kp); + } + + (void) signal(SIGINT, osigint); + (void) signal(SIGQUIT, osigquit); + (void) signal(SIGTERM, osigterm); + + doneinp = 0; + didfds = odidfds; + (void) close(SHIN); + (void) close(SHOUT); + (void) close(SHERR); + (void) close(OLDSTD); + SHIN = dmove(saveIN, oSHIN); + SHOUT = dmove(saveOUT, oSHOUT); + SHERR = dmove(saveDIAG, oSHERR); + OLDSTD = dmove(saveSTD, oOLDSTD); + + resexit(osetexit); + if (my_reenter) + stderror(ERR_SILENT); +} + +void +xechoit(t) + Char **t; +{ + if (adrof(STRecho)) { + (void) fflush(csherr); + blkpr(csherr, t); + (void) fputc('\n', csherr); + } +} + +void +/*ARGSUSED*/ +dohash(v, t) + Char **v; + struct command *t; +{ + DIR *dirp; + register struct dirent *dp; + register int cnt; + int i = 0; + struct varent *pathv = adrof(STRpath); + Char **pv; + int hashval; + + havhash = 1; + for (cnt = 0; cnt < sizeof xhash; cnt++) + xhash[cnt] = 0; + if (pathv == 0) + return; + for (pv = pathv->vec; *pv; pv++, i++) { + if (pv[0][0] != '/') + continue; + dirp = opendir(short2str(*pv)); + if (dirp == NULL) + continue; + while ((dp = readdir(dirp)) != NULL) { + if (dp->d_ino == 0) + continue; + if (dp->d_name[0] == '.' && + (dp->d_name[1] == '\0' || + (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) + continue; + hashval = hash(hashname(str2short(dp->d_name)), i); + bis(xhash, hashval); + /* tw_add_comm_name (dp->d_name); */ + } + (void) closedir(dirp); + } +} + +void +/*ARGSUSED*/ +dounhash(v, t) + Char **v; + struct command *t; +{ + havhash = 0; +} + +void +/*ARGSUSED*/ +hashstat(v, t) + Char **v; + struct command *t; +{ + if (hits + misses) + (void) fprintf(cshout, "%d hits, %d misses, %d%%\n", + hits, misses, 100 * hits / (hits + misses)); +} + +/* + * Hash a command name. + */ +static int +hashname(cp) + register Char *cp; +{ + register long h = 0; + + while (*cp) + h = hash(h, *cp++); + return ((int) h); +} + +static int +iscommand(name) + Char *name; +{ + register Char **pv; + register Char *sav; + register struct varent *v; + register bool slash = any(short2str(name), '/'); + register int hashval = 0, hashval1, i; + + v = adrof(STRpath); + if (v == 0 || v->vec[0] == 0 || slash) + pv = justabs; + else + pv = v->vec; + sav = Strspl(STRslash, name); /* / command name for postpending */ + if (havhash) + hashval = hashname(name); + i = 0; + do { + if (!slash && pv[0][0] == '/' && havhash) { + hashval1 = hash(hashval, i); + if (!bit(xhash, hashval1)) + goto cont; + } + if (pv[0][0] == 0 || eq(pv[0], STRdot)) { /* don't make ./xxx */ + if (executable(NULL, name, 0)) { + xfree((ptr_t) sav); + return i + 1; + } + } + else { + if (executable(*pv, sav, 0)) { + xfree((ptr_t) sav); + return i + 1; + } + } +cont: + pv++; + i++; + } while (*pv); + xfree((ptr_t) sav); + return 0; +} + +/* Also by: + * Andreas Luik + * I S A GmbH - Informationssysteme fuer computerintegrierte Automatisierung + * Azenberstr. 35 + * D-7000 Stuttgart 1 + * West-Germany + * is the executable() routine below and changes to iscommand(). + * Thanks again!! + */ + +/* + * executable() examines the pathname obtained by concatenating dir and name + * (dir may be NULL), and returns 1 either if it is executable by us, or + * if dir_ok is set and the pathname refers to a directory. + * This is a bit kludgy, but in the name of optimization... + */ +static int +executable(dir, name, dir_ok) + Char *dir, *name; + bool dir_ok; +{ + struct stat stbuf; + Char path[MAXPATHLEN + 1], *dp, *sp; + char *strname; + + if (dir && *dir) { + for (dp = path, sp = dir; *sp; *dp++ = *sp++) + if (dp == &path[MAXPATHLEN + 1]) { + *--dp = '\0'; + break; + } + for (sp = name; *sp; *dp++ = *sp++) + if (dp == &path[MAXPATHLEN + 1]) { + *--dp = '\0'; + break; + } + *dp = '\0'; + strname = short2str(path); + } + else + strname = short2str(name); + return (stat(strname, &stbuf) != -1 && + ((S_ISREG(stbuf.st_mode) && + /* save time by not calling access() in the hopeless case */ + (stbuf.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) && + access(strname, X_OK) == 0) || + (dir_ok && S_ISDIR(stbuf.st_mode)))); +} + +/* The dowhich() is by: + * Andreas Luik + * I S A GmbH - Informationssysteme fuer computerintegrierte Automatisierung + * Azenberstr. 35 + * D-7000 Stuttgart 1 + * West-Germany + * Thanks!! + */ +/*ARGSUSED*/ +void +dowhich(v, c) + register Char **v; + struct command *c; +{ + struct wordent lex[3]; + struct varent *vp; + + lex[0].next = &lex[1]; + lex[1].next = &lex[2]; + lex[2].next = &lex[0]; + + lex[0].prev = &lex[2]; + lex[1].prev = &lex[0]; + lex[2].prev = &lex[1]; + + lex[0].word = STRNULL; + lex[2].word = STRret; + + while (*++v) { + if ((vp = adrof1(*v, &aliases)) != NULL) { + (void) fprintf(cshout, "%s: \t aliased to ", vis_str(*v)); + blkpr(cshout, vp->vec); + (void) fputc('\n', cshout); + } + else { + lex[1].word = *v; + tellmewhat(lex); + } + } +} + +static void +tellmewhat(lex) + struct wordent *lex; +{ + register int i; + register struct biltins *bptr; + register struct wordent *sp = lex->next; + bool aliased = 0; + Char *s0, *s1, *s2, *cmd; + Char qc; + + if (adrof1(sp->word, &aliases)) { + alias(lex); + sp = lex->next; + aliased = 1; + } + + s0 = sp->word; /* to get the memory freeing right... */ + + /* handle quoted alias hack */ + if ((*(sp->word) & (QUOTE | TRIM)) == QUOTE) + (sp->word)++; + + /* do quoting, if it hasn't been done */ + s1 = s2 = sp->word; + while (*s2) + switch (*s2) { + case '\'': + case '"': + qc = *s2++; + while (*s2 && *s2 != qc) + *s1++ = *s2++ | QUOTE; + if (*s2) + s2++; + break; + case '\\': + if (*++s2) + *s1++ = *s2++ | QUOTE; + break; + default: + *s1++ = *s2++; + } + *s1 = '\0'; + + for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++) { + if (eq(sp->word, str2short(bptr->bname))) { + if (aliased) + prlex(cshout, lex); + (void) fprintf(cshout, "%s: shell built-in command.\n", + vis_str(sp->word)); + sp->word = s0; /* we save and then restore this */ + return; + } + } + + sp->word = cmd = globone(sp->word, G_IGNORE); + + if ((i = iscommand(strip(sp->word))) != 0) { + register Char **pv; + register struct varent *v; + bool slash = any(short2str(sp->word), '/'); + + v = adrof(STRpath); + if (v == 0 || v->vec[0] == 0 || slash) + pv = justabs; + else + pv = v->vec; + + while (--i) + pv++; + if (pv[0][0] == 0 || eq(pv[0], STRdot)) { + if (!slash) { + sp->word = Strspl(STRdotsl, sp->word); + prlex(cshout, lex); + xfree((ptr_t) sp->word); + } + else + prlex(cshout, lex); + sp->word = s0; /* we save and then restore this */ + xfree((ptr_t) cmd); + return; + } + s1 = Strspl(*pv, STRslash); + sp->word = Strspl(s1, sp->word); + xfree((ptr_t) s1); + prlex(cshout, lex); + xfree((ptr_t) sp->word); + } + else { + if (aliased) + prlex(cshout, lex); + (void) fprintf(csherr, "%s: Command not found.\n", vis_str(sp->word)); + } + sp->word = s0; /* we save and then restore this */ + xfree((ptr_t) cmd); +} diff --git a/bin/csh/file.c b/bin/csh/file.c new file mode 100644 index 000000000000..3a89d34e6ffa --- /dev/null +++ b/bin/csh/file.c @@ -0,0 +1,690 @@ +/*- + * Copyright (c) 1980, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)file.c 8.4 (Berkeley) 5/6/95"; +#endif /* not lint */ + +#ifdef FILEC + +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef SHORT_STRINGS +#include +#endif /* SHORT_STRINGS */ +#if __STDC__ +# include +#else +# include +#endif + +#include "csh.h" +#include "extern.h" + +/* + * Tenex style file name recognition, .. and more. + * History: + * Author: Ken Greer, Sept. 1975, CMU. + * Finally got around to adding to the Cshell., Ken Greer, Dec. 1981. + */ + +#define ON 1 +#define OFF 0 +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#define ESC '\033' + +typedef enum { + LIST, RECOGNIZE +} COMMAND; + +static void setup_tty __P((int)); +static void back_to_col_1 __P((void)); +static void pushback __P((Char *)); +static void catn __P((Char *, Char *, int)); +static void copyn __P((Char *, Char *, int)); +static Char filetype __P((Char *, Char *)); +static void print_by_column __P((Char *, Char *[], int)); +static Char *tilde __P((Char *, Char *)); +static void retype __P((void)); +static void beep __P((void)); +static void print_recognized_stuff __P((Char *)); +static void extract_dir_and_name __P((Char *, Char *, Char *)); +static Char *getentry __P((DIR *, int)); +static void free_items __P((Char **)); +static int tsearch __P((Char *, COMMAND, int)); +static int recognize __P((Char *, Char *, int, int)); +static int is_prefix __P((Char *, Char *)); +static int is_suffix __P((Char *, Char *)); +static int ignored __P((Char *)); + +/* + * Put this here so the binary can be patched with adb to enable file + * completion by default. Filec controls completion, nobeep controls + * ringing the terminal bell on incomplete expansions. + */ +bool filec = 0; + +static void +setup_tty(on) + int on; +{ + static struct termios tchars; + + (void) tcgetattr(SHIN, &tchars); + + if (on) { + tchars.c_cc[VEOL] = ESC; + if (tchars.c_lflag & ICANON) + on = TCSANOW; + else { + on = TCSAFLUSH; + tchars.c_lflag |= ICANON; + } + } + else { + tchars.c_cc[VEOL] = _POSIX_VDISABLE; + on = TCSANOW; + } + + (void) tcsetattr(SHIN, TCSANOW, &tchars); +} + +/* + * Move back to beginning of current line + */ +static void +back_to_col_1() +{ + struct termios tty, tty_normal; + sigset_t sigset, osigset; + + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigprocmask(SIG_BLOCK, &sigset, &osigset); + (void) tcgetattr(SHOUT, &tty); + tty_normal = tty; + tty.c_iflag &= ~INLCR; + tty.c_oflag &= ~ONLCR; + (void) tcsetattr(SHOUT, TCSANOW, &tty); + (void) write(SHOUT, "\r", 1); + (void) tcsetattr(SHOUT, TCSANOW, &tty_normal); + sigprocmask(SIG_SETMASK, &osigset, NULL); +} + +/* + * Push string contents back into tty queue + */ +static void +pushback(string) + Char *string; +{ + register Char *p; + struct termios tty, tty_normal; + sigset_t sigset, osigset; + char c; + + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigprocmask(SIG_BLOCK, &sigset, &osigset); + (void) tcgetattr(SHOUT, &tty); + tty_normal = tty; + tty.c_lflag &= ~(ECHOKE | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOCTL); + (void) tcsetattr(SHOUT, TCSANOW, &tty); + + for (p = string; (c = *p) != '\0'; p++) + (void) ioctl(SHOUT, TIOCSTI, (ioctl_t) & c); + (void) tcsetattr(SHOUT, TCSANOW, &tty_normal); + sigprocmask(SIG_SETMASK, &osigset, NULL); +} + +/* + * Concatenate src onto tail of des. + * Des is a string whose maximum length is count. + * Always null terminate. + */ +static void +catn(des, src, count) + register Char *des, *src; + register int count; +{ + while (--count >= 0 && *des) + des++; + while (--count >= 0) + if ((*des++ = *src++) == 0) + return; + *des = '\0'; +} + +/* + * Like strncpy but always leave room for trailing \0 + * and always null terminate. + */ +static void +copyn(des, src, count) + register Char *des, *src; + register int count; +{ + while (--count >= 0) + if ((*des++ = *src++) == 0) + return; + *des = '\0'; +} + +static Char +filetype(dir, file) + Char *dir, *file; +{ + Char path[MAXPATHLEN]; + struct stat statb; + + catn(Strcpy(path, dir), file, sizeof(path) / sizeof(Char)); + if (lstat(short2str(path), &statb) == 0) { + switch (statb.st_mode & S_IFMT) { + case S_IFDIR: + return ('/'); + + case S_IFLNK: + if (stat(short2str(path), &statb) == 0 && /* follow it out */ + S_ISDIR(statb.st_mode)) + return ('>'); + else + return ('@'); + + case S_IFSOCK: + return ('='); + + default: + if (statb.st_mode & 0111) + return ('*'); + } + } + return (' '); +} + +static struct winsize win; + +/* + * Print sorted down columns + */ +static void +print_by_column(dir, items, count) + Char *dir, *items[]; + int count; +{ + register int i, rows, r, c, maxwidth = 0, columns; + + if (ioctl(SHOUT, TIOCGWINSZ, (ioctl_t) & win) < 0 || win.ws_col == 0) + win.ws_col = 80; + for (i = 0; i < count; i++) + maxwidth = maxwidth > (r = Strlen(items[i])) ? maxwidth : r; + maxwidth += 2; /* for the file tag and space */ + columns = win.ws_col / maxwidth; + if (columns == 0) + columns = 1; + rows = (count + (columns - 1)) / columns; + for (r = 0; r < rows; r++) { + for (c = 0; c < columns; c++) { + i = c * rows + r; + if (i < count) { + register int w; + + (void) fprintf(cshout, "%s", vis_str(items[i])); + (void) fputc(dir ? filetype(dir, items[i]) : ' ', cshout); + if (c < columns - 1) { /* last column? */ + w = Strlen(items[i]) + 1; + for (; w < maxwidth; w++) + (void) fputc(' ', cshout); + } + } + } + (void) fputc('\r', cshout); + (void) fputc('\n', cshout); + } +} + +/* + * Expand file name with possible tilde usage + * ~person/mumble + * expands to + * home_directory_of_person/mumble + */ +static Char * +tilde(new, old) + Char *new, *old; +{ + register Char *o, *p; + register struct passwd *pw; + static Char person[40]; + + if (old[0] != '~') + return (Strcpy(new, old)); + + for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++) + continue; + *p = '\0'; + if (person[0] == '\0') + (void) Strcpy(new, value(STRhome)); + else { + pw = getpwnam(short2str(person)); + if (pw == NULL) + return (NULL); + (void) Strcpy(new, str2short(pw->pw_dir)); + } + (void) Strcat(new, o); + return (new); +} + +/* + * Cause pending line to be printed + */ +static void +retype() +{ + struct termios tty; + + (void) tcgetattr(SHOUT, &tty); + tty.c_lflag |= PENDIN; + (void) tcsetattr(SHOUT, TCSANOW, &tty); +} + +static void +beep() +{ + if (adrof(STRnobeep) == 0) + (void) write(SHOUT, "\007", 1); +} + +/* + * Erase that silly ^[ and + * print the recognized part of the string + */ +static void +print_recognized_stuff(recognized_part) + Char *recognized_part; +{ + /* An optimized erasing of that silly ^[ */ + (void) fputc('\b', cshout); + (void) fputc('\b', cshout); + switch (Strlen(recognized_part)) { + + case 0: /* erase two Characters: ^[ */ + (void) fputc(' ', cshout); + (void) fputc(' ', cshout); + (void) fputc('\b', cshout); + (void) fputc('\b', cshout); + break; + + case 1: /* overstrike the ^, erase the [ */ + (void) fprintf(cshout, "%s", vis_str(recognized_part)); + (void) fputc(' ', cshout); + (void) fputc('\b', cshout); + break; + + default: /* overstrike both Characters ^[ */ + (void) fprintf(cshout, "%s", vis_str(recognized_part)); + break; + } + (void) fflush(cshout); +} + +/* + * Parse full path in file into 2 parts: directory and file names + * Should leave final slash (/) at end of dir. + */ +static void +extract_dir_and_name(path, dir, name) + Char *path, *dir, *name; +{ + register Char *p; + + p = Strrchr(path, '/'); + if (p == NULL) { + copyn(name, path, MAXNAMLEN); + dir[0] = '\0'; + } + else { + copyn(name, ++p, MAXNAMLEN); + copyn(dir, path, p - path); + } +} + +static Char * +getentry(dir_fd, looking_for_lognames) + DIR *dir_fd; + int looking_for_lognames; +{ + register struct passwd *pw; + register struct dirent *dirp; + + if (looking_for_lognames) { + if ((pw = getpwent()) == NULL) + return (NULL); + return (str2short(pw->pw_name)); + } + if ((dirp = readdir(dir_fd)) != NULL) + return (str2short(dirp->d_name)); + return (NULL); +} + +static void +free_items(items) + register Char **items; +{ + register int i; + + for (i = 0; items[i]; i++) + xfree((ptr_t) items[i]); + xfree((ptr_t) items); +} + +#define FREE_ITEMS(items) { \ + sigset_t sigset, osigset;\ +\ + sigemptyset(&sigset);\ + sigaddset(&sigset, SIGINT);\ + sigprocmask(SIG_BLOCK, &sigset, &osigset);\ + free_items(items);\ + items = NULL;\ + sigprocmask(SIG_SETMASK, &osigset, NULL);\ +} + +/* + * Perform a RECOGNIZE or LIST command on string "word". + */ +static int +tsearch(word, command, max_word_length) + Char *word; + COMMAND command; + int max_word_length; +{ + static Char **items = NULL; + register DIR *dir_fd; + register numitems = 0, ignoring = TRUE, nignored = 0; + register name_length, looking_for_lognames; + Char tilded_dir[MAXPATHLEN + 1], dir[MAXPATHLEN + 1]; + Char name[MAXNAMLEN + 1], extended_name[MAXNAMLEN + 1]; + Char *entry; + +#define MAXITEMS 1024 + + if (items != NULL) + FREE_ITEMS(items); + + looking_for_lognames = (*word == '~') && (Strchr(word, '/') == NULL); + if (looking_for_lognames) { + (void) setpwent(); + copyn(name, &word[1], MAXNAMLEN); /* name sans ~ */ + dir_fd = NULL; + } + else { + extract_dir_and_name(word, dir, name); + if (tilde(tilded_dir, dir) == 0) + return (0); + dir_fd = opendir(*tilded_dir ? short2str(tilded_dir) : "."); + if (dir_fd == NULL) + return (0); + } + +again: /* search for matches */ + name_length = Strlen(name); + for (numitems = 0; (entry = getentry(dir_fd, looking_for_lognames)) != NULL;) { + if (!is_prefix(name, entry)) + continue; + /* Don't match . files on null prefix match */ + if (name_length == 0 && entry[0] == '.' && + !looking_for_lognames) + continue; + if (command == LIST) { + if (numitems >= MAXITEMS) { + (void) fprintf(csherr, "\nYikes!! Too many %s!!\n", + looking_for_lognames ? + "names in password file" : "files"); + break; + } + if (items == NULL) + items = (Char **) xcalloc(sizeof(items[0]), MAXITEMS); + items[numitems] = (Char *) xmalloc((size_t) (Strlen(entry) + 1) * + sizeof(Char)); + copyn(items[numitems], entry, MAXNAMLEN); + numitems++; + } + else { /* RECOGNIZE command */ + if (ignoring && ignored(entry)) + nignored++; + else if (recognize(extended_name, + entry, name_length, ++numitems)) + break; + } + } + if (ignoring && numitems == 0 && nignored > 0) { + ignoring = FALSE; + nignored = 0; + if (looking_for_lognames) + (void) setpwent(); + else + rewinddir(dir_fd); + goto again; + } + + if (looking_for_lognames) + (void) endpwent(); + else + (void) closedir(dir_fd); + if (numitems == 0) + return (0); + if (command == RECOGNIZE) { + if (looking_for_lognames) + copyn(word, STRtilde, 1); + else + /* put back dir part */ + copyn(word, dir, max_word_length); + /* add extended name */ + catn(word, extended_name, max_word_length); + return (numitems); + } + else { /* LIST */ + qsort((ptr_t) items, numitems, sizeof(items[0]), + (int (*) __P((const void *, const void *))) sortscmp); + print_by_column(looking_for_lognames ? NULL : tilded_dir, + items, numitems); + if (items != NULL) + FREE_ITEMS(items); + } + return (0); +} + +/* + * Object: extend what user typed up to an ambiguity. + * Algorithm: + * On first match, copy full entry (assume it'll be the only match) + * On subsequent matches, shorten extended_name to the first + * Character mismatch between extended_name and entry. + * If we shorten it back to the prefix length, stop searching. + */ +static int +recognize(extended_name, entry, name_length, numitems) + Char *extended_name, *entry; + int name_length, numitems; +{ + if (numitems == 1) /* 1st match */ + copyn(extended_name, entry, MAXNAMLEN); + else { /* 2nd & subsequent matches */ + register Char *x, *ent; + register int len = 0; + + x = extended_name; + for (ent = entry; *x && *x == *ent++; x++, len++) + continue; + *x = '\0'; /* Shorten at 1st Char diff */ + if (len == name_length) /* Ambiguous to prefix? */ + return (-1); /* So stop now and save time */ + } + return (0); +} + +/* + * Return true if check matches initial Chars in template. + * This differs from PWB imatch in that if check is null + * it matches anything. + */ +static int +is_prefix(check, template) + register Char *check, *template; +{ + do + if (*check == 0) + return (TRUE); + while (*check++ == *template++); + return (FALSE); +} + +/* + * Return true if the Chars in template appear at the + * end of check, I.e., are it's suffix. + */ +static int +is_suffix(check, template) + Char *check, *template; +{ + register Char *c, *t; + + for (c = check; *c++;) + continue; + for (t = template; *t++;) + continue; + for (;;) { + if (t == template) + return 1; + if (c == check || *--t != *--c) + return 0; + } +} + +int +tenex(inputline, inputline_size) + Char *inputline; + int inputline_size; +{ + register int numitems, num_read; + char tinputline[BUFSIZ]; + + + setup_tty(ON); + + while ((num_read = read(SHIN, tinputline, BUFSIZ)) > 0) { + int i; + static Char delims[] = {' ', '\'', '"', '\t', ';', '&', '<', + '>', '(', ')', '|', '^', '%', '\0'}; + register Char *str_end, *word_start, last_Char, should_retype; + register int space_left; + COMMAND command; + + for (i = 0; i < num_read; i++) + inputline[i] = (unsigned char) tinputline[i]; + last_Char = inputline[num_read - 1] & ASCII; + + if (last_Char == '\n' || num_read == inputline_size) + break; + command = (last_Char == ESC) ? RECOGNIZE : LIST; + if (command == LIST) + (void) fputc('\n', cshout); + str_end = &inputline[num_read]; + if (last_Char == ESC) + --str_end; /* wipeout trailing cmd Char */ + *str_end = '\0'; + /* + * Find LAST occurence of a delimiter in the inputline. The word start + * is one Character past it. + */ + for (word_start = str_end; word_start > inputline; --word_start) + if (Strchr(delims, word_start[-1])) + break; + space_left = inputline_size - (word_start - inputline) - 1; + numitems = tsearch(word_start, command, space_left); + + if (command == RECOGNIZE) { + /* print from str_end on */ + print_recognized_stuff(str_end); + if (numitems != 1) /* Beep = No match/ambiguous */ + beep(); + } + + /* + * Tabs in the input line cause trouble after a pushback. tty driver + * won't backspace over them because column positions are now + * incorrect. This is solved by retyping over current line. + */ + should_retype = FALSE; + if (Strchr(inputline, '\t')) { /* tab Char in input line? */ + back_to_col_1(); + should_retype = TRUE; + } + if (command == LIST) /* Always retype after a LIST */ + should_retype = TRUE; + if (should_retype) + printprompt(); + pushback(inputline); + if (should_retype) + retype(); + } + setup_tty(OFF); + return (num_read); +} + +static int +ignored(entry) + register Char *entry; +{ + struct varent *vp; + register Char **cp; + + if ((vp = adrof(STRfignore)) == NULL || (cp = vp->vec) == NULL) + return (FALSE); + for (; *cp != NULL; cp++) + if (is_suffix(entry, *cp)) + return (TRUE); + return (FALSE); +} +#endif /* FILEC */ diff --git a/bin/csh/func.c b/bin/csh/func.c new file mode 100644 index 000000000000..8d024794e0f6 --- /dev/null +++ b/bin/csh/func.c @@ -0,0 +1,1517 @@ +/*- + * Copyright (c) 1980, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)func.c 8.2 (Berkeley) 3/22/95"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#if __STDC__ +# include +#else +# include +#endif + +#include "csh.h" +#include "extern.h" +#include "pathnames.h" + +extern char **environ; + +static int zlast = -1; +static void islogin __P((void)); +static void reexecute __P((struct command *)); +static void preread __P((void)); +static void doagain __P((void)); +static void search __P((int, int, Char *)); +static int getword __P((Char *)); +static int keyword __P((Char *)); +static void toend __P((void)); +static void xecho __P((int, Char **)); +static void Unsetenv __P((Char *)); + +struct biltins * +isbfunc(t) + struct command *t; +{ + register Char *cp = t->t_dcom[0]; + register struct biltins *bp, *bp1, *bp2; + static struct biltins label = {"", dozip, 0, 0}; + static struct biltins foregnd = {"%job", dofg1, 0, 0}; + static struct biltins backgnd = {"%job &", dobg1, 0, 0}; + + if (lastchr(cp) == ':') { + label.bname = short2str(cp); + return (&label); + } + if (*cp == '%') { + if (t->t_dflg & F_AMPERSAND) { + t->t_dflg &= ~F_AMPERSAND; + backgnd.bname = short2str(cp); + return (&backgnd); + } + foregnd.bname = short2str(cp); + return (&foregnd); + } + /* + * Binary search Bp1 is the beginning of the current search range. Bp2 is + * one past the end. + */ + for (bp1 = bfunc, bp2 = bfunc + nbfunc; bp1 < bp2;) { + register i; + + bp = bp1 + ((bp2 - bp1) >> 1); + if ((i = *cp - *bp->bname) == 0 && + (i = Strcmp(cp, str2short(bp->bname))) == 0) + return bp; + if (i < 0) + bp2 = bp; + else + bp1 = bp + 1; + } + return (0); +} + +void +func(t, bp) + register struct command *t; + register struct biltins *bp; +{ + int i; + + xechoit(t->t_dcom); + setname(bp->bname); + i = blklen(t->t_dcom) - 1; + if (i < bp->minargs) + stderror(ERR_NAME | ERR_TOOFEW); + if (i > bp->maxargs) + stderror(ERR_NAME | ERR_TOOMANY); + (*bp->bfunct) (t->t_dcom, t); +} + +void +/*ARGSUSED*/ +doonintr(v, t) + Char **v; + struct command *t; +{ + register Char *cp; + register Char *vv = v[1]; + sigset_t sigset; + + if (parintr == SIG_IGN) + return; + if (setintr && intty) + stderror(ERR_NAME | ERR_TERMINAL); + cp = gointr; + gointr = 0; + xfree((ptr_t) cp); + if (vv == 0) { + if (setintr) { + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigprocmask(SIG_BLOCK, &sigset, NULL); + } else + (void) signal(SIGINT, SIG_DFL); + gointr = 0; + } + else if (eq((vv = strip(vv)), STRminus)) { + (void) signal(SIGINT, SIG_IGN); + gointr = Strsave(STRminus); + } + else { + gointr = Strsave(vv); + (void) signal(SIGINT, pintr); + } +} + +void +/*ARGSUSED*/ +donohup(v, t) + Char **v; + struct command *t; +{ + if (intty) + stderror(ERR_NAME | ERR_TERMINAL); + if (setintr == 0) { + (void) signal(SIGHUP, SIG_IGN); + } +} + +void +/*ARGSUSED*/ +dozip(v, t) + Char **v; + struct command *t; +{ + ; +} + +void +prvars() +{ + plist(&shvhed); +} + +void +/*ARGSUSED*/ +doalias(v, t) + Char **v; + struct command *t; +{ + register struct varent *vp; + register Char *p; + + v++; + p = *v++; + if (p == 0) + plist(&aliases); + else if (*v == 0) { + vp = adrof1(strip(p), &aliases); + if (vp) { + blkpr(cshout, vp->vec); + fputc('\n', cshout); + } + } + else { + if (eq(p, STRalias) || eq(p, STRunalias)) { + setname(vis_str(p)); + stderror(ERR_NAME | ERR_DANGER); + } + set1(strip(p), saveblk(v), &aliases); + } +} + +void +/*ARGSUSED*/ +unalias(v, t) + Char **v; + struct command *t; +{ + unset1(v, &aliases); +} + +void +/*ARGSUSED*/ +dologout(v, t) + Char **v; + struct command *t; +{ + islogin(); + goodbye(); +} + +void +/*ARGSUSED*/ +dologin(v, t) + Char **v; + struct command *t; +{ + islogin(); + rechist(); + (void) signal(SIGTERM, parterm); + (void) execl(_PATH_LOGIN, "login", short2str(v[1]), NULL); + untty(); + xexit(1); +} + +static void +islogin() +{ + if (chkstop == 0 && setintr) + panystop(0); + if (loginsh) + return; + stderror(ERR_NOTLOGIN); +} + +void +doif(v, kp) + Char **v; + struct command *kp; +{ + register int i; + register Char **vv; + + v++; + i = expr(&v); + vv = v; + if (*vv == NULL) + stderror(ERR_NAME | ERR_EMPTYIF); + if (eq(*vv, STRthen)) { + if (*++vv) + stderror(ERR_NAME | ERR_IMPRTHEN); + setname(vis_str(STRthen)); + /* + * If expression was zero, then scan to else, otherwise just fall into + * following code. + */ + if (!i) + search(T_IF, 0, NULL); + return; + } + /* + * Simple command attached to this if. Left shift the node in this tree, + * munging it so we can reexecute it. + */ + if (i) { + lshift(kp->t_dcom, vv - kp->t_dcom); + reexecute(kp); + donefds(); + } +} + +/* + * Reexecute a command, being careful not + * to redo i/o redirection, which is already set up. + */ +static void +reexecute(kp) + register struct command *kp; +{ + kp->t_dflg &= F_SAVE; + kp->t_dflg |= F_REPEAT; + /* + * If tty is still ours to arbitrate, arbitrate it; otherwise dont even set + * pgrp's as the jobs would then have no way to get the tty (we can't give + * it to them, and our parent wouldn't know their pgrp, etc. + */ + execute(kp, (tpgrp > 0 ? tpgrp : -1), NULL, NULL); +} + +void +/*ARGSUSED*/ +doelse(v, t) + Char **v; + struct command *t; +{ + search(T_ELSE, 0, NULL); +} + +void +/*ARGSUSED*/ +dogoto(v, t) + Char **v; + struct command *t; +{ + Char *lp; + + gotolab(lp = globone(v[1], G_ERROR)); + xfree((ptr_t) lp); +} + +void +gotolab(lab) + Char *lab; +{ + register struct whyle *wp; + /* + * While we still can, locate any unknown ends of existing loops. This + * obscure code is the WORST result of the fact that we don't really parse. + */ + zlast = T_GOTO; + for (wp = whyles; wp; wp = wp->w_next) + if (wp->w_end.type == F_SEEK && wp->w_end.f_seek == 0) { + search(T_BREAK, 0, NULL); + btell(&wp->w_end); + } + else + bseek(&wp->w_end); + search(T_GOTO, 0, lab); + /* + * Eliminate loops which were exited. + */ + wfree(); +} + +void +/*ARGSUSED*/ +doswitch(v, t) + Char **v; + struct command *t; +{ + register Char *cp, *lp; + + v++; + if (!*v || *(*v++) != '(') + stderror(ERR_SYNTAX); + cp = **v == ')' ? STRNULL : *v++; + if (*(*v++) != ')') + v--; + if (*v) + stderror(ERR_SYNTAX); + search(T_SWITCH, 0, lp = globone(cp, G_ERROR)); + xfree((ptr_t) lp); +} + +void +/*ARGSUSED*/ +dobreak(v, t) + Char **v; + struct command *t; +{ + if (whyles) + toend(); + else + stderror(ERR_NAME | ERR_NOTWHILE); +} + +void +/*ARGSUSED*/ +doexit(v, t) + Char **v; + struct command *t; +{ + if (chkstop == 0 && (intty || intact) && evalvec == 0) + panystop(0); + /* + * Don't DEMAND parentheses here either. + */ + v++; + if (*v) { + set(STRstatus, putn(expr(&v))); + if (*v) + stderror(ERR_NAME | ERR_EXPRESSION); + } + btoeof(); + if (intty) + (void) close(SHIN); +} + +void +/*ARGSUSED*/ +doforeach(v, t) + Char **v; + struct command *t; +{ + register Char *cp, *sp; + register struct whyle *nwp; + + v++; + sp = cp = strip(*v); + if (!letter(*sp)) + stderror(ERR_NAME | ERR_VARBEGIN); + while (*cp && alnum(*cp)) + cp++; + if (*cp) + stderror(ERR_NAME | ERR_VARALNUM); + if ((cp - sp) > MAXVARLEN) + stderror(ERR_NAME | ERR_VARTOOLONG); + cp = *v++; + if (v[0][0] != '(' || v[blklen(v) - 1][0] != ')') + stderror(ERR_NAME | ERR_NOPAREN); + v++; + gflag = 0, tglob(v); + v = globall(v); + if (v == 0) + stderror(ERR_NAME | ERR_NOMATCH); + nwp = (struct whyle *) xcalloc(1, sizeof *nwp); + nwp->w_fe = nwp->w_fe0 = v; + gargv = 0; + btell(&nwp->w_start); + nwp->w_fename = Strsave(cp); + nwp->w_next = whyles; + nwp->w_end.type = F_SEEK; + whyles = nwp; + /* + * Pre-read the loop so as to be more comprehensible to a terminal user. + */ + zlast = T_FOREACH; + if (intty) + preread(); + doagain(); +} + +void +/*ARGSUSED*/ +dowhile(v, t) + Char **v; + struct command *t; +{ + register int status; + register bool again = whyles != 0 && SEEKEQ(&whyles->w_start, &lineloc) && + whyles->w_fename == 0; + + v++; + /* + * Implement prereading here also, taking care not to evaluate the + * expression before the loop has been read up from a terminal. + */ + if (intty && !again) + status = !exp0(&v, 1); + else + status = !expr(&v); + if (*v) + stderror(ERR_NAME | ERR_EXPRESSION); + if (!again) { + register struct whyle *nwp = + (struct whyle *) xcalloc(1, sizeof(*nwp)); + + nwp->w_start = lineloc; + nwp->w_end.type = F_SEEK; + nwp->w_end.f_seek = 0; + nwp->w_next = whyles; + whyles = nwp; + zlast = T_WHILE; + if (intty) { + /* + * The tty preread + */ + preread(); + doagain(); + return; + } + } + if (status) + /* We ain't gonna loop no more, no more! */ + toend(); +} + +static void +preread() +{ + sigset_t sigset; + + whyles->w_end.type = I_SEEK; + if (setintr) { + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigprocmask(SIG_UNBLOCK, &sigset, NULL); + } + + search(T_BREAK, 0, NULL); /* read the expression in */ + if (setintr) + sigprocmask(SIG_BLOCK, &sigset, NULL); + btell(&whyles->w_end); +} + +void +/*ARGSUSED*/ +doend(v, t) + Char **v; + struct command *t; +{ + if (!whyles) + stderror(ERR_NAME | ERR_NOTWHILE); + btell(&whyles->w_end); + doagain(); +} + +void +/*ARGSUSED*/ +docontin(v, t) + Char **v; + struct command *t; +{ + if (!whyles) + stderror(ERR_NAME | ERR_NOTWHILE); + doagain(); +} + +static void +doagain() +{ + /* Repeating a while is simple */ + if (whyles->w_fename == 0) { + bseek(&whyles->w_start); + return; + } + /* + * The foreach variable list actually has a spurious word ")" at the end of + * the w_fe list. Thus we are at the of the list if one word beyond this + * is 0. + */ + if (!whyles->w_fe[1]) { + dobreak(NULL, NULL); + return; + } + set(whyles->w_fename, Strsave(*whyles->w_fe++)); + bseek(&whyles->w_start); +} + +void +dorepeat(v, kp) + Char **v; + struct command *kp; +{ + register int i; + sigset_t sigset; + + i = getn(v[1]); + if (setintr) { + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigprocmask(SIG_BLOCK, &sigset, NULL); + } + lshift(v, 2); + while (i > 0) { + if (setintr) + sigprocmask(SIG_UNBLOCK, &sigset, NULL); + reexecute(kp); + --i; + } + donefds(); + if (setintr) + sigprocmask(SIG_UNBLOCK, &sigset, NULL); +} + +void +/*ARGSUSED*/ +doswbrk(v, t) + Char **v; + struct command *t; +{ + search(T_BRKSW, 0, NULL); +} + +int +srchx(cp) + register Char *cp; +{ + register struct srch *sp, *sp1, *sp2; + register i; + + /* + * Binary search Sp1 is the beginning of the current search range. Sp2 is + * one past the end. + */ + for (sp1 = srchn, sp2 = srchn + nsrchn; sp1 < sp2;) { + sp = sp1 + ((sp2 - sp1) >> 1); + if ((i = *cp - *sp->s_name) == 0 && + (i = Strcmp(cp, str2short(sp->s_name))) == 0) + return sp->s_value; + if (i < 0) + sp2 = sp; + else + sp1 = sp + 1; + } + return (-1); +} + +static Char Stype; +static Char *Sgoal; + +/*VARARGS2*/ +static void +search(type, level, goal) + int type; + register int level; + Char *goal; +{ + Char wordbuf[BUFSIZ]; + register Char *aword = wordbuf; + register Char *cp; + + Stype = type; + Sgoal = goal; + if (type == T_GOTO) { + struct Ain a; + a.type = F_SEEK; + a.f_seek = 0; + bseek(&a); + } + do { + if (intty && fseekp == feobp && aret == F_SEEK) + (void) fprintf(cshout, "? "), (void) fflush(cshout); + aword[0] = 0; + (void) getword(aword); + switch (srchx(aword)) { + + case T_ELSE: + if (level == 0 && type == T_IF) + return; + break; + + case T_IF: + while (getword(aword)) + continue; + if ((type == T_IF || type == T_ELSE) && + eq(aword, STRthen)) + level++; + break; + + case T_ENDIF: + if (type == T_IF || type == T_ELSE) + level--; + break; + + case T_FOREACH: + case T_WHILE: + if (type == T_BREAK) + level++; + break; + + case T_END: + if (type == T_BREAK) + level--; + break; + + case T_SWITCH: + if (type == T_SWITCH || type == T_BRKSW) + level++; + break; + + case T_ENDSW: + if (type == T_SWITCH || type == T_BRKSW) + level--; + break; + + case T_LABEL: + if (type == T_GOTO && getword(aword) && eq(aword, goal)) + level = -1; + break; + + default: + if (type != T_GOTO && (type != T_SWITCH || level != 0)) + break; + if (lastchr(aword) != ':') + break; + aword[Strlen(aword) - 1] = 0; + if ((type == T_GOTO && eq(aword, goal)) || + (type == T_SWITCH && eq(aword, STRdefault))) + level = -1; + break; + + case T_CASE: + if (type != T_SWITCH || level != 0) + break; + (void) getword(aword); + if (lastchr(aword) == ':') + aword[Strlen(aword) - 1] = 0; + cp = strip(Dfix1(aword)); + if (Gmatch(goal, cp)) + level = -1; + xfree((ptr_t) cp); + break; + + case T_DEFAULT: + if (type == T_SWITCH && level == 0) + level = -1; + break; + } + (void) getword(NULL); + } while (level >= 0); +} + +static int +getword(wp) + register Char *wp; +{ + register int found = 0; + register int c, d; + int kwd = 0; + Char *owp = wp; + + c = readc(1); + d = 0; + do { + while (c == ' ' || c == '\t') + c = readc(1); + if (c == '#') + do + c = readc(1); + while (c >= 0 && c != '\n'); + if (c < 0) + goto past; + if (c == '\n') { + if (wp) + break; + return (0); + } + unreadc(c); + found = 1; + do { + c = readc(1); + if (c == '\\' && (c = readc(1)) == '\n') + c = ' '; + if (c == '\'' || c == '"') + if (d == 0) + d = c; + else if (d == c) + d = 0; + if (c < 0) + goto past; + if (wp) { + *wp++ = c; + *wp = 0; /* end the string b4 test */ + } + } while ((d || (!(kwd = keyword(owp)) && c != ' ' + && c != '\t')) && c != '\n'); + } while (wp == 0); + + /* + * if we have read a keyword ( "if", "switch" or "while" ) then we do not + * need to unreadc the look-ahead char + */ + if (!kwd) { + unreadc(c); + if (found) + *--wp = 0; + } + + return (found); + +past: + switch (Stype) { + + case T_IF: + stderror(ERR_NAME | ERR_NOTFOUND, "then/endif"); + + case T_ELSE: + stderror(ERR_NAME | ERR_NOTFOUND, "endif"); + + case T_BRKSW: + case T_SWITCH: + stderror(ERR_NAME | ERR_NOTFOUND, "endsw"); + + case T_BREAK: + stderror(ERR_NAME | ERR_NOTFOUND, "end"); + + case T_GOTO: + setname(vis_str(Sgoal)); + stderror(ERR_NAME | ERR_NOTFOUND, "label"); + } + /* NOTREACHED */ + return (0); +} + +/* + * keyword(wp) determines if wp is one of the built-n functions if, + * switch or while. It seems that when an if statement looks like + * "if(" then getword above sucks in the '(' and so the search routine + * never finds what it is scanning for. Rather than rewrite doword, I hack + * in a test to see if the string forms a keyword. Then doword stops + * and returns the word "if" -strike + */ + +static int +keyword(wp) + Char *wp; +{ + static Char STRif[] = {'i', 'f', '\0'}; + static Char STRwhile[] = {'w', 'h', 'i', 'l', 'e', '\0'}; + static Char STRswitch[] = {'s', 'w', 'i', 't', 'c', 'h', '\0'}; + + if (!wp) + return (0); + + if ((Strcmp(wp, STRif) == 0) || (Strcmp(wp, STRwhile) == 0) + || (Strcmp(wp, STRswitch) == 0)) + return (1); + + return (0); +} + +static void +toend() +{ + if (whyles->w_end.type == F_SEEK && whyles->w_end.f_seek == 0) { + search(T_BREAK, 0, NULL); + btell(&whyles->w_end); + whyles->w_end.f_seek--; + } + else + bseek(&whyles->w_end); + wfree(); +} + +void +wfree() +{ + struct Ain o; + struct whyle *nwp; + + btell(&o); + + for (; whyles; whyles = nwp) { + register struct whyle *wp = whyles; + nwp = wp->w_next; + + /* + * We free loops that have different seek types. + */ + if (wp->w_end.type != I_SEEK && wp->w_start.type == wp->w_end.type && + wp->w_start.type == o.type) { + if (wp->w_end.type == F_SEEK) { + if (o.f_seek >= wp->w_start.f_seek && + (wp->w_end.f_seek == 0 || o.f_seek < wp->w_end.f_seek)) + break; + } + else { + if (o.a_seek >= wp->w_start.a_seek && + (wp->w_end.a_seek == 0 || o.a_seek < wp->w_end.a_seek)) + break; + } + } + + if (wp->w_fe0) + blkfree(wp->w_fe0); + if (wp->w_fename) + xfree((ptr_t) wp->w_fename); + xfree((ptr_t) wp); + } +} + +void +/*ARGSUSED*/ +doecho(v, t) + Char **v; + struct command *t; +{ + xecho(' ', v); +} + +void +/*ARGSUSED*/ +doglob(v, t) + Char **v; + struct command *t; +{ + xecho(0, v); + (void) fflush(cshout); +} + +static void +xecho(sep, v) + int sep; + register Char **v; +{ + register Char *cp; + int nonl = 0; + sigset_t sigset; + + if (setintr) { + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigprocmask(SIG_UNBLOCK, &sigset, NULL); + } + v++; + if (*v == 0) + return; + gflag = 0, tglob(v); + if (gflag) { + v = globall(v); + if (v == 0) + stderror(ERR_NAME | ERR_NOMATCH); + } + else { + v = gargv = saveblk(v); + trim(v); + } + if (sep == ' ' && *v && eq(*v, STRmn)) + nonl++, v++; + while ((cp = *v++) != NULL) { + register int c; + + while ((c = *cp++) != '\0') + (void) vis_fputc(c | QUOTE, cshout); + + if (*v) + (void) vis_fputc(sep | QUOTE, cshout); + } + if (sep && nonl == 0) + (void) fputc('\n', cshout); + else + (void) fflush(cshout); + if (setintr) + sigprocmask(SIG_BLOCK, &sigset, NULL); + if (gargv) + blkfree(gargv), gargv = 0; +} + +void +/*ARGSUSED*/ +dosetenv(v, t) + Char **v; + struct command *t; +{ + Char *vp, *lp; + sigset_t sigset; + + v++; + if ((vp = *v++) == 0) { + register Char **ep; + + if (setintr) { + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigprocmask(SIG_UNBLOCK, &sigset, NULL); + } + for (ep = STR_environ; *ep; ep++) + (void) fprintf(cshout, "%s\n", vis_str(*ep)); + return; + } + if ((lp = *v++) == 0) + lp = STRNULL; + Setenv(vp, lp = globone(lp, G_APPEND)); + if (eq(vp, STRPATH)) { + importpath(lp); + dohash(NULL, NULL); + } + else if (eq(vp, STRLANG) || eq(vp, STRLC_CTYPE)) { +#ifdef NLS + int k; + + (void) setlocale(LC_ALL, ""); + for (k = 0200; k <= 0377 && !Isprint(k); k++) + continue; + AsciiOnly = k > 0377; +#else + AsciiOnly = 0; +#endif /* NLS */ + } + xfree((ptr_t) lp); +} + +void +/*ARGSUSED*/ +dounsetenv(v, t) + Char **v; + struct command *t; +{ + Char **ep, *p, *n; + int i, maxi; + static Char *name = NULL; + + if (name) + xfree((ptr_t) name); + /* + * Find the longest environment variable + */ + for (maxi = 0, ep = STR_environ; *ep; ep++) { + for (i = 0, p = *ep; *p && *p != '='; p++, i++) + continue; + if (i > maxi) + maxi = i; + } + + name = (Char *) xmalloc((size_t) (maxi + 1) * sizeof(Char)); + + while (++v && *v) + for (maxi = 1; maxi;) + for (maxi = 0, ep = STR_environ; *ep; ep++) { + for (n = name, p = *ep; *p && *p != '='; *n++ = *p++) + continue; + *n = '\0'; + if (!Gmatch(name, *v)) + continue; + maxi = 1; + if (eq(name, STRLANG) || eq(name, STRLC_CTYPE)) { +#ifdef NLS + int k; + + (void) setlocale(LC_ALL, ""); + for (k = 0200; k <= 0377 && !Isprint(k); k++) + continue; + AsciiOnly = k > 0377; +#else + AsciiOnly = getenv("LANG") == NULL && + getenv("LC_CTYPE") == NULL; +#endif /* NLS */ + } + /* + * Delete name, and start again cause the environment changes + */ + Unsetenv(name); + break; + } + xfree((ptr_t) name); + name = NULL; +} + +void +Setenv(name, val) + Char *name, *val; +{ + register Char **ep = STR_environ; + register Char *cp, *dp; + Char *blk[2]; + Char **oep = ep; + + + for (; *ep; ep++) { + for (cp = name, dp = *ep; *cp && *cp == *dp; cp++, dp++) + continue; + if (*cp != 0 || *dp != '=') + continue; + cp = Strspl(STRequal, val); + xfree((ptr_t) * ep); + *ep = strip(Strspl(name, cp)); + xfree((ptr_t) cp); + blkfree((Char **) environ); + environ = short2blk(STR_environ); + return; + } + cp = Strspl(name, STRequal); + blk[0] = strip(Strspl(cp, val)); + xfree((ptr_t) cp); + blk[1] = 0; + STR_environ = blkspl(STR_environ, blk); + blkfree((Char **) environ); + environ = short2blk(STR_environ); + xfree((ptr_t) oep); +} + +static void +Unsetenv(name) + Char *name; +{ + register Char **ep = STR_environ; + register Char *cp, *dp; + Char **oep = ep; + + for (; *ep; ep++) { + for (cp = name, dp = *ep; *cp && *cp == *dp; cp++, dp++) + continue; + if (*cp != 0 || *dp != '=') + continue; + cp = *ep; + *ep = 0; + STR_environ = blkspl(STR_environ, ep + 1); + environ = short2blk(STR_environ); + *ep = cp; + xfree((ptr_t) cp); + xfree((ptr_t) oep); + return; + } +} + +void +/*ARGSUSED*/ +doumask(v, t) + Char **v; + struct command *t; +{ + register Char *cp = v[1]; + register int i; + + if (cp == 0) { + i = umask(0); + (void) umask(i); + (void) fprintf(cshout, "%o\n", i); + return; + } + i = 0; + while (Isdigit(*cp) && *cp != '8' && *cp != '9') + i = i * 8 + *cp++ - '0'; + if (*cp || i < 0 || i > 0777) + stderror(ERR_NAME | ERR_MASK); + (void) umask(i); +} + +typedef quad_t RLIM_TYPE; + +static struct limits { + int limconst; + char *limname; + int limdiv; + char *limscale; +} limits[] = { + { RLIMIT_CPU, "cputime", 1, "seconds" }, + { RLIMIT_FSIZE, "filesize", 1024, "kbytes" }, + { RLIMIT_DATA, "datasize", 1024, "kbytes" }, + { RLIMIT_STACK, "stacksize", 1024, "kbytes" }, + { RLIMIT_CORE, "coredumpsize", 1024, "kbytes" }, + { RLIMIT_RSS, "memoryuse", 1024, "kbytes" }, + { RLIMIT_MEMLOCK, "memorylocked", 1024, "kbytes" }, + { RLIMIT_NPROC, "maxproc", 1, "" }, + { RLIMIT_NOFILE, "openfiles", 1, "" }, + { -1, NULL, 0, NULL } +}; + +static struct limits *findlim(); +static RLIM_TYPE getval(); +static void limtail(); +static void plim(); +static int setlim(); + +static struct limits * +findlim(cp) + Char *cp; +{ + register struct limits *lp, *res; + + res = (struct limits *) NULL; + for (lp = limits; lp->limconst >= 0; lp++) + if (prefix(cp, str2short(lp->limname))) { + if (res) + stderror(ERR_NAME | ERR_AMBIG); + res = lp; + } + if (res) + return (res); + stderror(ERR_NAME | ERR_LIMIT); + /* NOTREACHED */ + return (0); +} + +void +/*ARGSUSED*/ +dolimit(v, t) + Char **v; + struct command *t; +{ + register struct limits *lp; + register RLIM_TYPE limit; + char hard = 0; + + v++; + if (*v && eq(*v, STRmh)) { + hard = 1; + v++; + } + if (*v == 0) { + for (lp = limits; lp->limconst >= 0; lp++) + plim(lp, hard); + return; + } + lp = findlim(v[0]); + if (v[1] == 0) { + plim(lp, hard); + return; + } + limit = getval(lp, v + 1); + if (setlim(lp, hard, limit) < 0) + stderror(ERR_SILENT); +} + +static RLIM_TYPE +getval(lp, v) + register struct limits *lp; + Char **v; +{ + register float f; + double atof(); + Char *cp = *v++; + + f = atof(short2str(cp)); + + while (Isdigit(*cp) || *cp == '.' || *cp == 'e' || *cp == 'E') + cp++; + if (*cp == 0) { + if (*v == 0) + return ((RLIM_TYPE) ((f + 0.5) * lp->limdiv)); + cp = *v; + } + switch (*cp) { + case ':': + if (lp->limconst != RLIMIT_CPU) + goto badscal; + return ((RLIM_TYPE) (f * 60.0 + atof(short2str(cp + 1)))); + case 'h': + if (lp->limconst != RLIMIT_CPU) + goto badscal; + limtail(cp, "hours"); + f *= 3600.0; + break; + case 'm': + if (lp->limconst == RLIMIT_CPU) { + limtail(cp, "minutes"); + f *= 60.0; + break; + } + *cp = 'm'; + limtail(cp, "megabytes"); + f *= 1024.0 * 1024.0; + break; + case 's': + if (lp->limconst != RLIMIT_CPU) + goto badscal; + limtail(cp, "seconds"); + break; + case 'M': + if (lp->limconst == RLIMIT_CPU) + goto badscal; + *cp = 'm'; + limtail(cp, "megabytes"); + f *= 1024.0 * 1024.0; + break; + case 'k': + if (lp->limconst == RLIMIT_CPU) + goto badscal; + limtail(cp, "kbytes"); + f *= 1024.0; + break; + case 'u': + limtail(cp, "unlimited"); + return (RLIM_INFINITY); + default: +badscal: + stderror(ERR_NAME | ERR_SCALEF); + } + f += 0.5; + if (f > (float) RLIM_INFINITY) + return RLIM_INFINITY; + else + return ((RLIM_TYPE) f); +} + +static void +limtail(cp, str) + Char *cp; + char *str; +{ + while (*cp && *cp == *str) + cp++, str++; + if (*cp) + stderror(ERR_BADSCALE, str); +} + + +/*ARGSUSED*/ +static void +plim(lp, hard) + register struct limits *lp; + Char hard; +{ + struct rlimit rlim; + RLIM_TYPE limit; + + (void) fprintf(cshout, "%s \t", lp->limname); + + (void) getrlimit(lp->limconst, &rlim); + limit = hard ? rlim.rlim_max : rlim.rlim_cur; + + if (limit == RLIM_INFINITY) + (void) fprintf(cshout, "unlimited"); + else if (lp->limconst == RLIMIT_CPU) + psecs((long) limit); + else + (void) fprintf(cshout, "%ld %s", (long) (limit / lp->limdiv), + lp->limscale); + (void) fputc('\n', cshout); +} + +void +/*ARGSUSED*/ +dounlimit(v, t) + Char **v; + struct command *t; +{ + register struct limits *lp; + int lerr = 0; + Char hard = 0; + + v++; + if (*v && eq(*v, STRmh)) { + hard = 1; + v++; + } + if (*v == 0) { + for (lp = limits; lp->limconst >= 0; lp++) + if (setlim(lp, hard, (RLIM_TYPE) RLIM_INFINITY) < 0) + lerr++; + if (lerr) + stderror(ERR_SILENT); + return; + } + while (*v) { + lp = findlim(*v++); + if (setlim(lp, hard, (RLIM_TYPE) RLIM_INFINITY) < 0) + stderror(ERR_SILENT); + } +} + +static int +setlim(lp, hard, limit) + register struct limits *lp; + Char hard; + RLIM_TYPE limit; +{ + struct rlimit rlim; + + (void) getrlimit(lp->limconst, &rlim); + + if (hard) + rlim.rlim_max = limit; + else if (limit == RLIM_INFINITY && geteuid() != 0) + rlim.rlim_cur = rlim.rlim_max; + else + rlim.rlim_cur = limit; + + if (setrlimit(lp->limconst, &rlim) < 0) { + (void) fprintf(csherr, "%s: %s: Can't %s%s limit\n", bname, lp->limname, + limit == RLIM_INFINITY ? "remove" : "set", + hard ? " hard" : ""); + return (-1); + } + return (0); +} + +void +/*ARGSUSED*/ +dosuspend(v, t) + Char **v; + struct command *t; +{ + int ctpgrp; + + void (*old) (); + + if (loginsh) + stderror(ERR_SUSPLOG); + untty(); + + old = signal(SIGTSTP, SIG_DFL); + (void) kill(0, SIGTSTP); + /* the shell stops here */ + (void) signal(SIGTSTP, old); + + if (tpgrp != -1) { + ctpgrp = tcgetpgrp(FSHTTY); + while (ctpgrp != opgrp) { + old = signal(SIGTTIN, SIG_DFL); + (void) kill(0, SIGTTIN); + (void) signal(SIGTTIN, old); + } + (void) setpgid(0, shpgrp); + (void) tcsetpgrp(FSHTTY, shpgrp); + } +} + +/* This is the dreaded EVAL built-in. + * If you don't fiddle with file descriptors, and reset didfds, + * this command will either ignore redirection inside or outside + * its aguments, e.g. eval "date >x" vs. eval "date" >x + * The stuff here seems to work, but I did it by trial and error rather + * than really knowing what was going on. If tpgrp is zero, we are + * probably a background eval, e.g. "eval date &", and we want to + * make sure that any processes we start stay in our pgrp. + * This is also the case for "time eval date" -- stay in same pgrp. + * Otherwise, under stty tostop, processes will stop in the wrong + * pgrp, with no way for the shell to get them going again. -IAN! + */ +static Char **gv = NULL; +void +/*ARGSUSED*/ +doeval(v, t) + Char **v; + struct command *t; +{ + Char **oevalvec; + Char *oevalp; + int odidfds; + jmp_buf osetexit; + int my_reenter; + Char **savegv = gv; + int saveIN; + int saveOUT; + int saveERR; + int oSHIN; + int oSHOUT; + int oSHERR; + + UNREGISTER(v); + + oevalvec = evalvec; + oevalp = evalp; + odidfds = didfds; + oSHIN = SHIN; + oSHOUT = SHOUT; + oSHERR = SHERR; + + v++; + if (*v == 0) + return; + gflag = 0, tglob(v); + if (gflag) { + gv = v = globall(v); + gargv = 0; + if (v == 0) + stderror(ERR_NOMATCH); + v = copyblk(v); + } + else { + gv = NULL; + v = copyblk(v); + trim(v); + } + + saveIN = dcopy(SHIN, -1); + saveOUT = dcopy(SHOUT, -1); + saveERR = dcopy(SHERR, -1); + + getexit(osetexit); + + if ((my_reenter = setexit()) == 0) { + evalvec = v; + evalp = 0; + SHIN = dcopy(0, -1); + SHOUT = dcopy(1, -1); + SHERR = dcopy(2, -1); + didfds = 0; + process(0); + } + + evalvec = oevalvec; + evalp = oevalp; + doneinp = 0; + didfds = odidfds; + (void) close(SHIN); + (void) close(SHOUT); + (void) close(SHERR); + SHIN = dmove(saveIN, oSHIN); + SHOUT = dmove(saveOUT, oSHOUT); + SHERR = dmove(saveERR, oSHERR); + if (gv) + blkfree(gv), gv = NULL; + resexit(osetexit); + gv = savegv; + if (my_reenter) + stderror(ERR_SILENT); +} + +void +/*ARGSUSED*/ +doprintf(v, t) + Char **v; + struct command *t; +{ + char **c; + extern int progprintf __P((int, char **)); + int ret; + + ret = progprintf(blklen(v), c = short2blk(v)); + (void) fflush(cshout); + (void) fflush(csherr); + + blkfree((Char **) c); + if (ret) + stderror(ERR_SILENT); +} diff --git a/bin/csh/hist.c b/bin/csh/hist.c new file mode 100644 index 000000000000..673cf543fd04 --- /dev/null +++ b/bin/csh/hist.c @@ -0,0 +1,188 @@ +/*- + * Copyright (c) 1980, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)hist.c 8.2 (Berkeley) 3/22/95"; +#endif /* not lint */ + +#include +#include +#if __STDC__ +# include +#else +# include +#endif + +#include "csh.h" +#include "extern.h" + +static void hfree __P((struct Hist *)); +static void dohist1 __P((struct Hist *, int *, int, int)); +static void phist __P((struct Hist *, int)); + +void +savehist(sp) + struct wordent *sp; +{ + register struct Hist *hp, *np; + register int histlen = 0; + Char *cp; + + /* throw away null lines */ + if (sp->next->word[0] == '\n') + return; + cp = value(STRhistory); + if (*cp) { + register Char *p = cp; + + while (*p) { + if (!Isdigit(*p)) { + histlen = 0; + break; + } + histlen = histlen * 10 + *p++ - '0'; + } + } + for (hp = &Histlist; (np = hp->Hnext) != NULL;) + if (eventno - np->Href >= histlen || histlen == 0) + hp->Hnext = np->Hnext, hfree(np); + else + hp = np; + (void) enthist(++eventno, sp, 1); +} + +struct Hist * +enthist(event, lp, docopy) + int event; + register struct wordent *lp; + bool docopy; +{ + register struct Hist *np; + + np = (struct Hist *) xmalloc((size_t) sizeof(*np)); + np->Hnum = np->Href = event; + if (docopy) { + copylex(&np->Hlex, lp); + } + else { + np->Hlex.next = lp->next; + lp->next->prev = &np->Hlex; + np->Hlex.prev = lp->prev; + lp->prev->next = &np->Hlex; + } + np->Hnext = Histlist.Hnext; + Histlist.Hnext = np; + return (np); +} + +static void +hfree(hp) + register struct Hist *hp; +{ + + freelex(&hp->Hlex); + xfree((ptr_t) hp); +} + +void +/*ARGSUSED*/ +dohist(v, t) + Char **v; + struct command *t; +{ + int n, rflg = 0, hflg = 0; + sigset_t sigset; + + if (getn(value(STRhistory)) == 0) + return; + if (setintr) { + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigprocmask(SIG_UNBLOCK, &sigset, NULL); + } + while (*++v && **v == '-') { + Char *vp = *v; + + while (*++vp) + switch (*vp) { + case 'h': + hflg++; + break; + case 'r': + rflg++; + break; + case '-': /* ignore multiple '-'s */ + break; + default: + stderror(ERR_HISTUS); + break; + } + } + if (*v) + n = getn(*v); + else { + n = getn(value(STRhistory)); + } + dohist1(Histlist.Hnext, &n, rflg, hflg); +} + +static void +dohist1(hp, np, rflg, hflg) + struct Hist *hp; + int *np, rflg, hflg; +{ + bool print = (*np) > 0; + + for (; hp != 0; hp = hp->Hnext) { + (*np)--; + hp->Href++; + if (rflg == 0) { + dohist1(hp->Hnext, np, rflg, hflg); + if (print) + phist(hp, hflg); + return; + } + if (*np >= 0) + phist(hp, hflg); + } +} + +static void +phist(hp, hflg) + register struct Hist *hp; + int hflg; +{ + if (hflg == 0) + (void) fprintf(cshout, "%6d\t", hp->Hnum); + prlex(cshout, &hp->Hlex); +} diff --git a/bin/csh/lex.c b/bin/csh/lex.c new file mode 100644 index 000000000000..36791f99bdc3 --- /dev/null +++ b/bin/csh/lex.c @@ -0,0 +1,1639 @@ +/*- + * Copyright (c) 1980, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)lex.c 8.2 (Berkeley) 4/29/95"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#if __STDC__ +# include +#else +# include +#endif + +#include "csh.h" +#include "extern.h" + +/* + * These lexical routines read input and form lists of words. + * There is some involved processing here, because of the complications + * of input buffering, and especially because of history substitution. + */ + +static Char *word __P((void)); +static int getC1 __P((int)); +static void getdol __P((void)); +static void getexcl __P((int)); +static struct Hist + *findev __P((Char *, bool)); +static void setexclp __P((Char *)); +static int bgetc __P((void)); +static void bfree __P((void)); +static struct wordent + *gethent __P((int)); +static int matchs __P((Char *, Char *)); +static int getsel __P((int *, int *, int)); +static struct wordent + *getsub __P((struct wordent *)); +static Char *subword __P((Char *, int, bool *)); +static struct wordent + *dosub __P((int, struct wordent *, bool)); + +/* + * Peekc is a peek character for getC, peekread for readc. + * There is a subtlety here in many places... history routines + * will read ahead and then insert stuff into the input stream. + * If they push back a character then they must push it behind + * the text substituted by the history substitution. On the other + * hand in several places we need 2 peek characters. To make this + * all work, the history routines read with getC, and make use both + * of ungetC and unreadc. The key observation is that the state + * of getC at the call of a history reference is such that calls + * to getC from the history routines will always yield calls of + * readc, unless this peeking is involved. That is to say that during + * getexcl the variables lap, exclp, and exclnxt are all zero. + * + * Getdol invokes history substitution, hence the extra peek, peekd, + * which it can ungetD to be before history substitutions. + */ +static Char peekc = 0, peekd = 0; +static Char peekread = 0; + +/* (Tail of) current word from ! subst */ +static Char *exclp = NULL; + +/* The rest of the ! subst words */ +static struct wordent *exclnxt = NULL; + +/* Count of remaining words in ! subst */ +static int exclc = 0; + +/* "Globp" for alias resubstitution */ +Char *alvecp = NULL; +int aret = F_SEEK; + +/* + * Labuf implements a general buffer for lookahead during lexical operations. + * Text which is to be placed in the input stream can be stuck here. + * We stick parsed ahead $ constructs during initial input, + * process id's from `$$', and modified variable values (from qualifiers + * during expansion in sh.dol.c) here. + */ +static Char labuf[BUFSIZ]; + +/* + * Lex returns to its caller not only a wordlist (as a "var" parameter) + * but also whether a history substitution occurred. This is used in + * the main (process) routine to determine whether to echo, and also + * when called by the alias routine to determine whether to keep the + * argument list. + */ +static bool hadhist = 0; + +/* + * Avoid alias expansion recursion via \!# + */ +int hleft; + +static Char getCtmp; + +#define getC(f) ((getCtmp = peekc) ? (peekc = 0, getCtmp) : getC1(f)) +#define ungetC(c) peekc = c +#define ungetD(c) peekd = c + +int +lex(hp) + register struct wordent *hp; +{ + register struct wordent *wdp; + int c; + + btell(&lineloc); + hp->next = hp->prev = hp; + hp->word = STRNULL; + hadhist = 0; + do + c = readc(0); + while (c == ' ' || c == '\t'); + if (c == HISTSUB && intty) + /* ^lef^rit from tty is short !:s^lef^rit */ + getexcl(c); + else + unreadc(c); + wdp = hp; + /* + * The following loop is written so that the links needed by freelex will + * be ready and rarin to go even if it is interrupted. + */ + do { + register struct wordent *new; + + new = (struct wordent *) xmalloc((size_t) sizeof(*wdp)); + new->word = 0; + new->prev = wdp; + new->next = hp; + wdp->next = new; + wdp = new; + wdp->word = word(); + } while (wdp->word[0] != '\n'); + hp->prev = wdp; + return (hadhist); +} + +void +prlex(fp, sp0) + FILE *fp; + struct wordent *sp0; +{ + register struct wordent *sp = sp0->next; + + for (;;) { + (void) fprintf(fp, "%s", vis_str(sp->word)); + sp = sp->next; + if (sp == sp0) + break; + if (sp->word[0] != '\n') + (void) fputc(' ', fp); + } +} + +void +copylex(hp, fp) + register struct wordent *hp; + register struct wordent *fp; +{ + register struct wordent *wdp; + + wdp = hp; + fp = fp->next; + do { + register struct wordent *new; + + new = (struct wordent *) xmalloc((size_t) sizeof(*wdp)); + new->prev = wdp; + new->next = hp; + wdp->next = new; + wdp = new; + wdp->word = Strsave(fp->word); + fp = fp->next; + } while (wdp->word[0] != '\n'); + hp->prev = wdp; +} + +void +freelex(vp) + register struct wordent *vp; +{ + register struct wordent *fp; + + while (vp->next != vp) { + fp = vp->next; + vp->next = fp->next; + xfree((ptr_t) fp->word); + xfree((ptr_t) fp); + } + vp->prev = vp; +} + +static Char * +word() +{ + register Char c, c1; + register Char *wp; + Char wbuf[BUFSIZ]; + register bool dolflg; + register int i; + + wp = wbuf; + i = BUFSIZ - 4; +loop: + while ((c = getC(DOALL)) == ' ' || c == '\t') + continue; + if (cmap(c, _META | _ESC)) + switch (c) { + case '&': + case '|': + case '<': + case '>': + *wp++ = c; + c1 = getC(DOALL); + if (c1 == c) + *wp++ = c1; + else + ungetC(c1); + goto ret; + + case '#': + if (intty) + break; + c = 0; + do { + c1 = c; + c = getC(0); + } while (c != '\n'); + if (c1 == '\\') + goto loop; + /* fall into ... */ + + case ';': + case '(': + case ')': + case '\n': + *wp++ = c; + goto ret; + + case '\\': + c = getC(0); + if (c == '\n') { + if (onelflg == 1) + onelflg = 2; + goto loop; + } + if (c != HIST) + *wp++ = '\\', --i; + c |= QUOTE; + } + c1 = 0; + dolflg = DOALL; + for (;;) { + if (c1) { + if (c == c1) { + c1 = 0; + dolflg = DOALL; + } + else if (c == '\\') { + c = getC(0); + if (c == HIST) + c |= QUOTE; + else { + if (c == '\n') + /* + * if (c1 == '`') c = ' '; else + */ + c |= QUOTE; + ungetC(c); + c = '\\'; + } + } + else if (c == '\n') { + seterror(ERR_UNMATCHED, c1); + ungetC(c); + break; + } + } + else if (cmap(c, _META | _QF | _QB | _ESC)) { + if (c == '\\') { + c = getC(0); + if (c == '\n') { + if (onelflg == 1) + onelflg = 2; + break; + } + if (c != HIST) + *wp++ = '\\', --i; + c |= QUOTE; + } + else if (cmap(c, _QF | _QB)) { /* '"` */ + c1 = c; + dolflg = c == '"' ? DOALL : DOEXCL; + } + else if (c != '#' || !intty) { + ungetC(c); + break; + } + } + if (--i > 0) { + *wp++ = c; + c = getC(dolflg); + } + else { + seterror(ERR_WTOOLONG); + wp = &wbuf[1]; + break; + } + } +ret: + *wp = 0; + return (Strsave(wbuf)); +} + +static int +getC1(flag) + register int flag; +{ + register Char c; + + while (1) { + if ((c = peekc) != '\0') { + peekc = 0; + return (c); + } + if (lap) { + if ((c = *lap++) == 0) + lap = 0; + else { + if (cmap(c, _META | _QF | _QB)) + c |= QUOTE; + return (c); + } + } + if ((c = peekd) != '\0') { + peekd = 0; + return (c); + } + if (exclp) { + if ((c = *exclp++) != '\0') + return (c); + if (exclnxt && --exclc >= 0) { + exclnxt = exclnxt->next; + setexclp(exclnxt->word); + return (' '); + } + exclp = 0; + exclnxt = 0; + } + if (exclnxt) { + exclnxt = exclnxt->next; + if (--exclc < 0) + exclnxt = 0; + else + setexclp(exclnxt->word); + continue; + } + c = readc(0); + if (c == '$' && (flag & DODOL)) { + getdol(); + continue; + } + if (c == HIST && (flag & DOEXCL)) { + getexcl(0); + continue; + } + break; + } + return (c); +} + +static void +getdol() +{ + register Char *np, *ep; + Char name[4 * MAXVARLEN + 1]; + register int c; + int sc; + bool special = 0, toolong; + + np = name, *np++ = '$'; + c = sc = getC(DOEXCL); + if (any("\t \n", c)) { + ungetD(c); + ungetC('$' | QUOTE); + return; + } + if (c == '{') + *np++ = c, c = getC(DOEXCL); + if (c == '#' || c == '?') + special++, *np++ = c, c = getC(DOEXCL); + *np++ = c; + switch (c) { + + case '<': + case '$': + case '!': + if (special) + seterror(ERR_SPDOLLT); + *np = 0; + addla(name); + return; + + case '\n': + ungetD(c); + np--; + seterror(ERR_NEWLINE); + *np = 0; + addla(name); + return; + + case '*': + if (special) + seterror(ERR_SPSTAR); + *np = 0; + addla(name); + return; + + default: + toolong = 0; + if (Isdigit(c)) { +#ifdef notdef + /* let $?0 pass for now */ + if (special) { + seterror(ERR_DIGIT); + *np = 0; + addla(name); + return; + } +#endif + /* we know that np < &name[4] */ + ep = &np[MAXVARLEN]; + while ((c = getC(DOEXCL)) != '\0'){ + if (!Isdigit(c)) + break; + if (np < ep) + *np++ = c; + else + toolong = 1; + } + } + else if (letter(c)) { + /* we know that np < &name[4] */ + ep = &np[MAXVARLEN]; + toolong = 0; + while ((c = getC(DOEXCL)) != '\0') { + /* Bugfix for ${v123x} from Chris Torek, DAS DEC-90. */ + if (!letter(c) && !Isdigit(c)) + break; + if (np < ep) + *np++ = c; + else + toolong = 1; + } + } + else { + *np = 0; + seterror(ERR_VARILL); + addla(name); + return; + } + if (toolong) { + seterror(ERR_VARTOOLONG); + *np = 0; + addla(name); + return; + } + break; + } + if (c == '[') { + *np++ = c; + /* + * Name up to here is a max of MAXVARLEN + 8. + */ + ep = &np[2 * MAXVARLEN + 8]; + do { + /* + * Michael Greim: Allow $ expansion to take place in selector + * expressions. (limits the number of characters returned) + */ + c = getC(DOEXCL | DODOL); + if (c == '\n') { + ungetD(c); + np--; + seterror(ERR_NLINDEX); + *np = 0; + addla(name); + return; + } + if (np < ep) + *np++ = c; + } while (c != ']'); + *np = '\0'; + if (np >= ep) { + seterror(ERR_SELOVFL); + addla(name); + return; + } + c = getC(DOEXCL); + } + /* + * Name up to here is a max of 2 * MAXVARLEN + 8. + */ + if (c == ':') { + /* + * if the :g modifier is followed by a newline, then error right away! + * -strike + */ + + int gmodflag = 0, amodflag = 0; + + do { + *np++ = c, c = getC(DOEXCL); + if (c == 'g' || c == 'a') { + if (c == 'g') + gmodflag++; + else + amodflag++; + *np++ = c; c = getC(DOEXCL); + } + if ((c == 'g' && !gmodflag) || (c == 'a' && !amodflag)) { + if (c == 'g') + gmodflag++; + else + amodflag++; + *np++ = c; c = getC(DOEXCL); + } + *np++ = c; + /* scan s// [eichin:19910926.0512EST] */ + if (c == 's') { + int delimcnt = 2; + int delim = getC(0); + *np++ = delim; + + if (!delim || letter(delim) + || Isdigit(delim) || any(" \t\n", delim)) { + seterror(ERR_BADSUBST); + break; + } + while ((c = getC(0)) != (-1)) { + *np++ = c; + if(c == delim) delimcnt--; + if(!delimcnt) break; + } + if(delimcnt) { + seterror(ERR_BADSUBST); + break; + } + c = 's'; + } + if (!any("htrqxes", c)) { + if ((amodflag || gmodflag) && c == '\n') + stderror(ERR_VARSYN); /* strike */ + seterror(ERR_VARMOD, c); + *np = 0; + addla(name); + return; + } + } + while ((c = getC(DOEXCL)) == ':'); + ungetD(c); + } + else + ungetD(c); + if (sc == '{') { + c = getC(DOEXCL); + if (c != '}') { + ungetD(c); + seterror(ERR_MISSING, '}'); + *np = 0; + addla(name); + return; + } + *np++ = c; + } + *np = 0; + addla(name); + return; +} + +void +addla(cp) + Char *cp; +{ + Char buf[BUFSIZ]; + + if (Strlen(cp) + (lap ? Strlen(lap) : 0) >= + (sizeof(labuf) - 4) / sizeof(Char)) { + seterror(ERR_EXPOVFL); + return; + } + if (lap) + (void) Strcpy(buf, lap); + (void) Strcpy(labuf, cp); + if (lap) + (void) Strcat(labuf, buf); + lap = labuf; +} + +static Char lhsb[32]; +static Char slhs[32]; +static Char rhsb[64]; +static int quesarg; + +static void +getexcl(sc) + int sc; +{ + register struct wordent *hp, *ip; + int left, right, dol; + register int c; + + if (sc == 0) { + sc = getC(0); + if (sc != '{') { + ungetC(sc); + sc = 0; + } + } + quesarg = -1; + lastev = eventno; + hp = gethent(sc); + if (hp == 0) + return; + hadhist = 1; + dol = 0; + if (hp == alhistp) + for (ip = hp->next->next; ip != alhistt; ip = ip->next) + dol++; + else + for (ip = hp->next->next; ip != hp->prev; ip = ip->next) + dol++; + left = 0, right = dol; + if (sc == HISTSUB) { + ungetC('s'), unreadc(HISTSUB), c = ':'; + goto subst; + } + c = getC(0); + if (!any(":^$*-%", c)) + goto subst; + left = right = -1; + if (c == ':') { + c = getC(0); + unreadc(c); + if (letter(c) || c == '&') { + c = ':'; + left = 0, right = dol; + goto subst; + } + } + else + ungetC(c); + if (!getsel(&left, &right, dol)) + return; + c = getC(0); + if (c == '*') + ungetC(c), c = '-'; + if (c == '-') { + if (!getsel(&left, &right, dol)) + return; + c = getC(0); + } +subst: + exclc = right - left + 1; + while (--left >= 0) + hp = hp->next; + if (sc == HISTSUB || c == ':') { + do { + hp = getsub(hp); + c = getC(0); + } while (c == ':'); + } + unreadc(c); + if (sc == '{') { + c = getC(0); + if (c != '}') + seterror(ERR_BADBANG); + } + exclnxt = hp; +} + +static struct wordent * +getsub(en) + struct wordent *en; +{ + register Char *cp; + int delim; + register int c; + int sc; + bool global; + Char orhsb[sizeof(rhsb) / sizeof(Char)]; + + do { + exclnxt = 0; + global = 0; + sc = c = getC(0); + if (c == 'g' || c == 'a') { + global |= (c == 'g') ? 1 : 2; + sc = c = getC(0); + } + if (((c =='g') && !(global & 1)) || ((c == 'a') && !(global & 2))) { + global |= (c == 'g') ? 1 : 2; + sc = c = getC(0); + } + + switch (c) { + case 'p': + justpr++; + return (en); + + case 'x': + case 'q': + global |= 1; + + /* fall into ... */ + + case 'h': + case 'r': + case 't': + case 'e': + break; + + case '&': + if (slhs[0] == 0) { + seterror(ERR_NOSUBST); + return (en); + } + (void) Strcpy(lhsb, slhs); + break; + +#ifdef notdef + case '~': + if (lhsb[0] == 0) + goto badlhs; + break; +#endif + + case 's': + delim = getC(0); + if (letter(delim) || Isdigit(delim) || any(" \t\n", delim)) { + unreadc(delim); + lhsb[0] = 0; + seterror(ERR_BADSUBST); + return (en); + } + cp = lhsb; + for (;;) { + c = getC(0); + if (c == '\n') { + unreadc(c); + break; + } + if (c == delim) + break; + if (cp > &lhsb[sizeof(lhsb) / sizeof(Char) - 2]) { + lhsb[0] = 0; + seterror(ERR_BADSUBST); + return (en); + } + if (c == '\\') { + c = getC(0); + if (c != delim && c != '\\') + *cp++ = '\\'; + } + *cp++ = c; + } + if (cp != lhsb) + *cp++ = 0; + else if (lhsb[0] == 0) { + seterror(ERR_LHS); + return (en); + } + cp = rhsb; + (void) Strcpy(orhsb, cp); + for (;;) { + c = getC(0); + if (c == '\n') { + unreadc(c); + break; + } + if (c == delim) + break; +#ifdef notdef + if (c == '~') { + if (&cp[Strlen(orhsb)] > &rhsb[sizeof(rhsb) / + sizeof(Char) - 2]) + goto toorhs; + (void) Strcpy(cp, orhsb); + cp = Strend(cp); + continue; + } +#endif + if (cp > &rhsb[sizeof(rhsb) / sizeof(Char) - 2]) { + seterror(ERR_RHSLONG); + return (en); + } + if (c == '\\') { + c = getC(0); + if (c != delim /* && c != '~' */ ) + *cp++ = '\\'; + } + *cp++ = c; + } + *cp++ = 0; + break; + + default: + if (c == '\n') + unreadc(c); + seterror(ERR_BADBANGMOD, c); + return (en); + } + (void) Strcpy(slhs, lhsb); + if (exclc) + en = dosub(sc, en, global); + } + while ((c = getC(0)) == ':'); + unreadc(c); + return (en); +} + +static struct wordent * +dosub(sc, en, global) + int sc; + struct wordent *en; + bool global; +{ + struct wordent lexi; + bool didsub = 0, didone = 0; + struct wordent *hp = &lexi; + register struct wordent *wdp; + register int i = exclc; + + wdp = hp; + while (--i >= 0) { + register struct wordent *new = + (struct wordent *) xcalloc(1, sizeof *wdp); + + new->word = 0; + new->prev = wdp; + new->next = hp; + wdp->next = new; + wdp = new; + en = en->next; + if (en->word) { + Char *tword, *otword; + + if ((global & 1) || didsub == 0) { + tword = subword(en->word, sc, &didone); + if (didone) + didsub = 1; + if (global & 2) { + while (didone && tword != STRNULL) { + otword = tword; + tword = subword(otword, sc, &didone); + if (Strcmp(tword, otword) == 0) { + xfree((ptr_t) otword); + break; + } + else + xfree((ptr_t) otword); + } + } + } + else + tword = Strsave(en->word); + wdp->word = tword; + } + } + if (didsub == 0) + seterror(ERR_MODFAIL); + hp->prev = wdp; + return (&enthist(-1000, &lexi, 0)->Hlex); +} + +static Char * +subword(cp, type, adid) + Char *cp; + int type; + bool *adid; +{ + Char wbuf[BUFSIZ]; + register Char *wp, *mp, *np; + register int i; + + *adid = 0; + switch (type) { + + case 'r': + case 'e': + case 'h': + case 't': + case 'q': + case 'x': + wp = domod(cp, type); + if (wp == 0) + return (Strsave(cp)); + *adid = 1; + return (wp); + + default: + wp = wbuf; + i = BUFSIZ - 4; + for (mp = cp; *mp; mp++) + if (matchs(mp, lhsb)) { + for (np = cp; np < mp;) + *wp++ = *np++, --i; + for (np = rhsb; *np; np++) + switch (*np) { + + case '\\': + if (np[1] == '&') + np++; + /* fall into ... */ + + default: + if (--i < 0) { + seterror(ERR_SUBOVFL); + return (STRNULL); + } + *wp++ = *np; + continue; + + case '&': + i -= Strlen(lhsb); + if (i < 0) { + seterror(ERR_SUBOVFL); + return (STRNULL); + } + *wp = 0; + (void) Strcat(wp, lhsb); + wp = Strend(wp); + continue; + } + mp += Strlen(lhsb); + i -= Strlen(mp); + if (i < 0) { + seterror(ERR_SUBOVFL); + return (STRNULL); + } + *wp = 0; + (void) Strcat(wp, mp); + *adid = 1; + return (Strsave(wbuf)); + } + return (Strsave(cp)); + } +} + +Char * +domod(cp, type) + Char *cp; + int type; +{ + register Char *wp, *xp; + register int c; + + switch (type) { + + case 'x': + case 'q': + wp = Strsave(cp); + for (xp = wp; (c = *xp) != '\0'; xp++) + if ((c != ' ' && c != '\t') || type == 'q') + *xp |= QUOTE; + return (wp); + + case 'h': + case 't': + if (!any(short2str(cp), '/')) + return (type == 't' ? Strsave(cp) : 0); + wp = Strend(cp); + while (*--wp != '/') + continue; + if (type == 'h') + xp = Strsave(cp), xp[wp - cp] = 0; + else + xp = Strsave(wp + 1); + return (xp); + + case 'e': + case 'r': + wp = Strend(cp); + for (wp--; wp >= cp && *wp != '/'; wp--) + if (*wp == '.') { + if (type == 'e') + xp = Strsave(wp + 1); + else + xp = Strsave(cp), xp[wp - cp] = 0; + return (xp); + } + return (Strsave(type == 'e' ? STRNULL : cp)); + default: + break; + } + return (0); +} + +static int +matchs(str, pat) + register Char *str, *pat; +{ + while (*str && *pat && *str == *pat) + str++, pat++; + return (*pat == 0); +} + +static int +getsel(al, ar, dol) + register int *al, *ar; + int dol; +{ + register int c = getC(0); + register int i; + bool first = *al < 0; + + switch (c) { + + case '%': + if (quesarg == -1) { + seterror(ERR_BADBANGARG); + return (0); + } + if (*al < 0) + *al = quesarg; + *ar = quesarg; + break; + + case '-': + if (*al < 0) { + *al = 0; + *ar = dol - 1; + unreadc(c); + } + return (1); + + case '^': + if (*al < 0) + *al = 1; + *ar = 1; + break; + + case '$': + if (*al < 0) + *al = dol; + *ar = dol; + break; + + case '*': + if (*al < 0) + *al = 1; + *ar = dol; + if (*ar < *al) { + *ar = 0; + *al = 1; + return (1); + } + break; + + default: + if (Isdigit(c)) { + i = 0; + while (Isdigit(c)) { + i = i * 10 + c - '0'; + c = getC(0); + } + if (i < 0) + i = dol + 1; + if (*al < 0) + *al = i; + *ar = i; + } + else if (*al < 0) + *al = 0, *ar = dol; + else + *ar = dol - 1; + unreadc(c); + break; + } + if (first) { + c = getC(0); + unreadc(c); + if (any("-$*", c)) + return (1); + } + if (*al > *ar || *ar > dol) { + seterror(ERR_BADBANGARG); + return (0); + } + return (1); + +} + +static struct wordent * +gethent(sc) + int sc; +{ + register struct Hist *hp; + register Char *np; + register int c; + int event; + bool back = 0; + + c = sc == HISTSUB ? HIST : getC(0); + if (c == HIST) { + if (alhistp) + return (alhistp); + event = eventno; + } + else + switch (c) { + + case ':': + case '^': + case '$': + case '*': + case '%': + ungetC(c); + if (lastev == eventno && alhistp) + return (alhistp); + event = lastev; + break; + + case '#': /* !# is command being typed in (mrh) */ + if (--hleft == 0) { + seterror(ERR_HISTLOOP); + return (0); + } + else + return (¶ml); + /* NOTREACHED */ + + case '-': + back = 1; + c = getC(0); + /* FALLSTHROUGH */ + + default: + if (any("(=~", c)) { + unreadc(c); + ungetC(HIST); + return (0); + } + np = lhsb; + event = 0; + while (!cmap(c, _ESC | _META | _QF | _QB) && !any("${}:", c)) { + if (event != -1 && Isdigit(c)) + event = event * 10 + c - '0'; + else + event = -1; + if (np < &lhsb[sizeof(lhsb) / sizeof(Char) - 2]) + *np++ = c; + c = getC(0); + } + unreadc(c); + if (np == lhsb) { + ungetC(HIST); + return (0); + } + *np++ = 0; + if (event != -1) { + /* + * History had only digits + */ + if (back) + event = eventno + (alhistp == 0) - (event ? event : 0); + break; + } + hp = findev(lhsb, 0); + if (hp) + lastev = hp->Hnum; + return (&hp->Hlex); + + case '?': + np = lhsb; + for (;;) { + c = getC(0); + if (c == '\n') { + unreadc(c); + break; + } + if (c == '?') + break; + if (np < &lhsb[sizeof(lhsb) / sizeof(Char) - 2]) + *np++ = c; + } + if (np == lhsb) { + if (lhsb[0] == 0) { + seterror(ERR_NOSEARCH); + return (0); + } + } + else + *np++ = 0; + hp = findev(lhsb, 1); + if (hp) + lastev = hp->Hnum; + return (&hp->Hlex); + } + + for (hp = Histlist.Hnext; hp; hp = hp->Hnext) + if (hp->Hnum == event) { + hp->Href = eventno; + lastev = hp->Hnum; + return (&hp->Hlex); + } + np = putn(event); + seterror(ERR_NOEVENT, vis_str(np)); + return (0); +} + +static struct Hist * +findev(cp, anyarg) + Char *cp; + bool anyarg; +{ + register struct Hist *hp; + + for (hp = Histlist.Hnext; hp; hp = hp->Hnext) { + Char *dp; + register Char *p, *q; + register struct wordent *lp = hp->Hlex.next; + int argno = 0; + + /* + * The entries added by alias substitution don't have a newline but do + * have a negative event number. Savehist() trims off these entries, + * but it happens before alias expansion, too early to delete those + * from the previous command. + */ + if (hp->Hnum < 0) + continue; + if (lp->word[0] == '\n') + continue; + if (!anyarg) { + p = cp; + q = lp->word; + do + if (!*p) + return (hp); + while (*p++ == *q++); + continue; + } + do { + for (dp = lp->word; *dp; dp++) { + p = cp; + q = dp; + do + if (!*p) { + quesarg = argno; + return (hp); + } + while (*p++ == *q++); + } + lp = lp->next; + argno++; + } while (lp->word[0] != '\n'); + } + seterror(ERR_NOEVENT, vis_str(cp)); + return (0); +} + + +static void +setexclp(cp) + register Char *cp; +{ + if (cp && cp[0] == '\n') + return; + exclp = cp; +} + +void +unreadc(c) + int c; +{ + peekread = c; +} + +int +readc(wanteof) + bool wanteof; +{ + register int c; + static sincereal; + + aret = F_SEEK; + if ((c = peekread) != '\0') { + peekread = 0; + return (c); + } +top: + aret = F_SEEK; + if (alvecp) { + aret = A_SEEK; + if ((c = *alvecp++) != '\0') + return (c); + if (alvec && *alvec) { + alvecp = *alvec++; + return (' '); + } + else { + aret = F_SEEK; + alvecp = NULL; + return('\n'); + } + } + if (alvec) { + if ((alvecp = *alvec) != '\0') { + alvec++; + goto top; + } + /* Infinite source! */ + return ('\n'); + } + if (evalp) { + aret = E_SEEK; + if ((c = *evalp++) != '\0') + return (c); + if (evalvec && *evalvec) { + evalp = *evalvec++; + return (' '); + } + aret = F_SEEK; + evalp = 0; + } + if (evalvec) { + if (evalvec == (Char **) 1) { + doneinp = 1; + reset(); + } + if ((evalp = *evalvec) != '\0') { + evalvec++; + goto top; + } + evalvec = (Char **) 1; + return ('\n'); + } + do { + if (arginp == (Char *) 1 || onelflg == 1) { + if (wanteof) + return (-1); + exitstat(); + } + if (arginp) { + if ((c = *arginp++) == 0) { + arginp = (Char *) 1; + return ('\n'); + } + return (c); + } +reread: + c = bgetc(); + if (c < 0) { + struct termios tty; + if (wanteof) + return (-1); + /* was isatty but raw with ignoreeof yields problems */ + if (tcgetattr(SHIN, &tty) == 0 && (tty.c_lflag & ICANON)) + { + /* was 'short' for FILEC */ + int ctpgrp; + + if (++sincereal > 25) + goto oops; + if (tpgrp != -1 && + (ctpgrp = tcgetpgrp(FSHTTY)) != -1 && + tpgrp != ctpgrp) { + (void) tcsetpgrp(FSHTTY, tpgrp); + (void) killpg((pid_t) ctpgrp, SIGHUP); + (void) fprintf(csherr, "Reset tty pgrp from %d to %d\n", + ctpgrp, tpgrp); + goto reread; + } + if (adrof(STRignoreeof)) { + if (loginsh) + (void) fprintf(csherr,"\nUse \"logout\" to logout.\n"); + else + (void) fprintf(csherr,"\nUse \"exit\" to leave csh.\n"); + reset(); + } + if (chkstop == 0) + panystop(1); + } + oops: + doneinp = 1; + reset(); + } + sincereal = 0; + if (c == '\n' && onelflg) + onelflg--; + } while (c == 0); + return (c); +} + +static int +bgetc() +{ + register int buf, off, c; + +#ifdef FILEC + register int numleft = 0, roomleft; + Char ttyline[BUFSIZ]; +#endif + char tbuf[BUFSIZ + 1]; + + if (cantell) { + if (fseekp < fbobp || fseekp > feobp) { + fbobp = feobp = fseekp; + (void) lseek(SHIN, fseekp, L_SET); + } + if (fseekp == feobp) { + int i; + + fbobp = feobp; + do + c = read(SHIN, tbuf, BUFSIZ); + while (c < 0 && errno == EINTR); + if (c <= 0) + return (-1); + for (i = 0; i < c; i++) + fbuf[0][i] = (unsigned char) tbuf[i]; + feobp += c; + } + c = fbuf[0][fseekp - fbobp]; + fseekp++; + return (c); + } + +again: + buf = (int) fseekp / BUFSIZ; + if (buf >= fblocks) { + register Char **nfbuf = + (Char **) xcalloc((size_t) (fblocks + 2), + sizeof(Char **)); + + if (fbuf) { + (void) blkcpy(nfbuf, fbuf); + xfree((ptr_t) fbuf); + } + fbuf = nfbuf; + fbuf[fblocks] = (Char *) xcalloc(BUFSIZ, sizeof(Char)); + fblocks++; + if (!intty) + goto again; + } + if (fseekp >= feobp) { + buf = (int) feobp / BUFSIZ; + off = (int) feobp % BUFSIZ; + roomleft = BUFSIZ - off; + +#ifdef FILEC + roomleft = BUFSIZ - off; + for (;;) { + if (filec && intty) { + c = numleft ? numleft : tenex(ttyline, BUFSIZ); + if (c > roomleft) { + /* start with fresh buffer */ + feobp = fseekp = fblocks * BUFSIZ; + numleft = c; + goto again; + } + if (c > 0) + memmove(fbuf[buf] + off, ttyline, c * sizeof(Char)); + numleft = 0; + } + else { +#endif + c = read(SHIN, tbuf, roomleft); + if (c > 0) { + int i; + Char *ptr = fbuf[buf] + off; + + for (i = 0; i < c; i++) + ptr[i] = (unsigned char) tbuf[i]; + } +#ifdef FILEC + } +#endif + if (c >= 0) + break; + if (errno == EWOULDBLOCK) { + int off = 0; + + (void) ioctl(SHIN, FIONBIO, (ioctl_t) & off); + } + else if (errno != EINTR) + break; + } + if (c <= 0) + return (-1); + feobp += c; +#ifndef FILEC + goto again; +#else + if (filec && !intty) + goto again; +#endif + } + c = fbuf[buf][(int) fseekp % BUFSIZ]; + fseekp++; + return (c); +} + +static void +bfree() +{ + register int sb, i; + + if (cantell) + return; + if (whyles) + return; + sb = (int) (fseekp - 1) / BUFSIZ; + if (sb > 0) { + for (i = 0; i < sb; i++) + xfree((ptr_t) fbuf[i]); + (void) blkcpy(fbuf, &fbuf[sb]); + fseekp -= BUFSIZ * sb; + feobp -= BUFSIZ * sb; + fblocks -= sb; + } +} + +void +bseek(l) + struct Ain *l; +{ + switch (aret = l->type) { + case E_SEEK: + evalvec = l->a_seek; + evalp = l->c_seek; + return; + case A_SEEK: + alvec = l->a_seek; + alvecp = l->c_seek; + return; + case F_SEEK: + fseekp = l->f_seek; + return; + default: + (void) fprintf(csherr, "Bad seek type %d\n", aret); + abort(); + } +} + +void +btell(l) + struct Ain *l; +{ + switch (l->type = aret) { + case E_SEEK: + l->a_seek = evalvec; + l->c_seek = evalp; + return; + case A_SEEK: + l->a_seek = alvec; + l->c_seek = alvecp; + return; + case F_SEEK: + l->f_seek = fseekp; + l->a_seek = NULL; + return; + default: + (void) fprintf(csherr, "Bad seek type %d\n", aret); + abort(); + } +} + +void +btoeof() +{ + (void) lseek(SHIN, (off_t) 0, L_XTND); + aret = F_SEEK; + fseekp = feobp; + alvec = NULL; + alvecp = NULL; + evalvec = NULL; + evalp = NULL; + wfree(); + bfree(); +} + +void +settell() +{ + cantell = 0; + if (arginp || onelflg || intty) + return; + if (lseek(SHIN, (off_t) 0, L_INCR) < 0 || errno == ESPIPE) + return; + fbuf = (Char **) xcalloc(2, sizeof(Char **)); + fblocks = 1; + fbuf[0] = (Char *) xcalloc(BUFSIZ, sizeof(Char)); + fseekp = fbobp = feobp = lseek(SHIN, (off_t) 0, L_INCR); + cantell = 1; +} diff --git a/bin/csh/proc.c b/bin/csh/proc.c new file mode 100644 index 000000000000..6bba00aa21d2 --- /dev/null +++ b/bin/csh/proc.c @@ -0,0 +1,1379 @@ +/*- + * Copyright (c) 1980, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)proc.c 8.2 (Berkeley) 3/22/95"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#if __STDC__ +# include +#else +# include +#endif + +#include "csh.h" +#include "dir.h" +#include "proc.h" +#include "extern.h" + +#define BIGINDEX 9 /* largest desirable job index */ + +static struct rusage zru; + +static void pflushall __P((void)); +static void pflush __P((struct process *)); +static void pclrcurr __P((struct process *)); +static void padd __P((struct command *)); +static int pprint __P((struct process *, int)); +static void ptprint __P((struct process *)); +static void pads __P((Char *)); +static void pkill __P((Char **v, int)); +static struct process + *pgetcurr __P((struct process *)); +static void okpcntl __P((void)); + +/* + * pchild - called at interrupt level by the SIGCHLD signal + * indicating that at least one child has terminated or stopped + * thus at least one wait system call will definitely return a + * childs status. Top level routines (like pwait) must be sure + * to mask interrupts when playing with the proclist data structures! + */ +/* ARGUSED */ +void +pchild(notused) + int notused; +{ + register struct process *pp; + register struct process *fp; + register int pid; + extern int insource; + union wait w; + int jobflags; + struct rusage ru; + +loop: + errno = 0; /* reset, just in case */ + pid = wait3(&w.w_status, + (setintr && (intty || insource) ? WNOHANG | WUNTRACED : WNOHANG), &ru); + + if (pid <= 0) { + if (errno == EINTR) { + errno = 0; + goto loop; + } + pnoprocesses = pid == -1; + return; + } + for (pp = proclist.p_next; pp != NULL; pp = pp->p_next) + if (pid == pp->p_pid) + goto found; + goto loop; +found: + if (pid == atoi(short2str(value(STRchild)))) + unsetv(STRchild); + pp->p_flags &= ~(PRUNNING | PSTOPPED | PREPORTED); + if (WIFSTOPPED(w)) { + pp->p_flags |= PSTOPPED; + pp->p_reason = w.w_stopsig; + } + else { + if (pp->p_flags & (PTIME | PPTIME) || adrof(STRtime)) + (void) gettimeofday(&pp->p_etime, NULL); + + pp->p_rusage = ru; + if (WIFSIGNALED(w)) { + if (w.w_termsig == SIGINT) + pp->p_flags |= PINTERRUPTED; + else + pp->p_flags |= PSIGNALED; + if (w.w_coredump) + pp->p_flags |= PDUMPED; + pp->p_reason = w.w_termsig; + } + else { + pp->p_reason = w.w_retcode; + if (pp->p_reason != 0) + pp->p_flags |= PAEXITED; + else + pp->p_flags |= PNEXITED; + } + } + jobflags = 0; + fp = pp; + do { + if ((fp->p_flags & (PPTIME | PRUNNING | PSTOPPED)) == 0 && + !child && adrof(STRtime) && + fp->p_rusage.ru_utime.tv_sec + fp->p_rusage.ru_stime.tv_sec + >= atoi(short2str(value(STRtime)))) + fp->p_flags |= PTIME; + jobflags |= fp->p_flags; + } while ((fp = fp->p_friends) != pp); + pp->p_flags &= ~PFOREGND; + if (pp == pp->p_friends && (pp->p_flags & PPTIME)) { + pp->p_flags &= ~PPTIME; + pp->p_flags |= PTIME; + } + if ((jobflags & (PRUNNING | PREPORTED)) == 0) { + fp = pp; + do { + if (fp->p_flags & PSTOPPED) + fp->p_flags |= PREPORTED; + } while ((fp = fp->p_friends) != pp); + while (fp->p_pid != fp->p_jobid) + fp = fp->p_friends; + if (jobflags & PSTOPPED) { + if (pcurrent && pcurrent != fp) + pprevious = pcurrent; + pcurrent = fp; + } + else + pclrcurr(fp); + if (jobflags & PFOREGND) { + if (jobflags & (PSIGNALED | PSTOPPED | PPTIME) || +#ifdef IIASA + jobflags & PAEXITED || +#endif + !eq(dcwd->di_name, fp->p_cwd->di_name)) { + ; /* print in pjwait */ + } + /* PWP: print a newline after ^C */ + else if (jobflags & PINTERRUPTED) { + (void) vis_fputc('\r' | QUOTE, cshout); + (void) fputc('\n', cshout); + } + } + else { + if (jobflags & PNOTIFY || adrof(STRnotify)) { + (void) vis_fputc('\r' | QUOTE, cshout); + (void) fputc('\n', cshout); + (void) pprint(pp, NUMBER | NAME | REASON); + if ((jobflags & PSTOPPED) == 0) + pflush(pp); + } + else { + fp->p_flags |= PNEEDNOTE; + neednote++; + } + } + } + goto loop; +} + +void +pnote() +{ + register struct process *pp; + int flags; + sigset_t sigset, osigset; + + neednote = 0; + sigemptyset(&sigset); + sigaddset(&sigset, SIGCHLD); + for (pp = proclist.p_next; pp != NULL; pp = pp->p_next) { + if (pp->p_flags & PNEEDNOTE) { + sigprocmask(SIG_BLOCK, &sigset, &osigset); + pp->p_flags &= ~PNEEDNOTE; + flags = pprint(pp, NUMBER | NAME | REASON); + if ((flags & (PRUNNING | PSTOPPED)) == 0) + pflush(pp); + sigprocmask(SIG_SETMASK, &osigset, NULL); + } + } +} + +/* + * pwait - wait for current job to terminate, maintaining integrity + * of current and previous job indicators. + */ +void +pwait() +{ + register struct process *fp, *pp; + sigset_t sigset, osigset; + + /* + * Here's where dead procs get flushed. + */ + sigemptyset(&sigset); + sigaddset(&sigset, SIGCHLD); + sigprocmask(SIG_BLOCK, &sigset, &osigset); + for (pp = (fp = &proclist)->p_next; pp != NULL; pp = (fp = pp)->p_next) + if (pp->p_pid == 0) { + fp->p_next = pp->p_next; + xfree((ptr_t) pp->p_command); + if (pp->p_cwd && --pp->p_cwd->di_count == 0) + if (pp->p_cwd->di_next == 0) + dfree(pp->p_cwd); + xfree((ptr_t) pp); + pp = fp; + } + sigprocmask(SIG_SETMASK, &osigset, NULL); + pjwait(pcurrjob); +} + + +/* + * pjwait - wait for a job to finish or become stopped + * It is assumed to be in the foreground state (PFOREGND) + */ +void +pjwait(pp) + register struct process *pp; +{ + register struct process *fp; + int jobflags, reason; + sigset_t sigset, osigset; + + while (pp->p_pid != pp->p_jobid) + pp = pp->p_friends; + fp = pp; + + do { + if ((fp->p_flags & (PFOREGND | PRUNNING)) == PRUNNING) + (void) fprintf(csherr, "BUG: waiting for background job!\n"); + } while ((fp = fp->p_friends) != pp); + /* + * Now keep pausing as long as we are not interrupted (SIGINT), and the + * target process, or any of its friends, are running + */ + fp = pp; + sigemptyset(&sigset); + sigaddset(&sigset, SIGCHLD); + sigprocmask(SIG_BLOCK, &sigset, &osigset); + for (;;) { + sigemptyset(&sigset); + sigaddset(&sigset, SIGCHLD); + sigprocmask(SIG_BLOCK, &sigset, NULL); + jobflags = 0; + do + jobflags |= fp->p_flags; + while ((fp = (fp->p_friends)) != pp); + if ((jobflags & PRUNNING) == 0) + break; +#ifdef JOBDEBUG + (void) fprintf(csherr, "starting to sigsuspend for SIGCHLD on %d\n", + fp->p_pid); +#endif /* JOBDEBUG */ + sigset = osigset; + sigdelset(&sigset, SIGCHLD); + sigsuspend(&sigset); + } + sigprocmask(SIG_SETMASK, &osigset, NULL); + if (tpgrp > 0) /* get tty back */ + (void) tcsetpgrp(FSHTTY, tpgrp); + if ((jobflags & (PSIGNALED | PSTOPPED | PTIME)) || + !eq(dcwd->di_name, fp->p_cwd->di_name)) { + if (jobflags & PSTOPPED) { + (void) fputc('\n', cshout); + if (adrof(STRlistjobs)) { + Char *jobcommand[3]; + + jobcommand[0] = STRjobs; + if (eq(value(STRlistjobs), STRlong)) + jobcommand[1] = STRml; + else + jobcommand[1] = NULL; + jobcommand[2] = NULL; + + dojobs(jobcommand, NULL); + (void) pprint(pp, SHELLDIR); + } + else + (void) pprint(pp, AREASON | SHELLDIR); + } + else + (void) pprint(pp, AREASON | SHELLDIR); + } + if ((jobflags & (PINTERRUPTED | PSTOPPED)) && setintr && + (!gointr || !eq(gointr, STRminus))) { + if ((jobflags & PSTOPPED) == 0) + pflush(pp); + pintr1(0); + /* NOTREACHED */ + } + reason = 0; + fp = pp; + do { + if (fp->p_reason) + reason = fp->p_flags & (PSIGNALED | PINTERRUPTED) ? + fp->p_reason | META : fp->p_reason; + } while ((fp = fp->p_friends) != pp); + if ((reason != 0) && (adrof(STRprintexitvalue))) { + (void) fprintf(cshout, "Exit %d\n", reason); + } + set(STRstatus, putn(reason)); + if (reason && exiterr) + exitstat(); + pflush(pp); +} + +/* + * dowait - wait for all processes to finish + */ +void +/*ARGSUSED*/ +dowait(v, t) + Char **v; + struct command *t; +{ + register struct process *pp; + sigset_t sigset, osigset; + + pjobs++; + sigemptyset(&sigset); + sigaddset(&sigset, SIGCHLD); + sigprocmask(SIG_BLOCK, &sigset, &osigset); +loop: + for (pp = proclist.p_next; pp; pp = pp->p_next) + if (pp->p_pid && /* pp->p_pid == pp->p_jobid && */ + pp->p_flags & PRUNNING) { + sigemptyset(&sigset); + sigsuspend(&sigset); + goto loop; + } + sigprocmask(SIG_SETMASK, &osigset, NULL); + pjobs = 0; +} + +/* + * pflushall - flush all jobs from list (e.g. at fork()) + */ +static void +pflushall() +{ + register struct process *pp; + + for (pp = proclist.p_next; pp != NULL; pp = pp->p_next) + if (pp->p_pid) + pflush(pp); +} + +/* + * pflush - flag all process structures in the same job as the + * the argument process for deletion. The actual free of the + * space is not done here since pflush is called at interrupt level. + */ +static void +pflush(pp) + register struct process *pp; +{ + register struct process *np; + register int idx; + + if (pp->p_pid == 0) { + (void) fprintf(csherr, "BUG: process flushed twice"); + return; + } + while (pp->p_pid != pp->p_jobid) + pp = pp->p_friends; + pclrcurr(pp); + if (pp == pcurrjob) + pcurrjob = 0; + idx = pp->p_index; + np = pp; + do { + np->p_index = np->p_pid = 0; + np->p_flags &= ~PNEEDNOTE; + } while ((np = np->p_friends) != pp); + if (idx == pmaxindex) { + for (np = proclist.p_next, idx = 0; np; np = np->p_next) + if (np->p_index > idx) + idx = np->p_index; + pmaxindex = idx; + } +} + +/* + * pclrcurr - make sure the given job is not the current or previous job; + * pp MUST be the job leader + */ +static void +pclrcurr(pp) + register struct process *pp; +{ + + if (pp == pcurrent) + if (pprevious != NULL) { + pcurrent = pprevious; + pprevious = pgetcurr(pp); + } + else { + pcurrent = pgetcurr(pp); + pprevious = pgetcurr(pp); + } + else if (pp == pprevious) + pprevious = pgetcurr(pp); +} + +/* +4 here is 1 for '\0', 1 ea for << >& >> */ +static Char command[PMAXLEN + 4]; +static int cmdlen; +static Char *cmdp; + +/* + * palloc - allocate a process structure and fill it up. + * an important assumption is made that the process is running. + */ +void +palloc(pid, t) + int pid; + register struct command *t; +{ + register struct process *pp; + int i; + + pp = (struct process *) xcalloc(1, (size_t) sizeof(struct process)); + pp->p_pid = pid; + pp->p_flags = t->t_dflg & F_AMPERSAND ? PRUNNING : PRUNNING | PFOREGND; + if (t->t_dflg & F_TIME) + pp->p_flags |= PPTIME; + cmdp = command; + cmdlen = 0; + padd(t); + *cmdp++ = 0; + if (t->t_dflg & F_PIPEOUT) { + pp->p_flags |= PPOU; + if (t->t_dflg & F_STDERR) + pp->p_flags |= PERR; + } + pp->p_command = Strsave(command); + if (pcurrjob) { + struct process *fp; + + /* careful here with interrupt level */ + pp->p_cwd = 0; + pp->p_index = pcurrjob->p_index; + pp->p_friends = pcurrjob; + pp->p_jobid = pcurrjob->p_pid; + for (fp = pcurrjob; fp->p_friends != pcurrjob; fp = fp->p_friends) + continue; + fp->p_friends = pp; + } + else { + pcurrjob = pp; + pp->p_jobid = pid; + pp->p_friends = pp; + pp->p_cwd = dcwd; + dcwd->di_count++; + if (pmaxindex < BIGINDEX) + pp->p_index = ++pmaxindex; + else { + struct process *np; + + for (i = 1;; i++) { + for (np = proclist.p_next; np; np = np->p_next) + if (np->p_index == i) + goto tryagain; + pp->p_index = i; + if (i > pmaxindex) + pmaxindex = i; + break; + tryagain:; + } + } + if (pcurrent == NULL) + pcurrent = pp; + else if (pprevious == NULL) + pprevious = pp; + } + pp->p_next = proclist.p_next; + proclist.p_next = pp; + (void) gettimeofday(&pp->p_btime, NULL); +} + +static void +padd(t) + register struct command *t; +{ + Char **argp; + + if (t == 0) + return; + switch (t->t_dtyp) { + + case NODE_PAREN: + pads(STRLparensp); + padd(t->t_dspr); + pads(STRspRparen); + break; + + case NODE_COMMAND: + for (argp = t->t_dcom; *argp; argp++) { + pads(*argp); + if (argp[1]) + pads(STRspace); + } + break; + + case NODE_OR: + case NODE_AND: + case NODE_PIPE: + case NODE_LIST: + padd(t->t_dcar); + switch (t->t_dtyp) { + case NODE_OR: + pads(STRspor2sp); + break; + case NODE_AND: + pads(STRspand2sp); + break; + case NODE_PIPE: + pads(STRsporsp); + break; + case NODE_LIST: + pads(STRsemisp); + break; + } + padd(t->t_dcdr); + return; + } + if ((t->t_dflg & F_PIPEIN) == 0 && t->t_dlef) { + pads((t->t_dflg & F_READ) ? STRspLarrow2sp : STRspLarrowsp); + pads(t->t_dlef); + } + if ((t->t_dflg & F_PIPEOUT) == 0 && t->t_drit) { + pads((t->t_dflg & F_APPEND) ? STRspRarrow2 : STRspRarrow); + if (t->t_dflg & F_STDERR) + pads(STRand); + pads(STRspace); + pads(t->t_drit); + } +} + +static void +pads(cp) + Char *cp; +{ + register int i; + + /* + * Avoid the Quoted Space alias hack! Reported by: + * sam@john-bigboote.ICS.UCI.EDU (Sam Horrocks) + */ + if (cp[0] == STRQNULL[0]) + cp++; + + i = Strlen(cp); + + if (cmdlen >= PMAXLEN) + return; + if (cmdlen + i >= PMAXLEN) { + (void) Strcpy(cmdp, STRsp3dots); + cmdlen = PMAXLEN; + cmdp += 4; + return; + } + (void) Strcpy(cmdp, cp); + cmdp += i; + cmdlen += i; +} + +/* + * psavejob - temporarily save the current job on a one level stack + * so another job can be created. Used for { } in exp6 + * and `` in globbing. + */ +void +psavejob() +{ + + pholdjob = pcurrjob; + pcurrjob = NULL; +} + +/* + * prestjob - opposite of psavejob. This may be missed if we are interrupted + * somewhere, but pendjob cleans up anyway. + */ +void +prestjob() +{ + + pcurrjob = pholdjob; + pholdjob = NULL; +} + +/* + * pendjob - indicate that a job (set of commands) has been completed + * or is about to begin. + */ +void +pendjob() +{ + register struct process *pp, *tp; + + if (pcurrjob && (pcurrjob->p_flags & (PFOREGND | PSTOPPED)) == 0) { + pp = pcurrjob; + while (pp->p_pid != pp->p_jobid) + pp = pp->p_friends; + (void) fprintf(cshout, "[%d]", pp->p_index); + tp = pp; + do { + (void) fprintf(cshout, " %d", pp->p_pid); + pp = pp->p_friends; + } while (pp != tp); + (void) fputc('\n', cshout); + } + pholdjob = pcurrjob = 0; +} + +/* + * pprint - print a job + */ +static int +pprint(pp, flag) + register struct process *pp; + bool flag; +{ + register status, reason; + struct process *tp; + int jobflags, pstatus; + bool hadnl = 1; /* did we just have a newline */ + char *format; + + (void) fpurge(cshout); + + while (pp->p_pid != pp->p_jobid) + pp = pp->p_friends; + if (pp == pp->p_friends && (pp->p_flags & PPTIME)) { + pp->p_flags &= ~PPTIME; + pp->p_flags |= PTIME; + } + tp = pp; + status = reason = -1; + jobflags = 0; + do { + jobflags |= pp->p_flags; + pstatus = pp->p_flags & PALLSTATES; + if (tp != pp && !hadnl && !(flag & FANCY) && + ((pstatus == status && pp->p_reason == reason) || + !(flag & REASON))) { + (void) fputc(' ', cshout); + hadnl = 0; + } + else { + if (tp != pp && !hadnl) { + (void) fputc('\n', cshout); + hadnl = 1; + } + if (flag & NUMBER) { + if (pp == tp) + (void) fprintf(cshout, "[%d]%s %c ", pp->p_index, + pp->p_index < 10 ? " " : "", + pp == pcurrent ? '+' : + (pp == pprevious ? '-' : ' ')); + else + (void) fprintf(cshout, " "); + hadnl = 0; + } + if (flag & FANCY) { + (void) fprintf(cshout, "%5d ", pp->p_pid); + hadnl = 0; + } + if (flag & (REASON | AREASON)) { + if (flag & NAME) + format = "%-23s"; + else + format = "%s"; + if (pstatus == status) + if (pp->p_reason == reason) { + (void) fprintf(cshout, format, ""); + hadnl = 0; + goto prcomd; + } + else + reason = pp->p_reason; + else { + status = pstatus; + reason = pp->p_reason; + } + switch (status) { + + case PRUNNING: + (void) fprintf(cshout, format, "Running "); + hadnl = 0; + break; + + case PINTERRUPTED: + case PSTOPPED: + case PSIGNALED: + /* + * tell what happened to the background job + * From: Michael Schroeder + * + */ + if ((flag & REASON) + || ((flag & AREASON) + && reason != SIGINT + && (reason != SIGPIPE + || (pp->p_flags & PPOU) == 0))) { + (void) fprintf(cshout, format, + sys_siglist[(unsigned char) + pp->p_reason]); + hadnl = 0; + } + break; + + case PNEXITED: + case PAEXITED: + if (flag & REASON) { + if (pp->p_reason) + (void) fprintf(cshout, "Exit %-18d", pp->p_reason); + else + (void) fprintf(cshout, format, "Done"); + hadnl = 0; + } + break; + + default: + (void) fprintf(csherr, "BUG: status=%-9o", status); + } + } + } +prcomd: + if (flag & NAME) { + (void) fprintf(cshout, "%s", vis_str(pp->p_command)); + if (pp->p_flags & PPOU) + (void) fprintf(cshout, " |"); + if (pp->p_flags & PERR) + (void) fputc('&', cshout); + hadnl = 0; + } + if (flag & (REASON | AREASON) && pp->p_flags & PDUMPED) { + (void) fprintf(cshout, " (core dumped)"); + hadnl = 0; + } + if (tp == pp->p_friends) { + if (flag & AMPERSAND) { + (void) fprintf(cshout, " &"); + hadnl = 0; + } + if (flag & JOBDIR && + !eq(tp->p_cwd->di_name, dcwd->di_name)) { + (void) fprintf(cshout, " (wd: "); + dtildepr(value(STRhome), tp->p_cwd->di_name); + (void) fputc(')', cshout); + hadnl = 0; + } + } + if (pp->p_flags & PPTIME && !(status & (PSTOPPED | PRUNNING))) { + if (!hadnl) + (void) fprintf(cshout, "\n\t"); + prusage(&zru, &pp->p_rusage, &pp->p_etime, + &pp->p_btime); + hadnl = 1; + } + if (tp == pp->p_friends) { + if (!hadnl) { + (void) fputc('\n', cshout); + hadnl = 1; + } + if (flag & SHELLDIR && !eq(tp->p_cwd->di_name, dcwd->di_name)) { + (void) fprintf(cshout, "(wd now: "); + dtildepr(value(STRhome), dcwd->di_name); + (void) fprintf(cshout, ")\n"); + hadnl = 1; + } + } + } while ((pp = pp->p_friends) != tp); + if (jobflags & PTIME && (jobflags & (PSTOPPED | PRUNNING)) == 0) { + if (jobflags & NUMBER) + (void) fprintf(cshout, " "); + ptprint(tp); + hadnl = 1; + } + (void) fflush(cshout); + return (jobflags); +} + +static void +ptprint(tp) + register struct process *tp; +{ + struct timeval tetime, diff; + static struct timeval ztime; + struct rusage ru; + static struct rusage zru; + register struct process *pp = tp; + + ru = zru; + tetime = ztime; + do { + ruadd(&ru, &pp->p_rusage); + tvsub(&diff, &pp->p_etime, &pp->p_btime); + if (timercmp(&diff, &tetime, >)) + tetime = diff; + } while ((pp = pp->p_friends) != tp); + prusage(&zru, &ru, &tetime, &ztime); +} + +/* + * dojobs - print all jobs + */ +void +/*ARGSUSED*/ +dojobs(v, t) + Char **v; + struct command *t; +{ + register struct process *pp; + register int flag = NUMBER | NAME | REASON; + int i; + + if (chkstop) + chkstop = 2; + if (*++v) { + if (v[1] || !eq(*v, STRml)) + stderror(ERR_JOBS); + flag |= FANCY | JOBDIR; + } + for (i = 1; i <= pmaxindex; i++) + for (pp = proclist.p_next; pp; pp = pp->p_next) + if (pp->p_index == i && pp->p_pid == pp->p_jobid) { + pp->p_flags &= ~PNEEDNOTE; + if (!(pprint(pp, flag) & (PRUNNING | PSTOPPED))) + pflush(pp); + break; + } +} + +/* + * dofg - builtin - put the job into the foreground + */ +void +/*ARGSUSED*/ +dofg(v, t) + Char **v; + struct command *t; +{ + register struct process *pp; + + okpcntl(); + ++v; + do { + pp = pfind(*v); + pstart(pp, 1); + pjwait(pp); + } while (*v && *++v); +} + +/* + * %... - builtin - put the job into the foreground + */ +void +/*ARGSUSED*/ +dofg1(v, t) + Char **v; + struct command *t; +{ + register struct process *pp; + + okpcntl(); + pp = pfind(v[0]); + pstart(pp, 1); + pjwait(pp); +} + +/* + * dobg - builtin - put the job into the background + */ +void +/*ARGSUSED*/ +dobg(v, t) + Char **v; + struct command *t; +{ + register struct process *pp; + + okpcntl(); + ++v; + do { + pp = pfind(*v); + pstart(pp, 0); + } while (*v && *++v); +} + +/* + * %... & - builtin - put the job into the background + */ +void +/*ARGSUSED*/ +dobg1(v, t) + Char **v; + struct command *t; +{ + register struct process *pp; + + pp = pfind(v[0]); + pstart(pp, 0); +} + +/* + * dostop - builtin - stop the job + */ +void +/*ARGSUSED*/ +dostop(v, t) + Char **v; + struct command *t; +{ + pkill(++v, SIGSTOP); +} + +/* + * dokill - builtin - superset of kill (1) + */ +void +/*ARGSUSED*/ +dokill(v, t) + Char **v; + struct command *t; +{ + register int signum = SIGTERM; + register char *name; + + v++; + if (v[0] && v[0][0] == '-') { + if (v[0][1] == 'l') { + for (signum = 1; signum < NSIG; signum++) { + (void) fprintf(cshout, "%s ", sys_signame[signum]); + if (signum == NSIG / 2) + (void) fputc('\n', cshout); + } + (void) fputc('\n', cshout); + return; + } + if (Isdigit(v[0][1])) { + signum = atoi(short2str(v[0] + 1)); + if (signum < 0 || signum > NSIG) + stderror(ERR_NAME | ERR_BADSIG); + } + else { + name = short2str(&v[0][1]); + if (!strncasecmp(name, "sig", 3)) + name += 3; + + for (signum = 1; signum < NSIG; signum++) + if (!strcasecmp(sys_signame[signum], name)) + break; + + if (signum == NSIG) { + setname(vis_str(&v[0][1])); + stderror(ERR_NAME | ERR_UNKSIG); + } + } + v++; + } + pkill(v, signum); +} + +static void +pkill(v, signum) + Char **v; + int signum; +{ + register struct process *pp, *np; + register int jobflags = 0; + int pid, err1 = 0; + sigset_t sigset; + Char *cp; + + sigemptyset(&sigset); + sigaddset(&sigset, SIGCHLD); + if (setintr) + sigaddset(&sigset, SIGINT); + sigprocmask(SIG_BLOCK, &sigset, NULL); + gflag = 0, tglob(v); + if (gflag) { + v = globall(v); + if (v == 0) + stderror(ERR_NAME | ERR_NOMATCH); + } + else { + v = gargv = saveblk(v); + trim(v); + } + + while (v && (cp = *v)) { + if (*cp == '%') { + np = pp = pfind(cp); + do + jobflags |= np->p_flags; + while ((np = np->p_friends) != pp); + switch (signum) { + + case SIGSTOP: + case SIGTSTP: + case SIGTTIN: + case SIGTTOU: + if ((jobflags & PRUNNING) == 0) { + (void) fprintf(csherr, "%s: Already suspended\n", + vis_str(cp)); + err1++; + goto cont; + } + break; + /* + * suspend a process, kill -CONT %, then type jobs; the shell + * says it is suspended, but it is running; thanks jaap.. + */ + case SIGCONT: + pstart(pp, 0); + goto cont; + } + if (killpg((pid_t) pp->p_jobid, signum) < 0) { + (void) fprintf(csherr, "%s: %s\n", vis_str(cp), + strerror(errno)); + err1++; + } + if (signum == SIGTERM || signum == SIGHUP) + (void) killpg((pid_t) pp->p_jobid, SIGCONT); + } + else if (!(Isdigit(*cp) || *cp == '-')) + stderror(ERR_NAME | ERR_JOBARGS); + else { + pid = atoi(short2str(cp)); + if (kill((pid_t) pid, signum) < 0) { + (void) fprintf(csherr, "%d: %s\n", pid, strerror(errno)); + err1++; + goto cont; + } + if (signum == SIGTERM || signum == SIGHUP) + (void) kill((pid_t) pid, SIGCONT); + } +cont: + v++; + } + if (gargv) + blkfree(gargv), gargv = 0; + sigprocmask(SIG_UNBLOCK, &sigset, NULL); + if (err1) + stderror(ERR_SILENT); +} + +/* + * pstart - start the job in foreground/background + */ +void +pstart(pp, foregnd) + register struct process *pp; + int foregnd; +{ + register struct process *np; + sigset_t sigset, osigset; + long jobflags = 0; + + sigemptyset(&sigset); + sigaddset(&sigset, SIGCHLD); + sigprocmask(SIG_BLOCK, &sigset, &osigset); + np = pp; + do { + jobflags |= np->p_flags; + if (np->p_flags & (PRUNNING | PSTOPPED)) { + np->p_flags |= PRUNNING; + np->p_flags &= ~PSTOPPED; + if (foregnd) + np->p_flags |= PFOREGND; + else + np->p_flags &= ~PFOREGND; + } + } while ((np = np->p_friends) != pp); + if (!foregnd) + pclrcurr(pp); + (void) pprint(pp, foregnd ? NAME | JOBDIR : NUMBER | NAME | AMPERSAND); + if (foregnd) + (void) tcsetpgrp(FSHTTY, pp->p_jobid); + if (jobflags & PSTOPPED) + (void) killpg((pid_t) pp->p_jobid, SIGCONT); + sigprocmask(SIG_SETMASK, &osigset, NULL); +} + +void +panystop(neednl) + bool neednl; +{ + register struct process *pp; + + chkstop = 2; + for (pp = proclist.p_next; pp; pp = pp->p_next) + if (pp->p_flags & PSTOPPED) + stderror(ERR_STOPPED, neednl ? "\n" : ""); +} + +struct process * +pfind(cp) + Char *cp; +{ + register struct process *pp, *np; + + if (cp == 0 || cp[1] == 0 || eq(cp, STRcent2) || eq(cp, STRcentplus)) { + if (pcurrent == NULL) + stderror(ERR_NAME | ERR_JOBCUR); + return (pcurrent); + } + if (eq(cp, STRcentminus) || eq(cp, STRcenthash)) { + if (pprevious == NULL) + stderror(ERR_NAME | ERR_JOBPREV); + return (pprevious); + } + if (Isdigit(cp[1])) { + int idx = atoi(short2str(cp + 1)); + + for (pp = proclist.p_next; pp; pp = pp->p_next) + if (pp->p_index == idx && pp->p_pid == pp->p_jobid) + return (pp); + stderror(ERR_NAME | ERR_NOSUCHJOB); + } + np = NULL; + for (pp = proclist.p_next; pp; pp = pp->p_next) + if (pp->p_pid == pp->p_jobid) { + if (cp[1] == '?') { + register Char *dp; + + for (dp = pp->p_command; *dp; dp++) { + if (*dp != cp[2]) + continue; + if (prefix(cp + 2, dp)) + goto match; + } + } + else if (prefix(cp + 1, pp->p_command)) { + match: + if (np) + stderror(ERR_NAME | ERR_AMBIG); + np = pp; + } + } + if (np) + return (np); + stderror(ERR_NAME | cp[1] == '?' ? ERR_JOBPAT : ERR_NOSUCHJOB); + /* NOTREACHED */ + return (0); +} + + +/* + * pgetcurr - find most recent job that is not pp, preferably stopped + */ +static struct process * +pgetcurr(pp) + register struct process *pp; +{ + register struct process *np; + register struct process *xp = NULL; + + for (np = proclist.p_next; np; np = np->p_next) + if (np != pcurrent && np != pp && np->p_pid && + np->p_pid == np->p_jobid) { + if (np->p_flags & PSTOPPED) + return (np); + if (xp == NULL) + xp = np; + } + return (xp); +} + +/* + * donotify - flag the job so as to report termination asynchronously + */ +void +/*ARGSUSED*/ +donotify(v, t) + Char **v; + struct command *t; +{ + register struct process *pp; + + pp = pfind(*++v); + pp->p_flags |= PNOTIFY; +} + +/* + * Do the fork and whatever should be done in the child side that + * should not be done if we are not forking at all (like for simple builtin's) + * Also do everything that needs any signals fiddled with in the parent side + * + * Wanttty tells whether process and/or tty pgrps are to be manipulated: + * -1: leave tty alone; inherit pgrp from parent + * 0: already have tty; manipulate process pgrps only + * 1: want to claim tty; manipulate process and tty pgrps + * It is usually just the value of tpgrp. + */ + +int +pfork(t, wanttty) + struct command *t; /* command we are forking for */ + int wanttty; +{ + register int pid; + bool ignint = 0; + int pgrp; + sigset_t sigset, osigset; + + /* + * A child will be uninterruptible only under very special conditions. + * Remember that the semantics of '&' is implemented by disconnecting the + * process from the tty so signals do not need to ignored just for '&'. + * Thus signals are set to default action for children unless: we have had + * an "onintr -" (then specifically ignored) we are not playing with + * signals (inherit action) + */ + if (setintr) + ignint = (tpgrp == -1 && (t->t_dflg & F_NOINTERRUPT)) + || (gointr && eq(gointr, STRminus)); + /* + * Check for maximum nesting of 16 processes to avoid Forking loops + */ + if (child == 16) + stderror(ERR_NESTING, 16); + /* + * Hold SIGCHLD until we have the process installed in our table. + */ + sigemptyset(&sigset); + sigaddset(&sigset, SIGCHLD); + sigprocmask(SIG_BLOCK, &sigset, &osigset); + while ((pid = fork()) < 0) + if (setintr == 0) + (void) sleep(FORKSLEEP); + else { + sigprocmask(SIG_SETMASK, &osigset, NULL); + stderror(ERR_NOPROC); + } + if (pid == 0) { + settimes(); + pgrp = pcurrjob ? pcurrjob->p_jobid : getpid(); + pflushall(); + pcurrjob = NULL; + child++; + if (setintr) { + setintr = 0; /* until I think otherwise */ + /* + * Children just get blown away on SIGINT, SIGQUIT unless "onintr + * -" seen. + */ + (void) signal(SIGINT, ignint ? SIG_IGN : SIG_DFL); + (void) signal(SIGQUIT, ignint ? SIG_IGN : SIG_DFL); + if (wanttty >= 0) { + /* make stoppable */ + (void) signal(SIGTSTP, SIG_DFL); + (void) signal(SIGTTIN, SIG_DFL); + (void) signal(SIGTTOU, SIG_DFL); + } + (void) signal(SIGTERM, parterm); + } + else if (tpgrp == -1 && (t->t_dflg & F_NOINTERRUPT)) { + (void) signal(SIGINT, SIG_IGN); + (void) signal(SIGQUIT, SIG_IGN); + } + pgetty(wanttty, pgrp); + /* + * Nohup and nice apply only to NODE_COMMAND's but it would be nice + * (?!?) if you could say "nohup (foo;bar)" Then the parser would have + * to know about nice/nohup/time + */ + if (t->t_dflg & F_NOHUP) + (void) signal(SIGHUP, SIG_IGN); + if (t->t_dflg & F_NICE) + (void) setpriority(PRIO_PROCESS, 0, t->t_nice); + } + else { + if (wanttty >= 0) + (void) setpgid(pid, pcurrjob ? pcurrjob->p_jobid : pid); + palloc(pid, t); + sigprocmask(SIG_SETMASK, &osigset, NULL); + } + + return (pid); +} + +static void +okpcntl() +{ + if (tpgrp == -1) + stderror(ERR_JOBCONTROL); + if (tpgrp == 0) + stderror(ERR_JOBCTRLSUB); +} + +/* + * if we don't have vfork(), things can still go in the wrong order + * resulting in the famous 'Stopped (tty output)'. But some systems + * don't permit the setpgid() call, (these are more recent secure + * systems such as ibm's aix). Then we'd rather print an error message + * than hang the shell! + * I am open to suggestions how to fix that. + */ +void +pgetty(wanttty, pgrp) + int wanttty, pgrp; +{ + sigset_t sigset, osigset; + + /* + * christos: I am blocking the tty signals till I've set things + * correctly.... + */ + if (wanttty > 0) { + sigemptyset(&sigset); + sigaddset(&sigset, SIGTSTP); + sigaddset(&sigset, SIGTTIN); + sigaddset(&sigset, SIGTTOU); + sigprocmask(SIG_BLOCK, &sigset, &osigset); + } + /* + * From: Michael Schroeder + * Don't check for tpgrp >= 0 so even non-interactive shells give + * background jobs process groups Same for the comparison in the other part + * of the #ifdef + */ + if (wanttty >= 0) + if (setpgid(0, pgrp) == -1) { + (void) fprintf(csherr, "csh: setpgid error.\n"); + xexit(0); + } + + if (wanttty > 0) { + (void) tcsetpgrp(FSHTTY, pgrp); + sigprocmask(SIG_SETMASK, &osigset, NULL); + } + + if (tpgrp > 0) + tpgrp = 0; /* gave tty away */ +} diff --git a/bin/csh/sem.c b/bin/csh/sem.c new file mode 100644 index 000000000000..775ad356235e --- /dev/null +++ b/bin/csh/sem.c @@ -0,0 +1,653 @@ +/*- + * Copyright (c) 1980, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)sem.c 8.3 (Berkeley) 4/29/95"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#if __STDC__ +# include +#else +# include +#endif + +#include "csh.h" +#include "proc.h" +#include "extern.h" + +static void vffree __P((int)); +static Char *splicepipe __P((struct command *t, Char *)); +static void doio __P((struct command *t, int *, int *)); +static void chkclob __P((char *)); + +void +execute(t, wanttty, pipein, pipeout) + register struct command *t; + int wanttty, *pipein, *pipeout; +{ + bool forked = 0; + struct biltins *bifunc; + int pid = 0; + int pv[2]; + sigset_t sigset; + + static sigset_t csigset; + + static sigset_t ocsigset; + static int onosigchld = 0; + static int nosigchld = 0; + + UNREGISTER(forked); + UNREGISTER(bifunc); + UNREGISTER(wanttty); + + if (t == 0) + return; + + if (t->t_dflg & F_AMPERSAND) + wanttty = 0; + switch (t->t_dtyp) { + + case NODE_COMMAND: + if ((t->t_dcom[0][0] & (QUOTE | TRIM)) == QUOTE) + (void) Strcpy(t->t_dcom[0], t->t_dcom[0] + 1); + if ((t->t_dflg & F_REPEAT) == 0) + Dfix(t); /* $ " ' \ */ + if (t->t_dcom[0] == 0) + return; + /* fall into... */ + + case NODE_PAREN: + if (t->t_dflg & F_PIPEOUT) + mypipe(pipeout); + /* + * Must do << early so parent will know where input pointer should be. + * If noexec then this is all we do. + */ + if (t->t_dflg & F_READ) { + (void) close(0); + heredoc(t->t_dlef); + if (noexec) + (void) close(0); + } + + set(STRstatus, Strsave(STR0)); + + /* + * This mess is the necessary kludge to handle the prefix builtins: + * nice, nohup, time. These commands can also be used by themselves, + * and this is not handled here. This will also work when loops are + * parsed. + */ + while (t->t_dtyp == NODE_COMMAND) + if (eq(t->t_dcom[0], STRnice)) + if (t->t_dcom[1]) + if (strchr("+-", t->t_dcom[1][0])) + if (t->t_dcom[2]) { + setname("nice"); + t->t_nice = + getn(t->t_dcom[1]); + lshift(t->t_dcom, 2); + t->t_dflg |= F_NICE; + } + else + break; + else { + t->t_nice = 4; + lshift(t->t_dcom, 1); + t->t_dflg |= F_NICE; + } + else + break; + else if (eq(t->t_dcom[0], STRnohup)) + if (t->t_dcom[1]) { + t->t_dflg |= F_NOHUP; + lshift(t->t_dcom, 1); + } + else + break; + else if (eq(t->t_dcom[0], STRtime)) + if (t->t_dcom[1]) { + t->t_dflg |= F_TIME; + lshift(t->t_dcom, 1); + } + else + break; + else + break; + + /* is it a command */ + if (t->t_dtyp == NODE_COMMAND) { + /* + * Check if we have a builtin function and remember which one. + */ + bifunc = isbfunc(t); + if (noexec) { + /* + * Continue for builtins that are part of the scripting language + */ + if (bifunc->bfunct != dobreak && bifunc->bfunct != docontin && + bifunc->bfunct != doelse && bifunc->bfunct != doend && + bifunc->bfunct != doforeach && bifunc->bfunct != dogoto && + bifunc->bfunct != doif && bifunc->bfunct != dorepeat && + bifunc->bfunct != doswbrk && bifunc->bfunct != doswitch && + bifunc->bfunct != dowhile && bifunc->bfunct != dozip) + break; + } + } + else { /* not a command */ + bifunc = NULL; + if (noexec) + break; + } + + /* + * We fork only if we are timed, or are not the end of a parenthesized + * list and not a simple builtin function. Simple meaning one that is + * not pipedout, niced, nohupped, or &'d. It would be nice(?) to not + * fork in some of these cases. + */ + /* + * Prevent forking cd, pushd, popd, chdir cause this will cause the + * shell not to change dir! + */ + if (bifunc && (bifunc->bfunct == dochngd || + bifunc->bfunct == dopushd || + bifunc->bfunct == dopopd)) + t->t_dflg &= ~(F_NICE); + if (((t->t_dflg & F_TIME) || ((t->t_dflg & F_NOFORK) == 0 && + (!bifunc || t->t_dflg & + (F_PIPEOUT | F_AMPERSAND | F_NICE | F_NOHUP)))) || + /* + * We have to fork for eval too. + */ + (bifunc && (t->t_dflg & (F_PIPEIN | F_PIPEOUT)) != 0 && + bifunc->bfunct == doeval)) + if (t->t_dtyp == NODE_PAREN || + t->t_dflg & (F_REPEAT | F_AMPERSAND) || bifunc) { + forked++; + /* + * We need to block SIGCHLD here, so that if the process does + * not die before we can set the process group + */ + if (wanttty >= 0 && !nosigchld) { + sigemptyset(&sigset); + sigaddset(&sigset, SIGCHLD); + sigprocmask(SIG_BLOCK, &sigset, &csigset); + nosigchld = 1; + } + + pid = pfork(t, wanttty); + if (pid == 0 && nosigchld) { + sigprocmask(SIG_SETMASK, &csigset, NULL); + nosigchld = 0; + } + else if (pid != 0 && (t->t_dflg & F_AMPERSAND)) + backpid = pid; + + } + else { + int ochild, osetintr, ohaderr, odidfds; + int oSHIN, oSHOUT, oSHERR, oOLDSTD, otpgrp; + sigset_t osigset; + + /* + * Prepare for the vfork by saving everything that the child + * corrupts before it exec's. Note that in some signal + * implementations which keep the signal info in user space + * (e.g. Sun's) it will also be necessary to save and restore + * the current sigaction's for the signals the child touches + * before it exec's. + */ + if (wanttty >= 0 && !nosigchld && !noexec) { + sigemptyset(&sigset); + sigaddset(&sigset, SIGCHLD); + sigprocmask(SIG_BLOCK, &sigset, &csigset); + nosigchld = 1; + } + sigemptyset(&sigset); + sigaddset(&sigset, SIGCHLD); + sigaddset(&sigset, SIGINT); + sigprocmask(SIG_BLOCK, &sigset, &osigset); + ochild = child; + osetintr = setintr; + ohaderr = haderr; + odidfds = didfds; + oSHIN = SHIN; + oSHOUT = SHOUT; + oSHERR = SHERR; + oOLDSTD = OLDSTD; + otpgrp = tpgrp; + ocsigset = csigset; + onosigchld = nosigchld; + Vsav = Vdp = 0; + Vexpath = 0; + Vt = 0; + pid = vfork(); + + if (pid < 0) { + sigprocmask(SIG_SETMASK, &osigset, NULL); + stderror(ERR_NOPROC); + } + forked++; + if (pid) { /* parent */ + child = ochild; + setintr = osetintr; + haderr = ohaderr; + didfds = odidfds; + SHIN = oSHIN; + SHOUT = oSHOUT; + SHERR = oSHERR; + OLDSTD = oOLDSTD; + tpgrp = otpgrp; + csigset = ocsigset; + nosigchld = onosigchld; + + xfree((ptr_t) Vsav); + Vsav = 0; + xfree((ptr_t) Vdp); + Vdp = 0; + xfree((ptr_t) Vexpath); + Vexpath = 0; + blkfree((Char **) Vt); + Vt = 0; + /* this is from pfork() */ + palloc(pid, t); + sigprocmask(SIG_SETMASK, &osigset, NULL); + } + else { /* child */ + /* this is from pfork() */ + int pgrp; + bool ignint = 0; + + if (nosigchld) { + sigprocmask(SIG_SETMASK, &csigset, NULL); + nosigchld = 0; + } + + if (setintr) + ignint = + (tpgrp == -1 && + (t->t_dflg & F_NOINTERRUPT)) + || (gointr && eq(gointr, STRminus)); + pgrp = pcurrjob ? pcurrjob->p_jobid : getpid(); + child++; + if (setintr) { + setintr = 0; + if (ignint) { + (void) signal(SIGINT, SIG_IGN); + (void) signal(SIGQUIT, SIG_IGN); + } + else { + (void) signal(SIGINT, vffree); + (void) signal(SIGQUIT, SIG_DFL); + } + + if (wanttty >= 0) { + (void) signal(SIGTSTP, SIG_DFL); + (void) signal(SIGTTIN, SIG_DFL); + (void) signal(SIGTTOU, SIG_DFL); + } + + (void) signal(SIGTERM, parterm); + } + else if (tpgrp == -1 && + (t->t_dflg & F_NOINTERRUPT)) { + (void) signal(SIGINT, SIG_IGN); + (void) signal(SIGQUIT, SIG_IGN); + } + + pgetty(wanttty, pgrp); + if (t->t_dflg & F_NOHUP) + (void) signal(SIGHUP, SIG_IGN); + if (t->t_dflg & F_NICE) + (void) setpriority(PRIO_PROCESS, 0, t->t_nice); + } + + } + if (pid != 0) { + /* + * It would be better if we could wait for the whole job when we + * knew the last process had been started. Pwait, in fact, does + * wait for the whole job anyway, but this test doesn't really + * express our intentions. + */ + if (didfds == 0 && t->t_dflg & F_PIPEIN) { + (void) close(pipein[0]); + (void) close(pipein[1]); + } + if ((t->t_dflg & F_PIPEOUT) == 0) { + if (nosigchld) { + sigprocmask(SIG_SETMASK, &csigset, NULL); + nosigchld = 0; + } + if ((t->t_dflg & F_AMPERSAND) == 0) + pwait(); + } + break; + } + doio(t, pipein, pipeout); + if (t->t_dflg & F_PIPEOUT) { + (void) close(pipeout[0]); + (void) close(pipeout[1]); + } + /* + * Perform a builtin function. If we are not forked, arrange for + * possible stopping + */ + if (bifunc) { + func(t, bifunc); + if (forked) + exitstat(); + break; + } + if (t->t_dtyp != NODE_PAREN) { + doexec(NULL, t); + /* NOTREACHED */ + } + /* + * For () commands must put new 0,1,2 in FSH* and recurse + */ + OLDSTD = dcopy(0, FOLDSTD); + SHOUT = dcopy(1, FSHOUT); + SHERR = dcopy(2, FSHERR); + (void) close(SHIN); + SHIN = -1; + didfds = 0; + wanttty = -1; + t->t_dspr->t_dflg |= t->t_dflg & F_NOINTERRUPT; + execute(t->t_dspr, wanttty, NULL, NULL); + exitstat(); + + case NODE_PIPE: + t->t_dcar->t_dflg |= F_PIPEOUT | + (t->t_dflg & (F_PIPEIN | F_AMPERSAND | F_STDERR | F_NOINTERRUPT)); + execute(t->t_dcar, wanttty, pipein, pv); + t->t_dcdr->t_dflg |= F_PIPEIN | (t->t_dflg & + (F_PIPEOUT | F_AMPERSAND | F_NOFORK | F_NOINTERRUPT)); + if (wanttty > 0) + wanttty = 0; /* got tty already */ + execute(t->t_dcdr, wanttty, pv, pipeout); + break; + + case NODE_LIST: + if (t->t_dcar) { + t->t_dcar->t_dflg |= t->t_dflg & F_NOINTERRUPT; + execute(t->t_dcar, wanttty, NULL, NULL); + /* + * In strange case of A&B make a new job after A + */ + if (t->t_dcar->t_dflg & F_AMPERSAND && t->t_dcdr && + (t->t_dcdr->t_dflg & F_AMPERSAND) == 0) + pendjob(); + } + if (t->t_dcdr) { + t->t_dcdr->t_dflg |= t->t_dflg & + (F_NOFORK | F_NOINTERRUPT); + execute(t->t_dcdr, wanttty, NULL, NULL); + } + break; + + case NODE_OR: + case NODE_AND: + if (t->t_dcar) { + t->t_dcar->t_dflg |= t->t_dflg & F_NOINTERRUPT; + execute(t->t_dcar, wanttty, NULL, NULL); + if ((getn(value(STRstatus)) == 0) != + (t->t_dtyp == NODE_AND)) + return; + } + if (t->t_dcdr) { + t->t_dcdr->t_dflg |= t->t_dflg & + (F_NOFORK | F_NOINTERRUPT); + execute(t->t_dcdr, wanttty, NULL, NULL); + } + break; + } + /* + * Fall through for all breaks from switch + * + * If there will be no more executions of this command, flush all file + * descriptors. Places that turn on the F_REPEAT bit are responsible for + * doing donefds after the last re-execution + */ + if (didfds && !(t->t_dflg & F_REPEAT)) + donefds(); +} + +static void +vffree(i) +int i; +{ + register Char **v; + + if ((v = gargv) != NULL) { + gargv = 0; + xfree((ptr_t) v); + } + if ((v = pargv) != NULL) { + pargv = 0; + xfree((ptr_t) v); + } + _exit(i); +} + +/* + * Expand and glob the words after an i/o redirection. + * If more than one word is generated, then update the command vector. + * + * This is done differently in all the shells: + * 1. in the bourne shell and ksh globbing is not performed + * 2. Bash/csh say ambiguous + * 3. zsh does i/o to/from all the files + * 4. itcsh concatenates the words. + * + * I don't know what is best to do. I think that Ambiguous is better + * than restructuring the command vector, because the user can get + * unexpected results. In any case, the command vector restructuring + * code is present and the user can choose it by setting noambiguous + */ +static Char * +splicepipe(t, cp) + register struct command *t; + Char *cp; /* word after < or > */ +{ + Char *blk[2]; + + if (adrof(STRnoambiguous)) { + Char **pv; + + blk[0] = Dfix1(cp); /* expand $ */ + blk[1] = NULL; + + gflag = 0, tglob(blk); + if (gflag) { + pv = globall(blk); + if (pv == NULL) { + setname(vis_str(blk[0])); + xfree((ptr_t) blk[0]); + stderror(ERR_NAME | ERR_NOMATCH); + } + gargv = NULL; + if (pv[1] != NULL) { /* we need to fix the command vector */ + Char **av = blkspl(t->t_dcom, &pv[1]); + xfree((ptr_t) t->t_dcom); + t->t_dcom = av; + } + xfree((ptr_t) blk[0]); + blk[0] = pv[0]; + xfree((ptr_t) pv); + } + } + else { + blk[0] = globone(blk[1] = Dfix1(cp), G_ERROR); + xfree((ptr_t) blk[1]); + } + return(blk[0]); +} + +/* + * Perform io redirection. + * We may or maynot be forked here. + */ +static void +doio(t, pipein, pipeout) + register struct command *t; + int *pipein, *pipeout; +{ + register int fd; + register Char *cp; + register int flags = t->t_dflg; + + if (didfds || (flags & F_REPEAT)) + return; + if ((flags & F_READ) == 0) {/* F_READ already done */ + if (t->t_dlef) { + char tmp[MAXPATHLEN+1]; + + /* + * so < /dev/std{in,out,err} work + */ + (void) dcopy(SHIN, 0); + (void) dcopy(SHOUT, 1); + (void) dcopy(SHERR, 2); + cp = splicepipe(t, t->t_dlef); + (void) strncpy(tmp, short2str(cp), MAXPATHLEN); + tmp[MAXPATHLEN] = '\0'; + xfree((ptr_t) cp); + if ((fd = open(tmp, O_RDONLY)) < 0) + stderror(ERR_SYSTEM, tmp, strerror(errno)); + (void) dmove(fd, 0); + } + else if (flags & F_PIPEIN) { + (void) close(0); + (void) dup(pipein[0]); + (void) close(pipein[0]); + (void) close(pipein[1]); + } + else if ((flags & F_NOINTERRUPT) && tpgrp == -1) { + (void) close(0); + (void) open(_PATH_DEVNULL, O_RDONLY); + } + else { + (void) close(0); + (void) dup(OLDSTD); + (void) ioctl(0, FIONCLEX, NULL); + } + } + if (t->t_drit) { + char tmp[MAXPATHLEN+1]; + + cp = splicepipe(t, t->t_drit); + (void) strncpy(tmp, short2str(cp), MAXPATHLEN); + tmp[MAXPATHLEN] = '\0'; + xfree((ptr_t) cp); + /* + * so > /dev/std{out,err} work + */ + (void) dcopy(SHOUT, 1); + (void) dcopy(SHERR, 2); + if ((flags & F_APPEND) && +#ifdef O_APPEND + (fd = open(tmp, O_WRONLY | O_APPEND)) >= 0); +#else + (fd = open(tmp, O_WRONLY)) >= 0) + (void) lseek(1, (off_t) 0, L_XTND); +#endif + else { + if (!(flags & F_OVERWRITE) && adrof(STRnoclobber)) { + if (flags & F_APPEND) + stderror(ERR_SYSTEM, tmp, strerror(errno)); + chkclob(tmp); + } + if ((fd = open(tmp, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) + stderror(ERR_SYSTEM, tmp, strerror(errno)); + } + (void) dmove(fd, 1); + } + else if (flags & F_PIPEOUT) { + (void) close(1); + (void) dup(pipeout[1]); + } + else { + (void) close(1); + (void) dup(SHOUT); + (void) ioctl(1, FIONCLEX, NULL); + } + + (void) close(2); + if (flags & F_STDERR) { + (void) dup(1); + } + else { + (void) dup(SHERR); + (void) ioctl(2, FIONCLEX, NULL); + } + didfds = 1; +} + +void +mypipe(pv) + register int *pv; +{ + + if (pipe(pv) < 0) + goto oops; + pv[0] = dmove(pv[0], -1); + pv[1] = dmove(pv[1], -1); + if (pv[0] >= 0 && pv[1] >= 0) + return; +oops: + stderror(ERR_PIPE); +} + +static void +chkclob(cp) + register char *cp; +{ + struct stat stb; + + if (stat(cp, &stb) < 0) + return; + if (S_ISCHR(stb.st_mode)) + return; + stderror(ERR_EXISTS, cp); +} diff --git a/bin/csh/set.c b/bin/csh/set.c new file mode 100644 index 000000000000..119216351f2e --- /dev/null +++ b/bin/csh/set.c @@ -0,0 +1,848 @@ +/*- + * Copyright (c) 1980, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)set.c 8.2 (Berkeley) 3/22/95"; +#endif /* not lint */ + +#include +#include +#ifndef SHORT_STRINGS +#include +#endif /* SHORT_STRINGS */ +#if __STDC__ +# include +#else +# include +#endif + +#include "csh.h" +#include "extern.h" + +static Char *getinx __P((Char *, int *)); +static void asx __P((Char *, int, Char *)); +static struct varent + *getvx __P((Char *, int)); +static Char *xset __P((Char *, Char ***)); +static Char *operate __P((int, Char *, Char *)); +static void putn1 __P((int)); +static struct varent + *madrof __P((Char *, struct varent *)); +static void unsetv1 __P((struct varent *)); +static void exportpath __P((Char **)); +static void balance __P((struct varent *, int, int)); + + +/* + * C Shell + */ + +void +/*ARGSUSED*/ +doset(v, t) + Char **v; + struct command *t; +{ + register Char *p; + Char *vp, op; + Char **vecp; + bool hadsub; + int subscr; + + v++; + p = *v++; + if (p == 0) { + prvars(); + return; + } + do { + hadsub = 0; + vp = p; + if (letter(*p)) + for (; alnum(*p); p++) + continue; + if (vp == p || !letter(*vp)) + stderror(ERR_NAME | ERR_VARBEGIN); + if ((p - vp) > MAXVARLEN) { + stderror(ERR_NAME | ERR_VARTOOLONG); + return; + } + if (*p == '[') { + hadsub++; + p = getinx(p, &subscr); + } + if ((op = *p) != '\0') { + *p++ = 0; + if (*p == 0 && *v && **v == '(') + p = *v++; + } + else if (*v && eq(*v, STRequal)) { + op = '=', v++; + if (*v) + p = *v++; + } + if (op && op != '=') + stderror(ERR_NAME | ERR_SYNTAX); + if (eq(p, STRLparen)) { + register Char **e = v; + + if (hadsub) + stderror(ERR_NAME | ERR_SYNTAX); + for (;;) { + if (!*e) + stderror(ERR_NAME | ERR_MISSING, ')'); + if (**e == ')') + break; + e++; + } + p = *e; + *e = 0; + vecp = saveblk(v); + set1(vp, vecp, &shvhed); + *e = p; + v = e + 1; + } + else if (hadsub) + asx(vp, subscr, Strsave(p)); + else + set(vp, Strsave(p)); + if (eq(vp, STRpath)) { + exportpath(adrof(STRpath)->vec); + dohash(NULL, NULL); + } + else if (eq(vp, STRhistchars)) { + register Char *pn = value(STRhistchars); + + HIST = *pn++; + HISTSUB = *pn; + } + else if (eq(vp, STRuser)) { + Setenv(STRUSER, value(vp)); + Setenv(STRLOGNAME, value(vp)); + } + else if (eq(vp, STRwordchars)) { + word_chars = value(vp); + } + else if (eq(vp, STRterm)) + Setenv(STRTERM, value(vp)); + else if (eq(vp, STRhome)) { + register Char *cp; + + cp = Strsave(value(vp)); /* get the old value back */ + + /* + * convert to cononical pathname (possibly resolving symlinks) + */ + cp = dcanon(cp, cp); + + set(vp, Strsave(cp)); /* have to save the new val */ + + /* and now mirror home with HOME */ + Setenv(STRHOME, cp); + /* fix directory stack for new tilde home */ + dtilde(); + xfree((ptr_t) cp); + } +#ifdef FILEC + else if (eq(vp, STRfilec)) + filec = 1; +#endif + } while ((p = *v++) != NULL); +} + +static Char * +getinx(cp, ip) + register Char *cp; + register int *ip; +{ + + *ip = 0; + *cp++ = 0; + while (*cp && Isdigit(*cp)) + *ip = *ip * 10 + *cp++ - '0'; + if (*cp++ != ']') + stderror(ERR_NAME | ERR_SUBSCRIPT); + return (cp); +} + +static void +asx(vp, subscr, p) + Char *vp; + int subscr; + Char *p; +{ + register struct varent *v = getvx(vp, subscr); + + xfree((ptr_t) v->vec[subscr - 1]); + v->vec[subscr - 1] = globone(p, G_APPEND); +} + +static struct varent * +getvx(vp, subscr) + Char *vp; + int subscr; +{ + register struct varent *v = adrof(vp); + + if (v == 0) + udvar(vp); + if (subscr < 1 || subscr > blklen(v->vec)) + stderror(ERR_NAME | ERR_RANGE); + return (v); +} + +void +/*ARGSUSED*/ +dolet(v, t) + Char **v; + struct command *t; +{ + register Char *p; + Char *vp, c, op; + bool hadsub; + int subscr; + + v++; + p = *v++; + if (p == 0) { + prvars(); + return; + } + do { + hadsub = 0; + vp = p; + if (letter(*p)) + for (; alnum(*p); p++) + continue; + if (vp == p || !letter(*vp)) + stderror(ERR_NAME | ERR_VARBEGIN); + if ((p - vp) > MAXVARLEN) + stderror(ERR_NAME | ERR_VARTOOLONG); + if (*p == '[') { + hadsub++; + p = getinx(p, &subscr); + } + if (*p == 0 && *v) + p = *v++; + if ((op = *p) != '\0') + *p++ = 0; + else + stderror(ERR_NAME | ERR_ASSIGN); + + if (*p == '\0' && *v == NULL) + stderror(ERR_NAME | ERR_ASSIGN); + + vp = Strsave(vp); + if (op == '=') { + c = '='; + p = xset(p, &v); + } + else { + c = *p++; + if (any("+-", c)) { + if (c != op || *p) + stderror(ERR_NAME | ERR_UNKNOWNOP); + p = Strsave(STR1); + } + else { + if (any("<>", op)) { + if (c != op) + stderror(ERR_NAME | ERR_UNKNOWNOP); + c = *p++; + stderror(ERR_NAME | ERR_SYNTAX); + } + if (c != '=') + stderror(ERR_NAME | ERR_UNKNOWNOP); + p = xset(p, &v); + } + } + if (op == '=') + if (hadsub) + asx(vp, subscr, p); + else + set(vp, p); + else if (hadsub) { + struct varent *gv = getvx(vp, subscr); + + asx(vp, subscr, operate(op, gv->vec[subscr - 1], p)); + } + else + set(vp, operate(op, value(vp), p)); + if (eq(vp, STRpath)) { + exportpath(adrof(STRpath)->vec); + dohash(NULL, NULL); + } + xfree((ptr_t) vp); + if (c != '=') + xfree((ptr_t) p); + } while ((p = *v++) != NULL); +} + +static Char * +xset(cp, vp) + Char *cp, ***vp; +{ + register Char *dp; + + if (*cp) { + dp = Strsave(cp); + --(*vp); + xfree((ptr_t) ** vp); + **vp = dp; + } + return (putn(expr(vp))); +} + +static Char * +operate(op, vp, p) + int op; + Char *vp, *p; +{ + Char opr[2]; + Char *vec[5]; + register Char **v = vec; + Char **vecp = v; + register int i; + + if (op != '=') { + if (*vp) + *v++ = vp; + opr[0] = op; + opr[1] = 0; + *v++ = opr; + if (op == '<' || op == '>') + *v++ = opr; + } + *v++ = p; + *v++ = 0; + i = expr(&vecp); + if (*vecp) + stderror(ERR_NAME | ERR_EXPRESSION); + return (putn(i)); +} + +static Char *putp; + +Char * +putn(n) + register int n; +{ + int num; + static Char number[15]; + + putp = number; + if (n < 0) { + n = -n; + *putp++ = '-'; + } + num = 2; /* confuse lint */ + if (sizeof(int) == num && ((unsigned int) n) == 0x8000) { + *putp++ = '3'; + n = 2768; +#ifdef pdp11 + } +#else + } + else { + num = 4; /* confuse lint */ + if (sizeof(int) == num && ((unsigned int) n) == 0x80000000) { + *putp++ = '2'; + n = 147483648; + } + } +#endif + putn1(n); + *putp = 0; + return (Strsave(number)); +} + +static void +putn1(n) + register int n; +{ + if (n > 9) + putn1(n / 10); + *putp++ = n % 10 + '0'; +} + +int +getn(cp) + register Char *cp; +{ + register int n; + int sign; + + sign = 0; + if (cp[0] == '+' && cp[1]) + cp++; + if (*cp == '-') { + sign++; + cp++; + if (!Isdigit(*cp)) + stderror(ERR_NAME | ERR_BADNUM); + } + n = 0; + while (Isdigit(*cp)) + n = n * 10 + *cp++ - '0'; + if (*cp) + stderror(ERR_NAME | ERR_BADNUM); + return (sign ? -n : n); +} + +Char * +value1(var, head) + Char *var; + struct varent *head; +{ + register struct varent *vp; + + vp = adrof1(var, head); + return (vp == 0 || vp->vec[0] == 0 ? STRNULL : vp->vec[0]); +} + +static struct varent * +madrof(pat, vp) + Char *pat; + register struct varent *vp; +{ + register struct varent *vp1; + + for (; vp; vp = vp->v_right) { + if (vp->v_left && (vp1 = madrof(pat, vp->v_left))) + return vp1; + if (Gmatch(vp->v_name, pat)) + return vp; + } + return vp; +} + +struct varent * +adrof1(name, v) + register Char *name; + register struct varent *v; +{ + register cmp; + + v = v->v_left; + while (v && ((cmp = *name - *v->v_name) || + (cmp = Strcmp(name, v->v_name)))) + if (cmp < 0) + v = v->v_left; + else + v = v->v_right; + return v; +} + +/* + * The caller is responsible for putting value in a safe place + */ +void +set(var, val) + Char *var, *val; +{ + register Char **vec = (Char **) xmalloc((size_t) (2 * sizeof(Char **))); + + vec[0] = val; + vec[1] = 0; + set1(var, vec, &shvhed); +} + +void +set1(var, vec, head) + Char *var, **vec; + struct varent *head; +{ + register Char **oldv = vec; + + gflag = 0; + tglob(oldv); + if (gflag) { + vec = globall(oldv); + if (vec == 0) { + blkfree(oldv); + stderror(ERR_NAME | ERR_NOMATCH); + return; + } + blkfree(oldv); + gargv = 0; + } + setq(var, vec, head); +} + + +void +setq(name, vec, p) + Char *name, **vec; + register struct varent *p; +{ + register struct varent *c; + register f; + + f = 0; /* tree hangs off the header's left link */ + while ((c = p->v_link[f]) != NULL) { + if ((f = *name - *c->v_name) == 0 && + (f = Strcmp(name, c->v_name)) == 0) { + blkfree(c->vec); + goto found; + } + p = c; + f = f > 0; + } + p->v_link[f] = c = (struct varent *) xmalloc((size_t) sizeof(struct varent)); + c->v_name = Strsave(name); + c->v_bal = 0; + c->v_left = c->v_right = 0; + c->v_parent = p; + balance(p, f, 0); +found: + trim(c->vec = vec); +} + +void +/*ARGSUSED*/ +unset(v, t) + Char **v; + struct command *t; +{ + unset1(v, &shvhed); +#ifdef FILEC + if (adrof(STRfilec) == 0) + filec = 0; +#endif + if (adrof(STRhistchars) == 0) { + HIST = '!'; + HISTSUB = '^'; + } + if (adrof(STRwordchars) == 0) + word_chars = STR_WORD_CHARS; +} + +void +unset1(v, head) + register Char *v[]; + struct varent *head; +{ + register struct varent *vp; + register int cnt; + + while (*++v) { + cnt = 0; + while ((vp = madrof(*v, head->v_left)) != NULL) + unsetv1(vp), cnt++; + if (cnt == 0) + setname(vis_str(*v)); + } +} + +void +unsetv(var) + Char *var; +{ + register struct varent *vp; + + if ((vp = adrof1(var, &shvhed)) == 0) + udvar(var); + unsetv1(vp); +} + +static void +unsetv1(p) + register struct varent *p; +{ + register struct varent *c, *pp; + register f; + + /* + * Free associated memory first to avoid complications. + */ + blkfree(p->vec); + xfree((ptr_t) p->v_name); + /* + * If p is missing one child, then we can move the other into where p is. + * Otherwise, we find the predecessor of p, which is guaranteed to have no + * right child, copy it into p, and move it's left child into it. + */ + if (p->v_right == 0) + c = p->v_left; + else if (p->v_left == 0) + c = p->v_right; + else { + for (c = p->v_left; c->v_right; c = c->v_right) + continue; + p->v_name = c->v_name; + p->vec = c->vec; + p = c; + c = p->v_left; + } + /* + * Move c into where p is. + */ + pp = p->v_parent; + f = pp->v_right == p; + if ((pp->v_link[f] = c) != NULL) + c->v_parent = pp; + /* + * Free the deleted node, and rebalance. + */ + xfree((ptr_t) p); + balance(pp, f, 1); +} + +void +setNS(cp) + Char *cp; +{ + set(cp, Strsave(STRNULL)); +} + +void +/*ARGSUSED*/ +shift(v, t) + Char **v; + struct command *t; +{ + register struct varent *argv; + register Char *name; + + v++; + name = *v; + if (name == 0) + name = STRargv; + else + (void) strip(name); + argv = adrof(name); + if (argv == 0) + udvar(name); + if (argv->vec[0] == 0) + stderror(ERR_NAME | ERR_NOMORE); + lshift(argv->vec, 1); +} + +static void +exportpath(val) + Char **val; +{ + Char exppath[BUFSIZ]; + + exppath[0] = 0; + if (val) + while (*val) { + if (Strlen(*val) + Strlen(exppath) + 2 > BUFSIZ) { + (void) fprintf(csherr, + "Warning: ridiculously long PATH truncated\n"); + break; + } + if ((**val != '/' || **val == '\0') && (euid == 0 || uid == 0)) + (void) fprintf(csherr, + "Warning: exported path contains relative components.\n"); + (void) Strcat(exppath, *val++); + if (*val == 0 || eq(*val, STRRparen)) + break; + (void) Strcat(exppath, STRcolon); + } + Setenv(STRPATH, exppath); +} + +#ifndef lint + /* + * Lint thinks these have null effect + */ + /* macros to do single rotations on node p */ +#define rright(p) (\ + t = (p)->v_left,\ + (t)->v_parent = (p)->v_parent,\ + ((p)->v_left = t->v_right) ? (t->v_right->v_parent = (p)) : 0,\ + (t->v_right = (p))->v_parent = t,\ + (p) = t) +#define rleft(p) (\ + t = (p)->v_right,\ + (t)->v_parent = (p)->v_parent,\ + ((p)->v_right = t->v_left) ? (t->v_left->v_parent = (p)) : 0,\ + (t->v_left = (p))->v_parent = t,\ + (p) = t) +#else +struct varent * +rleft(p) + struct varent *p; +{ + return (p); +} +struct varent * +rright(p) + struct varent *p; +{ + return (p); +} + +#endif /* ! lint */ + + +/* + * Rebalance a tree, starting at p and up. + * F == 0 means we've come from p's left child. + * D == 1 means we've just done a delete, otherwise an insert. + */ +static void +balance(p, f, d) + register struct varent *p; + register int f, d; +{ + register struct varent *pp; + +#ifndef lint + register struct varent *t; /* used by the rotate macros */ + +#endif + register ff; + + /* + * Ok, from here on, p is the node we're operating on; pp is it's parent; f + * is the branch of p from which we have come; ff is the branch of pp which + * is p. + */ + for (; (pp = p->v_parent) != NULL; p = pp, f = ff) { + ff = pp->v_right == p; + if (f ^ d) { /* right heavy */ + switch (p->v_bal) { + case -1: /* was left heavy */ + p->v_bal = 0; + break; + case 0: /* was balanced */ + p->v_bal = 1; + break; + case 1: /* was already right heavy */ + switch (p->v_right->v_bal) { + case 1: /* sigle rotate */ + pp->v_link[ff] = rleft(p); + p->v_left->v_bal = 0; + p->v_bal = 0; + break; + case 0: /* single rotate */ + pp->v_link[ff] = rleft(p); + p->v_left->v_bal = 1; + p->v_bal = -1; + break; + case -1: /* double rotate */ + (void) rright(p->v_right); + pp->v_link[ff] = rleft(p); + p->v_left->v_bal = + p->v_bal < 1 ? 0 : -1; + p->v_right->v_bal = + p->v_bal > -1 ? 0 : 1; + p->v_bal = 0; + break; + } + break; + } + } + else { /* left heavy */ + switch (p->v_bal) { + case 1: /* was right heavy */ + p->v_bal = 0; + break; + case 0: /* was balanced */ + p->v_bal = -1; + break; + case -1: /* was already left heavy */ + switch (p->v_left->v_bal) { + case -1: /* single rotate */ + pp->v_link[ff] = rright(p); + p->v_right->v_bal = 0; + p->v_bal = 0; + break; + case 0: /* signle rotate */ + pp->v_link[ff] = rright(p); + p->v_right->v_bal = -1; + p->v_bal = 1; + break; + case 1: /* double rotate */ + (void) rleft(p->v_left); + pp->v_link[ff] = rright(p); + p->v_left->v_bal = + p->v_bal < 1 ? 0 : -1; + p->v_right->v_bal = + p->v_bal > -1 ? 0 : 1; + p->v_bal = 0; + break; + } + break; + } + } + /* + * If from insert, then we terminate when p is balanced. If from + * delete, then we terminate when p is unbalanced. + */ + if ((p->v_bal == 0) ^ d) + break; + } +} + +void +plist(p) + register struct varent *p; +{ + register struct varent *c; + register len; + sigset_t sigset; + + if (setintr) { + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigprocmask(SIG_UNBLOCK, &sigset, NULL); + } + + for (;;) { + while (p->v_left) + p = p->v_left; +x: + if (p->v_parent == 0) /* is it the header? */ + return; + len = blklen(p->vec); + (void) fprintf(cshout, "%s\t", short2str(p->v_name)); + if (len != 1) + (void) fputc('(', cshout); + blkpr(cshout, p->vec); + if (len != 1) + (void) fputc(')', cshout); + (void) fputc('\n', cshout); + if (p->v_right) { + p = p->v_right; + continue; + } + do { + c = p; + p = p->v_parent; + } while (p->v_right == c); + goto x; + } +} diff --git a/bin/date/date.1 b/bin/date/date.1 new file mode 100644 index 000000000000..3b26b85eff80 --- /dev/null +++ b/bin/date/date.1 @@ -0,0 +1,232 @@ +.\" 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 +.\" +.Dd April 28, 1995 +.Dt DATE 1 +.Os +.Sh NAME +.Nm date +.Nd display or set date and time +.Sh SYNOPSIS +.Nm date +.Op Fl d Ar dst +.Op Fl r Ar seconds +.Op Fl t Ar minutes_west +.Op Fl nu +.Op Cm + Ns Ar format +.Op [yy[mm[dd[hh]]]]mm[\&.ss] +.Sh DESCRIPTION +.Nm Date +displays the current date and time when invoked without arguments. +Providing arguments will format the date and time in a user-defined +way or set the date. +Only the superuser may set the date. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl d +Set the kernel's value for daylight savings time. +If +.Ar dst +is non-zero, future calls +to +.Xr gettimeofday 2 +will return a non-zero +.Ql tz_dsttime . +.It Fl n +The utility +.Xr timed 8 +is used to synchronize the clocks on groups of machines. +By default, if +.Xr timed +is running, +.Nm date +will set the time on all of the machines in the local group. +The +.Fl n +option stops +.Nm date +from setting the time for other than the current machine. +.It Fl r +Print out the date and time in +.Ar seconds +from the Epoch. +.It Fl t +Set the kernel's value for minutes west of +.Tn GMT . +.Ar Minutes_west +specifies the number of minutes returned in +.Ql tz_minuteswest +by future calls to +.Xr gettimeofday 2 . +.It Fl u +Display or set the date in +.Tn UCT +(universal) time. +.El +.Pp +An operand with a leading plus (``+'') sign signals a user-defined format +string which specifies the format in which to display the date and time. +The format string may contain any of the conversion specifications described +in the +.Xr strftime 3 +manual page, as well as any arbitrary text. +A character is always output after the characters specified by +the format string. +The format string for the default display is: +.Bd -literal -offset indent +``%a %b %e %H:%M:%S %Z %Y''. +.Ed +.Pp +If an operand does not have a leading plus sign, it is interpreted as +a value for setting the system's notion of the current date and time. +The canonical representation for setting the date and time is: +.Pp +.Bl -tag -width Ds -compact -offset indent +.It Ar yy +Year in abbreviated form (.e.g 89 for 1989). +.It Ar mm +Numeric month. +A number from 1 to 12. +.It Ar dd +Day, a number from 1 to 31. +.It Ar hh +Hour, a number from 0 to 23. +.It Ar mm +Minutes, a number from 0 to 59. +.It Ar .ss +Seconds, a number from 0 to 61 (59 plus a a maximum of two leap seconds). +.El +.Pp +Everything but the minutes is optional. +.Pp +Time changes for Daylight Saving and Standard time and leap seconds +and years are handled automatically. +.Sh EXAMPLES +The command: +.Bd -literal -offset indent +date ``+DATE: %m/%d/%y%nTIME: %H:%M:%S'' +.Ed +.Pp +will display: +.Bd -literal -offset indent +DATE: 11/21/87 +TIME: 13:36:16 +.Ed +.Pp +The command: +.Bd -literal -offset indent +date 8506131627 +.Ed +.Pp +sets the date to +.Dq Li "June 13, 1985, 4:27 PM" . +.Pp +The command: +.Bd -literal -offset indent +date 1432 +.Ed +.Pp +sets the time to +.Li "2:32 PM" , +without modifying the date. +.Sh ENVIRONMENTAL VARIABLES +The following environment variables affect the execution of +.Nm date : +.Bl -tag -width TZ +.It Ev TZ +The timezone to use when displaying dates. +See +.Xr environ 7 +for more information. +.El +.Sh FILES +.Bl -tag -width /var/log/messages -compact +.It Pa /var/log/wtmp +A record of date resets and time changes. +.It Pa /var/log/messages +A record of the user setting the time. +.El +.Sh SEE ALSO +.Xr gettimeofday 2 , +.Xr strftime 3 , +.Xr utmp 5 , +.Xr timed 8 +.Rs +.%T "TSP: The Time Synchronization Protocol for UNIX 4.3BSD" +.%A R. Gusella +.%A S. Zatti +.Re +.Sh DIAGNOSTICS +Exit status is 0 on success, 1 if unable to set the date, and 2 +if able to set the local date, but unable to set it globally. +.Pp +Occasionally, when +.Xr timed +synchronizes the time on many hosts, the setting of a new time value may +require more than a few seconds. +On these occasions, +.Nm date +prints: +.Ql Network time being set . +The message +.Ql Communication error with timed +occurs when the communication +between +.Nm date +and +.Xr timed +fails. +.Sh BUGS +The system attempts to keep the date in a format closely compatible +with +.Tn VMS . +.Tn VMS , +however, uses local time (rather than +.Tn GMT ) +and does not understand daylight-savings time. +Thus, if you use both +.Tn UNIX +and +.Tn VMS , +.Tn VMS +will be running on +.Tn GMT . +.Sh STANDARDS +The +.Nm date +command is expected to be compatible with +.St -p1003.2 . diff --git a/bin/date/date.c b/bin/date/date.c new file mode 100644 index 000000000000..6d338b5a4944 --- /dev/null +++ b/bin/date/date.c @@ -0,0 +1,237 @@ +/* + * Copyright (c) 1985, 1987, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1985, 1987, 1988, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)date.c 8.2 (Berkeley) 4/28/95"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "extern.h" + +time_t tval; +int retval, nflag; + +static void setthetime __P((char *)); +static void badformat __P((void)); +static void usage __P((void)); + +int logwtmp __P((char *, char *, char *)); + +int +main(argc, argv) + int argc; + char **argv; +{ + extern int optind; + extern char *optarg; + struct timezone tz; + int ch, rflag; + char *format, buf[1024]; + + tz.tz_dsttime = tz.tz_minuteswest = 0; + rflag = 0; + while ((ch = getopt(argc, argv, "d:nr:ut:")) != -1) + switch((char)ch) { + case 'd': /* daylight savings time */ + tz.tz_dsttime = atoi(optarg) ? 1 : 0; + break; + case 'n': /* don't set network */ + nflag = 1; + break; + case 'r': /* user specified seconds */ + rflag = 1; + tval = atol(optarg); + break; + case 'u': /* do everything in GMT */ + (void)setenv("TZ", "GMT0", 1); + break; + case 't': /* minutes west of GMT */ + /* error check; don't allow "PST" */ + if (isdigit(*optarg)) { + tz.tz_minuteswest = atoi(optarg); + break; + } + /* FALLTHROUGH */ + default: + usage(); + } + argc -= optind; + argv += optind; + + /* + * If -d or -t, set the timezone or daylight savings time; this + * doesn't belong here, there kernel should not know about either. + */ + if ((tz.tz_minuteswest || tz.tz_dsttime) && + settimeofday(NULL, &tz)) + err(1, "settimeofday"); + + if (!rflag && time(&tval) == -1) + err(1, "time"); + + format = "%a %b %e %H:%M:%S %Z %Y"; + + /* allow the operands in any order */ + if (*argv && **argv == '+') { + format = *argv + 1; + ++argv; + } + + if (*argv) { + setthetime(*argv); + ++argv; + } + + if (*argv && **argv == '+') + format = *argv + 1; + + (void)strftime(buf, sizeof(buf), format, localtime(&tval)); + (void)printf("%s\n", buf); + exit(retval); +} + +#define ATOI2(ar) ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2; +void +setthetime(p) + register char *p; +{ + register struct tm *lt; + struct timeval tv; + char *dot, *t; + + for (t = p, dot = NULL; *t; ++t) { + if (isdigit(*t)) + continue; + if (*t == '.' && dot == NULL) { + dot = t; + continue; + } + badformat(); + } + + lt = localtime(&tval); + + if (dot != NULL) { /* .ss */ + *dot++ = '\0'; + if (strlen(dot) != 2) + badformat(); + lt->tm_sec = ATOI2(dot); + if (lt->tm_sec > 61) + badformat(); + } else + lt->tm_sec = 0; + + switch (strlen(p)) { + case 10: /* yy */ + lt->tm_year = ATOI2(p); + if (lt->tm_year < 69) /* hack for 2000 ;-} */ + lt->tm_year += 100; + /* FALLTHROUGH */ + case 8: /* mm */ + lt->tm_mon = ATOI2(p); + if (lt->tm_mon > 12) + badformat(); + --lt->tm_mon; /* time struct is 0 - 11 */ + /* FALLTHROUGH */ + case 6: /* dd */ + lt->tm_mday = ATOI2(p); + if (lt->tm_mday > 31) + badformat(); + /* FALLTHROUGH */ + case 4: /* hh */ + lt->tm_hour = ATOI2(p); + if (lt->tm_hour > 23) + badformat(); + /* FALLTHROUGH */ + case 2: /* mm */ + lt->tm_min = ATOI2(p); + if (lt->tm_min > 59) + badformat(); + break; + default: + badformat(); + } + + /* convert broken-down time to GMT clock time */ + if ((tval = mktime(lt)) == -1) + badformat(); + + /* set the time */ + if (nflag || netsettime(tval)) { + logwtmp("|", "date", ""); + tv.tv_sec = tval; + tv.tv_usec = 0; + if (settimeofday(&tv, NULL)) { + perror("date: settimeofday"); + exit(1); + } + logwtmp("{", "date", ""); + } + + if ((p = getlogin()) == NULL) + p = "???"; + syslog(LOG_AUTH | LOG_NOTICE, "date set by %s", p); +} + +static void +badformat() +{ + warnx("illegal time format"); + usage(); +} + +static void +usage() +{ + (void)fprintf(stderr, + "usage: date [-nu] [-d dst] [-r seconds] [-t west] [+format]\n"); + (void)fprintf(stderr, " [yy[mm[dd[hh]]]]mm[.ss]]\n"); + exit(1); +} diff --git a/bin/date/netdate.c b/bin/date/netdate.c new file mode 100644 index 000000000000..ef254c6f3120 --- /dev/null +++ b/bin/date/netdate.c @@ -0,0 +1,183 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)netdate.c 8.2 (Berkeley) 4/28/95"; +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#define TSPTYPES +#include + +#include +#include +#include +#include +#include + +#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); + } + + memset(&dest, 0, sizeof(dest)); + dest.sin_port = sp->s_port; + dest.sin_family = AF_INET; + dest.sin_addr.s_addr = htonl((u_long)INADDR_ANY); + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) { + if (errno != EPROTONOSUPPORT) + warn("timed"); + return (retval = 2); + } + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + for (port = IPPORT_RESERVED - 1; port > IPPORT_RESERVED / 2; port--) { + sin.sin_port = htons((u_short)port); + if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0) + break; + if (errno == EADDRINUSE) + continue; + if (errno != EADDRNOTAVAIL) + warn("bind"); + goto bad; + } + if (port == IPPORT_RESERVED / 2) { + warnx("all ports in use"); + goto bad; + } + msg.tsp_type = TSP_SETDATE; + msg.tsp_vers = TSPVERSION; + if (gethostname(hostname, sizeof(hostname))) { + warn("gethostname"); + goto bad; + } + (void)strncpy(msg.tsp_name, hostname, sizeof(hostname)); + msg.tsp_seq = htons((u_short)0); + msg.tsp_time.tv_sec = htonl((u_long)tval); + msg.tsp_time.tv_usec = htonl((u_long)0); + length = sizeof(struct sockaddr_in); + if (connect(s, (struct sockaddr *)&dest, length) < 0) { + warn("connect"); + goto bad; + } + if (send(s, (char *)&msg, sizeof(struct tsp), 0) < 0) { + if (errno != ECONNREFUSED) + warn("send"); + goto bad; + } + + timed_ack = -1; + waittime = WAITACK; +loop: + tout.tv_sec = waittime; + tout.tv_usec = 0; + + FD_ZERO(&ready); + FD_SET(s, &ready); + found = select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0, &tout); + + length = sizeof(err); + if (!getsockopt(s, + SOL_SOCKET, SO_ERROR, (char *)&err, &length) && err) { + if (err != ECONNREFUSED) + warn("send (delayed error)"); + goto bad; + } + + if (found > 0 && FD_ISSET(s, &ready)) { + length = sizeof(struct sockaddr_in); + if (recvfrom(s, &msg, sizeof(struct tsp), 0, + (struct sockaddr *)&from, &length) < 0) { + if (errno != ECONNREFUSED) + warn("recvfrom"); + goto bad; + } + msg.tsp_seq = ntohs(msg.tsp_seq); + msg.tsp_time.tv_sec = ntohl(msg.tsp_time.tv_sec); + msg.tsp_time.tv_usec = ntohl(msg.tsp_time.tv_usec); + switch (msg.tsp_type) { + case TSP_ACK: + timed_ack = TSP_ACK; + waittime = WAITDATEACK; + goto loop; + case TSP_DATEACK: + (void)close(s); + return (0); + default: + warnx("wrong ack received from timed: %s", + tsptype[msg.tsp_type]); + timed_ack = -1; + break; + } + } + if (timed_ack == -1) + warnx("can't reach time daemon, time set locally"); + +bad: + (void)close(s); + return (retval = 2); +} diff --git a/bin/hostname/hostname.1 b/bin/hostname/hostname.1 new file mode 100644 index 000000000000..aa7ea5469aac --- /dev/null +++ b/bin/hostname/hostname.1 @@ -0,0 +1,66 @@ +.\" 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 +.\" +.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/netstart , +normally run at boot +time. +.Pp +Options: +.Bl -tag -width flag +.It Fl s +Trims off any domain information from the printed +name. +.El +.Sh SEE ALSO +.Xr gethostname 2 , +.Xr sethostname 2 +.Sh HISTORY +The +.Nm hostname +command appeared in +.Bx 4.2 . diff --git a/bin/hostname/hostname.c b/bin/hostname/hostname.c new file mode 100644 index 000000000000..cbe654c81bda --- /dev/null +++ b/bin/hostname/hostname.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1988, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)hostname.c 8.2 (Berkeley) 4/28/95"; +#endif /* not lint */ + +#include + +#include +#include +#include +#include +#include + +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 (*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] [hostname]\n"); + exit(1); +} diff --git a/bin/kill/kill.1 b/bin/kill/kill.1 new file mode 100644 index 000000000000..66779b7be573 --- /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 +.\" +.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 kill 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 +.Nm Kill +is a built-in to +.Xr csh 1 ; +it allows job specifiers of the form ``%...'' as arguments +so process id's are not as often used as +.Nm kill +arguments. +See +.Xr csh 1 +for details. +.Sh SEE ALSO +.Xr csh 1 , +.Xr ps 1 , +.Xr kill 2 , +.Xr sigvec 2 +.Sh STANDARDS +The +.Nm kill +function is expected to be +.St -p1003.2 +compatible. +.Sh HISTORY +A +.Nm kill +command appeared in +.At v6 . +.Sh BUGS +A replacement for the command +.Dq Li kill 0 +for +.Xr csh 1 +users should be provided. diff --git a/bin/kill/kill.c b/bin/kill/kill.c new file mode 100644 index 000000000000..a3316a42a878 --- /dev/null +++ b/bin/kill/kill.c @@ -0,0 +1,187 @@ +/* + * Copyright (c) 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1988, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)kill.c 8.4 (Berkeley) 4/28/95"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include + +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[]; +{ + const char *const *p; + 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, "usage: kill [-s signal_name] pid ...\n"); + (void)fprintf(stderr, " kill -l [exit_status]\n"); + (void)fprintf(stderr, " kill -signal_name pid ...\n"); + (void)fprintf(stderr, " kill -signal_number pid ...\n"); + exit(1); +} diff --git a/bin/ls/ls.1 b/bin/ls/ls.1 new file mode 100644 index 000000000000..6f5f58fbad44 --- /dev/null +++ b/bin/ls/ls.1 @@ -0,0 +1,338 @@ +.\" Copyright (c) 1980, 1990, 1991, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)ls.1 8.7 (Berkeley) 7/29/94 +.\" +.Dd July 29, 1994 +.Dt LS 1 +.Os +.Sh NAME +.Nm ls +.Nd list directory contents +.Sh SYNOPSIS +.Nm ls +.Op Fl ACFLRTWacdfiloqrstu1 +.Op Ar file ... +.Sh DESCRIPTION +For each operand that names a +.Ar file +of a type other than +directory, +.Nm ls +displays its name as well as any requested, +associated information. +For each operand that names a +.Ar file +of type directory, +.Nm ls +displays the names of files contained +within that directory, as well as any requested, associated +information. +.Pp +If no operands are given, the contents of the current +directory are displayed. +If more than one operand is given, +non-directory operands are displayed first; directory +and non-directory operands are sorted separately and in +lexicographical order. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl A +List all entries except for +.Ql \&. +and +.Ql \&.. . +Always set for the super-user. +.It Fl C +Force multi-column output; this is the default when output is to a terminal. +.It Fl F +Display a slash (/) immediately after each pathname +that is a directory, an asterisk (*) after each that is +executable, +an at sign (@) after each symbolic link, +and a percent sign (%) after each whiteout. +.\" and a vertical bar (|) after each that is a +.\" .Tn FIFO . +.It Fl L +If argument is a symbolic link, list the file or directory the link references +rather than the link itself. +.It Fl R +Recursively list subdirectories encountered. +.It Fl T +Display complete time information for the file, including +month, day, hour, minute, second, and year. +.It Fl W +Display whiteouts when scanning directories. +.It Fl a +Include directory entries whose names begin with a +dot (.). +.It Fl c +Use time when file status was last changed for sorting or printing. +.It Fl d +Directories are listed as plain files (not searched recursively) and +symbolic links in the argument list are not indirected through. +.It Fl f +Output is not sorted. +.It Fl i +For each file, print the file's file serial number (inode number). +.It Fl l +(The lowercase letter ``ell.'') List in long format. (See below.) +If the output is to a terminal, a total sum for all the file +sizes is output on a line before the long listing. +.It Fl o +Include the file flags in a long +.Pq Fl l +output +.It Fl q +Force printing of non-graphic characters in file names as +the character `?'; this is the default when output is to a terminal. +.It Fl r +Reverse the order of the sort to get reverse +lexicographical order or the oldest entries first. +.It Fl s +Display the number of file system blocks actually used by each file, in units +of 512 bytes, where partial units are rounded up to the next integer value. +If the output is to a terminal, a total sum for all the file +sizes is output on a line before the listing. +.It Fl t +Sort by time modified (most recently modified +first) before sorting the operands by lexicographical +order. +.It Fl u +Use time of last access, +instead of last modification +of the file for sorting +.Pq Fl t +or printing +.Pq Fl l . +.It Fl \&1 +(The numeric digit ``one.'') Force output to be +one entry per line. +This is the default when +output is not to a terminal. +.El +.Pp +The +.Fl 1 , +.Fl C , +and +.Fl l +options all override each other; the last one specified determines +the format used. +.Pp +The +.Fl c , +and +.Fl u +options override each other; the last one specified determines +the file time used. +.Pp +By default, +.Nm ls +lists one entry per line to standard +output; the exceptions are to terminals or when the +.Fl C +option is specified. +.Pp +File information is displayed with one or more +s separating the information associated with the +.Fl i , +.Fl s , +and +.Fl l +options. +.Ss The Long Format +If the +.Fl l +option is given, the following information +is displayed for each file: +file mode, +number of links, owner name, group name, +number of bytes in the file, abbreviated +month, day-of-month file was last modified, +hour file last modified, minute file last +modified, and the pathname. +In addition, for each directory whose contents are displayed, the total +number of 512-byte blocks used by the files in the directory is displayed +on a line by itself immediately before the information for the files in the +directory. +.Pp +If the owner or group names are not a known user or group name +the numeric ID's are displayed. +.Pp +If the file is a character special or block special file, +the major and minor device numbers for the file are displayed +in the size field. If the file is a symbolic link the pathname of the +linked-to file is preceded by +.Dq \-> . +.Pp +The file mode printed under the -l option consists of the +entry type, owner permissions, and group permissions. +The entry type character describes the type of file, as +follows: +.Pp +.Bl -tag -width 4n -offset indent -compact +.It Sy b +Block special file. +.It Sy c +Character special file. +.It Sy d +Directory. +.It Sy l +Symbolic link. +.It Sy s +Socket link. +.\" .It Sy p +.\" .Tn FIFO . +.It Sy w +Whiteout. +.It Sy \- +Regular file. +.El +.Pp +The next three fields +are three characters each: +owner permissions, +group permissions, and +other permissions. +Each field has three character positions: +.Bl -enum -offset indent +.It +If +.Sy r , +the file is readable; if +.Sy \- , +it is not readable. +.It +If +.Sy w , +the file is writable; if +.Sy \- , +it is not writable. +.It +The first of the following that applies: +.Bl -tag -width 4n -offset indent +.It Sy S +If in the owner permissions, the file is not executable and +set-user-ID mode is set. +If in the group permissions, the file is not executable +and set-group-ID mode is set. +.It Sy s +If in the owner permissions, the file is executable +and set-user-ID mode is set. +If in the group permissions, the file is executable +and setgroup-ID mode is set. +.It Sy x +The file is executable or the directory is +searchable. +.It Sy \- +The file is neither readable, writeable, executable, +nor set-user-ID nor set-group-ID mode, nor sticky. (See below.) +.El +.Pp +These next two apply only to the third character in the last group +(other permissions). +.Bl -tag -width 4n -offset indent +.It Sy T +The sticky bit is set +(mode +.Li 1000 ) , +but not execute or search permission. (See +.Xr chmod 1 +or +.Xr sticky 8 . ) +.It Sy t +The sticky bit is set (mode +.Li 1000 ) , +and is searchable or executable. +(See +.Xr chmod 1 +or +.Xr sticky 8 . ) +.El +.El +.Pp +The +.Nm ls +utility exits 0 on success, and >0 if an error occurs. +.Sh ENVIRONMENTAL VARIABLES +The following environment variables affect the execution of +.Nm ls : +.Bl -tag -width BLOCKSIZE +.It Ev BLOCKSIZE +If the environmental variable +.Ev BLOCKSIZE +is set, the block counts +(see +.Fl s ) +will be displayed in units of that size block. +.It COLUMNS +If this variable contains a string representing a +decimal integer, it is used as the +column position width for displaying +multiple-text-column output. +The +.Nm ls +utility calculates how +many pathname text columns to display +based on the width provided. +(See +.Fl C . ) +.It Ev TZ +The timezone to use when displaying dates. +See +.Xr environ 7 +for more information. +.El +.Sh COMPATIBILITY +The group field is now automatically included in the long listing for +files in order to be compatible with the +.St -p1003.2 +specification. +.Sh SEE ALSO +.Xr chmod 1 , +.Xr symlink 7 , +.Xr sticky 8 +.Sh HISTORY +An +.Nm ls +command appeared in +.At v6 . +.Sh STANDARDS +The +.Nm ls +function is expected to be a superset of the +.St -p1003.2 +specification. diff --git a/bin/ls/ls.c b/bin/ls/ls.c new file mode 100644 index 000000000000..18ac946a56a3 --- /dev/null +++ b/bin/ls/ls.c @@ -0,0 +1,518 @@ +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Michael Fischbein. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1989, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)ls.c 8.7 (Berkeley) 8/5/94"; +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ls.h" +#include "extern.h" + +static void display __P((FTSENT *, FTSENT *)); +static int mastercmp __P((const FTSENT **, const FTSENT **)); +static void traverse __P((int, char **, int)); + +static void (*printfcn) __P((DISPLAY *)); +static int (*sortfcn) __P((const FTSENT *, const FTSENT *)); + +long blocksize; /* block size units */ +int termwidth = 80; /* default terminal width */ + +/* flags */ +int f_accesstime; /* use time of last access */ +int f_column; /* columnated format */ +int f_flags; /* show flags associated with a file */ +int f_inode; /* print inode */ +int f_listdir; /* list actual directory, not contents */ +int f_listdot; /* list files beginning with . */ +int f_longform; /* long listing format */ +int f_newline; /* if precede with newline */ +int f_nonprint; /* show unprintables as ? */ +int f_nosort; /* don't sort output */ +int f_recursive; /* ls subdirectories also */ +int f_reversesort; /* reverse whatever sort is used */ +int f_sectime; /* print the real time for all files */ +int f_singlecol; /* use single column output */ +int f_size; /* list size in short listing */ +int f_statustime; /* use time of last mode change */ +int f_dirname; /* if precede with directory name */ +int f_timesort; /* sort by time vice name */ +int f_type; /* add type character for non-regular files */ +int f_whiteout; /* show whiteout entries */ + +int +main(argc, argv) + int argc; + char *argv[]; +{ + static char dot[] = ".", *dotav[] = { dot, NULL }; + struct winsize win; + int ch, fts_options, notused; + char *p; + + /* Terminal defaults to -Cq, non-terminal defaults to -1. */ + if (isatty(STDOUT_FILENO)) { + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == -1 || + !win.ws_col) { + if ((p = getenv("COLUMNS")) != NULL) + termwidth = atoi(p); + } + else + termwidth = win.ws_col; + f_column = f_nonprint = 1; + } else + f_singlecol = 1; + + /* Root is -A automatically. */ + if (!getuid()) + f_listdot = 1; + + fts_options = FTS_PHYSICAL; + while ((ch = getopt(argc, argv, "1ACFLRTWacdfgiloqrstu")) != EOF) { + switch (ch) { + /* + * The -1, -C and -l options all override each other so shell + * aliasing works right. + */ + case '1': + f_singlecol = 1; + f_column = f_longform = 0; + break; + case 'C': + f_column = 1; + f_longform = f_singlecol = 0; + break; + case 'l': + f_longform = 1; + f_column = f_singlecol = 0; + break; + /* The -c and -u options override each other. */ + case 'c': + f_statustime = 1; + f_accesstime = 0; + break; + case 'u': + f_accesstime = 1; + f_statustime = 0; + break; + case 'F': + f_type = 1; + break; + case 'L': + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + break; + case 'R': + f_recursive = 1; + break; + case 'a': + fts_options |= FTS_SEEDOT; + /* FALLTHROUGH */ + case 'A': + f_listdot = 1; + break; + /* The -d option turns off the -R option. */ + case 'd': + f_listdir = 1; + f_recursive = 0; + break; + case 'f': + f_nosort = 1; + break; + case 'g': /* Compatibility with 4.3BSD. */ + break; + case 'i': + f_inode = 1; + break; + case 'o': + f_flags = 1; + break; + case 'q': + f_nonprint = 1; + break; + case 'r': + f_reversesort = 1; + break; + case 's': + f_size = 1; + break; + case 'T': + f_sectime = 1; + break; + case 't': + f_timesort = 1; + break; + case 'W': + f_whiteout = 1; + break; + default: + case '?': + usage(); + } + } + argc -= optind; + argv += optind; + + /* + * If not -F, -i, -l, -s or -t options, don't require stat + * information. + */ + if (!f_inode && !f_longform && !f_size && !f_timesort && !f_type) + fts_options |= FTS_NOSTAT; + + /* + * If not -F, -d or -l options, follow any symbolic links listed on + * the command line. + */ + if (!f_longform && !f_listdir && !f_type) + fts_options |= FTS_COMFOLLOW; + + /* + * If -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) { + (void)getbsize(¬used, &blocksize); + blocksize /= 512; + } + + /* Select a sort function. */ + if (f_reversesort) { + if (!f_timesort) + sortfcn = revnamecmp; + else if (f_accesstime) + sortfcn = revacccmp; + else if (f_statustime) + sortfcn = revstatcmp; + else /* Use modification time. */ + sortfcn = revmodcmp; + } else { + if (!f_timesort) + sortfcn = namecmp; + else if (f_accesstime) + sortfcn = acccmp; + else if (f_statustime) + sortfcn = statcmp; + else /* Use modification time. */ + sortfcn = modcmp; + } + + /* Select a print function. */ + if (f_singlecol) + printfcn = printscol; + else if (f_longform) + printfcn = printlong; + else + printfcn = printcol; + + if (argc) + traverse(argc, argv, fts_options); + else + traverse(1, dotav, fts_options); + exit(0); +} + +static int output; /* If anything output. */ + +/* + * Traverse() walks the logical directory structure specified by the argv list + * in the order specified by the mastercmp() comparison function. During the + * traversal it passes linked lists of structures to display() which represent + * a superset (may be exact set) of the files to be displayed. + */ +static void +traverse(argc, argv, options) + int argc, options; + char *argv[]; +{ + FTS *ftsp; + FTSENT *p, *chp; + int ch_options; + + if ((ftsp = + fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL) + err(1, NULL); + + display(NULL, fts_children(ftsp, 0)); + if (f_listdir) + return; + + /* + * If not recursing down this tree and don't need stat info, just get + * the names. + */ + ch_options = !f_recursive && options & FTS_NOSTAT ? FTS_NAMEONLY : 0; + + while ((p = fts_read(ftsp)) != NULL) + switch (p->fts_info) { + case FTS_DC: + warnx("%s: directory causes a cycle", p->fts_name); + break; + case FTS_DNR: + case FTS_ERR: + warnx("%s: %s", p->fts_name, strerror(p->fts_errno)); + break; + case FTS_D: + if (p->fts_level != FTS_ROOTLEVEL && + p->fts_name[0] == '.' && !f_listdot) + break; + + /* + * If already output something, put out a newline as + * a separator. If multiple arguments, precede each + * directory with its name. + */ + if (output) + (void)printf("\n%s:\n", p->fts_path); + else if (argc > 1) { + (void)printf("%s:\n", p->fts_path); + output = 1; + } + + chp = fts_children(ftsp, ch_options); + display(p, chp); + + if (!f_recursive && chp != NULL) + (void)fts_set(ftsp, p, FTS_SKIP); + break; + } + if (errno) + err(1, "fts_read"); +} + +/* + * Display() takes a linked list of FTSENT structures and passes the list + * along with any other necessary information to the print function. P + * points to the parent directory of the display list. + */ +static void +display(p, list) + FTSENT *p, *list; +{ + struct stat *sp; + DISPLAY d; + FTSENT *cur; + NAMES *np; + u_quad_t maxsize; + u_long btotal, maxblock, maxinode, maxlen, maxnlink; + int bcfile, flen, glen, ulen, maxflags, maxgroup, maxuser; + int entries, needstats; + char *user, *group, *flags, buf[20]; /* 32 bits == 10 digits */ + + /* + * If list is NULL there are two possibilities: that the parent + * directory p has no children, or that fts_children() returned an + * error. We ignore the error case since it will be replicated + * on the next call to fts_read() on the post-order visit to the + * directory p, and will be signalled in traverse(). + */ + if (list == NULL) + return; + + needstats = f_inode || f_longform || f_size; + flen = 0; + btotal = maxblock = maxinode = maxlen = maxnlink = 0; + bcfile = 0; + maxuser = maxgroup = maxflags = 0; + maxsize = 0; + for (cur = list, entries = 0; cur; cur = cur->fts_link) { + if (cur->fts_info == FTS_ERR || cur->fts_info == FTS_NS) { + warnx("%s: %s", + cur->fts_name, strerror(cur->fts_errno)); + cur->fts_number = NO_PRINT; + continue; + } + + /* + * P is NULL if list is the argv list, to which different rules + * apply. + */ + if (p == NULL) { + /* Directories will be displayed later. */ + if (cur->fts_info == FTS_D && !f_listdir) { + cur->fts_number = NO_PRINT; + continue; + } + } else { + /* Only display dot file if -a/-A set. */ + if (cur->fts_name[0] == '.' && !f_listdot) { + cur->fts_number = NO_PRINT; + continue; + } + } + if (f_nonprint) + prcopy(cur->fts_name, cur->fts_name, cur->fts_namelen); + if (cur->fts_namelen > maxlen) + maxlen = cur->fts_namelen; + if (needstats) { + sp = cur->fts_statp; + if (sp->st_blocks > maxblock) + maxblock = sp->st_blocks; + if (sp->st_ino > maxinode) + maxinode = sp->st_ino; + if (sp->st_nlink > maxnlink) + maxnlink = sp->st_nlink; + if (sp->st_size > maxsize) + maxsize = sp->st_size; + + btotal += sp->st_blocks; + if (f_longform) { + user = user_from_uid(sp->st_uid, 0); + if ((ulen = strlen(user)) > maxuser) + maxuser = ulen; + group = group_from_gid(sp->st_gid, 0); + if ((glen = strlen(group)) > maxgroup) + maxgroup = glen; + if (f_flags) { + flags = + flags_to_string(sp->st_flags, "-"); + if ((flen = strlen(flags)) > maxflags) + maxflags = flen; + } else + flen = 0; + + if ((np = malloc(sizeof(NAMES) + + ulen + glen + flen + 3)) == NULL) + err(1, NULL); + + np->user = &np->data[0]; + (void)strcpy(np->user, user); + np->group = &np->data[ulen + 1]; + (void)strcpy(np->group, group); + + if (S_ISCHR(sp->st_mode) || + S_ISBLK(sp->st_mode)) + bcfile = 1; + + if (f_flags) { + np->flags = &np->data[ulen + glen + 2]; + (void)strcpy(np->flags, flags); + } + cur->fts_pointer = np; + } + } + ++entries; + } + + if (!entries) + return; + + d.list = list; + d.entries = entries; + d.maxlen = maxlen; + if (needstats) { + d.bcfile = bcfile; + d.btotal = btotal; + (void)snprintf(buf, sizeof(buf), "%lu", maxblock); + d.s_block = strlen(buf); + d.s_flags = maxflags; + d.s_group = maxgroup; + (void)snprintf(buf, sizeof(buf), "%lu", maxinode); + d.s_inode = strlen(buf); + (void)snprintf(buf, sizeof(buf), "%lu", maxnlink); + d.s_nlink = strlen(buf); + (void)snprintf(buf, sizeof(buf), "%qu", maxsize); + d.s_size = strlen(buf); + d.s_user = maxuser; + } + + printfcn(&d); + output = 1; + + if (f_longform) + for (cur = list; cur; cur = cur->fts_link) + free(cur->fts_pointer); +} + +/* + * Ordering for mastercmp: + * If ordering the argv (fts_level = FTS_ROOTLEVEL) return non-directories + * as larger than directories. Within either group, use the sort function. + * All other levels use the sort function. Error entries remain unsorted. + */ +static int +mastercmp(a, b) + const FTSENT **a, **b; +{ + int a_info, b_info; + + a_info = (*a)->fts_info; + if (a_info == FTS_ERR) + return (0); + b_info = (*b)->fts_info; + if (b_info == FTS_ERR) + return (0); + + if (a_info == FTS_NS || b_info == FTS_NS) + return (namecmp(*a, *b)); + + if (a_info == b_info) + return (sortfcn(*a, *b)); + + if ((*a)->fts_level == FTS_ROOTLEVEL) + if (a_info == FTS_D) + return (1); + else if (b_info == FTS_D) + return (-1); + else + return (sortfcn(*a, *b)); + else + return (sortfcn(*a, *b)); +} diff --git a/bin/ls/print.c b/bin/ls/print.c new file mode 100644 index 000000000000..c5e64847b076 --- /dev/null +++ b/bin/ls/print.c @@ -0,0 +1,296 @@ +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Michael Fischbein. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)print.c 8.5 (Berkeley) 7/28/94"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ls.h" +#include "extern.h" + +static int printaname __P((FTSENT *, u_long, u_long)); +static void printlink __P((FTSENT *)); +static void printtime __P((time_t)); +static int printtype __P((u_int)); + +#define IS_NOPRINT(p) ((p)->fts_number == NO_PRINT) + +void +printscol(dp) + DISPLAY *dp; +{ + FTSENT *p; + + for (p = dp->list; p; p = p->fts_link) { + if (IS_NOPRINT(p)) + continue; + (void)printaname(p, dp->s_inode, dp->s_block); + (void)putchar('\n'); + } +} + +void +printlong(dp) + DISPLAY *dp; +{ + struct stat *sp; + FTSENT *p; + NAMES *np; + char buf[20]; + + if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size)) + (void)printf("total %lu\n", howmany(dp->btotal, blocksize)); + + for (p = dp->list; p; p = p->fts_link) { + if (IS_NOPRINT(p)) + continue; + sp = p->fts_statp; + if (f_inode) + (void)printf("%*lu ", dp->s_inode, sp->st_ino); + if (f_size) + (void)printf("%*qd ", + dp->s_block, howmany(sp->st_blocks, blocksize)); + (void)strmode(sp->st_mode, buf); + np = p->fts_pointer; + (void)printf("%s %*u %-*s %-*s ", buf, dp->s_nlink, + sp->st_nlink, dp->s_user, np->user, dp->s_group, + np->group); + if (f_flags) + (void)printf("%-*s ", dp->s_flags, np->flags); + if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode)) + (void)printf("%3d, %3d ", + major(sp->st_rdev), minor(sp->st_rdev)); + else if (dp->bcfile) + (void)printf("%*s%*qd ", + 8 - dp->s_size, "", dp->s_size, sp->st_size); + else + (void)printf("%*qd ", dp->s_size, sp->st_size); + if (f_accesstime) + printtime(sp->st_atime); + else if (f_statustime) + printtime(sp->st_ctime); + else + printtime(sp->st_mtime); + (void)printf("%s", p->fts_name); + if (f_type) + (void)printtype(sp->st_mode); + if (S_ISLNK(sp->st_mode)) + printlink(p); + (void)putchar('\n'); + } +} + +#define TAB 8 + +void +printcol(dp) + DISPLAY *dp; +{ + extern int termwidth; + static FTSENT **array; + static int lastentries = -1; + FTSENT *p; + int base, chcnt, cnt, col, colwidth, num; + int endcol, numcols, numrows, row; + + /* + * Have to do random access in the linked list -- build a table + * of pointers. + */ + if (dp->entries > lastentries) { + lastentries = dp->entries; + if ((array = + realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) { + warn(NULL); + printscol(dp); + } + } + for (p = dp->list, num = 0; p; p = p->fts_link) + if (p->fts_number != NO_PRINT) + array[num++] = p; + + colwidth = dp->maxlen; + if (f_inode) + colwidth += dp->s_inode + 1; + if (f_size) + colwidth += dp->s_block + 1; + if (f_type) + colwidth += 1; + + colwidth = (colwidth + TAB) & ~(TAB - 1); + if (termwidth < 2 * colwidth) { + printscol(dp); + return; + } + + numcols = termwidth / colwidth; + numrows = num / numcols; + if (num % numcols) + ++numrows; + + if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size)) + (void)printf("total %lu\n", howmany(dp->btotal, blocksize)); + for (row = 0; row < numrows; ++row) { + endcol = colwidth; + for (base = row, chcnt = col = 0; col < numcols; ++col) { + chcnt += printaname(array[base], dp->s_inode, + dp->s_block); + if ((base += numrows) >= num) + break; + while ((cnt = (chcnt + TAB & ~(TAB - 1))) <= endcol) { + (void)putchar('\t'); + chcnt = cnt; + } + endcol += colwidth; + } + (void)putchar('\n'); + } +} + +/* + * print [inode] [size] name + * return # of characters printed, no trailing characters. + */ +static int +printaname(p, inodefield, sizefield) + FTSENT *p; + u_long sizefield, inodefield; +{ + struct stat *sp; + int chcnt; + + sp = p->fts_statp; + chcnt = 0; + if (f_inode) + chcnt += printf("%*lu ", (int)inodefield, sp->st_ino); + if (f_size) + chcnt += printf("%*qd ", + (int)sizefield, howmany(sp->st_blocks, blocksize)); + chcnt += printf("%s", p->fts_name); + if (f_type) + chcnt += printtype(sp->st_mode); + return (chcnt); +} + +static void +printtime(ftime) + time_t ftime; +{ + int i; + char *longstring; + + longstring = ctime(&ftime); + for (i = 4; i < 11; ++i) + (void)putchar(longstring[i]); + +#define SIXMONTHS ((DAYSPERNYEAR / 2) * SECSPERDAY) + if (f_sectime) + for (i = 11; i < 24; i++) + (void)putchar(longstring[i]); + else if (ftime + SIXMONTHS > time(NULL)) + for (i = 11; i < 16; ++i) + (void)putchar(longstring[i]); + else { + (void)putchar(' '); + for (i = 20; i < 24; ++i) + (void)putchar(longstring[i]); + } + (void)putchar(' '); +} + +static int +printtype(mode) + u_int mode; +{ + switch (mode & S_IFMT) { + case S_IFDIR: + (void)putchar('/'); + return (1); + case S_IFIFO: + (void)putchar('|'); + return (1); + case S_IFLNK: + (void)putchar('@'); + return (1); + case S_IFSOCK: + (void)putchar('='); + return (1); + case S_IFWHT: + (void)putchar('%'); + return (1); + } + if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { + (void)putchar('*'); + return (1); + } + return (0); +} + +static void +printlink(p) + FTSENT *p; +{ + int lnklen; + char name[MAXPATHLEN + 1], path[MAXPATHLEN + 1]; + + if (p->fts_level == FTS_ROOTLEVEL) + (void)snprintf(name, sizeof(name), "%s", p->fts_name); + else + (void)snprintf(name, sizeof(name), + "%s/%s", p->fts_parent->fts_accpath, p->fts_name); + if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) { + (void)fprintf(stderr, "\nls: %s: %s\n", name, strerror(errno)); + return; + } + path[lnklen] = '\0'; + (void)printf(" -> %s", path); +} diff --git a/bin/ls/stat_flags.c b/bin/ls/stat_flags.c new file mode 100644 index 000000000000..794bce6472c2 --- /dev/null +++ b/bin/ls/stat_flags.c @@ -0,0 +1,153 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)stat_flags.c 8.2 (Berkeley) 7/28/94"; +#endif /* not lint */ + +#include +#include + +#include +#include + +#define SAPPEND(s) { \ + if (prefix != NULL) \ + (void)strcat(string, prefix); \ + (void)strcat(string, s); \ + prefix = ","; \ +} + +/* + * flags_to_string -- + * Convert stat flags to a comma-separated string. If no flags + * are set, return the default string. + */ +char * +flags_to_string(flags, def) + u_long flags; + char *def; +{ + static char string[128]; + char *prefix; + + string[0] = '\0'; + prefix = NULL; + if (flags & UF_APPEND) + SAPPEND("uappnd"); + if (flags & UF_IMMUTABLE) + SAPPEND("uchg"); + if (flags & UF_NODUMP) + SAPPEND("nodump"); + if (flags & UF_OPAQUE) + SAPPEND("opaque"); + if (flags & SF_APPEND) + SAPPEND("sappnd"); + if (flags & SF_ARCHIVED) + SAPPEND("arch"); + if (flags & SF_IMMUTABLE) + SAPPEND("schg"); + return (prefix == NULL && def != NULL ? def : string); +} + +#define TEST(a, b, f) { \ + if (!memcmp(a, b, sizeof(b))) { \ + if (clear) { \ + if (clrp) \ + *clrp |= (f); \ + } else if (setp) \ + *setp |= (f); \ + break; \ + } \ +} + +/* + * string_to_flags -- + * Take string of arguments and return stat flags. Return 0 on + * success, 1 on failure. On failure, stringp is set to point + * to the offending token. + */ +int +string_to_flags(stringp, setp, clrp) + char **stringp; + u_long *setp, *clrp; +{ + int clear; + char *string, *p; + + clear = 0; + if (setp) + *setp = 0; + if (clrp) + *clrp = 0; + string = *stringp; + while ((p = strsep(&string, "\t ,")) != NULL) { + *stringp = p; + if (*p == '\0') + continue; + if (p[0] == 'n' && p[1] == 'o') { + clear = 1; + p += 2; + } + switch (p[0]) { + case 'a': + TEST(p, "arch", SF_ARCHIVED); + TEST(p, "archived", SF_ARCHIVED); + return (1); + case 'd': + clear = !clear; + TEST(p, "dump", UF_NODUMP); + return (1); + case 'o': + TEST(p, "opaque", UF_OPAQUE); + return (1); + case 's': + TEST(p, "sappnd", SF_APPEND); + TEST(p, "sappend", SF_APPEND); + TEST(p, "schg", SF_IMMUTABLE); + TEST(p, "schange", SF_IMMUTABLE); + TEST(p, "simmutable", SF_IMMUTABLE); + return (1); + case 'u': + TEST(p, "uappnd", UF_APPEND); + TEST(p, "uappend", UF_APPEND); + TEST(p, "uchg", UF_IMMUTABLE); + TEST(p, "uchange", UF_IMMUTABLE); + TEST(p, "uimmutable", UF_IMMUTABLE); + /* FALLTHROUGH */ + default: + return (1); + } + } + return (0); +} diff --git a/bin/ls/util.c b/bin/ls/util.c new file mode 100644 index 000000000000..b8268732fedc --- /dev/null +++ b/bin/ls/util.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Michael Fischbein. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)util.c 8.5 (Berkeley) 4/28/95"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "ls.h" +#include "extern.h" + +void +prcopy(src, dest, len) + char *src, *dest; + int len; +{ + int ch; + + while (len--) { + ch = *src++; + *dest++ = isprint(ch) ? ch : '?'; + } +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: ls [-1ACFLRTWacdfikloqrstu] [file ...]\n"); + exit(1); +} diff --git a/bin/ps/fmt.c b/bin/ps/fmt.c new file mode 100644 index 000000000000..de1812f2e9a5 --- /dev/null +++ b/bin/ps/fmt.c @@ -0,0 +1,119 @@ +/*- + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)fmt.c 8.5 (Berkeley) 4/27/95"; +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "ps.h" + +static char *cmdpart __P((char *)); +static char *shquote __P((char **)); + +/* + * XXX + * This is a stub until marc does the real one. + */ +static char * +shquote(argv) + char **argv; +{ + char **p, *dst, *src; + static char buf[4096]; /* XXX */ + + if (*argv == 0) { + buf[0] = 0; + return (buf); + } + dst = buf; + for (p = argv; (src = *p++) != 0; ) { + if (*src == 0) + continue; + strvis(dst, src, VIS_NL | VIS_CSTYLE); + while (*dst) + dst++; + *dst++ = ' '; + } + 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/pwd/pwd.1 b/bin/pwd/pwd.1 new file mode 100644 index 000000000000..84f0913a50f7 --- /dev/null +++ b/bin/pwd/pwd.1 @@ -0,0 +1,68 @@ +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)pwd.1 8.2 (Berkeley) 4/28/95 +.\" +.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 +The pwd utility exits 0 on success, and >0 if an error occurs. +.Sh STANDARDS +The +.Nm pwd +command is expected to be +.St -p1003.2 +compatible . +.Sh SEE ALSO +.Xr cd 1 , +.Xr csh 1 , +.Xr getcwd 3 +.Sh BUGS +In +.Xr csh 1 +the command +.Ic dirs +is always faster (although it can give a different answer in the rare case +that the current directory or a containing directory was moved after +the shell descended into it). diff --git a/bin/rm/rm.1 b/bin/rm/rm.1 new file mode 100644 index 000000000000..8344bde739c6 --- /dev/null +++ b/bin/rm/rm.1 @@ -0,0 +1,155 @@ +.\" 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 +.\" +.Dd December 5, 1994 +.Dt RM 1 +.Os +.Sh NAME +.Nm rm +.Nd remove directory entries +.Sh SYNOPSIS +.Nm rm +.Op Fl f | Fl i +.Op Fl dPRrW +.Ar file ... +.Sh DESCRIPTION +The +.Nm rm +utility attempts to remove the non-directory type files specified on the +command line. +If the permissions of the file do not permit writing, and the standard +input device is a terminal, the user is prompted (on the standard error +output) for confirmation. +.Pp +The options are as follows: +.Bl -tag -width flag +.It Fl d +Attempt to remove directories as well as other types of files. +.It Fl f +Attempt to remove the files without prompting for confirmation, +regardless of the file's permissions. +If the file does not exist, do not display a diagnostic message or modify +the exit status to reflect an error. +The +.Fl f +option overrides any previous +.Fl i +options. +.It Fl i +Request confirmation before attempting to remove each file, regardless of +the file's permissions, or whether or not the standard input device is a +terminal. +The +.Fl i +option overrides any previous +.Fl f +options. +.It Fl P +Overwrite regular files before deleting them. +Files are overwritten three times, first with the byte pattern 0xff, +then 0x00, and then 0xff again, before they are deleted. +.It Fl R +Attempt to remove the file hierarchy rooted in each file argument. +The +.Fl R +option implies the +.Fl d +option. +If the +.Fl i +option is specified, the user is prompted for confirmation before +each directory's contents are processed (as well as before the attempt +is made to remove the directory). +If the user does not respond affirmatively, the file hierarchy rooted in +that directory is skipped. +.Pp +.It Fl r +Equivalent to +.Fl R . +.It Fl W +Attempts to undelete the named files. +Currently, this option can only be used to recover +files covered by whiteouts. +.El +.Pp +The +.Nm rm +utility removes symbolic links, not the files referenced by the links. +.Pp +It is an error to attempt to remove the files ``.'' and ``..''. +.Pp +The +.Nm rm +utility exits 0 if all of the named files or file hierarchies were removed, +or if the +.Fl f +option was specified and all of the existing files or file hierarchies were +removed. +If an error occurs, +.Nm rm +exits with a value >0. +.Sh SEE ALSO +.Xr rmdir 1 , +.Xr undelete 2 , +.Xr unlink 2 , +.Xr fts 3 , +.Xr symlink 7 +.Sh BUGS +The +.Fl P +option assumes that the underlying file system is a fixed-block file +system. +UFS is a fixed-block file system, LFS is not. +In addition, only regular files are overwritten, other types of files +are not. +.Sh COMPATIBILITY +The +.Nm rm +utility differs from historical implementations in that the +.Fl f +option only masks attempts to remove non-existent files instead of +masking a large variety of errors. +.Pp +Also, historical +.Bx +implementations prompted on the standard output, +not the standard error output. +.Sh STANDARDS +The +.Nm rm +command is expected to be +.St -p1003.2 +compatible. diff --git a/bin/rm/rm.c b/bin/rm/rm.c new file mode 100644 index 000000000000..bcecfe39c397 --- /dev/null +++ b/bin/rm/rm.c @@ -0,0 +1,403 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1990, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)rm.c 8.8 (Berkeley) 4/27/95"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +int dflag, eval, fflag, iflag, Pflag, Wflag, stdin_ok; + +int check __P((char *, char *, struct stat *)); +void checkdot __P((char **)); +void rm_file __P((char **)); +void rm_overwrite __P((char *, struct stat *)); +void rm_tree __P((char **)); +void usage __P((void)); + +/* + * rm -- + * This rm is different from historic rm's, but is expected to match + * POSIX 1003.2 behavior. The most visible difference is that -f + * has two specific effects now, ignore non-existent files and force + * file removal. + */ +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch, rflag; + + Pflag = rflag = 0; + while ((ch = getopt(argc, argv, "dfiPRrW")) != -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 'W': + Wflag = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc < 1) + usage(); + + checkdot(argv); + + 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; + + /* + * Remove a file hierarchy. If forcing removal (-f), or interactive + * (-i) or can't ask anyway (stdin_ok), don't stat the file. + */ + needstat = !fflag && !iflag && stdin_ok; + + /* + * If the -i option is specified, the user can skip on the pre-order + * visit. The fts_number field flags skipped directories. + */ +#define SKIPPED 1 + + 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; + } + 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; + } + + /* + * 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: + if (!rmdir(p->fts_accpath) || fflag && errno == ENOENT) + continue; + break; + + case FTS_W: + if (!undelete(p->fts_accpath) || + fflag && errno == ENOENT) + continue; + break; + + default: + if (Pflag) + rm_overwrite(p->fts_accpath, NULL); + if (!unlink(p->fts_accpath) || fflag && errno == ENOENT) + continue; + } + warn("%s", p->fts_path); + eval = 1; + } + if (errno) + err(1, "fts_read"); +} + +void +rm_file(argv) + char **argv; +{ + struct stat sb; + int 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; + 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; + } + } +} + +/* + * rm_overwrite -- + * Overwrite the file 3 times with varying bit patterns. + * + * XXX + * This is a cheap way to *really* delete files. Note that only regular + * files are deleted, directories (and therefore names) will remain. + * Also, this assumes a fixed-block file system (like FFS, or a V7 or a + * System V file system). In a logging file system, you'll have to have + * kernel support. + */ +void +rm_overwrite(file, sbp) + char *file; + struct stat *sbp; +{ + struct stat sb; + off_t len; + int fd, wlen; + char buf[8 * 1024]; + + fd = -1; + if (sbp == NULL) { + if (lstat(file, &sb)) + goto err; + sbp = &sb; + } + if (!S_ISREG(sbp->st_mode)) + return; + if ((fd = open(file, O_WRONLY, 0)) == -1) + goto err; + +#define PASS(byte) { \ + memset(buf, byte, sizeof(buf)); \ + for (len = sbp->st_size; len > 0; len -= wlen) { \ + wlen = len < sizeof(buf) ? len : sizeof(buf); \ + if (write(fd, buf, wlen) != wlen) \ + goto err; \ + } \ +} + PASS(0xff); + if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) + goto err; + PASS(0x00); + if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) + goto err; + PASS(0xff); + if (!fsync(fd) && !close(fd)) + return; + +err: eval = 1; + warn("%s", file); +} + + +int +check(path, name, sp) + char *path, *name; + struct stat *sp; +{ + int ch, first; + char modep[15]; + + /* Check -i first. */ + if (iflag) + (void)fprintf(stderr, "remove %s? ", path); + else { + /* + * If it's not a symbolic link and it's unwritable and we're + * talking to a terminal, ask. Symbolic links are excluded + * because their permissions are meaningless. Check stdin_ok + * first because we may not have stat'ed the file. + */ + if (!stdin_ok || S_ISLNK(sp->st_mode) || !access(name, W_OK)) + return (1); + strmode(sp->st_mode, modep); + (void)fprintf(stderr, "override %s%s%s/%s for %s? ", + modep + 1, modep[9] == ' ' ? "" : " ", + user_from_uid(sp->st_uid, 0), + group_from_gid(sp->st_gid, 0), path); + } + (void)fflush(stderr); + + first = ch = getchar(); + while (ch != '\n' && ch != EOF) + ch = getchar(); + return (first == 'y'); +} + +#define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || (a)[1] == '.' && !(a)[2])) +void +checkdot(argv) + char **argv; +{ + char *p, **save, **t; + int complained; + + complained = 0; + for (t = argv; *t;) { + if ((p = strrchr(*t, '/')) != NULL) + ++p; + else + p = *t; + if (ISDOT(p)) { + if (!complained++) + warnx("\".\" and \"..\" may not be removed"); + eval = 1; + for (save = t; (t[0] = t[1]) != NULL; ++t) + continue; + t = save; + } else + ++t; + } +} + +void +usage() +{ + + (void)fprintf(stderr, "usage: rm [-dfiPRrW] file ...\n"); + exit(1); +} diff --git a/bin/rmail/rmail.c b/bin/rmail/rmail.c new file mode 100644 index 000000000000..29af02a90f6d --- /dev/null +++ b/bin/rmail/rmail.c @@ -0,0 +1,373 @@ +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1988, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)rmail.c 8.3 (Berkeley) 5/15/95"; +#endif /* not lint */ + +/* + * RMAIL -- UUCP mail server. + * + * This program reads the >From ... remote from ... lines that UUCP is so + * fond of and turns them into something reasonable. It then execs sendmail + * with various options built from these lines. + * + * The expected syntax is: + * + * := [-a-z0-9]+ + * := ctime format + * := [-a-z0-9!]+ + * := "^\n$" + * := "From" + * [ "remote from" ] + * := ">" + * msg := * + * + * The output of rmail(8) compresses the lines into a single + * from path. + * + * The err(3) routine is included here deliberately to make this code + * a bit more portable. + */ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef MAX +# define MAX(a, b) ((a) < (b) ? (b) : (a)) +#endif + +void err __P((int, const char *, ...)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; + extern int errno, optind; + FILE *fp; + struct stat sb; + size_t fplen, fptlen, len; + off_t offset; + int ch, debug, i, pdes[2], pid, status; + char *addrp, *domain, *p, *t; + char *from_path, *from_sys, *from_user; + char *args[100], buf[2048], lbuf[2048]; + + debug = 0; + domain = "UUCP"; /* Default "domain". */ + while ((ch = getopt(argc, argv, "D:T")) != EOF) + switch (ch) { + case 'T': + debug = 1; + break; + case 'D': + domain = optarg; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc < 1) + usage(); + + from_path = from_sys = from_user = NULL; + for (offset = 0;;) { + + /* Get and nul-terminate the line. */ + if (fgets(lbuf, sizeof(lbuf), stdin) == NULL) + exit (EX_DATAERR); + if ((p = strchr(lbuf, '\n')) == NULL) + err(EX_DATAERR, "line too long"); + *p = '\0'; + + /* Parse lines until reach a non-"From" line. */ + if (!strncmp(lbuf, "From ", 5)) + addrp = lbuf + 5; + else if (!strncmp(lbuf, ">From ", 6)) + addrp = lbuf + 6; + else if (offset == 0) + err(EX_DATAERR, + "missing or empty From line: %s", lbuf); + else { + *p = '\n'; + break; + } + + if (*addrp == '\0') + err(EX_DATAERR, "corrupted From line: %s", lbuf); + + /* Use the "remote from" if it exists. */ + for (p = addrp; (p = strchr(p + 1, 'r')) != NULL;) + if (!strncmp(p, "remote from ", 12)) { + for (t = p += 12; *t && !isspace(*t); ++t); + *t = '\0'; + if (debug) + (void)fprintf(stderr, + "remote from: %s\n", p); + break; + } + + /* Else use the string up to the last bang. */ + if (p == NULL) + if (*addrp == '!') + err(EX_DATAERR, + "bang starts address: %s", addrp); + else if ((t = strrchr(addrp, '!')) != NULL) { + *t = '\0'; + p = addrp; + addrp = t + 1; + if (*addrp == '\0') + err(EX_DATAERR, + "corrupted From line: %s", lbuf); + if (debug) + (void)fprintf(stderr, "bang: %s\n", p); + } + + /* 'p' now points to any system string from this line. */ + if (p != NULL) { + /* Nul terminate it as necessary. */ + for (t = p; *t && !isspace(*t); ++t); + *t = '\0'; + + /* If the first system, copy to the from_sys string. */ + if (from_sys == NULL) { + if ((from_sys = strdup(p)) == NULL) + err(EX_TEMPFAIL, NULL); + if (debug) + (void)fprintf(stderr, + "from_sys: %s\n", from_sys); + } + + /* Concatenate to the path string. */ + len = t - p; + if (from_path == NULL) { + fplen = 0; + if ((from_path = malloc(fptlen = 256)) == NULL) + err(EX_TEMPFAIL, NULL); + } + if (fplen + len + 2 > fptlen) { + fptlen += MAX(fplen + len + 2, 256); + if ((from_path = + realloc(from_path, fptlen)) == NULL) + err(EX_TEMPFAIL, NULL); + } + memmove(from_path + fplen, p, len); + fplen += len; + from_path[fplen++] = '!'; + from_path[fplen] = '\0'; + } + + /* Save off from user's address; the last one wins. */ + for (p = addrp; *p && !isspace(*p); ++p); + *p = '\0'; + if (*addrp == '\0') + addrp = "<>"; + if (from_user != NULL) + free(from_user); + if ((from_user = strdup(addrp)) == NULL) + err(EX_TEMPFAIL, NULL); + + if (debug) { + if (from_path != NULL) + (void)fprintf(stderr, + "from_path: %s\n", from_path); + (void)fprintf(stderr, "from_user: %s\n", from_user); + } + + if (offset != -1) + offset = (off_t)ftell(stdin); + } + + i = 0; + args[i++] = _PATH_SENDMAIL; /* Build sendmail's argument list. */ + args[i++] = "-oee"; /* No errors, just status. */ + args[i++] = "-odq"; /* Queue it, don't try to deliver. */ + args[i++] = "-oi"; /* Ignore '.' on a line by itself. */ + + /* set from system and protocol used */ + if (from_sys == NULL) + (void)snprintf(buf, sizeof(buf), "-p%s", domain); + else if (strchr(from_sys, '.') == NULL) + (void)snprintf(buf, sizeof(buf), "-p%s:%s.%s", + domain, from_sys, domain); + else + (void)snprintf(buf, sizeof(buf), "-p%s:%s", domain, from_sys); + if ((args[i++] = strdup(buf)) == NULL) + err(EX_TEMPFAIL, NULL); + + /* Set name of ``from'' person. */ + (void)snprintf(buf, sizeof(buf), "-f%s%s", + from_path ? from_path : "", from_user); + if ((args[i++] = strdup(buf)) == NULL) + err(EX_TEMPFAIL, NULL); + + /* + * Don't copy arguments beginning with - as they will be + * passed to sendmail and could be interpreted as flags. + * To prevent confusion of sendmail wrap < and > around + * the address (helps to pass addrs like @gw1,@gw2:aa@bb) + */ + while (*argv) { + if (**argv == '-') + err(EX_USAGE, "dash precedes argument: %s", *argv); + if (strchr(*argv, ',') == NULL || strchr(*argv, '<') != NULL) + args[i++] = *argv; + else { + if ((args[i] = malloc(strlen(*argv) + 3)) == NULL) + err(EX_TEMPFAIL, "Cannot malloc"); + sprintf (args [i++], "<%s>", *argv); + } + argv++; + } + args[i] = 0; + + if (debug) { + (void)fprintf(stderr, "Sendmail arguments:\n"); + for (i = 0; args[i]; i++) + (void)fprintf(stderr, "\t%s\n", args[i]); + } + + /* + * If called with a regular file as standard input, seek to the right + * position in the file and just exec sendmail. Could probably skip + * skip the stat, but it's not unreasonable to believe that a failed + * seek will cause future reads to fail. + */ + if (!fstat(STDIN_FILENO, &sb) && S_ISREG(sb.st_mode)) { + if (lseek(STDIN_FILENO, offset, SEEK_SET) != offset) + err(EX_TEMPFAIL, "stdin seek"); + execv(_PATH_SENDMAIL, args); + err(EX_OSERR, "%s", _PATH_SENDMAIL); + } + + if (pipe(pdes) < 0) + err(EX_OSERR, NULL); + + switch (pid = vfork()) { + case -1: /* Err. */ + err(EX_OSERR, NULL); + case 0: /* Child. */ + if (pdes[0] != STDIN_FILENO) { + (void)dup2(pdes[0], STDIN_FILENO); + (void)close(pdes[0]); + } + (void)close(pdes[1]); + execv(_PATH_SENDMAIL, args); + _exit(127); + /* NOTREACHED */ + } + + if ((fp = fdopen(pdes[1], "w")) == NULL) + err(EX_OSERR, NULL); + (void)close(pdes[0]); + + /* Copy the file down the pipe. */ + do { + (void)fprintf(fp, "%s", lbuf); + } while (fgets(lbuf, sizeof(lbuf), stdin) != NULL); + + if (ferror(stdin)) + err(EX_TEMPFAIL, "stdin: %s", strerror(errno)); + + if (fclose(fp)) + err(EX_OSERR, NULL); + + if ((waitpid(pid, &status, 0)) == -1) + err(EX_OSERR, "%s", _PATH_SENDMAIL); + + if (!WIFEXITED(status)) + err(EX_OSERR, + "%s: did not terminate normally", _PATH_SENDMAIL); + + if (WEXITSTATUS(status)) + err(status, "%s: terminated with %d (non-zero) status", + _PATH_SENDMAIL, WEXITSTATUS(status)); + exit(EX_OK); +} + +void +usage() +{ + (void)fprintf(stderr, "usage: rmail [-T] [-D domain] user ...\n"); + exit(EX_USAGE); +} + +#ifdef __STDC__ +#include +#else +#include +#endif + +void +#ifdef __STDC__ +err(int eval, const char *fmt, ...) +#else +err(eval, fmt, va_alist) + int eval; + const char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)fprintf(stderr, "rmail: "); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); + exit(eval); +} diff --git a/bin/stty/key.c b/bin/stty/key.c new file mode 100644 index 000000000000..713ff58cd198 --- /dev/null +++ b/bin/stty/key.c @@ -0,0 +1,300 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)key.c 8.4 (Berkeley) 2/20/95"; +#endif /* not lint */ + +#include + +#include +#include +#include +#include +#include + +#include "stty.h" +#include "extern.h" + +__BEGIN_DECLS +void f_all __P((struct info *)); +void f_cbreak __P((struct info *)); +void f_columns __P((struct info *)); +void f_dec __P((struct info *)); +void f_everything __P((struct info *)); +void f_extproc __P((struct info *)); +void f_ispeed __P((struct info *)); +void f_nl __P((struct info *)); +void f_ospeed __P((struct info *)); +void f_raw __P((struct info *)); +void f_rows __P((struct info *)); +void f_sane __P((struct info *)); +void f_size __P((struct info *)); +void f_speed __P((struct info *)); +void f_tty __P((struct info *)); +__END_DECLS + +static struct key { + char *name; /* name */ + void (*f) __P((struct info *)); /* function */ +#define F_NEEDARG 0x01 /* needs an argument */ +#define F_OFFOK 0x02 /* can turn off */ + int flags; +} keys[] = { + { "all", f_all, 0 }, + { "cbreak", f_cbreak, F_OFFOK }, + { "cols", f_columns, F_NEEDARG }, + { "columns", f_columns, F_NEEDARG }, + { "cooked", f_sane, 0 }, + { "dec", f_dec, 0 }, + { "everything", f_everything, 0 }, + { "extproc", f_extproc, F_OFFOK }, + { "ispeed", f_ispeed, F_NEEDARG }, + { "new", f_tty, 0 }, + { "nl", f_nl, F_OFFOK }, + { "old", f_tty, 0 }, + { "ospeed", f_ospeed, F_NEEDARG }, + { "raw", f_raw, F_OFFOK }, + { "rows", f_rows, F_NEEDARG }, + { "sane", f_sane, 0 }, + { "size", f_size, 0 }, + { "speed", f_speed, 0 }, + { "tty", f_tty, 0 }, +}; + +static int +c_key(a, b) + const void *a, *b; +{ + + return (strcmp(((struct key *)a)->name, ((struct key *)b)->name)); +} + +int +ksearch(argvp, ip) + char ***argvp; + struct info *ip; +{ + char *name; + struct key *kp, tmp; + + name = **argvp; + if (*name == '-') { + ip->off = 1; + ++name; + } else + ip->off = 0; + + tmp.name = name; + if (!(kp = (struct key *)bsearch(&tmp, keys, + sizeof(keys)/sizeof(struct key), sizeof(struct key), c_key))) + return (0); + if (!(kp->flags & F_OFFOK) && ip->off) { + errx(1, "illegal option -- %s", name); + usage(); + } + if (kp->flags & F_NEEDARG && !(ip->arg = *++*argvp)) { + errx(1, "option requires an argument -- %s", name); + usage(); + } + kp->f(ip); + return (1); +} + +void +f_all(ip) + struct info *ip; +{ + print(&ip->t, &ip->win, ip->ldisc, BSD); +} + +void +f_cbreak(ip) + struct info *ip; +{ + + if (ip->off) + f_sane(ip); + else { + ip->t.c_iflag |= BRKINT|IXON|IMAXBEL; + ip->t.c_oflag |= OPOST; + ip->t.c_lflag |= ISIG|IEXTEN; + ip->t.c_lflag &= ~ICANON; + ip->set = 1; + } +} + +void +f_columns(ip) + struct info *ip; +{ + + ip->win.ws_col = atoi(ip->arg); + ip->wset = 1; +} + +void +f_dec(ip) + struct info *ip; +{ + + ip->t.c_cc[VERASE] = (u_char)0177; + ip->t.c_cc[VKILL] = CTRL('u'); + ip->t.c_cc[VINTR] = CTRL('c'); + ip->t.c_lflag &= ~ECHOPRT; + ip->t.c_lflag |= ECHOE|ECHOKE|ECHOCTL; + ip->t.c_iflag &= ~IXANY; + ip->set = 1; +} + +void +f_everything(ip) + struct info *ip; +{ + + print(&ip->t, &ip->win, ip->ldisc, BSD); +} + +void +f_extproc(ip) + struct info *ip; +{ + + if (ip->off) { + int tmp = 0; + (void)ioctl(ip->fd, TIOCEXT, &tmp); + } else { + int tmp = 1; + (void)ioctl(ip->fd, TIOCEXT, &tmp); + } + ip->set = 1; +} + +void +f_ispeed(ip) + struct info *ip; +{ + + cfsetispeed(&ip->t, atoi(ip->arg)); + ip->set = 1; +} + +void +f_nl(ip) + struct info *ip; +{ + + if (ip->off) { + ip->t.c_iflag |= ICRNL; + ip->t.c_oflag |= ONLCR; + } else { + ip->t.c_iflag &= ~ICRNL; + ip->t.c_oflag &= ~ONLCR; + } + ip->set = 1; +} + +void +f_ospeed(ip) + struct info *ip; +{ + + cfsetospeed(&ip->t, atoi(ip->arg)); + ip->set = 1; +} + +void +f_raw(ip) + struct info *ip; +{ + + if (ip->off) + f_sane(ip); + else { + cfmakeraw(&ip->t); + ip->t.c_cflag &= ~(CSIZE|PARENB); + ip->t.c_cflag |= CS8; + ip->set = 1; + } +} + +void +f_rows(ip) + struct info *ip; +{ + + ip->win.ws_row = atoi(ip->arg); + ip->wset = 1; +} + +void +f_sane(ip) + struct info *ip; +{ + + ip->t.c_cflag = TTYDEF_CFLAG | (ip->t.c_cflag & CLOCAL); + ip->t.c_iflag = TTYDEF_IFLAG; + ip->t.c_iflag |= ICRNL; + /* preserve user-preference flags in lflag */ +#define LKEEP (ECHOKE|ECHOE|ECHOK|ECHOPRT|ECHOCTL|ALTWERASE|TOSTOP|NOFLSH) + ip->t.c_lflag = TTYDEF_LFLAG | (ip->t.c_lflag & LKEEP); + ip->t.c_oflag = TTYDEF_OFLAG; + ip->set = 1; +} + +void +f_size(ip) + struct info *ip; +{ + + (void)printf("%d %d\n", ip->win.ws_row, ip->win.ws_col); +} + +void +f_speed(ip) + struct info *ip; +{ + + (void)printf("%d\n", cfgetospeed(&ip->t)); +} + +void +f_tty(ip) + struct info *ip; +{ + int tmp; + + tmp = TTYDISC; + if (ioctl(0, TIOCSETD, &tmp) < 0) + err(1, "TIOCSETD"); +} diff --git a/bin/stty/stty.1 b/bin/stty/stty.1 new file mode 100644 index 000000000000..ac2ab1693b72 --- /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.5 (Berkeley) 6/1/94 +.\" +.Dd June 1, 1994 +.Dt STTY 1 +.Os +.Sh NAME +.Nm stty +.Nd set the options for a terminal device interface +.Sh SYNOPSIS +.Nm stty +.Op Fl a | Fl e | Fl g +.Op Fl f Ar file +.Op operands +.Sh DESCRIPTION +The +.Nm stty +utility sets or reports on terminal +characteristics for the device that is its standard input. +If no options or operands are specified, it reports the settings of a subset +of characteristics as well as additional ones if they differ from their +default values. +Otherwise it modifies +the terminal state according to the specified arguments. +Some combinations of arguments are mutually +exclusive on some terminal types. +.Pp +The following options are available: +.Bl -tag -width Ds +.It Fl a +Display all the current settings for the terminal to standard output +as per +.St -p1003.2 . +.It Fl e +Display all the current settings for the terminal to standard output +in the traditional +.Tn BSD +``all'' and ``everything'' formats. +.It Fl f +Open and use the terminal named by +.Ar file +rather than using standard input. The file is opened +using the +.Dv O_NONBLOCK +flag of +.Fn open , +making it possible to +set or display settings on a terminal that might otherwise +block on the open. +.It Fl g +Display all the current settings for the terminal to standard output +in a form that may be used as an argument to a subsequent invocation of +.Nm stty +to restore the current terminal state as per +.St -p1003.2 . +.El +.Pp +The following arguments are available to set the terminal +characteristics: +.Ss Control Modes: +.Pp +Control mode flags affect hardware characteristics associated with the +terminal. This corresponds to the c_cflag in the termios structure. +.Bl -tag -width Fl +.It Cm parenb Pq Fl parenb +Enable (disable) parity generation +and detection. +.It Cm parodd Pq Fl parodd +Select odd (even) parity. +.It Cm cs5 cs6 cs7 cs8 +Select character size, if possible. +.It Ar number +Set terminal baud rate to the +number given, if possible. +If the +baud rate is set to zero, modem +control is no longer +asserted. +.It Cm ispeed Ar number +Set terminal input baud rate to the +number given, if possible. +If the +input baud rate is set to zero, the +input baud rate is set to the +value of the output baud +rate. +.It Cm ospeed Ar number +Set terminal output baud rate to +the number given, if possible. +If +the output baud rate is set to +zero, modem control is +no longer asserted. +.ne 1i +.It Cm speed Ar number +This sets both +.Cm ispeed +and +.Cm ospeed +to +.Ar number . +.It Cm hupcl Pq Fl hupcl +Stop asserting modem control +(do not stop asserting modem control) on last close. +.It Cm hup Pq Fl hup +Same as hupcl +.Pq Fl hupcl . +.It Cm cstopb Pq Fl cstopb +Use two (one) stop bits per character. +.It Cm cread Pq Fl cread +Enable (disable) the receiver. +.It Cm clocal Pq Fl clocal +Assume a line without (with) modem +control. +.It Cm crtscts Pq Fl crtscts +Enable RTS/CTS flow control. +.El +.Ss Input Modes: +This corresponds to the c_iflag in the termios structure. +.Bl -tag -width Fl +.It Cm ignbrk Pq Fl ignbrk +Ignore (do not ignore) break on +input. +.It Cm brkint Pq Fl brkint +Signal (do not signal) +.Dv INTR +on +break. +.It Cm ignpar Pq Fl ignpar +Ignore (do not ignore) parity +errors. +.It Cm parmrk Pq Fl parmrk +Mark (do not mark) parity errors. +.It Cm inpck Pq Fl inpck +Enable (disable) input parity +checking. +.It Cm istrip Pq Fl istrip +Strip (do not strip) input characters +to seven bits. +.It Cm inlcr Pq Fl inlcr +Map (do not map) +.Dv NL +to +.Dv CR +on input. +.It Cm igncr Pq Fl igncr +Ignore (do not ignore) +.Dv CR +on input. +.It Cm icrnl Pq Fl icrnl +Map (do not map) +.Dv CR +to +.Dv NL +on input. +.It Cm ixon Pq Fl ixon +Enable (disable) +.Dv START/STOP +output +control. +Output from the system is +stopped when the system receives +.Dv STOP +and started when the system +receives +.Dv START , +or if +.Cm ixany +is set, any character restarts output. +.ne 1i +.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. +.ne 1i +.It Cm echoke Pq Fl echoke +The +.Dv KILL +character shall (shall +not) visually erase the +the current line from the +display, if possible. +.It Cm echonl Pq Fl echonl +Echo (do not echo) +.Dv NL , +even if echo +is disabled. +.It Cm echoctl Pq Fl echoctl +If +.Cm echoctl +is set, echo control characters as ^X. Otherwise control characters +echo as themselves. +.It Cm echoprt Pq Fl echoprt +For printing terminals. If set, echo erased characters backwards within ``\\'' +and ``/''. Otherwise, disable this feature. +.It Cm noflsh Pq Fl noflsh +Disable (enable) flush after +.Dv INTR , QUIT , SUSP . +.It Cm tostop Pq Fl tostop +Send (do not send) +.Dv SIGTTOU +for background output. This causes background jobs to stop if they attempt +terminal output. +.It Cm altwerase Pq Fl altwerase +Use (do not use) an alternate word erase algorithm when processing +.Dv WERASE +characters. +This alternate algorithm considers sequences of +alphanumeric/underscores as words. +It also skips the first preceding character in its classification +(as a convenience since the one preceding character could have been +erased with simply an +.Dv ERASE +character.) +.It Cm mdmbuf Pq Fl mdmbuf +If set, flow control output based on condition of Carrier Detect. Otherwise +writes return an error if Carrier Detect is low (and Carrier is not being +ignored with the +.Dv CLOCAL +flag.) +.It Cm flusho Pq Fl flusho +Indicates output is (is not) being discarded. +.It Cm pendin Pq Fl pendin +Indicates input is (is not) pending after a switch from non-canonical +to canonical mode and will be re-input when a read becomes pending +or more input arrives. +.El +.Ss Control Characters: +.Bl -tag -width Fl +.It Ar control-character Ar string +Set +.Ar control-character +to +.Ar string . +If string is a single character, +the control character is set to +that character. +If string is the +two character sequence "^-" or the +string "undef" the control character +is disabled (i.e. set to +.Pf { Dv _POSIX_VDISABLE Ns } . ) +.Pp +Recognized control-characters: +.Bd -ragged -offset indent +.Bl -column character Subscript +.It control- +.It character Subscript Description +.It _________ _________ _______________ +.It eof Ta Tn VEOF EOF No character +.It eol Ta Tn VEOL EOL No character +.It eol2 Ta Tn VEOL2 EOL2 No character +.It erase Ta Tn VERASE ERASE No character +.It werase Ta Tn VWERASE WERASE No character +.It intr Ta Tn VINTR INTR No character +.It kill Ta Tn VKILL KILL No character +.It quit Ta Tn VQUIT QUIT No character +.It susp Ta Tn VSUSP SUSP No character +.It start Ta Tn VSTART START No character +.It stop Ta Tn VSTOP STOP No character +.It dsusp Ta Tn VDSUSP DSUSP No character +.It lnext Ta Tn VLNEXT LNEXT No character +.It reprint Ta Tn VREPRINT REPRINT No character +.It status Ta Tn VSTATUS STATUS No character +.El +.Ed +.It Cm min Ar number +.It Cm time Ar number +Set the value of min or time to +number. +.Dv MIN +and +.Dv TIME +are used in +Non-Canonical mode input processing +(-icanon). +.El +.Ss Combination Modes: +.Pp +.Bl -tag -width Fl +.It Ar saved settings +Set the current terminal +characteristics to the saved settings +produced by the +.Fl g +option. +.It Cm evenp No or Cm parity +Enable parenb and cs7; disable +parodd. +.It Cm oddp +Enable parenb, cs7, and parodd. +.It Fl parity , evenp , oddp +Disable parenb, and set cs8. +.It Cm \&nl Pq Fl \&nl +Enable (disable) icrnl. +In addition +-nl unsets inlcr and igncr. +.It Cm ek +Reset +.Dv ERASE +and +.Dv KILL +characters +back to system defaults. +.It Cm sane +Resets all modes to reasonable values for interactive terminal use. +.It Cm tty +Set the line discipline to the standard terminal line discipline +.Dv TTYDISC . +.It Cm crt Pq Fl crt +Set (disable) all modes suitable for a CRT display device. +.It Cm kerninfo Pq Fl kerninfo +Enable (disable) the system generated status line associated with +processing a +.Dv STATUS +character (usually set to ^T). The status line consists of the +system load average, the current command name, its process ID, the +event the process is waiting on (or the status of the process), the user +and system times, percent cpu, and current memory usage. +.It Cm columns Ar number +The terminal size is recorded as having +.Ar number +columns. +.It Cm cols Ar number +is an alias for +.Cm columns. +.ne 1i +.It Cm rows Ar number +The terminal size is recorded as having +.Ar number +rows. +.It Cm dec +Set modes suitable for users of Digital Equipment Corporation systems ( +.Dv ERASE , +.Dv KILL , +and +.Dv INTR +characters are set to ^?, ^U, and ^C; +.Dv ixany +is disabled, and +.Dv crt +is enabled.) +.It Cm extproc Pq Fl extproc +If set, this flag indicates that some amount of terminal processing is being +performed by either the terminal hardware or by the remote side connected +to a pty. +.It Cm raw Pq Fl raw +If set, change the modes of the terminal so that no input or output processing +is performed. If unset, change the modes of the terminal to some reasonable +state that performs input and output processing. Note that since the +terminal driver no longer has a single +.Dv RAW +bit, it is not possible to intuit what flags were set prior to setting +.Cm raw . +This means that unsetting +.Cm raw +may not put back all the setting that were previously in effect. +To set the terminal into a raw state and then accurately restore it, the following +shell code is recommended: +.nf + +save_state=$(stty -g) +stty raw +\&... +stty "$save_state" + +.fi +.It Cm size +The size of the terminal is printed as two numbers on a single line, +first rows, then columns. +.El +.Ss Compatibility Modes: +.Pp +These modes remain for compatibility with the previous version of +the stty command. +.Bl -tag -width Fl +.It Cm all +Reports all the terminal modes as with +.Cm stty Fl a +except that the control characters are printed in a columnar format. +.It Cm everything +Same as +.Cm all . +.It Cm cooked +Same as +.Cm sane . +.It Cm cbreak +If set, enables +.Cm brkint , ixon , imaxbel , opost , +.Cm isig , iexten , +and +.Cm Fl icanon . +If unset, same as +.Cm sane . +.It Cm new +Same as +.Cm tty . +.It Cm old +Same as +.Cm tty . +.It Cm newcrt Pq Fl newcrt +Same as +.Cm crt . +.It Cm pass8 +The converse of +.Cm parity . +.It Cm tandem Pq Fl tandem +Same as +.Cm ixoff . +.It Cm decctlq Pq Fl decctlq +The converse of +.Cm ixany . +.ne 1i +.It Cm crterase Pq Fl crterase +Same as +.Cm echoe . +.It Cm crtbs Pq Fl crtbs +Same as +.Cm echoe . +.It Cm crtkill Pq Fl crtkill +Same as +.Cm echoke . +.It Cm ctlecho Pq Fl ctlecho +Same as +.Cm echoctl . +.It Cm prterase Pq Fl prterase +Same as +.Cm echoprt . +.It Cm litout Pq Fl litout +The converse of +.Cm opost . +.It Cm tabs Pq Fl tabs +The converse of +.Cm tabs . +.It Cm brk Ar value +Same as the control character +.Cm eol . +.It Cm flush Ar value +Same as the control character +.Cm discard . +.It Cm rprnt Ar value +Same as the control character +.Cm reprint . +.El +.Pp +The +.Nm stty +utility exits with a value of 0 if successful, and >0 if an error occurs. +.Sh SEE ALSO +.Xr termios 4 +.Sh STANDARDS +The +.Nm stty +function is expected to be +.St -p1003.2 +compatible. The flags +.Fl e +and +.Fl f +are +extensions to the standard. -- cgit v1.3 From 25e43cba725c919917fea240af20d74d1a50edd1 Mon Sep 17 00:00:00 2001 From: Bruce Evans Date: Wed, 2 Jul 1997 00:25:26 +0000 Subject: Import Lite2's src/sbin, except for XNSrouted and routed. All relevant files in src/sbin are off the vendor branch, so this doesn't change the active versions. --- sbin/badsect/badsect.c | 209 +++++++ sbin/clri/clri.c | 131 ++++ sbin/disklabel/disklabel.5.5 | 388 ++++++++++++ sbin/disklabel/disklabel.c | 1315 +++++++++++++++++++++++++++++++++++++++ sbin/fastboot/fastboot.8 | 69 ++ sbin/ifconfig/ifconfig.8 | 277 +++++++++ sbin/init/init.8 | 295 +++++++++ sbin/init/init.c | 1297 ++++++++++++++++++++++++++++++++++++++ sbin/ping/ping.8 | 328 ++++++++++ sbin/ping/ping.c | 986 +++++++++++++++++++++++++++++ sbin/route/ccitt_addr.c | 175 ++++++ sbin/route/route.8 | 325 ++++++++++ sbin/route/route.c | 1415 ++++++++++++++++++++++++++++++++++++++++++ sbin/savecore/savecore.c | 649 +++++++++++++++++++ sbin/scsiformat/scsiformat.c | 664 ++++++++++++++++++++ sbin/shutdown/shutdown.8 | 162 +++++ sbin/shutdown/shutdown.c | 493 +++++++++++++++ sbin/tunefs/tunefs.8 | 166 +++++ sbin/tunefs/tunefs.c | 327 ++++++++++ 19 files changed, 9671 insertions(+) create mode 100644 sbin/badsect/badsect.c create mode 100644 sbin/clri/clri.c create mode 100644 sbin/disklabel/disklabel.5.5 create mode 100644 sbin/disklabel/disklabel.c create mode 100644 sbin/fastboot/fastboot.8 create mode 100644 sbin/ifconfig/ifconfig.8 create mode 100644 sbin/init/init.8 create mode 100644 sbin/init/init.c create mode 100644 sbin/ping/ping.8 create mode 100644 sbin/ping/ping.c create mode 100644 sbin/route/ccitt_addr.c create mode 100644 sbin/route/route.8 create mode 100644 sbin/route/route.c create mode 100644 sbin/savecore/savecore.c create mode 100644 sbin/scsiformat/scsiformat.c create mode 100644 sbin/shutdown/shutdown.8 create mode 100644 sbin/shutdown/shutdown.c create mode 100644 sbin/tunefs/tunefs.8 create mode 100644 sbin/tunefs/tunefs.c diff --git a/sbin/badsect/badsect.c b/sbin/badsect/badsect.c new file mode 100644 index 000000000000..f6f01ccc5dad --- /dev/null +++ b/sbin/badsect/badsect.c @@ -0,0 +1,209 @@ +/* + * Copyright (c) 1981, 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1981, 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)badsect.c 8.2 (Berkeley) 5/4/95"; +#endif /* not lint */ + +/* + * badsect + * + * Badsect takes a list of file-system relative sector numbers + * and makes files containing the blocks of which these sectors are a part. + * It can be used to contain sectors which have problems if these sectors + * are not part of the bad file for the pack (see bad144). For instance, + * this program can be used if the driver for the file system in question + * does not support bad block forwarding. + */ +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +union { + struct fs fs; + char fsx[SBSIZE]; +} ufs; +#define sblock ufs.fs +union { + struct cg cg; + char cgx[MAXBSIZE]; +} ucg; +#define acg ucg.cg +struct fs *fs; +int fso, fsi; +int errs; +long dev_bsize = 1; + +char buf[MAXBSIZE]; + +void rdfs __P((daddr_t, int, char *)); +int chkuse __P((daddr_t, int)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + daddr_t number; + struct stat stbuf, devstat; + register struct direct *dp; + DIR *dirp; + char name[BUFSIZ]; + + if (argc < 3) { + fprintf(stderr, "usage: badsect bbdir blkno [ blkno ]\n"); + exit(1); + } + if (chdir(argv[1]) < 0 || stat(".", &stbuf) < 0) { + perror(argv[1]); + exit(2); + } + strcpy(name, _PATH_DEV); + if ((dirp = opendir(name)) == NULL) { + perror(name); + exit(3); + } + while ((dp = readdir(dirp)) != NULL) { + strcpy(&name[5], dp->d_name); + if (stat(name, &devstat) < 0) { + perror(name); + exit(4); + } + if (stbuf.st_dev == devstat.st_rdev && + (devstat.st_mode & IFMT) == IFBLK) + break; + } + closedir(dirp); + if (dp == NULL) { + printf("Cannot find dev 0%o corresponding to %s\n", + stbuf.st_rdev, argv[1]); + exit(5); + } + if ((fsi = open(name, 0)) < 0) { + perror(name); + exit(6); + } + fs = &sblock; + rdfs(SBOFF, SBSIZE, (char *)fs); + dev_bsize = fs->fs_fsize / fsbtodb(fs, 1); + for (argc -= 2, argv += 2; argc > 0; argc--, argv++) { + number = atoi(*argv); + if (chkuse(number, 1)) + continue; + if (mknod(*argv, IFMT|0600, dbtofsb(fs, number)) < 0) { + perror(*argv); + errs++; + } + } + printf("Don't forget to run ``fsck %s''\n", name); + exit(errs); +} + +int +chkuse(blkno, cnt) + daddr_t blkno; + int cnt; +{ + int cg; + daddr_t fsbn, bn; + + fsbn = dbtofsb(fs, blkno); + if ((unsigned)(fsbn+cnt) > fs->fs_size) { + printf("block %d out of range of file system\n", blkno); + return (1); + } + cg = dtog(fs, fsbn); + if (fsbn < cgdmin(fs, cg)) { + if (cg == 0 || (fsbn+cnt) > cgsblock(fs, cg)) { + printf("block %d in non-data area: cannot attach\n", + blkno); + return (1); + } + } else { + if ((fsbn+cnt) > cgbase(fs, cg+1)) { + printf("block %d in non-data area: cannot attach\n", + blkno); + return (1); + } + } + rdfs(fsbtodb(fs, cgtod(fs, cg)), (int)sblock.fs_cgsize, + (char *)&acg); + if (!cg_chkmagic(&acg)) { + fprintf(stderr, "cg %d: bad magic number\n", cg); + errs++; + return (1); + } + bn = dtogd(fs, fsbn); + if (isclr(cg_blksfree(&acg), bn)) + printf("Warning: sector %d is in use\n", blkno); + return (0); +} + +/* + * read a block from the file system + */ +void +rdfs(bno, size, bf) + daddr_t bno; + int size; + char *bf; +{ + int n; + + if (lseek(fsi, (off_t)bno * dev_bsize, SEEK_SET) < 0) { + printf("seek error: %ld\n", bno); + perror("rdfs"); + exit(1); + } + n = read(fsi, bf, size); + if (n != size) { + printf("read error: %ld\n", bno); + perror("rdfs"); + exit(1); + } +} diff --git a/sbin/clri/clri.c b/sbin/clri/clri.c new file mode 100644 index 000000000000..18412018e02b --- /dev/null +++ b/sbin/clri/clri.c @@ -0,0 +1,131 @@ +/* + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Rich $alz of BBN Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1990, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)clri.c 8.3 (Berkeley) 4/28/95"; +#endif /* not lint */ + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +int +main(argc, argv) + int argc; + char *argv[]; +{ + register struct fs *sbp; + register struct dinode *ip; + register int fd; + struct dinode ibuf[MAXBSIZE / sizeof (struct dinode)]; + long generation, bsize; + off_t offset; + int inonum; + char *fs, sblock[SBSIZE]; + + if (argc < 3) { + (void)fprintf(stderr, "usage: clri filesystem inode ...\n"); + exit(1); + } + + fs = *++argv; + + /* get the superblock. */ + if ((fd = open(fs, O_RDWR, 0)) < 0) + err(1, "%s", fs); + if (lseek(fd, (off_t)(SBLOCK * DEV_BSIZE), SEEK_SET) < 0) + err(1, "%s", fs); + if (read(fd, sblock, sizeof(sblock)) != sizeof(sblock)) + errx(1, "%s: can't read superblock", fs); + + sbp = (struct fs *)sblock; + if (sbp->fs_magic != FS_MAGIC) + errx(1, "%s: superblock magic number 0x%x, not 0x%x", + fs, sbp->fs_magic, FS_MAGIC); + bsize = sbp->fs_bsize; + + /* remaining arguments are inode numbers. */ + while (*++argv) { + /* get the inode number. */ + if ((inonum = atoi(*argv)) <= 0) + errx(1, "%s is not a valid inode number", *argv); + (void)printf("clearing %d\n", inonum); + + /* read in the appropriate block. */ + offset = ino_to_fsba(sbp, inonum); /* inode to fs blk */ + offset = fsbtodb(sbp, offset); /* fs blk disk blk */ + offset *= DEV_BSIZE; /* disk blk to bytes */ + + /* seek and read the block */ + if (lseek(fd, offset, SEEK_SET) < 0) + err(1, "%s", fs); + if (read(fd, ibuf, bsize) != bsize) + err(1, "%s", fs); + + /* get the inode within the block. */ + ip = &ibuf[ino_to_fsbo(sbp, inonum)]; + + /* clear the inode, and bump the generation count. */ + generation = ip->di_gen + 1; + memset(ip, 0, sizeof(*ip)); + ip->di_gen = generation; + + /* backup and write the block */ + if (lseek(fd, (off_t)-bsize, SEEK_CUR) < 0) + err(1, "%s", fs); + if (write(fd, ibuf, bsize) != bsize) + err(1, "%s", fs); + (void)fsync(fd); + } + (void)close(fd); + exit(0); +} diff --git a/sbin/disklabel/disklabel.5.5 b/sbin/disklabel/disklabel.5.5 new file mode 100644 index 000000000000..7b4f70b6c0a4 --- /dev/null +++ b/sbin/disklabel/disklabel.5.5 @@ -0,0 +1,388 @@ +.\" Copyright (c) 1987, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Symmetric Computer Systems. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)disklabel.5.5 8.2 (Berkeley) 5/6/94 +.\" +.Dd May 6, 1994 +.Dt DISKLABEL 5 +.Os +.Sh NAME +.Nm disklabel +.Nd disk pack label +.Sh SYNOPSIS +.Fd #include +.Sh DESCRIPTION +Each disk or disk pack on a system may contain a disk label +which provides detailed information +about the geometry of the disk and the partitions into which the disk +is divided. +It should be initialized when the disk is formatted, +and may be changed later with the +.Xr disklabel 8 +program. +This information is used by the system disk driver and by the bootstrap +program to determine how to program the drive +and where to find the filesystems on the disk partitions. +Additional information is used by the filesystem in order +to use the disk most efficiently and to locate important filesystem information. +The description of each partition contains an identifier for the partition +type (standard filesystem, swap area, etc.). +The filesystem updates the in-core copy of the label if it contains +incomplete information about the filesystem. +.Pp +The label is located in sector number +.Dv LABELSECTOR +of the drive, usually sector 0 where it may be found +without any information about the disk geometry. +It is at an offset +.Dv LABELOFFSET +from the beginning of the sector, to allow room for the initial bootstrap. +The disk sector containing the label is normally made read-only +so that it is not accidentally overwritten by pack-to-pack copies +or swap operations; +the +.Dv DIOCWLABEL +.Xr ioctl 2 , +which is done as needed by the +.Xr disklabel +program. +.Pp +A copy of the in-core label for a disk can be obtained with the +.Dv DIOCGDINFO +.Xr ioctl ; +this works with a file descriptor for a block or character (``raw'') device +for any partition of the disk. +The in-core copy of the label is set by the +.Dv DIOCSDINFO +.Xr ioctl . +The offset of a partition cannot generally be changed while it is open, +nor can it be made smaller while it is open. +One exception is that any change is allowed if no label was found +on the disk, and the driver was able to construct only a skeletal label +without partition information. +Finally, the +.Dv DIOCWDINFO +.Xr ioctl +operation sets the in-core label and then updates the on-disk label; +there must be an existing label on the disk for this operation to succeed. +Thus, the initial label for a disk or disk pack must be installed +by writing to the raw disk. +All of these operations are normally done using +.Xr disklabel . +.Pp +The format of the disk label, as specified in +.Aw Pa sys/disklabel.h , +is +.Bd -literal +/* +* Disk description table, see disktab(5) +*/ +#define DISKTAB "/etc/disktab" + +/* +* Each disk has a label which includes information about the hardware +* disk geometry, filesystem partitions, and drive specific information. +* The label is in block 0 or 1, possibly offset from the beginning +* to leave room for a bootstrap, etc. +*/ + +#ifndef LABELSECTOR +#define LABELSECTOR 0 /* sector containing label */ +#endif + +#ifndef LABELOFFSET +#define LABELOFFSET 64 /* offset of label in sector */ +#endif + +#define DISKMAGIC ((u_long) 0x82564557) /* The disk magic number */ +#ifndef MAXPARTITIONS +#define MAXPARTITIONS 8 +#endif + +#ifndef LOCORE +struct disklabel { + u_long d_magic; /* the magic number */ + short d_type; /* drive type */ + short d_subtype; /* controller/d_type specific */ + char d_typename[16]; /* type name, e.g. "eagle" */ + /* + * d_packname contains the pack identifier and is returned when + * the disklabel is read off the disk or in-core copy. + * d_boot0 and d_boot1 are the (optional) names of the + * primary (block 0) and secondary (block 1-15) bootstraps + * as found in /usr/mdec. These are returned when using + * getdiskbyname(3) + to retrieve the values from /etc/disktab. + */ +#if defined(KERNEL) || defined(STANDALONE) + char d_packname[16]; /* pack identifier */ +#else + union { + char un_d_packname[16]; /* pack identifier */ + struct { + char *un_d_boot0; /* primary bootstrap name */ + char *un_d_boot1; /* secondary bootstrap name */ + } un_b; + } d_un; + +#define d_packname d_un.un_d_packname +#define d_boot0 d_un.un_b.un_d_boot0 +#define d_boot1 d_un.un_b.un_d_boot1 +#endif /* ! KERNEL or STANDALONE */ + + /* disk geometry: */ + u_long d_secsize; /* # of bytes per sector */ + u_long d_nsectors; /* # of data sectors per track */ + u_long d_ntracks; /* # of tracks per cylinder */ + u_long d_ncylinders; /* # of data cylinders per unit */ + u_long d_secpercyl; /* # of data sectors per cylinder */ + u_long d_secperunit; /* # of data sectors per unit */ + /* + * Spares (bad sector replacements) below + * are not counted in d_nsectors or d_secpercyl. + * Spare sectors are assumed to be physical sectors + * which occupy space at the end of each track and/or cylinder. + */ + u_short d_sparespertrack; /* # of spare sectors per track */ + u_short d_sparespercyl; /* # of spare sectors per cylinder */ + /* + * Alternate cylinders include maintenance, replacement, + * configuration description areas, etc. + */ + u_long d_acylinders; /* # of alt. cylinders per unit */ + + /* hardware characteristics: */ + /* + * d_interleave, d_trackskew and d_cylskew describe perturbations + * in the media format used to compensate for a slow controller. + * Interleave is physical sector interleave, set up by the formatter + * or controller when formatting. When interleaving is in use, + * logically adjacent sectors are not physically contiguous, + * but instead are separated by some number of sectors. + * It is specified as the ratio of physical sectors traversed + * per logical sector. Thus an interleave of 1:1 implies contiguous + * layout, while 2:1 implies that logical sector 0 is separated + * by one sector from logical sector 1. + * d_trackskew is the offset of sector 0 on track N + * relative to sector 0 on track N-1 on the same cylinder. + * Finally, d_cylskew is the offset of sector 0 on cylinder N + * relative to sector 0 on cylinder N-1. + */ + u_short d_rpm; /* rotational speed */ + u_short d_interleave; /* hardware sector interleave */ + u_short d_trackskew; /* sector 0 skew, per track */ + u_short d_cylskew; /* sector 0 skew, per cylinder */ + u_long d_headswitch; /* head switch time, usec */ + u_long d_trkseek; /* track-to-track seek, usec */ + u_long d_flags; /* generic flags */ +#define NDDATA 5 + u_long d_drivedata[NDDATA]; /* drive-type specific information */ +#define NSPARE 5 + u_long d_spare[NSPARE]; /* reserved for future use */ + u_long d_magic2; /* the magic number (again) */ + u_short d_checksum; /* xor of data incl. partitions */ + + /* filesystem and partition information: */ + u_short d_npartitions; /* number of partitions in following */ + u_long d_bbsize; /* size of boot area at sn0, bytes */ + u_long d_sbsize; /* max size of fs superblock, bytes */ + struct partition { /* the partition table */ + u_long p_size; /* number of sectors in partition */ + u_long p_offset; /* starting sector */ + u_long p_fsize; /* filesystem basic fragment size */ + u_char p_fstype; /* filesystem type, see below */ + u_char p_frag; /* filesystem fragments per block */ + union { + u_short cpg; /* UFS: FS cylinders per group */ + u_short sgs; /* LFS: FS segment shift */ + } __partition_u1; +#define p_cpg __partition_u1.cpg +#define p_sgs __partition_u1.sgs + u_short p_cpg; /* filesystem cylinders per group */ + } d_partitions[MAXPARTITIONS]; /* actually may be more */ +}; + +/* d_type values: */ +#define DTYPE_SMD 1 /* SMD, XSMD; VAX hp/up */ +#define DTYPE_MSCP 2 /* MSCP */ +#define DTYPE_DEC 3 /* other DEC (rk, rl) */ +#define DTYPE_SCSI 4 /* SCSI */ +#define DTYPE_ESDI 5 /* ESDI interface */ +#define DTYPE_ST506 6 /* ST506 etc. */ +#define DTYPE_HPIB 7 /* CS/80 on HP-IB */ +#define DTYPE_HPFL 8 /* HP Fiber-link */ +#define DTYPE_FLOPPY 10 /* floppy */ + +#ifdef DKTYPENAMES +static char *dktypenames[] = { + "unknown", + "SMD", + "MSCP", + "old DEC", + "SCSI", + "ESDI", + "ST506", + "HP-IB", + "HP-FL", + "type 9", + "floppy", + 0 +}; +#define DKMAXTYPES (sizeof(dktypenames) / sizeof(dktypenames[0]) - 1) +#endif + +/* +* Filesystem type and version. +* Used to interpret other filesystem-specific +* per-partition information. +*/ +#define FS_UNUSED 0 /* unused */ +#define FS_SWAP 1 /* swap */ +#define FS_V6 2 /* Sixth Edition */ +#define FS_V7 3 /* Seventh Edition */ +#define FS_SYSV 4 /* System V */ +#define FS_V71K 5 /* V7 with 1K blocks (4.1, 2.9) */ +#define FS_V8 6 /* Eighth Edition, 4K blocks */ +#define FS_BSDFFS 7 /* 4.2BSD fast file system */ +#define FS_MSDOS 8 /* MSDOS file system */ +#define FS_BSDLFS 9 /* 4.4BSD log-structured file system */ +#define FS_OTHER 10 /* in use, but unknown/unsupported */ +#define FS_HPFS 11 /* OS/2 high-performance file system */ +#define FS_ISO9660 12 /* ISO 9660, normally CD-ROM */ +#define FS_BOOT 13 /* partition contains bootstrap */ + +#ifdef DKTYPENAMES +static char *fstypenames[] = { + "unused", + "swap", + "Version 6", + "Version 7", + "System V", + "4.1BSD", + "Eighth Edition", + "4.2BSD", + "MSDOS", + "4.4LFS", + "unknown", + "HPFS", + "ISO9660", + "boot", + 0 +}; +#define FSMAXTYPES (sizeof(fstypenames) / sizeof(fstypenames[0]) - 1) +#endif + +/* +* flags shared by various drives: +*/ +#define D_REMOVABLE 0x01 /* removable media */ +#define D_ECC 0x02 /* supports ECC */ +#define D_BADSECT 0x04 /* supports bad sector forw. */ +#define D_RAMDISK 0x08 /* disk emulator */ +#define D_CHAIN 0x10 /* can do back-back transfers */ + +/* +* Drive data for SMD. +*/ + +#define d_smdflags d_drivedata[0] +#define D_SSE 0x1 /* supports skip sectoring */ +#define d_mindist d_drivedata[1] +#define d_maxdist d_drivedata[2] +#define d_sdist d_drivedata[3] + +/* +* Drive data for ST506. +*/ +#define d_precompcyl d_drivedata[0] +#define d_gap3 d_drivedata[1] /* used only when formatting */ + +/* + * Drive data for SCSI. + */ +#define d_blind d_drivedata[0] + +#ifndef LOCORE +/* +* Structure used to perform a format +* or other raw operation, returning data +* and/or register values. +* Register identification and format +* are device- and driver-dependent. +*/ +struct format_op { + char *df_buf; + int df_count; /* value-result */ + daddr_t df_startblk; + int df_reg[8]; /* result */ +}; + +/* +* Structure used internally to retrieve +* information about a partition on a disk. +*/ +struct partinfo { + struct disklabel *disklab; + struct partition *part; +}; + +/* +* Disk-specific ioctls. +*/ + /* get and set disklabel; DIOCGPART used internally */ +#define DIOCGDINFO _IOR('d', 101, struct disklabel) /* get */ +#define DIOCSDINFO _IOW('d', 102, struct disklabel) /* set */ +#define DIOCWDINFO _IOW('d', 103, struct disklabel) /* set, update disk */ +#define DIOCGPART _IOW('d', 104, struct partinfo) /* get partition */ + +/* do format operation, read or write */ +#define DIOCRFORMAT _IOWR('d', 105, struct format_op) +#define DIOCWFORMAT _IOWR('d', 106, struct format_op) + +#define DIOCSSTEP _IOW('d', 107, int) /* set step rate */ +#define DIOCSRETRIES _IOW('d', 108, int) /* set # of retries */ +#define DIOCWLABEL _IOW('d', 109, int) /* write en/disable label */ + +#define DIOCSBAD _IOW('d', 110, struct dkbad) /* set kernel dkbad */ + +#endif LOCORE +.Ed +.Sh SEE ALSO +.Xr disktab 5 , +.Xr disklabel 8 +.Sh HISTORY +The +.Nm disklabel +function was introduced in +.Bx 4.3 Tahoe . diff --git a/sbin/disklabel/disklabel.c b/sbin/disklabel/disklabel.c new file mode 100644 index 000000000000..7a70c30829e2 --- /dev/null +++ b/sbin/disklabel/disklabel.c @@ -0,0 +1,1315 @@ +/* + * Copyright (c) 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Symmetric Computer Systems. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1987, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)disklabel.c 8.4 (Berkeley) 5/4/95"; +/* from static char sccsid[] = "@(#)disklabel.c 1.2 (Symmetric) 11/28/85"; */ +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#define DKTYPENAMES +#include +#include +#include +#include +#include +#include +#include +#include "pathnames.h" + +/* + * Disklabel: read and write disklabels. + * The label is usually placed on one of the first sectors of the disk. + * Many machines also place a bootstrap in the same area, + * in which case the label is embedded in the bootstrap. + * The bootstrap source must leave space at the proper offset + * for the label on such machines. + */ + +#ifdef tahoe +#define RAWPARTITION 'a' +#else +#define RAWPARTITION 'c' +#endif + +#ifndef BBSIZE +#define BBSIZE 8192 /* size of boot area, with label */ +#endif + +#ifdef tahoe +#define NUMBOOT 0 +#else +#if defined(hp300) || defined(hp800) +#define NUMBOOT 1 +#else +#define NUMBOOT 2 +#endif +#endif + +#define DEFEDITOR _PATH_VI +#define streq(a,b) (strcmp(a,b) == 0) + +char *dkname; +char *specname; +char tmpfil[] = _PATH_TMP; + +extern int errno; +char namebuf[BBSIZE], *np = namebuf; +struct disklabel lab; +struct disklabel *readlabel(), *makebootarea(); +char bootarea[BBSIZE]; + +#if NUMBOOT > 0 +int installboot; /* non-zero if we should install a boot program */ +char *bootbuf; /* pointer to buffer with remainder of boot prog */ +int bootsize; /* size of remaining boot program */ +char *xxboot; /* primary boot */ +char *bootxx; /* secondary boot */ +char boot0[MAXPATHLEN]; +char boot1[MAXPATHLEN]; +#endif + +enum { + UNSPEC, EDIT, NOWRITE, READ, RESTORE, WRITE, WRITEABLE, WRITEBOOT +} op = UNSPEC; + +int rflag; + +#ifdef DEBUG +int debug; +#define OPTIONS "BNRWb:ders:w" +#else +#define OPTIONS "BNRWb:ers:w" +#endif + + +main(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; + extern int optind; + register struct disklabel *lp; + FILE *t; + int ch, f, flag, error = 0; + char *name = 0; + + while ((ch = getopt(argc, argv, OPTIONS)) != EOF) + switch (ch) { +#if NUMBOOT > 0 + case 'B': + ++installboot; + break; + case 'b': + xxboot = optarg; + break; +#if NUMBOOT > 1 + case 's': + bootxx = optarg; + break; +#endif +#endif + case 'N': + if (op != UNSPEC) + usage(); + op = NOWRITE; + break; + case 'R': + if (op != UNSPEC) + usage(); + op = RESTORE; + break; + case 'W': + if (op != UNSPEC) + usage(); + op = WRITEABLE; + break; + case 'e': + if (op != UNSPEC) + usage(); + op = EDIT; + break; + case 'r': + ++rflag; + break; + case 'w': + if (op != UNSPEC) + usage(); + op = WRITE; + break; +#ifdef DEBUG + case 'd': + debug++; + break; +#endif + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; +#if NUMBOOT > 0 + if (installboot) { + rflag++; + if (op == UNSPEC) + op = WRITEBOOT; + } else { + if (op == UNSPEC) + op = READ; + xxboot = bootxx = 0; + } +#else + if (op == UNSPEC) + op = READ; +#endif + if (argc < 1) + usage(); + + dkname = argv[0]; + if (dkname[0] != '/') { + (void)sprintf(np, "%sr%s%c", _PATH_DEV, dkname, RAWPARTITION); + specname = np; + np += strlen(specname) + 1; + } else + specname = dkname; + f = open(specname, op == READ ? O_RDONLY : O_RDWR); + if (f < 0 && errno == ENOENT && dkname[0] != '/') { + (void)sprintf(specname, "%sr%s", _PATH_DEV, dkname); + np = namebuf + strlen(specname) + 1; + f = open(specname, op == READ ? O_RDONLY : O_RDWR); + } + if (f < 0) + Perror(specname); + + switch(op) { + + case EDIT: + if (argc != 1) + usage(); + lp = readlabel(f); + error = edit(lp, f); + break; + + case NOWRITE: + flag = 0; + if (ioctl(f, DIOCWLABEL, (char *)&flag) < 0) + Perror("ioctl DIOCWLABEL"); + break; + + case READ: + if (argc != 1) + usage(); + lp = readlabel(f); + display(stdout, lp); + error = checklabel(lp); + break; + + case RESTORE: +#if NUMBOOT > 0 + if (installboot && argc == 3) { + makelabel(argv[2], 0, &lab); + argc--; + } +#endif + if (argc != 2) + usage(); + lp = makebootarea(bootarea, &lab, f); + if (!(t = fopen(argv[1], "r"))) + Perror(argv[1]); + if (getasciilabel(t, lp)) + error = writelabel(f, bootarea, lp); + break; + + case WRITE: + if (argc == 3) { + name = argv[2]; + argc--; + } + if (argc != 2) + usage(); + makelabel(argv[1], name, &lab); + lp = makebootarea(bootarea, &lab, f); + *lp = lab; + if (checklabel(lp) == 0) + error = writelabel(f, bootarea, lp); + break; + + case WRITEABLE: + flag = 1; + if (ioctl(f, DIOCWLABEL, (char *)&flag) < 0) + Perror("ioctl DIOCWLABEL"); + break; + +#if NUMBOOT > 0 + case WRITEBOOT: + { + struct disklabel tlab; + + lp = readlabel(f); + tlab = *lp; + if (argc == 2) + makelabel(argv[1], 0, &lab); + lp = makebootarea(bootarea, &lab, f); + *lp = tlab; + if (checklabel(lp) == 0) + error = writelabel(f, bootarea, lp); + break; + } +#endif + } + exit(error); +} + +/* + * Construct a prototype disklabel from /etc/disktab. As a side + * effect, set the names of the primary and secondary boot files + * if specified. + */ +makelabel(type, name, lp) + char *type, *name; + register struct disklabel *lp; +{ + register struct disklabel *dp; + char *strcpy(); + + dp = getdiskbyname(type); + if (dp == NULL) { + fprintf(stderr, "%s: unknown disk type\n", type); + exit(1); + } + *lp = *dp; +#if NUMBOOT > 0 + /* + * Set bootstrap name(s). + * 1. If set from command line, use those, + * 2. otherwise, check if disktab specifies them (b0 or b1), + * 3. otherwise, makebootarea() will choose ones based on the name + * of the disk special file. E.g. /dev/ra0 -> raboot, bootra + */ + if (!xxboot && lp->d_boot0) { + if (*lp->d_boot0 != '/') + (void)sprintf(boot0, "%s/%s", + _PATH_BOOTDIR, lp->d_boot0); + else + (void)strcpy(boot0, lp->d_boot0); + xxboot = boot0; + } +#if NUMBOOT > 1 + if (!bootxx && lp->d_boot1) { + if (*lp->d_boot1 != '/') + (void)sprintf(boot1, "%s/%s", + _PATH_BOOTDIR, lp->d_boot1); + else + (void)strcpy(boot1, lp->d_boot1); + bootxx = boot1; + } +#endif +#endif + /* d_packname is union d_boot[01], so zero */ + memset(lp->d_packname, 0, sizeof(lp->d_packname)); + if (name) + (void)strncpy(lp->d_packname, name, sizeof(lp->d_packname)); +} + +writelabel(f, boot, lp) + int f; + char *boot; + register struct disklabel *lp; +{ + register int i; + int flag; + + setbootflag(lp); + lp->d_magic = DISKMAGIC; + lp->d_magic2 = DISKMAGIC; + lp->d_checksum = 0; + lp->d_checksum = dkcksum(lp); + if (rflag) { + /* + * First set the kernel disk label, + * then write a label to the raw disk. + * If the SDINFO ioctl fails because it is unimplemented, + * keep going; otherwise, the kernel consistency checks + * may prevent us from changing the current (in-core) + * label. + */ + if (ioctl(f, DIOCSDINFO, lp) < 0 && + errno != ENODEV && errno != ENOTTY) { + l_perror("ioctl DIOCSDINFO"); + return (1); + } + (void)lseek(f, (off_t)0, SEEK_SET); + /* + * write enable label sector before write (if necessary), + * disable after writing. + */ + flag = 1; + if (ioctl(f, DIOCWLABEL, &flag) < 0) + perror("ioctl DIOCWLABEL"); + if (write(f, boot, lp->d_bbsize) != lp->d_bbsize) { + perror("write"); + return (1); + } +#if NUMBOOT > 0 + /* + * Output the remainder of the disklabel + */ + if (bootbuf && write(f, bootbuf, bootsize) != bootsize) { + perror("write"); + return(1); + } +#endif + flag = 0; + (void) ioctl(f, DIOCWLABEL, &flag); + } else if (ioctl(f, DIOCWDINFO, lp) < 0) { + l_perror("ioctl DIOCWDINFO"); + return (1); + } +#ifdef vax + if (lp->d_type == DTYPE_SMD && lp->d_flags & D_BADSECT) { + daddr_t alt; + + alt = lp->d_ncylinders * lp->d_secpercyl - lp->d_nsectors; + for (i = 1; i < 11 && i < lp->d_nsectors; i += 2) { + (void)lseek(f, (off_t)((alt + i) * lp->d_secsize), + SEEK_SET); + if (write(f, boot, lp->d_secsize) < lp->d_secsize) { + int oerrno = errno; + fprintf(stderr, "alternate label %d ", i/2); + errno = oerrno; + perror("write"); + } + } + } +#endif + return (0); +} + +l_perror(s) + char *s; +{ + int saverrno = errno; + + fprintf(stderr, "disklabel: %s: ", s); + + switch (saverrno) { + + case ESRCH: + fprintf(stderr, "No disk label on disk;\n"); + fprintf(stderr, + "use \"disklabel -r\" to install initial label\n"); + break; + + case EINVAL: + fprintf(stderr, "Label magic number or checksum is wrong!\n"); + fprintf(stderr, "(disklabel or kernel is out of date?)\n"); + break; + + case EBUSY: + fprintf(stderr, "Open partition would move or shrink\n"); + break; + + case EXDEV: + fprintf(stderr, + "Labeled partition or 'a' partition must start at beginning of disk\n"); + break; + + default: + errno = saverrno; + perror((char *)NULL); + break; + } +} + +/* + * Fetch disklabel for disk. + * Use ioctl to get label unless -r flag is given. + */ +struct disklabel * +readlabel(f) + int f; +{ + register struct disklabel *lp; + + if (rflag) { + if (read(f, bootarea, BBSIZE) < BBSIZE) + Perror(specname); + for (lp = (struct disklabel *)bootarea; + lp <= (struct disklabel *)(bootarea + BBSIZE - sizeof(*lp)); + lp = (struct disklabel *)((char *)lp + 16)) + if (lp->d_magic == DISKMAGIC && + lp->d_magic2 == DISKMAGIC) + break; + if (lp > (struct disklabel *)(bootarea+BBSIZE-sizeof(*lp)) || + lp->d_magic != DISKMAGIC || lp->d_magic2 != DISKMAGIC || + dkcksum(lp) != 0) { + fprintf(stderr, + "Bad pack magic number (label is damaged, or pack is unlabeled)\n"); + /* lp = (struct disklabel *)(bootarea + LABELOFFSET); */ + exit (1); + } + } else { + lp = &lab; + if (ioctl(f, DIOCGDINFO, lp) < 0) + Perror("ioctl DIOCGDINFO"); + } + return (lp); +} + +/* + * Construct a bootarea (d_bbsize bytes) in the specified buffer ``boot'' + * Returns a pointer to the disklabel portion of the bootarea. + */ +struct disklabel * +makebootarea(boot, dp, f) + char *boot; + register struct disklabel *dp; + int f; +{ + struct disklabel *lp; + register char *p; + int b; +#if NUMBOOT > 0 + char *dkbasename; + struct stat sb; +#endif + + /* XXX */ + if (dp->d_secsize == 0) { + dp->d_secsize = DEV_BSIZE; + dp->d_bbsize = BBSIZE; + } + lp = (struct disklabel *) + (boot + (LABELSECTOR * dp->d_secsize) + LABELOFFSET); + memset(lp, 0, sizeof *lp); +#if NUMBOOT > 0 + /* + * If we are not installing a boot program but we are installing a + * label on disk then we must read the current bootarea so we don't + * clobber the existing boot. + */ + if (!installboot) { + if (rflag) { + if (read(f, boot, BBSIZE) < BBSIZE) + Perror(specname); + memset(lp, 0, sizeof *lp); + } + return (lp); + } + /* + * We are installing a boot program. Determine the name(s) and + * read them into the appropriate places in the boot area. + */ + if (!xxboot || !bootxx) { + dkbasename = np; + if ((p = strrchr(dkname, '/')) == NULL) + p = dkname; + else + p++; + while (*p && !isdigit(*p)) + *np++ = *p++; + *np++ = '\0'; + + if (!xxboot) { + (void)sprintf(np, "%s/%sboot", + _PATH_BOOTDIR, dkbasename); + if (access(np, F_OK) < 0 && dkbasename[0] == 'r') + dkbasename++; + xxboot = np; + (void)sprintf(xxboot, "%s/%sboot", + _PATH_BOOTDIR, dkbasename); + np += strlen(xxboot) + 1; + } +#if NUMBOOT > 1 + if (!bootxx) { + (void)sprintf(np, "%s/boot%s", + _PATH_BOOTDIR, dkbasename); + if (access(np, F_OK) < 0 && dkbasename[0] == 'r') + dkbasename++; + bootxx = np; + (void)sprintf(bootxx, "%s/boot%s", + _PATH_BOOTDIR, dkbasename); + np += strlen(bootxx) + 1; + } +#endif + } +#ifdef DEBUG + if (debug) + fprintf(stderr, "bootstraps: xxboot = %s, bootxx = %s\n", + xxboot, bootxx ? bootxx : "NONE"); +#endif + + /* + * Strange rules: + * 1. One-piece bootstrap (hp300/hp800) + * up to d_bbsize bytes of ``xxboot'' go in bootarea, the rest + * is remembered and written later following the bootarea. + * 2. Two-piece bootstraps (vax/i386?/mips?) + * up to d_secsize bytes of ``xxboot'' go in first d_secsize + * bytes of bootarea, remaining d_bbsize-d_secsize filled + * from ``bootxx''. + */ + b = open(xxboot, O_RDONLY); + if (b < 0) + Perror(xxboot); +#if NUMBOOT > 1 + if (read(b, boot, (int)dp->d_secsize) < 0) + Perror(xxboot); + (void)close(b); + b = open(bootxx, O_RDONLY); + if (b < 0) + Perror(bootxx); + if (read(b, &boot[dp->d_secsize], (int)(dp->d_bbsize-dp->d_secsize)) < 0) + Perror(bootxx); +#else + if (read(b, boot, (int)dp->d_bbsize) < 0) + Perror(xxboot); + (void)fstat(b, &sb); + bootsize = (int)sb.st_size - dp->d_bbsize; + if (bootsize > 0) { + /* XXX assume d_secsize is a power of two */ + bootsize = (bootsize + dp->d_secsize-1) & ~(dp->d_secsize-1); + bootbuf = (char *)malloc((size_t)bootsize); + if (bootbuf == 0) + Perror(xxboot); + if (read(b, bootbuf, bootsize) < 0) { + free(bootbuf); + Perror(xxboot); + } + } +#endif + (void)close(b); +#endif + /* + * Make sure no part of the bootstrap is written in the area + * reserved for the label. + */ + for (p = (char *)lp; p < (char *)lp + sizeof(struct disklabel); p++) + if (*p) { + fprintf(stderr, + "Bootstrap doesn't leave room for disk label\n"); + exit(2); + } + return (lp); +} + +display(f, lp) + FILE *f; + register struct disklabel *lp; +{ + register int i, j; + register struct partition *pp; + + fprintf(f, "# %s:\n", specname); + if ((unsigned) lp->d_type < DKMAXTYPES) + fprintf(f, "type: %s\n", dktypenames[lp->d_type]); + else + fprintf(f, "type: %d\n", lp->d_type); + fprintf(f, "disk: %.*s\n", sizeof(lp->d_typename), lp->d_typename); + fprintf(f, "label: %.*s\n", sizeof(lp->d_packname), lp->d_packname); + fprintf(f, "flags:"); + if (lp->d_flags & D_REMOVABLE) + fprintf(f, " removeable"); + if (lp->d_flags & D_ECC) + fprintf(f, " ecc"); + if (lp->d_flags & D_BADSECT) + fprintf(f, " badsect"); + fprintf(f, "\n"); + fprintf(f, "bytes/sector: %d\n", lp->d_secsize); + fprintf(f, "sectors/track: %d\n", lp->d_nsectors); + fprintf(f, "tracks/cylinder: %d\n", lp->d_ntracks); + fprintf(f, "sectors/cylinder: %d\n", lp->d_secpercyl); + fprintf(f, "cylinders: %d\n", lp->d_ncylinders); + fprintf(f, "rpm: %d\n", lp->d_rpm); + fprintf(f, "interleave: %d\n", lp->d_interleave); + fprintf(f, "trackskew: %d\n", lp->d_trackskew); + fprintf(f, "cylinderskew: %d\n", lp->d_cylskew); + fprintf(f, "headswitch: %d\t\t# milliseconds\n", lp->d_headswitch); + fprintf(f, "track-to-track seek: %d\t# milliseconds\n", lp->d_trkseek); + fprintf(f, "drivedata: "); + for (i = NDDATA - 1; i >= 0; i--) + if (lp->d_drivedata[i]) + break; + if (i < 0) + i = 0; + for (j = 0; j <= i; j++) + fprintf(f, "%d ", lp->d_drivedata[j]); + fprintf(f, "\n\n%d partitions:\n", lp->d_npartitions); + fprintf(f, + "# size offset fstype [fsize bsize cpg]\n"); + pp = lp->d_partitions; + for (i = 0; i < lp->d_npartitions; i++, pp++) { + if (pp->p_size) { + fprintf(f, " %c: %8d %8d ", 'a' + i, + pp->p_size, pp->p_offset); + if ((unsigned) pp->p_fstype < FSMAXTYPES) + fprintf(f, "%8.8s", fstypenames[pp->p_fstype]); + else + fprintf(f, "%8d", pp->p_fstype); + switch (pp->p_fstype) { + + case FS_UNUSED: /* XXX */ + fprintf(f, " %5d %5d %5.5s ", + pp->p_fsize, pp->p_fsize * pp->p_frag, ""); + break; + + case FS_BSDFFS: + fprintf(f, " %5d %5d %5d ", + pp->p_fsize, pp->p_fsize * pp->p_frag, + pp->p_cpg); + break; + + default: + fprintf(f, "%20.20s", ""); + break; + } + fprintf(f, "\t# (Cyl. %4d", + pp->p_offset / lp->d_secpercyl); + if (pp->p_offset % lp->d_secpercyl) + putc('*', f); + else + putc(' ', f); + fprintf(f, "- %d", + (pp->p_offset + + pp->p_size + lp->d_secpercyl - 1) / + lp->d_secpercyl - 1); + if (pp->p_size % lp->d_secpercyl) + putc('*', f); + fprintf(f, ")\n"); + } + } + fflush(f); +} + +edit(lp, f) + struct disklabel *lp; + int f; +{ + register int c; + struct disklabel label; + FILE *fd; + char *mktemp(); + + (void) mktemp(tmpfil); + fd = fopen(tmpfil, "w"); + if (fd == NULL) { + fprintf(stderr, "%s: Can't create\n", tmpfil); + return (1); + } + (void)fchmod(fileno(fd), 0600); + display(fd, lp); + fclose(fd); + for (;;) { + if (!editit()) + break; + fd = fopen(tmpfil, "r"); + if (fd == NULL) { + fprintf(stderr, "%s: Can't reopen for reading\n", + tmpfil); + break; + } + memset(&label, 0, sizeof(label)); + if (getasciilabel(fd, &label)) { + *lp = label; + if (writelabel(f, bootarea, lp) == 0) { + (void) unlink(tmpfil); + return (0); + } + } + printf("re-edit the label? [y]: "); fflush(stdout); + c = getchar(); + if (c != EOF && c != (int)'\n') + while (getchar() != (int)'\n') + ; + if (c == (int)'n') + break; + } + (void) unlink(tmpfil); + return (1); +} + +editit() +{ + register int pid, xpid; + int stat, omask; + extern char *getenv(); + + omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP)); + while ((pid = fork()) < 0) { + extern int errno; + + if (errno == EPROCLIM) { + fprintf(stderr, "You have too many processes\n"); + return(0); + } + if (errno != EAGAIN) { + perror("fork"); + return(0); + } + sleep(1); + } + if (pid == 0) { + register char *ed; + + sigsetmask(omask); + setgid(getgid()); + setuid(getuid()); + if ((ed = getenv("EDITOR")) == (char *)0) + ed = DEFEDITOR; + execlp(ed, ed, tmpfil, 0); + perror(ed); + exit(1); + } + while ((xpid = wait(&stat)) >= 0) + if (xpid == pid) + break; + sigsetmask(omask); + return(!stat); +} + +char * +skip(cp) + register char *cp; +{ + + while (*cp != '\0' && isspace(*cp)) + cp++; + if (*cp == '\0' || *cp == '#') + return ((char *)NULL); + return (cp); +} + +char * +word(cp) + register char *cp; +{ + register char c; + + while (*cp != '\0' && !isspace(*cp) && *cp != '#') + cp++; + if ((c = *cp) != '\0') { + *cp++ = '\0'; + if (c != '#') + return (skip(cp)); + } + return ((char *)NULL); +} + +/* + * Read an ascii label in from fd f, + * in the same format as that put out by display(), + * and fill in lp. + */ +getasciilabel(f, lp) + FILE *f; + register struct disklabel *lp; +{ + register char **cpp, *cp; + register struct partition *pp; + char *tp, *s, line[BUFSIZ]; + int v, lineno = 0, errors = 0; + + lp->d_bbsize = BBSIZE; /* XXX */ + lp->d_sbsize = SBSIZE; /* XXX */ + while (fgets(line, sizeof(line) - 1, f)) { + lineno++; + if (cp = strchr(line,'\n')) + *cp = '\0'; + cp = skip(line); + if (cp == NULL) + continue; + tp = strchr(cp, ':'); + if (tp == NULL) { + fprintf(stderr, "line %d: syntax error\n", lineno); + errors++; + continue; + } + *tp++ = '\0', tp = skip(tp); + if (streq(cp, "type")) { + if (tp == NULL) + tp = "unknown"; + cpp = dktypenames; + for (; cpp < &dktypenames[DKMAXTYPES]; cpp++) + if ((s = *cpp) && streq(s, tp)) { + lp->d_type = cpp - dktypenames; + goto next; + } + v = atoi(tp); + if ((unsigned)v >= DKMAXTYPES) + fprintf(stderr, "line %d:%s %d\n", lineno, + "Warning, unknown disk type", v); + lp->d_type = v; + continue; + } + if (streq(cp, "flags")) { + for (v = 0; (cp = tp) && *cp != '\0';) { + tp = word(cp); + if (streq(cp, "removeable")) + v |= D_REMOVABLE; + else if (streq(cp, "ecc")) + v |= D_ECC; + else if (streq(cp, "badsect")) + v |= D_BADSECT; + else { + fprintf(stderr, + "line %d: %s: bad flag\n", + lineno, cp); + errors++; + } + } + lp->d_flags = v; + continue; + } + if (streq(cp, "drivedata")) { + register int i; + + for (i = 0; (cp = tp) && *cp != '\0' && i < NDDATA;) { + lp->d_drivedata[i++] = atoi(cp); + tp = word(cp); + } + continue; + } + if (sscanf(cp, "%d partitions", &v) == 1) { + if (v == 0 || (unsigned)v > MAXPARTITIONS) { + fprintf(stderr, + "line %d: bad # of partitions\n", lineno); + lp->d_npartitions = MAXPARTITIONS; + errors++; + } else + lp->d_npartitions = v; + continue; + } + if (tp == NULL) + tp = ""; + if (streq(cp, "disk")) { + strncpy(lp->d_typename, tp, sizeof (lp->d_typename)); + continue; + } + if (streq(cp, "label")) { + strncpy(lp->d_packname, tp, sizeof (lp->d_packname)); + continue; + } + if (streq(cp, "bytes/sector")) { + v = atoi(tp); + if (v <= 0 || (v % 512) != 0) { + fprintf(stderr, + "line %d: %s: bad sector size\n", + lineno, tp); + errors++; + } else + lp->d_secsize = v; + continue; + } + if (streq(cp, "sectors/track")) { + v = atoi(tp); + if (v <= 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_nsectors = v; + continue; + } + if (streq(cp, "sectors/cylinder")) { + v = atoi(tp); + if (v <= 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_secpercyl = v; + continue; + } + if (streq(cp, "tracks/cylinder")) { + v = atoi(tp); + if (v <= 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_ntracks = v; + continue; + } + if (streq(cp, "cylinders")) { + v = atoi(tp); + if (v <= 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_ncylinders = v; + continue; + } + if (streq(cp, "rpm")) { + v = atoi(tp); + if (v <= 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_rpm = v; + continue; + } + if (streq(cp, "interleave")) { + v = atoi(tp); + if (v <= 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_interleave = v; + continue; + } + if (streq(cp, "trackskew")) { + v = atoi(tp); + if (v < 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_trackskew = v; + continue; + } + if (streq(cp, "cylinderskew")) { + v = atoi(tp); + if (v < 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_cylskew = v; + continue; + } + if (streq(cp, "headswitch")) { + v = atoi(tp); + if (v < 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_headswitch = v; + continue; + } + if (streq(cp, "track-to-track seek")) { + v = atoi(tp); + if (v < 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_trkseek = v; + continue; + } + if ('a' <= *cp && *cp <= 'z' && cp[1] == '\0') { + unsigned part = *cp - 'a'; + + if (part > lp->d_npartitions) { + fprintf(stderr, + "line %d: bad partition name\n", lineno); + errors++; + continue; + } + pp = &lp->d_partitions[part]; +#define NXTNUM(n) { \ + cp = tp, tp = word(cp); \ + if (tp == NULL) \ + tp = cp; \ + (n) = atoi(cp); \ + } + + NXTNUM(v); + if (v < 0) { + fprintf(stderr, + "line %d: %s: bad partition size\n", + lineno, cp); + errors++; + } else + pp->p_size = v; + NXTNUM(v); + if (v < 0) { + fprintf(stderr, + "line %d: %s: bad partition offset\n", + lineno, cp); + errors++; + } else + pp->p_offset = v; + cp = tp, tp = word(cp); + cpp = fstypenames; + for (; cpp < &fstypenames[FSMAXTYPES]; cpp++) + if ((s = *cpp) && streq(s, cp)) { + pp->p_fstype = cpp - fstypenames; + goto gottype; + } + if (isdigit(*cp)) + v = atoi(cp); + else + v = FSMAXTYPES; + if ((unsigned)v >= FSMAXTYPES) { + fprintf(stderr, "line %d: %s %s\n", lineno, + "Warning, unknown filesystem type", cp); + v = FS_UNUSED; + } + pp->p_fstype = v; + gottype: + + switch (pp->p_fstype) { + + case FS_UNUSED: /* XXX */ + NXTNUM(pp->p_fsize); + if (pp->p_fsize == 0) + break; + NXTNUM(v); + pp->p_frag = v / pp->p_fsize; + break; + + case FS_BSDFFS: + NXTNUM(pp->p_fsize); + if (pp->p_fsize == 0) + break; + NXTNUM(v); + pp->p_frag = v / pp->p_fsize; + NXTNUM(pp->p_cpg); + break; + + default: + break; + } + continue; + } + fprintf(stderr, "line %d: %s: Unknown disklabel field\n", + lineno, cp); + errors++; + next: + ; + } + errors += checklabel(lp); + return (errors == 0); +} + +/* + * Check disklabel for errors and fill in + * derived fields according to supplied values. + */ +checklabel(lp) + register struct disklabel *lp; +{ + register struct partition *pp; + int i, errors = 0; + char part; + + if (lp->d_secsize == 0) { + fprintf(stderr, "sector size %d\n", lp->d_secsize); + return (1); + } + if (lp->d_nsectors == 0) { + fprintf(stderr, "sectors/track %d\n", lp->d_nsectors); + return (1); + } + if (lp->d_ntracks == 0) { + fprintf(stderr, "tracks/cylinder %d\n", lp->d_ntracks); + return (1); + } + if (lp->d_ncylinders == 0) { + fprintf(stderr, "cylinders/unit %d\n", lp->d_ncylinders); + errors++; + } + if (lp->d_rpm == 0) + Warning("revolutions/minute %d", lp->d_rpm); + if (lp->d_secpercyl == 0) + lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks; + if (lp->d_secperunit == 0) + lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders; + if (lp->d_bbsize == 0) { + fprintf(stderr, "boot block size %d\n", lp->d_bbsize); + errors++; + } else if (lp->d_bbsize % lp->d_secsize) + Warning("boot block size %% sector-size != 0"); + if (lp->d_sbsize == 0) { + fprintf(stderr, "super block size %d\n", lp->d_sbsize); + errors++; + } else if (lp->d_sbsize % lp->d_secsize) + Warning("super block size %% sector-size != 0"); + if (lp->d_npartitions > MAXPARTITIONS) + Warning("number of partitions (%d) > MAXPARTITIONS (%d)", + lp->d_npartitions, MAXPARTITIONS); + for (i = 0; i < lp->d_npartitions; i++) { + part = 'a' + i; + pp = &lp->d_partitions[i]; + if (pp->p_size == 0 && pp->p_offset != 0) + Warning("partition %c: size 0, but offset %d", + part, pp->p_offset); +#ifdef notdef + if (pp->p_size % lp->d_secpercyl) + Warning("partition %c: size %% cylinder-size != 0", + part); + if (pp->p_offset % lp->d_secpercyl) + Warning("partition %c: offset %% cylinder-size != 0", + part); +#endif + if (pp->p_offset > lp->d_secperunit) { + fprintf(stderr, + "partition %c: offset past end of unit\n", part); + errors++; + } + if (pp->p_offset + pp->p_size > lp->d_secperunit) { + fprintf(stderr, + "partition %c: partition extends past end of unit\n", + part); + errors++; + } + } + for (; i < MAXPARTITIONS; i++) { + part = 'a' + i; + pp = &lp->d_partitions[i]; + if (pp->p_size || pp->p_offset) + Warning("unused partition %c: size %d offset %d", + 'a' + i, pp->p_size, pp->p_offset); + } + return (errors); +} + +/* + * If we are installing a boot program that doesn't fit in d_bbsize + * we need to mark those partitions that the boot overflows into. + * This allows newfs to prevent creation of a filesystem where it might + * clobber bootstrap code. + */ +setbootflag(lp) + register struct disklabel *lp; +{ + register struct partition *pp; + int i, errors = 0; + char part; + u_long boffset; + + if (bootbuf == 0) + return; + boffset = bootsize / lp->d_secsize; + for (i = 0; i < lp->d_npartitions; i++) { + part = 'a' + i; + pp = &lp->d_partitions[i]; + if (pp->p_size == 0) + continue; + if (boffset <= pp->p_offset) { + if (pp->p_fstype == FS_BOOT) + pp->p_fstype = FS_UNUSED; + } else if (pp->p_fstype != FS_BOOT) { + if (pp->p_fstype != FS_UNUSED) { + fprintf(stderr, + "boot overlaps used partition %c\n", + part); + errors++; + } else { + pp->p_fstype = FS_BOOT; + Warning("boot overlaps partition %c, %s", + part, "marked as FS_BOOT"); + } + } + } + if (errors) { + fprintf(stderr, "Cannot install boot program\n"); + exit(4); + } +} + +/*VARARGS1*/ +Warning(fmt, a1, a2, a3, a4, a5) + char *fmt; +{ + + fprintf(stderr, "Warning, "); + fprintf(stderr, fmt, a1, a2, a3, a4, a5); + fprintf(stderr, "\n"); +} + +Perror(str) + char *str; +{ + fputs("disklabel: ", stderr); perror(str); + exit(4); +} + +usage() +{ +#if NUMBOOT > 0 + fprintf(stderr, +"%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n", +"usage: disklabel [-r] disk", + "(to read label)", +"or disklabel -w [-r] disk type [ packid ]", + "(to write label with existing boot program)", +"or disklabel -e [-r] disk", + "(to edit label)", +"or disklabel -R [-r] disk protofile", + "(to restore label with existing boot program)", +#if NUMBOOT > 1 +"or disklabel -B [ -b boot1 [ -s boot2 ] ] disk [ type ]", + "(to install boot program with existing label)", +"or disklabel -w -B [ -b boot1 [ -s boot2 ] ] disk type [ packid ]", + "(to write label and boot program)", +"or disklabel -R -B [ -b boot1 [ -s boot2 ] ] disk protofile [ type ]", + "(to restore label and boot program)", +#else +"or disklabel -B [ -b bootprog ] disk [ type ]", + "(to install boot program with existing on-disk label)", +"or disklabel -w -B [ -b bootprog ] disk type [ packid ]", + "(to write label and install boot program)", +"or disklabel -R -B [ -b bootprog ] disk protofile [ type ]", + "(to restore label and install boot program)", +#endif +"or disklabel [-NW] disk", + "(to write disable/enable label)"); +#else + fprintf(stderr, "%-43s%s\n%-43s%s\n%-43s%s\n%-43s%s\n%-43s%s\n", +"usage: disklabel [-r] disk", "(to read label)", +"or disklabel -w [-r] disk type [ packid ]", "(to write label)", +"or disklabel -e [-r] disk", "(to edit label)", +"or disklabel -R [-r] disk protofile", "(to restore label)", +"or disklabel [-NW] disk", "(to write disable/enable label)"); +#endif + exit(1); +} diff --git a/sbin/fastboot/fastboot.8 b/sbin/fastboot/fastboot.8 new file mode 100644 index 000000000000..2f6ac829da99 --- /dev/null +++ b/sbin/fastboot/fastboot.8 @@ -0,0 +1,69 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)fastboot.8 8.1 (Berkeley) 6/5/93 +.\" +.Dd June 5, 1993 +.Dt FASTBOOT 8 +.Os BSD 4.2 +.Sh NAME +.Nm fastboot , +.Nm fasthalt +.Nd "reboot/halt the system without checking the disks" +.Sh SYNOPSIS +.Nm fastboot +.Op Ar boot-options +.Nm fasthalt +.Op Ar halt-options +.Sh DESCRIPTION +.Nm Fastboot +and +.Nm fasthalt +are shell scripts which reboot and halt the system without +checking the file systems. This is done by creating a +file +.Pa /fastboot , +then invoking the +.Xr reboot +program. The system startup script, +.Pa /etc/rc , +looks for this file and, if present, skips the normal +invocation of +.Xr fsck 8 . +.Sh SEE ALSO +.Xr halt 8 , +.Xr reboot 8 , +.Xr rc 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8 new file mode 100644 index 000000000000..b7bc4dbb6b7c --- /dev/null +++ b/sbin/ifconfig/ifconfig.8 @@ -0,0 +1,277 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)ifconfig.8 8.4 (Berkeley) 6/1/94 +.\" +.Dd June 1, 1994 +.Dt IFCONFIG 8 +.Os BSD 4.2 +.Sh NAME +.Nm ifconfig +.Nd configure network interface parameters +.Sh SYNOPSIS +.Nm ifconfig +.Ar interface address_family +.Oo +.Ar address +.Op Ar dest_address +.Oc +.Op Ar parameters +.Nm ifconfig +.Ar interface +.Op Ar protocol_family +.Sh DESCRIPTION +.Nm Ifconfig +is used to assign an address +to a network interface and/or configure +network interface parameters. +.Nm Ifconfig +must be used at boot time to define the network address +of each interface present on a machine; it may also be used at +a later time to redefine an interface's address +or other operating parameters. +.Pp +Available operands for +.Nm ifconfig: +.Bl -tag -width Ds +.It Ar Address +For the +.Tn DARPA-Internet +family, +the address is either a host name present in the host name data +base, +.Xr hosts 5 , +or a +.Tn DARPA +Internet address expressed in the Internet standard +.Dq dot notation . +For the Xerox Network Systems(tm) family, +addresses are +.Ar net:a.b.c.d.e.f , +where +.Ar net +is the assigned network number (in decimal), +and each of the six bytes of the host number, +.Ar a +through +.Ar f , +are specified in hexadecimal. +The host number may be omitted on 10Mb/s Ethernet interfaces, +which use the hardware physical address, +and on interfaces other than the first. +For the +.Tn ISO +family, addresses are specified as a long hexadecimal string, +as in the Xerox family. However, two consecutive dots imply a zero +byte, and the dots are optional, if the user wishes to (carefully) +count out long strings of digits in network byte order. +.It Ar address_family +Specifies the +.Ar address family +which affects interpretation of the remaining parameters. +Since an interface can receive transmissions in differing protocols +with different naming schemes, specifying the address family is recommeded. +The address or protocol families currently +supported are +.Dq inet , +.Dq iso , +and +.Dq ns . +.It Ar Interface +The +.Ar interface +parameter is a string of the form +.Dq name unit , +for example, +.Dq en0 +.El +.Pp +The following parameters may be set with +.Nm ifconfig : +.Bl -tag -width dest_addressxx +.It Cm alias +Establish an additional network address for this interface. +This is sometimes useful when changing network numbers, and +one wishes to accept packets addressed to the old interface. +.It Cm arp +Enable the use of the Address Resolution Protocol in mapping +between network level addresses and link level addresses (default). +This is currently implemented for mapping between +.Tn DARPA +Internet +addresses and 10Mb/s Ethernet addresses. +.It Fl arp +Disable the use of the Address Resolution Protocol. +.It Cm broadcast +(Inet only) +Specify the address to use to represent broadcasts to the +network. +The default broadcast address is the address with a host part of all 1's. +.It Cm debug +Enable driver dependent debugging code; usually, this turns on +extra console error logging. +.It Fl debug +Disable driver dependent debugging code. +.ne 1i +.It Cm delete +Remove the network address specified. +This would be used if you incorrectly specified an alias, or it +was no longer needed. +If you have incorrectly set an NS address having the side effect +of specifying the host portion, removing all NS addresses will +allow you to respecify the host portion. +.It Cm dest_address +Specify the address of the correspondent on the other end +of a point to point link. +.It Cm down +Mark an interface ``down''. When an interface is +marked ``down'', the system will not attempt to +transmit messages through that interface. +If possible, the interface will be reset to disable reception as well. +This action does not automatically disable routes using the interface. +.It Cm ipdst +This is used to specify an Internet host who is willing to receive +ip packets encapsulating NS packets bound for a remote network. +An apparent point to point link is constructed, and +the address specified will be taken as the NS address and network +of the destination. +IP encapsulation of +.Tn CLNP +packets is done differently. +.It Cm metric Ar n +Set the routing metric of the interface to +.Ar n , +default 0. +The routing metric is used by the routing protocol +.Pq Xr routed 8 . +Higher metrics have the effect of making a route +less favorable; metrics are counted as addition hops +to the destination network or host. +.It Cm netmask Ar mask +(Inet and ISO) +Specify how much of the address to reserve for subdividing +networks into sub-networks. +The mask includes the network part of the local address +and the subnet part, which is taken from the host field of the address. +The mask can be specified as a single hexadecimal number +with a leading 0x, with a dot-notation Internet address, +or with a pseudo-network name listed in the network table +.Xr networks 5 . +The mask contains 1's for the bit positions in the 32-bit address +which are to be used for the network and subnet parts, +and 0's for the host part. +The mask should contain at least the standard network portion, +and the subnet field should be contiguous with the network +portion. +.\" see +.\" Xr eon 5 . +.It Cm nsellength Ar n +.Pf ( Tn ISO +only) +This specifies a trailing number of bytes for a received +.Tn NSAP +used for local identification, the remaining leading part of which is +taken to be the +.Tn NET +(Network Entity Title). +The default value is 1, which is conformant to US +.Tn GOSIP . +When an ISO address is set in an ifconfig command, +it is really the +.Tn NSAP +which is being specified. +For example, in +.Tn US GOSIP , +20 hex digits should be +specified in the +.Tn ISO NSAP +to be assigned to the interface. +There is some evidence that a number different from 1 may be useful +for +.Tn AFI +37 type addresses. +.It Cm trailers +Request the use of a ``trailer'' link level encapsulation when +sending (default). +If a network interface supports +.Cm trailers , +the system will, when possible, encapsulate outgoing +messages in a manner which minimizes the number of +memory to memory copy operations performed by the receiver. +On networks that support the Address Resolution Protocol (see +.Xr arp 4 ; +currently, only 10 Mb/s Ethernet), +this flag indicates that the system should request that other +systems use trailers when sending to this host. +Similarly, trailer encapsulations will be sent to other +hosts that have made such requests. +Currently used by Internet protocols only. +.It Fl trailers +Disable the use of a ``trailer'' link level encapsulation. +.It Cm link[0-2] +Enable special processing of the link level of the interface. +These three options are interface specific in actual effect, however, +they are in general used to select special modes of operation. An example +of this is to enable SLIP compression. Currently, only used by SLIP. +.ne 1i +.It Fl link[0-2] +Disable special processing at the link level with the specified interface. +.It Cm up +Mark an interface ``up''. +This may be used to enable an interface after an ``ifconfig down.'' +It happens automatically when setting the first address on an interface. +If the interface was reset when previously marked down, +the hardware will be re-initialized. +.El +.Pp +.Pp +.Nm Ifconfig +displays the current configuration for a network interface +when no optional parameters are supplied. +If a protocol family is specified, +Ifconfig will report only the details specific to that protocol family. +.Pp +Only the super-user may modify the configuration of a network interface. +.Sh DIAGNOSTICS +Messages indicating the specified interface does not exit, the +requested address is unknown, or the user is not privileged and +tried to alter an interface's configuration. +.Sh SEE ALSO +.Xr netstat 1 , +.Xr netintro 4 , +.Xr rc 8 , +.Xr routed 8 , +.\" .Xr eon 5 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/sbin/init/init.8 b/sbin/init/init.8 new file mode 100644 index 000000000000..8db766dedb31 --- /dev/null +++ b/sbin/init/init.8 @@ -0,0 +1,295 @@ +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Donn Seeley at Berkeley Software Design, 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. +.\" +.\" @(#)init.8 8.6 (Berkeley) 5/26/95 +.\" +.Dd May 26, 1995 +.Dt INIT 8 +.Os BSD 4 +.Sh NAME +.Nm init +.Nd process control initialization +.Sh SYNOPSIS +.Nm init +.Sh DESCRIPTION +The +.Nm init +program +is the last stage of the boot process. +It normally runs the automatic reboot sequence as described in +.Xr reboot 8 , +and if this succeeds, begins multi-user operation. +If the reboot scripts fail, +.Nm init +commences single user operation by giving +the super-user a shell on the console. +The +.Nm init +program may be passed parameters +from the boot program to +prevent the system from going multi-user and to instead execute +a single user shell without starting the normal daemons. +The system is then quiescent for maintenance work and may +later be made to go to multi-user by exiting the +single-user shell (with ^D). +This +causes +.Nm init +to run the +.Pa /etc/rc +start up command file in fastboot mode (skipping disk checks). +.Pp +If the +.Nm console +entry in the +.Xr ttys 5 +file is marked ``insecure'', +then +.Nm init +will require that the superuser password be +entered before the system will start a single-user shell. +The password check is skipped if the +.Nm console +is marked as ``secure''. +.Pp +The kernel runs with four different levels of security. +Any superuser process can raise the security level, but only +.Nm init +can lower it. +Security levels are defined as follows: +.Bl -tag -width flag +.It Ic -1 +Permanently insecure mode \- always run system in level 0 mode. +.It Ic 0 +Insecure mode \- immutable and append-only flags may be turned off. +All devices may be read or written subject to their permissions. +.It Ic 1 +Secure mode \- immutable and append-only flags may not be changed; +disks for mounted filesystems, +.Pa /dev/mem , +and +.Pa /dev/kmem +are read-only. +The +.Xr settimeofday 2 +system call can only advance the time. +.It Ic 2 +Highly secure mode \- same as secure mode, plus disks are always +read-only whether mounted or not. +This level precludes tampering with filesystems by unmounting them, +but also inhibits running +.Xr newfs 8 +while the system is multi-user. +.El +.Pp +Normally, the system runs in level 0 mode while single user +and in level 1 mode while multiuser. +If the level 2 mode is desired while running multiuser, +it can be set in the startup script +.Pa /etc/rc +using +.Xr sysctl 8 . +If it is desired to run the system in level 0 mode while multiuser, +the administrator must build a kernel with the variable +.Nm securelevel +defined in the file +.Pa /sys/compile/MACHINE/param.c +and initialize it to -1. +.Pp +In multi-user operation, +.Nm init +maintains +processes for the terminal ports found in the file +.Xr ttys 5 . +.Nm Init +reads this file, and executes the command found in the second field. +This command is usually +.Xr getty 8 ; +.Xr getty +opens and initializes the tty line +and +executes the +.Xr login +program. +The +.Xr login +program, when a valid user logs in, +executes a shell for that user. When this shell +dies, either because the user logged out +or an abnormal termination occurred (a signal), +the +.Nm init +program wakes up, deletes the user +from the +.Xr utmp 5 +file of current users and records the logout in the +.Xr wtmp +file. +The cycle is +then restarted by +.Nm init +executing a new +.Xr getty +for the line. +.pl +1 +.Pp +Line status (on, off, secure, getty, or window information) +may be changed in the +.Xr ttys +file without a reboot by sending the signal +.Dv SIGHUP +to +.Nm init +with the command +.Dq Li "kill \-s HUP 1" . +On receipt of this signal, +.Nm init +re-reads the +.Xr ttys +file. +When a line is turned off in +.Xr ttys , +.Nm init +will send a SIGHUP signal to the controlling process +for the session associated with the line. +For any lines that were previously turned off in the +.Xr ttys +file and are now on, +.Nm init +executes a new +.Xr getty +to enable a new login. +If the getty or window field for a line is changed, +the change takes effect at the end of the current +login session (e.g., the next time +.Nm init +starts a process on the line). +If a line is commented out or deleted from +.Xr ttys , +.Nm init +will not do anything at all to that line. +However, it will complain that the relationship between lines +in the +.Xr ttys +file and records in the +.Xr utmp +file is out of sync, +so this practice is not recommended. +.Pp +.Nm Init +will terminate multi-user operations and resume single-user mode +if sent a terminate +.Pq Dv TERM +signal, for example, +.Dq Li "kill \-s TERM 1" . +If there are processes outstanding that are deadlocked (because of +hardware or software failure), +.Xr init +will not wait for them all to die (which might take forever), but +will time out after 30 seconds and print a warning message. +.Pp +.Nm Init +will cease creating new +.Xr getty Ns 's +and allow the system to slowly die away, if it is sent a terminal stop +.Pq Dv TSTP +signal, i.e. +.Dq Li "kill \-s TSTP 1" . +A later hangup will resume full +multi-user operations, or a terminate will start a single user shell. +This hook is used by +.Xr reboot 8 +and +.Xr halt 8 . +.Pp +The role of +.Nm init +is so critical that if it dies, the system will reboot itself +automatically. +If, at bootstrap time, the +.Xr init +process cannot be located, the system will panic with the message +``panic: "init died (signal %d, exit %d)''. +.Sh DIAGNOSTICS +.Bl -diag +.It "getty repeating too quickly on port %s, sleeping" +A process being started to service a line is exiting quickly +each time it is started. +This is often caused by a ringing or noisy terminal line. +.Em "Init will sleep for 10 seconds" , +.Em "then continue trying to start the process" . +.Pp +.It "some processes would not die; ps axl advised." +A process +is hung and could not be killed when the system was shutting down. +This condition is usually caused by a process +that is stuck in a device driver because of +a persistent device error condition. +.El +.Sh FILES +.Bl -tag -width /var/log/wtmp -compact +.It Pa /dev/console +System console device. +.It Pa /dev/tty* +Terminal ports found in +.Xr ttys . +.It Pa /var/run/utmp +Record of Current users on the system. +.It Pa /var/log/wtmp +Record of all logins and logouts. +.It Pa /etc/ttys +The terminal initialization information file. +.It Pa /etc/rc +System startup commands. +.El +.Sh SEE ALSO +.Xr login 1 , +.Xr kill 1 , +.Xr sh 1 , +.Xr ttys 5 , +.Xr crash 8 , +.Xr getty 8 , +.Xr rc 8 , +.Xr reboot 8 , +.Xr halt 8 , +.Xr shutdown 8 +.Sh HISTORY +A +.Nm +command appeared in +.At v6 . +.Sh BUGS +Systems without +.Xr sysctl +behave as though they have security level \-1. diff --git a/sbin/init/init.c b/sbin/init/init.c new file mode 100644 index 000000000000..316aa939283a --- /dev/null +++ b/sbin/init/init.c @@ -0,0 +1,1297 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Donn Seeley at Berkeley Software Design, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)init.c 8.2 (Berkeley) 4/28/95"; +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __STDC__ +#include +#else +#include +#endif + +#ifdef SECURE +#include +#endif + +#include "pathnames.h" + +/* + * Until the mythical util.h arrives... + */ +extern int login_tty __P((int)); +extern int logout __P((const char *)); +extern void logwtmp __P((const char *, const char *, const char *)); + +/* + * Sleep times; used to prevent thrashing. + */ +#define GETTY_SPACING 5 /* N secs minimum getty spacing */ +#define GETTY_SLEEP 30 /* sleep N secs after spacing problem */ +#define WINDOW_WAIT 3 /* wait N secs after starting window */ +#define STALL_TIMEOUT 30 /* wait N secs after warning */ +#define DEATH_WATCH 10 /* wait N secs for procs to die */ + +void handle __P((sig_t, ...)); +void delset __P((sigset_t *, ...)); + +void stall __P((char *, ...)); +void warning __P((char *, ...)); +void emergency __P((char *, ...)); +void disaster __P((int)); +void badsys __P((int)); + +/* + * We really need a recursive typedef... + * The following at least guarantees that the return type of (*state_t)() + * is sufficiently wide to hold a function pointer. + */ +typedef long (*state_func_t) __P((void)); +typedef state_func_t (*state_t) __P((void)); + +state_func_t single_user __P((void)); +state_func_t runcom __P((void)); +state_func_t read_ttys __P((void)); +state_func_t multi_user __P((void)); +state_func_t clean_ttys __P((void)); +state_func_t catatonia __P((void)); +state_func_t death __P((void)); + +enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT; + +void transition __P((state_t)); +state_t requested_transition = runcom; + +void setctty __P((char *)); + +typedef struct init_session { + int se_index; /* index of entry in ttys file */ + pid_t se_process; /* controlling process */ + time_t se_started; /* used to avoid thrashing */ + int se_flags; /* status of session */ +#define SE_SHUTDOWN 0x1 /* session won't be restarted */ + char *se_device; /* filename of port */ + char *se_getty; /* what to run on that port */ + char **se_getty_argv; /* pre-parsed argument array */ + char *se_window; /* window system (started only once) */ + char **se_window_argv; /* pre-parsed argument array */ + struct init_session *se_prev; + struct init_session *se_next; +} session_t; + +void free_session __P((session_t *)); +session_t *new_session __P((session_t *, int, struct ttyent *)); +session_t *sessions; + +char **construct_argv __P((char *)); +void start_window_system __P((session_t *)); +void collect_child __P((pid_t)); +pid_t start_getty __P((session_t *)); +void transition_handler __P((int)); +void alrm_handler __P((int)); +void setsecuritylevel __P((int)); +int getsecuritylevel __P((void)); +int setupargv __P((session_t *, struct ttyent *)); +int clang; + +void clear_session_logs __P((session_t *)); + +int start_session_db __P((void)); +void add_session __P((session_t *)); +void del_session __P((session_t *)); +session_t *find_session __P((pid_t)); +DB *session_db; + +/* + * The mother of all processes. + */ +int +main(argc, argv) + int argc; + char **argv; +{ + int c; + struct sigaction sa; + sigset_t mask; + + + /* Dispose of random users. */ + if (getuid() != 0) { + (void)fprintf(stderr, "init: %s\n", strerror(EPERM)); + exit (1); + } + + /* System V users like to reexec init. */ + if (getpid() != 1) { + (void)fprintf(stderr, "init: already running\n"); + exit (1); + } + + /* + * Note that this does NOT open a file... + * Does 'init' deserve its own facility number? + */ + openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH); + + /* + * Create an initial session. + */ + if (setsid() < 0) + warning("initial setsid() failed: %m"); + + /* + * Establish an initial user so that programs running + * single user do not freak out and die (like passwd). + */ + if (setlogin("root") < 0) + warning("setlogin() failed: %m"); + + /* + * This code assumes that we always get arguments through flags, + * never through bits set in some random machine register. + */ + while ((c = getopt(argc, argv, "sf")) != -1) + switch (c) { + case 's': + requested_transition = single_user; + break; + case 'f': + runcom_mode = FASTBOOT; + break; + default: + warning("unrecognized flag '-%c'", c); + break; + } + + if (optind != argc) + warning("ignoring excess arguments"); + + /* + * We catch or block signals rather than ignore them, + * so that they get reset on exec. + */ + handle(badsys, SIGSYS, 0); + handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV, + SIGBUS, SIGXCPU, SIGXFSZ, 0); + handle(transition_handler, SIGHUP, SIGTERM, SIGTSTP, 0); + handle(alrm_handler, SIGALRM, 0); + sigfillset(&mask); + delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS, + SIGXCPU, SIGXFSZ, SIGHUP, SIGTERM, SIGTSTP, SIGALRM, 0); + sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = SIG_IGN; + (void) sigaction(SIGTTIN, &sa, (struct sigaction *)0); + (void) sigaction(SIGTTOU, &sa, (struct sigaction *)0); + + /* + * Paranoia. + */ + close(0); + close(1); + close(2); + + /* + * Start the state machine. + */ + transition(requested_transition); + + /* + * Should never reach here. + */ + return 1; +} + +/* + * Associate a function with a signal handler. + */ +void +#ifdef __STDC__ +handle(sig_t handler, ...) +#else +handle(va_alist) + va_dcl +#endif +{ + int sig; + struct sigaction sa; + int mask_everything; + va_list ap; +#ifndef __STDC__ + sig_t handler; + + va_start(ap); + handler = va_arg(ap, sig_t); +#else + va_start(ap, handler); +#endif + + sa.sa_handler = handler; + sigfillset(&mask_everything); + + while (sig = va_arg(ap, int)) { + sa.sa_mask = mask_everything; + /* XXX SA_RESTART? */ + sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0; + sigaction(sig, &sa, (struct sigaction *) 0); + } + va_end(ap); +} + +/* + * Delete a set of signals from a mask. + */ +void +#ifdef __STDC__ +delset(sigset_t *maskp, ...) +#else +delset(va_alist) + va_dcl +#endif +{ + int sig; + va_list ap; +#ifndef __STDC__ + sigset_t *maskp; + + va_start(ap); + maskp = va_arg(ap, sigset_t *); +#else + va_start(ap, maskp); +#endif + + while (sig = va_arg(ap, int)) + sigdelset(maskp, sig); + va_end(ap); +} + +/* + * Log a message and sleep for a while (to give someone an opportunity + * to read it and to save log or hardcopy output if the problem is chronic). + * NB: should send a message to the session logger to avoid blocking. + */ +void +#ifdef __STDC__ +stall(char *message, ...) +#else +stall(va_alist) + va_dcl +#endif +{ + va_list ap; +#ifndef __STDC__ + char *message; + + va_start(ap); + message = va_arg(ap, char *); +#else + va_start(ap, message); +#endif + + vsyslog(LOG_ALERT, message, ap); + va_end(ap); + sleep(STALL_TIMEOUT); +} + +/* + * Like stall(), but doesn't sleep. + * If cpp had variadic macros, the two functions could be #defines for another. + * NB: should send a message to the session logger to avoid blocking. + */ +void +#ifdef __STDC__ +warning(char *message, ...) +#else +warning(va_alist) + va_dcl +#endif +{ + va_list ap; +#ifndef __STDC__ + char *message; + + va_start(ap); + message = va_arg(ap, char *); +#else + va_start(ap, message); +#endif + + vsyslog(LOG_ALERT, message, ap); + va_end(ap); +} + +/* + * Log an emergency message. + * NB: should send a message to the session logger to avoid blocking. + */ +void +#ifdef __STDC__ +emergency(char *message, ...) +#else +emergency(va_alist) + va_dcl +#endif +{ + va_list ap; +#ifndef __STDC__ + char *message; + + va_start(ap); + message = va_arg(ap, char *); +#else + va_start(ap, message); +#endif + + vsyslog(LOG_EMERG, message, ap); + va_end(ap); +} + +/* + * Catch a SIGSYS signal. + * + * These may arise if a system does not support sysctl. + * We tolerate up to 25 of these, then throw in the towel. + */ +void +badsys(sig) + int sig; +{ + static int badcount = 0; + + if (badcount++ < 25) + return; + disaster(sig); +} + +/* + * Catch an unexpected signal. + */ +void +disaster(sig) + int sig; +{ + emergency("fatal signal: %s", + sig < (unsigned) NSIG ? sys_siglist[sig] : "unknown signal"); + + sleep(STALL_TIMEOUT); + _exit(sig); /* reboot */ +} + +/* + * Get the security level of the kernel. + */ +int +getsecuritylevel() +{ +#ifdef KERN_SECURELVL + int name[2], curlevel; + size_t len; + extern int errno; + + name[0] = CTL_KERN; + name[1] = KERN_SECURELVL; + len = sizeof curlevel; + if (sysctl(name, 2, &curlevel, &len, NULL, 0) == -1) { + emergency("cannot get kernel security level: %s", + strerror(errno)); + return (-1); + } + return (curlevel); +#else + return (-1); +#endif +} + +/* + * Set the security level of the kernel. + */ +void +setsecuritylevel(newlevel) + int newlevel; +{ +#ifdef KERN_SECURELVL + int name[2], curlevel; + extern int errno; + + curlevel = getsecuritylevel(); + if (newlevel == curlevel) + return; + name[0] = CTL_KERN; + name[1] = KERN_SECURELVL; + if (sysctl(name, 2, NULL, NULL, &newlevel, sizeof newlevel) == -1) { + emergency( + "cannot change kernel security level from %d to %d: %s", + curlevel, newlevel, strerror(errno)); + return; + } +#ifdef SECURE + warning("kernel security level changed from %d to %d", + curlevel, newlevel); +#endif +#endif +} + +/* + * Change states in the finite state machine. + * The initial state is passed as an argument. + */ +void +transition(s) + state_t s; +{ + for (;;) + s = (state_t) (*s)(); +} + +/* + * Close out the accounting files for a login session. + * NB: should send a message to the session logger to avoid blocking. + */ +void +clear_session_logs(sp) + session_t *sp; +{ + char *line = sp->se_device + sizeof(_PATH_DEV) - 1; + + if (logout(line)) + logwtmp(line, "", ""); +} + +/* + * Start a session and allocate a controlling terminal. + * Only called by children of init after forking. + */ +void +setctty(name) + char *name; +{ + int fd; + + (void) revoke(name); + sleep (2); /* leave DTR low */ + if ((fd = open(name, O_RDWR)) == -1) { + stall("can't open %s: %m", name); + _exit(1); + } + if (login_tty(fd) == -1) { + stall("can't get %s for controlling terminal: %m", name); + _exit(1); + } +} + +/* + * Bring the system up single user. + */ +state_func_t +single_user() +{ + pid_t pid, wpid; + int status; + sigset_t mask; + char *shell = _PATH_BSHELL; + char *argv[2]; +#ifdef SECURE + struct ttyent *typ; + struct passwd *pp; + static const char banner[] = + "Enter root password, or ^D to go multi-user\n"; + char *clear, *password; +#endif + + /* + * If the kernel is in secure mode, downgrade it to insecure mode. + */ + if (getsecuritylevel() > 0) + setsecuritylevel(0); + + if ((pid = fork()) == 0) { + /* + * Start the single user session. + */ + setctty(_PATH_CONSOLE); + +#ifdef SECURE + /* + * Check the root password. + * We don't care if the console is 'on' by default; + * it's the only tty that can be 'off' and 'secure'. + */ + typ = getttynam("console"); + pp = getpwnam("root"); + if (typ && (typ->ty_status & TTY_SECURE) == 0 && pp) { + write(2, banner, sizeof banner - 1); + for (;;) { + clear = getpass("Password:"); + if (clear == 0 || *clear == '\0') + _exit(0); + password = crypt(clear, pp->pw_passwd); + memset(clear, 0, _PASSWORD_LEN); + if (strcmp(password, pp->pw_passwd) == 0) + break; + warning("single-user login failed\n"); + } + } + endttyent(); + endpwent(); +#endif /* SECURE */ + +#ifdef DEBUGSHELL + { + char altshell[128], *cp = altshell; + int num; + +#define SHREQUEST \ + "Enter pathname of shell or RETURN for sh: " + (void)write(STDERR_FILENO, + SHREQUEST, sizeof(SHREQUEST) - 1); + while ((num = read(STDIN_FILENO, cp, 1)) != -1 && + num != 0 && *cp != '\n' && cp < &altshell[127]) + cp++; + *cp = '\0'; + if (altshell[0] != '\0') + shell = altshell; + } +#endif /* DEBUGSHELL */ + + /* + * Unblock signals. + * We catch all the interesting ones, + * and those are reset to SIG_DFL on exec. + */ + sigemptyset(&mask); + sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); + + /* + * Fire off a shell. + * If the default one doesn't work, try the Bourne shell. + */ + argv[0] = "-sh"; + argv[1] = 0; + execv(shell, argv); + emergency("can't exec %s for single user: %m", shell); + execv(_PATH_BSHELL, argv); + emergency("can't exec %s for single user: %m", _PATH_BSHELL); + sleep(STALL_TIMEOUT); + _exit(1); + } + + if (pid == -1) { + /* + * We are seriously hosed. Do our best. + */ + emergency("can't fork single-user shell, trying again"); + while (waitpid(-1, (int *) 0, WNOHANG) > 0) + continue; + return (state_func_t) single_user; + } + + requested_transition = 0; + do { + if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) + collect_child(wpid); + if (wpid == -1) { + if (errno == EINTR) + continue; + warning("wait for single-user shell failed: %m; restarting"); + return (state_func_t) single_user; + } + if (wpid == pid && WIFSTOPPED(status)) { + warning("init: shell stopped, restarting\n"); + kill(pid, SIGCONT); + wpid = -1; + } + } while (wpid != pid && !requested_transition); + + if (requested_transition) + return (state_func_t) requested_transition; + + if (!WIFEXITED(status)) { + if (WTERMSIG(status) == SIGKILL) { + /* + * reboot(8) killed shell? + */ + warning("single user shell terminated."); + sleep(STALL_TIMEOUT); + _exit(0); + } else { + warning("single user shell terminated, restarting"); + return (state_func_t) single_user; + } + } + + runcom_mode = FASTBOOT; + return (state_func_t) runcom; +} + +/* + * Run the system startup script. + */ +state_func_t +runcom() +{ + pid_t pid, wpid; + int status; + char *argv[4]; + struct sigaction sa; + + if ((pid = fork()) == 0) { + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = SIG_IGN; + (void) sigaction(SIGTSTP, &sa, (struct sigaction *)0); + (void) sigaction(SIGHUP, &sa, (struct sigaction *)0); + + setctty(_PATH_CONSOLE); + + argv[0] = "sh"; + argv[1] = _PATH_RUNCOM; + argv[2] = runcom_mode == AUTOBOOT ? "autoboot" : 0; + argv[3] = 0; + + sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0); + + execv(_PATH_BSHELL, argv); + stall("can't exec %s for %s: %m", _PATH_BSHELL, _PATH_RUNCOM); + _exit(1); /* force single user mode */ + } + + if (pid == -1) { + emergency("can't fork for %s on %s: %m", + _PATH_BSHELL, _PATH_RUNCOM); + while (waitpid(-1, (int *) 0, WNOHANG) > 0) + continue; + sleep(STALL_TIMEOUT); + return (state_func_t) single_user; + } + + /* + * Copied from single_user(). This is a bit paranoid. + */ + do { + if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) + collect_child(wpid); + if (wpid == -1) { + if (errno == EINTR) + continue; + warning("wait for %s on %s failed: %m; going to single user mode", + _PATH_BSHELL, _PATH_RUNCOM); + return (state_func_t) single_user; + } + if (wpid == pid && WIFSTOPPED(status)) { + warning("init: %s on %s stopped, restarting\n", + _PATH_BSHELL, _PATH_RUNCOM); + kill(pid, SIGCONT); + wpid = -1; + } + } while (wpid != pid); + + if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM && + requested_transition == catatonia) { + /* /etc/rc executed /sbin/reboot; wait for the end quietly */ + sigset_t s; + + sigfillset(&s); + for (;;) + sigsuspend(&s); + } + + if (!WIFEXITED(status)) { + warning("%s on %s terminated abnormally, going to single user mode", + _PATH_BSHELL, _PATH_RUNCOM); + return (state_func_t) single_user; + } + + if (WEXITSTATUS(status)) + return (state_func_t) single_user; + + runcom_mode = AUTOBOOT; /* the default */ + /* NB: should send a message to the session logger to avoid blocking. */ + logwtmp("~", "reboot", ""); + return (state_func_t) read_ttys; +} + +/* + * Open the session database. + * + * NB: We could pass in the size here; is it necessary? + */ +int +start_session_db() +{ + if (session_db && (*session_db->close)(session_db)) + emergency("session database close: %s", strerror(errno)); + if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == 0) { + emergency("session database open: %s", strerror(errno)); + return (1); + } + return (0); + +} + +/* + * Add a new login session. + */ +void +add_session(sp) + session_t *sp; +{ + DBT key; + DBT data; + + key.data = &sp->se_process; + key.size = sizeof sp->se_process; + data.data = &sp; + data.size = sizeof sp; + + if ((*session_db->put)(session_db, &key, &data, 0)) + emergency("insert %d: %s", sp->se_process, strerror(errno)); +} + +/* + * Delete an old login session. + */ +void +del_session(sp) + session_t *sp; +{ + DBT key; + + key.data = &sp->se_process; + key.size = sizeof sp->se_process; + + if ((*session_db->del)(session_db, &key, 0)) + emergency("delete %d: %s", sp->se_process, strerror(errno)); +} + +/* + * Look up a login session by pid. + */ +session_t * +#ifdef __STDC__ +find_session(pid_t pid) +#else +find_session(pid) + pid_t pid; +#endif +{ + DBT key; + DBT data; + session_t *ret; + + key.data = &pid; + key.size = sizeof pid; + if ((*session_db->get)(session_db, &key, &data, 0) != 0) + return 0; + memmove(&ret, data.data, sizeof(ret)); + return ret; +} + +/* + * Construct an argument vector from a command line. + */ +char ** +construct_argv(command) + char *command; +{ + register int argc = 0; + register char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1) + * sizeof (char *)); + static const char separators[] = " \t"; + + if ((argv[argc++] = strtok(command, separators)) == 0) + return 0; + while (argv[argc++] = strtok((char *) 0, separators)) + continue; + return argv; +} + +/* + * Deallocate a session descriptor. + */ +void +free_session(sp) + register session_t *sp; +{ + free(sp->se_device); + if (sp->se_getty) { + free(sp->se_getty); + free(sp->se_getty_argv); + } + if (sp->se_window) { + free(sp->se_window); + free(sp->se_window_argv); + } + free(sp); +} + +/* + * Allocate a new session descriptor. + */ +session_t * +new_session(sprev, session_index, typ) + session_t *sprev; + int session_index; + register struct ttyent *typ; +{ + register session_t *sp; + + if ((typ->ty_status & TTY_ON) == 0 || + typ->ty_name == 0 || + typ->ty_getty == 0) + return 0; + + sp = (session_t *) malloc(sizeof (session_t)); + memset(sp, 0, sizeof *sp); + + sp->se_index = session_index; + + sp->se_device = malloc(sizeof(_PATH_DEV) + strlen(typ->ty_name)); + (void) sprintf(sp->se_device, "%s%s", _PATH_DEV, typ->ty_name); + + if (setupargv(sp, typ) == 0) { + free_session(sp); + return (0); + } + + sp->se_next = 0; + if (sprev == 0) { + sessions = sp; + sp->se_prev = 0; + } else { + sprev->se_next = sp; + sp->se_prev = sprev; + } + + return sp; +} + +/* + * Calculate getty and if useful window argv vectors. + */ +int +setupargv(sp, typ) + session_t *sp; + struct ttyent *typ; +{ + + if (sp->se_getty) { + free(sp->se_getty); + free(sp->se_getty_argv); + } + sp->se_getty = malloc(strlen(typ->ty_getty) + strlen(typ->ty_name) + 2); + (void) sprintf(sp->se_getty, "%s %s", typ->ty_getty, typ->ty_name); + sp->se_getty_argv = construct_argv(sp->se_getty); + if (sp->se_getty_argv == 0) { + warning("can't parse getty for port %s", sp->se_device); + free(sp->se_getty); + sp->se_getty = 0; + return (0); + } + if (typ->ty_window) { + if (sp->se_window) + free(sp->se_window); + sp->se_window = strdup(typ->ty_window); + sp->se_window_argv = construct_argv(sp->se_window); + if (sp->se_window_argv == 0) { + warning("can't parse window for port %s", + sp->se_device); + free(sp->se_window); + sp->se_window = 0; + return (0); + } + } + return (1); +} + +/* + * Walk the list of ttys and create sessions for each active line. + */ +state_func_t +read_ttys() +{ + int session_index = 0; + register session_t *sp, *snext; + register struct ttyent *typ; + + /* + * Destroy any previous session state. + * There shouldn't be any, but just in case... + */ + for (sp = sessions; sp; sp = snext) { + if (sp->se_process) + clear_session_logs(sp); + snext = sp->se_next; + free_session(sp); + } + sessions = 0; + if (start_session_db()) + return (state_func_t) single_user; + + /* + * Allocate a session entry for each active port. + * Note that sp starts at 0. + */ + while (typ = getttyent()) + if (snext = new_session(sp, ++session_index, typ)) + sp = snext; + + endttyent(); + + return (state_func_t) multi_user; +} + +/* + * Start a window system running. + */ +void +start_window_system(sp) + session_t *sp; +{ + pid_t pid; + sigset_t mask; + + if ((pid = fork()) == -1) { + emergency("can't fork for window system on port %s: %m", + sp->se_device); + /* hope that getty fails and we can try again */ + return; + } + + if (pid) + return; + + sigemptyset(&mask); + sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); + + if (setsid() < 0) + emergency("setsid failed (window) %m"); + + execv(sp->se_window_argv[0], sp->se_window_argv); + stall("can't exec window system '%s' for port %s: %m", + sp->se_window_argv[0], sp->se_device); + _exit(1); +} + +/* + * Start a login session running. + */ +pid_t +start_getty(sp) + session_t *sp; +{ + pid_t pid; + sigset_t mask; + time_t current_time = time((time_t *) 0); + + /* + * fork(), not vfork() -- we can't afford to block. + */ + if ((pid = fork()) == -1) { + emergency("can't fork for getty on port %s: %m", sp->se_device); + return -1; + } + + if (pid) + return pid; + + if (current_time > sp->se_started && + current_time - sp->se_started < GETTY_SPACING) { + warning("getty repeating too quickly on port %s, sleeping", + sp->se_device); + sleep((unsigned) GETTY_SLEEP); + } + + if (sp->se_window) { + start_window_system(sp); + sleep(WINDOW_WAIT); + } + + sigemptyset(&mask); + sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); + + execv(sp->se_getty_argv[0], sp->se_getty_argv); + stall("can't exec getty '%s' for port %s: %m", + sp->se_getty_argv[0], sp->se_device); + _exit(1); +} + +/* + * Collect exit status for a child. + * If an exiting login, start a new login running. + */ +void +#ifdef __STDC__ +collect_child(pid_t pid) +#else +collect_child(pid) + pid_t pid; +#endif +{ + register session_t *sp, *sprev, *snext; + + if (! sessions) + return; + + if (! (sp = find_session(pid))) + return; + + clear_session_logs(sp); + del_session(sp); + sp->se_process = 0; + + if (sp->se_flags & SE_SHUTDOWN) { + if (sprev = sp->se_prev) + sprev->se_next = sp->se_next; + else + sessions = sp->se_next; + if (snext = sp->se_next) + snext->se_prev = sp->se_prev; + free_session(sp); + return; + } + + if ((pid = start_getty(sp)) == -1) { + /* serious trouble */ + requested_transition = clean_ttys; + return; + } + + sp->se_process = pid; + sp->se_started = time((time_t *) 0); + add_session(sp); +} + +/* + * Catch a signal and request a state transition. + */ +void +transition_handler(sig) + int sig; +{ + + switch (sig) { + case SIGHUP: + requested_transition = clean_ttys; + break; + case SIGTERM: + requested_transition = death; + break; + case SIGTSTP: + requested_transition = catatonia; + break; + default: + requested_transition = 0; + break; + } +} + +/* + * Take the system multiuser. + */ +state_func_t +multi_user() +{ + pid_t pid; + register session_t *sp; + + requested_transition = 0; + + /* + * If the administrator has not set the security level to -1 + * to indicate that the kernel should not run multiuser in secure + * mode, and the run script has not set a higher level of security + * than level 1, then put the kernel into secure mode. + */ + if (getsecuritylevel() == 0) + setsecuritylevel(1); + + for (sp = sessions; sp; sp = sp->se_next) { + if (sp->se_process) + continue; + if ((pid = start_getty(sp)) == -1) { + /* serious trouble */ + requested_transition = clean_ttys; + break; + } + sp->se_process = pid; + sp->se_started = time((time_t *) 0); + add_session(sp); + } + + while (!requested_transition) + if ((pid = waitpid(-1, (int *) 0, 0)) != -1) + collect_child(pid); + + return (state_func_t) requested_transition; +} + +/* + * This is an n-squared algorithm. We hope it isn't run often... + */ +state_func_t +clean_ttys() +{ + register session_t *sp, *sprev; + register struct ttyent *typ; + register int session_index = 0; + register int devlen; + + if (! sessions) + return (state_func_t) multi_user; + + devlen = sizeof(_PATH_DEV) - 1; + while (typ = getttyent()) { + ++session_index; + + for (sprev = 0, sp = sessions; sp; sprev = sp, sp = sp->se_next) + if (strcmp(typ->ty_name, sp->se_device + devlen) == 0) + break; + + if (sp) { + if (sp->se_index != session_index) { + warning("port %s changed utmp index from %d to %d", + sp->se_device, sp->se_index, + session_index); + sp->se_index = session_index; + } + if ((typ->ty_status & TTY_ON) == 0 || + typ->ty_getty == 0) { + sp->se_flags |= SE_SHUTDOWN; + kill(sp->se_process, SIGHUP); + continue; + } + sp->se_flags &= ~SE_SHUTDOWN; + if (setupargv(sp, typ) == 0) { + warning("can't parse getty for port %s", + sp->se_device); + sp->se_flags |= SE_SHUTDOWN; + kill(sp->se_process, SIGHUP); + } + continue; + } + + new_session(sprev, session_index, typ); + } + + endttyent(); + + return (state_func_t) multi_user; +} + +/* + * Block further logins. + */ +state_func_t +catatonia() +{ + register session_t *sp; + + for (sp = sessions; sp; sp = sp->se_next) + sp->se_flags |= SE_SHUTDOWN; + + return (state_func_t) multi_user; +} + +/* + * Note SIGALRM. + */ +void +alrm_handler(sig) + int sig; +{ + clang = 1; +} + +/* + * Bring the system down to single user. + */ +state_func_t +death() +{ + register session_t *sp; + register int i; + pid_t pid; + static const int death_sigs[3] = { SIGHUP, SIGTERM, SIGKILL }; + + for (sp = sessions; sp; sp = sp->se_next) + sp->se_flags |= SE_SHUTDOWN; + + /* NB: should send a message to the session logger to avoid blocking. */ + logwtmp("~", "shutdown", ""); + + for (i = 0; i < 3; ++i) { + if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH) + return (state_func_t) single_user; + + clang = 0; + alarm(DEATH_WATCH); + do + if ((pid = waitpid(-1, (int *)0, 0)) != -1) + collect_child(pid); + while (clang == 0 && errno != ECHILD); + + if (errno == ECHILD) + return (state_func_t) single_user; + } + + warning("some processes would not die; ps axl advised"); + + return (state_func_t) single_user; +} diff --git a/sbin/ping/ping.8 b/sbin/ping/ping.8 new file mode 100644 index 000000000000..2382365b266f --- /dev/null +++ b/sbin/ping/ping.8 @@ -0,0 +1,328 @@ +.\" Copyright (c) 1985, 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. +.\" +.\" @(#)ping.8 8.3 (Berkeley) 4/28/95 +.\" +.Dd April 28, 1995 +.Dt PING 8 +.Os BSD 4.3 +.Sh NAME +.Nm ping +.Nd send +.Tn ICMP ECHO_REQUEST +packets to network hosts +.Sh SYNOPSIS +.Nm ping +.Op Fl Rdfnqrv +.Op Fl c Ar count +.Op Fl i Ar wait +.Op Fl l Ar preload +.Op Fl p Ar pattern +.Op Fl s Ar packetsize +.Ar host +.Sh DESCRIPTION +.Nm Ping +uses the +.Tn ICMP +protocol's mandatory +.Tn ECHO_REQUEST +datagram to elicit an +.Tn ICMP ECHO_RESPONSE +from a host or gateway. +.Tn ECHO_REQUEST +datagrams (``pings'') have an IP and +.Tn ICMP +header, +followed by a +.Dq struct timeval +and then an arbitrary number of ``pad'' bytes used to fill out the +packet. +The options are as follows: +.Bl -tag -width Ds +.It Fl c Ar count +Stop after sending (and receiving) +.Ar count +.Tn ECHO_RESPONSE +packets. +.It Fl d +Set the +.Dv SO_DEBUG +option on the socket being used. +.It Fl f +Flood ping. +Outputs packets as fast as they come back or one hundred times per second, +whichever is more. +For every +.Tn ECHO_REQUEST +sent a period ``.'' is printed, while for every +.Tn ECHO_REPLY +received a backspace is printed. +This provides a rapid display of how many packets are being dropped. +Only the super-user may use this option. +.Bf -emphasis +This can be very hard on a network and should be used with caution. +.Ef +.It Fl i Ar wait +Wait +.Ar wait +seconds +.Em between sending each packet . +The default is to wait for one second between each packet. +This option is incompatible with the +.Fl f +option. +.It Fl l Ar preload +If +.Ar preload +is specified, +.Nm ping +sends that many packets as fast as possible before falling into its normal +mode of behavior. +.It Fl n +Numeric output only. +No attempt will be made to lookup symbolic names for host addresses. +.It Fl p Ar pattern +You may specify up to 16 ``pad'' bytes to fill out the packet you send. +This is useful for diagnosing data-dependent problems in a network. +For example, +.Dq Li \-p ff +will cause the sent packet to be filled with all +ones. +.It Fl q +Quiet output. +Nothing is displayed except the summary lines at startup time and +when finished. +.It Fl R +Record route. +Includes the +.Tn RECORD_ROUTE +option in the +.Tn ECHO_REQUEST +packet and displays +the route buffer on returned packets. +Note that the IP header is only large enough for nine such routes. +Many hosts ignore or discard this option. +.It Fl r +Bypass the normal routing tables and send directly to a host on an attached +network. +If the host is not on a directly-attached network, an error is returned. +This option can be used to ping a local host through an interface +that has no route through it (e.g., after the interface was dropped by +.Xr routed 8 ) . +.It Fl s Ar packetsize +Specifies the number of data bytes to be sent. +The default is 56, which translates into 64 +.Tn ICMP +data bytes when combined +with the 8 bytes of +.Tn ICMP +header data. +.It Fl v +Verbose output. +.Tn ICMP +packets other than +.Tn ECHO_RESPONSE +that are received are listed. +.El +.Pp +When using +.Nm ping +for fault isolation, it should first be run on the local host, to verify +that the local network interface is up and running. +Then, hosts and gateways further and further away should be ``pinged''. +Round-trip times and packet loss statistics are computed. +If duplicate packets are received, they are not included in the packet +loss calculation, although the round trip time of these packets is used +in calculating the minimum/average/maximum round-trip time numbers. +When the specified number of packets have been sent (and received) or +if the program is terminated with a +.Dv SIGINT , +a brief summary is displayed. +.Pp +This program is intended for use in network testing, measurement and +management. +Because of the load it can impose on the network, it is unwise to use +.Nm ping +during normal operations or from automated scripts. +.Sh ICMP PACKET DETAILS +An IP header without options is 20 bytes. +An +.Tn ICMP +.Tn ECHO_REQUEST +packet contains an additional 8 bytes worth +of +.Tn ICMP +header followed by an arbitrary amount of data. +When a +.Ar packetsize +is given, this indicated the size of this extra piece of data (the +default is 56). +Thus the amount of data received inside of an IP packet of type +.Tn ICMP +.Tn ECHO_REPLY +will always be 8 bytes more than the requested data space +(the +.Tn ICMP +header). +.Pp +If the data space is at least eight bytes large, +.Nm ping +uses the first eight bytes of this space to include a timestamp which +it uses in the computation of round trip times. +If less than eight bytes of pad are specified, no round trip times are +given. +.Sh DUPLICATE AND DAMAGED PACKETS +.Nm Ping +will report duplicate and damaged packets. +Duplicate packets should never occur, and seem to be caused by +inappropriate link-level retransmissions. +Duplicates may occur in many situations and are rarely (if ever) a +good sign, although the presence of low levels of duplicates may not +always be cause for alarm. +.Pp +Damaged packets are obviously serious cause for alarm and often +indicate broken hardware somewhere in the +.Nm ping +packet's path (in the network or in the hosts). +.Sh TRYING DIFFERENT DATA PATTERNS +The (inter)network layer should never treat packets differently depending +on the data contained in the data portion. +Unfortunately, data-dependent problems have been known to sneak into +networks and remain undetected for long periods of time. +In many cases the particular pattern that will have problems is something +that doesn't have sufficient ``transitions'', such as all ones or all +zeros, or a pattern right at the edge, such as almost all zeros. +It isn't necessarily enough to specify a data pattern of all zeros (for +example) on the command line because the pattern that is of interest is +at the data link level, and the relationship between what you type and +what the controllers transmit can be complicated. +.Pp +This means that if you have a data-dependent problem you will probably +have to do a lot of testing to find it. +If you are lucky, you may manage to find a file that either can't be sent +across your network or that takes much longer to transfer than other +similar length files. +You can then examine this file for repeated patterns that you can test +using the +.Fl p +option of +.Nm ping . +.Sh TTL DETAILS +The +.Tn TTL +value of an IP packet represents the maximum number of IP routers +that the packet can go through before being thrown away. +In current practice you can expect each router in the Internet to decrement +the +.Tn TTL +field by exactly one. +.Pp +The +.Tn TCP/IP +specification states that the +.Tn TTL +field for +.Tn TCP +packets should +be set to 60, but many systems use smaller values (4.3 +.Tn BSD +uses 30, 4.2 used +15). +.Pp +The maximum possible value of this field is 255, and most Unix systems set +the +.Tn TTL +field of +.Tn ICMP ECHO_REQUEST +packets to 255. +This is why you will find you can ``ping'' some hosts, but not reach them +with +.Xr telnet 1 +or +.Xr ftp 1 . +.Pp +In normal operation ping prints the ttl value from the packet it receives. +When a remote system receives a ping packet, it can do one of three things +with the +.Tn TTL +field in its response: +.Bl -bullet +.It +Not change it; this is what Berkeley Unix systems did before the +.Bx 4.3 tahoe +release. +In this case the +.Tn TTL +value in the received packet will be 255 minus the +number of routers in the round-trip path. +.It +Set it to 255; this is what current Berkeley Unix systems do. +In this case the +.Tn TTL +value in the received packet will be 255 minus the +number of routers in the path +.Xr from +the remote system +.Em to +the +.Nm ping Ns Em ing +host. +.It +Set it to some other value. +Some machines use the same value for +.Tn ICMP +packets that they use for +.Tn TCP +packets, for example either 30 or 60. +Others may use completely wild values. +.El +.Sh BUGS +Many Hosts and Gateways ignore the +.Tn RECORD_ROUTE +option. +.Pp +The maximum IP header length is too small for options like +.Tn RECORD_ROUTE +to +be completely useful. +There's not much that that can be done about this, however. +.Pp +Flood pinging is not recommended in general, and flood pinging the +broadcast address should only be done under very controlled conditions. +.Sh SEE ALSO +.Xr netstat 1 , +.Xr ifconfig 8 , +.Xr routed 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.3 . diff --git a/sbin/ping/ping.c b/sbin/ping/ping.c new file mode 100644 index 000000000000..7ccec1e02e9a --- /dev/null +++ b/sbin/ping/ping.c @@ -0,0 +1,986 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Muuss. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)ping.c 8.3 (Berkeley) 4/28/95"; +#endif /* not lint */ + +/* + * P I N G . C + * + * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility, + * measure round-trip-delays and packet loss across network paths. + * + * Author - + * Mike Muuss + * U. S. Army Ballistic Research Laboratory + * December, 1983 + * + * Status - + * Public Domain. Distribution Unlimited. + * Bugs - + * More statistics could always be gathered. + * This program has to run SUID to ROOT to access the ICMP socket. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEFDATALEN (64 - 8) /* default data length */ +#define MAXIPLEN 60 +#define MAXICMPLEN 76 +#define MAXPACKET (65536 - 60 - 8)/* max packet size */ +#define MAXWAIT 10 /* max seconds to wait for response */ +#define NROUTES 9 /* number of record route slots */ + +#define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */ +#define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */ +#define SET(bit) (A(bit) |= B(bit)) +#define CLR(bit) (A(bit) &= (~B(bit))) +#define TST(bit) (A(bit) & B(bit)) + +/* various options */ +int options; +#define F_FLOOD 0x001 +#define F_INTERVAL 0x002 +#define F_NUMERIC 0x004 +#define F_PINGFILLED 0x008 +#define F_QUIET 0x010 +#define F_RROUTE 0x020 +#define F_SO_DEBUG 0x040 +#define F_SO_DONTROUTE 0x080 +#define F_VERBOSE 0x100 + +/* + * MAX_DUP_CHK is the number of bits in received table, i.e. the maximum + * number of received sequence numbers we can keep track of. Change 128 + * to 8192 for complete accuracy... + */ +#define MAX_DUP_CHK (8 * 128) +int mx_dup_ck = MAX_DUP_CHK; +char rcvd_tbl[MAX_DUP_CHK / 8]; + +struct sockaddr whereto; /* who to ping */ +int datalen = DEFDATALEN; +int s; /* socket file descriptor */ +u_char outpack[MAXPACKET]; +char BSPACE = '\b'; /* characters written for flood */ +char DOT = '.'; +char *hostname; +int ident; /* process id to identify our packets */ + +/* counters */ +long npackets; /* max packets to transmit */ +long nreceived; /* # of packets we got back */ +long nrepeats; /* number of duplicates */ +long ntransmitted; /* sequence # for outbound packets = #sent */ +int interval = 1; /* interval between packets */ + +/* timing */ +int timing; /* flag to do timing */ +double tmin = 999999999.0; /* minimum round trip time */ +double tmax = 0.0; /* maximum round trip time */ +double tsum = 0.0; /* sum of all times, for doing average */ + +char *pr_addr(); +void catcher(), finish(); + +main(argc, argv) + int argc; + char **argv; +{ + extern int errno, optind; + extern char *optarg; + struct timeval timeout; + struct hostent *hp; + struct sockaddr_in *to; + struct protoent *proto; + register int i; + int ch, fdmask, hold, packlen, preload; + u_char *datap, *packet; + char *target, hnamebuf[MAXHOSTNAMELEN], *malloc(); +#ifdef IP_OPTIONS + char rspace[3 + 4 * NROUTES + 1]; /* record route space */ +#endif + + preload = 0; + datap = &outpack[8 + sizeof(struct timeval)]; + while ((ch = getopt(argc, argv, "Rc:dfh:i:l:np:qrs:v")) != EOF) + switch(ch) { + case 'c': + npackets = atoi(optarg); + if (npackets <= 0) { + (void)fprintf(stderr, + "ping: bad number of packets to transmit.\n"); + exit(1); + } + break; + case 'd': + options |= F_SO_DEBUG; + break; + case 'f': + if (getuid()) { + (void)fprintf(stderr, + "ping: %s\n", strerror(EPERM)); + exit(1); + } + options |= F_FLOOD; + setbuf(stdout, (char *)NULL); + break; + case 'i': /* wait between sending packets */ + interval = atoi(optarg); + if (interval <= 0) { + (void)fprintf(stderr, + "ping: bad timing interval.\n"); + exit(1); + } + options |= F_INTERVAL; + break; + case 'l': + preload = atoi(optarg); + if (preload < 0) { + (void)fprintf(stderr, + "ping: bad preload value.\n"); + exit(1); + } + break; + case 'n': + options |= F_NUMERIC; + break; + case 'p': /* fill buffer with user pattern */ + options |= F_PINGFILLED; + fill((char *)datap, optarg); + break; + case 'q': + options |= F_QUIET; + break; + case 'R': + options |= F_RROUTE; + break; + case 'r': + options |= F_SO_DONTROUTE; + break; + case 's': /* size of packet to send */ + datalen = atoi(optarg); + if (datalen > MAXPACKET) { + (void)fprintf(stderr, + "ping: packet size too large.\n"); + exit(1); + } + if (datalen <= 0) { + (void)fprintf(stderr, + "ping: illegal packet size.\n"); + exit(1); + } + break; + case 'v': + options |= F_VERBOSE; + break; + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 1) + usage(); + target = *argv; + + memset(&whereto, 0, sizeof(struct sockaddr)); + to = (struct sockaddr_in *)&whereto; + to->sin_family = AF_INET; + to->sin_addr.s_addr = inet_addr(target); + if (to->sin_addr.s_addr != (u_int)-1) + hostname = target; + else { + hp = gethostbyname(target); + if (!hp) { + (void)fprintf(stderr, + "ping: unknown host %s\n", target); + exit(1); + } + to->sin_family = hp->h_addrtype; + memmove(&to->sin_addr, hp->h_addr, hp->h_length); + (void)strncpy(hnamebuf, hp->h_name, sizeof(hnamebuf) - 1); + hostname = hnamebuf; + } + + if (options & F_FLOOD && options & F_INTERVAL) { + (void)fprintf(stderr, + "ping: -f and -i incompatible options.\n"); + exit(1); + } + + if (datalen >= sizeof(struct timeval)) /* can we time transfer */ + timing = 1; + packlen = datalen + MAXIPLEN + MAXICMPLEN; + if (!(packet = (u_char *)malloc((u_int)packlen))) { + (void)fprintf(stderr, "ping: out of memory.\n"); + exit(1); + } + if (!(options & F_PINGFILLED)) + for (i = 8; i < datalen; ++i) + *datap++ = i; + + ident = getpid() & 0xFFFF; + + if (!(proto = getprotobyname("icmp"))) { + (void)fprintf(stderr, "ping: unknown protocol icmp.\n"); + exit(1); + } + if ((s = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) { + perror("ping: socket"); + exit(1); + } + hold = 1; + if (options & F_SO_DEBUG) + (void)setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&hold, + sizeof(hold)); + if (options & F_SO_DONTROUTE) + (void)setsockopt(s, SOL_SOCKET, SO_DONTROUTE, (char *)&hold, + sizeof(hold)); + + /* record route option */ + if (options & F_RROUTE) { +#ifdef IP_OPTIONS + rspace[IPOPT_OPTVAL] = IPOPT_RR; + rspace[IPOPT_OLEN] = sizeof(rspace)-1; + rspace[IPOPT_OFFSET] = IPOPT_MINOFF; + if (setsockopt(s, IPPROTO_IP, IP_OPTIONS, rspace, + sizeof(rspace)) < 0) { + perror("ping: record route"); + exit(1); + } +#else + (void)fprintf(stderr, + "ping: record route not available in this implementation.\n"); + exit(1); +#endif /* IP_OPTIONS */ + } + + /* + * When pinging the broadcast address, you can get a lot of answers. + * Doing something so evil is useful if you are trying to stress the + * ethernet, or just want to fill the arp cache to get some stuff for + * /etc/ethers. + */ + hold = 48 * 1024; + (void)setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&hold, + sizeof(hold)); + + if (to->sin_family == AF_INET) + (void)printf("PING %s (%s): %d data bytes\n", hostname, + inet_ntoa(*(struct in_addr *)&to->sin_addr.s_addr), + datalen); + else + (void)printf("PING %s: %d data bytes\n", hostname, datalen); + + (void)signal(SIGINT, finish); + (void)signal(SIGALRM, catcher); + + while (preload--) /* fire off them quickies */ + pinger(); + + if ((options & F_FLOOD) == 0) + catcher(); /* start things going */ + + for (;;) { + struct sockaddr_in from; + register int cc; + int fromlen; + + if (options & F_FLOOD) { + pinger(); + timeout.tv_sec = 0; + timeout.tv_usec = 10000; + fdmask = 1 << s; + if (select(s + 1, (fd_set *)&fdmask, (fd_set *)NULL, + (fd_set *)NULL, &timeout) < 1) + continue; + } + fromlen = sizeof(from); + if ((cc = recvfrom(s, (char *)packet, packlen, 0, + (struct sockaddr *)&from, &fromlen)) < 0) { + if (errno == EINTR) + continue; + perror("ping: recvfrom"); + continue; + } + pr_pack((char *)packet, cc, &from); + if (npackets && nreceived >= npackets) + break; + } + finish(); + /* NOTREACHED */ +} + +/* + * catcher -- + * This routine causes another PING to be transmitted, and then + * schedules another SIGALRM for 1 second from now. + * + * bug -- + * Our sense of time will slowly skew (i.e., packets will not be + * launched exactly at 1-second intervals). This does not affect the + * quality of the delay and loss statistics. + */ +void +catcher() +{ + int waittime; + + pinger(); + (void)signal(SIGALRM, catcher); + if (!npackets || ntransmitted < npackets) + alarm((u_int)interval); + else { + if (nreceived) { + waittime = 2 * tmax / 1000; + if (!waittime) + waittime = 1; + } else + waittime = MAXWAIT; + (void)signal(SIGALRM, finish); + (void)alarm((u_int)waittime); + } +} + +/* + * pinger -- + * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet + * will be added on by the kernel. The ID field is our UNIX process ID, + * and the sequence number is an ascending integer. The first 8 bytes + * of the data portion are used to hold a UNIX "timeval" struct in VAX + * byte-order, to compute the round-trip time. + */ +pinger() +{ + register struct icmp *icp; + register int cc; + int i; + + icp = (struct icmp *)outpack; + icp->icmp_type = ICMP_ECHO; + icp->icmp_code = 0; + icp->icmp_cksum = 0; + icp->icmp_seq = ntransmitted++; + icp->icmp_id = ident; /* ID */ + + CLR(icp->icmp_seq % mx_dup_ck); + + if (timing) + (void)gettimeofday((struct timeval *)&outpack[8], + (struct timezone *)NULL); + + cc = datalen + 8; /* skips ICMP portion */ + + /* compute ICMP checksum here */ + icp->icmp_cksum = in_cksum((u_short *)icp, cc); + + i = sendto(s, (char *)outpack, cc, 0, &whereto, + sizeof(struct sockaddr)); + + if (i < 0 || i != cc) { + if (i < 0) + perror("ping: sendto"); + (void)printf("ping: wrote %s %d chars, ret=%d\n", + hostname, cc, i); + } + if (!(options & F_QUIET) && options & F_FLOOD) + (void)write(STDOUT_FILENO, &DOT, 1); +} + +/* + * pr_pack -- + * Print out the packet, if it came from us. This logic is necessary + * because ALL readers of the ICMP socket get a copy of ALL ICMP packets + * which arrive ('tis only fair). This permits multiple copies of this + * program to be run without having intermingled output (or statistics!). + */ +pr_pack(buf, cc, from) + char *buf; + int cc; + struct sockaddr_in *from; +{ + register struct icmp *icp; + register u_long l; + register int i, j; + register u_char *cp,*dp; + static int old_rrlen; + static char old_rr[MAX_IPOPTLEN]; + struct ip *ip; + struct timeval tv, *tp; + double triptime; + int hlen, dupflag; + + (void)gettimeofday(&tv, (struct timezone *)NULL); + + /* Check the IP header */ + ip = (struct ip *)buf; + hlen = ip->ip_hl << 2; + if (cc < hlen + ICMP_MINLEN) { + if (options & F_VERBOSE) + (void)fprintf(stderr, + "ping: packet too short (%d bytes) from %s\n", cc, + inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr)); + return; + } + + /* Now the ICMP part */ + cc -= hlen; + icp = (struct icmp *)(buf + hlen); + if (icp->icmp_type == ICMP_ECHOREPLY) { + if (icp->icmp_id != ident) + return; /* 'Twas not our ECHO */ + ++nreceived; + if (timing) { +#ifndef icmp_data + tp = (struct timeval *)&icp->icmp_ip; +#else + tp = (struct timeval *)icp->icmp_data; +#endif + tvsub(&tv, tp); + triptime = ((double)tv.tv_sec) * 1000.0 + + ((double)tv.tv_usec) / 1000.0; + tsum += triptime; + if (triptime < tmin) + tmin = triptime; + if (triptime > tmax) + tmax = triptime; + } + + if (TST(icp->icmp_seq % mx_dup_ck)) { + ++nrepeats; + --nreceived; + dupflag = 1; + } else { + SET(icp->icmp_seq % mx_dup_ck); + dupflag = 0; + } + + if (options & F_QUIET) + return; + + if (options & F_FLOOD) + (void)write(STDOUT_FILENO, &BSPACE, 1); + else { + (void)printf("%d bytes from %s: icmp_seq=%u", cc, + inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr), + icp->icmp_seq); + (void)printf(" ttl=%d", ip->ip_ttl); + if (timing) + (void)printf(" time=%g ms", triptime); + if (dupflag) + (void)printf(" (DUP!)"); + /* check the data */ + cp = (u_char*)&icp->icmp_data[8]; + dp = &outpack[8 + sizeof(struct timeval)]; + for (i = 8; i < datalen; ++i, ++cp, ++dp) { + if (*cp != *dp) { + (void)printf("\nwrong data byte #%d should be 0x%x but was 0x%x", + i, *dp, *cp); + cp = (u_char*)&icp->icmp_data[0]; + for (i = 8; i < datalen; ++i, ++cp) { + if ((i % 32) == 8) + (void)printf("\n\t"); + (void)printf("%x ", *cp); + } + break; + } + } + } + } else { + /* We've got something other than an ECHOREPLY */ + if (!(options & F_VERBOSE)) + return; + (void)printf("%d bytes from %s: ", cc, + pr_addr(from->sin_addr.s_addr)); + pr_icmph(icp); + } + + /* Display any IP options */ + cp = (u_char *)buf + sizeof(struct ip); + + for (; hlen > (int)sizeof(struct ip); --hlen, ++cp) + switch (*cp) { + case IPOPT_EOL: + hlen = 0; + break; + case IPOPT_LSRR: + (void)printf("\nLSRR: "); + hlen -= 2; + j = *++cp; + ++cp; + if (j > IPOPT_MINOFF) + for (;;) { + l = *++cp; + l = (l<<8) + *++cp; + l = (l<<8) + *++cp; + l = (l<<8) + *++cp; + if (l == 0) + (void)printf("\t0.0.0.0"); + else + (void)printf("\t%s", pr_addr(ntohl(l))); + hlen -= 4; + j -= 4; + if (j <= IPOPT_MINOFF) + break; + (void)putchar('\n'); + } + break; + case IPOPT_RR: + j = *++cp; /* get length */ + i = *++cp; /* and pointer */ + hlen -= 2; + if (i > j) + i = j; + i -= IPOPT_MINOFF; + if (i <= 0) + continue; + if (i == old_rrlen + && cp == (u_char *)buf + sizeof(struct ip) + 2 + && !memcmp(cp, old_rr, i) + && !(options & F_FLOOD)) { + (void)printf("\t(same route)"); + i = ((i + 3) / 4) * 4; + hlen -= i; + cp += i; + break; + } + old_rrlen = i; + memmove(old_rr, cp, i); + (void)printf("\nRR: "); + for (;;) { + l = *++cp; + l = (l<<8) + *++cp; + l = (l<<8) + *++cp; + l = (l<<8) + *++cp; + if (l == 0) + (void)printf("\t0.0.0.0"); + else + (void)printf("\t%s", pr_addr(ntohl(l))); + hlen -= 4; + i -= 4; + if (i <= 0) + break; + (void)putchar('\n'); + } + break; + case IPOPT_NOP: + (void)printf("\nNOP"); + break; + default: + (void)printf("\nunknown option %x", *cp); + break; + } + if (!(options & F_FLOOD)) { + (void)putchar('\n'); + (void)fflush(stdout); + } +} + +/* + * in_cksum -- + * Checksum routine for Internet Protocol family headers (C Version) + */ +in_cksum(addr, len) + u_short *addr; + int len; +{ + register int nleft = len; + register u_short *w = addr; + register int sum = 0; + u_short answer = 0; + + /* + * Our algorithm is simple, using a 32 bit accumulator (sum), we add + * sequential 16 bit words to it, and at the end, fold back all the + * carry bits from the top 16 bits into the lower 16 bits. + */ + while (nleft > 1) { + sum += *w++; + nleft -= 2; + } + + /* mop up an odd byte, if necessary */ + if (nleft == 1) { + *(u_char *)(&answer) = *(u_char *)w ; + sum += answer; + } + + /* add back carry outs from top 16 bits to low 16 bits */ + sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ + sum += (sum >> 16); /* add carry */ + answer = ~sum; /* truncate to 16 bits */ + return(answer); +} + +/* + * tvsub -- + * Subtract 2 timeval structs: out = out - in. Out is assumed to + * be >= in. + */ +tvsub(out, in) + register struct timeval *out, *in; +{ + if ((out->tv_usec -= in->tv_usec) < 0) { + --out->tv_sec; + out->tv_usec += 1000000; + } + out->tv_sec -= in->tv_sec; +} + +/* + * finish -- + * Print out statistics, and give up. + */ +void +finish() +{ + register int i; + + (void)signal(SIGINT, SIG_IGN); + (void)putchar('\n'); + (void)fflush(stdout); + (void)printf("--- %s ping statistics ---\n", hostname); + (void)printf("%ld packets transmitted, ", ntransmitted); + (void)printf("%ld packets received, ", nreceived); + if (nrepeats) + (void)printf("+%ld duplicates, ", nrepeats); + if (ntransmitted) + if (nreceived > ntransmitted) + (void)printf("-- somebody's printing up packets!"); + else + (void)printf("%d%% packet loss", + (int) (((ntransmitted - nreceived) * 100) / + ntransmitted)); + (void)putchar('\n'); + if (nreceived && timing) { + /* Only display average to microseconds */ + i = 1000.0 * tsum / (nreceived + nrepeats); + (void)printf("round-trip min/avg/max = %g/%g/%g ms\n", + tmin, ((double)i) / 1000.0, tmax); + } + exit(0); +} + +#ifdef notdef +static char *ttab[] = { + "Echo Reply", /* ip + seq + udata */ + "Dest Unreachable", /* net, host, proto, port, frag, sr + IP */ + "Source Quench", /* IP */ + "Redirect", /* redirect type, gateway, + IP */ + "Echo", + "Time Exceeded", /* transit, frag reassem + IP */ + "Parameter Problem", /* pointer + IP */ + "Timestamp", /* id + seq + three timestamps */ + "Timestamp Reply", /* " */ + "Info Request", /* id + sq */ + "Info Reply" /* " */ +}; +#endif + +/* + * pr_icmph -- + * Print a descriptive string about an ICMP header. + */ +pr_icmph(icp) + struct icmp *icp; +{ + switch(icp->icmp_type) { + case ICMP_ECHOREPLY: + (void)printf("Echo Reply\n"); + /* XXX ID + Seq + Data */ + break; + case ICMP_UNREACH: + switch(icp->icmp_code) { + case ICMP_UNREACH_NET: + (void)printf("Destination Net Unreachable\n"); + break; + case ICMP_UNREACH_HOST: + (void)printf("Destination Host Unreachable\n"); + break; + case ICMP_UNREACH_PROTOCOL: + (void)printf("Destination Protocol Unreachable\n"); + break; + case ICMP_UNREACH_PORT: + (void)printf("Destination Port Unreachable\n"); + break; + case ICMP_UNREACH_NEEDFRAG: + (void)printf("frag needed and DF set\n"); + break; + case ICMP_UNREACH_SRCFAIL: + (void)printf("Source Route Failed\n"); + break; + default: + (void)printf("Dest Unreachable, Bad Code: %d\n", + icp->icmp_code); + break; + } + /* Print returned IP header information */ +#ifndef icmp_data + pr_retip(&icp->icmp_ip); +#else + pr_retip((struct ip *)icp->icmp_data); +#endif + break; + case ICMP_SOURCEQUENCH: + (void)printf("Source Quench\n"); +#ifndef icmp_data + pr_retip(&icp->icmp_ip); +#else + pr_retip((struct ip *)icp->icmp_data); +#endif + break; + case ICMP_REDIRECT: + switch(icp->icmp_code) { + case ICMP_REDIRECT_NET: + (void)printf("Redirect Network"); + break; + case ICMP_REDIRECT_HOST: + (void)printf("Redirect Host"); + break; + case ICMP_REDIRECT_TOSNET: + (void)printf("Redirect Type of Service and Network"); + break; + case ICMP_REDIRECT_TOSHOST: + (void)printf("Redirect Type of Service and Host"); + break; + default: + (void)printf("Redirect, Bad Code: %d", icp->icmp_code); + break; + } + (void)printf("(New addr: 0x%08lx)\n", icp->icmp_gwaddr.s_addr); +#ifndef icmp_data + pr_retip(&icp->icmp_ip); +#else + pr_retip((struct ip *)icp->icmp_data); +#endif + break; + case ICMP_ECHO: + (void)printf("Echo Request\n"); + /* XXX ID + Seq + Data */ + break; + case ICMP_TIMXCEED: + switch(icp->icmp_code) { + case ICMP_TIMXCEED_INTRANS: + (void)printf("Time to live exceeded\n"); + break; + case ICMP_TIMXCEED_REASS: + (void)printf("Frag reassembly time exceeded\n"); + break; + default: + (void)printf("Time exceeded, Bad Code: %d\n", + icp->icmp_code); + break; + } +#ifndef icmp_data + pr_retip(&icp->icmp_ip); +#else + pr_retip((struct ip *)icp->icmp_data); +#endif + break; + case ICMP_PARAMPROB: + (void)printf("Parameter problem: pointer = 0x%02x\n", + icp->icmp_hun.ih_pptr); +#ifndef icmp_data + pr_retip(&icp->icmp_ip); +#else + pr_retip((struct ip *)icp->icmp_data); +#endif + break; + case ICMP_TSTAMP: + (void)printf("Timestamp\n"); + /* XXX ID + Seq + 3 timestamps */ + break; + case ICMP_TSTAMPREPLY: + (void)printf("Timestamp Reply\n"); + /* XXX ID + Seq + 3 timestamps */ + break; + case ICMP_IREQ: + (void)printf("Information Request\n"); + /* XXX ID + Seq */ + break; + case ICMP_IREQREPLY: + (void)printf("Information Reply\n"); + /* XXX ID + Seq */ + break; +#ifdef ICMP_MASKREQ + case ICMP_MASKREQ: + (void)printf("Address Mask Request\n"); + break; +#endif +#ifdef ICMP_MASKREPLY + case ICMP_MASKREPLY: + (void)printf("Address Mask Reply\n"); + break; +#endif + default: + (void)printf("Bad ICMP type: %d\n", icp->icmp_type); + } +} + +/* + * pr_iph -- + * Print an IP header with options. + */ +pr_iph(ip) + struct ip *ip; +{ + int hlen; + u_char *cp; + + hlen = ip->ip_hl << 2; + cp = (u_char *)ip + 20; /* point to options */ + + (void)printf("Vr HL TOS Len ID Flg off TTL Pro cks Src Dst Data\n"); + (void)printf(" %1x %1x %02x %04x %04x", + ip->ip_v, ip->ip_hl, ip->ip_tos, ip->ip_len, ip->ip_id); + (void)printf(" %1x %04x", ((ip->ip_off) & 0xe000) >> 13, + (ip->ip_off) & 0x1fff); + (void)printf(" %02x %02x %04x", ip->ip_ttl, ip->ip_p, ip->ip_sum); + (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_src.s_addr)); + (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_dst.s_addr)); + /* dump and option bytes */ + while (hlen-- > 20) { + (void)printf("%02x", *cp++); + } + (void)putchar('\n'); +} + +/* + * pr_addr -- + * Return an ascii host address as a dotted quad and optionally with + * a hostname. + */ +char * +pr_addr(l) + u_long l; +{ + struct hostent *hp; + static char buf[80]; + + if ((options & F_NUMERIC) || + !(hp = gethostbyaddr((char *)&l, 4, AF_INET))) + (void)sprintf(buf, "%s", inet_ntoa(*(struct in_addr *)&l)); + else + (void)sprintf(buf, "%s (%s)", hp->h_name, + inet_ntoa(*(struct in_addr *)&l)); + return(buf); +} + +/* + * pr_retip -- + * Dump some info on a returned (via ICMP) IP packet. + */ +pr_retip(ip) + struct ip *ip; +{ + int hlen; + u_char *cp; + + pr_iph(ip); + hlen = ip->ip_hl << 2; + cp = (u_char *)ip + hlen; + + if (ip->ip_p == 6) + (void)printf("TCP: from port %u, to port %u (decimal)\n", + (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3))); + else if (ip->ip_p == 17) + (void)printf("UDP: from port %u, to port %u (decimal)\n", + (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3))); +} + +fill(bp, patp) + char *bp, *patp; +{ + register int ii, jj, kk; + int pat[16]; + char *cp; + + for (cp = patp; *cp; cp++) + if (!isxdigit(*cp)) { + (void)fprintf(stderr, + "ping: patterns must be specified as hex digits.\n"); + exit(1); + } + ii = sscanf(patp, + "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x", + &pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], &pat[6], + &pat[7], &pat[8], &pat[9], &pat[10], &pat[11], &pat[12], + &pat[13], &pat[14], &pat[15]); + + if (ii > 0) + for (kk = 0; + kk <= MAXPACKET - (8 + sizeof(struct timeval) + ii); + kk += ii) + for (jj = 0; jj < ii; ++jj) + bp[jj + kk] = pat[jj]; + if (!(options & F_QUIET)) { + (void)printf("PATTERN: 0x"); + for (jj = 0; jj < ii; ++jj) + (void)printf("%02x", bp[jj] & 0xFF); + (void)printf("\n"); + } +} + +usage() +{ + (void)fprintf(stderr, + "usage: ping [-Rdfnqrv] [-c count] [-i wait] [-l preload]\n\t[-p pattern] [-s packetsize] host\n"); + exit(1); +} diff --git a/sbin/route/ccitt_addr.c b/sbin/route/ccitt_addr.c new file mode 100644 index 000000000000..54fb9b265b3d --- /dev/null +++ b/sbin/route/ccitt_addr.c @@ -0,0 +1,175 @@ +/* + * 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. + * + * @(#)ccitt_addr.c 8.2 (Berkeley) 4/28/95 + */ +/* + * parse CCITT addresses + * + * Addresses must have the format: [hpr],x121address[,userdata][,protocol] + * items enclosed with square brackets are optional + * 'h' or 'p' means hi priority (packet size = 128; specific to Datapac + * and necessary only for X.25(76) and non-negotiating X.25(80) DTE's) + * 'r' means reverse charge (remote DTE pays for call). + * The x121address consists of an optional netid and dot, followed + * by a dte address. + * + * Frank Pronk + * The University of British Columbia + * Laboratory for Computational Vision + * Copyright (c) 1984 + */ + +#include +#include +#include + +static char *copychar (); + +ccitt_addr (addr, xp) +char *addr; +register struct sockaddr_x25 *xp; +{ + register char *p, *ap, *limit; + int havenet = 0; + + memset(xp, 0, sizeof (*xp)); + xp->x25_family = AF_CCITT; + xp->x25_len = sizeof(*xp); + p = addr; + + /* + * process optional priority and reverse charging flags + */ + + if (*p == 'p' || *p == 'r' || *p == 'h') { + while (*p == 'p' || *p == 'r' || *p == 'h') { + if (*p == 'p' || *p == 'h') + xp->x25_opts.op_psize = X25_PS128; + else if (*p == 'r') + xp->x25_opts.op_flags |= X25_REVERSE_CHARGE; + p++; + } + if (*p != ',') + return (0); + p++; + } + if (*p == '\0') + return (0); + + /* + * [network id:]X.121 address + */ + + ap = xp->x25_addr; + limit = ap + sizeof (xp->x25_addr) - 1; + while (*p) { + if (*p == ',') + break; + if (*p == '.' || *p == ':') { + if (havenet) + return (0); + havenet++; + xp->x25_net = atoi (xp->x25_addr); + p++; + ap = xp->x25_addr; + *ap = '\0'; + } + if (*p < '0' || *p > '9') + return (0); + if (ap >= limit) + return (0); + *ap++ = *p++; + } + if (*p == '\0') + return (1); + + /* + * optional user data, bytes 4 to 16 + */ + + p++; + ap = xp->x25_udata + 4; /* first four bytes are protocol id */ + limit = ap + sizeof (xp->x25_udata) - 4; + xp->x25_udlen = 4; + while (*p) { + if (*p == ',') + break; + if (ap >= limit) + return (0); + p = copychar (p, ap++); + xp->x25_udlen++; + } + if (xp->x25_udlen == 4) + xp->x25_udlen = 0; + if (*p == '\0') + return (1); + + p++; + ap = xp->x25_udata; /* protocol id */ + limit = ap + (xp->x25_udlen ? 4 : sizeof(xp->x25_udata)); + while (*p) { + if (*p == ',') + return (0); + if (ap >= limit) + return (0); + p = copychar (p, ap++); + } + if (xp->x25_udlen == 0) + xp->x25_udlen = ap - xp->x25_udata; + return (1); +} + +static char * +copychar (from, to) +register char *from, *to; +{ + register int n; + + if (*from != '\\' || from[1] < '0' || from[1] > '7') { + *to = *from++; + return (from); + } + n = *++from - '0'; + from++; + if (*from >= '0' && *from <= '7') { + register int n1; + + n = n*8 + *from++ - '0'; + if (*from >= '0' && *from <= '7' && (n1 = n*8 + *from-'0') < 256) { + n = n1; + from++; + } + } + *to = n; + return (from); +} diff --git a/sbin/route/route.8 b/sbin/route/route.8 new file mode 100644 index 000000000000..62b9cfe44708 --- /dev/null +++ b/sbin/route/route.8 @@ -0,0 +1,325 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)route.8 8.4 (Berkeley) 6/1/94 +.\" +.Dd June 1, 1994 +.Dt ROUTE 8 +.Os BSD 4.4 +.Sh NAME +.Nm route +.Nd manually manipulate the routing tables. +.Sh SYNOPSIS +.Nm route +.Op Fl nqv +.Ar command +.Oo +.Op Ar modifiers +.Ar args +.Oc +.Sh DESCRIPTION +.Nm Route +is a utility used to manually manipulate the network +routing tables. It normally is not needed, as a +system routing table management daemon such as +.Xr routed 8 , +should tend to this task. +.Pp +The +.Nm route : +utility supports a limited number of general options, +but a rich command language, enabling the user to specify +any arbitrary request that could be delivered via the +programmatic interface discussed in +.Xr route 4 . +.Pp +.Bl -tag -width Ds +.It Fl n +Bypasses attempts to print host and network names symbolically +when reporting actions. (The process of translating between symbolic +names and numerical equivalents can be quite time consuming, and +may require correct operation of the network; thus it may be expedient +to forgo this, especially when attempting to repair networking operations), +.It Fl v +(verbose) Print additional details. +.It Fl q +Suppress all output. +.El +.Pp +The +.Nm route : +utility provides six commands: +.Pp +.Bl -tag -width Fl -compact +.It Cm add +Add a route. +.It Cm flush +Remove all routes. +.It Cm delete +Delete a specific route. +.It Cm change +Change aspects of a route (such as its gateway). +.It Cm get +Lookup and display the route for a destination. +.It Cm monitor +Continuously report any changes to the routing information base, +routing lookup misses, or suspected network partitionings. +.El +.Pp +The monitor command has the syntax +.Pp +.Bd -filled -offset indent -compact +.Nm route Op Fl n +.Cm monitor +.Ed +.Pp +The flush command has the syntax +.Pp +.Bd -filled -offset indent -compact +.Nm route Op Fl n +.Cm flush +.Op Ar family +.Ed +.Pp +If the +.Cm flush +command is specified, +.Nm route +will ``flush'' the routing tables of all gateway entries. +When the address family may is specified by any of the +.Fl osi , +.Fl xns , +or +.Fl inet +modifiers, only routes having destinations with addresses in the +delineated family will be deleted. +.Pp +The other commands have the following syntax: +.Pp +.Bd -filled -offset indent -compact +.Nm route Op Fl n +.Ar command +.Op Fl net No \&| Fl host +.Ar destination gateway +.Ed +.Pp +where +.Ar destination +is the destination host or network, +.Ar gateway +is the next-hop intermediary via which packets should be routed. +Routes to a particular host may be distinguished from those to +a network by interpreting the Internet address specified as the +.Ar destination argument. +The optional modifiers +.Fl net +and +.Fl host +force the destination to be interpreted as a network or a host, respectively. +Otherwise, if the +.Ar destination +has a ``local address part'' of +INADDR_ANY , +or if the +.Ar destination +is the symbolic name of a network, then the route is +assumed to be to a network; otherwise, it is presumed to be a +route to a host. +.Pp +For example, +.Li 128.32 +is interpreted as +.Fl host Li 128.0.0.32 ; +.Li 128.32.130 +is interpreted as +.Fl host Li 128.32.0.130 ; +.Fl net Li 128.32 +is interpreted as +.Li 128.32.0.0; +and +.Fl net Li 128.32.130 +is interpreted as +.Li 128.32.130.0 . +.Pp +If the destination is directly reachable +via an interface requiring +no intermediary system to act as a gateway, the +.Fl interface +modifier should be specified; +the gateway given is the address of this host on the common network, +indicating the interface to be used for transmission. +.Pp +The optional modifiers +.Fl xns , +.Fl osi , +and +.Fl link +specify that all subsequent addresses are in the +.Tn XNS +.Tn OSI +address families, +or are specified as link-level addresses, +and the names must be numeric specifications rather than +symbolic names. +.Pp +The optional +.Fl netmask +qualifier is intended +to achieve the effect of an +.Tn OSI +.Tn ESIS +redirect with the netmask option, +or to manually add subnet routes with +netmasks different from that of the implied network interface +(as would otherwise be communicated using the OSPF or ISIS routing protocols). +One specifies an additional ensuing address parameter +(to be interpreted as a network mask). +The implicit network mask generated in the AF_INET case +can be overridden by making sure this option follows the destination parameter. +.Pp +Routes have associated flags which influence operation of the protocols +when sending to destinations matched by the routes. +These flags may be set (or sometimes cleared) +by indicating the following corresponding modifiers: +.Bd -literal +-cloning RTF_CLONING - generates a new route on use +-xresolve RTF_XRESOLVE - emit mesg on use (for external lookup) +-iface ~RTF_GATEWAY - destination is directly reachable +-static RTF_STATIC - manually added route +-nostatic ~RTF_STATIC - pretend route added by kernel or daemon +-reject RTF_REJECT - emit an ICMP unreachable when matched +-blackhole RTF_BLACKHOLE - silently discard pkts (during updates) +-proto1 RTF_PROTO1 - set protocol specific routing flag #1 +-proto2 RTF_PROTO2 - set protocol specific routing flag #2 +-llinfo RTF_LLINFO - validly translates proto addr to link addr +.Ed +.Pp +The optional modifiers +.Fl rtt , +.Fl rttvar , +.Fl sendpipe , +.Fl recvpipe , +.Fl mtu , +.Fl hopcount , +.Fl expire , +and +.Fl ssthresh +provide initial values to quantities maintained in the routing entry +by transport level protocols, such as TCP or TP4. +These may be individually locked by preceding each such modifier to +be locked by +the +.Fl lock +meta-modifier, or one can +specify that all ensuing metrics may be locked by the +.Fl lockrest +meta-modifier. +.Pp +In a +.Cm change +or +.Cm add +command where the destination and gateway are not sufficient to specify +the route (as in the +.Tn ISO +case where several interfaces may have the +same address), the +.Fl ifp +or +.Fl ifa +modifiers may be used to determine the interface or interface address. +.Pp +All symbolic names specified for a +.Ar destination +or +.Ar gateway +are looked up first as a host name using +.Xr gethostbyname 3 . +If this lookup fails, +.Xr getnetbyname 3 +is then used to interpret the name as that of a network. +.Pp +.Nm Route +uses a routing socket and the new message types +RTM_ADD, +RTM_DELETE, +RTM_GET, +and +RTM_CHANGE. +As such, only the super-user may modify +the routing tables. +.ne 1i +.Sh DIAGNOSTICS +.Bl -tag -width Ds +.It Sy "add [host \&| network ] %s: gateway %s flags %x" +The specified route is being added to the tables. The +values printed are from the routing table entry supplied +in the +.Xr ioctl 2 +call. +If the gateway address used was not the primary address of the gateway +(the first one returned by +.Xr gethostbyname 3 ) , +the gateway address is printed numerically as well as symbolically. +.It Sy "delete [ host &| network ] %s: gateway %s flags %x" +As above, but when deleting an entry. +.It Sy "%s %s done" +When the +.Cm flush +command is specified, each routing table entry deleted +is indicated with a message of this form. +.It Sy "Network is unreachable" +An attempt to add a route failed because the gateway listed was not +on a directly-connected network. +The next-hop gateway must be given. +.It Sy "not in table" +A delete operation was attempted for an entry which +wasn't present in the tables. +.It Sy "routing table overflow" +An add operation was attempted, but the system was +low on resources and was unable to allocate memory +to create the new entry. +.El +.Sh SEE ALSO +.Xr netintro 4 , +.Xr route 4 , +.Xr esis 4 , +.Xr routed 8 , +.Xr XNSrouted 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . +.Sh BUGS +The first paragraph may have slightly exaggerated +.Xr routed Ns 's +abilities. diff --git a/sbin/route/route.c b/sbin/route/route.c new file mode 100644 index 000000000000..2e48e71a2cca --- /dev/null +++ b/sbin/route/route.c @@ -0,0 +1,1415 @@ +/* + * + * Copyright (c) 1983, 1989, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1989, 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)route.c 8.6 (Berkeley) 4/28/95"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +struct keytab { + char *kt_cp; + int kt_i; +} keywords[] = { +#include "keywords.h" + {0, 0} +}; + +struct ortentry route; +union sockunion { + struct sockaddr sa; + struct sockaddr_in sin; + struct sockaddr_ns sns; + struct sockaddr_iso siso; + struct sockaddr_dl sdl; + struct sockaddr_x25 sx25; +} so_dst, so_gate, so_mask, so_genmask, so_ifa, so_ifp; + +typedef union sockunion *sup; +int pid, rtm_addrs, uid; +int s; +int forcehost, forcenet, doflush, nflag, af, qflag, tflag, keyword(); +int iflag, verbose, aflen = sizeof (struct sockaddr_in); +int locking, lockrest, debugonly; +struct rt_metrics rt_metrics; +u_long rtm_inits; +struct in_addr inet_makeaddr(); +char *routename(), *netname(); +void flushroutes(), newroute(), monitor(), sockaddr(), sodump(), bprintf(); +void print_getmsg(), print_rtmsg(), pmsg_common(), pmsg_addrs(), mask_addr(); +int getaddr(), rtmsg(), x25_makemask(); +extern char *inet_ntoa(), *iso_ntoa(), *link_ntoa(); + +__dead void +usage(cp) + char *cp; +{ + if (cp) + (void) fprintf(stderr, "route: botched keyword: %s\n", cp); + (void) fprintf(stderr, + "usage: route [ -nqv ] cmd [[ - ] args ]\n"); + exit(1); + /* NOTREACHED */ +} + +void +quit(s) + char *s; +{ + int sverrno = errno; + + (void) fprintf(stderr, "route: "); + if (s) + (void) fprintf(stderr, "%s: ", s); + (void) fprintf(stderr, "%s\n", strerror(sverrno)); + exit(1); + /* NOTREACHED */ +} + +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) +#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) + +int +main(argc, argv) + int argc; + char **argv; +{ + extern int optind; + int ch; + + if (argc < 2) + usage((char *)NULL); + + while ((ch = getopt(argc, argv, "nqdtv")) != EOF) + switch(ch) { + case 'n': + nflag = 1; + break; + case 'q': + qflag = 1; + break; + case 'v': + verbose = 1; + break; + case 't': + tflag = 1; + break; + case 'd': + debugonly = 1; + break; + case '?': + default: + usage((char *)NULL); + } + argc -= optind; + argv += optind; + + pid = getpid(); + uid = getuid(); + if (tflag) + s = open("/dev/null", O_WRONLY, 0); + else + s = socket(PF_ROUTE, SOCK_RAW, 0); + if (s < 0) + quit("socket"); + if (*argv) + switch (keyword(*argv)) { + case K_GET: + uid = 0; + /* FALLTHROUGH */ + + case K_CHANGE: + case K_ADD: + case K_DELETE: + newroute(argc, argv); + exit(0); + /* NOTREACHED */ + + case K_MONITOR: + monitor(); + /* NOTREACHED */ + + case K_FLUSH: + flushroutes(argc, argv); + exit(0); + /* NOTREACHED */ + } + usage(*argv); + /* NOTREACHED */ +} + +/* + * Purge all entries in the routing tables not + * associated with network interfaces. + */ +void +flushroutes(argc, argv) + int argc; + char *argv[]; +{ + size_t needed; + int mib[6], rlen, seqno; + char *buf, *next, *lim; + register struct rt_msghdr *rtm; + + if (uid) { + errno = EACCES; + quit("must be root to alter routing table"); + } + shutdown(s, 0); /* Don't want to read back our messages */ + if (argc > 1) { + argv++; + if (argc == 2 && **argv == '-') + switch (keyword(*argv + 1)) { + case K_INET: + af = AF_INET; + break; + case K_XNS: + af = AF_NS; + break; + case K_LINK: + af = AF_LINK; + break; + case K_ISO: + case K_OSI: + af = AF_ISO; + break; + case K_X25: + af = AF_CCITT; + default: + goto bad; + } else +bad: usage(*argv); + } + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; /* protocol */ + mib[3] = 0; /* wildcard address family */ + mib[4] = NET_RT_DUMP; + mib[5] = 0; /* no flags */ + if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) + quit("route-sysctl-estimate"); + if ((buf = malloc(needed)) == NULL) + quit("malloc"); + if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) + quit("actual retrieval of routing table"); + lim = buf + needed; + if (verbose) + (void) printf("Examining routing table from sysctl\n"); + seqno = 0; /* ??? */ + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + if (verbose) + print_rtmsg(rtm, rtm->rtm_msglen); + if ((rtm->rtm_flags & RTF_GATEWAY) == 0) + continue; + if (af) { + struct sockaddr *sa = (struct sockaddr *)(rtm + 1); + + if (sa->sa_family != af) + continue; + } + if (debugonly) + continue; + rtm->rtm_type = RTM_DELETE; + rtm->rtm_seq = seqno; + rlen = write(s, next, rtm->rtm_msglen); + if (rlen < (int)rtm->rtm_msglen) { + (void) fprintf(stderr, + "route: write to routing socket: %s\n", + strerror(errno)); + (void) printf("got only %d for rlen\n", rlen); + break; + } + seqno++; + if (qflag) + continue; + if (verbose) + print_rtmsg(rtm, rlen); + else { + struct sockaddr *sa = (struct sockaddr *)(rtm + 1); + (void) printf("%-20.20s ", rtm->rtm_flags & RTF_HOST ? + routename(sa) : netname(sa)); + sa = (struct sockaddr *)(sa->sa_len + (char *)sa); + (void) printf("%-20.20s ", routename(sa)); + (void) printf("done\n"); + } + } +} + +char * +routename(sa) + struct sockaddr *sa; +{ + register char *cp; + static char line[50]; + struct hostent *hp; + static char domain[MAXHOSTNAMELEN + 1]; + static int first = 1; + char *ns_print(); + + if (first) { + first = 0; + if (gethostname(domain, MAXHOSTNAMELEN) == 0 && + (cp = strchr(domain, '.'))) + (void) strcpy(domain, cp + 1); + else + domain[0] = 0; + } + + if (sa->sa_len == 0) + strcpy(line, "default"); + else switch (sa->sa_family) { + + case AF_INET: + { struct in_addr in; + in = ((struct sockaddr_in *)sa)->sin_addr; + + cp = 0; + if (in.s_addr == INADDR_ANY || sa->sa_len < 4) + cp = "default"; + if (cp == 0 && !nflag) { + hp = gethostbyaddr((char *)&in, sizeof (struct in_addr), + AF_INET); + if (hp) { + if ((cp = strchr(hp->h_name, '.')) && + !strcmp(cp + 1, domain)) + *cp = 0; + cp = hp->h_name; + } + } + if (cp) + strcpy(line, cp); + else { +#define C(x) ((x) & 0xff) + in.s_addr = ntohl(in.s_addr); + (void) sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24), + C(in.s_addr >> 16), C(in.s_addr >> 8), C(in.s_addr)); + } + break; + } + + case AF_NS: + return (ns_print((struct sockaddr_ns *)sa)); + + case AF_LINK: + return (link_ntoa((struct sockaddr_dl *)sa)); + + case AF_ISO: + (void) sprintf(line, "iso %s", + iso_ntoa(&((struct sockaddr_iso *)sa)->siso_addr)); + break; + + default: + { u_short *s = (u_short *)sa; + u_short *slim = s + ((sa->sa_len + 1) >> 1); + char *cp = line + sprintf(line, "(%d)", sa->sa_family); + + while (++s < slim) /* start with sa->sa_data */ + cp += sprintf(cp, " %x", *s); + break; + } + } + return (line); +} + +/* + * Return the name of the network whose address is given. + * The address is assumed to be that of a net or subnet, not a host. + */ +char * +netname(sa) + struct sockaddr *sa; +{ + char *cp = 0; + static char line[50]; + struct netent *np = 0; + u_long net, mask; + register u_long i; + int subnetshift; + char *ns_print(); + + switch (sa->sa_family) { + + case AF_INET: + { struct in_addr in; + in = ((struct sockaddr_in *)sa)->sin_addr; + + i = in.s_addr = ntohl(in.s_addr); + if (in.s_addr == 0) + cp = "default"; + else if (!nflag) { + if (IN_CLASSA(i)) { + mask = IN_CLASSA_NET; + subnetshift = 8; + } else if (IN_CLASSB(i)) { + mask = IN_CLASSB_NET; + subnetshift = 8; + } else { + mask = IN_CLASSC_NET; + subnetshift = 4; + } + /* + * If there are more bits than the standard mask + * would suggest, subnets must be in use. + * Guess at the subnet mask, assuming reasonable + * width subnet fields. + */ + while (in.s_addr &~ mask) + mask = (long)mask >> subnetshift; + net = in.s_addr & mask; + while ((mask & 1) == 0) + mask >>= 1, net >>= 1; + np = getnetbyaddr(net, AF_INET); + if (np) + cp = np->n_name; + } + if (cp) + strcpy(line, cp); + else if ((in.s_addr & 0xffffff) == 0) + (void) sprintf(line, "%u", C(in.s_addr >> 24)); + else if ((in.s_addr & 0xffff) == 0) + (void) sprintf(line, "%u.%u", C(in.s_addr >> 24), + C(in.s_addr >> 16)); + else if ((in.s_addr & 0xff) == 0) + (void) sprintf(line, "%u.%u.%u", C(in.s_addr >> 24), + C(in.s_addr >> 16), C(in.s_addr >> 8)); + else + (void) sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24), + C(in.s_addr >> 16), C(in.s_addr >> 8), + C(in.s_addr)); + break; + } + + case AF_NS: + return (ns_print((struct sockaddr_ns *)sa)); + break; + + case AF_LINK: + return (link_ntoa((struct sockaddr_dl *)sa)); + + case AF_ISO: + (void) sprintf(line, "iso %s", + iso_ntoa(&((struct sockaddr_iso *)sa)->siso_addr)); + break; + + default: + { u_short *s = (u_short *)sa->sa_data; + u_short *slim = s + ((sa->sa_len + 1)>>1); + char *cp = line + sprintf(line, "af %d:", sa->sa_family); + + while (s < slim) + cp += sprintf(cp, " %x", *s++); + break; + } + } + return (line); +} + +void +set_metric(value, key) + char *value; + int key; +{ + int flag = 0; + u_long noval, *valp = &noval; + + switch (key) { +#define caseof(x, y, z) case x: valp = &rt_metrics.z; flag = y; break + caseof(K_MTU, RTV_MTU, rmx_mtu); + caseof(K_HOPCOUNT, RTV_HOPCOUNT, rmx_hopcount); + caseof(K_EXPIRE, RTV_EXPIRE, rmx_expire); + caseof(K_RECVPIPE, RTV_RPIPE, rmx_recvpipe); + caseof(K_SENDPIPE, RTV_SPIPE, rmx_sendpipe); + caseof(K_SSTHRESH, RTV_SSTHRESH, rmx_ssthresh); + caseof(K_RTT, RTV_RTT, rmx_rtt); + caseof(K_RTTVAR, RTV_RTTVAR, rmx_rttvar); + } + rtm_inits |= flag; + if (lockrest || locking) + rt_metrics.rmx_locks |= flag; + if (locking) + locking = 0; + *valp = atoi(value); +} + +void +newroute(argc, argv) + int argc; + register char **argv; +{ + char *cmd, *dest = "", *gateway = "", *err; + int ishost = 0, ret, attempts, oerrno, flags = RTF_STATIC; + int key; + struct hostent *hp = 0; + + if (uid) { + errno = EACCES; + quit("must be root to alter routing table"); + } + cmd = argv[0]; + if (*cmd != 'g') + shutdown(s, 0); /* Don't want to read back our messages */ + while (--argc > 0) { + if (**(++argv)== '-') { + switch (key = keyword(1 + *argv)) { + case K_LINK: + af = AF_LINK; + aflen = sizeof(struct sockaddr_dl); + break; + case K_OSI: + case K_ISO: + af = AF_ISO; + aflen = sizeof(struct sockaddr_iso); + break; + case K_INET: + af = AF_INET; + aflen = sizeof(struct sockaddr_in); + break; + case K_X25: + af = AF_CCITT; + aflen = sizeof(struct sockaddr_x25); + break; + case K_SA: + af = PF_ROUTE; + aflen = sizeof(union sockunion); + break; + case K_XNS: + af = AF_NS; + aflen = sizeof(struct sockaddr_ns); + break; + case K_IFACE: + case K_INTERFACE: + iflag++; + case K_NOSTATIC: + flags &= ~RTF_STATIC; + break; + case K_LOCK: + locking = 1; + break; + case K_LOCKREST: + lockrest = 1; + break; + case K_HOST: + forcehost++; + break; + case K_REJECT: + flags |= RTF_REJECT; + break; + case K_BLACKHOLE: + flags |= RTF_BLACKHOLE; + break; + case K_PROTO1: + flags |= RTF_PROTO1; + break; + case K_PROTO2: + flags |= RTF_PROTO2; + break; + case K_CLONING: + flags |= RTF_CLONING; + break; + case K_XRESOLVE: + flags |= RTF_XRESOLVE; + break; + case K_STATIC: + flags |= RTF_STATIC; + break; + case K_IFA: + argc--; + (void) getaddr(RTA_IFA, *++argv, 0); + break; + case K_IFP: + argc--; + (void) getaddr(RTA_IFP, *++argv, 0); + break; + case K_GENMASK: + argc--; + (void) getaddr(RTA_GENMASK, *++argv, 0); + break; + case K_GATEWAY: + argc--; + (void) getaddr(RTA_GATEWAY, *++argv, 0); + break; + case K_DST: + argc--; + ishost = getaddr(RTA_DST, *++argv, &hp); + dest = *argv; + break; + case K_NETMASK: + argc--; + (void) getaddr(RTA_NETMASK, *++argv, 0); + /* FALLTHROUGH */ + case K_NET: + forcenet++; + break; + case K_MTU: + case K_HOPCOUNT: + case K_EXPIRE: + case K_RECVPIPE: + case K_SENDPIPE: + case K_SSTHRESH: + case K_RTT: + case K_RTTVAR: + argc--; + set_metric(*++argv, key); + break; + default: + usage(1+*argv); + } + } else { + if ((rtm_addrs & RTA_DST) == 0) { + dest = *argv; + ishost = getaddr(RTA_DST, *argv, &hp); + } else if ((rtm_addrs & RTA_GATEWAY) == 0) { + gateway = *argv; + (void) getaddr(RTA_GATEWAY, *argv, &hp); + } else { + int ret = atoi(*argv); + + if (ret == 0) { + if (strcmp(*argv, "0") == 0) + printf("%s,%s", + "old usage of trailing 0", + "assuming route to if\n"); + else + usage((char *)NULL); + iflag = 1; + continue; + } else if (ret > 0 && ret < 10) { + printf("old usage of trailing digit, "); + printf("assuming route via gateway\n"); + iflag = 0; + continue; + } + (void) getaddr(RTA_NETMASK, *argv, 0); + } + } + } + if (forcehost) + ishost = 1; + if (forcenet) + ishost = 0; + flags |= RTF_UP; + if (ishost) + flags |= RTF_HOST; + if (iflag == 0) + flags |= RTF_GATEWAY; + for (attempts = 1; ; attempts++) { + errno = 0; + if ((ret = rtmsg(*cmd, flags)) == 0) + break; + if (errno != ENETUNREACH && errno != ESRCH) + break; + if (af == AF_INET && *gateway && hp && hp->h_addr_list[1]) { + hp->h_addr_list++; + memmove(&so_gate.sin.sin_addr, hp->h_addr_list[0], + hp->h_length); + } else + break; + } + if (*cmd == 'g') + exit(0); + oerrno = errno; + (void) printf("%s %s %s", cmd, ishost? "host" : "net", dest); + if (*gateway) { + (void) printf(": gateway %s", gateway); + if (attempts > 1 && ret == 0 && af == AF_INET) + (void) printf(" (%s)", + inet_ntoa(((struct sockaddr_in *)&route.rt_gateway)->sin_addr)); + } + if (ret == 0) + (void) printf("\n"); + else { + switch (oerrno) { + case ESRCH: + err = "not in table"; + break; + case EBUSY: + err = "entry in use"; + break; + case ENOBUFS: + err = "routing table overflow"; + break; + default: + err = strerror(oerrno); + break; + } + (void) printf(": %s\n", err); + } +} + +void +inet_makenetandmask(net, sin) + u_long net; + register struct sockaddr_in *sin; +{ + u_long addr, mask = 0; + register char *cp; + + rtm_addrs |= RTA_NETMASK; + if (net == 0) + mask = addr = 0; + else if (net < 128) { + addr = net << IN_CLASSA_NSHIFT; + mask = IN_CLASSA_NET; + } else if (net < 65536) { + addr = net << IN_CLASSB_NSHIFT; + mask = IN_CLASSB_NET; + } else if (net < 16777216L) { + addr = net << IN_CLASSC_NSHIFT; + mask = IN_CLASSC_NET; + } else { + addr = net; + if ((addr & IN_CLASSA_HOST) == 0) + mask = IN_CLASSA_NET; + else if ((addr & IN_CLASSB_HOST) == 0) + mask = IN_CLASSB_NET; + else if ((addr & IN_CLASSC_HOST) == 0) + mask = IN_CLASSC_NET; + else + mask = -1; + } + sin->sin_addr.s_addr = htonl(addr); + sin = &so_mask.sin; + sin->sin_addr.s_addr = htonl(mask); + sin->sin_len = 0; + sin->sin_family = 0; + cp = (char *)(&sin->sin_addr + 1); + while (*--cp == 0 && cp > (char *)sin) + ; + sin->sin_len = 1 + cp - (char *)sin; +} + +/* + * Interpret an argument as a network address of some kind, + * returning 1 if a host address, 0 if a network address. + */ +int +getaddr(which, s, hpp) + int which; + char *s; + struct hostent **hpp; +{ + register sup su; + struct ns_addr ns_addr(); + struct iso_addr *iso_addr(); + struct hostent *hp; + struct netent *np; + u_long val; + + if (af == 0) { + af = AF_INET; + aflen = sizeof(struct sockaddr_in); + } + rtm_addrs |= which; + switch (which) { + case RTA_DST: + su = &so_dst; + su->sa.sa_family = af; + break; + case RTA_GATEWAY: + su = &so_gate; + su->sa.sa_family = af; + break; + case RTA_NETMASK: + su = &so_mask; + break; + case RTA_GENMASK: + su = &so_genmask; + break; + case RTA_IFP: + su = &so_ifp; + su->sa.sa_family = af; + break; + case RTA_IFA: + su = &so_ifa; + su->sa.sa_family = af; + break; + default: + usage("Internal Error"); + /*NOTREACHED*/ + } + su->sa.sa_len = aflen; + if (strcmp(s, "default") == 0) { + switch (which) { + case RTA_DST: + forcenet++; + (void) getaddr(RTA_NETMASK, s, 0); + break; + case RTA_NETMASK: + case RTA_GENMASK: + su->sa.sa_len = 0; + } + return (0); + } + switch (af) { + case AF_NS: + if (which == RTA_DST) { + extern short ns_bh[3]; + struct sockaddr_ns *sms = &(so_mask.sns); + memset(sms, 0, sizeof(*sms)); + sms->sns_family = 0; + sms->sns_len = 6; + sms->sns_addr.x_net = *(union ns_net *)ns_bh; + rtm_addrs |= RTA_NETMASK; + } + su->sns.sns_addr = ns_addr(s); + return (!ns_nullhost(su->sns.sns_addr)); + + case AF_OSI: + su->siso.siso_addr = *iso_addr(s); + if (which == RTA_NETMASK || which == RTA_GENMASK) { + register char *cp = (char *)TSEL(&su->siso); + su->siso.siso_nlen = 0; + do {--cp ;} while ((cp > (char *)su) && (*cp == 0)); + su->siso.siso_len = 1 + cp - (char *)su; + } + return (1); + + case AF_LINK: + link_addr(s, &su->sdl); + return (1); + + case AF_CCITT: + ccitt_addr(s, &su->sx25); + return (which == RTA_DST ? x25_makemask() : 1); + + case PF_ROUTE: + su->sa.sa_len = sizeof(*su); + sockaddr(s, &su->sa); + return (1); + + case AF_INET: + default: + break; + } + + if (hpp == NULL) + hpp = &hp; + *hpp = NULL; + if (((val = inet_addr(s)) != -1) && + (which != RTA_DST || forcenet == 0)) { + su->sin.sin_addr.s_addr = val; + if (inet_lnaof(su->sin.sin_addr) != INADDR_ANY) + return (1); + else { + val = ntohl(val); + goto netdone; + } + } + if ((val = inet_network(s)) != -1 || + ((np = getnetbyname(s)) != NULL && (val = np->n_net) != 0)) { +netdone: + if (which == RTA_DST) + inet_makenetandmask(val, &su->sin); + return (0); + } + hp = gethostbyname(s); + if (hp) { + *hpp = hp; + su->sin.sin_family = hp->h_addrtype; + memmove(&su->sin.sin_addr, hp->h_addr, hp->h_length); + return (1); + } + (void) fprintf(stderr, "%s: bad value\n", s); + exit(1); +} + +int +x25_makemask() +{ + register char *cp; + + if ((rtm_addrs & RTA_NETMASK) == 0) { + rtm_addrs |= RTA_NETMASK; + for (cp = (char *)&so_mask.sx25.x25_net; + cp < &so_mask.sx25.x25_opts.op_flags; cp++) + *cp = -1; + so_mask.sx25.x25_len = (u_char)&(((sup)0)->sx25.x25_opts); + } + return 0; +} + +short ns_nullh[] = {0,0,0}; +short ns_bh[] = {-1,-1,-1}; + +char * +ns_print(sns) + struct sockaddr_ns *sns; +{ + struct ns_addr work; + union { union ns_net net_e; u_long long_e; } net; + u_short port; + static char mybuf[50], cport[10], chost[25]; + char *host = ""; + register char *p; + register u_char *q; + + work = sns->sns_addr; + port = ntohs(work.x_port); + work.x_port = 0; + net.net_e = work.x_net; + if (ns_nullhost(work) && net.long_e == 0) { + if (!port) + return ("*.*"); + (void) sprintf(mybuf, "*.%XH", port); + return (mybuf); + } + + if (memcmp(ns_bh, work.x_host.c_host, 6) == 0) + host = "any"; + else if (memcmp(ns_nullh, work.x_host.c_host, 6) == 0) + host = "*"; + else { + q = work.x_host.c_host; + (void) sprintf(chost, "%02X%02X%02X%02X%02X%02XH", + q[0], q[1], q[2], q[3], q[4], q[5]); + for (p = chost; *p == '0' && p < chost + 12; p++) + /* void */; + host = p; + } + if (port) + (void) sprintf(cport, ".%XH", htons(port)); + else + *cport = 0; + + (void) sprintf(mybuf,"%XH.%s%s", ntohl(net.long_e), host, cport); + return (mybuf); +} + +void +interfaces() +{ + size_t needed; + int mib[6]; + char *buf, *lim, *next; + register struct rt_msghdr *rtm; + + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; /* protocol */ + mib[3] = 0; /* wildcard address family */ + mib[4] = NET_RT_IFLIST; + mib[5] = 0; /* no flags */ + if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) + quit("route-sysctl-estimate"); + if ((buf = malloc(needed)) == NULL) + quit("malloc"); + if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) + quit("actual retrieval of interface table"); + lim = buf + needed; + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + print_rtmsg(rtm, rtm->rtm_msglen); + } +} + +void +monitor() +{ + int n; + char msg[2048]; + + verbose = 1; + if (debugonly) { + interfaces(); + exit(0); + } + for(;;) { + n = read(s, msg, 2048); + (void) printf("got message of size %d\n", n); + print_rtmsg((struct rt_msghdr *)msg, n); + } +} + +struct { + struct rt_msghdr m_rtm; + char m_space[512]; +} m_rtmsg; + +int +rtmsg(cmd, flags) + int cmd, flags; +{ + static int seq; + int rlen; + register char *cp = m_rtmsg.m_space; + register int l; + +#define NEXTADDR(w, u) \ + if (rtm_addrs & (w)) {\ + l = ROUNDUP(u.sa.sa_len); memmove(cp, &(u), l); cp += l;\ + if (verbose) sodump(&(u),"u");\ + } + + errno = 0; + memset(&m_rtmsg, 0, sizeof(m_rtmsg)); + if (cmd == 'a') + cmd = RTM_ADD; + else if (cmd == 'c') + cmd = RTM_CHANGE; + else if (cmd == 'g') { + cmd = RTM_GET; + if (so_ifp.sa.sa_family == 0) { + so_ifp.sa.sa_family = AF_LINK; + so_ifp.sa.sa_len = sizeof(struct sockaddr_dl); + rtm_addrs |= RTA_IFP; + } + } else + cmd = RTM_DELETE; +#define rtm m_rtmsg.m_rtm + rtm.rtm_type = cmd; + rtm.rtm_flags = flags; + rtm.rtm_version = RTM_VERSION; + rtm.rtm_seq = ++seq; + rtm.rtm_addrs = rtm_addrs; + rtm.rtm_rmx = rt_metrics; + rtm.rtm_inits = rtm_inits; + + if (rtm_addrs & RTA_NETMASK) + mask_addr(); + NEXTADDR(RTA_DST, so_dst); + NEXTADDR(RTA_GATEWAY, so_gate); + NEXTADDR(RTA_NETMASK, so_mask); + NEXTADDR(RTA_GENMASK, so_genmask); + NEXTADDR(RTA_IFP, so_ifp); + NEXTADDR(RTA_IFA, so_ifa); + rtm.rtm_msglen = l = cp - (char *)&m_rtmsg; + if (verbose) + print_rtmsg(&rtm, l); + if (debugonly) + return (0); + if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { + perror("writing to routing socket"); + return (-1); + } + if (cmd == RTM_GET) { + do { + l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); + } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid)); + if (l < 0) + (void) fprintf(stderr, + "route: read from routing socket: %s\n", + strerror(errno)); + else + print_getmsg(&rtm, l); + } +#undef rtm + return (0); +} + +void +mask_addr() +{ + int olen = so_mask.sa.sa_len; + register char *cp1 = olen + (char *)&so_mask, *cp2; + + for (so_mask.sa.sa_len = 0; cp1 > (char *)&so_mask; ) + if (*--cp1 != 0) { + so_mask.sa.sa_len = 1 + cp1 - (char *)&so_mask; + break; + } + if ((rtm_addrs & RTA_DST) == 0) + return; + switch (so_dst.sa.sa_family) { + case AF_NS: + case AF_INET: + case AF_CCITT: + case 0: + return; + case AF_ISO: + olen = MIN(so_dst.siso.siso_nlen, + MAX(so_mask.sa.sa_len - 6, 0)); + break; + } + cp1 = so_mask.sa.sa_len + 1 + (char *)&so_dst; + cp2 = so_dst.sa.sa_len + 1 + (char *)&so_dst; + while (cp2 > cp1) + *--cp2 = 0; + cp2 = so_mask.sa.sa_len + 1 + (char *)&so_mask; + while (cp1 > so_dst.sa.sa_data) + *--cp1 &= *--cp2; + switch (so_dst.sa.sa_family) { + case AF_ISO: + so_dst.siso.siso_nlen = olen; + break; + } +} + +char *msgtypes[] = { + "", + "RTM_ADD: Add Route", + "RTM_DELETE: Delete Route", + "RTM_CHANGE: Change Metrics or flags", + "RTM_GET: Report Metrics", + "RTM_LOSING: Kernel Suspects Partitioning", + "RTM_REDIRECT: Told to use different route", + "RTM_MISS: Lookup failed on this address", + "RTM_LOCK: fix specified metrics", + "RTM_OLDADD: caused by SIOCADDRT", + "RTM_OLDDEL: caused by SIOCDELRT", + "RTM_RESOLVE: Route created by cloning", + "RTM_NEWADDR: address being added to iface", + "RTM_DELADDR: address being removed from iface", + "RTM_IFINFO: iface status change", + 0, +}; + +char metricnames[] = +"\011pksent\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire\2hopcount\1mtu"; +char routeflags[] = +"\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010MASK_PRESENT\011CLONING\012XRESOLVE\013LLINFO\014STATIC\017PROTO2\020PROTO1"; +char ifnetflags[] = +"\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6NOTRAILERS\7RUNNING\010NOARP\011PPROMISC\012ALLMULTI\013OACTIVE\014SIMPLEX\015LINK0\016LINK1\017LINK2\020MULTICAST"; +char addrnames[] = +"\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD"; + +void +print_rtmsg(rtm, msglen) + register struct rt_msghdr *rtm; + int msglen; +{ + struct if_msghdr *ifm; + struct ifa_msghdr *ifam; + + if (verbose == 0) + return; + if (rtm->rtm_version != RTM_VERSION) { + (void) printf("routing message version %d not understood\n", + rtm->rtm_version); + return; + } + (void)printf("%s: len %d, ", msgtypes[rtm->rtm_type], rtm->rtm_msglen); + switch (rtm->rtm_type) { + case RTM_IFINFO: + ifm = (struct if_msghdr *)rtm; + (void) printf("if# %d, flags:", ifm->ifm_index); + bprintf(stdout, ifm->ifm_flags, ifnetflags); + pmsg_addrs((char *)(ifm + 1), ifm->ifm_addrs); + break; + case RTM_NEWADDR: + case RTM_DELADDR: + ifam = (struct ifa_msghdr *)rtm; + (void) printf("metric %d, flags:", ifam->ifam_metric); + bprintf(stdout, ifam->ifam_flags, routeflags); + pmsg_addrs((char *)(ifam + 1), ifam->ifam_addrs); + break; + default: + (void) printf("pid: %d, seq %d, errno %d, flags:", + rtm->rtm_pid, rtm->rtm_seq, rtm->rtm_errno); + bprintf(stdout, rtm->rtm_flags, routeflags); + pmsg_common(rtm); + } +} + +void +print_getmsg(rtm, msglen) + register struct rt_msghdr *rtm; + int msglen; +{ + struct sockaddr *dst = NULL, *gate = NULL, *mask = NULL; + struct sockaddr_dl *ifp = NULL; + register struct sockaddr *sa; + register char *cp; + register int i; + + (void) printf(" route to: %s\n", routename(&so_dst)); + if (rtm->rtm_version != RTM_VERSION) { + (void)fprintf(stderr, + "routing message version %d not understood\n", + rtm->rtm_version); + return; + } + if (rtm->rtm_msglen > msglen) { + (void)fprintf(stderr, + "message length mismatch, in packet %d, returned %d\n", + rtm->rtm_msglen, msglen); + } + if (rtm->rtm_errno) { + (void) fprintf(stderr, "RTM_GET: %s (errno %d)\n", + strerror(rtm->rtm_errno), rtm->rtm_errno); + return; + } + cp = ((char *)(rtm + 1)); + if (rtm->rtm_addrs) + for (i = 1; i; i <<= 1) + if (i & rtm->rtm_addrs) { + sa = (struct sockaddr *)cp; + switch (i) { + case RTA_DST: + dst = sa; + break; + case RTA_GATEWAY: + gate = sa; + break; + case RTA_NETMASK: + mask = sa; + break; + case RTA_IFP: + if (sa->sa_family == AF_LINK && + ((struct sockaddr_dl *)sa)->sdl_nlen) + ifp = (struct sockaddr_dl *)sa; + break; + } + ADVANCE(cp, sa); + } + if (dst && mask) + mask->sa_family = dst->sa_family; /* XXX */ + if (dst) + (void)printf("destination: %s\n", routename(dst)); + if (mask) { + int savenflag = nflag; + + nflag = 1; + (void)printf(" mask: %s\n", routename(mask)); + nflag = savenflag; + } + if (gate && rtm->rtm_flags & RTF_GATEWAY) + (void)printf(" gateway: %s\n", routename(gate)); + if (ifp) + (void)printf(" interface: %.*s\n", + ifp->sdl_nlen, ifp->sdl_data); + (void)printf(" flags: "); + bprintf(stdout, rtm->rtm_flags, routeflags); + +#define lock(f) ((rtm->rtm_rmx.rmx_locks & __CONCAT(RTV_,f)) ? 'L' : ' ') +#define msec(u) (((u) + 500) / 1000) /* usec to msec */ + + (void) printf("\n%s\n", "\ + recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire"); + printf("%8d%c ", rtm->rtm_rmx.rmx_recvpipe, lock(RPIPE)); + printf("%8d%c ", rtm->rtm_rmx.rmx_sendpipe, lock(SPIPE)); + printf("%8d%c ", rtm->rtm_rmx.rmx_ssthresh, lock(SSTHRESH)); + printf("%8d%c ", msec(rtm->rtm_rmx.rmx_rtt), lock(RTT)); + printf("%8d%c ", msec(rtm->rtm_rmx.rmx_rttvar), lock(RTTVAR)); + printf("%8d%c ", rtm->rtm_rmx.rmx_hopcount, lock(HOPCOUNT)); + printf("%8d%c ", rtm->rtm_rmx.rmx_mtu, lock(MTU)); + if (rtm->rtm_rmx.rmx_expire) + rtm->rtm_rmx.rmx_expire -= time(0); + printf("%8d%c\n", rtm->rtm_rmx.rmx_expire, lock(EXPIRE)); +#undef lock +#undef msec +#define RTA_IGN (RTA_DST|RTA_GATEWAY|RTA_NETMASK|RTA_IFP|RTA_IFA|RTA_BRD) + if (verbose) + pmsg_common(rtm); + else if (rtm->rtm_addrs &~ RTA_IGN) { + (void) printf("sockaddrs: "); + bprintf(stdout, rtm->rtm_addrs, addrnames); + putchar('\n'); + } +#undef RTA_IGN +} + +void +pmsg_common(rtm) + register struct rt_msghdr *rtm; +{ + (void) printf("\nlocks: "); + bprintf(stdout, rtm->rtm_rmx.rmx_locks, metricnames); + (void) printf(" inits: "); + bprintf(stdout, rtm->rtm_inits, metricnames); + pmsg_addrs(((char *)(rtm + 1)), rtm->rtm_addrs); +} + +void +pmsg_addrs(cp, addrs) + char *cp; + int addrs; +{ + register struct sockaddr *sa; + int i; + + if (addrs == 0) + return; + (void) printf("\nsockaddrs: "); + bprintf(stdout, addrs, addrnames); + (void) putchar('\n'); + for (i = 1; i; i <<= 1) + if (i & addrs) { + sa = (struct sockaddr *)cp; + (void) printf(" %s", routename(sa)); + ADVANCE(cp, sa); + } + (void) putchar('\n'); + (void) fflush(stdout); +} + +void +bprintf(fp, b, s) + register FILE *fp; + register int b; + register u_char *s; +{ + register int i; + int gotsome = 0; + + if (b == 0) + return; + while (i = *s++) { + if (b & (1 << (i-1))) { + if (gotsome == 0) + i = '<'; + else + i = ','; + (void) putc(i, fp); + gotsome = 1; + for (; (i = *s) > 32; s++) + (void) putc(i, fp); + } else + while (*s > 32) + s++; + } + if (gotsome) + (void) putc('>', fp); +} + +int +keyword(cp) + char *cp; +{ + register struct keytab *kt = keywords; + + while (kt->kt_cp && strcmp(kt->kt_cp, cp)) + kt++; + return kt->kt_i; +} + +void +sodump(su, which) + register sup su; + char *which; +{ + switch (su->sa.sa_family) { + case AF_LINK: + (void) printf("%s: link %s; ", + which, link_ntoa(&su->sdl)); + break; + case AF_ISO: + (void) printf("%s: iso %s; ", + which, iso_ntoa(&su->siso.siso_addr)); + break; + case AF_INET: + (void) printf("%s: inet %s; ", + which, inet_ntoa(su->sin.sin_addr)); + break; + case AF_NS: + (void) printf("%s: xns %s; ", + which, ns_ntoa(su->sns.sns_addr)); + break; + } + (void) fflush(stdout); +} + +/* States*/ +#define VIRGIN 0 +#define GOTONE 1 +#define GOTTWO 2 +/* Inputs */ +#define DIGIT (4*0) +#define END (4*1) +#define DELIM (4*2) + +void +sockaddr(addr, sa) + register char *addr; + register struct sockaddr *sa; +{ + register char *cp = (char *)sa; + int size = sa->sa_len; + char *cplim = cp + size; + register int byte = 0, state = VIRGIN, new; + + memset(cp, 0, size); + cp++; + do { + if ((*addr >= '0') && (*addr <= '9')) { + new = *addr - '0'; + } else if ((*addr >= 'a') && (*addr <= 'f')) { + new = *addr - 'a' + 10; + } else if ((*addr >= 'A') && (*addr <= 'F')) { + new = *addr - 'A' + 10; + } else if (*addr == 0) + state |= END; + else + state |= DELIM; + addr++; + switch (state /* | INPUT */) { + case GOTTWO | DIGIT: + *cp++ = byte; /*FALLTHROUGH*/ + case VIRGIN | DIGIT: + state = GOTONE; byte = new; continue; + case GOTONE | DIGIT: + state = GOTTWO; byte = new + (byte << 4); continue; + default: /* | DELIM */ + state = VIRGIN; *cp++ = byte; byte = 0; continue; + case GOTONE | END: + case GOTTWO | END: + *cp++ = byte; /* FALLTHROUGH */ + case VIRGIN | END: + break; + } + break; + } while (cp < cplim); + sa->sa_len = cp - (char *)sa; +} diff --git a/sbin/savecore/savecore.c b/sbin/savecore/savecore.c new file mode 100644 index 000000000000..a377fdc5a2a6 --- /dev/null +++ b/sbin/savecore/savecore.c @@ -0,0 +1,649 @@ +/*- + * Copyright (c) 1986, 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1986, 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)savecore.c 8.5 (Berkeley) 4/28/95"; +#endif /* not lint */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ok(number) ((number) - KERNBASE) + +struct nlist current_nl[] = { /* Namelist for currently running system. */ +#define X_DUMPDEV 0 + { "_dumpdev" }, +#define X_DUMPLO 1 + { "_dumplo" }, +#define X_TIME 2 + { "_time" }, +#define X_DUMPSIZE 3 + { "_dumpsize" }, +#define X_VERSION 4 + { "_version" }, +#define X_PANICSTR 5 + { "_panicstr" }, +#define X_DUMPMAG 6 + { "_dumpmag" }, + { "" }, +}; +int cursyms[] = { X_DUMPDEV, X_DUMPLO, X_VERSION, X_DUMPMAG, -1 }; +int dumpsyms[] = { X_TIME, X_DUMPSIZE, X_VERSION, X_PANICSTR, X_DUMPMAG, -1 }; + +struct nlist dump_nl[] = { /* Name list for dumped system. */ + { "_dumpdev" }, /* Entries MUST be the same as */ + { "_dumplo" }, /* those in current_nl[]. */ + { "_time" }, + { "_dumpsize" }, + { "_version" }, + { "_panicstr" }, + { "_dumpmag" }, + { "" }, +}; + +/* Types match kernel declarations. */ +long dumplo; /* where dump starts on dumpdev */ +int dumpmag; /* magic number in dump */ +int dumpsize; /* amount of memory dumped */ + +char *vmunix; +char *dirname; /* directory to save dumps in */ +char *ddname; /* name of dump device */ +dev_t dumpdev; /* dump device */ +int dumpfd; /* read/write descriptor on block dev */ +time_t now; /* current date */ +char panic_mesg[1024]; +int panicstr; +char vers[1024]; + +int clear, compress, force, verbose; /* flags */ + +void check_kmem __P((void)); +int check_space __P((void)); +void clear_dump __P((void)); +int Create __P((char *, int)); +int dump_exists __P((void)); +char *find_dev __P((dev_t, int)); +int get_crashtime __P((void)); +void kmem_setup __P((void)); +void log __P((int, char *, ...)); +void Lseek __P((int, off_t, int)); +int Open __P((char *, int rw)); +int Read __P((int, void *, int)); +char *rawname __P((char *s)); +void save_core __P((void)); +void usage __P((void)); +void Write __P((int, void *, int)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch; + + openlog("savecore", LOG_PERROR, LOG_DAEMON); + + while ((ch = getopt(argc, argv, "cdfN:vz")) != EOF) + switch(ch) { + case 'c': + clear = 1; + break; + case 'd': /* Not documented. */ + case 'v': + verbose = 1; + break; + case 'f': + force = 1; + break; + case 'N': + vmunix = optarg; + break; + case 'z': + compress = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (!clear) { + if (argc != 1 && argc != 2) + usage(); + dirname = argv[0]; + } + if (argc == 2) + vmunix = argv[1]; + + (void)time(&now); + kmem_setup(); + + if (clear) { + clear_dump(); + exit(0); + } + + if (!dump_exists() && !force) + exit(1); + + check_kmem(); + + if (panicstr) + syslog(LOG_ALERT, "reboot after panic: %s", panic_mesg); + else + syslog(LOG_ALERT, "reboot"); + + if ((!get_crashtime() || !check_space()) && !force) + exit(1); + + save_core(); + + clear_dump(); + exit(0); +} + +void +kmem_setup() +{ + FILE *fp; + int kmem, i; + char *dump_sys; + + /* + * Some names we need for the currently running system, others for + * the system that was running when the dump was made. The values + * obtained from the current system are used to look for things in + * /dev/kmem that cannot be found in the dump_sys namelist, but are + * presumed to be the same (since the disk partitions are probably + * the same!) + */ + if ((nlist(_PATH_UNIX, current_nl)) == -1) + syslog(LOG_ERR, "%s: nlist: %s", _PATH_UNIX, strerror(errno)); + for (i = 0; cursyms[i] != -1; i++) + if (current_nl[cursyms[i]].n_value == 0) { + syslog(LOG_ERR, "%s: %s not in namelist", + _PATH_UNIX, current_nl[cursyms[i]].n_name); + exit(1); + } + + dump_sys = vmunix ? vmunix : _PATH_UNIX; + if ((nlist(dump_sys, dump_nl)) == -1) + syslog(LOG_ERR, "%s: nlist: %s", dump_sys, strerror(errno)); + for (i = 0; dumpsyms[i] != -1; i++) + if (dump_nl[dumpsyms[i]].n_value == 0) { + syslog(LOG_ERR, "%s: %s not in namelist", + dump_sys, dump_nl[dumpsyms[i]].n_name); + exit(1); + } + + kmem = Open(_PATH_KMEM, O_RDONLY); + Lseek(kmem, (off_t)current_nl[X_DUMPDEV].n_value, L_SET); + (void)Read(kmem, &dumpdev, sizeof(dumpdev)); + if (dumpdev == NODEV) { + syslog(LOG_WARNING, "no core dump (no dumpdev)"); + exit(1); + } + Lseek(kmem, (off_t)current_nl[X_DUMPLO].n_value, L_SET); + (void)Read(kmem, &dumplo, sizeof(dumplo)); + if (verbose) + (void)printf("dumplo = %d (%d * %d)\n", + dumplo, dumplo/DEV_BSIZE, DEV_BSIZE); + Lseek(kmem, (off_t)current_nl[X_DUMPMAG].n_value, L_SET); + (void)Read(kmem, &dumpmag, sizeof(dumpmag)); + dumplo *= DEV_BSIZE; + ddname = find_dev(dumpdev, S_IFBLK); + dumpfd = Open(ddname, O_RDWR); + fp = fdopen(kmem, "r"); + if (fp == NULL) { + syslog(LOG_ERR, "%s: fdopen: %m", _PATH_KMEM); + exit(1); + } + if (vmunix) + return; + (void)fseek(fp, (off_t)current_nl[X_VERSION].n_value, L_SET); + (void)fgets(vers, sizeof(vers), fp); + + /* Don't fclose(fp), we use dumpfd later. */ +} + +void +check_kmem() +{ + register char *cp; + FILE *fp; + char core_vers[1024]; + + fp = fdopen(dumpfd, "r"); + if (fp == NULL) { + syslog(LOG_ERR, "%s: fdopen: %m", ddname); + exit(1); + } + fseek(fp, (off_t)(dumplo + ok(dump_nl[X_VERSION].n_value)), L_SET); + fgets(core_vers, sizeof(core_vers), fp); + if (strcmp(vers, core_vers) && vmunix == 0) + syslog(LOG_WARNING, + "warning: %s version mismatch:\n\t%s\nand\t%s\n", + _PATH_UNIX, vers, core_vers); + (void)fseek(fp, + (off_t)(dumplo + ok(dump_nl[X_PANICSTR].n_value)), L_SET); + (void)fread(&panicstr, sizeof(panicstr), 1, fp); + if (panicstr) { + (void)fseek(fp, dumplo + ok(panicstr), L_SET); + cp = panic_mesg; + do + *cp = getc(fp); + while (*cp++ && cp < &panic_mesg[sizeof(panic_mesg)]); + } + /* Don't fclose(fp), we use dumpfd later. */ +} + +void +clear_dump() +{ + long newdumplo; + + newdumplo = 0; + Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET); + Write(dumpfd, &newdumplo, sizeof(newdumplo)); +} + +int +dump_exists() +{ + int newdumpmag; + + Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET); + (void)Read(dumpfd, &newdumpmag, sizeof(newdumpmag)); + if (newdumpmag != dumpmag) { + if (verbose) + syslog(LOG_WARNING, "magic number mismatch (%x != %x)", + newdumpmag, dumpmag); + syslog(LOG_WARNING, "no core dump"); + return (0); + } + return (1); +} + +char buf[1024 * 1024]; + +void +save_core() +{ + register FILE *fp; + register int bounds, ifd, nr, nw, ofd; + char *rawp, path[MAXPATHLEN]; + + /* + * Get the current number and update the bounds file. Do the update + * now, because may fail later and don't want to overwrite anything. + */ + (void)snprintf(path, sizeof(path), "%s/bounds", dirname); + if ((fp = fopen(path, "r")) == NULL) + goto err1; + if (fgets(buf, sizeof(buf), fp) == NULL) { + if (ferror(fp)) +err1: syslog(LOG_WARNING, "%s: %s", path, strerror(errno)); + bounds = 0; + } else + bounds = atoi(buf); + if (fp != NULL) + (void)fclose(fp); + if ((fp = fopen(path, "w")) == NULL) + syslog(LOG_ERR, "%s: %m", path); + else { + (void)fprintf(fp, "%d\n", bounds + 1); + (void)fclose(fp); + } + (void)fclose(fp); + + /* Create the core file. */ + (void)snprintf(path, sizeof(path), "%s/vmcore.%d%s", + dirname, bounds, compress ? ".Z" : ""); + if (compress) { + if ((fp = zopen(path, "w", 0)) == NULL) { + syslog(LOG_ERR, "%s: %s", path, strerror(errno)); + exit(1); + } + } else + ofd = Create(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + /* Open the raw device. */ + rawp = rawname(ddname); + if ((ifd = open(rawp, O_RDONLY)) == -1) { + syslog(LOG_WARNING, "%s: %m; using block device", rawp); + ifd = dumpfd; + } + + /* Read the dump size. */ + Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPSIZE].n_value)), L_SET); + (void)Read(dumpfd, &dumpsize, sizeof(dumpsize)); + + /* Seek to the start of the core. */ + Lseek(ifd, (off_t)dumplo, L_SET); + + /* Copy the core file. */ + dumpsize *= NBPG; + syslog(LOG_NOTICE, "writing %score to %s", + compress ? "compressed " : "", path); + for (; dumpsize > 0; dumpsize -= nr) { + (void)printf("%6dK\r", dumpsize / 1024); + (void)fflush(stdout); + nr = read(ifd, buf, MIN(dumpsize, sizeof(buf))); + if (nr <= 0) { + if (nr == 0) + syslog(LOG_WARNING, + "WARNING: EOF on dump device"); + else + syslog(LOG_ERR, "%s: %m", rawp); + goto err2; + } + if (compress) + nw = fwrite(buf, 1, nr, fp); + else + nw = write(ofd, buf, nr); + if (nw != nr) { + syslog(LOG_ERR, "%s: %s", + path, strerror(nw == 0 ? EIO : errno)); +err2: syslog(LOG_WARNING, + "WARNING: vmcore may be incomplete"); + (void)printf("\n"); + exit(1); + } + } + (void)printf("\n"); + (void)close(ifd); + if (compress) + (void)fclose(fp); + else + (void)close(ofd); + + /* Copy the kernel. */ + ifd = Open(vmunix ? vmunix : _PATH_UNIX, O_RDONLY); + (void)snprintf(path, sizeof(path), "%s/vmunix.%d%s", + dirname, bounds, compress ? ".Z" : ""); + if (compress) { + if ((fp = zopen(path, "w", 0)) == NULL) { + syslog(LOG_ERR, "%s: %s", path, strerror(errno)); + exit(1); + } + } else + ofd = Create(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + syslog(LOG_NOTICE, "writing %skernel to %s", + compress ? "compressed " : "", path); + while ((nr = read(ifd, buf, sizeof(buf))) > 0) { + if (compress) + nw = fwrite(buf, 1, nr, fp); + else + nw = write(ofd, buf, nr); + if (nw != nr) { + syslog(LOG_ERR, "%s: %s", + path, strerror(nw == 0 ? EIO : errno)); + syslog(LOG_WARNING, + "WARNING: vmunix may be incomplete"); + exit(1); + } + } + if (nr < 0) { + syslog(LOG_ERR, "%s: %s", + vmunix ? vmunix : _PATH_UNIX, strerror(errno)); + syslog(LOG_WARNING, + "WARNING: vmunix may be incomplete"); + exit(1); + } + if (compress) + (void)fclose(fp); + else + (void)close(ofd); +} + +char * +find_dev(dev, type) + register dev_t dev; + register int type; +{ + register DIR *dfd; + struct dirent *dir; + struct stat sb; + char *dp, devname[MAXPATHLEN + 1]; + + if ((dfd = opendir(_PATH_DEV)) == NULL) { + syslog(LOG_ERR, "%s: %s", _PATH_DEV, strerror(errno)); + exit(1); + } + (void)strcpy(devname, _PATH_DEV); + while ((dir = readdir(dfd))) { + (void)strcpy(devname + sizeof(_PATH_DEV) - 1, dir->d_name); + if (lstat(devname, &sb)) { + syslog(LOG_ERR, "%s: %s", devname, strerror(errno)); + continue; + } + if ((sb.st_mode & S_IFMT) != type) + continue; + if (dev == sb.st_rdev) { + closedir(dfd); + if ((dp = strdup(devname)) == NULL) { + syslog(LOG_ERR, "%s", strerror(errno)); + exit(1); + } + return (dp); + } + } + closedir(dfd); + syslog(LOG_ERR, "can't find device %d/%d", major(dev), minor(dev)); + exit(1); +} + +char * +rawname(s) + char *s; +{ + char *sl, name[MAXPATHLEN]; + + if ((sl = strrchr(s, '/')) == NULL || sl[1] == '0') { + syslog(LOG_ERR, + "can't make raw dump device name from %s", s); + return (s); + } + (void)snprintf(name, sizeof(name), "%.*s/r%s", sl - s, s, sl + 1); + if ((sl = strdup(name)) == NULL) { + syslog(LOG_ERR, "%s", strerror(errno)); + exit(1); + } + return (sl); +} + +int +get_crashtime() +{ + time_t dumptime; /* Time the dump was taken. */ + + Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_TIME].n_value)), L_SET); + (void)Read(dumpfd, &dumptime, sizeof(dumptime)); + if (dumptime == 0) { + if (verbose) + syslog(LOG_ERR, "dump time is zero"); + return (0); + } + (void)printf("savecore: system went down at %s", ctime(&dumptime)); +#define LEEWAY (7 * SECSPERDAY) + if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) { + (void)printf("dump time is unreasonable\n"); + return (0); + } + return (1); +} + +int +check_space() +{ + register FILE *fp; + char *tvmunix; + off_t minfree, spacefree, vmunixsize, needed; + struct stat st; + struct statfs fsbuf; + char buf[100], path[MAXPATHLEN]; + + tvmunix = vmunix ? vmunix : _PATH_UNIX; + if (stat(tvmunix, &st) < 0) { + syslog(LOG_ERR, "%s: %m", tvmunix); + exit(1); + } + vmunixsize = st.st_blocks * S_BLKSIZE; + if (statfs(dirname, &fsbuf) < 0) { + syslog(LOG_ERR, "%s: %m", dirname); + exit(1); + } + spacefree = (fsbuf.f_bavail * fsbuf.f_bsize) / 1024; + + (void)snprintf(path, sizeof(path), "%s/minfree", dirname); + if ((fp = fopen(path, "r")) == NULL) + minfree = 0; + else { + if (fgets(buf, sizeof(buf), fp) == NULL) + minfree = 0; + else + minfree = atoi(buf); + (void)fclose(fp); + } + + needed = (dumpsize + vmunixsize) / 1024; + if (minfree > 0 && spacefree - needed < minfree) { + syslog(LOG_WARNING, + "no dump, not enough free space on device"); + return (0); + } + if (spacefree - needed < minfree) + syslog(LOG_WARNING, + "dump performed, but free space threshold crossed"); + return (1); +} + +int +Open(name, rw) + char *name; + int rw; +{ + int fd; + + if ((fd = open(name, rw, 0)) < 0) { + syslog(LOG_ERR, "%s: %m", name); + exit(1); + } + return (fd); +} + +int +Read(fd, bp, size) + int fd, size; + void *bp; +{ + int nr; + + nr = read(fd, bp, size); + if (nr != size) { + syslog(LOG_ERR, "read: %m"); + exit(1); + } + return (nr); +} + +void +Lseek(fd, off, flag) + int fd, flag; + off_t off; +{ + off_t ret; + + ret = lseek(fd, off, flag); + if (ret == -1) { + syslog(LOG_ERR, "lseek: %m"); + exit(1); + } +} + +int +Create(file, mode) + char *file; + int mode; +{ + register int fd; + + fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, mode); + if (fd < 0) { + syslog(LOG_ERR, "%s: %m", file); + exit(1); + } + return (fd); +} + +void +Write(fd, bp, size) + int fd, size; + void *bp; +{ + int n; + + if ((n = write(fd, bp, size)) < size) { + syslog(LOG_ERR, "write: %s", strerror(n == -1 ? errno : EIO)); + exit(1); + } +} + +void +usage() +{ + (void)syslog(LOG_ERR, "usage: savecore [-cfvz] [-N system] directory"); + exit(1); +} diff --git a/sbin/scsiformat/scsiformat.c b/sbin/scsiformat/scsiformat.c new file mode 100644 index 000000000000..a185a8ee154c --- /dev/null +++ b/sbin/scsiformat/scsiformat.c @@ -0,0 +1,664 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)scsiformat.c 5.5 (Berkeley) 4/2/94 + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)scsiformat.c 5.5 (Berkeley) 4/2/94"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include + +#define COMPAT_HPSCSI + +#include +#include +#include +#include +#include +#include + +int fd; +char *device; + +void scsi_str __P((char *, char *, int)); +void do_command __P((int, struct scsi_cdb *, void *, int)); +void do_format __P((void)); +void print_capacity __P((void)); +void print_inquiry __P((void)); +void prflags __P((int, const char *)); +u_char *print_mode_page __P((u_char *)); +void print_mode_sense __P((void)); +void usage __P((void)); + +#define N2(c, d) (((c) << 8) | (d)) +#define N3(b, c, d) (((b) << 16) | N2(c, d)) +#define N4(a, b, c, d) (((a) << 24) | N3(b, c, d)) + +int sense_pctl; + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; + int ch, readonly; + + readonly = 0; + sense_pctl = SCSI_MSENSE_PCTL_CUR; + while ((ch = getopt(argc, argv, "rp:")) != EOF) { + switch(ch) { + case 'r': + readonly = 1; + break; + case 'p': /* mode sense page control */ + switch (*optarg) { + case 'c': + sense_pctl = SCSI_MSENSE_PCTL_CUR; + break; + case 'd': + sense_pctl = SCSI_MSENSE_PCTL_DFLT; + break; + case 's': + sense_pctl = SCSI_MSENSE_PCTL_SAVED; + break; + case 'v': + (void)printf( + "*** note: for variable parameters, 1-bit means ``can write here''\n"); + sense_pctl = SCSI_MSENSE_PCTL_VAR; + break; + } + /* FALLTHROUGH */ + case '?': + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc != 1) + usage(); + + device = *argv; + fd = open(device, readonly ? O_RDONLY : O_RDWR, 0); + if (fd < 0) { + (void)fprintf(stderr, + "scsiformat: %s: %s\n", device, strerror(errno)); + exit(1); + } + print_inquiry(); + print_capacity(); + print_mode_sense(); + + if (!readonly) + do_format(); + exit(0); +} + +/* + * Copy a counted string, trimming trailing blanks, and turning the + * result into a C-style string. + */ +void +scsi_str(src, dst, len) + register char *src, *dst; + register int len; +{ + + while (src[len - 1] == ' ') { + if (--len == 0) { + *dst = 0; + return; + } + } + bcopy(src, dst, len); + dst[len] = 0; +} + +void +print_inquiry() +{ + register struct scsi_inq_ansi *si; + int ver; + struct scsi_inquiry inqbuf; + char vendor[10], product[17], rev[5]; + static struct scsi_cdb inq = { + CMD_INQUIRY, 0, 0, 0, sizeof(inqbuf), 0 + }; + + do_command(fd, &inq, &inqbuf, sizeof(inqbuf)); + (void)printf("%s: ", device); + + ver = (inqbuf.si_version >> VER_ANSI_SHIFT) & VER_ANSI_MASK; + if (ver != 1 && ver != 2) { + (void)printf("type 0x%x, qual 0x%x, ver 0x%x (ansi %d)\n", + inqbuf.si_type, inqbuf.si_qual, inqbuf.si_version, ver); + return; + } + si = (struct scsi_inq_ansi *)&inqbuf; + switch (si->si_type & TYPE_TYPE_MASK) { + + case TYPE_DAD: + (void)printf("(disk)"); + break; + + case TYPE_WORM: + (void)printf("(WORM)"); + break; + + case TYPE_ROM: + (void)printf("(CD-ROM)"); + break; + + case TYPE_MO: + (void)printf("(MO-DISK)"); + break; + + case TYPE_JUKEBOX: + (void)printf("(jukebox)"); + break; + + default: + (void)printf("(??)"); + break; + } + scsi_str(si->si_vendor, vendor, sizeof(si->si_vendor)); + scsi_str(si->si_product, product, sizeof(si->si_product)); + scsi_str(si->si_rev, rev, sizeof(si->si_rev)); + (void)printf(" %s %s rev %s:", vendor, product, rev); +} + +void +print_capacity() +{ + struct scsi_rc rc; /* for READ CAPACITY */ + static struct scsi_cdb cap = { CMD_READ_CAPACITY }; + + do_command(fd, &cap, &rc, sizeof(rc)); + (void)printf(" %d blocks of %d bytes each\n", + N4(rc.rc_lbah, rc.rc_lbahm, rc.rc_lbalm, rc.rc_lbal) + 1, + N4(rc.rc_blh, rc.rc_blhm, rc.rc_bllm, rc.rc_bll)); +} + +void +print_mode_sense() +{ + register u_char *cp, *ep; + register struct scsi_ms_bd *bd; + register int n, i, l, len, bdlen; +#ifdef TEN_BYTE_SENSE + struct { + struct scsi_ms10 ms; + u_char p[1023 - sizeof(struct scsi_ms10)]; + } msbuf; + static struct scsi_cdb modesense = { + CMD_MODE_SENSE10, SCSI_MSENSE_DBD, 0, 0, 0, 0, 0, + sizeof(msbuf) >> 8, sizeof (msbuf), 0 + }; + + CDB10(&modesense)->cdb_lbam = sense_pctl | SCSI_MS_PC_ALL; + do_command(fd, &modesense, &msbuf, sizeof(msbuf)); + len = N2(msbuf.ms.ms_lenh, msbuf.ms.ms_lenl); + bdlen = N2(msbuf.ms.ms_bdlh, msbuf.ms.ms_bdll); +#else + struct { + struct scsi_ms6 ms; + u_char p[255 - sizeof(struct scsi_ms6)]; + } msbuf; + static struct scsi_cdb modesense = { + CMD_MODE_SENSE6, 0, 0, 0, sizeof(msbuf), 0 + }; + + CDB6(&modesense)->cdb_lbam = sense_pctl | SCSI_MS_PC_ALL; + do_command(fd, &modesense, &msbuf, sizeof(msbuf)); + len = msbuf.ms.ms_len; + bdlen = msbuf.ms.ms_bdl; +#endif + (void)printf("\n%d bytes of mode sense data. ", len); + (void)printf("medium type 0x%x, %swrite protected\n", + msbuf.ms.ms_mt, msbuf.ms.ms_dsp & SCSI_MS_DSP_WP ? "" : "not "); + if ((n = bdlen) != 0) { + bd = (struct scsi_ms_bd *)msbuf.p; + for (n /= sizeof(*bd); --n >= 0; bd++) { + (void)printf("\tdensity code 0x%x, ", bd->bd_dc); + i = N3(bd->bd_nbh, bd->bd_nbm, bd->bd_nbl); + l = N3(bd->bd_blh, bd->bd_blm, bd->bd_bll); + if (i) + (void)printf("%d blocks of length %d\n", i, l); + else + (void)printf("all blocks of length %d\n", l); + } + } + /* + * Sense header lengths includes the sense header, while mode page + * lengths do not ... let's hear it for consistency! + */ + cp = msbuf.p + bdlen; + ep = msbuf.p + len - sizeof(msbuf.ms); + while (cp < ep) + cp = print_mode_page(cp); +} + +void +prflags(v, cp) + int v; + register const char *cp; +{ + register const char *np; + char f, sep; + + for (sep = '<'; (f = *cp++) != 0; cp = np) { + for (np = cp; *np >= ' ';) + np++; + if ((v & (1 << (f - 1))) == 0) + continue; + printf("%c%.*s", sep, np - cp, cp); + sep = ','; + } + if (sep != '<') + putchar('>'); +} + +static char * +cache_policy(x) + int x; +{ + static char rsvd[30]; + + switch (x) { + + case SCSI_CACHE_DEFAULT: + return ("default"); + + case SCSI_CACHE_KEEPPF: + return ("toss cmd data, save prefetch"); + + case SCSI_CACHE_KEEPCMD: + return ("toss prefetch data, save cmd"); + + default: + (void)sprintf(rsvd, "reserved %d", x); + return (rsvd); + } + /* NOTREACHED */ +} + +u_char * +print_mode_page(cp) + u_char *cp; +{ + register struct scsi_ms_page_hdr *mp; + int len, code, i; + u_char *tp; + const char *s; + + mp = (struct scsi_ms_page_hdr *)cp; + code = mp->mp_psc & SCSI_MS_PC_MASK; + len = mp->mp_len; + (void)printf("\npage type %d%s (%d bytes): ", + code, mp->mp_psc & SCSI_MS_MP_SAVEABLE ? " (saveable)" : "", len); + switch (code) { + + case SCSI_MS_PC_RWERRREC: +#define rw ((struct scsi_page_rwerrrec *)(mp + 1)) + (void)printf("Read/Write Error Recovery parameters.\n"); + (void)printf("\tflags = 0x%x", rw->rw_flags); + prflags(rw->rw_flags, + "\10AWRE\7ARRE\6TB\5RC\4EER\3PER\2DTE\1DCR"); + (void)printf(",\n\t%d read retries, %d correction span bits,\n", + rw->rw_read_retry, rw->rw_corr_span); + (void)printf("\t%d head offsets, %d data strobe offsets%s\n", + rw->rw_hd_off, rw->rw_ds_off, len > 6 ? "," : "."); + if (len <= 6) + break; + (void)printf("\t%d write retries, ", rw->rw_write_retry); + i = N2(rw->rw_rtlh, rw->rw_rtll); + if (i != 0xffff) + (void)printf("%d", i); + else + (void)printf("no"); + (void)printf(" recovery time limit.\n"); + break; +#undef rw + + case SCSI_MS_PC_DR: +#define dr ((struct scsi_page_dr *)(mp + 1)) + (void)printf("Disconnect/Reconnect control.\n"); + (void)printf("\tbuffer full ratio %d, buffer empty ratio %d,\n", + dr->dr_full, dr->dr_empty); + (void)printf("\ttime limits: %d bus inactivity, ", + N2(dr->dr_inacth, dr->dr_inactl)); + (void)printf("%d disconnect, %d connect.\n", + N2(dr->dr_disconh, dr->dr_disconl), + N2(dr->dr_conh, dr->dr_conl)); + (void)printf("\tmaximum burst size %d,\n", + N2(dr->dr_bursth, dr->dr_burstl)); + switch (dr->dr_dtdc & SCSI_DR_DTDC_MASK) { + case SCSI_DR_DTDC_NONE: + s = "never"; + break; + case SCSI_DR_DTDC_NOTDATA: + s = "during data transfer"; + break; + case SCSI_DR_DTDC_RSVD: + s = "???"; + break; + case SCSI_DR_DTDC_NOTD2: + s = "during and after data transfer"; + break; + } + (void)printf("\tsuppress disconnect %s.\n", s); + break; +#undef dr + + case SCSI_MS_PC_FMT: +#define fmt ((struct scsi_page_fmt *)(mp + 1)) + (void)printf("Format parameters.\n"); + (void)printf("\t%d tracks/zone, %d alt.sect./zone, ", + N2(fmt->fmt_tpzh, fmt->fmt_tpzl), + N2(fmt->fmt_aspzh, fmt->fmt_aspzl)); + (void)printf("%d alt.tracks/zone,\n\t%d alt.tracks/vol., ", + N2(fmt->fmt_atpzh, fmt->fmt_atpzl), + N2(fmt->fmt_atpvh, fmt->fmt_atpvl)); + (void)printf("%d sectors/track, %d bytes/phys.sector,\n", + N2(fmt->fmt_spth, fmt->fmt_sptl), + N2(fmt->fmt_dbppsh, fmt->fmt_dbppsl)); + (void)printf("\tinterleave %d, track skew %d, cyl.skew %d,\n", + N2(fmt->fmt_ilh, fmt->fmt_ill), + N2(fmt->fmt_tsfh, fmt->fmt_tsfl), + N2(fmt->fmt_csfh, fmt->fmt_csfl)); + (void)printf("\tdrive flags 0x%x", fmt->fmt_flags); + prflags(fmt->fmt_flags, "\10SSEC\7HSEC\6RMB\5SURF"); + (void)printf(".\n"); + break; +#undef fmt + + case SCSI_MS_PC_RDGEOM: +#define rd ((struct scsi_page_rdgeom *)(mp + 1)) + (void)printf("Disk Geometry parameters.\n"); + (void)printf("\t%d cylinders, %d heads,\n", + N3(rd->rd_ncylh, rd->rd_ncylm, rd->rd_ncyll), + rd->rd_nheads); + (void)printf("\tstart write precompensation at cyl %d,\n", + N3(rd->rd_wpcylh, rd->rd_wpcylm, rd->rd_wpcyll)); + (void)printf("\tstart reduced write current at cyl %d,\n", + N3(rd->rd_rwcylh, rd->rd_rwcylm, rd->rd_rwcyll)); + (void)printf("\tseek step rate %f us, landing zone cyl %d,\n", + N2(rd->rd_steph, rd->rd_stepl) * 0.1, + N3(rd->rd_lcylh, rd->rd_lcylm, rd->rd_lcyll)); + switch (rd->rd_rpl & SCSI_RD_RPL_MASK) { + case SCSI_RD_RPL_NONE: + s = "disabled or unsupported"; + break; + case SCSI_RD_RPL_SLAVE: + s = "slave"; + break; + case SCSI_RD_RPL_MASTER: + s = "master"; + break; + case SCSI_RD_RPL_MCONTROL: + s = "master control"; + break; + } + (void)printf("\trotational synch %s, offset %d/256%s\n", + s, rd->rd_roff, len > 18 ? "," : "."); + if (len > 18) + (void)printf("\trotation %d rpm.\n", + N2(rd->rd_rpmh, rd->rd_rpml)); + break; +#undef rd + + case SCSI_MS_PC_VERRREC: +#define v ((struct scsi_page_verrrec *)(mp + 1)) + (void)printf("Verify Error Recovery parameters.\n"); + (void)printf("\tflags = 0x%x", v->v_flags); + prflags(v->v_flags, "\4EER\3PER\2DTE\1DCR"); + (void)printf(",\n\t%d verify retries, %d %s span bits,\n\t", + v->v_verify_retry, v->v_corr_span, "correction"); + (void)printf("%d recovery time limit.\n", + N2(v->v_rtlh, v->v_rtll)); + break; +#undef v + + case SCSI_MS_PC_CACHE: +#define cache ((struct scsi_page_cache *)(mp + 1)) + (void)printf("Caching Page.\n"); + (void)printf("\tflags = 0x%x", cache->cache_flags); + prflags(cache->cache_flags, "\3WCE\2MF\1RCD"); + (void)printf( + ",\n\tread retention = %s, write retention = %s,\n", + cache_policy(SCSI_CACHE_RDPOLICY(cache->cache_reten)), + cache_policy(SCSI_CACHE_WRPOLICY(cache->cache_reten))); + (void)printf("\tdisable prefetch transfer length = %d,\n", + N2(cache->cache_dptlh, cache->cache_dptll)); + (void)printf("\tmin prefetch = %d, max prefetch = %d, ", + N2(cache->cache_minpfh, cache->cache_minpfl), + N2(cache->cache_maxpfh, cache->cache_maxpfl)); + (void)printf("max prefetch ceiling = %d.\n", + N2(cache->cache_mpch, cache->cache_mpcl)); + break; +#undef cache + + case SCSI_MS_PC_CTLMODE: +#define cm ((struct scsi_page_ctlmode *)(mp + 1)) + (void)printf("Control Mode Page.\n"); + (void)printf("\t%s report log-activity error conditions,\n", + cm->cm_rlec & SCSI_CM_RLEC ? "do" : "do not"); + (void)printf("\tqueue algorithm modifier = %d, flags = 0x%x", + SCSI_CM_QMOD(cm->cm_qctl), + cm->cm_qctl & (SCSI_CM_QERR|SCSI_CM_DQUE)); + prflags(cm->cm_qctl, "\2QERR\1DQUE"); + (void)printf(",\n\tECA/AEN flags = 0x%x", cm->cm_ecaaen); + prflags(cm->cm_ecaaen, "\10ECA\3RAENP\2UUAENP\1EAENP"); + (void)printf(", AEN holdoff period = %d ms.\n", + N2(cm->cm_aenholdh, cm->cm_aenholdl)); + break; +#undef cm + + /* + * Vendor Unique, but what the heck. + */ + case SCSI_MS_PC_CDCCACHECTL: +#define ccm ((struct scsi_page_CDCcachectlmode *)(mp + 1)) + (void)printf("CDC-specific Cache Control Mode Page.\n"); + (void)printf("\tflags = 0x%x", ccm->ccm_flags); + prflags(ccm->ccm_flags, "\7WIE\5ENABLE"); + (void)printf(", table size = %d, prefetch threshold = %d\n", + SCSI_CDC_CCM_TBLSZ(ccm->ccm_flags), + ccm->ccm_pfthresh); + (void)printf("\tmaximum %s = %d, maximum %s = %d,\n", + "threshold", ccm->ccm_maxthresh, + "prefetch multiplier", ccm->ccm_maxpfmult); + (void)printf("\tminimum %s = %d, minimum %s = %d.\n", + "threshold", ccm->ccm_minthresh, + "prefetch multiplier", ccm->ccm_minpfmult); + break; +#undef ccm + + default: + (void)printf("Unknown page type."); + for (tp = cp + sizeof(*mp), i = 0; i < len; ++i) { + if ((i & 7) == 0) + (void)printf("\n\t%2d: ", i); + (void)printf(" %02x", *tp++); + } + (void)printf(".\n"); + break; + } + return (cp + sizeof(*mp) + len); +} + +void +pr_sense(fd) + int fd; +{ + static struct scsi_fmt_sense s; + register struct scsi_sense *sn; + + if (ioctl(fd, SDIOCSENSE, &s) < 0) + (void)fprintf(stderr, + "scsiformat: SDIOCSENSE: %s\n", strerror(errno)); + + (void)printf("scsi status 0x%x", s.status); + if (s.status & STS_CHECKCOND) { + sn = (struct scsi_sense *)s.sense; + + (void)printf(" sense class %d, code %d", + SENSE_ECLASS(sn), SENSE_ECODE(sn)); + if (SENSE_ISXSENSE(sn)) { + (void)printf(", key %d", XSENSE_KEY(sn)); + if (XSENSE_IVALID(sn)) + (void)printf(", blk %d", XSENSE_INFO(sn)); + } + } + (void)printf("\n"); +} + +void +do_format() +{ + struct { + struct scsi_ms6 ms; /* mode select header */ + struct scsi_ms_bd bd; /* block descriptor */ + struct scsi_ms_page_hdr mp; /* ctl mode page hdr */ + struct scsi_page_ctlmode cm; /* ctl mode page */ + u_char pad[4]; /* ??? */ + } msel; + u_char fmtbuf[128]; + static struct scsi_cdb modeselect = { + CMD_MODE_SELECT6, + SCSI_MSEL_SCSI2_DATA | SCSI_MSEL_SAVEPAGES, 0, 0, + sizeof(msel), 0 + }; + static struct scsi_cdb format = { CMD_FORMAT_UNIT }; + + /* want mostly 0s; set them all zero here */ + bzero(&msel, sizeof(msel)); + + /* one block descriptor */ + msel.ms.ms_bdl = sizeof(struct scsi_ms_bd); + + /* block length = 512 bytes */ + msel.bd.bd_blm = 512 / 256; + msel.bd.bd_bll = 512 % 256; + + /* + * In the following, the mystery pad region is copied from + * the original driver. I have no idea what it is for. + * (Anyone got SCSI-2 documents?) + */ + + /* mode page parameters: report log-activity exception conditions */ + msel.mp.mp_psc = SCSI_MS_PC_CTLMODE; + msel.mp.mp_len = sizeof(msel.cm) + sizeof(msel.pad); + msel.cm.cm_rlec = SCSI_CM_RLEC; + + do_command(fd, &modeselect, &msel, sizeof(msel)); + + bzero(fmtbuf, sizeof(fmtbuf)); + do_command(fd, &format, fmtbuf, sizeof(fmtbuf)); +} + +void +do_command(fd, cdb, buf, len) + int fd; + struct scsi_cdb *cdb; + void *buf; + int len; +{ + static int on = 1, off = 0; + int user, ret; + + bzero(buf, len); + if (ioctl(fd, SDIOCSFORMAT, &on) < 0) { + (void)fprintf(stderr, + "scsiformat: SDIOCSFORMAT (on): %s\n", strerror(errno)); + if (ioctl(fd, SDIOCGFORMAT, &user) == 0 && user != 0) + (void)fprintf(stderr, "scsiformat: pid %d has it\n", + user); + return; + } + ret = ioctl(fd, SDIOCSCSICOMMAND, cdb); +#ifdef COMPAT_HPSCSI + if (ret < 0) { + static const char scsicmdlen[8] = { 6, 10, 0, 0, 0, 12, 0, 0 }; +#define SCSICMDLEN(cmd) scsicmdlen[(cmd) >> 5] + struct scsi_fmt_cdb { + int len; + u_char cdb[28]; + } sc; +#define OSDIOCSCSICOMMAND _IOW('S', 0x3, struct scsi_fmt_cdb) + + sc.len = SCSICMDLEN(cdb->cdb_bytes[0]); + bcopy(cdb->cdb_bytes, sc.cdb, sc.len); + ret = ioctl(fd, OSDIOCSCSICOMMAND, &sc); + } +#endif + if (ret < 0) + (void)fprintf(stderr, + "scsiformat: SDIOCSCSICOMMAND: %s\n", strerror(errno)); + else if (read(fd, buf, len) < 0) { + (void)fprintf(stderr, + "scsiformat: read: %s\n", strerror(errno)); + pr_sense(fd); + } + + if (ioctl(fd, SDIOCSFORMAT, &off) < 0) + (void)fprintf(stderr, + "scsiformat: SDIOCSFORMAT (off): %s\n", strerror(errno)); +} + +void +usage() +{ + (void)fprintf(stderr, "usage: scsiformat [-r] [-p c|d|s|v] device\n"); + exit(1); +} diff --git a/sbin/shutdown/shutdown.8 b/sbin/shutdown/shutdown.8 new file mode 100644 index 000000000000..74126f83cc66 --- /dev/null +++ b/sbin/shutdown/shutdown.8 @@ -0,0 +1,162 @@ +.\" Copyright (c) 1988, 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. +.\" +.\" @(#)shutdown.8 8.2 (Berkeley) 4/27/95 +.\" +.Dd April 27, 1995 +.Dt SHUTDOWN 8 +.Os BSD 4 +.Sh NAME +.Nm shutdown +.Nd "close down the system at a given time" +.Sh SYNOPSIS +.Nm shutdown +.Op Fl +.Op Fl fhkrn +.Ar time +.Op Ar warning-message ... +.Sh DESCRIPTION +.Nm Shutdown +provides an automated shutdown procedure for super-users +to nicely notify users when the system is shutting down, +saving them from system administrators, hackers, and gurus, who +would otherwise not bother with such niceties. +.Pp +Available friendlinesses: +.Bl -tag -width time +.It Fl f +.Nm Shutdown +arranges, in the manner of +.Xr fastboot 8 , +for the file systems +.Em not to be +checked on reboot. +.It Fl h +The system is halted at the specified +.Ar time +when +.Nm shutdown +execs +.Xr halt 8 . +.It Fl k +Kick everybody off. +The +.Fl k +option +does not actually halt the system, but leaves the +system multi-user with logins disabled (for all but super-user). +.It Fl n +Prevent the normal +.Xr sync 2 +before stopping. +.It Fl r +.Nm Shutdown +execs +.Xr reboot 8 +at the specified +.Ar time . +.It Ar time +.Ar Time +is the time at which +.Nm shutdown +will bring the system down and +may be the word +.Ar now +(indicating an immediate shutdown) or +specify a future time in one of two formats: +.Ar +number , +or +.Ar yymmddhhmm , +where the year, month, and day may be defaulted +to the current system values. The first form brings the system down in +.Ar number +minutes and the second at the absolute time specified. +.It Ar warning-message +Any other arguments comprise the warning message that is broadcast +to users currently logged into the system. +.It Fl +If +.Ql Fl +is supplied as an option, the warning message is read from the standard +input. +.El +.Pp +At intervals, becoming more frequent as apocalypse approaches +and starting at ten hours before shutdown, warning messages are displayed +on the terminals of all users logged in. Five minutes before +shutdown, or immediately if shutdown is in less than 5 minutes, +logins are disabled by creating +.Pa /etc/nologin +and copying the +warning message there. If this file exists when a user attempts to +log in, +.Xr login 1 +prints its contents and exits. The file is +removed just before +.Nm shutdown +exits. +.Pp +At shutdown time a message is written in the system log, containing the +time of shutdown, who initiated the shutdown and the reason. +A terminate +signal is then sent to +.Xr init +to bring the system down to single-user state (depending on above +options). +The time of the shutdown and the warning message +are placed in +.Pa /etc/nologin +and should be used to +inform the users about when the system will be back up +and why it is going down (or anything else). +.Sh FILES +.Bl -tag -width /etc/nologin -compact +.It Pa /etc/nologin +tells login not to let anyone log in +.It Pa /fastboot +tells +.Xr rc 8 +not to run fsck when rebooting +.El +.Sh SEE ALSO +.Xr login 1 , +.Xr wall 1 , +.Xr fastboot 8 , +.Xr halt 8 , +.Xr reboot 8 +.Sh BACKWARD COMPATIBILITY +The hours and minutes in the second time format may be separated by +a colon (``:'') for backward compatibility. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.0 . diff --git a/sbin/shutdown/shutdown.c b/sbin/shutdown/shutdown.c new file mode 100644 index 000000000000..373c6f7f6631 --- /dev/null +++ b/sbin/shutdown/shutdown.c @@ -0,0 +1,493 @@ +/* + * Copyright (c) 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1988, 1990, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)shutdown.c 8.4 (Berkeley) 4/28/95"; +#endif /* not lint */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pathnames.h" + +#ifdef DEBUG +#undef _PATH_NOLOGIN +#define _PATH_NOLOGIN "./nologin" +#undef _PATH_FASTBOOT +#define _PATH_FASTBOOT "./fastboot" +#endif + +#define H *60*60 +#define M *60 +#define S *1 +#define NOLOG_TIME 5*60 +struct interval { + int timeleft, timetowait; +} tlist[] = { + 10 H, 5 H, 5 H, 3 H, 2 H, 1 H, 1 H, 30 M, + 30 M, 10 M, 20 M, 10 M, 10 M, 5 M, 5 M, 3 M, + 2 M, 1 M, 1 M, 30 S, 30 S, 30 S, + 0, 0, +}; +#undef H +#undef M +#undef S + +static time_t offset, shuttime; +static int dofast, dohalt, doreboot, killflg, mbuflen; +static char *nosync, *whom, mbuf[BUFSIZ]; + +void badtime __P((void)); +void die_you_gravy_sucking_pig_dog __P((void)); +void doitfast __P((void)); +void finish __P((int)); +void getoffset __P((char *)); +void loop __P((void)); +void nolog __P((void)); +void timeout __P((int)); +void timewarn __P((int)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern int optind; + register char *p, *endp; + struct passwd *pw; + int arglen, ch, len, readstdin; + +#ifndef DEBUG + if (geteuid()) { + (void)fprintf(stderr, "shutdown: NOT super-user\n"); + exit(1); + } +#endif + nosync = NULL; + readstdin = 0; + while ((ch = getopt(argc, argv, "-fhknr")) != EOF) + switch (ch) { + case '-': + readstdin = 1; + break; + case 'f': + dofast = 1; + break; + case 'h': + dohalt = 1; + break; + case 'k': + killflg = 1; + break; + case 'n': + nosync = "-n"; + break; + case 'r': + doreboot = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc < 1) + usage(); + + if (dofast && nosync) { + (void)fprintf(stderr, + "shutdown: incompatible switches -f and -n.\n"); + usage(); + } + if (doreboot && dohalt) { + (void)fprintf(stderr, + "shutdown: incompatible switches -h and -r.\n"); + usage(); + } + getoffset(*argv++); + + if (*argv) { + for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) { + arglen = strlen(*argv); + if ((len -= arglen) <= 2) + break; + if (p != mbuf) + *p++ = ' '; + memmove(p, *argv, arglen); + p += arglen; + } + *p = '\n'; + *++p = '\0'; + } + + if (readstdin) { + p = mbuf; + endp = mbuf + sizeof(mbuf) - 2; + for (;;) { + if (!fgets(p, endp - p + 1, stdin)) + break; + for (; *p && p < endp; ++p); + if (p == endp) { + *p = '\n'; + *++p = '\0'; + break; + } + } + } + mbuflen = strlen(mbuf); + + if (offset) + (void)printf("Shutdown at %.24s.\n", ctime(&shuttime)); + else + (void)printf("Shutdown NOW!\n"); + + if (!(whom = getlogin())) + whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; + +#ifdef DEBUG + (void)putc('\n', stdout); +#else + (void)setpriority(PRIO_PROCESS, 0, PRIO_MIN); + { + int forkpid; + + forkpid = fork(); + if (forkpid == -1) { + perror("shutdown: fork"); + exit(1); + } + if (forkpid) { + (void)printf("shutdown: [pid %d]\n", forkpid); + exit(0); + } + } +#endif + openlog("shutdown", LOG_CONS, LOG_AUTH); + loop(); + /* NOTREACHED */ +} + +void +loop() +{ + struct interval *tp; + u_int sltime; + int logged; + + if (offset <= NOLOG_TIME) { + logged = 1; + nolog(); + } + else + logged = 0; + tp = tlist; + if (tp->timeleft < offset) + (void)sleep((u_int)(offset - tp->timeleft)); + else { + while (offset < tp->timeleft) + ++tp; + /* + * Warn now, if going to sleep more than a fifth of + * the next wait time. + */ + if (sltime = offset - tp->timeleft) { + if (sltime > tp->timetowait / 5) + timewarn(offset); + (void)sleep(sltime); + } + } + for (;; ++tp) { + timewarn(tp->timeleft); + if (!logged && tp->timeleft <= NOLOG_TIME) { + logged = 1; + nolog(); + } + (void)sleep((u_int)tp->timetowait); + if (!tp->timeleft) + break; + } + die_you_gravy_sucking_pig_dog(); +} + +static jmp_buf alarmbuf; + +void +timewarn(timeleft) + int timeleft; +{ + static int first; + static char hostname[MAXHOSTNAMELEN + 1]; + FILE *pf; + char wcmd[MAXPATHLEN + 4]; + + if (!first++) + (void)gethostname(hostname, sizeof(hostname)); + + /* undoc -n option to wall suppresses normal wall banner */ + (void)snprintf(wcmd, sizeof(wcmd), "%s -n", _PATH_WALL); + if (!(pf = popen(wcmd, "w"))) { + syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL); + return; + } + + (void)fprintf(pf, + "\007*** %sSystem shutdown message from %s@%s ***\007\n", + timeleft ? "": "FINAL ", whom, hostname); + + if (timeleft > 10*60) + (void)fprintf(pf, "System going down at %5.5s\n\n", + ctime(&shuttime) + 11); + else if (timeleft > 59) + (void)fprintf(pf, "System going down in %d minute%s\n\n", + timeleft / 60, (timeleft > 60) ? "s" : ""); + else if (timeleft) + (void)fprintf(pf, "System going down in 30 seconds\n\n"); + else + (void)fprintf(pf, "System going down IMMEDIATELY\n\n"); + + if (mbuflen) + (void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf); + + /* + * play some games, just in case wall doesn't come back + * probably unecessary, given that wall is careful. + */ + if (!setjmp(alarmbuf)) { + (void)signal(SIGALRM, timeout); + (void)alarm((u_int)30); + (void)pclose(pf); + (void)alarm((u_int)0); + (void)signal(SIGALRM, SIG_DFL); + } +} + +void +timeout(signo) + int signo; +{ + longjmp(alarmbuf, 1); +} + +void +die_you_gravy_sucking_pig_dog() +{ + + syslog(LOG_NOTICE, "%s by %s: %s", + doreboot ? "reboot" : dohalt ? "halt" : "shutdown", whom, mbuf); + (void)sleep(2); + + (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n"); + if (killflg) { + (void)printf("\rbut you'll have to do it yourself\r\n"); + finish(0); + } + if (dofast) + doitfast(); +#ifdef DEBUG + if (doreboot) + (void)printf("reboot"); + else if (dohalt) + (void)printf("halt"); + if (nosync) + (void)printf(" no sync"); + if (dofast) + (void)printf(" no fsck"); + (void)printf("\nkill -HUP 1\n"); +#else + if (doreboot) { + execle(_PATH_REBOOT, "reboot", "-l", nosync, 0); + syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_REBOOT); + perror("shutdown"); + } + else if (dohalt) { + execle(_PATH_HALT, "halt", "-l", nosync, 0); + syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_HALT); + perror("shutdown"); + } + (void)kill(1, SIGTERM); /* to single user */ +#endif + finish(0); +} + +#define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2; + +void +getoffset(timearg) + register char *timearg; +{ + register struct tm *lt; + register char *p; + time_t now; + + if (!strcasecmp(timearg, "now")) { /* now */ + offset = 0; + return; + } + + (void)time(&now); + if (*timearg == '+') { /* +minutes */ + if (!isdigit(*++timearg)) + badtime(); + offset = atoi(timearg) * 60; + shuttime = now + offset; + return; + } + + /* handle hh:mm by getting rid of the colon */ + for (p = timearg; *p; ++p) + if (!isascii(*p) || !isdigit(*p)) + if (*p == ':' && strlen(p) == 3) { + p[0] = p[1]; + p[1] = p[2]; + p[2] = '\0'; + } + else + badtime(); + + unsetenv("TZ"); /* OUR timezone */ + lt = localtime(&now); /* current time val */ + + switch(strlen(timearg)) { + case 10: + lt->tm_year = ATOI2(timearg); + /* FALLTHROUGH */ + case 8: + lt->tm_mon = ATOI2(timearg); + if (--lt->tm_mon < 0 || lt->tm_mon > 11) + badtime(); + /* FALLTHROUGH */ + case 6: + lt->tm_mday = ATOI2(timearg); + if (lt->tm_mday < 1 || lt->tm_mday > 31) + badtime(); + /* FALLTHROUGH */ + case 4: + lt->tm_hour = ATOI2(timearg); + if (lt->tm_hour < 0 || lt->tm_hour > 23) + badtime(); + lt->tm_min = ATOI2(timearg); + if (lt->tm_min < 0 || lt->tm_min > 59) + badtime(); + lt->tm_sec = 0; + if ((shuttime = mktime(lt)) == -1) + badtime(); + if ((offset = shuttime - now) < 0) { + (void)fprintf(stderr, + "shutdown: that time is already past.\n"); + exit(1); + } + break; + default: + badtime(); + } +} + +#define FSMSG "fastboot file for fsck\n" +void +doitfast() +{ + int fastfd; + + if ((fastfd = open(_PATH_FASTBOOT, O_WRONLY|O_CREAT|O_TRUNC, + 0664)) >= 0) { + (void)write(fastfd, FSMSG, sizeof(FSMSG) - 1); + (void)close(fastfd); + } +} + +#define NOMSG "\n\nNO LOGINS: System going down at " +void +nolog() +{ + int logfd; + char *ct; + + (void)unlink(_PATH_NOLOGIN); /* in case linked to another file */ + (void)signal(SIGINT, finish); + (void)signal(SIGHUP, finish); + (void)signal(SIGQUIT, finish); + (void)signal(SIGTERM, finish); + if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC, + 0664)) >= 0) { + (void)write(logfd, NOMSG, sizeof(NOMSG) - 1); + ct = ctime(&shuttime); + (void)write(logfd, ct + 11, 5); + (void)write(logfd, "\n\n", 2); + (void)write(logfd, mbuf, strlen(mbuf)); + (void)close(logfd); + } +} + +void +finish(signo) + int signo; +{ + if (!killflg) + (void)unlink(_PATH_NOLOGIN); + exit(0); +} + +void +badtime() +{ + (void)fprintf(stderr, "shutdown: bad time format.\n"); + exit(1); +} + +void +usage() +{ + fprintf(stderr, "usage: shutdown [-fhknr] shutdowntime [ message ]\n"); + exit(1); +} diff --git a/sbin/tunefs/tunefs.8 b/sbin/tunefs/tunefs.8 new file mode 100644 index 000000000000..dc2c397e8975 --- /dev/null +++ b/sbin/tunefs/tunefs.8 @@ -0,0 +1,166 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)tunefs.8 8.3 (Berkeley) 5/3/95 +.\" +.Dd May 3, 1995 +.Dt TUNEFS 8 +.Os BSD 4.2 +.Sh NAME +.Nm tunefs +.Nd tune up an existing file system +.Sh SYNOPSIS +.Nm tunefs +.Op Fl AN +.Op Fl a Ar maxcontig +.Op Fl d Ar rotdelay +.Op Fl e Ar maxbpg +.Op Fl m Ar minfree +.Bk -words +.Op Fl o Ar optimize_preference +.Ek +.Op Fl t Ar trackskew +.Op Ar special | Ar filesys +.Sh DESCRIPTION +The +.Nm tunefs +program is designed to change the dynamic parameters of a file system +which affect the layout policies. +The +.Fl N +flag displays all the settable options +(after any changes from the tuning options) +but does not cause any of them to be changed. +The +.Fl A +flag causes the values to be updated in all the alternate +superblocks instead of just the standard superblock. +If this option is not used, +then use of a backup superblock by +.Xr fsck 8 +will lose anything changed by +.Nm tunefs . +The +.Fl A +flag is ignored when the +.Fl N +flag is specified. +.Pp +The parameters which are to be changed are indicated by the flags +given below: +.Bl -tag -width Ds +.It Fl a Ar maxcontig +This specifies the maximum number of contiguous blocks that will +be laid out before forcing a rotational delay (see +.Fl d +below). +The default value is one, since most device drivers require +an interrupt per disk transfer. +Device drivers that can chain several buffers together in a single +transfer should set this to the maximum chain length. +.It Fl d Ar rotdelay +This specifies the expected time (in milliseconds) +to service a transfer completion +interrupt and initiate a new transfer on the same disk. +It is used to decide how much rotational spacing to place between +successive blocks in a file. +.It Fl e Ar maxbpg +This indicates the maximum number of blocks any single file can +allocate out of a cylinder group before it is forced to begin +allocating blocks from another cylinder group. +Typically this value is set to about one quarter of the total blocks +in a cylinder group. +The intent is to prevent any single file from using up all the +blocks in a single cylinder group, +thus degrading access times for all files subsequently allocated +in that cylinder group. +The effect of this limit is to cause big files to do long seeks +more frequently than if they were allowed to allocate all the blocks +in a cylinder group before seeking elsewhere. +For file systems with exclusively large files, +this parameter should be set higher. +.It Fl m Ar minfree +This value specifies the percentage of space held back +from normal users; the minimum free space threshold. +The default value used is 10%. +This value can be set to zero, however up to a factor of three +in throughput will be lost over the performance obtained at a 10% +threshold. +Note that if the value is raised above the current usage level, +users will be unable to allocate files until enough files have +been deleted to get under the higher threshold. +.It Fl o Ar optimize_preference +The file system can either try to minimize the time spent +allocating blocks, or it can attempt to minimize the space +fragmentation on the disk. +If the value of minfree (see above) is less than 10%, +then the file system should optimize for space to avoid +running out of full sized blocks. +For values of minfree greater than or equal to 10%, +fragmentation is unlikely to be problematical, and +the file system can be optimized for time. +.It Fl t Ar trackskew +This specifies the skew in sectors from one track to the next in a cylinder. +The default value is zero, indicating that each track in a cylinder begins at +the same rotational position. +.El +.Sh SEE ALSO +.Xr fs 5 , +.Xr dumpfs 8 , +.Xr fsck 8 , +.Xr newfs 8 , +.Xr mkfs 8 +.Rs +.%A M. McKusick +.%A W. Joy +.%A S. Leffler +.%A R. Fabry +.%T "A Fast File System for UNIX" +.%J "ACM Transactions on Computer Systems 2" +.%N 3 +.%P pp 181-197 +.%D August 1984 +.%O "(reprinted in the BSD System Manager's Manual, SMM:5)" +.Re +.Sh BUGS +This program should work on mounted and active file systems. +Because the super-block is not kept in the buffer cache, +the changes will only take effect if the program +is run on dismounted file systems. +To change the root file system, the system must be rebooted +after the file system is tuned. +.Pp +You can tune a file system, but you can't tune a fish. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/sbin/tunefs/tunefs.c b/sbin/tunefs/tunefs.c new file mode 100644 index 000000000000..d5868ebd469a --- /dev/null +++ b/sbin/tunefs/tunefs.c @@ -0,0 +1,327 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)tunefs.c 8.3 (Berkeley) 5/3/95"; +#endif /* not lint */ + +/* + * tunefs: change layout parameters to an existing file system. + */ +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* the optimization warning string template */ +#define OPTWARN "should optimize for %s with minfree %s %d%%" + +union { + struct fs sb; + char pad[MAXBSIZE]; +} sbun; +#define sblock sbun.sb + +int fi; +long dev_bsize = 1; + +void bwrite(daddr_t, char *, int); +int bread(daddr_t, char *, int); +void getsb(struct fs *, char *); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + char *cp, *special, *name; + struct stat st; + int i; + int Aflag = 0, Nflag = 0; + struct fstab *fs; + char *chg[2], device[MAXPATHLEN]; + + argc--, argv++; + if (argc < 2) + usage(); + special = argv[argc - 1]; + fs = getfsfile(special); + if (fs) + special = fs->fs_spec; +again: + if (stat(special, &st) < 0) { + if (*special != '/') { + if (*special == 'r') + special++; + (void)sprintf(device, "%s/%s", _PATH_DEV, special); + special = device; + goto again; + } + err(1, "%s", special); + } + if ((st.st_mode & S_IFMT) != S_IFBLK && + (st.st_mode & S_IFMT) != S_IFCHR) + errx(10, "%s: not a block or character device", special); + getsb(&sblock, special); + chg[FS_OPTSPACE] = "space"; + chg[FS_OPTTIME] = "time"; + for (; argc > 0 && argv[0][0] == '-'; argc--, argv++) { + for (cp = &argv[0][1]; *cp; cp++) + switch (*cp) { + + case 'A': + Aflag++; + continue; + + case 'N': + Nflag++; + continue; + + case 'a': + name = "maximum contiguous block count"; + if (argc < 1) + errx(10, "-a: missing %s", name); + argc--, argv++; + i = atoi(*argv); + if (i < 1) + errx(10, "%s must be >= 1 (was %s)", + name, *argv); + warnx("%s changes from %d to %d", + name, sblock.fs_maxcontig, i); + sblock.fs_maxcontig = i; + continue; + + case 'd': + name = + "rotational delay between contiguous blocks"; + if (argc < 1) + errx(10, "-d: missing %s", name); + argc--, argv++; + i = atoi(*argv); + warnx("%s changes from %dms to %dms", + name, sblock.fs_rotdelay, i); + sblock.fs_rotdelay = i; + continue; + + case 'e': + name = + "maximum blocks per file in a cylinder group"; + if (argc < 1) + errx(10, "-e: missing %s", name); + argc--, argv++; + i = atoi(*argv); + if (i < 1) + errx(10, "%s must be >= 1 (was %s)", + name, *argv); + warnx("%s changes from %d to %d", + name, sblock.fs_maxbpg, i); + sblock.fs_maxbpg = i; + continue; + + case 'm': + name = "minimum percentage of free space"; + if (argc < 1) + errx(10, "-m: missing %s", name); + argc--, argv++; + i = atoi(*argv); + if (i < 0 || i > 99) + errx(10, "bad %s (%s)", name, *argv); + warnx("%s changes from %d%% to %d%%", + name, sblock.fs_minfree, i); + sblock.fs_minfree = i; + if (i >= MINFREE && + sblock.fs_optim == FS_OPTSPACE) + warnx(OPTWARN, "time", ">=", MINFREE); + if (i < MINFREE && + sblock.fs_optim == FS_OPTTIME) + warnx(OPTWARN, "space", "<", MINFREE); + continue; + + case 'o': + name = "optimization preference"; + if (argc < 1) + errx(10, "-o: missing %s", name); + argc--, argv++; + if (strcmp(*argv, chg[FS_OPTSPACE]) == 0) + i = FS_OPTSPACE; + else if (strcmp(*argv, chg[FS_OPTTIME]) == 0) + i = FS_OPTTIME; + else + errx(10, "bad %s (options are `space' or `time')", + name); + if (sblock.fs_optim == i) { + warnx("%s remains unchanged as %s", + name, chg[i]); + continue; + } + warnx("%s changes from %s to %s", + name, chg[sblock.fs_optim], chg[i]); + sblock.fs_optim = i; + if (sblock.fs_minfree >= MINFREE && + i == FS_OPTSPACE) + warnx(OPTWARN, "time", ">=", MINFREE); + if (sblock.fs_minfree < MINFREE && + i == FS_OPTTIME) + warnx(OPTWARN, "space", "<", MINFREE); + continue; + + case 't': + name = "track skew in sectors"; + if (argc < 1) + errx(10, "-t: missing %s", name); + argc--, argv++; + i = atoi(*argv); + if (i < 0) + errx(10, "%s: %s must be >= 0", + *argv, name); + warnx("%s changes from %d to %d", + name, sblock.fs_trackskew, i); + sblock.fs_trackskew = i; + continue; + + default: + usage(); + } + } + if (argc != 1) + usage(); + if (Nflag) { + fprintf(stdout, "tunefs: current settings\n"); + fprintf(stdout, "\tmaximum contiguous block count %d\n", + sblock.fs_maxcontig); + fprintf(stdout, + "\trotational delay between contiguous blocks %dms\n", + sblock.fs_rotdelay); + fprintf(stdout, + "\tmaximum blocks per file in a cylinder group %d\n", + sblock.fs_maxbpg); + fprintf(stdout, "\tminimum percentage of free space %d%%\n", + sblock.fs_minfree); + fprintf(stdout, "\toptimization preference: %s\n", + chg[sblock.fs_optim]); + fprintf(stdout, "\ttrack skew %d sectors\n", + sblock.fs_trackskew); + fprintf(stdout, "tunefs: no changes made\n"); + exit(0); + } + fi = open(special, 1); + if (fi < 0) + err(3, "cannot open %s for writing", special); + bwrite((daddr_t)SBOFF / dev_bsize, (char *)&sblock, SBSIZE); + if (Aflag) + for (i = 0; i < sblock.fs_ncg; i++) + bwrite(fsbtodb(&sblock, cgsblock(&sblock, i)), + (char *)&sblock, SBSIZE); + close(fi); + exit(0); +} + +void +usage() +{ + + fprintf(stderr, "Usage: tunefs [-AN] tuneup-options special-device\n"); + fprintf(stderr, "where tuneup-options are:\n"); + fprintf(stderr, "\t-d rotational delay between contiguous blocks\n"); + fprintf(stderr, "\t-a maximum contiguous blocks\n"); + fprintf(stderr, "\t-e maximum blocks per file in a cylinder group\n"); + fprintf(stderr, "\t-m minimum percentage of free space\n"); + fprintf(stderr, "\t-o optimization preference (`space' or `time')\n"); + fprintf(stderr, "\t-t track skew in sectors\n"); + exit(2); +} + +void +getsb(fs, file) + register struct fs *fs; + char *file; +{ + + fi = open(file, 0); + if (fi < 0) + err(3, "cannot open %s for reading", file); + if (bread((daddr_t)SBOFF, (char *)fs, SBSIZE)) + err(4, "%s: bad super block", file); + if (fs->fs_magic != FS_MAGIC) + err(5, "%s: bad magic number", file); + dev_bsize = fs->fs_fsize / fsbtodb(fs, 1); + close(fi); +} + +void +bwrite(blk, buf, size) + daddr_t blk; + char *buf; + int size; +{ + + if (lseek(fi, (off_t)blk * dev_bsize, SEEK_SET) < 0) + err(6, "FS SEEK"); + if (write(fi, buf, size) != size) + err(7, "FS WRITE"); +} + +int +bread(bno, buf, cnt) + daddr_t bno; + char *buf; + int cnt; +{ + int i; + + if (lseek(fi, (off_t)bno * dev_bsize, SEEK_SET) < 0) + return(1); + if ((i = read(fi, buf, cnt)) != cnt) { + for(i=0; i