From c44252b6e8da46f58fd1882b9e7305a0f9114c16 Mon Sep 17 00:00:00 2001 From: Bruce Evans Date: Sun, 6 Jul 1997 06:54:14 +0000 Subject: [PATCH] Finish importing Lite2's src/usr.bin, except for ex, diff, grep, mail, pascal and vmstat.sparc. All changed files on the vendor branch should already have been imported. --- usr.bin/apropos/Makefile | 7 + usr.bin/apropos/apropos.1 | 120 + usr.bin/apropos/apropos.c | 224 ++ usr.bin/ar/ar.1 | 320 ++ usr.bin/ar/ar.5.5 | 146 + usr.bin/ar/archive.c | 326 ++ usr.bin/ar/misc.c | 149 + usr.bin/ar/replace.c | 178 + usr.bin/biff/biff.c | 105 + usr.bin/cal/cal.1 | 81 + usr.bin/calendar/calendar.c | 411 +++ usr.bin/cap_mkdb/cap_mkdb.c | 259 ++ usr.bin/chflags/chflags.1 | 124 + usr.bin/ctags/ctags.c | 272 ++ usr.bin/file/file.1 | 72 + usr.bin/ftp/cmds.c | 2217 ++++++++++++ usr.bin/ftp/cmdtab.c | 188 + usr.bin/ftp/extern.h | 153 + usr.bin/ftp/ftp.1 | 1157 ++++++ usr.bin/ftp/ftp.c | 1530 ++++++++ usr.bin/ftp/ftp_var.h | 126 + usr.bin/ftp/main.c | 515 +++ usr.bin/ftp/ruserpass.c | 281 ++ usr.bin/id/id.1 | 139 + usr.bin/id/id.c | 351 ++ usr.bin/kdump/kdump.c | 418 +++ usr.bin/kdump/mkioctls | 68 + usr.bin/ktrace/ktrace.c | 182 + usr.bin/ktrace/subr.c | 109 + usr.bin/lastcomm/lastcomm.c | 220 ++ usr.bin/ld/Makefile | 7 + usr.bin/ld/cplus-dem.c | 970 +++++ usr.bin/ld/ld.c | 4718 +++++++++++++++++++++++++ usr.bin/ld/symseg.h | 358 ++ usr.bin/locate/bigram/locate.bigram.c | 85 + usr.bin/locate/code/locate.code.c | 213 ++ usr.bin/locate/locate/updatedb.csh | 77 + usr.bin/login/login.1 | 148 + usr.bin/lorder/lorder.1 | 73 + usr.bin/m4/eval.c | 790 +++++ usr.bin/m4/expr.c | 574 +++ usr.bin/m4/serv.c | 475 +++ usr.bin/man/Makefile | 8 + usr.bin/man/config.c | 176 + usr.bin/man/config.h | 57 + usr.bin/man/man.1 | 188 + usr.bin/man/man.c | 712 ++++ usr.bin/man/man.conf | 46 + usr.bin/man/man.conf.5 | 195 + usr.bin/man/pathnames.h | 39 + usr.bin/mklocale/Japanese | 158 + usr.bin/more/output.c | 254 ++ usr.bin/msgs/Makefile | 7 + usr.bin/msgs/msgs.1 | 214 ++ usr.bin/msgs/msgs.c | 862 +++++ usr.bin/nfsstat/nfsstat.c | 404 +++ usr.bin/nice/nice.c | 94 + usr.bin/passwd/kpasswd_proto.h | 54 + usr.bin/passwd/krb_passwd.c | 319 ++ usr.bin/patch/EXTERN.h | 15 + usr.bin/patch/INTERN.h | 15 + usr.bin/patch/Makefile | 6 + usr.bin/patch/README | 79 + usr.bin/patch/common.h | 138 + usr.bin/patch/config.h | 16 + usr.bin/patch/inp.c | 313 ++ usr.bin/patch/inp.h | 18 + usr.bin/patch/patch.1 | 446 +++ usr.bin/patch/patch.c | 800 +++++ usr.bin/patch/patchlevel.h | 1 + usr.bin/patch/pch.c | 1108 ++++++ usr.bin/patch/pch.h | 36 + usr.bin/patch/util.c | 339 ++ usr.bin/patch/util.h | 74 + usr.bin/patch/version.c | 28 + usr.bin/patch/version.h | 9 + usr.bin/pr/pr.c | 1807 ++++++++++ usr.bin/printf/printf.c | 410 +++ usr.bin/quota/quota.c | 516 +++ usr.bin/ranlib/ranlib.5.5 | 70 + usr.bin/rlogin/des_rw.c | 203 ++ usr.bin/rlogin/rlogin.1 | 188 + usr.bin/rlogin/rlogin.c | 991 ++++++ usr.bin/rsh/rsh.1 | 187 + usr.bin/rsh/rsh.c | 490 +++ usr.bin/sccs/Makefile | 5 + usr.bin/sccs/PSD.doc/Makefile | 7 + usr.bin/sccs/PSD.doc/sccs.me | 1609 +++++++++ usr.bin/sccs/PSD.doc/spell.ok | 77 + usr.bin/sccs/pathnames.h | 51 + usr.bin/sccs/sccs.1 | 398 +++ usr.bin/sccs/sccs.c | 1621 +++++++++ usr.bin/sed/compile.c | 779 ++++ usr.bin/showmount/showmount.8 | 93 + usr.bin/showmount/showmount.c | 363 ++ usr.bin/sort/sort.1 | 310 ++ usr.bin/split/split.c | 288 ++ usr.bin/strip/strip.c | 236 ++ usr.bin/tcopy/tcopy.c | 331 ++ usr.bin/tip/acu.c | 196 + usr.bin/tip/aculib/biz22.c | 187 + usr.bin/tip/aculib/biz31.c | 248 ++ usr.bin/tip/aculib/courier.c | 380 ++ usr.bin/tip/aculib/df.c | 132 + usr.bin/tip/aculib/dn11.c | 142 + usr.bin/tip/aculib/hayes.c | 305 ++ usr.bin/tip/aculib/t3000.c | 408 +++ usr.bin/tip/aculib/v3451.c | 214 ++ usr.bin/tip/aculib/v831.c | 259 ++ usr.bin/tip/aculib/ventel.c | 251 ++ usr.bin/tip/acutab.c | 97 + usr.bin/tip/cmds.c | 888 +++++ usr.bin/tip/cmdtab.c | 64 + usr.bin/tip/cu.c | 132 + usr.bin/tip/hunt.c | 93 + usr.bin/tip/log.c | 86 + usr.bin/tip/partab.c | 58 + usr.bin/tip/remcap.c | 426 +++ usr.bin/tip/remote.c | 226 ++ usr.bin/tip/tip.1 | 451 +++ usr.bin/tip/value.c | 353 ++ usr.bin/tip/vars.c | 112 + usr.bin/touch/touch.1 | 167 + usr.bin/touch/touch.c | 342 ++ usr.bin/tput/tput.c | 226 ++ usr.bin/uucp/acucntrl/acucntrl.8 | 165 + usr.bin/uucp/acucntrl/acucntrl.c | 814 +++++ usr.bin/uucp/uupoll/uupoll.8 | 111 + usr.bin/uucp/uupoll/uupoll.c | 129 + usr.bin/uucp/uuq/Makefile | 9 + usr.bin/uucp/uuq/uuq.1 | 126 + usr.bin/uucp/uuq/uuq.c | 435 +++ usr.bin/uucp/uusend/Makefile | 6 + usr.bin/uucp/uusend/uusend.1 | 96 + usr.bin/uucp/uusend/uusend.c | 403 +++ usr.bin/uucp/uusnap/uusnap.8 | 80 + usr.bin/uucp/uusnap/uusnap.c | 348 ++ usr.bin/vacation/vacation.1 | 171 + usr.bin/vmstat/vmstat.c | 876 +++++ usr.bin/w/w.c | 428 +++ usr.bin/wc/wc.c | 243 ++ usr.bin/whatis/Makefile | 7 + usr.bin/whatis/whatis.1 | 105 + usr.bin/whatis/whatis.c | 218 ++ usr.bin/whereis/whereis.1 | 63 + usr.bin/whereis/whereis.c | 120 + usr.bin/whois/whois.1 | 80 + usr.bin/window/wwinit.c | 388 ++ usr.bin/window/wwopen.c | 190 + usr.bin/write/write.c | 303 ++ 150 files changed, 49551 insertions(+) create mode 100644 usr.bin/apropos/Makefile create mode 100644 usr.bin/apropos/apropos.1 create mode 100644 usr.bin/apropos/apropos.c create mode 100644 usr.bin/ar/ar.1 create mode 100644 usr.bin/ar/ar.5.5 create mode 100644 usr.bin/ar/archive.c create mode 100644 usr.bin/ar/misc.c create mode 100644 usr.bin/ar/replace.c create mode 100644 usr.bin/biff/biff.c create mode 100644 usr.bin/cal/cal.1 create mode 100644 usr.bin/calendar/calendar.c create mode 100644 usr.bin/cap_mkdb/cap_mkdb.c create mode 100644 usr.bin/chflags/chflags.1 create mode 100644 usr.bin/ctags/ctags.c create mode 100644 usr.bin/file/file.1 create mode 100644 usr.bin/ftp/cmds.c create mode 100644 usr.bin/ftp/cmdtab.c create mode 100644 usr.bin/ftp/extern.h create mode 100644 usr.bin/ftp/ftp.1 create mode 100644 usr.bin/ftp/ftp.c create mode 100644 usr.bin/ftp/ftp_var.h create mode 100644 usr.bin/ftp/main.c create mode 100644 usr.bin/ftp/ruserpass.c create mode 100644 usr.bin/id/id.1 create mode 100644 usr.bin/id/id.c create mode 100644 usr.bin/kdump/kdump.c create mode 100644 usr.bin/kdump/mkioctls create mode 100644 usr.bin/ktrace/ktrace.c create mode 100644 usr.bin/ktrace/subr.c create mode 100644 usr.bin/lastcomm/lastcomm.c create mode 100644 usr.bin/ld/Makefile create mode 100644 usr.bin/ld/cplus-dem.c create mode 100644 usr.bin/ld/ld.c create mode 100644 usr.bin/ld/symseg.h create mode 100644 usr.bin/locate/bigram/locate.bigram.c create mode 100644 usr.bin/locate/code/locate.code.c create mode 100644 usr.bin/locate/locate/updatedb.csh create mode 100644 usr.bin/login/login.1 create mode 100644 usr.bin/lorder/lorder.1 create mode 100644 usr.bin/m4/eval.c create mode 100644 usr.bin/m4/expr.c create mode 100644 usr.bin/m4/serv.c create mode 100644 usr.bin/man/Makefile create mode 100644 usr.bin/man/config.c create mode 100644 usr.bin/man/config.h create mode 100644 usr.bin/man/man.1 create mode 100644 usr.bin/man/man.c create mode 100644 usr.bin/man/man.conf create mode 100644 usr.bin/man/man.conf.5 create mode 100644 usr.bin/man/pathnames.h create mode 100644 usr.bin/mklocale/Japanese create mode 100644 usr.bin/more/output.c create mode 100644 usr.bin/msgs/Makefile create mode 100644 usr.bin/msgs/msgs.1 create mode 100644 usr.bin/msgs/msgs.c create mode 100644 usr.bin/nfsstat/nfsstat.c create mode 100644 usr.bin/nice/nice.c create mode 100644 usr.bin/passwd/kpasswd_proto.h create mode 100644 usr.bin/passwd/krb_passwd.c create mode 100644 usr.bin/patch/EXTERN.h create mode 100644 usr.bin/patch/INTERN.h create mode 100644 usr.bin/patch/Makefile create mode 100644 usr.bin/patch/README create mode 100644 usr.bin/patch/common.h create mode 100644 usr.bin/patch/config.h create mode 100644 usr.bin/patch/inp.c create mode 100644 usr.bin/patch/inp.h create mode 100644 usr.bin/patch/patch.1 create mode 100644 usr.bin/patch/patch.c create mode 100644 usr.bin/patch/patchlevel.h create mode 100644 usr.bin/patch/pch.c create mode 100644 usr.bin/patch/pch.h create mode 100644 usr.bin/patch/util.c create mode 100644 usr.bin/patch/util.h create mode 100644 usr.bin/patch/version.c create mode 100644 usr.bin/patch/version.h create mode 100644 usr.bin/pr/pr.c create mode 100644 usr.bin/printf/printf.c create mode 100644 usr.bin/quota/quota.c create mode 100644 usr.bin/ranlib/ranlib.5.5 create mode 100644 usr.bin/rlogin/des_rw.c create mode 100644 usr.bin/rlogin/rlogin.1 create mode 100644 usr.bin/rlogin/rlogin.c create mode 100644 usr.bin/rsh/rsh.1 create mode 100644 usr.bin/rsh/rsh.c create mode 100644 usr.bin/sccs/Makefile create mode 100644 usr.bin/sccs/PSD.doc/Makefile create mode 100644 usr.bin/sccs/PSD.doc/sccs.me create mode 100644 usr.bin/sccs/PSD.doc/spell.ok create mode 100644 usr.bin/sccs/pathnames.h create mode 100644 usr.bin/sccs/sccs.1 create mode 100644 usr.bin/sccs/sccs.c create mode 100644 usr.bin/sed/compile.c create mode 100644 usr.bin/showmount/showmount.8 create mode 100644 usr.bin/showmount/showmount.c create mode 100644 usr.bin/sort/sort.1 create mode 100644 usr.bin/split/split.c create mode 100644 usr.bin/strip/strip.c create mode 100644 usr.bin/tcopy/tcopy.c create mode 100644 usr.bin/tip/acu.c create mode 100644 usr.bin/tip/aculib/biz22.c create mode 100644 usr.bin/tip/aculib/biz31.c create mode 100644 usr.bin/tip/aculib/courier.c create mode 100644 usr.bin/tip/aculib/df.c create mode 100644 usr.bin/tip/aculib/dn11.c create mode 100644 usr.bin/tip/aculib/hayes.c create mode 100644 usr.bin/tip/aculib/t3000.c create mode 100644 usr.bin/tip/aculib/v3451.c create mode 100644 usr.bin/tip/aculib/v831.c create mode 100644 usr.bin/tip/aculib/ventel.c create mode 100644 usr.bin/tip/acutab.c create mode 100644 usr.bin/tip/cmds.c create mode 100644 usr.bin/tip/cmdtab.c create mode 100644 usr.bin/tip/cu.c create mode 100644 usr.bin/tip/hunt.c create mode 100644 usr.bin/tip/log.c create mode 100644 usr.bin/tip/partab.c create mode 100644 usr.bin/tip/remcap.c create mode 100644 usr.bin/tip/remote.c create mode 100644 usr.bin/tip/tip.1 create mode 100644 usr.bin/tip/value.c create mode 100644 usr.bin/tip/vars.c create mode 100644 usr.bin/touch/touch.1 create mode 100644 usr.bin/touch/touch.c create mode 100644 usr.bin/tput/tput.c create mode 100644 usr.bin/uucp/acucntrl/acucntrl.8 create mode 100644 usr.bin/uucp/acucntrl/acucntrl.c create mode 100644 usr.bin/uucp/uupoll/uupoll.8 create mode 100644 usr.bin/uucp/uupoll/uupoll.c create mode 100644 usr.bin/uucp/uuq/Makefile create mode 100644 usr.bin/uucp/uuq/uuq.1 create mode 100644 usr.bin/uucp/uuq/uuq.c create mode 100644 usr.bin/uucp/uusend/Makefile create mode 100644 usr.bin/uucp/uusend/uusend.1 create mode 100644 usr.bin/uucp/uusend/uusend.c create mode 100644 usr.bin/uucp/uusnap/uusnap.8 create mode 100644 usr.bin/uucp/uusnap/uusnap.c create mode 100644 usr.bin/vacation/vacation.1 create mode 100644 usr.bin/vmstat/vmstat.c create mode 100644 usr.bin/w/w.c create mode 100644 usr.bin/wc/wc.c create mode 100644 usr.bin/whatis/Makefile create mode 100644 usr.bin/whatis/whatis.1 create mode 100644 usr.bin/whatis/whatis.c create mode 100644 usr.bin/whereis/whereis.1 create mode 100644 usr.bin/whereis/whereis.c create mode 100644 usr.bin/whois/whois.1 create mode 100644 usr.bin/window/wwinit.c create mode 100644 usr.bin/window/wwopen.c create mode 100644 usr.bin/write/write.c diff --git a/usr.bin/apropos/Makefile b/usr.bin/apropos/Makefile new file mode 100644 index 00000000000..028a42c467e --- /dev/null +++ b/usr.bin/apropos/Makefile @@ -0,0 +1,7 @@ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 + +PROG= apropos +SRCS= apropos.c config.c +.PATH: ${.CURDIR}/../man + +.include diff --git a/usr.bin/apropos/apropos.1 b/usr.bin/apropos/apropos.1 new file mode 100644 index 00000000000..eb68f375561 --- /dev/null +++ b/usr.bin/apropos/apropos.1 @@ -0,0 +1,120 @@ +.\" Copyright (c) 1989, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)apropos.1 8.1 (Berkeley) 6/29/93 +.\" +.Dd June 29, 1993 +.Dt APROPOS 1 +.Os +.Sh NAME +.Nm apropos +.Nd locate commands by keyword lookup +.Sh SYNOPSIS +.Nm apropos +.Op Fl M Ar path +.Op Fl m Ar path +.Ar keyword ... +.Sh DESCRIPTION +.Nm Apropos +shows which manual pages contain instances of any of the given +.Ar keyword(s) +in their title line. +Each word is considered separately and case of letters is ignored. +Words which are part of other words are considered; when looking for +.Dq compile , +.Nm apropos +will also list all instances of +.Dq compiler . +.Pp +If the line output by +.Nm apropos +starts +.Dq Li name(section) ... +you can enter +.Dq Li man section name +to get +its documentation. +.Pp +The options are as follows: +.Bl -tag -width flag +.It Fl M +Override the list of standard directories +.Nm apropos +searches for a database named +.Pa whatis.db . +The supplied +.Ar path +must be a colon +.Dq \&: +separated list of directories. +This search path may also be set using the environment variable +.Ev MANPATH . +.It Fl m +Augment the list of standard directories +.Nm apropos +searches for its database. +The supplied +.Ar path +must be a colon +.Dq \&: +separated list of directories. +These directories will be searched before the standard directories, +or the directories supplied with the +.Fl M +option or the +.Ev MANPATH +environment variable. +.Sh ENVIRONMENT +.Bl -tag -width MANPATH +.It Ev MANPATH +The standard search path used by +.Xr man 1 +may be overridden by specifying a path in the +.Ev MANPATH +environment variable. +The format of the path is a colon +.Dq \&: +separated list of directories. +.El +.Sh FILES +.Bl -tag -width whatis.db -compact +.It Pa whatis.db +name of the apropos database +.El +.Sh SEE ALSO +.Xr man 1 , +.Xr whatis 1 , +.Xr whereis 1 +.Sh HISTORY +The +.Nm apropos +command appeared in +.Bx 3.0 . diff --git a/usr.bin/apropos/apropos.c b/usr.bin/apropos/apropos.c new file mode 100644 index 00000000000..112aadad025 --- /dev/null +++ b/usr.bin/apropos/apropos.c @@ -0,0 +1,224 @@ +/* + * Copyright (c) 1987, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1987, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)apropos.c 8.8 (Berkeley) 5/4/95"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "../man/config.h" +#include "../man/pathnames.h" + +static int *found, foundman; + +void apropos __P((char **, char *, int)); +void lowstr __P((char *, char *)); +int match __P((char *, char *)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + ENTRY *ep; + TAG *tp; + int ch, rv; + char *conffile, **p, *p_augment, *p_path; + + conffile = NULL; + p_augment = p_path = NULL; + while ((ch = getopt(argc, argv, "C:M:m:P:")) != EOF) + switch (ch) { + case 'C': + conffile = optarg; + break; + case 'M': + case 'P': /* backward compatible */ + p_path = optarg; + break; + case 'm': + p_augment = optarg; + break; + case '?': + default: + usage(); + } + argv += optind; + argc -= optind; + + if (argc < 1) + usage(); + + if ((found = malloc((u_int)argc * sizeof(int))) == NULL) + err(1, NULL); + memset(found, 0, argc * sizeof(int)); + + for (p = argv; *p; ++p) /* convert to lower-case */ + lowstr(*p, *p); + + if (p_augment) + apropos(argv, p_augment, 1); + if (p_path || (p_path = getenv("MANPATH"))) + apropos(argv, p_path, 1); + else { + config(conffile); + ep = (tp = getlist("_whatdb")) == NULL ? + NULL : tp->list.tqh_first; + for (; ep != NULL; ep = ep->q.tqe_next) + apropos(argv, ep->s, 0); + } + + if (!foundman) + errx(1, "no %s file found", _PATH_WHATIS); + + rv = 1; + for (p = argv; *p; ++p) + if (found[p - argv]) + rv = 0; + else + (void)printf("%s: nothing appropriate\n", *p); + exit(rv); +} + +void +apropos(argv, path, buildpath) + char **argv, *path; + int buildpath; +{ + char *end, *name, **p; + char buf[LINE_MAX + 1], wbuf[LINE_MAX + 1]; + + for (name = path; name; name = end) { /* through name list */ + if (end = strchr(name, ':')) + *end++ = '\0'; + + if (buildpath) { + char hold[MAXPATHLEN + 1]; + + (void)sprintf(hold, "%s/%s", name, _PATH_WHATIS); + name = hold; + } + + if (!freopen(name, "r", stdin)) + continue; + + foundman = 1; + + /* for each file found */ + while (fgets(buf, sizeof(buf), stdin)) { + if (!strchr(buf, '\n')) { + warnx("%s: line too long", name); + continue; + } + lowstr(buf, wbuf); + for (p = argv; *p; ++p) + if (match(wbuf, *p)) { + (void)printf("%s", buf); + found[p - argv] = 1; + + /* only print line once */ + while (*++p) + if (match(wbuf, *p)) + found[p - argv] = 1; + break; + } + } + } +} + +/* + * match -- + * match anywhere the string appears + */ +int +match(bp, str) + char *bp, *str; +{ + int len; + char test; + + if (!*bp) + return (0); + /* backward compatible: everything matches empty string */ + if (!*str) + return (1); + for (test = *str++, len = strlen(str); *bp;) + if (test == *bp++ && !strncmp(bp, str, len)) + return (1); + return (0); +} + +/* + * lowstr -- + * convert a string to lower case + */ +void +lowstr(from, to) + char *from, *to; +{ + char ch; + + while ((ch = *from++) && ch != '\n') + *to++ = isupper(ch) ? tolower(ch) : ch; + *to = '\0'; +} + +/* + * usage -- + * print usage message and die + */ +void +usage() +{ + + (void)fprintf(stderr, + "usage: apropos [-C file] [-M path] [-m path] keyword ...\n"); + exit(1); +} diff --git a/usr.bin/ar/ar.1 b/usr.bin/ar/ar.1 new file mode 100644 index 00000000000..fd5ce4f717c --- /dev/null +++ b/usr.bin/ar/ar.1 @@ -0,0 +1,320 @@ +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Hugh Smith at The University of Guelph. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)ar.1 8.4 (Berkeley) 4/28/95 +.\" +.Dd "April 28, 1995" +.Dt AR 1 +.Os +.Sh NAME +.Nm ar +.Nd create and maintain library archives +.Sh SYNOPSIS +.Nm ar +.Fl d +.Op Fl \Tv +.Ar archive file ... +.Nm ar +.Fl m +.Op Fl \Tv +.Ar archive file ... +.Nm ar +.Fl m +.Op Fl abiTv +.Ar position archive file ... +.Nm ar +.Fl p +.Op Fl \Tv +.Ar archive +.Op Ar file ... +.Nm ar +.Fl q +.Op Fl cTv +.Ar archive file ... +.Nm ar +.Fl r +.Op Fl cuTv +.Ar archive file ... +.Nm ar +.Fl r +.Op Fl abciuTv +.Ar position archive file ... +.Nm ar +.Fl t +.Op Fl \Tv +.Ar archive +.Op Ar file ... +.Nm ar +.Fl x +.Op Fl ouTv +.Ar archive +.Op Ar file ... +.Sh DESCRIPTION +The +.Nm ar +utility creates and maintains groups of files combined into an archive. +Once an archive has been created, new files can be added and existing +files can be extracted, deleted, or replaced. +.Pp +Files are named in the archive by a single component, i.e., if a file +referenced by a path containing a slash (``/'') is archived it will be +named by the last component of that path. +When matching paths listed on the command line against file names stored +in the archive, only the last component of the path will be compared. +.Pp +All informational and error messages use the path listed on the command +line, if any was specified; otherwise the name in the archive is used. +If multiple files in the archive have the same name, and paths are listed +on the command line to ``select'' archive files for an operation, only the +.Em first +file with a matching name will be selected. +.Pp +The normal use of +.Nm ar +is for the creation and maintenance of libraries suitable for use with +the loader (see +.Xr ld 1 ), +although it is not restricted to this purpose. +The options are as follows: +.Bl -tag -width indent +.It Fl a +A positioning modifier used with the options +.Fl r +and +.Fl m . +The files are entered or moved +.Em after +the archive member +.Ar position , +which must be specified. +.It Fl b +A positioning modifier used with the options +.Fl r +and +.Fl m . +The files are entered or moved +.Em before +the archive member +.Ar position , +which must be specified. +.It Fl c +Whenever an archive is created, an informational message to that effect +is written to standard error. +If the +.Fl c +option is specified, +.Nm ar +creates the archive silently. +.It Fl d +Delete the specified archive files. +.It Fl i +Identical to the +.Fl b +option. +.It Fl m +Move the specified archive files within the archive. +If one of the options +.Fl a , +.Fl b +or +.Fl i +is specified, the files are moved before or after the +.Ar position +file in the archive. +If none of those options are specified, the files are moved +to the end of the archive. +.It Fl o +Set the access and modification times of extracted files to the +modification time of the file when it was entered into the archive. +This will fail if the user is not the owner of the extracted file +or the super-user. +.It Fl p +Write the contents of the specified archive files to the standard output. +If no files are specified, the contents of all the files in the archive +are written in the order they appear in the archive. +.It Fl q +(Quickly) append the specified files to the archive. +If the archive does not exist a new archive file is created. +Much faster than the +.Fl r +option, when creating a large archive +piece-by-piece, as no checking is done to see if the files already +exist in the archive. +.It Fl r +Replace or add the specified files to the archive. +If the archive does not exist a new archive file is created. +Files that replace existing files do not change the order of the files +within the archive. +New files are appended to the archive unless one of the options +.Fl a , +.Fl b +or +.Fl i +is specified. +.It Fl T +Select and/or name archive members using only the first fifteen characters +of the archive member or command line file name. +The historic archive format had sixteen bytes for the name, but some +historic archiver and loader implementations were unable to handle names +that used the entire space. +This means that file names that are not unique in their first fifteen +characters can subsequently be confused. +A warning message is printed to the standard error output if any file +names are truncated. +(See +.Xr ar 5 +for more information.) +.It Fl t +List the specified files in the order in which they appear in the archive, +each on a separate line. +If no files are specified, all files in the archive are listed. +.It Fl u +Update files. +When used with the +.Fl r +option, files in the archive will be replaced +only if the disk file has a newer modification time than the file in +the archive. +When used with the +.Fl x +option, files in the archive will be extracted +only if the archive file has a newer modification time than the file +on disk. +.It Fl v +Provide verbose output. +When used with the +.Fl d , +.Fl m , +.Fl q +or +.Fl x +options, +.Nm ar +gives a file-by-file description of the archive modification. +This description consists of three, white-space separated fields: the +option letter, a dash (``-'') and the file name. +When used with the +.Fl r +option, +.Nm ar +displays the description as above, but the initial letter is an ``a'' if +the file is added to the archive and an ``r'' if the file replaces a file +already in the archive. +.Pp +When used with the +.Fl p +option, +the name of each printed file, +enclosed in less-than (``<'') and greater-than (``>'') characters, +is written to the standard output before +the contents of the file; +it is preceded by a single newline character, and +followed by two newline characters. +.Pp +When used with the +.Fl t +option, +.Nm ar +displays an ``ls -l'' style listing of information about the members of +the archive. +This listing consists of eight, white-space separated fields: +the file permissions (see +.Xr strmode 3 ), +the decimal user and group ID's separated by a single slash (``/''), +the file size (in bytes), the file modification time (in the +.Xr date 1 +format ``%b %e %H:%M %Y''), and the name of the file. +.It Fl x +Extract the specified archive members into the files named by the command +line arguments. +If no members are specified, all the members of the archive are extracted into +the current directory. +.Pp +If the file does not exist, it is created; if it does exist, the owner +and group will be unchanged. +The file access and modification times are the time of the extraction +(but see the +.Fl o +option). +The file permissions will be set to those of the file when it was entered +into the archive; this will fail if the user is not the owner of the +extracted file or the super-user. +.El +.Pp +The +.Nm ar +utility exits 0 on success, and >0 if an error occurs. +.Sh ENVIRONMENT +.Bl -tag -width indent -compact +.It Ev TMPDIR +The pathname of the directory to use when creating temporary files. +.El +.Sh FILES +.Bl -tag -width indent -compact +.It Pa /tmp +default temporary file directory +.It Pa ar.XXXXXX +temporary file names +.El +.Sh COMPATIBILITY +By default, +.Nm ar +writes archives that may be incompatible with historic archives, as +the format used for storing archive members with names longer than +fifteen characters has changed. +This implementation of +.Nm ar +is backward compatible with previous versions of +.Nm ar +in that it can read and write (using the +.Fl T +option) historic archives. +The +.Fl T +option is provided for compatibility only, and will be deleted +in a future release. +See +.Xr ar 5 +for more information. +.SH STANDARDS +The +.Nm ar +utility is expected to offer a superset of the +.St -p1003.2 +functionality. +.Sh SEE ALSO +.Xr ld 1 , +.Xr ranlib 1 , +.Xr strmode 3 , +.Xr ar 5 diff --git a/usr.bin/ar/ar.5.5 b/usr.bin/ar/ar.5.5 new file mode 100644 index 00000000000..2ab5b2d9985 --- /dev/null +++ b/usr.bin/ar/ar.5.5 @@ -0,0 +1,146 @@ +.\" Copyright (c) 1990, 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. +.\" +.\" @(#)ar.5.5 8.2 (Berkeley) 6/1/94 +.\" +.Dd June 1, 1994 +.Dt AR 5 +.Os +.Sh NAME +.Nm ar +.Nd archive (library) file format +.Sh SYNOPSIS +.Fd #include +.Sh DESCRIPTION +The archive command +.Nm ar +combines several files into one. +Archives are mainly used as libraries of object files intended to be +loaded using the link-editor +.Xr ld 1 . +.Pp +A file created with +.Nm ar +begins with the ``magic'' string "!\en". +The rest of the archive is made up of objects, each of which is composed +of a header for a file, a possible file name, and the file contents. +The header is portable between machine architectures, and, if the file +contents are printable, the archive is itself printable. +.Pp +The header is made up of six variable length +.Tn ASCII +fields, followed by a +two character trailer. +The fields are the object name (16 characters), the file last modification +time (12 characters), the user and group id's (each 6 characters), the file +mode (8 characters) and the file size (10 characters). +All numeric fields are in decimal, except for the file mode which is in +octal. +.Pp +The modification time is the file +.Fa st_mtime +field, i.e., +.Dv CUT +seconds since +the epoch. +The user and group id's are the file +.Fa st_uid +and +.Fa st_gid +fields. +The file mode is the file +.Fa st_mode +field. +The file size is the file +.Fa st_size +field. +The two-byte trailer is the string "\`\en". +.Pp +Only the name field has any provision for overflow. +If any file name is more than 16 characters in length or contains an +embedded space, the string "#1/" followed by the +.Tn ASCII +length of the +name is written in the name field. +The file size (stored in the archive header) is incremented by the length +of the name. +The name is then written immediately following the archive header. +.Pp +Any unused characters in any of these fields are written as space +characters. +If any fields are their particular maximum number of characters in +length, there will be no separation between the fields. +.Pp +Objects in the archive are always an even number of bytes long; files +which are an odd number of bytes long are padded with a newline (``\en'') +character, although the size in the header does not reflect this. +.Sh SEE ALSO +.Xr ar 1 , +.Xr stat 2 +.Sh HISTORY +There have been at least four +.Nm ar +formats. +The first was denoted by the leading ``magic'' number 0177555 (stored as +type int). +These archives were almost certainly created on a 16-bit machine, and +contain headers made up of five fields. +The fields are the object name (8 characters), the file last modification +time (type long), the user id (type char), the file mode (type char) and +the file size (type unsigned int). +Files were padded to an even number of bytes. +.Pp +The second was denoted by the leading ``magic'' number 0177545 (stored as +type int). +These archives may have been created on either 16 or 32-bit machines, and +contain headers made up of six fields. +The fields are the object name (14 characters), the file last modification +time (type long), the user and group id's (each type char), the file mode +(type int) and the file size (type long). +Files were padded to an even number of bytes. +For more information on converting from this format see +.Xr arcv 8 . +.ne 1i +.Pp +The current archive format (without support for long character names and +names with embedded spaces) was introduced in +.Bx 4.0 . +The headers were the same as the current format, with the exception that +names longer than 16 characters were truncated, and names with embedded +spaces (and often trailing spaces) were not supported. +It has been extended for these reasons, +as described above. +This format first appeared in 4.4BSD. +.Sh COMPATIBILITY +No archive format is currently specified by any standard. +.At V +has historically distributed archives in a different format from +all of the above. diff --git a/usr.bin/ar/archive.c b/usr.bin/ar/archive.c new file mode 100644 index 00000000000..9dd6b2077f5 --- /dev/null +++ b/usr.bin/ar/archive.c @@ -0,0 +1,326 @@ +/*- + * 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 + * Hugh Smith at The University of Guelph. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE 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[] = "@(#)archive.c 8.4 (Berkeley) 4/27/95"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "archive.h" +#include "extern.h" + +typedef struct ar_hdr HDR; +static char hb[sizeof(HDR) + 1]; /* real header */ + +int +open_archive(mode) + int mode; +{ + int created, fd, nr; + char buf[SARMAG]; + + created = 0; + if (mode & O_CREAT) { + mode |= O_EXCL; + if ((fd = open(archive, mode, DEFFILEMODE)) >= 0) { + /* POSIX.2 puts create message on stderr. */ + if (!(options & AR_C)) + warnx("creating archive %s", archive); + created = 1; + goto opened; + } + if (errno != EEXIST) + error(archive); + mode &= ~O_EXCL; + } + if ((fd = open(archive, mode, DEFFILEMODE)) < 0) + error(archive); + + /* + * Attempt to place a lock on the opened file - if we get an + * error then someone is already working on this library (or + * it's going across NFS). + */ +opened: if (flock(fd, LOCK_EX|LOCK_NB) && errno != EOPNOTSUPP) + error(archive); + + /* + * If not created, O_RDONLY|O_RDWR indicates that it has to be + * in archive format. + */ + if (!created && + ((mode & O_ACCMODE) == O_RDONLY || (mode & O_ACCMODE) == O_RDWR)) { + if ((nr = read(fd, buf, SARMAG) != SARMAG)) { + if (nr >= 0) + badfmt(); + error(archive); + } else if (bcmp(buf, ARMAG, SARMAG)) + badfmt(); + } else if (write(fd, ARMAG, SARMAG) != SARMAG) + error(archive); + return (fd); +} + +void +close_archive(fd) + int fd; +{ + + (void)close(fd); /* Implicit unlock. */ +} + +/* Convert ar header field to an integer. */ +#define AR_ATOI(from, to, len, base) { \ + memmove(buf, from, len); \ + buf[len] = '\0'; \ + to = strtol(buf, (char **)NULL, base); \ +} + +/* + * get_arobj -- + * read the archive header for this member + */ +int +get_arobj(fd) + int fd; +{ + struct ar_hdr *hdr; + int len, nr; + char *p, buf[20]; + + nr = read(fd, hb, sizeof(HDR)); + if (nr != sizeof(HDR)) { + if (!nr) + return (0); + if (nr < 0) + error(archive); + badfmt(); + } + + hdr = (struct ar_hdr *)hb; + if (strncmp(hdr->ar_fmag, ARFMAG, sizeof(ARFMAG) - 1)) + badfmt(); + + /* Convert the header into the internal format. */ +#define DECIMAL 10 +#define OCTAL 8 + + AR_ATOI(hdr->ar_date, chdr.date, sizeof(hdr->ar_date), DECIMAL); + AR_ATOI(hdr->ar_uid, chdr.uid, sizeof(hdr->ar_uid), DECIMAL); + AR_ATOI(hdr->ar_gid, chdr.gid, sizeof(hdr->ar_gid), DECIMAL); + AR_ATOI(hdr->ar_mode, chdr.mode, sizeof(hdr->ar_mode), OCTAL); + AR_ATOI(hdr->ar_size, chdr.size, sizeof(hdr->ar_size), DECIMAL); + + /* Leading spaces should never happen. */ + if (hdr->ar_name[0] == ' ') + badfmt(); + + /* + * Long name support. Set the "real" size of the file, and the + * long name flag/size. + */ + if (!bcmp(hdr->ar_name, AR_EFMT1, sizeof(AR_EFMT1) - 1)) { + chdr.lname = len = atoi(hdr->ar_name + sizeof(AR_EFMT1) - 1); + if (len <= 0 || len > MAXNAMLEN) + badfmt(); + nr = read(fd, chdr.name, len); + if (nr != len) { + if (nr < 0) + error(archive); + badfmt(); + } + chdr.name[len] = 0; + chdr.size -= len; + } else { + chdr.lname = 0; + memmove(chdr.name, hdr->ar_name, sizeof(hdr->ar_name)); + + /* Strip trailing spaces, null terminate. */ + for (p = chdr.name + sizeof(hdr->ar_name) - 1; *p == ' '; --p); + *++p = '\0'; + } + return (1); +} + +static int already_written; + +/* + * put_arobj -- + * Write an archive member to a file. + */ +void +put_arobj(cfp, sb) + CF *cfp; + struct stat *sb; +{ + int lname; + char *name; + struct ar_hdr *hdr; + off_t size; + + /* + * If passed an sb structure, reading a file from disk. Get stat(2) + * information, build a name and construct a header. (Files are named + * by their last component in the archive.) If not, then just write + * the last header read. + */ + if (sb) { + name = rname(cfp->rname); + (void)fstat(cfp->rfd, sb); + + /* + * If not truncating names and the name is too long or contains + * a space, use extended format 1. + */ + lname = strlen(name); + if (options & AR_TR) { + if (lname > OLDARMAXNAME) { + (void)fflush(stdout); + warnx("warning: %s truncated to %.*s", + name, OLDARMAXNAME, name); + (void)fflush(stderr); + } + (void)sprintf(hb, HDR3, name, sb->st_mtimespec.ts_sec, + sb->st_uid, sb->st_gid, sb->st_mode, sb->st_size, + ARFMAG); + lname = 0; + } else if (lname > sizeof(hdr->ar_name) || strchr(name, ' ')) + (void)sprintf(hb, HDR1, AR_EFMT1, lname, + sb->st_mtimespec.ts_sec, sb->st_uid, sb->st_gid, + sb->st_mode, sb->st_size + lname, ARFMAG); + else { + lname = 0; + (void)sprintf(hb, HDR2, name, sb->st_mtimespec.ts_sec, + sb->st_uid, sb->st_gid, sb->st_mode, sb->st_size, + ARFMAG); + } + size = sb->st_size; + } else { + lname = chdr.lname; + name = chdr.name; + size = chdr.size; + } + + if (write(cfp->wfd, hb, sizeof(HDR)) != sizeof(HDR)) + error(cfp->wname); + if (lname) { + if (write(cfp->wfd, name, lname) != lname) + error(cfp->wname); + already_written = lname; + } + copy_ar(cfp, size); + already_written = 0; +} + +/* + * copy_ar -- + * Copy size bytes from one file to another - taking care to handle the + * extra byte (for odd size files) when reading archives and writing an + * extra byte if necessary when adding files to archive. The length of + * the object is the long name plus the object itself; the variable + * already_written gets set if a long name was written. + * + * The padding is really unnecessary, and is almost certainly a remnant + * of early archive formats where the header included binary data which + * a PDP-11 required to start on an even byte boundary. (Or, perhaps, + * because 16-bit word addressed copies were faster?) Anyhow, it should + * have been ripped out long ago. + */ +void +copy_ar(cfp, size) + CF *cfp; + off_t size; +{ + static char pad = '\n'; + off_t sz; + int from, nr, nw, off, to; + char buf[8*1024]; + + if (!(sz = size)) + return; + + from = cfp->rfd; + to = cfp->wfd; + sz = size; + while (sz && (nr = read(from, buf, MIN(sz, sizeof(buf)))) > 0) { + sz -= nr; + for (off = 0; off < nr; nr -= off, off += nw) + if ((nw = write(to, buf + off, nr)) < 0) + error(cfp->wname); + } + if (sz) { + if (nr == 0) + badfmt(); + error(cfp->rname); + } + + if (cfp->flags & RPAD && (size + chdr.lname) & 1 && + (nr = read(from, buf, 1)) != 1) { + if (nr == 0) + badfmt(); + error(cfp->rname); + } + if (cfp->flags & WPAD && (size + already_written) & 1 && + write(to, &pad, 1) != 1) + error(cfp->wname); +} + +/* + * skip_arobj - + * Skip over an object -- taking care to skip the pad bytes. + */ +void +skip_arobj(fd) + int fd; +{ + off_t len; + + len = chdr.size + (chdr.size + chdr.lname & 1); + if (lseek(fd, len, SEEK_CUR) == (off_t)-1) + error(archive); +} diff --git a/usr.bin/ar/misc.c b/usr.bin/ar/misc.c new file mode 100644 index 00000000000..f9c0d14578c --- /dev/null +++ b/usr.bin/ar/misc.c @@ -0,0 +1,149 @@ +/*- + * 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 + * Hugh Smith at The University of Guelph. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)misc.c 8.4 (Berkeley) 4/27/95"; +#endif /* not lint */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "archive.h" +#include "extern.h" +#include "pathnames.h" + +char *tname = "temporary file"; /* temporary file "name" */ + +int +tmp() +{ + extern char *envtmp; + sigset_t set, oset; + static int first; + int fd; + char path[MAXPATHLEN]; + + if (!first && !envtmp) { + envtmp = getenv("TMPDIR"); + first = 1; + } + + if (envtmp) + (void)sprintf(path, "%s/%s", envtmp, _NAME_ARTMP); + else + strcpy(path, _PATH_ARTMP); + + sigfillset(&set); + (void)sigprocmask(SIG_BLOCK, &set, &oset); + if ((fd = mkstemp(path)) == -1) + error(tname); + (void)unlink(path); + (void)sigprocmask(SIG_SETMASK, &oset, NULL); + return (fd); +} + +/* + * files -- + * See if the current file matches any file in the argument list; if it + * does, remove it from the argument list. + */ +char * +files(argv) + char **argv; +{ + char **list, *p; + + for (list = argv; *list; ++list) + if (compare(*list)) { + p = *list; + for (; list[0] = list[1]; ++list) + continue; + return (p); + } + return (NULL); +} + +void +orphans(argv) + char **argv; +{ + + for (; *argv; ++argv) + warnx("%s: not found in archive", *argv); +} + +char * +rname(path) + char *path; +{ + char *ind; + + return ((ind = strrchr(path, '/')) ? ind + 1 : path); +} + +int +compare(dest) + char *dest; +{ + + if (options & AR_TR) + return (!strncmp(chdr.name, rname(dest), OLDARMAXNAME)); + return (!strcmp(chdr.name, rname(dest))); +} + +void +badfmt() +{ + + errno = EFTYPE; + err(1, "%s", archive); +} + +void +error(name) + char *name; +{ + + err(1, "%s", name); +} diff --git a/usr.bin/ar/replace.c b/usr.bin/ar/replace.c new file mode 100644 index 00000000000..4500b9077f6 --- /dev/null +++ b/usr.bin/ar/replace.c @@ -0,0 +1,178 @@ +/*- + * 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 + * Hugh Smith at The University of Guelph. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE 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[] = "@(#)replace.c 8.4 (Berkeley) 4/27/95"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "archive.h" +#include "extern.h" + +/* + * replace -- + * Replace or add named members to archive. Entries already in the + * archive are swapped in place. Others are added before or after + * the key entry, based on the a, b and i options. If the u option + * is specified, modification dates select for replacement. + */ +int +replace(argv) + char **argv; +{ + char *file; + int afd, curfd, errflg, exists, mods, sfd, tfd1, tfd2; + struct stat sb; + CF cf; + off_t size, tsize; + + errflg = 0; + /* + * If doesn't exist, simply append to the archive. There's + * a race here, but it's pretty short, and not worth fixing. + */ + exists = !stat(archive, &sb); + afd = open_archive(O_CREAT|O_RDWR); + + if (!exists) { + tfd1 = -1; + tfd2 = tmp(); + goto append; + } + + tfd1 = tmp(); /* Files before key file. */ + tfd2 = tmp(); /* Files after key file. */ + + /* + * Break archive into two parts -- entries before and after the key + * entry. If positioning before the key, place the key at the + * beginning of the after key entries and if positioning after the + * key, place the key at the end of the before key entries. Put it + * all back together at the end. + */ + mods = (options & (AR_A|AR_B)); + for (curfd = tfd1; get_arobj(afd);) { + if (*argv && (file = files(argv))) { + if ((sfd = open(file, O_RDONLY)) < 0) { + errflg = 1; + warn("%s", file); + goto useold; + } + (void)fstat(sfd, &sb); + if (options & AR_U && sb.st_mtime <= chdr.date) { + (void)close(sfd); + goto useold; + } + + if (options & AR_V) + (void)printf("r - %s\n", file); + + /* Read from disk, write to an archive; pad on write */ + SETCF(sfd, file, curfd, tname, WPAD); + put_arobj(&cf, &sb); + (void)close(sfd); + skip_arobj(afd); + continue; + } + + if (mods && compare(posname)) { + mods = 0; + if (options & AR_B) + curfd = tfd2; + /* Read and write to an archive; pad on both. */ + SETCF(afd, archive, curfd, tname, RPAD|WPAD); + put_arobj(&cf, (struct stat *)NULL); + if (options & AR_A) + curfd = tfd2; + } else { + /* Read and write to an archive; pad on both. */ +useold: SETCF(afd, archive, curfd, tname, RPAD|WPAD); + put_arobj(&cf, (struct stat *)NULL); + } + } + + if (mods) { + warnx("%s: archive member not found", posarg); + close_archive(afd); + return (1); + } + + /* Append any left-over arguments to the end of the after file. */ +append: while (file = *argv++) { + if (options & AR_V) + (void)printf("a - %s\n", file); + if ((sfd = open(file, O_RDONLY)) < 0) { + errflg = 1; + warn("%s", file); + continue; + } + (void)fstat(sfd, &sb); + /* Read from disk, write to an archive; pad on write. */ + SETCF(sfd, file, + options & (AR_A|AR_B) ? tfd1 : tfd2, tname, WPAD); + put_arobj(&cf, &sb); + (void)close(sfd); + } + + (void)lseek(afd, (off_t)SARMAG, SEEK_SET); + + SETCF(tfd1, tname, afd, archive, NOPAD); + if (tfd1 != -1) { + tsize = size = lseek(tfd1, (off_t)0, SEEK_CUR); + (void)lseek(tfd1, (off_t)0, SEEK_SET); + copy_ar(&cf, size); + } else + tsize = 0; + + tsize += size = lseek(tfd2, (off_t)0, SEEK_CUR); + (void)lseek(tfd2, (off_t)0, SEEK_SET); + cf.rfd = tfd2; + copy_ar(&cf, size); + + (void)ftruncate(afd, tsize + SARMAG); + close_archive(afd); + return (errflg); +} diff --git a/usr.bin/biff/biff.c b/usr.bin/biff/biff.c new file mode 100644 index 00000000000..0c5fd331a5c --- /dev/null +++ b/usr.bin/biff/biff.c @@ -0,0 +1,105 @@ +/* + * 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1980, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)biff.c 8.2 (Berkeley) 4/29/95"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +static void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct stat sb; + int ch; + char *name; + + while ((ch = getopt(argc, argv, "")) != EOF) + switch(ch) { + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if ((name = ttyname(STDERR_FILENO)) == NULL) + err(2, "tty"); + + if (stat(name, &sb)) + err(2, "stat"); + + if (*argv == NULL) { + (void)printf("is %s\n", sb.st_mode&0100 ? "y" : "n"); + exit(sb.st_mode & 0100 ? 0 : 1); + } + + switch(argv[0][0]) { + case 'n': + if (chmod(name, sb.st_mode & ~0100) < 0) + err(2, name); + break; + case 'y': + if (chmod(name, sb.st_mode | 0100) < 0) + err(2, name); + break; + default: + usage(); + } + exit(sb.st_mode & 0100 ? 0 : 1); +} + +static void +usage() +{ + (void)fprintf(stderr, "usage: biff [y | n]\n"); + exit(2); +} diff --git a/usr.bin/cal/cal.1 b/usr.bin/cal/cal.1 new file mode 100644 index 00000000000..f1b5a91637e --- /dev/null +++ b/usr.bin/cal/cal.1 @@ -0,0 +1,81 @@ +.\" 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 +.\" Kim Letkeman. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)cal.1 8.2 (Berkeley) 4/28/95 +.\" +.Dd April 28, 1995 +.Dt CAL 1 +.Os +.Sh NAME +.Nm cal +.Nd displays a calendar +.Sh SYNOPSIS +.Nm cal +.Op Fl jy +.Op Oo Ar month Oc Ar \ year +.Sh DESCRIPTION +.Nm Cal +displays a simple calendar. +If arguments are not specified, +the current month is displayed. +The options are as follows: +.Bl -tag -width Ds +.It Fl j +Display julian dates (days one-based, numbered from January 1). +.It Fl y +Display a calendar for the current year. +.El +.Pp +A single parameter specifies the year (1 - 9999) to be displayed; +note the year must be fully specified: +.Dq Li cal 89 +will +.Em not +display a calendar for 1989. +Two parameters denote the month (1 - 12) and year. +If no parameters are specified, the current month's calendar is +displayed. +.Pp +A year starts on Jan 1. +.Pp +The Gregorian Reformation is assumed to have occurred in 1752 on the 3rd +of September. +By this time, most countries had recognized the reformation (although a +few did not recognize it until the early 1900's.) +Ten days following that date were eliminated by the reformation, so the +calendar for that month is a bit unusual. +.Sh HISTORY +A +.Nm +command appeared in Version 6 AT&T UNIX. diff --git a/usr.bin/calendar/calendar.c b/usr.bin/calendar/calendar.c new file mode 100644 index 00000000000..248d97db9d7 --- /dev/null +++ b/usr.bin/calendar/calendar.c @@ -0,0 +1,411 @@ +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)calendar.c 8.4 (Berkeley) 1/7/95"; +#endif /* not lint */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pathnames.h" + +struct passwd *pw; +int doall; + +void cal __P((void)); +void closecal __P((FILE *)); +int getday __P((char *)); +int getfield __P((char *, char **, int *)); +int getmonth __P((char *)); +int isnow __P((char *)); +FILE *opencal __P((void)); +void settime __P((void)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern int optind; + int ch; + + while ((ch = getopt(argc, argv, "-a")) != EOF) + switch (ch) { + case '-': /* backward contemptible */ + case 'a': + if (getuid()) { + errno = EPERM; + err(1, NULL); + } + doall = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc) + usage(); + + settime(); + if (doall) + while ((pw = getpwent()) != NULL) { + (void)setegid(pw->pw_gid); + (void)seteuid(pw->pw_uid); + if (!chdir(pw->pw_dir)) + cal(); + (void)seteuid(0); + } + else + cal(); + exit(0); +} + +void +cal() +{ + register int printing; + register char *p; + FILE *fp; + int ch; + char buf[2048 + 1]; + + if ((fp = opencal()) == NULL) + return; + for (printing = 0; fgets(buf, sizeof(buf), stdin) != NULL;) { + if ((p = strchr(buf, '\n')) != NULL) + *p = '\0'; + else + while ((ch = getchar()) != '\n' && ch != EOF); + if (buf[0] == '\0') + continue; + if (buf[0] != '\t') + printing = isnow(buf) ? 1 : 0; + if (printing) + (void)fprintf(fp, "%s\n", buf); + } + closecal(fp); +} + +struct iovec header[] = { + "From: ", 6, + NULL, 0, + " (Reminder Service)\nTo: ", 24, + NULL, 0, + "\nSubject: ", 10, + NULL, 0, + "'s Calendar\nPrecedence: bulk\n\n", 30, +}; + +/* 1-based month, 0-based days, cumulative */ +int daytab[][14] = { + 0, -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364, + 0, -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365, +}; +struct tm *tp; +int *cumdays, offset, yrdays; +char dayname[10]; + +void +settime() +{ + time_t now; + + (void)time(&now); + tp = localtime(&now); + if (isleap(tp->tm_year + 1900)) { + yrdays = DAYSPERLYEAR; + cumdays = daytab[1]; + } else { + yrdays = DAYSPERNYEAR; + cumdays = daytab[0]; + } + /* Friday displays Monday's events */ + offset = tp->tm_wday == 5 ? 3 : 1; + header[5].iov_base = dayname; + header[5].iov_len = strftime(dayname, sizeof(dayname), "%A", tp); +} + +/* + * Possible date formats include any combination of: + * 3-charmonth (January, Jan, Jan) + * 3-charweekday (Friday, Monday, mon.) + * numeric month or day (1, 2, 04) + * + * Any character may separate them, or they may not be separated. Any line, + * following a line that is matched, that starts with "whitespace", is shown + * along with the matched line. + */ +int +isnow(endp) + char *endp; +{ + int day, flags, month, v1, v2; + +#define F_ISMONTH 0x01 +#define F_ISDAY 0x02 + flags = 0; + /* didn't recognize anything, skip it */ + if (!(v1 = getfield(endp, &endp, &flags))) + return (0); + if (flags & F_ISDAY || v1 > 12) { + /* found a day */ + day = v1; + /* if no recognizable month, assume just a day alone */ + if (!(month = getfield(endp, &endp, &flags))) + month = tp->tm_mon + 1; + } else if (flags & F_ISMONTH) { + month = v1; + /* if no recognizable day, assume the first */ + if (!(day = getfield(endp, &endp, &flags))) + day = 1; + } else { + v2 = getfield(endp, &endp, &flags); + if (flags & F_ISMONTH) { + day = v1; + month = v2; + } else { + /* F_ISDAY set, v2 > 12, or no way to tell */ + month = v1; + /* if no recognizable day, assume the first */ + day = v2 ? v2 : 1; + } + } + if (flags & F_ISDAY) + day = tp->tm_mday + (((day - 1) - tp->tm_wday + 7) % 7); + day = cumdays[month] + day; + + /* if today or today + offset days */ + if (day >= tp->tm_yday && day <= tp->tm_yday + offset) + return (1); + /* if number of days left in this year + days to event in next year */ + if (yrdays - tp->tm_yday + day <= offset) + return (1); + return (0); +} + +int +getfield(p, endp, flags) + char *p, **endp; + int *flags; +{ + int val; + char *start, savech; + + for (; !isdigit(*p) && !isalpha(*p) && *p != '*'; ++p); + if (*p == '*') { /* `*' is current month */ + *flags |= F_ISMONTH; + *endp = p+1; + return (tp->tm_mon + 1); + } + if (isdigit(*p)) { + val = strtol(p, &p, 10); /* if 0, it's failure */ + for (; !isdigit(*p) && !isalpha(*p) && *p != '*'; ++p); + *endp = p; + return (val); + } + for (start = p; isalpha(*++p);); + savech = *p; + *p = '\0'; + if ((val = getmonth(start)) != 0) + *flags |= F_ISMONTH; + else if ((val = getday(start)) != 0) + *flags |= F_ISDAY; + else { + *p = savech; + return (0); + } + for (*p = savech; !isdigit(*p) && !isalpha(*p) && *p != '*'; ++p); + *endp = p; + return (val); +} + +char path[MAXPATHLEN + 1]; + +FILE * +opencal() +{ + int fd, pdes[2]; + + /* open up calendar file as stdin */ + if (!freopen("calendar", "r", stdin)) { + if (doall) + return (NULL); + errx(1, "no calendar file."); + } + if (pipe(pdes) < 0) + return (NULL); + switch (vfork()) { + case -1: /* error */ + (void)close(pdes[0]); + (void)close(pdes[1]); + return (NULL); + case 0: + /* child -- stdin already setup, set stdout to pipe input */ + if (pdes[1] != STDOUT_FILENO) { + (void)dup2(pdes[1], STDOUT_FILENO); + (void)close(pdes[1]); + } + (void)close(pdes[0]); + execl(_PATH_CPP, "cpp", "-P", "-I.", _PATH_INCLUDE, NULL); + (void)fprintf(stderr, + "calendar: execl: %s: %s.\n", _PATH_CPP, strerror(errno)); + _exit(1); + } + /* parent -- set stdin to pipe output */ + (void)dup2(pdes[0], STDIN_FILENO); + (void)close(pdes[0]); + (void)close(pdes[1]); + + /* not reading all calendar files, just set output to stdout */ + if (!doall) + return (stdout); + + /* set output to a temporary file, so if no output don't send mail */ + (void)snprintf(path, sizeof(path), "%s/_calXXXXXX", _PATH_TMP); + if ((fd = mkstemp(path)) < 0) + return (NULL); + return (fdopen(fd, "w+")); +} + +void +closecal(fp) + FILE *fp; +{ + struct stat sbuf; + int nread, pdes[2], status; + char buf[1024]; + + if (!doall) + return; + + (void)rewind(fp); + if (fstat(fileno(fp), &sbuf) || !sbuf.st_size) + goto done; + if (pipe(pdes) < 0) + goto done; + switch (vfork()) { + case -1: /* error */ + (void)close(pdes[0]); + (void)close(pdes[1]); + goto done; + case 0: + /* child -- set stdin to pipe output */ + if (pdes[0] != STDIN_FILENO) { + (void)dup2(pdes[0], STDIN_FILENO); + (void)close(pdes[0]); + } + (void)close(pdes[1]); + execl(_PATH_SENDMAIL, "sendmail", "-i", "-t", "-F", + "\"Reminder Service\"", "-f", "root", NULL); + (void)fprintf(stderr, + "calendar: %s: %s.\n", _PATH_SENDMAIL, strerror(errno)); + _exit(1); + } + /* parent -- write to pipe input */ + (void)close(pdes[0]); + + header[1].iov_base = header[3].iov_base = pw->pw_name; + header[1].iov_len = header[3].iov_len = strlen(pw->pw_name); + writev(pdes[1], header, 7); + while ((nread = read(fileno(fp), buf, sizeof(buf))) > 0) + (void)write(pdes[1], buf, nread); + (void)close(pdes[1]); +done: (void)fclose(fp); + (void)unlink(path); + while (wait(&status) >= 0); +} + +static char *months[] = { + "jan", "feb", "mar", "apr", "may", "jun", + "jul", "aug", "sep", "oct", "nov", "dec", NULL, +}; + +int +getmonth(s) + register char *s; +{ + register char **p; + + for (p = months; *p; ++p) + if (!strncasecmp(s, *p, 3)) + return ((p - months) + 1); + return (0); +} + +static char *days[] = { + "sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL, +}; + +int +getday(s) + register char *s; +{ + register char **p; + + for (p = days; *p; ++p) + if (!strncasecmp(s, *p, 3)) + return ((p - days) + 1); + return (0); +} + +void +usage() +{ + (void)fprintf(stderr, "usage: calendar [-a]\n"); + exit(1); +} diff --git a/usr.bin/cap_mkdb/cap_mkdb.c b/usr.bin/cap_mkdb/cap_mkdb.c new file mode 100644 index 00000000000..e3c3ce52a1d --- /dev/null +++ b/usr.bin/cap_mkdb/cap_mkdb.c @@ -0,0 +1,259 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char 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[] = "@(#)cap_mkdb.c 8.2 (Berkeley) 4/27/95"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void db_build __P((char **)); +void dounlink __P((void)); +void usage __P((void)); + +DB *capdbp; +int verbose; +char *capdb, *capname, buf[8 * 1024]; + +HASHINFO openinfo = { + 4096, /* bsize */ + 16, /* ffactor */ + 256, /* nelem */ + 2048 * 1024, /* cachesize */ + NULL, /* hash() */ + 0 /* lorder */ +}; + +/* + * Mkcapdb creates a capability hash database for quick retrieval of capability + * records. The database contains 2 types of entries: records and references + * marked by the first byte in the data. A record entry contains the actual + * capability record whereas a reference contains the name (key) under which + * the correct record is stored. + */ +int +main(argc, argv) + int argc; + char *argv[]; +{ + int c; + + capname = NULL; + while ((c = getopt(argc, argv, "f:v")) != EOF) { + switch(c) { + case 'f': + capname = optarg; + break; + case 'v': + verbose = 1; + break; + case '?': + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (*argv == NULL) + usage(); + + /* + * The database file is the first argument if no name is specified. + * Make arrangements to unlink it if exit badly. + */ + (void)snprintf(buf, sizeof(buf), "%s.db", capname ? capname : *argv); + if ((capname = strdup(buf)) == NULL) + err(1, ""); + if ((capdbp = dbopen(capname, O_CREAT | O_TRUNC | O_RDWR, + DEFFILEMODE, DB_HASH, &openinfo)) == NULL) + err(1, "%s", buf); + + if (atexit(dounlink)) + err(1, "atexit"); + + db_build(argv); + + if (capdbp->close(capdbp) < 0) + err(1, "%s", capname); + capname = NULL; + exit(0); +} + +void +dounlink() +{ + if (capname != NULL) + (void)unlink(capname); +} + +/* + * Any changes to these definitions should be made also in the getcap(3) + * library routines. + */ +#define RECOK (char)0 +#define TCERR (char)1 +#define SHADOW (char)2 + +/* + * Db_build() builds the name and capabilty databases according to the + * details above. + */ +void +db_build(ifiles) + char **ifiles; +{ + DBT key, data; + recno_t reccnt; + size_t len, bplen; + int st; + char *bp, *p, *t; + + data.data = NULL; + key.data = NULL; + for (reccnt = 0, bplen = 0; (st = cgetnext(&bp, ifiles)) > 0;) { + + /* + * Allocate enough memory to store record, terminating + * NULL and one extra byte. + */ + len = strlen(bp); + if (bplen <= len + 2) { + bplen += MAX(256, len + 2); + if ((data.data = realloc(data.data, bplen)) == NULL) + err(1, ""); + } + + /* Find the end of the name field. */ + if ((p = strchr(bp, ':')) == NULL) { + warnx("no name field: %.*s", MIN(len, 20), bp); + continue; + } + + /* First byte of stored record indicates status. */ + switch(st) { + case 1: + ((char *)(data.data))[0] = RECOK; + break; + case 2: + ((char *)(data.data))[0] = TCERR; + warnx("Record not tc expanded: %.*s", p - bp, bp); + break; + } + + /* Create the stored record. */ + memmove(&((u_char *)(data.data))[1], bp, len + 1); + data.size = len + 2; + + /* Store the record under the name field. */ + key.data = bp; + key.size = p - bp; + + switch(capdbp->put(capdbp, &key, &data, R_NOOVERWRITE)) { + case -1: + err(1, "put"); + /* NOTREACHED */ + case 1: + warnx("ignored duplicate: %.*s", + key.size, (char *)key.data); + continue; + } + ++reccnt; + + /* If only one name, ignore the rest. */ + if ((p = strchr(bp, '|')) == NULL) + continue; + + /* The rest of the names reference the entire name. */ + ((char *)(data.data))[0] = SHADOW; + memmove(&((u_char *)(data.data))[1], key.data, key.size); + data.size = key.size + 1; + + /* Store references for other names. */ + for (p = t = bp;; ++p) { + if (p > t && (*p == ':' || *p == '|')) { + key.size = p - t; + key.data = t; + switch(capdbp->put(capdbp, + &key, &data, R_NOOVERWRITE)) { + case -1: + err(1, "put"); + /* NOTREACHED */ + case 1: + warnx("ignored duplicate: %.*s", + key.size, (char *)key.data); + } + t = p + 1; + } + if (*p == ':') + break; + } + } + + switch(st) { + case -1: + err(1, "file argument"); + /* NOTREACHED */ + case -2: + errx(1, "potential reference loop detected"); + /* NOTREACHED */ + } + + if (verbose) + (void)printf("cap_mkdb: %d capability records\n", reccnt); +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: cap_mkdb [-v] [-f outfile] file1 [file2 ...]\n"); + exit(1); +} diff --git a/usr.bin/chflags/chflags.1 b/usr.bin/chflags/chflags.1 new file mode 100644 index 00000000000..8b07e9a77e0 --- /dev/null +++ b/usr.bin/chflags/chflags.1 @@ -0,0 +1,124 @@ +.\" Copyright (c) 1989, 1990, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)chflags.1 8.4 (Berkeley) 5/2/95 +.\" +.Dd May 2, 1995 +.Dt CHFLAGS 1 +.Os +.Sh NAME +.Nm chflags +.Nd change file flags +.Sh SYNOPSIS +.Nm chflags +.Oo +.Fl R +.Op Fl H | Fl L | Fl P +.Oc +.Ar flags +.Ar file ... +.Sh DESCRIPTION +The +.Nm chflags +utility modifies the file flags of the listed files +as specified by the +.Ar flags +operand. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl H +If the +.Fl R +option is specified, symbolic links on the command line are followed. +(Symbolic links encountered in the tree traversal are not followed.) +.It Fl L +If the +.Fl R +option is specified, all symbolic links are followed. +.It Fl P +If the +.Fl R +option is specified, no symbolic links are followed. +.It Fl R +Change the file flags for the file hierarchies rooted +in the files instead of just the files themselves. +.El +.Pp +Flags are a comma separated list of keywords. +The following keywords are currently defined: +.Bd -literal -offset indent compact +.\"arch nothing yet. +opaque set the opaque flag (owner or super-user only) +nodump set the nodump flag (owner or super-user only) +sappnd set the system append-only flag (super-user only) +schg set the system immutable flag (super-user only) +uappnd set the user append-only flag (owner or super-user only) +uchg set the user immutable flag (owner or super-user only) +.Ed +.Pp +Putting the letters +.Dq no +before an option causes the flag to be turned off. +For example: +.Bd -literal -offset indent compact +nouchg the immutable bit should be cleared +.Ed +.Pp +Symbolic links do not have flags, so unless the +.Fl H +or +.Fl L +option is set, +.Nm chflags +on a symbolic link always succeeds and has no effect. +The +.Fl H , +.Fl L +and +.Fl P +options are ignored unless the +.Fl R +option is specified. +In addition, these options override each other and the +command's actions are determined by the last one specified. +.Pp +The +.Nm chflags +utility exits 0 on success, and >0 if an error occurs. +.Sh SEE ALSO +.Xr chflags 2 , +.Xr stat 2 , +.Xr fts 3 , +.Xr symlink 7 diff --git a/usr.bin/ctags/ctags.c b/usr.bin/ctags/ctags.c new file mode 100644 index 00000000000..64f7c93e4d9 --- /dev/null +++ b/usr.bin/ctags/ctags.c @@ -0,0 +1,272 @@ +/* + * Copyright (c) 1987, 1993, 1994, 1995 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1987, 1993, 1994, 1995\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)ctags.c 8.4 (Berkeley) 2/7/95"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include + +#include "ctags.h" + +/* + * ctags: create a tags file + */ + +NODE *head; /* head of the sorted binary tree */ + + /* boolean "func" (see init()) */ +bool _wht[256], _etk[256], _itk[256], _btk[256], _gd[256]; + +FILE *inf; /* ioptr for current input file */ +FILE *outf; /* ioptr for tags file */ + +long lineftell; /* ftell after getc( inf ) == '\n' */ + +int lineno; /* line number of current line */ +int dflag; /* -d: non-macro defines */ +int tflag; /* -t: create tags for typedefs */ +int vflag; /* -v: vgrind style index output */ +int wflag; /* -w: suppress warnings */ +int xflag; /* -x: cxref style output */ + +char *curfile; /* current input file name */ +char searchar = '/'; /* use /.../ searches by default */ +char lbuf[LINE_MAX]; + +void init __P((void)); +void find_entries __P((char *)); + +int +main(argc, argv) + int argc; + char **argv; +{ + static char *outfile = "tags"; /* output file */ + int aflag; /* -a: append to tags */ + int uflag; /* -u: update tags */ + int exit_val; /* exit value */ + int step; /* step through args */ + int ch; /* getopts char */ + char cmd[100]; /* too ugly to explain */ + + aflag = uflag = NO; + while ((ch = getopt(argc, argv, "BFadf:tuwvx")) != EOF) + switch(ch) { + case 'B': + searchar = '?'; + break; + case 'F': + searchar = '/'; + break; + case 'a': + aflag++; + break; + case 'd': + dflag++; + break; + case 'f': + outfile = optarg; + break; + case 't': + tflag++; + break; + case 'u': + uflag++; + break; + case 'w': + wflag++; + break; + case 'v': + vflag++; + case 'x': + xflag++; + break; + case '?': + default: + goto usage; + } + argv += optind; + argc -= optind; + if (!argc) { +usage: (void)fprintf(stderr, + "usage: ctags [-BFadtuwvx] [-f tagsfile] file ...\n"); + exit(1); + } + + init(); + + for (exit_val = step = 0; step < argc; ++step) + if (!(inf = fopen(argv[step], "r"))) { + warn("%s", argv[step]); + exit_val = 1; + } + else { + curfile = argv[step]; + find_entries(argv[step]); + (void)fclose(inf); + } + + if (head) + if (xflag) + put_entries(head); + else { + if (uflag) { + for (step = 0; step < argc; step++) { + (void)sprintf(cmd, + "mv %s OTAGS; fgrep -v '\t%s\t' OTAGS >%s; rm OTAGS", + outfile, argv[step], + outfile); + system(cmd); + } + ++aflag; + } + if (!(outf = fopen(outfile, aflag ? "a" : "w"))) + err(exit_val, "%s", outfile); + put_entries(head); + (void)fclose(outf); + if (uflag) { + (void)sprintf(cmd, "sort -o %s %s", + outfile, outfile); + system(cmd); + } + } + exit(exit_val); +} + +/* + * init -- + * this routine sets up the boolean psuedo-functions which work by + * setting boolean flags dependent upon the corresponding character. + * Every char which is NOT in that string is false with respect to + * the pseudo-function. Therefore, all of the array "_wht" is NO + * by default and then the elements subscripted by the chars in + * CWHITE are set to YES. Thus, "_wht" of a char is YES if it is in + * the string CWHITE, else NO. + */ +void +init() +{ + int i; + unsigned char *sp; + + for (i = 0; i < 256; i++) { + _wht[i] = _etk[i] = _itk[i] = _btk[i] = NO; + _gd[i] = YES; + } +#define CWHITE " \f\t\n" + for (sp = CWHITE; *sp; sp++) /* white space chars */ + _wht[*sp] = YES; +#define CTOKEN " \t\n\"'#()[]{}=-+%*/&|^~!<>;,.:?" + for (sp = CTOKEN; *sp; sp++) /* token ending chars */ + _etk[*sp] = YES; +#define CINTOK "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz0123456789" + for (sp = CINTOK; *sp; sp++) /* valid in-token chars */ + _itk[*sp] = YES; +#define CBEGIN "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz" + for (sp = CBEGIN; *sp; sp++) /* token starting chars */ + _btk[*sp] = YES; +#define CNOTGD ",;" + for (sp = CNOTGD; *sp; sp++) /* invalid after-function chars */ + _gd[*sp] = NO; +} + +/* + * find_entries -- + * this routine opens the specified file and calls the function + * which searches the file. + */ +void +find_entries(file) + char *file; +{ + char *cp; + + lineno = 0; /* should be 1 ?? KB */ + if (cp = strrchr(file, '.')) { + if (cp[1] == 'l' && !cp[2]) { + int c; + + for (;;) { + if (GETC(==, EOF)) + return; + if (!iswhite(c)) { + rewind(inf); + break; + } + } +#define LISPCHR ";([" +/* lisp */ if (strchr(LISPCHR, c)) { + l_entries(); + return; + } +/* lex */ else { + /* + * we search all 3 parts of a lex file + * for C references. This may be wrong. + */ + toss_yysec(); + (void)strcpy(lbuf, "%%$"); + pfnote("yylex", lineno); + rewind(inf); + } + } +/* yacc */ else if (cp[1] == 'y' && !cp[2]) { + /* + * we search only the 3rd part of a yacc file + * for C references. This may be wrong. + */ + toss_yysec(); + (void)strcpy(lbuf, "%%$"); + pfnote("yyparse", lineno); + y_entries(); + } +/* fortran */ else if ((cp[1] != 'c' && cp[1] != 'h') && !cp[2]) { + if (PF_funcs()) + return; + rewind(inf); + } + } +/* C */ c_entries(); +} diff --git a/usr.bin/file/file.1 b/usr.bin/file/file.1 new file mode 100644 index 00000000000..8e70f847051 --- /dev/null +++ b/usr.bin/file/file.1 @@ -0,0 +1,72 @@ +.\" 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. +.\" +.\" @(#)file.1 8.2 (Berkeley) 3/31/94 +.\" +.Dd March 31, 1994 +.Dt FILE 1 +.Os +.Sh NAME +.Nm file +.Nd identify file content +.Sh SYNOPSIS +.Nm file +.Op Fl h +.Ar file ... +.Sh DESCRIPTION +.Nm File +tests the specified files and attempts to classify them. +If a file is a text file, +it attempts to determine the type of text (e.g. +.Xr csh 1 +commands or C source code). +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl h +If a symbolic link is specified, identify the file as a symbolic +link. +By default, symbolic links are only identified if they reference +non-existent files. +.El +.Sh COMPATIBILITY +Previous implementations of the +.Nm file +utility did not follow symbolic links by default. +.Sh BUGS +As many tests are used and +.Nm file +stops testing on the first successful one, it can often make mistakes. +.Sh HISTORY +A +.Nm file +command appeared in +.At v6 . diff --git a/usr.bin/ftp/cmds.c b/usr.bin/ftp/cmds.c new file mode 100644 index 00000000000..f64c73e7c07 --- /dev/null +++ b/usr.bin/ftp/cmds.c @@ -0,0 +1,2217 @@ +/* + * Copyright (c) 1985, 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)cmds.c 8.6 (Berkeley) 10/9/94"; +#endif /* not lint */ + +/* + * FTP User Program -- Command Routines. + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ftp_var.h" +#include "pathnames.h" + +jmp_buf jabort; +char *mname; +char *home = "/"; + +/* + * `Another' gets another argument, and stores the new argc and argv. + * It reverts to the top level (via main.c's intr()) on EOF/error. + * + * Returns false if no new arguments have been added. + */ +int +another(pargc, pargv, prompt) + int *pargc; + char ***pargv; + char *prompt; +{ + int len = strlen(line), ret; + + if (len >= sizeof(line) - 3) { + printf("sorry, arguments too long\n"); + intr(); + } + printf("(%s) ", prompt); + line[len++] = ' '; + if (fgets(&line[len], sizeof(line) - len, stdin) == NULL) + intr(); + len += strlen(&line[len]); + if (len > 0 && line[len - 1] == '\n') + line[len - 1] = '\0'; + makeargv(); + ret = margc > *pargc; + *pargc = margc; + *pargv = margv; + return (ret); +} + +/* + * Connect to peer server and + * auto-login, if possible. + */ +void +setpeer(argc, argv) + int argc; + char *argv[]; +{ + char *host; + short port; + + if (connected) { + printf("Already connected to %s, use close first.\n", + hostname); + code = -1; + return; + } + if (argc < 2) + (void) another(&argc, &argv, "to"); + if (argc < 2 || argc > 3) { + printf("usage: %s host-name [port]\n", argv[0]); + code = -1; + return; + } + port = sp->s_port; + if (argc > 2) { + port = atoi(argv[2]); + if (port <= 0) { + printf("%s: bad port number-- %s\n", argv[1], argv[2]); + printf ("usage: %s host-name [port]\n", argv[0]); + code = -1; + return; + } + port = htons(port); + } + host = hookup(argv[1], port); + if (host) { + int overbose; + + connected = 1; + /* + * Set up defaults for FTP. + */ + (void) strcpy(typename, "ascii"), type = TYPE_A; + curtype = TYPE_A; + (void) strcpy(formname, "non-print"), form = FORM_N; + (void) strcpy(modename, "stream"), mode = MODE_S; + (void) strcpy(structname, "file"), stru = STRU_F; + (void) strcpy(bytename, "8"), bytesize = 8; + if (autologin) + (void) login(argv[1]); + +#if defined(unix) && NBBY == 8 +/* + * this ifdef is to keep someone form "porting" this to an incompatible + * system and not checking this out. This way they have to think about it. + */ + overbose = verbose; + if (debug == 0) + verbose = -1; + if (command("SYST") == COMPLETE && overbose) { + char *cp, c; + cp = strchr(reply_string+4, ' '); + if (cp == NULL) + cp = strchr(reply_string+4, '\r'); + if (cp) { + if (cp[-1] == '.') + cp--; + c = *cp; + *cp = '\0'; + } + + printf("Remote system type is %s.\n", + reply_string+4); + if (cp) + *cp = c; + } + if (!strncmp(reply_string, "215 UNIX Type: L8", 17)) { + if (proxy) + unix_proxy = 1; + else + unix_server = 1; + /* + * Set type to 0 (not specified by user), + * meaning binary by default, but don't bother + * telling server. We can use binary + * for text files unless changed by the user. + */ + type = 0; + (void) strcpy(typename, "binary"); + if (overbose) + printf("Using %s mode to transfer files.\n", + typename); + } else { + if (proxy) + unix_proxy = 0; + else + unix_server = 0; + if (overbose && + !strncmp(reply_string, "215 TOPS20", 10)) + printf( +"Remember to set tenex mode when transfering binary files from this machine.\n"); + } + verbose = overbose; +#endif /* unix */ + } +} + +struct types { + char *t_name; + char *t_mode; + int t_type; + char *t_arg; +} types[] = { + { "ascii", "A", TYPE_A, 0 }, + { "binary", "I", TYPE_I, 0 }, + { "image", "I", TYPE_I, 0 }, + { "ebcdic", "E", TYPE_E, 0 }, + { "tenex", "L", TYPE_L, bytename }, + { NULL } +}; + +/* + * Set transfer type. + */ +void +settype(argc, argv) + int argc; + char *argv[]; +{ + struct types *p; + int comret; + + if (argc > 2) { + char *sep; + + printf("usage: %s [", argv[0]); + sep = " "; + for (p = types; p->t_name; p++) { + printf("%s%s", sep, p->t_name); + sep = " | "; + } + printf(" ]\n"); + code = -1; + return; + } + if (argc < 2) { + printf("Using %s mode to transfer files.\n", typename); + code = 0; + return; + } + for (p = types; p->t_name; p++) + if (strcmp(argv[1], p->t_name) == 0) + break; + if (p->t_name == 0) { + printf("%s: unknown mode\n", argv[1]); + code = -1; + return; + } + if ((p->t_arg != NULL) && (*(p->t_arg) != '\0')) + comret = command ("TYPE %s %s", p->t_mode, p->t_arg); + else + comret = command("TYPE %s", p->t_mode); + if (comret == COMPLETE) { + (void) strcpy(typename, p->t_name); + curtype = type = p->t_type; + } +} + +/* + * Internal form of settype; changes current type in use with server + * without changing our notion of the type for data transfers. + * Used to change to and from ascii for listings. + */ +void +changetype(newtype, show) + int newtype, show; +{ + struct types *p; + int comret, oldverbose = verbose; + + if (newtype == 0) + newtype = TYPE_I; + if (newtype == curtype) + return; + if (debug == 0 && show == 0) + verbose = 0; + for (p = types; p->t_name; p++) + if (newtype == p->t_type) + break; + if (p->t_name == 0) { + printf("ftp: internal error: unknown type %d\n", newtype); + return; + } + if (newtype == TYPE_L && bytename[0] != '\0') + comret = command("TYPE %s %s", p->t_mode, bytename); + else + comret = command("TYPE %s", p->t_mode); + if (comret == COMPLETE) + curtype = newtype; + verbose = oldverbose; +} + +char *stype[] = { + "type", + "", + 0 +}; + +/* + * Set binary transfer type. + */ +/*VARARGS*/ +void +setbinary(argc, argv) + int argc; + char **argv; +{ + + stype[1] = "binary"; + settype(2, stype); +} + +/* + * Set ascii transfer type. + */ +/*VARARGS*/ +void +setascii(argc, argv) + int argc; + char *argv[]; +{ + + stype[1] = "ascii"; + settype(2, stype); +} + +/* + * Set tenex transfer type. + */ +/*VARARGS*/ +void +settenex(argc, argv) + int argc; + char *argv[]; +{ + + stype[1] = "tenex"; + settype(2, stype); +} + +/* + * Set file transfer mode. + */ +/*ARGSUSED*/ +void +setftmode(argc, argv) + int argc; + char *argv[]; +{ + + printf("We only support %s mode, sorry.\n", modename); + code = -1; +} + +/* + * Set file transfer format. + */ +/*ARGSUSED*/ +void +setform(argc, argv) + int argc; + char *argv[]; +{ + + printf("We only support %s format, sorry.\n", formname); + code = -1; +} + +/* + * Set file transfer structure. + */ +/*ARGSUSED*/ +void +setstruct(argc, argv) + int argc; + char *argv[]; +{ + + printf("We only support %s structure, sorry.\n", structname); + code = -1; +} + +/* + * Send a single file. + */ +void +put(argc, argv) + int argc; + char *argv[]; +{ + char *cmd; + int loc = 0; + char *oldargv1, *oldargv2; + + if (argc == 2) { + argc++; + argv[2] = argv[1]; + loc++; + } + if (argc < 2 && !another(&argc, &argv, "local-file")) + goto usage; + if (argc < 3 && !another(&argc, &argv, "remote-file")) { +usage: + printf("usage: %s local-file remote-file\n", argv[0]); + code = -1; + return; + } + oldargv1 = argv[1]; + oldargv2 = argv[2]; + if (!globulize(&argv[1])) { + code = -1; + return; + } + /* + * If "globulize" modifies argv[1], and argv[2] is a copy of + * the old argv[1], make it a copy of the new argv[1]. + */ + if (argv[1] != oldargv1 && argv[2] == oldargv1) { + argv[2] = argv[1]; + } + cmd = (argv[0][0] == 'a') ? "APPE" : ((sunique) ? "STOU" : "STOR"); + if (loc && ntflag) { + argv[2] = dotrans(argv[2]); + } + if (loc && mapflag) { + argv[2] = domap(argv[2]); + } + sendrequest(cmd, argv[1], argv[2], + argv[1] != oldargv1 || argv[2] != oldargv2); +} + +/* + * Send multiple files. + */ +void +mput(argc, argv) + int argc; + char **argv; +{ + int i; + sig_t oldintr; + int ointer; + char *tp; + + if (argc < 2 && !another(&argc, &argv, "local-files")) { + printf("usage: %s local-files\n", argv[0]); + code = -1; + return; + } + mname = argv[0]; + mflag = 1; + oldintr = signal(SIGINT, mabort); + (void) setjmp(jabort); + if (proxy) { + char *cp, *tp2, tmpbuf[MAXPATHLEN]; + + while ((cp = remglob(argv,0)) != NULL) { + if (*cp == 0) { + mflag = 0; + continue; + } + if (mflag && confirm(argv[0], cp)) { + tp = cp; + if (mcase) { + while (*tp && !islower(*tp)) { + tp++; + } + if (!*tp) { + tp = cp; + tp2 = tmpbuf; + while ((*tp2 = *tp) != NULL) { + if (isupper(*tp2)) { + *tp2 = 'a' + *tp2 - 'A'; + } + tp++; + tp2++; + } + } + tp = tmpbuf; + } + if (ntflag) { + tp = dotrans(tp); + } + if (mapflag) { + tp = domap(tp); + } + sendrequest((sunique) ? "STOU" : "STOR", + cp, tp, cp != tp || !interactive); + if (!mflag && fromatty) { + ointer = interactive; + interactive = 1; + if (confirm("Continue with","mput")) { + mflag++; + } + interactive = ointer; + } + } + } + (void) signal(SIGINT, oldintr); + mflag = 0; + return; + } + for (i = 1; i < argc; i++) { + char **cpp, **gargs; + glob_t gl; + int flags; + + if (!doglob) { + if (mflag && confirm(argv[0], argv[i])) { + tp = (ntflag) ? dotrans(argv[i]) : argv[i]; + tp = (mapflag) ? domap(tp) : tp; + sendrequest((sunique) ? "STOU" : "STOR", + argv[i], tp, tp != argv[i] || !interactive); + if (!mflag && fromatty) { + ointer = interactive; + interactive = 1; + if (confirm("Continue with","mput")) { + mflag++; + } + interactive = ointer; + } + } + continue; + } + + memset(&gl, 0, sizeof(gl)); + flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; + if (glob(argv[i], flags, NULL, &gl) || gl.gl_pathc == 0) { + warnx("%s: not found", argv[i]); + globfree(&gl); + continue; + } + for (cpp = gl.gl_pathv; cpp && *cpp != NULL; cpp++) { + if (mflag && confirm(argv[0], *cpp)) { + tp = (ntflag) ? dotrans(*cpp) : *cpp; + tp = (mapflag) ? domap(tp) : tp; + sendrequest((sunique) ? "STOU" : "STOR", + *cpp, tp, *cpp != tp || !interactive); + if (!mflag && fromatty) { + ointer = interactive; + interactive = 1; + if (confirm("Continue with","mput")) { + mflag++; + } + interactive = ointer; + } + } + } + globfree(&gl); + } + (void) signal(SIGINT, oldintr); + mflag = 0; +} + +void +reget(argc, argv) + int argc; + char *argv[]; +{ + + (void) getit(argc, argv, 1, "r+w"); +} + +void +get(argc, argv) + int argc; + char *argv[]; +{ + + (void) getit(argc, argv, 0, restart_point ? "r+w" : "w" ); +} + +/* + * Receive one file. + */ +int +getit(argc, argv, restartit, mode) + int argc; + char *argv[]; + char *mode; + int restartit; +{ + int loc = 0; + char *oldargv1, *oldargv2; + + if (argc == 2) { + argc++; + argv[2] = argv[1]; + loc++; + } + if (argc < 2 && !another(&argc, &argv, "remote-file")) + goto usage; + if (argc < 3 && !another(&argc, &argv, "local-file")) { +usage: + printf("usage: %s remote-file [ local-file ]\n", argv[0]); + code = -1; + return (0); + } + oldargv1 = argv[1]; + oldargv2 = argv[2]; + if (!globulize(&argv[2])) { + code = -1; + return (0); + } + if (loc && mcase) { + char *tp = argv[1], *tp2, tmpbuf[MAXPATHLEN]; + + while (*tp && !islower(*tp)) { + tp++; + } + if (!*tp) { + tp = argv[2]; + tp2 = tmpbuf; + while ((*tp2 = *tp) != NULL) { + if (isupper(*tp2)) { + *tp2 = 'a' + *tp2 - 'A'; + } + tp++; + tp2++; + } + argv[2] = tmpbuf; + } + } + if (loc && ntflag) + argv[2] = dotrans(argv[2]); + if (loc && mapflag) + argv[2] = domap(argv[2]); + if (restartit) { + struct stat stbuf; + int ret; + + ret = stat(argv[2], &stbuf); + if (restartit == 1) { + if (ret < 0) { + warn("local: %s", argv[2]); + return (0); + } + restart_point = stbuf.st_size; + } else { + if (ret == 0) { + int overbose; + + overbose = verbose; + if (debug == 0) + verbose = -1; + if (command("MDTM %s", argv[1]) == COMPLETE) { + int yy, mo, day, hour, min, sec; + struct tm *tm; + verbose = overbose; + sscanf(reply_string, + "%*s %04d%02d%02d%02d%02d%02d", + &yy, &mo, &day, &hour, &min, &sec); + tm = gmtime(&stbuf.st_mtime); + tm->tm_mon++; + if (tm->tm_year > yy%100) + return (1); + if ((tm->tm_year == yy%100 && + tm->tm_mon > mo) || + (tm->tm_mon == mo && + tm->tm_mday > day) || + (tm->tm_mday == day && + tm->tm_hour > hour) || + (tm->tm_hour == hour && + tm->tm_min > min) || + (tm->tm_min == min && + tm->tm_sec > sec)) + return (1); + } else { + printf("%s\n", reply_string); + verbose = overbose; + return (0); + } + } + } + } + + recvrequest("RETR", argv[2], argv[1], mode, + argv[1] != oldargv1 || argv[2] != oldargv2); + restart_point = 0; + return (0); +} + +/* ARGSUSED */ +void +mabort(signo) + int signo; +{ + int ointer; + + printf("\n"); + (void) fflush(stdout); + if (mflag && fromatty) { + ointer = interactive; + interactive = 1; + if (confirm("Continue with", mname)) { + interactive = ointer; + longjmp(jabort,0); + } + interactive = ointer; + } + mflag = 0; + longjmp(jabort,0); +} + +/* + * Get multiple files. + */ +void +mget(argc, argv) + int argc; + char **argv; +{ + sig_t oldintr; + int ch, ointer; + char *cp, *tp, *tp2, tmpbuf[MAXPATHLEN]; + + if (argc < 2 && !another(&argc, &argv, "remote-files")) { + printf("usage: %s remote-files\n", argv[0]); + code = -1; + return; + } + mname = argv[0]; + mflag = 1; + oldintr = signal(SIGINT, mabort); + (void) setjmp(jabort); + while ((cp = remglob(argv,proxy)) != NULL) { + if (*cp == '\0') { + mflag = 0; + continue; + } + if (mflag && confirm(argv[0], cp)) { + tp = cp; + if (mcase) { + for (tp2 = tmpbuf; ch = *tp++;) + *tp2++ = isupper(ch) ? tolower(ch) : ch; + tp = tmpbuf; + } + if (ntflag) { + tp = dotrans(tp); + } + if (mapflag) { + tp = domap(tp); + } + recvrequest("RETR", tp, cp, "w", + tp != cp || !interactive); + if (!mflag && fromatty) { + ointer = interactive; + interactive = 1; + if (confirm("Continue with","mget")) { + mflag++; + } + interactive = ointer; + } + } + } + (void) signal(SIGINT,oldintr); + mflag = 0; +} + +char * +remglob(argv,doswitch) + char *argv[]; + int doswitch; +{ + char temp[16]; + static char buf[MAXPATHLEN]; + static FILE *ftemp = NULL; + static char **args; + int oldverbose, oldhash; + char *cp, *mode; + + if (!mflag) { + if (!doglob) { + args = NULL; + } + else { + if (ftemp) { + (void) fclose(ftemp); + ftemp = NULL; + } + } + return (NULL); + } + if (!doglob) { + if (args == NULL) + args = argv; + if ((cp = *++args) == NULL) + args = NULL; + return (cp); + } + if (ftemp == NULL) { + (void) strcpy(temp, _PATH_TMP); + (void) mktemp(temp); + oldverbose = verbose, verbose = 0; + oldhash = hash, hash = 0; + if (doswitch) { + pswitch(!proxy); + } + for (mode = "w"; *++argv != NULL; mode = "a") + recvrequest ("NLST", temp, *argv, mode, 0); + if (doswitch) { + pswitch(!proxy); + } + verbose = oldverbose; hash = oldhash; + ftemp = fopen(temp, "r"); + (void) unlink(temp); + if (ftemp == NULL) { + printf("can't find list of remote files, oops\n"); + return (NULL); + } + } + if (fgets(buf, sizeof (buf), ftemp) == NULL) { + (void) fclose(ftemp), ftemp = NULL; + return (NULL); + } + if ((cp = strchr(buf, '\n')) != NULL) + *cp = '\0'; + return (buf); +} + +char * +onoff(bool) + int bool; +{ + + return (bool ? "on" : "off"); +} + +/* + * Show status. + */ +/*ARGSUSED*/ +void +status(argc, argv) + int argc; + char *argv[]; +{ + int i; + + if (connected) + printf("Connected to %s.\n", hostname); + else + printf("Not connected.\n"); + if (!proxy) { + pswitch(1); + if (connected) { + printf("Connected for proxy commands to %s.\n", hostname); + } + else { + printf("No proxy connection.\n"); + } + pswitch(0); + } + printf("Mode: %s; Type: %s; Form: %s; Structure: %s\n", + modename, typename, formname, structname); + printf("Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s\n", + onoff(verbose), onoff(bell), onoff(interactive), + onoff(doglob)); + printf("Store unique: %s; Receive unique: %s\n", onoff(sunique), + onoff(runique)); + printf("Case: %s; CR stripping: %s\n",onoff(mcase),onoff(crflag)); + if (ntflag) { + printf("Ntrans: (in) %s (out) %s\n", ntin,ntout); + } + else { + printf("Ntrans: off\n"); + } + if (mapflag) { + printf("Nmap: (in) %s (out) %s\n", mapin, mapout); + } + else { + printf("Nmap: off\n"); + } + printf("Hash mark printing: %s; Use of PORT cmds: %s\n", + onoff(hash), onoff(sendport)); + if (macnum > 0) { + printf("Macros:\n"); + for (i=0; i 1) { + val = atoi(argv[1]); + if (val < 0) { + printf("%s: bad debugging value.\n", argv[1]); + code = -1; + return; + } + } else + val = !debug; + debug = val; + if (debug) + options |= SO_DEBUG; + else + options &= ~SO_DEBUG; + printf("Debugging %s (debug=%d).\n", onoff(debug), debug); + code = debug > 0; +} + +/* + * Set current working directory + * on remote machine. + */ +void +cd(argc, argv) + int argc; + char *argv[]; +{ + + if (argc < 2 && !another(&argc, &argv, "remote-directory")) { + printf("usage: %s remote-directory\n", argv[0]); + code = -1; + return; + } + if (command("CWD %s", argv[1]) == ERROR && code == 500) { + if (verbose) + printf("CWD command not recognized, trying XCWD\n"); + (void) command("XCWD %s", argv[1]); + } +} + +/* + * Set current working directory + * on local machine. + */ +void +lcd(argc, argv) + int argc; + char *argv[]; +{ + char buf[MAXPATHLEN]; + + if (argc < 2) + argc++, argv[1] = home; + if (argc != 2) { + printf("usage: %s local-directory\n", argv[0]); + code = -1; + return; + } + if (!globulize(&argv[1])) { + code = -1; + return; + } + if (chdir(argv[1]) < 0) { + warn("local: %s", argv[1]); + code = -1; + return; + } + if (getwd(buf) != NULL) + printf("Local directory now %s\n", buf); + else + warnx("getwd: %s", buf); + code = 0; +} + +/* + * Delete a single file. + */ +void +delete(argc, argv) + int argc; + char *argv[]; +{ + + if (argc < 2 && !another(&argc, &argv, "remote-file")) { + printf("usage: %s remote-file\n", argv[0]); + code = -1; + return; + } + (void) command("DELE %s", argv[1]); +} + +/* + * Delete multiple files. + */ +void +mdelete(argc, argv) + int argc; + char **argv; +{ + sig_t oldintr; + int ointer; + char *cp; + + if (argc < 2 && !another(&argc, &argv, "remote-files")) { + printf("usage: %s remote-files\n", argv[0]); + code = -1; + return; + } + mname = argv[0]; + mflag = 1; + oldintr = signal(SIGINT, mabort); + (void) setjmp(jabort); + while ((cp = remglob(argv,0)) != NULL) { + if (*cp == '\0') { + mflag = 0; + continue; + } + if (mflag && confirm(argv[0], cp)) { + (void) command("DELE %s", cp); + if (!mflag && fromatty) { + ointer = interactive; + interactive = 1; + if (confirm("Continue with", "mdelete")) { + mflag++; + } + interactive = ointer; + } + } + } + (void) signal(SIGINT, oldintr); + mflag = 0; +} + +/* + * Rename a remote file. + */ +void +renamefile(argc, argv) + int argc; + char *argv[]; +{ + + if (argc < 2 && !another(&argc, &argv, "from-name")) + goto usage; + if (argc < 3 && !another(&argc, &argv, "to-name")) { +usage: + printf("%s from-name to-name\n", argv[0]); + code = -1; + return; + } + if (command("RNFR %s", argv[1]) == CONTINUE) + (void) command("RNTO %s", argv[2]); +} + +/* + * Get a directory listing + * of remote files. + */ +void +ls(argc, argv) + int argc; + char *argv[]; +{ + char *cmd; + + if (argc < 2) + argc++, argv[1] = NULL; + if (argc < 3) + argc++, argv[2] = "-"; + if (argc > 3) { + printf("usage: %s remote-directory local-file\n", argv[0]); + code = -1; + return; + } + cmd = argv[0][0] == 'n' ? "NLST" : "LIST"; + if (strcmp(argv[2], "-") && !globulize(&argv[2])) { + code = -1; + return; + } + if (strcmp(argv[2], "-") && *argv[2] != '|') + if (!globulize(&argv[2]) || !confirm("output to local-file:", argv[2])) { + code = -1; + return; + } + recvrequest(cmd, argv[2], argv[1], "w", 0); +} + +/* + * Get a directory listing + * of multiple remote files. + */ +void +mls(argc, argv) + int argc; + char **argv; +{ + sig_t oldintr; + int ointer, i; + char *cmd, mode[1], *dest; + + if (argc < 2 && !another(&argc, &argv, "remote-files")) + goto usage; + if (argc < 3 && !another(&argc, &argv, "local-file")) { +usage: + printf("usage: %s remote-files local-file\n", argv[0]); + code = -1; + return; + } + dest = argv[argc - 1]; + argv[argc - 1] = NULL; + if (strcmp(dest, "-") && *dest != '|') + if (!globulize(&dest) || + !confirm("output to local-file:", dest)) { + code = -1; + return; + } + cmd = argv[0][1] == 'l' ? "NLST" : "LIST"; + mname = argv[0]; + mflag = 1; + oldintr = signal(SIGINT, mabort); + (void) setjmp(jabort); + for (i = 1; mflag && i < argc-1; ++i) { + *mode = (i == 1) ? 'w' : 'a'; + recvrequest(cmd, dest, argv[i], mode, 0); + if (!mflag && fromatty) { + ointer = interactive; + interactive = 1; + if (confirm("Continue with", argv[0])) { + mflag ++; + } + interactive = ointer; + } + } + (void) signal(SIGINT, oldintr); + mflag = 0; +} + +/* + * Do a shell escape + */ +/*ARGSUSED*/ +void +shell(argc, argv) + int argc; + char **argv; +{ + pid_t pid; + sig_t old1, old2; + char shellnam[40], *shell, *namep; + union wait status; + + old1 = signal (SIGINT, SIG_IGN); + old2 = signal (SIGQUIT, SIG_IGN); + if ((pid = fork()) == 0) { + for (pid = 3; pid < 20; pid++) + (void) close(pid); + (void) signal(SIGINT, SIG_DFL); + (void) signal(SIGQUIT, SIG_DFL); + shell = getenv("SHELL"); + if (shell == NULL) + shell = _PATH_BSHELL; + namep = strrchr(shell,'/'); + if (namep == NULL) + namep = shell; + (void) strcpy(shellnam,"-"); + (void) strcat(shellnam, ++namep); + if (strcmp(namep, "sh") != 0) + shellnam[0] = '+'; + if (debug) { + printf ("%s\n", shell); + (void) fflush (stdout); + } + if (argc > 1) { + execl(shell,shellnam,"-c",altarg,(char *)0); + } + else { + execl(shell,shellnam,(char *)0); + } + warn("%s", shell); + code = -1; + exit(1); + } + if (pid > 0) + while (wait((int *)&status) != pid) + ; + (void) signal(SIGINT, old1); + (void) signal(SIGQUIT, old2); + if (pid == -1) { + warn("%s", "Try again later"); + code = -1; + } + else { + code = 0; + } +} + +/* + * Send new user information (re-login) + */ +void +user(argc, argv) + int argc; + char **argv; +{ + char acct[80]; + int n, aflag = 0; + + if (argc < 2) + (void) another(&argc, &argv, "username"); + if (argc < 2 || argc > 4) { + printf("usage: %s username [password] [account]\n", argv[0]); + code = -1; + return; + } + n = command("USER %s", argv[1]); + if (n == CONTINUE) { + if (argc < 3 ) + argv[2] = getpass("Password: "), argc++; + n = command("PASS %s", argv[2]); + } + if (n == CONTINUE) { + if (argc < 4) { + printf("Account: "); (void) fflush(stdout); + (void) fgets(acct, sizeof(acct) - 1, stdin); + acct[strlen(acct) - 1] = '\0'; + argv[3] = acct; argc++; + } + n = command("ACCT %s", argv[3]); + aflag++; + } + if (n != COMPLETE) { + fprintf(stdout, "Login failed.\n"); + return; + } + if (!aflag && argc == 4) { + (void) command("ACCT %s", argv[3]); + } +} + +/* + * Print working directory. + */ +/*VARARGS*/ +void +pwd(argc, argv) + int argc; + char *argv[]; +{ + int oldverbose = verbose; + + /* + * If we aren't verbose, this doesn't do anything! + */ + verbose = 1; + if (command("PWD") == ERROR && code == 500) { + printf("PWD command not recognized, trying XPWD\n"); + (void) command("XPWD"); + } + verbose = oldverbose; +} + +/* + * Make a directory. + */ +void +makedir(argc, argv) + int argc; + char *argv[]; +{ + + if (argc < 2 && !another(&argc, &argv, "directory-name")) { + printf("usage: %s directory-name\n", argv[0]); + code = -1; + return; + } + if (command("MKD %s", argv[1]) == ERROR && code == 500) { + if (verbose) + printf("MKD command not recognized, trying XMKD\n"); + (void) command("XMKD %s", argv[1]); + } +} + +/* + * Remove a directory. + */ +void +removedir(argc, argv) + int argc; + char *argv[]; +{ + + if (argc < 2 && !another(&argc, &argv, "directory-name")) { + printf("usage: %s directory-name\n", argv[0]); + code = -1; + return; + } + if (command("RMD %s", argv[1]) == ERROR && code == 500) { + if (verbose) + printf("RMD command not recognized, trying XRMD\n"); + (void) command("XRMD %s", argv[1]); + } +} + +/* + * Send a line, verbatim, to the remote machine. + */ +void +quote(argc, argv) + int argc; + char *argv[]; +{ + + if (argc < 2 && !another(&argc, &argv, "command line to send")) { + printf("usage: %s line-to-send\n", argv[0]); + code = -1; + return; + } + quote1("", argc, argv); +} + +/* + * Send a SITE command to the remote machine. The line + * is sent verbatim to the remote machine, except that the + * word "SITE" is added at the front. + */ +void +site(argc, argv) + int argc; + char *argv[]; +{ + + if (argc < 2 && !another(&argc, &argv, "arguments to SITE command")) { + printf("usage: %s line-to-send\n", argv[0]); + code = -1; + return; + } + quote1("SITE ", argc, argv); +} + +/* + * Turn argv[1..argc) into a space-separated string, then prepend initial text. + * Send the result as a one-line command and get response. + */ +void +quote1(initial, argc, argv) + char *initial; + int argc; + char **argv; +{ + int i, len; + char buf[BUFSIZ]; /* must be >= sizeof(line) */ + + (void) strcpy(buf, initial); + if (argc > 1) { + len = strlen(buf); + len += strlen(strcpy(&buf[len], argv[1])); + for (i = 2; i < argc; i++) { + buf[len++] = ' '; + len += strlen(strcpy(&buf[len], argv[i])); + } + } + if (command(buf) == PRELIM) { + while (getreply(0) == PRELIM) + continue; + } +} + +void +do_chmod(argc, argv) + int argc; + char *argv[]; +{ + + if (argc < 2 && !another(&argc, &argv, "mode")) + goto usage; + if (argc < 3 && !another(&argc, &argv, "file-name")) { +usage: + printf("usage: %s mode file-name\n", argv[0]); + code = -1; + return; + } + (void) command("SITE CHMOD %s %s", argv[1], argv[2]); +} + +void +do_umask(argc, argv) + int argc; + char *argv[]; +{ + int oldverbose = verbose; + + verbose = 1; + (void) command(argc == 1 ? "SITE UMASK" : "SITE UMASK %s", argv[1]); + verbose = oldverbose; +} + +void +idle(argc, argv) + int argc; + char *argv[]; +{ + int oldverbose = verbose; + + verbose = 1; + (void) command(argc == 1 ? "SITE IDLE" : "SITE IDLE %s", argv[1]); + verbose = oldverbose; +} + +/* + * Ask the other side for help. + */ +void +rmthelp(argc, argv) + int argc; + char *argv[]; +{ + int oldverbose = verbose; + + verbose = 1; + (void) command(argc == 1 ? "HELP" : "HELP %s", argv[1]); + verbose = oldverbose; +} + +/* + * Terminate session and exit. + */ +/*VARARGS*/ +void +quit(argc, argv) + int argc; + char *argv[]; +{ + + if (connected) + disconnect(0, 0); + pswitch(1); + if (connected) { + disconnect(0, 0); + } + exit(0); +} + +/* + * Terminate session, but don't exit. + */ +void +disconnect(argc, argv) + int argc; + char *argv[]; +{ + + if (!connected) + return; + (void) command("QUIT"); + if (cout) { + (void) fclose(cout); + } + cout = NULL; + connected = 0; + data = -1; + if (!proxy) { + macnum = 0; + } +} + +int +confirm(cmd, file) + char *cmd, *file; +{ + char line[BUFSIZ]; + + if (!interactive) + return (1); + printf("%s %s? ", cmd, file); + (void) fflush(stdout); + if (fgets(line, sizeof line, stdin) == NULL) + return (0); + return (*line != 'n' && *line != 'N'); +} + +void +fatal(msg) + char *msg; +{ + + errx(1, "%s", msg); +} + +/* + * Glob a local file name specification with + * the expectation of a single return value. + * Can't control multiple values being expanded + * from the expression, we return only the first. + */ +int +globulize(cpp) + char **cpp; +{ + glob_t gl; + int flags; + + if (!doglob) + return (1); + + flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; + memset(&gl, 0, sizeof(gl)); + if (glob(*cpp, flags, NULL, &gl) || + gl.gl_pathc == 0) { + warnx("%s: not found", *cpp); + globfree(&gl); + return (0); + } + *cpp = strdup(gl.gl_pathv[0]); /* XXX - wasted memory */ + globfree(&gl); + return (1); +} + +void +account(argc,argv) + int argc; + char **argv; +{ + char acct[50], *ap; + + if (argc > 1) { + ++argv; + --argc; + (void) strncpy(acct,*argv,49); + acct[49] = '\0'; + while (argc > 1) { + --argc; + ++argv; + (void) strncat(acct,*argv, 49-strlen(acct)); + } + ap = acct; + } + else { + ap = getpass("Account:"); + } + (void) command("ACCT %s", ap); +} + +jmp_buf abortprox; + +void +proxabort() +{ + + if (!proxy) { + pswitch(1); + } + if (connected) { + proxflag = 1; + } + else { + proxflag = 0; + } + pswitch(0); + longjmp(abortprox,1); +} + +void +doproxy(argc, argv) + int argc; + char *argv[]; +{ + struct cmd *c; + sig_t oldintr; + + if (argc < 2 && !another(&argc, &argv, "command")) { + printf("usage: %s command\n", argv[0]); + code = -1; + return; + } + c = getcmd(argv[1]); + if (c == (struct cmd *) -1) { + printf("?Ambiguous command\n"); + (void) fflush(stdout); + code = -1; + return; + } + if (c == 0) { + printf("?Invalid command\n"); + (void) fflush(stdout); + code = -1; + return; + } + if (!c->c_proxy) { + printf("?Invalid proxy command\n"); + (void) fflush(stdout); + code = -1; + return; + } + if (setjmp(abortprox)) { + code = -1; + return; + } + oldintr = signal(SIGINT, proxabort); + pswitch(1); + if (c->c_conn && !connected) { + printf("Not connected\n"); + (void) fflush(stdout); + pswitch(0); + (void) signal(SIGINT, oldintr); + code = -1; + return; + } + (*c->c_handler)(argc-1, argv+1); + if (connected) { + proxflag = 1; + } + else { + proxflag = 0; + } + pswitch(0); + (void) signal(SIGINT, oldintr); +} + +void +setcase(argc, argv) + int argc; + char *argv[]; +{ + + mcase = !mcase; + printf("Case mapping %s.\n", onoff(mcase)); + code = mcase; +} + +void +setcr(argc, argv) + int argc; + char *argv[]; +{ + + crflag = !crflag; + printf("Carriage Return stripping %s.\n", onoff(crflag)); + code = crflag; +} + +void +setntrans(argc,argv) + int argc; + char *argv[]; +{ + if (argc == 1) { + ntflag = 0; + printf("Ntrans off.\n"); + code = ntflag; + return; + } + ntflag++; + code = ntflag; + (void) strncpy(ntin, argv[1], 16); + ntin[16] = '\0'; + if (argc == 2) { + ntout[0] = '\0'; + return; + } + (void) strncpy(ntout, argv[2], 16); + ntout[16] = '\0'; +} + +char * +dotrans(name) + char *name; +{ + static char new[MAXPATHLEN]; + char *cp1, *cp2 = new; + int i, ostop, found; + + for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++) + continue; + for (cp1 = name; *cp1; cp1++) { + found = 0; + for (i = 0; *(ntin + i) && i < 16; i++) { + if (*cp1 == *(ntin + i)) { + found++; + if (i < ostop) { + *cp2++ = *(ntout + i); + } + break; + } + } + if (!found) { + *cp2++ = *cp1; + } + } + *cp2 = '\0'; + return (new); +} + +void +setnmap(argc, argv) + int argc; + char *argv[]; +{ + char *cp; + + if (argc == 1) { + mapflag = 0; + printf("Nmap off.\n"); + code = mapflag; + return; + } + if (argc < 3 && !another(&argc, &argv, "mapout")) { + printf("Usage: %s [mapin mapout]\n",argv[0]); + code = -1; + return; + } + mapflag = 1; + code = 1; + cp = strchr(altarg, ' '); + if (proxy) { + while(*++cp == ' ') + continue; + altarg = cp; + cp = strchr(altarg, ' '); + } + *cp = '\0'; + (void) strncpy(mapin, altarg, MAXPATHLEN - 1); + while (*++cp == ' ') + continue; + (void) strncpy(mapout, cp, MAXPATHLEN - 1); +} + +char * +domap(name) + char *name; +{ + static char new[MAXPATHLEN]; + char *cp1 = name, *cp2 = mapin; + char *tp[9], *te[9]; + int i, toks[9], toknum = 0, match = 1; + + for (i=0; i < 9; ++i) { + toks[i] = 0; + } + while (match && *cp1 && *cp2) { + switch (*cp2) { + case '\\': + if (*++cp2 != *cp1) { + match = 0; + } + break; + case '$': + if (*(cp2+1) >= '1' && (*cp2+1) <= '9') { + if (*cp1 != *(++cp2+1)) { + toks[toknum = *cp2 - '1']++; + tp[toknum] = cp1; + while (*++cp1 && *(cp2+1) + != *cp1); + te[toknum] = cp1; + } + cp2++; + break; + } + /* FALLTHROUGH */ + default: + if (*cp2 != *cp1) { + match = 0; + } + break; + } + if (match && *cp1) { + cp1++; + } + if (match && *cp2) { + cp2++; + } + } + if (!match && *cp1) /* last token mismatch */ + { + toks[toknum] = 0; + } + cp1 = new; + *cp1 = '\0'; + cp2 = mapout; + while (*cp2) { + match = 0; + switch (*cp2) { + case '\\': + if (*(cp2 + 1)) { + *cp1++ = *++cp2; + } + break; + case '[': +LOOP: + if (*++cp2 == '$' && isdigit(*(cp2+1))) { + if (*++cp2 == '0') { + char *cp3 = name; + + while (*cp3) { + *cp1++ = *cp3++; + } + match = 1; + } + else if (toks[toknum = *cp2 - '1']) { + char *cp3 = tp[toknum]; + + while (cp3 != te[toknum]) { + *cp1++ = *cp3++; + } + match = 1; + } + } + else { + while (*cp2 && *cp2 != ',' && + *cp2 != ']') { + if (*cp2 == '\\') { + cp2++; + } + else if (*cp2 == '$' && + isdigit(*(cp2+1))) { + if (*++cp2 == '0') { + char *cp3 = name; + + while (*cp3) { + *cp1++ = *cp3++; + } + } + else if (toks[toknum = + *cp2 - '1']) { + char *cp3=tp[toknum]; + + while (cp3 != + te[toknum]) { + *cp1++ = *cp3++; + } + } + } + else if (*cp2) { + *cp1++ = *cp2++; + } + } + if (!*cp2) { + printf("nmap: unbalanced brackets\n"); + return (name); + } + match = 1; + cp2--; + } + if (match) { + while (*++cp2 && *cp2 != ']') { + if (*cp2 == '\\' && *(cp2 + 1)) { + cp2++; + } + } + if (!*cp2) { + printf("nmap: unbalanced brackets\n"); + return (name); + } + break; + } + switch (*++cp2) { + case ',': + goto LOOP; + case ']': + break; + default: + cp2--; + goto LOOP; + } + break; + case '$': + if (isdigit(*(cp2 + 1))) { + if (*++cp2 == '0') { + char *cp3 = name; + + while (*cp3) { + *cp1++ = *cp3++; + } + } + else if (toks[toknum = *cp2 - '1']) { + char *cp3 = tp[toknum]; + + while (cp3 != te[toknum]) { + *cp1++ = *cp3++; + } + } + break; + } + /* intentional drop through */ + default: + *cp1++ = *cp2; + break; + } + cp2++; + } + *cp1 = '\0'; + if (!*new) { + return (name); + } + return (new); +} + +void +setpassive(argc, argv) + int argc; + char *argv[]; +{ + + passivemode = !passivemode; + printf("Passive mode %s.\n", onoff(passivemode)); + code = passivemode; +} + +void +setsunique(argc, argv) + int argc; + char *argv[]; +{ + + sunique = !sunique; + printf("Store unique %s.\n", onoff(sunique)); + code = sunique; +} + +void +setrunique(argc, argv) + int argc; + char *argv[]; +{ + + runique = !runique; + printf("Receive unique %s.\n", onoff(runique)); + code = runique; +} + +/* change directory to perent directory */ +void +cdup(argc, argv) + int argc; + char *argv[]; +{ + + if (command("CDUP") == ERROR && code == 500) { + if (verbose) + printf("CDUP command not recognized, trying XCUP\n"); + (void) command("XCUP"); + } +} + +/* restart transfer at specific point */ +void +restart(argc, argv) + int argc; + char *argv[]; +{ + + if (argc != 2) + printf("restart: offset not specified\n"); + else { + restart_point = atol(argv[1]); + printf("restarting at %qd. %s\n", restart_point, + "execute get, put or append to initiate transfer"); + } +} + +/* show remote system type */ +void +syst(argc, argv) + int argc; + char *argv[]; +{ + + (void) command("SYST"); +} + +void +macdef(argc, argv) + int argc; + char *argv[]; +{ + char *tmp; + int c; + + if (macnum == 16) { + printf("Limit of 16 macros have already been defined\n"); + code = -1; + return; + } + if (argc < 2 && !another(&argc, &argv, "macro name")) { + printf("Usage: %s macro_name\n",argv[0]); + code = -1; + return; + } + if (interactive) { + printf("Enter macro line by line, terminating it with a null line\n"); + } + (void) strncpy(macros[macnum].mac_name, argv[1], 8); + if (macnum == 0) { + macros[macnum].mac_start = macbuf; + } + else { + macros[macnum].mac_start = macros[macnum - 1].mac_end + 1; + } + tmp = macros[macnum].mac_start; + while (tmp != macbuf+4096) { + if ((c = getchar()) == EOF) { + printf("macdef:end of file encountered\n"); + code = -1; + return; + } + if ((*tmp = c) == '\n') { + if (tmp == macros[macnum].mac_start) { + macros[macnum++].mac_end = tmp; + code = 0; + return; + } + if (*(tmp-1) == '\0') { + macros[macnum++].mac_end = tmp - 1; + code = 0; + return; + } + *tmp = '\0'; + } + tmp++; + } + while (1) { + while ((c = getchar()) != '\n' && c != EOF) + /* LOOP */; + if (c == EOF || getchar() == '\n') { + printf("Macro not defined - 4k buffer exceeded\n"); + code = -1; + return; + } + } +} + +/* + * get size of file on remote machine + */ +void +sizecmd(argc, argv) + int argc; + char *argv[]; +{ + + if (argc < 2 && !another(&argc, &argv, "filename")) { + printf("usage: %s filename\n", argv[0]); + code = -1; + return; + } + (void) command("SIZE %s", argv[1]); +} + +/* + * get last modification time of file on remote machine + */ +void +modtime(argc, argv) + int argc; + char *argv[]; +{ + int overbose; + + if (argc < 2 && !another(&argc, &argv, "filename")) { + printf("usage: %s filename\n", argv[0]); + code = -1; + return; + } + overbose = verbose; + if (debug == 0) + verbose = -1; + if (command("MDTM %s", argv[1]) == COMPLETE) { + int yy, mo, day, hour, min, sec; + sscanf(reply_string, "%*s %04d%02d%02d%02d%02d%02d", &yy, &mo, + &day, &hour, &min, &sec); + /* might want to print this in local time */ + printf("%s\t%02d/%02d/%04d %02d:%02d:%02d GMT\n", argv[1], + mo, day, yy, hour, min, sec); + } else + printf("%s\n", reply_string); + verbose = overbose; +} + +/* + * show status on reomte machine + */ +void +rmtstatus(argc, argv) + int argc; + char *argv[]; +{ + + (void) command(argc > 1 ? "STAT %s" : "STAT" , argv[1]); +} + +/* + * get file if modtime is more recent than current file + */ +void +newer(argc, argv) + int argc; + char *argv[]; +{ + + if (getit(argc, argv, -1, "w")) + printf("Local file \"%s\" is newer than remote file \"%s\"\n", + argv[2], argv[1]); +} diff --git a/usr.bin/ftp/cmdtab.c b/usr.bin/ftp/cmdtab.c new file mode 100644 index 00000000000..1b6da8c172e --- /dev/null +++ b/usr.bin/ftp/cmdtab.c @@ -0,0 +1,188 @@ +/* + * Copyright (c) 1985, 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)cmdtab.c 8.4 (Berkeley) 10/9/94"; +#endif /* not lint */ + +#include +#include "ftp_var.h" + +/* + * User FTP -- Command Tables. + */ + +char accounthelp[] = "send account command to remote server"; +char appendhelp[] = "append to a file"; +char asciihelp[] = "set ascii transfer type"; +char beephelp[] = "beep when command completed"; +char binaryhelp[] = "set binary transfer type"; +char casehelp[] = "toggle mget upper/lower case id mapping"; +char cdhelp[] = "change remote working directory"; +char cduphelp[] = "change remote working directory to parent directory"; +char chmodhelp[] = "change file permissions of remote file"; +char connecthelp[] = "connect to remote tftp"; +char crhelp[] = "toggle carriage return stripping on ascii gets"; +char deletehelp[] = "delete remote file"; +char debughelp[] = "toggle/set debugging mode"; +char dirhelp[] = "list contents of remote directory"; +char disconhelp[] = "terminate ftp session"; +char domachelp[] = "execute macro"; +char formhelp[] = "set file transfer format"; +char globhelp[] = "toggle metacharacter expansion of local file names"; +char hashhelp[] = "toggle printing `#' for each buffer transferred"; +char helphelp[] = "print local help information"; +char idlehelp[] = "get (set) idle timer on remote side"; +char lcdhelp[] = "change local working directory"; +char lshelp[] = "list contents of remote directory"; +char macdefhelp[] = "define a macro"; +char mdeletehelp[] = "delete multiple files"; +char mdirhelp[] = "list contents of multiple remote directories"; +char mgethelp[] = "get multiple files"; +char mkdirhelp[] = "make directory on the remote machine"; +char mlshelp[] = "list contents of multiple remote directories"; +char modtimehelp[] = "show last modification time of remote file"; +char modehelp[] = "set file transfer mode"; +char mputhelp[] = "send multiple files"; +char newerhelp[] = "get file if remote file is newer than local file "; +char nlisthelp[] = "nlist contents of remote directory"; +char nmaphelp[] = "set templates for default file name mapping"; +char ntranshelp[] = "set translation table for default file name mapping"; +char porthelp[] = "toggle use of PORT cmd for each data connection"; +char prompthelp[] = "force interactive prompting on multiple commands"; +char proxyhelp[] = "issue command on alternate connection"; +char pwdhelp[] = "print working directory on remote machine"; +char quithelp[] = "terminate ftp session and exit"; +char quotehelp[] = "send arbitrary ftp command"; +char receivehelp[] = "receive file"; +char regethelp[] = "get file restarting at end of local file"; +char remotehelp[] = "get help from remote server"; +char renamehelp[] = "rename file"; +char restarthelp[]= "restart file transfer at bytecount"; +char rmdirhelp[] = "remove directory on the remote machine"; +char rmtstatushelp[]="show status of remote machine"; +char runiquehelp[] = "toggle store unique for local files"; +char resethelp[] = "clear queued command replies"; +char sendhelp[] = "send one file"; +char passivehelp[] = "enter passive transfer mode"; +char sitehelp[] = "send site specific command to remote server\n\t\tTry \"rhelp site\" or \"site help\" for more information"; +char shellhelp[] = "escape to the shell"; +char sizecmdhelp[] = "show size of remote file"; +char statushelp[] = "show current status"; +char structhelp[] = "set file transfer structure"; +char suniquehelp[] = "toggle store unique on remote machine"; +char systemhelp[] = "show remote system type"; +char tenexhelp[] = "set tenex file transfer type"; +char tracehelp[] = "toggle packet tracing"; +char typehelp[] = "set file transfer type"; +char umaskhelp[] = "get (set) umask on remote side"; +char userhelp[] = "send new user information"; +char verbosehelp[] = "toggle verbose mode"; + +struct cmd cmdtab[] = { + { "!", shellhelp, 0, 0, 0, shell }, + { "$", domachelp, 1, 0, 0, domacro }, + { "account", accounthelp, 0, 1, 1, account}, + { "append", appendhelp, 1, 1, 1, put }, + { "ascii", asciihelp, 0, 1, 1, setascii }, + { "bell", beephelp, 0, 0, 0, setbell }, + { "binary", binaryhelp, 0, 1, 1, setbinary }, + { "bye", quithelp, 0, 0, 0, quit }, + { "case", casehelp, 0, 0, 1, setcase }, + { "cd", cdhelp, 0, 1, 1, cd }, + { "cdup", cduphelp, 0, 1, 1, cdup }, + { "chmod", chmodhelp, 0, 1, 1, do_chmod }, + { "close", disconhelp, 0, 1, 1, disconnect }, + { "cr", crhelp, 0, 0, 0, setcr }, + { "delete", deletehelp, 0, 1, 1, delete }, + { "debug", debughelp, 0, 0, 0, setdebug }, + { "dir", dirhelp, 1, 1, 1, ls }, + { "disconnect", disconhelp, 0, 1, 1, disconnect }, + { "form", formhelp, 0, 1, 1, setform }, + { "get", receivehelp, 1, 1, 1, get }, + { "glob", globhelp, 0, 0, 0, setglob }, + { "hash", hashhelp, 0, 0, 0, sethash }, + { "help", helphelp, 0, 0, 1, help }, + { "idle", idlehelp, 0, 1, 1, idle }, + { "image", binaryhelp, 0, 1, 1, setbinary }, + { "lcd", lcdhelp, 0, 0, 0, lcd }, + { "ls", lshelp, 1, 1, 1, ls }, + { "macdef", macdefhelp, 0, 0, 0, macdef }, + { "mdelete", mdeletehelp, 1, 1, 1, mdelete }, + { "mdir", mdirhelp, 1, 1, 1, mls }, + { "mget", mgethelp, 1, 1, 1, mget }, + { "mkdir", mkdirhelp, 0, 1, 1, makedir }, + { "mls", mlshelp, 1, 1, 1, mls }, + { "mode", modehelp, 0, 1, 1, setftmode }, + { "modtime", modtimehelp, 0, 1, 1, modtime }, + { "mput", mputhelp, 1, 1, 1, mput }, + { "newer", newerhelp, 1, 1, 1, newer }, + { "nmap", nmaphelp, 0, 0, 1, setnmap }, + { "nlist", nlisthelp, 1, 1, 1, ls }, + { "ntrans", ntranshelp, 0, 0, 1, setntrans }, + { "open", connecthelp, 0, 0, 1, setpeer }, + { "passive", passivehelp, 0, 0, 0, setpassive }, + { "prompt", prompthelp, 0, 0, 0, setprompt }, + { "proxy", proxyhelp, 0, 0, 1, doproxy }, + { "sendport", porthelp, 0, 0, 0, setport }, + { "put", sendhelp, 1, 1, 1, put }, + { "pwd", pwdhelp, 0, 1, 1, pwd }, + { "quit", quithelp, 0, 0, 0, quit }, + { "quote", quotehelp, 1, 1, 1, quote }, + { "recv", receivehelp, 1, 1, 1, get }, + { "reget", regethelp, 1, 1, 1, reget }, + { "rstatus", rmtstatushelp, 0, 1, 1, rmtstatus }, + { "rhelp", remotehelp, 0, 1, 1, rmthelp }, + { "rename", renamehelp, 0, 1, 1, renamefile }, + { "reset", resethelp, 0, 1, 1, reset }, + { "restart", restarthelp, 1, 1, 1, restart }, + { "rmdir", rmdirhelp, 0, 1, 1, removedir }, + { "runique", runiquehelp, 0, 0, 1, setrunique }, + { "send", sendhelp, 1, 1, 1, put }, + { "site", sitehelp, 0, 1, 1, site }, + { "size", sizecmdhelp, 1, 1, 1, sizecmd }, + { "status", statushelp, 0, 0, 1, status }, + { "struct", structhelp, 0, 1, 1, setstruct }, + { "system", systemhelp, 0, 1, 1, syst }, + { "sunique", suniquehelp, 0, 0, 1, setsunique }, + { "tenex", tenexhelp, 0, 1, 1, settenex }, + { "trace", tracehelp, 0, 0, 0, settrace }, + { "type", typehelp, 0, 1, 1, settype }, + { "user", userhelp, 0, 1, 1, user }, + { "umask", umaskhelp, 0, 1, 1, do_umask }, + { "verbose", verbosehelp, 0, 0, 0, setverbose }, + { "?", helphelp, 0, 0, 1, help }, + { 0 }, +}; + +int NCMDS = (sizeof (cmdtab) / sizeof (cmdtab[0])) - 1; diff --git a/usr.bin/ftp/extern.h b/usr.bin/ftp/extern.h new file mode 100644 index 00000000000..2a8bb26731b --- /dev/null +++ b/usr.bin/ftp/extern.h @@ -0,0 +1,153 @@ +/*- + * Copyright (c) 1994 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.3 (Berkeley) 10/9/94 + */ + +struct timeval; +struct fd_set; + +void abort_remote __P((FILE *)); +void abortpt __P(()); +void abortrecv __P(()); +void abortsend __P(()); +void account __P((int, char **)); +int another __P((int *, char ***, char *)); +void blkfree __P((char **)); +void cd __P((int, char **)); +void cdup __P((int, char **)); +void changetype __P((int, int)); +void cmdabort __P(()); +void cmdscanner __P((int)); +int command __P(()); +int confirm __P((char *, char *)); +FILE *dataconn __P((char *)); +void delete __P((int, char **)); +void disconnect __P((int, char **)); +void do_chmod __P((int, char **)); +void do_umask __P((int, char **)); +void domacro __P((int, char **)); +char *domap __P((char *)); +void doproxy __P((int, char **)); +char *dotrans __P((char *)); +int empty __P((struct fd_set *, int)); +void fatal __P((char *)); +void get __P((int, char **)); +struct cmd *getcmd __P((char *)); +int getit __P((int, char **, int, char *)); +int getreply __P((int)); +int globulize __P((char **)); +char *gunique __P((char *)); +void help __P((int, char **)); +char *hookup __P((char *, int)); +void idle __P((int, char **)); +int initconn __P((void)); +void intr __P(()); +void lcd __P((int, char **)); +int login __P((char *)); +void lostpeer __P(()); +void ls __P((int, char **)); +void mabort __P((int)); +void macdef __P((int, char **)); +void makeargv __P((void)); +void makedir __P((int, char **)); +void mdelete __P((int, char **)); +void mget __P((int, char **)); +void mls __P((int, char **)); +void modtime __P((int, char **)); +void mput __P((int, char **)); +char *onoff __P((int)); +void newer __P((int, char **)); +void proxabort __P(()); +void proxtrans __P((char *, char *, char *)); +void psabort __P(()); +void pswitch __P((int)); +void ptransfer __P((char *, long, struct timeval *, struct timeval *)); +void put __P((int, char **)); +void pwd __P((int, char **)); +void quit __P((int, char **)); +void quote __P((int, char **)); +void quote1 __P((char *, int, char **)); +void recvrequest __P((char *, char *, char *, char *, int)); +void reget __P((int, char **)); +char *remglob __P((char **, int)); +void removedir __P((int, char **)); +void renamefile __P((int, char **)); +void reset __P((int, char **)); +void restart __P((int, char **)); +void rmthelp __P((int, char **)); +void rmtstatus __P((int, char **)); +int ruserpass __P((char *, char **, char **, char **)); +void sendrequest __P((char *, char *, char *, int)); +void setascii __P((int, char **)); +void setbell __P((int, char **)); +void setbinary __P((int, char **)); +void setcase __P((int, char **)); +void setcr __P((int, char **)); +void setdebug __P((int, char **)); +void setform __P((int, char **)); +void setftmode __P((int, char **)); +void setglob __P((int, char **)); +void sethash __P((int, char **)); +void setnmap __P((int, char **)); +void setntrans __P((int, char **)); +void setpassive __P((int, char **)); +void setpeer __P((int, char **)); +void setport __P((int, char **)); +void setprompt __P((int, char **)); +void setrunique __P((int, char **)); +void setstruct __P((int, char **)); +void setsunique __P((int, char **)); +void settenex __P((int, char **)); +void settrace __P((int, char **)); +void settype __P((int, char **)); +void setverbose __P((int, char **)); +void shell __P((int, char **)); +void site __P((int, char **)); +void sizecmd __P((int, char **)); +char *slurpstring __P((void)); +void status __P((int, char **)); +void syst __P((int, char **)); +void tvsub __P((struct timeval *, struct timeval *, struct timeval *)); +void user __P((int, char **)); + +extern jmp_buf abortprox; +extern int abrtflag; +extern struct cmd cmdtab[]; +extern FILE *cout; +extern int data; +extern char *home; +extern jmp_buf jabort; +extern int proxy; +extern char reply_string[]; +extern off_t restart_point; +extern int NCMDS; diff --git a/usr.bin/ftp/ftp.1 b/usr.bin/ftp/ftp.1 new file mode 100644 index 00000000000..79dcbae40d4 --- /dev/null +++ b/usr.bin/ftp/ftp.1 @@ -0,0 +1,1157 @@ +.\" Copyright (c) 1985, 1989, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)ftp.1 8.3 (Berkeley) 10/9/94 +.\" +.Dd October 9, 1994 +.Dt FTP 1 +.Os BSD 4.2 +.Sh NAME +.Nm ftp +.Nd +.Tn ARPANET +file transfer program +.Sh SYNOPSIS +.Nm ftp +.Op Fl v +.Op Fl d +.Op Fl i +.Op Fl n +.Op Fl g +.Op Ar host +.Sh DESCRIPTION +.Nm Ftp +is the user interface to the +.Tn ARPANET +standard File Transfer Protocol. +The program allows a user to transfer files to and from a +remote network site. +.Pp +Options may be specified at the command line, or to the +command interpreter. +.Bl -tag -width flag +.It Fl v +Verbose option forces +.Nm ftp +to show all responses from the remote server, as well +as report on data transfer statistics. +.It Fl n +Restrains +.Nm ftp +from attempting \*(Lqauto-login\*(Rq upon initial connection. +If auto-login is enabled, +.Nm ftp +will check the +.Pa .netrc +(see below) file in the user's home directory for an entry describing +an account on the remote machine. +If no entry exists, +.Nm ftp +will prompt for the remote machine login name (default is the user +identity on the local machine), and, if necessary, prompt for a password +and an account with which to login. +.It Fl i +Turns off interactive prompting during +multiple file transfers. +.It Fl d +Enables debugging. +.It Fl g +Disables file name globbing. +.El +.Pp +The client host with which +.Nm ftp +is to communicate may be specified on the command line. +If this is done, +.Nm ftp +will immediately attempt to establish a connection to an +.Tn FTP +server on that host; otherwise, +.Nm ftp +will enter its command interpreter and await instructions +from the user. +When +.Nm ftp +is awaiting commands from the user the prompt +.Ql ftp> +is provided to the user. +The following commands are recognized +by +.Nm ftp : +.Bl -tag -width Fl +.It Ic \&! Op Ar command Op Ar args +Invoke an interactive shell on the local machine. +If there are arguments, the first is taken to be a command to execute +directly, with the rest of the arguments as its arguments. +.It Ic \&$ Ar macro-name Op Ar args +Execute the macro +.Ar macro-name +that was defined with the +.Ic macdef +command. +Arguments are passed to the macro unglobbed. +.It Ic account Op Ar passwd +Supply a supplemental password required by a remote system for access +to resources once a login has been successfully completed. +If no argument is included, the user will be prompted for an account +password in a non-echoing input mode. +.It Ic append Ar local-file Op Ar remote-file +Append a local file to a file on the remote machine. +If +.Ar remote-file +is left unspecified, the local file name is used in naming the +remote file after being altered by any +.Ic ntrans +or +.Ic nmap +setting. +File transfer uses the current settings for +.Ic type , +.Ic format , +.Ic mode , +and +.Ic structure . +.It Ic ascii +Set the file transfer +.Ic type +to network +.Tn ASCII . +This is the default type. +.It Ic bell +Arrange that a bell be sounded after each file transfer +command is completed. +.It Ic binary +Set the file transfer +.Ic type +to support binary image transfer. +.It Ic bye +Terminate the +.Tn FTP +session with the remote server +and exit +.Nm ftp . +An end of file will also terminate the session and exit. +.It Ic case +Toggle remote computer file name case mapping during +.Ic mget +commands. +When +.Ic case +is on (default is off), remote computer file names with all letters in +upper case are written in the local directory with the letters mapped +to lower case. +.It Ic \&cd Ar remote-directory +Change the working directory on the remote machine +to +.Ar remote-directory . +.It Ic cdup +Change the remote machine working directory to the parent of the +current remote machine working directory. +.It Ic chmod Ar mode file-name +Change the permission modes of the file +.Ar file-name +on the remote +sytem to +.Ar mode . +.It Ic close +Terminate the +.Tn FTP +session with the remote server, and +return to the command interpreter. +Any defined macros are erased. +.It Ic \&cr +Toggle carriage return stripping during +ascii type file retrieval. +Records are denoted by a carriage return/linefeed sequence +during ascii type file transfer. +When +.Ic \&cr +is on (the default), carriage returns are stripped from this +sequence to conform with the +.Ux +single linefeed record +delimiter. +Records on +.Pf non\- Ns Ux +remote systems may contain single linefeeds; +when an ascii type transfer is made, these linefeeds may be +distinguished from a record delimiter only when +.Ic \&cr +is off. +.It Ic delete Ar remote-file +Delete the file +.Ar remote-file +on the remote machine. +.It Ic debug Op Ar debug-value +Toggle debugging mode. +If an optional +.Ar debug-value +is specified it is used to set the debugging level. +When debugging is on, +.Nm ftp +prints each command sent to the remote machine, preceded +by the string +.Ql \-\-> +.It Xo +.Ic dir +.Op Ar remote-directory +.Op Ar local-file +.Xc +Print a listing of the directory contents in the +directory, +.Ar remote-directory , +and, optionally, placing the output in +.Ar local-file . +If interactive prompting is on, +.Nm ftp +will prompt the user to verify that the last argument is indeed the +target local file for receiving +.Ic dir +output. +If no directory is specified, the current working +directory on the remote machine is used. +If no local +file is specified, or +.Ar local-file +is +.Fl , +output comes to the terminal. +.It Ic disconnect +A synonym for +.Ar close . +.It Ic form Ar format +Set the file transfer +.Ic form +to +.Ar format . +The default format is \*(Lqfile\*(Rq. +.It Ic get Ar remote-file Op Ar local-file +Retrieve the +.Ar remote-file +and store it on the local machine. +If the local +file name is not specified, it is given the same +name it has on the remote machine, subject to +alteration by the current +.Ic case , +.Ic ntrans , +and +.Ic nmap +settings. +The current settings for +.Ic type , +.Ic form , +.Ic mode , +and +.Ic structure +are used while transferring the file. +.It Ic glob +Toggle filename expansion for +.Ic mdelete , +.Ic mget +and +.Ic mput . +If globbing is turned off with +.Ic glob , +the file name arguments +are taken literally and not expanded. +Globbing for +.Ic mput +is done as in +.Xr csh 1 . +For +.Ic mdelete +and +.Ic mget , +each remote file name is expanded +separately on the remote machine and the lists are not merged. +Expansion of a directory name is likely to be +different from expansion of the name of an ordinary file: +the exact result depends on the foreign operating system and ftp server, +and can be previewed by doing +.Ql mls remote-files \- +Note: +.Ic mget +and +.Ic mput +are not meant to transfer +entire directory subtrees of files. +That can be done by +transferring a +.Xr tar 1 +archive of the subtree (in binary mode). +.It Ic hash +Toggle hash-sign (``#'') printing for each data block +transferred. +The size of a data block is 1024 bytes. +.It Ic help Op Ar command +Print an informative message about the meaning of +.Ar command . +If no argument is given, +.Nm ftp +prints a list of the known commands. +.It Ic idle Op Ar seconds +Set the inactivity timer on the remote server to +.Ar seconds +seconds. +If +.Ar seconds +is omitted, the current inactivity timer is printed. +.It Ic lcd Op Ar directory +Change the working directory on the local machine. +If +no +.Ar directory +is specified, the user's home directory is used. +.It Xo +.Ic \&ls +.Op Ar remote-directory +.Op Ar local-file +.Xc +Print a listing of the contents of a +directory on the remote machine. +The listing includes any system-dependent information that the server +chooses to include; for example, most +.Ux +systems will produce +output from the command +.Ql ls \-l . +(See also +.Ic nlist . ) +If +.Ar remote-directory +is left unspecified, the current working directory is used. +If interactive prompting is on, +.Nm ftp +will prompt the user to verify that the last argument is indeed the +target local file for receiving +.Ic \&ls +output. +If no local file is specified, or if +.Ar local-file +is +.Sq Fl , +the output is sent to the terminal. +.It Ic macdef Ar macro-name +Define a macro. +Subsequent lines are stored as the macro +.Ar macro-name ; +a null line (consecutive newline characters +in a file or +carriage returns from the terminal) terminates macro input mode. +There is a limit of 16 macros and 4096 total characters in all +defined macros. +Macros remain defined until a +.Ic close +command is executed. +The macro processor interprets `$' and `\e' as special characters. +A `$' followed by a number (or numbers) is replaced by the +corresponding argument on the macro invocation command line. +A `$' followed by an `i' signals that macro processor that the +executing macro is to be looped. +On the first pass `$i' is +replaced by the first argument on the macro invocation command line, +on the second pass it is replaced by the second argument, and so on. +A `\e' followed by any character is replaced by that character. +Use the `\e' to prevent special treatment of the `$'. +.It Ic mdelete Op Ar remote-files +Delete the +.Ar remote-files +on the remote machine. +.It Ic mdir Ar remote-files local-file +Like +.Ic dir , +except multiple remote files may be specified. +If interactive prompting is on, +.Nm ftp +will prompt the user to verify that the last argument is indeed the +target local file for receiving +.Ic mdir +output. +.It Ic mget Ar remote-files +Expand the +.Ar remote-files +on the remote machine +and do a +.Ic get +for each file name thus produced. +See +.Ic glob +for details on the filename expansion. +Resulting file names will then be processed according to +.Ic case , +.Ic ntrans , +and +.Ic nmap +settings. +Files are transferred into the local working directory, +which can be changed with +.Ql lcd directory ; +new local directories can be created with +.Ql "\&! mkdir directory" . +.It Ic mkdir Ar directory-name +Make a directory on the remote machine. +.It Ic mls Ar remote-files local-file +Like +.Ic nlist , +except multiple remote files may be specified, +and the +.Ar local-file +must be specified. +If interactive prompting is on, +.Nm ftp +will prompt the user to verify that the last argument is indeed the +target local file for receiving +.Ic mls +output. +.It Ic mode Op Ar mode-name +Set the file transfer +.Ic mode +to +.Ar mode-name . +The default mode is \*(Lqstream\*(Rq mode. +.It Ic modtime Ar file-name +Show the last modification time of the file on the remote machine. +.It Ic mput Ar local-files +Expand wild cards in the list of local files given as arguments +and do a +.Ic put +for each file in the resulting list. +See +.Ic glob +for details of filename expansion. +Resulting file names will then be processed according to +.Ic ntrans +and +.Ic nmap +settings. +.It Ic newer Ar file-name +Get the file only if the modification time of the remote file is more +recent that the file on the current system. +If the file does not +exist on the current system, the remote file is considered +.Ic newer . +Otherwise, this command is identical to +.Ar get . +.It Xo +.Ic nlist +.Op Ar remote-directory +.Op Ar local-file +.Xc +Print a list of the files in a +directory on the remote machine. +If +.Ar remote-directory +is left unspecified, the current working directory is used. +If interactive prompting is on, +.Nm ftp +will prompt the user to verify that the last argument is indeed the +target local file for receiving +.Ic nlist +output. +If no local file is specified, or if +.Ar local-file +is +.Fl , +the output is sent to the terminal. +.It Ic nmap Op Ar inpattern outpattern +Set or unset the filename mapping mechanism. +If no arguments are specified, the filename mapping mechanism is unset. +If arguments are specified, remote filenames are mapped during +.Ic mput +commands and +.Ic put +commands issued without a specified remote target filename. +If arguments are specified, local filenames are mapped during +.Ic mget +commands and +.Ic get +commands issued without a specified local target filename. +This command is useful when connecting to a +.No non\- Ns Ux +remote computer +with different file naming conventions or practices. +The mapping follows the pattern set by +.Ar inpattern +and +.Ar outpattern . +.Op Ar Inpattern +is a template for incoming filenames (which may have already been +processed according to the +.Ic ntrans +and +.Ic case +settings). +Variable templating is accomplished by including the +sequences `$1', `$2', ..., `$9' in +.Ar inpattern . +Use `\\' to prevent this special treatment of the `$' character. +All other characters are treated literally, and are used to determine the +.Ic nmap +.Op Ar inpattern +variable values. +For example, given +.Ar inpattern +$1.$2 and the remote file name "mydata.data", $1 would have the value +"mydata", and $2 would have the value "data". +The +.Ar outpattern +determines the resulting mapped filename. +The sequences `$1', `$2', ...., `$9' are replaced by any value resulting +from the +.Ar inpattern +template. +The sequence `$0' is replace by the original filename. +Additionally, the sequence +.Ql Op Ar seq1 , Ar seq2 +is replaced by +.Op Ar seq1 +if +.Ar seq1 +is not a null string; otherwise it is replaced by +.Ar seq2 . +For example, the command +.Pp +.Bd -literal -offset indent -compact +nmap $1.$2.$3 [$1,$2].[$2,file] +.Ed +.Pp +would yield +the output filename "myfile.data" for input filenames "myfile.data" and +"myfile.data.old", "myfile.file" for the input filename "myfile", and +"myfile.myfile" for the input filename ".myfile". +Spaces may be included in +.Ar outpattern , +as in the example: `nmap $1 sed "s/ *$//" > $1' . +Use the `\e' character to prevent special treatment +of the `$','[','[', and `,' characters. +.It Ic ntrans Op Ar inchars Op Ar outchars +Set or unset the filename character translation mechanism. +If no arguments are specified, the filename character +translation mechanism is unset. +If arguments are specified, characters in +remote filenames are translated during +.Ic mput +commands and +.Ic put +commands issued without a specified remote target filename. +If arguments are specified, characters in +local filenames are translated during +.Ic mget +commands and +.Ic get +commands issued without a specified local target filename. +This command is useful when connecting to a +.No non\- Ns Ux +remote computer +with different file naming conventions or practices. +Characters in a filename matching a character in +.Ar inchars +are replaced with the corresponding character in +.Ar outchars . +If the character's position in +.Ar inchars +is longer than the length of +.Ar outchars , +the character is deleted from the file name. +.It Ic open Ar host Op Ar port +Establish a connection to the specified +.Ar host +.Tn FTP +server. +An optional port number may be supplied, +in which case, +.Nm ftp +will attempt to contact an +.Tn FTP +server at that port. +If the +.Ic auto-login +option is on (default), +.Nm ftp +will also attempt to automatically log the user in to +the +.Tn FTP +server (see below). +.It Ic passive +Toggle passive mode. If passive mode is turned on +(default is off), the ftp client will +send a +.Dv PASV +command for all data connections instead of the usual +.Dv PORT +command. The +.Dv PASV +command requests that the remote server open a port for the data connection +and return the address of that port. The remote server listens on that +port and the client connects to it. When using the more traditional +.Dv PORT +command, the client listens on a port and sends that address to the remote +server, who connects back to it. Passive mode is useful when using +.Nm ftp +through a gateway router or host that controls the directionality of +traffic. +(Note that though ftp servers are required to support the +.Dv PASV +command by RFC 1123, some do not.) +.It Ic prompt +Toggle interactive prompting. +Interactive prompting +occurs during multiple file transfers to allow the +user to selectively retrieve or store files. +If prompting is turned off (default is on), any +.Ic mget +or +.Ic mput +will transfer all files, and any +.Ic mdelete +will delete all files. +.It Ic proxy Ar ftp-command +Execute an ftp command on a secondary control connection. +This command allows simultaneous connection to two remote ftp +servers for transferring files between the two servers. +The first +.Ic proxy +command should be an +.Ic open , +to establish the secondary control connection. +Enter the command "proxy ?" to see other ftp commands executable on the +secondary connection. +The following commands behave differently when prefaced by +.Ic proxy : +.Ic open +will not define new macros during the auto-login process, +.Ic close +will not erase existing macro definitions, +.Ic get +and +.Ic mget +transfer files from the host on the primary control connection +to the host on the secondary control connection, and +.Ic put , +.Ic mput , +and +.Ic append +transfer files from the host on the secondary control connection +to the host on the primary control connection. +Third party file transfers depend upon support of the ftp protocol +.Dv PASV +command by the server on the secondary control connection. +.It Ic put Ar local-file Op Ar remote-file +Store a local file on the remote machine. +If +.Ar remote-file +is left unspecified, the local file name is used +after processing according to any +.Ic ntrans +or +.Ic nmap +settings +in naming the remote file. +File transfer uses the +current settings for +.Ic type , +.Ic format , +.Ic mode , +and +.Ic structure . +.It Ic pwd +Print the name of the current working directory on the remote +machine. +.It Ic quit +A synonym for +.Ic bye . +.It Ic quote Ar arg1 arg2 ... +The arguments specified are sent, verbatim, to the remote +.Tn FTP +server. +.It Ic recv Ar remote-file Op Ar local-file +A synonym for get. +.It Ic reget Ar remote-file Op Ar local-file +Reget acts like get, except that if +.Ar local-file +exists and is +smaller than +.Ar remote-file , +.Ar local-file +is presumed to be +a partially transferred copy of +.Ar remote-file +and the transfer +is continued from the apparent point of failure. +This command +is useful when transferring very large files over networks that +are prone to dropping connections. +.It Ic remotehelp Op Ar command-name +Request help from the remote +.Tn FTP +server. +If a +.Ar command-name +is specified it is supplied to the server as well. +.It Ic remotestatus Op Ar file-name +With no arguments, show status of remote machine. +If +.Ar file-name +is specified, show status of +.Ar file-name +on remote machine. +.It Xo +.Ic rename +.Op Ar from +.Op Ar to +.Xc +Rename the file +.Ar from +on the remote machine, to the file +.Ar to . +.It Ic reset +Clear reply queue. +This command re-synchronizes command/reply sequencing with the remote +ftp server. +Resynchronization may be necessary following a violation of the ftp protocol +by the remote server. +.It Ic restart Ar marker +Restart the immediately following +.Ic get +or +.Ic put +at the +indicated +.Ar marker . +On +.Ux +systems, marker is usually a byte +offset into the file. +.It Ic rmdir Ar directory-name +Delete a directory on the remote machine. +.It Ic runique +Toggle storing of files on the local system with unique filenames. +If a file already exists with a name equal to the target +local filename for a +.Ic get +or +.Ic mget +command, a ".1" is appended to the name. +If the resulting name matches another existing file, +a ".2" is appended to the original name. +If this process continues up to ".99", an error +message is printed, and the transfer does not take place. +The generated unique filename will be reported. +Note that +.Ic runique +will not affect local files generated from a shell command +(see below). +The default value is off. +.It Ic send Ar local-file Op Ar remote-file +A synonym for put. +.It Ic sendport +Toggle the use of +.Dv PORT +commands. +By default, +.Nm ftp +will attempt to use a +.Dv PORT +command when establishing +a connection for each data transfer. +The use of +.Dv PORT +commands can prevent delays +when performing multiple file transfers. +If the +.Dv PORT +command fails, +.Nm ftp +will use the default data port. +When the use of +.Dv PORT +commands is disabled, no attempt will be made to use +.Dv PORT +commands for each data transfer. +This is useful +for certain +.Tn FTP +implementations which do ignore +.Dv PORT +commands but, incorrectly, indicate they've been accepted. +.It Ic site Ar arg1 arg2 ... +The arguments specified are sent, verbatim, to the remote +.Tn FTP +server as a +.Dv SITE +command. +.It Ic size Ar file-name +Return size of +.Ar file-name +on remote machine. +.It Ic status +Show the current status of +.Nm ftp . +.It Ic struct Op Ar struct-name +Set the file transfer +.Ar structure +to +.Ar struct-name . +By default \*(Lqstream\*(Rq structure is used. +.It Ic sunique +Toggle storing of files on remote machine under unique file names. +Remote ftp server must support ftp protocol +.Dv STOU +command for +successful completion. +The remote server will report unique name. +Default value is off. +.It Ic system +Show the type of operating system running on the remote machine. +.It Ic tenex +Set the file transfer type to that needed to +talk to +.Tn TENEX +machines. +.It Ic trace +Toggle packet tracing. +.It Ic type Op Ar type-name +Set the file transfer +.Ic type +to +.Ar type-name . +If no type is specified, the current type +is printed. +The default type is network +.Tn ASCII . +.It Ic umask Op Ar newmask +Set the default umask on the remote server to +.Ar newmask . +If +.Ar newmask +is omitted, the current umask is printed. +.It Xo +.Ic user Ar user-name +.Op Ar password +.Op Ar account +.Xc +Identify yourself to the remote +.Tn FTP +server. +If the +.Ar password +is not specified and the server requires it, +.Nm ftp +will prompt the user for it (after disabling local echo). +If an +.Ar account +field is not specified, and the +.Tn FTP +server +requires it, the user will be prompted for it. +If an +.Ar account +field is specified, an account command will +be relayed to the remote server after the login sequence +is completed if the remote server did not require it +for logging in. +Unless +.Nm ftp +is invoked with \*(Lqauto-login\*(Rq disabled, this +process is done automatically on initial connection to +the +.Tn FTP +server. +.It Ic verbose +Toggle verbose mode. +In verbose mode, all responses from +the +.Tn FTP +server are displayed to the user. +In addition, +if verbose is on, when a file transfer completes, statistics +regarding the efficiency of the transfer are reported. +By default, +verbose is on. +.It Ic ? Op Ar command +A synonym for help. +.El +.Pp +Command arguments which have embedded spaces may be quoted with +quote `"' marks. +.Sh ABORTING A FILE TRANSFER +To abort a file transfer, use the terminal interrupt key +(usually Ctrl-C). +Sending transfers will be immediately halted. +Receiving transfers will be halted by sending a ftp protocol +.Dv ABOR +command to the remote server, and discarding any further data received. +The speed at which this is accomplished depends upon the remote +server's support for +.Dv ABOR +processing. +If the remote server does not support the +.Dv ABOR +command, an +.Ql ftp> +prompt will not appear until the remote server has completed +sending the requested file. +.Pp +The terminal interrupt key sequence will be ignored when +.Nm ftp +has completed any local processing and is awaiting a reply +from the remote server. +A long delay in this mode may result from the ABOR processing described +above, or from unexpected behavior by the remote server, including +violations of the ftp protocol. +If the delay results from unexpected remote server behavior, the local +.Nm ftp +program must be killed by hand. +.Sh FILE NAMING CONVENTIONS +Files specified as arguments to +.Nm ftp +commands are processed according to the following rules. +.Bl -enum +.It +If the file name +.Sq Fl +is specified, the +.Ar stdin +(for reading) or +.Ar stdout +(for writing) is used. +.It +If the first character of the file name is +.Sq \&| , +the +remainder of the argument is interpreted as a shell command. +.Nm Ftp +then forks a shell, using +.Xr popen 3 +with the argument supplied, and reads (writes) from the stdout +(stdin). +If the shell command includes spaces, the argument +must be quoted; e.g. +\*(Lq" ls -lt"\*(Rq. +A particularly +useful example of this mechanism is: \*(Lqdir more\*(Rq. +.It +Failing the above checks, if ``globbing'' is enabled, +local file names are expanded +according to the rules used in the +.Xr csh 1 ; +c.f. the +.Ic glob +command. +If the +.Nm ftp +command expects a single local file (.e.g. +.Ic put ) , +only the first filename generated by the "globbing" operation is used. +.It +For +.Ic mget +commands and +.Ic get +commands with unspecified local file names, the local filename is +the remote filename, which may be altered by a +.Ic case , +.Ic ntrans , +or +.Ic nmap +setting. +The resulting filename may then be altered if +.Ic runique +is on. +.It +For +.Ic mput +commands and +.Ic put +commands with unspecified remote file names, the remote filename is +the local filename, which may be altered by a +.Ic ntrans +or +.Ic nmap +setting. +The resulting filename may then be altered by the remote server if +.Ic sunique +is on. +.El +.Sh FILE TRANSFER PARAMETERS +The FTP specification specifies many parameters which may +affect a file transfer. +The +.Ic type +may be one of \*(Lqascii\*(Rq, \*(Lqimage\*(Rq (binary), +\*(Lqebcdic\*(Rq, and \*(Lqlocal byte size\*(Rq (for +.Tn PDP Ns -10's +and +.Tn PDP Ns -20's +mostly). +.Nm Ftp +supports the ascii and image types of file transfer, +plus local byte size 8 for +.Ic tenex +mode transfers. +.Pp +.Nm Ftp +supports only the default values for the remaining +file transfer parameters: +.Ic mode , +.Ic form , +and +.Ic struct . +.Sh THE .netrc FILE +The +.Pa .netrc +file contains login and initialization information +used by the auto-login process. +It resides in the user's home directory. +The following tokens are recognized; they may be separated by spaces, +tabs, or new-lines: +.Bl -tag -width password +.It Ic machine Ar name +Identify a remote machine +.Ar name . +The auto-login process searches the +.Pa .netrc +file for a +.Ic machine +token that matches the remote machine specified on the +.Nm ftp +command line or as an +.Ic open +command argument. +Once a match is made, the subsequent +.Pa .netrc +tokens are processed, +stopping when the end of file is reached or another +.Ic machine +or a +.Ic default +token is encountered. +.It Ic default +This is the same as +.Ic machine +.Ar name +except that +.Ic default +matches any name. +There can be only one +.Ic default +token, and it must be after all +.Ic machine +tokens. +This is normally used as: +.Pp +.Dl default login anonymous password user@site +.Pp +thereby giving the user +.Ar automatic +anonymous ftp login to +machines not specified in +.Pa .netrc . +This can be overridden +by using the +.Fl n +flag to disable auto-login. +.It Ic login Ar name +Identify a user on the remote machine. +If this token is present, the auto-login process will initiate +a login using the specified +.Ar name . +.It Ic password Ar string +Supply a password. +If this token is present, the auto-login process will supply the +specified string if the remote server requires a password as part +of the login process. +Note that if this token is present in the +.Pa .netrc +file for any user other +than +.Ar anonymous , +.Nm ftp +will abort the auto-login process if the +.Pa .netrc +is readable by +anyone besides the user. +.It Ic account Ar string +Supply an additional account password. +If this token is present, the auto-login process will supply the +specified string if the remote server requires an additional +account password, or the auto-login process will initiate an +.Dv ACCT +command if it does not. +.It Ic macdef Ar name +Define a macro. +This token functions like the +.Nm ftp +.Ic macdef +command functions. +A macro is defined with the specified name; its contents begin with the +next +.Pa .netrc +line and continue until a null line (consecutive new-line +characters) is encountered. +If a macro named +.Ic init +is defined, it is automatically executed as the last step in the +auto-login process. +.El +.Sh ENVIRONMENT +.Nm Ftp +utilizes the following environment variables. +.Bl -tag -width Fl +.It Ev HOME +For default location of a +.Pa .netrc +file, if one exists. +.It Ev SHELL +For default shell. +.El +.Sh SEE ALSO +.Xr ftpd 8 +.Sh HISTORY +The +.Nm ftp +command appeared in +.Bx 4.2 . +.Sh BUGS +Correct execution of many commands depends upon proper behavior +by the remote server. +.Pp +An error in the treatment of carriage returns +in the +.Bx 4.2 +ascii-mode transfer code +has been corrected. +This correction may result in incorrect transfers of binary files +to and from +.Bx 4.2 +servers using the ascii type. +Avoid this problem by using the binary image type. diff --git a/usr.bin/ftp/ftp.c b/usr.bin/ftp/ftp.c new file mode 100644 index 00000000000..90eccb08c85 --- /dev/null +++ b/usr.bin/ftp/ftp.c @@ -0,0 +1,1530 @@ +/* + * Copyright (c) 1985, 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ftp.c 8.6 (Berkeley) 10/27/94"; +#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 +#include +#include + +#include "ftp_var.h" + +extern int h_errno; + +struct sockaddr_in hisctladdr; +struct sockaddr_in data_addr; +int data = -1; +int abrtflag = 0; +jmp_buf ptabort; +int ptabflg; +int ptflag = 0; +struct sockaddr_in myctladdr; +off_t restart_point = 0; + + +FILE *cin, *cout; + +char * +hookup(host, port) + char *host; + int port; +{ + struct hostent *hp = 0; + int s, len, tos; + static char hostnamebuf[80]; + + memset((char *)&hisctladdr, 0, sizeof (hisctladdr)); + hisctladdr.sin_addr.s_addr = inet_addr(host); + if (hisctladdr.sin_addr.s_addr != -1) { + hisctladdr.sin_family = AF_INET; + (void) strncpy(hostnamebuf, host, sizeof(hostnamebuf)); + } else { + hp = gethostbyname(host); + if (hp == NULL) { + warnx("%s: %s", host, hstrerror(h_errno)); + code = -1; + return ((char *) 0); + } + hisctladdr.sin_family = hp->h_addrtype; + memmove((caddr_t)&hisctladdr.sin_addr, + hp->h_addr_list[0], hp->h_length); + (void) strncpy(hostnamebuf, hp->h_name, sizeof(hostnamebuf)); + } + hostname = hostnamebuf; + s = socket(hisctladdr.sin_family, SOCK_STREAM, 0); + if (s < 0) { + warn("socket"); + code = -1; + return (0); + } + hisctladdr.sin_port = port; + while (connect(s, (struct sockaddr *)&hisctladdr, sizeof (hisctladdr)) < 0) { + if (hp && hp->h_addr_list[1]) { + int oerrno = errno; + char *ia; + + ia = inet_ntoa(hisctladdr.sin_addr); + errno = oerrno; + warn("connect to address %s", ia); + hp->h_addr_list++; + memmove((caddr_t)&hisctladdr.sin_addr, + hp->h_addr_list[0], hp->h_length); + fprintf(stdout, "Trying %s...\n", + inet_ntoa(hisctladdr.sin_addr)); + (void) close(s); + s = socket(hisctladdr.sin_family, SOCK_STREAM, 0); + if (s < 0) { + warn("socket"); + code = -1; + return (0); + } + continue; + } + warn("connect"); + code = -1; + goto bad; + } + len = sizeof (myctladdr); + if (getsockname(s, (struct sockaddr *)&myctladdr, &len) < 0) { + warn("getsockname"); + code = -1; + goto bad; + } +#ifdef IP_TOS + tos = IPTOS_LOWDELAY; + if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) + warn("setsockopt TOS (ignored)"); +#endif + cin = fdopen(s, "r"); + cout = fdopen(s, "w"); + if (cin == NULL || cout == NULL) { + warnx("fdopen failed."); + if (cin) + (void) fclose(cin); + if (cout) + (void) fclose(cout); + code = -1; + goto bad; + } + if (verbose) + printf("Connected to %s.\n", hostname); + if (getreply(0) > 2) { /* read startup message from server */ + if (cin) + (void) fclose(cin); + if (cout) + (void) fclose(cout); + code = -1; + goto bad; + } +#ifdef SO_OOBINLINE + { + int on = 1; + + if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) + < 0 && debug) { + warn("setsockopt"); + } + } +#endif /* SO_OOBINLINE */ + + return (hostname); +bad: + (void) close(s); + return ((char *)0); +} + +int +login(host) + char *host; +{ + char tmp[80]; + char *user, *pass, *acct; + int n, aflag = 0; + + user = pass = acct = 0; + if (ruserpass(host, &user, &pass, &acct) < 0) { + code = -1; + return (0); + } + while (user == NULL) { + char *myname = getlogin(); + + if (myname == NULL) { + struct passwd *pp = getpwuid(getuid()); + + if (pp != NULL) + myname = pp->pw_name; + } + if (myname) + printf("Name (%s:%s): ", host, myname); + else + printf("Name (%s): ", host); + (void) fgets(tmp, sizeof(tmp) - 1, stdin); + tmp[strlen(tmp) - 1] = '\0'; + if (*tmp == '\0') + user = myname; + else + user = tmp; + } + n = command("USER %s", user); + if (n == CONTINUE) { + if (pass == NULL) + pass = getpass("Password:"); + n = command("PASS %s", pass); + } + if (n == CONTINUE) { + aflag++; + acct = getpass("Account:"); + n = command("ACCT %s", acct); + } + if (n != COMPLETE) { + warnx("Login failed."); + return (0); + } + if (!aflag && acct != NULL) + (void) command("ACCT %s", acct); + if (proxy) + return (1); + for (n = 0; n < macnum; ++n) { + if (!strcmp("init", macros[n].mac_name)) { + (void) strcpy(line, "$init"); + makeargv(); + domacro(margc, margv); + break; + } + } + return (1); +} + +void +cmdabort() +{ + + printf("\n"); + (void) fflush(stdout); + abrtflag++; + if (ptflag) + longjmp(ptabort,1); +} + +/*VARARGS*/ +int +command(va_alist) +va_dcl +{ + va_list ap; + char *fmt; + int r; + sig_t oldintr; + + abrtflag = 0; + if (debug) { + printf("---> "); + va_start(ap); + fmt = va_arg(ap, char *); + if (strncmp("PASS ", fmt, 5) == 0) + printf("PASS XXXX"); + else + vfprintf(stdout, fmt, ap); + va_end(ap); + printf("\n"); + (void) fflush(stdout); + } + if (cout == NULL) { + warn("No control connection for command"); + code = -1; + return (0); + } + oldintr = signal(SIGINT, cmdabort); + va_start(ap); + fmt = va_arg(ap, char *); + vfprintf(cout, fmt, ap); + va_end(ap); + fprintf(cout, "\r\n"); + (void) fflush(cout); + cpend = 1; + r = getreply(!strcmp(fmt, "QUIT")); + if (abrtflag && oldintr != SIG_IGN) + (*oldintr)(SIGINT); + (void) signal(SIGINT, oldintr); + return (r); +} + +char reply_string[BUFSIZ]; /* last line of previous reply */ + +int +getreply(expecteof) + int expecteof; +{ + int c, n; + int dig; + int originalcode = 0, continuation = 0; + sig_t oldintr; + int pflag = 0; + char *cp, *pt = pasv; + + oldintr = signal(SIGINT, cmdabort); + for (;;) { + dig = n = code = 0; + cp = reply_string; + while ((c = getc(cin)) != '\n') { + if (c == IAC) { /* handle telnet commands */ + switch (c = getc(cin)) { + case WILL: + case WONT: + c = getc(cin); + fprintf(cout, "%c%c%c", IAC, DONT, c); + (void) fflush(cout); + break; + case DO: + case DONT: + c = getc(cin); + fprintf(cout, "%c%c%c", IAC, WONT, c); + (void) fflush(cout); + break; + default: + break; + } + continue; + } + dig++; + if (c == EOF) { + if (expecteof) { + (void) signal(SIGINT,oldintr); + code = 221; + return (0); + } + lostpeer(); + if (verbose) { + printf("421 Service not available, remote server has closed connection\n"); + (void) fflush(stdout); + } + code = 421; + return (4); + } + if (c != '\r' && (verbose > 0 || + (verbose > -1 && n == '5' && dig > 4))) { + if (proxflag && + (dig == 1 || dig == 5 && verbose == 0)) + printf("%s:",hostname); + (void) putchar(c); + } + if (dig < 4 && isdigit(c)) + code = code * 10 + (c - '0'); + if (!pflag && code == 227) + pflag = 1; + if (dig > 4 && pflag == 1 && isdigit(c)) + pflag = 2; + if (pflag == 2) { + if (c != '\r' && c != ')') + *pt++ = c; + else { + *pt = '\0'; + pflag = 3; + } + } + if (dig == 4 && c == '-') { + if (continuation) + code = 0; + continuation++; + } + if (n == 0) + n = c; + if (cp < &reply_string[sizeof(reply_string) - 1]) + *cp++ = c; + } + if (verbose > 0 || verbose > -1 && n == '5') { + (void) putchar(c); + (void) fflush (stdout); + } + if (continuation && code != originalcode) { + if (originalcode == 0) + originalcode = code; + continue; + } + *cp = '\0'; + if (n != '1') + cpend = 0; + (void) signal(SIGINT,oldintr); + if (code == 421 || originalcode == 421) + lostpeer(); + if (abrtflag && oldintr != cmdabort && oldintr != SIG_IGN) + (*oldintr)(SIGINT); + return (n - '0'); + } +} + +int +empty(mask, sec) + struct fd_set *mask; + int sec; +{ + struct timeval t; + + t.tv_sec = (long) sec; + t.tv_usec = 0; + return (select(32, mask, (struct fd_set *) 0, (struct fd_set *) 0, &t)); +} + +jmp_buf sendabort; + +void +abortsend() +{ + + mflag = 0; + abrtflag = 0; + printf("\nsend aborted\nwaiting for remote to finish abort\n"); + (void) fflush(stdout); + longjmp(sendabort, 1); +} + +#define HASHBYTES 1024 + +void +sendrequest(cmd, local, remote, printnames) + char *cmd, *local, *remote; + int printnames; +{ + struct stat st; + struct timeval start, stop; + int c, d; + FILE *fin, *dout = 0, *popen(); + int (*closefunc) __P((FILE *)); + sig_t oldintr, oldintp; + long bytes = 0, hashbytes = HASHBYTES; + char *lmode, buf[BUFSIZ], *bufp; + + if (verbose && printnames) { + if (local && *local != '-') + printf("local: %s ", local); + if (remote) + printf("remote: %s\n", remote); + } + if (proxy) { + proxtrans(cmd, local, remote); + return; + } + if (curtype != type) + changetype(type, 0); + closefunc = NULL; + oldintr = NULL; + oldintp = NULL; + lmode = "w"; + if (setjmp(sendabort)) { + while (cpend) { + (void) getreply(0); + } + if (data >= 0) { + (void) close(data); + data = -1; + } + if (oldintr) + (void) signal(SIGINT,oldintr); + if (oldintp) + (void) signal(SIGPIPE,oldintp); + code = -1; + return; + } + oldintr = signal(SIGINT, abortsend); + if (strcmp(local, "-") == 0) + fin = stdin; + else if (*local == '|') { + oldintp = signal(SIGPIPE,SIG_IGN); + fin = popen(local + 1, "r"); + if (fin == NULL) { + warn("%s", local + 1); + (void) signal(SIGINT, oldintr); + (void) signal(SIGPIPE, oldintp); + code = -1; + return; + } + closefunc = pclose; + } else { + fin = fopen(local, "r"); + if (fin == NULL) { + warn("local: %s", local); + (void) signal(SIGINT, oldintr); + code = -1; + return; + } + closefunc = fclose; + if (fstat(fileno(fin), &st) < 0 || + (st.st_mode&S_IFMT) != S_IFREG) { + fprintf(stdout, "%s: not a plain file.\n", local); + (void) signal(SIGINT, oldintr); + fclose(fin); + code = -1; + return; + } + } + if (initconn()) { + (void) signal(SIGINT, oldintr); + if (oldintp) + (void) signal(SIGPIPE, oldintp); + code = -1; + if (closefunc != NULL) + (*closefunc)(fin); + return; + } + if (setjmp(sendabort)) + goto abort; + + if (restart_point && + (strcmp(cmd, "STOR") == 0 || strcmp(cmd, "APPE") == 0)) { + int rc; + + switch (curtype) { + case TYPE_A: + rc = fseek(fin, (long) restart_point, SEEK_SET); + break; + case TYPE_I: + case TYPE_L: + rc = lseek(fileno(fin), restart_point, SEEK_SET); + break; + } + if (rc < 0) { + warn("local: %s", local); + restart_point = 0; + if (closefunc != NULL) + (*closefunc)(fin); + return; + } + if (command("REST %ld", (long) restart_point) + != CONTINUE) { + restart_point = 0; + if (closefunc != NULL) + (*closefunc)(fin); + return; + } + restart_point = 0; + lmode = "r+w"; + } + if (remote) { + if (command("%s %s", cmd, remote) != PRELIM) { + (void) signal(SIGINT, oldintr); + if (oldintp) + (void) signal(SIGPIPE, oldintp); + if (closefunc != NULL) + (*closefunc)(fin); + return; + } + } else + if (command("%s", cmd) != PRELIM) { + (void) signal(SIGINT, oldintr); + if (oldintp) + (void) signal(SIGPIPE, oldintp); + if (closefunc != NULL) + (*closefunc)(fin); + return; + } + dout = dataconn(lmode); + if (dout == NULL) + goto abort; + (void) gettimeofday(&start, (struct timezone *)0); + oldintp = signal(SIGPIPE, SIG_IGN); + switch (curtype) { + + case TYPE_I: + case TYPE_L: + errno = d = 0; + while ((c = read(fileno(fin), buf, sizeof (buf))) > 0) { + bytes += c; + for (bufp = buf; c > 0; c -= d, bufp += d) + if ((d = write(fileno(dout), bufp, c)) <= 0) + break; + if (hash) { + while (bytes >= hashbytes) { + (void) putchar('#'); + hashbytes += HASHBYTES; + } + (void) fflush(stdout); + } + } + if (hash && bytes > 0) { + if (bytes < HASHBYTES) + (void) putchar('#'); + (void) putchar('\n'); + (void) fflush(stdout); + } + if (c < 0) + warn("local: %s", local); + if (d < 0) { + if (errno != EPIPE) + warn("netout"); + bytes = -1; + } + break; + + case TYPE_A: + while ((c = getc(fin)) != EOF) { + if (c == '\n') { + while (hash && (bytes >= hashbytes)) { + (void) putchar('#'); + (void) fflush(stdout); + hashbytes += HASHBYTES; + } + if (ferror(dout)) + break; + (void) putc('\r', dout); + bytes++; + } + (void) putc(c, dout); + bytes++; + /* if (c == '\r') { */ + /* (void) putc('\0', dout); // this violates rfc */ + /* bytes++; */ + /* } */ + } + if (hash) { + if (bytes < hashbytes) + (void) putchar('#'); + (void) putchar('\n'); + (void) fflush(stdout); + } + if (ferror(fin)) + warn("local: %s", local); + if (ferror(dout)) { + if (errno != EPIPE) + warn("netout"); + bytes = -1; + } + break; + } + if (closefunc != NULL) + (*closefunc)(fin); + (void) fclose(dout); + (void) gettimeofday(&stop, (struct timezone *)0); + (void) getreply(0); + (void) signal(SIGINT, oldintr); + if (oldintp) + (void) signal(SIGPIPE, oldintp); + if (bytes > 0) + ptransfer("sent", bytes, &start, &stop); + return; +abort: + (void) signal(SIGINT, oldintr); + if (oldintp) + (void) signal(SIGPIPE, oldintp); + if (!cpend) { + code = -1; + return; + } + if (data >= 0) { + (void) close(data); + data = -1; + } + if (dout) + (void) fclose(dout); + (void) getreply(0); + code = -1; + if (closefunc != NULL && fin != NULL) + (*closefunc)(fin); + (void) gettimeofday(&stop, (struct timezone *)0); + if (bytes > 0) + ptransfer("sent", bytes, &start, &stop); +} + +jmp_buf recvabort; + +void +abortrecv() +{ + + mflag = 0; + abrtflag = 0; + printf("\nreceive aborted\nwaiting for remote to finish abort\n"); + (void) fflush(stdout); + longjmp(recvabort, 1); +} + +void +recvrequest(cmd, local, remote, lmode, printnames) + char *cmd, *local, *remote, *lmode; + int printnames; +{ + FILE *fout, *din = 0; + int (*closefunc) __P((FILE *)); + sig_t oldintr, oldintp; + int c, d, is_retr, tcrflag, bare_lfs = 0; + static int bufsize; + static char *buf; + long bytes = 0, hashbytes = HASHBYTES; + struct timeval start, stop; + struct stat st; + + is_retr = strcmp(cmd, "RETR") == 0; + if (is_retr && verbose && printnames) { + if (local && *local != '-') + printf("local: %s ", local); + if (remote) + printf("remote: %s\n", remote); + } + if (proxy && is_retr) { + proxtrans(cmd, local, remote); + return; + } + closefunc = NULL; + oldintr = NULL; + oldintp = NULL; + tcrflag = !crflag && is_retr; + if (setjmp(recvabort)) { + while (cpend) { + (void) getreply(0); + } + if (data >= 0) { + (void) close(data); + data = -1; + } + if (oldintr) + (void) signal(SIGINT, oldintr); + code = -1; + return; + } + oldintr = signal(SIGINT, abortrecv); + if (strcmp(local, "-") && *local != '|') { + if (access(local, 2) < 0) { + char *dir = strrchr(local, '/'); + + if (errno != ENOENT && errno != EACCES) { + warn("local: %s", local); + (void) signal(SIGINT, oldintr); + code = -1; + return; + } + if (dir != NULL) + *dir = 0; + d = access(dir ? local : ".", 2); + if (dir != NULL) + *dir = '/'; + if (d < 0) { + warn("local: %s", local); + (void) signal(SIGINT, oldintr); + code = -1; + return; + } + if (!runique && errno == EACCES && + chmod(local, 0600) < 0) { + warn("local: %s", local); + (void) signal(SIGINT, oldintr); + (void) signal(SIGINT, oldintr); + code = -1; + return; + } + if (runique && errno == EACCES && + (local = gunique(local)) == NULL) { + (void) signal(SIGINT, oldintr); + code = -1; + return; + } + } + else if (runique && (local = gunique(local)) == NULL) { + (void) signal(SIGINT, oldintr); + code = -1; + return; + } + } + if (!is_retr) { + if (curtype != TYPE_A) + changetype(TYPE_A, 0); + } else if (curtype != type) + changetype(type, 0); + if (initconn()) { + (void) signal(SIGINT, oldintr); + code = -1; + return; + } + if (setjmp(recvabort)) + goto abort; + if (is_retr && restart_point && + command("REST %ld", (long) restart_point) != CONTINUE) + return; + if (remote) { + if (command("%s %s", cmd, remote) != PRELIM) { + (void) signal(SIGINT, oldintr); + return; + } + } else { + if (command("%s", cmd) != PRELIM) { + (void) signal(SIGINT, oldintr); + return; + } + } + din = dataconn("r"); + if (din == NULL) + goto abort; + if (strcmp(local, "-") == 0) + fout = stdout; + else if (*local == '|') { + oldintp = signal(SIGPIPE, SIG_IGN); + fout = popen(local + 1, "w"); + if (fout == NULL) { + warn("%s", local+1); + goto abort; + } + closefunc = pclose; + } else { + fout = fopen(local, lmode); + if (fout == NULL) { + warn("local: %s", local); + goto abort; + } + closefunc = fclose; + } + if (fstat(fileno(fout), &st) < 0 || st.st_blksize == 0) + st.st_blksize = BUFSIZ; + if (st.st_blksize > bufsize) { + if (buf) + (void) free(buf); + buf = malloc((unsigned)st.st_blksize); + if (buf == NULL) { + warn("malloc"); + bufsize = 0; + goto abort; + } + bufsize = st.st_blksize; + } + (void) gettimeofday(&start, (struct timezone *)0); + switch (curtype) { + + case TYPE_I: + case TYPE_L: + if (restart_point && + lseek(fileno(fout), restart_point, SEEK_SET) < 0) { + warn("local: %s", local); + if (closefunc != NULL) + (*closefunc)(fout); + return; + } + errno = d = 0; + while ((c = read(fileno(din), buf, bufsize)) > 0) { + if ((d = write(fileno(fout), buf, c)) != c) + break; + bytes += c; + if (hash) { + while (bytes >= hashbytes) { + (void) putchar('#'); + hashbytes += HASHBYTES; + } + (void) fflush(stdout); + } + } + if (hash && bytes > 0) { + if (bytes < HASHBYTES) + (void) putchar('#'); + (void) putchar('\n'); + (void) fflush(stdout); + } + if (c < 0) { + if (errno != EPIPE) + warn("netin"); + bytes = -1; + } + if (d < c) { + if (d < 0) + warn("local: %s", local); + else + warnx("%s: short write", local); + } + break; + + case TYPE_A: + if (restart_point) { + int i, n, ch; + + if (fseek(fout, 0L, SEEK_SET) < 0) + goto done; + n = restart_point; + for (i = 0; i++ < n;) { + if ((ch = getc(fout)) == EOF) + goto done; + if (ch == '\n') + i++; + } + if (fseek(fout, 0L, SEEK_CUR) < 0) { +done: + warn("local: %s", local); + if (closefunc != NULL) + (*closefunc)(fout); + return; + } + } + while ((c = getc(din)) != EOF) { + if (c == '\n') + bare_lfs++; + while (c == '\r') { + while (hash && (bytes >= hashbytes)) { + (void) putchar('#'); + (void) fflush(stdout); + hashbytes += HASHBYTES; + } + bytes++; + if ((c = getc(din)) != '\n' || tcrflag) { + if (ferror(fout)) + goto break2; + (void) putc('\r', fout); + if (c == '\0') { + bytes++; + goto contin2; + } + if (c == EOF) + goto contin2; + } + } + (void) putc(c, fout); + bytes++; + contin2: ; + } +break2: + if (bare_lfs) { + printf("WARNING! %d bare linefeeds received in ASCII mode\n", bare_lfs); + printf("File may not have transferred correctly.\n"); + } + if (hash) { + if (bytes < hashbytes) + (void) putchar('#'); + (void) putchar('\n'); + (void) fflush(stdout); + } + if (ferror(din)) { + if (errno != EPIPE) + warn("netin"); + bytes = -1; + } + if (ferror(fout)) + warn("local: %s", local); + break; + } + if (closefunc != NULL) + (*closefunc)(fout); + (void) signal(SIGINT, oldintr); + if (oldintp) + (void) signal(SIGPIPE, oldintp); + (void) fclose(din); + (void) gettimeofday(&stop, (struct timezone *)0); + (void) getreply(0); + if (bytes > 0 && is_retr) + ptransfer("received", bytes, &start, &stop); + return; +abort: + +/* abort using RFC959 recommended IP,SYNC sequence */ + + if (oldintp) + (void) signal(SIGPIPE, oldintr); + (void) signal(SIGINT, SIG_IGN); + if (!cpend) { + code = -1; + (void) signal(SIGINT, oldintr); + return; + } + + abort_remote(din); + code = -1; + if (data >= 0) { + (void) close(data); + data = -1; + } + if (closefunc != NULL && fout != NULL) + (*closefunc)(fout); + if (din) + (void) fclose(din); + (void) gettimeofday(&stop, (struct timezone *)0); + if (bytes > 0) + ptransfer("received", bytes, &start, &stop); + (void) signal(SIGINT, oldintr); +} + +/* + * Need to start a listen on the data channel before we send the command, + * otherwise the server's connect may fail. + */ +int +initconn() +{ + char *p, *a; + int result, len, tmpno = 0; + int on = 1; + int a0, a1, a2, a3, p0, p1; + + if (passivemode) { + data = socket(AF_INET, SOCK_STREAM, 0); + if (data < 0) { + perror("ftp: socket"); + return(1); + } + if ((options & SO_DEBUG) && + setsockopt(data, SOL_SOCKET, SO_DEBUG, (char *)&on, + sizeof (on)) < 0) + perror("ftp: setsockopt (ignored)"); + if (command("PASV") != COMPLETE) { + printf("Passive mode refused.\n"); + goto bad; + } + + /* + * What we've got at this point is a string of comma + * separated one-byte unsigned integer values. + * The first four are the an IP address. The fifth is + * the MSB of the port number, the sixth is the LSB. + * From that we'll prepare a sockaddr_in. + */ + + if (sscanf(pasv,"%d,%d,%d,%d,%d,%d", + &a0, &a1, &a2, &a3, &p0, &p1) != 6) { + printf("Passive mode address scan failure. " + "Shouldn't happen!\n"); + goto bad; + } + + bzero(&data_addr, sizeof(data_addr)); + data_addr.sin_family = AF_INET; + a = (char *)&data_addr.sin_addr.s_addr; + a[0] = a0 & 0xff; + a[1] = a1 & 0xff; + a[2] = a2 & 0xff; + a[3] = a3 & 0xff; + p = (char *)&data_addr.sin_port; + p[0] = p0 & 0xff; + p[1] = p1 & 0xff; + + if (connect(data, (struct sockaddr *)&data_addr, + sizeof(data_addr)) < 0) { + perror("ftp: connect"); + goto bad; + } +#ifdef IP_TOS + on = IPTOS_THROUGHPUT; + if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&on, + sizeof(int)) < 0) + perror("ftp: setsockopt TOS (ignored)"); +#endif + return(0); + } + +noport: + data_addr = myctladdr; + if (sendport) + data_addr.sin_port = 0; /* let system pick one */ + if (data != -1) + (void) close(data); + data = socket(AF_INET, SOCK_STREAM, 0); + if (data < 0) { + warn("socket"); + if (tmpno) + sendport = 1; + return (1); + } + if (!sendport) + if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof (on)) < 0) { + warn("setsockopt (reuse address)"); + goto bad; + } + if (bind(data, (struct sockaddr *)&data_addr, sizeof (data_addr)) < 0) { + warn("bind"); + goto bad; + } + if (options & SO_DEBUG && + setsockopt(data, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof (on)) < 0) + warn("setsockopt (ignored)"); + len = sizeof (data_addr); + if (getsockname(data, (struct sockaddr *)&data_addr, &len) < 0) { + warn("getsockname"); + goto bad; + } + if (listen(data, 1) < 0) + warn("listen"); + if (sendport) { + a = (char *)&data_addr.sin_addr; + p = (char *)&data_addr.sin_port; +#define UC(b) (((int)b)&0xff) + result = + command("PORT %d,%d,%d,%d,%d,%d", + UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), + UC(p[0]), UC(p[1])); + if (result == ERROR && sendport == -1) { + sendport = 0; + tmpno = 1; + goto noport; + } + return (result != COMPLETE); + } + if (tmpno) + sendport = 1; +#ifdef IP_TOS + on = IPTOS_THROUGHPUT; + if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0) + warn("setsockopt TOS (ignored)"); +#endif + return (0); +bad: + (void) close(data), data = -1; + if (tmpno) + sendport = 1; + return (1); +} + +FILE * +dataconn(lmode) + char *lmode; +{ + struct sockaddr_in from; + int s, fromlen = sizeof (from), tos; + + if (passivemode) + return (fdopen(data, lmode)); + + s = accept(data, (struct sockaddr *) &from, &fromlen); + if (s < 0) { + warn("accept"); + (void) close(data), data = -1; + return (NULL); + } + (void) close(data); + data = s; +#ifdef IP_TOS + tos = IPTOS_THROUGHPUT; + if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) + warn("setsockopt TOS (ignored)"); +#endif + return (fdopen(data, lmode)); +} + +void +ptransfer(direction, bytes, t0, t1) + char *direction; + long bytes; + struct timeval *t0, *t1; +{ + struct timeval td; + float s; + long bs; + + if (verbose) { + tvsub(&td, t1, t0); + s = td.tv_sec + (td.tv_usec / 1000000.); +#define nz(x) ((x) == 0 ? 1 : (x)) + bs = bytes / nz(s); + printf("%ld bytes %s in %.3g seconds (%ld bytes/s)\n", + bytes, direction, s, bs); + } +} + +/* +void +tvadd(tsum, t0) + struct timeval *tsum, *t0; +{ + + tsum->tv_sec += t0->tv_sec; + tsum->tv_usec += t0->tv_usec; + if (tsum->tv_usec > 1000000) + tsum->tv_sec++, tsum->tv_usec -= 1000000; +} +*/ + +void +tvsub(tdiff, t1, t0) + struct timeval *tdiff, *t1, *t0; +{ + + tdiff->tv_sec = t1->tv_sec - t0->tv_sec; + tdiff->tv_usec = t1->tv_usec - t0->tv_usec; + if (tdiff->tv_usec < 0) + tdiff->tv_sec--, tdiff->tv_usec += 1000000; +} + +void +psabort() +{ + + abrtflag++; +} + +void +pswitch(flag) + int flag; +{ + sig_t oldintr; + static struct comvars { + int connect; + char name[MAXHOSTNAMELEN]; + struct sockaddr_in mctl; + struct sockaddr_in hctl; + FILE *in; + FILE *out; + int tpe; + int curtpe; + int cpnd; + int sunqe; + int runqe; + int mcse; + int ntflg; + char nti[17]; + char nto[17]; + int mapflg; + char mi[MAXPATHLEN]; + char mo[MAXPATHLEN]; + } proxstruct, tmpstruct; + struct comvars *ip, *op; + + abrtflag = 0; + oldintr = signal(SIGINT, psabort); + if (flag) { + if (proxy) + return; + ip = &tmpstruct; + op = &proxstruct; + proxy++; + } else { + if (!proxy) + return; + ip = &proxstruct; + op = &tmpstruct; + proxy = 0; + } + ip->connect = connected; + connected = op->connect; + if (hostname) { + (void) strncpy(ip->name, hostname, sizeof(ip->name) - 1); + ip->name[strlen(ip->name)] = '\0'; + } else + ip->name[0] = 0; + hostname = op->name; + ip->hctl = hisctladdr; + hisctladdr = op->hctl; + ip->mctl = myctladdr; + myctladdr = op->mctl; + ip->in = cin; + cin = op->in; + ip->out = cout; + cout = op->out; + ip->tpe = type; + type = op->tpe; + ip->curtpe = curtype; + curtype = op->curtpe; + ip->cpnd = cpend; + cpend = op->cpnd; + ip->sunqe = sunique; + sunique = op->sunqe; + ip->runqe = runique; + runique = op->runqe; + ip->mcse = mcase; + mcase = op->mcse; + ip->ntflg = ntflag; + ntflag = op->ntflg; + (void) strncpy(ip->nti, ntin, 16); + (ip->nti)[strlen(ip->nti)] = '\0'; + (void) strcpy(ntin, op->nti); + (void) strncpy(ip->nto, ntout, 16); + (ip->nto)[strlen(ip->nto)] = '\0'; + (void) strcpy(ntout, op->nto); + ip->mapflg = mapflag; + mapflag = op->mapflg; + (void) strncpy(ip->mi, mapin, MAXPATHLEN - 1); + (ip->mi)[strlen(ip->mi)] = '\0'; + (void) strcpy(mapin, op->mi); + (void) strncpy(ip->mo, mapout, MAXPATHLEN - 1); + (ip->mo)[strlen(ip->mo)] = '\0'; + (void) strcpy(mapout, op->mo); + (void) signal(SIGINT, oldintr); + if (abrtflag) { + abrtflag = 0; + (*oldintr)(SIGINT); + } +} + +void +abortpt() +{ + + printf("\n"); + (void) fflush(stdout); + ptabflg++; + mflag = 0; + abrtflag = 0; + longjmp(ptabort, 1); +} + +void +proxtrans(cmd, local, remote) + char *cmd, *local, *remote; +{ + sig_t oldintr; + int secndflag = 0, prox_type, nfnd; + char *cmd2; + struct fd_set mask; + + if (strcmp(cmd, "RETR")) + cmd2 = "RETR"; + else + cmd2 = runique ? "STOU" : "STOR"; + if ((prox_type = type) == 0) { + if (unix_server && unix_proxy) + prox_type = TYPE_I; + else + prox_type = TYPE_A; + } + if (curtype != prox_type) + changetype(prox_type, 1); + if (command("PASV") != COMPLETE) { + printf("proxy server does not support third party transfers.\n"); + return; + } + pswitch(0); + if (!connected) { + printf("No primary connection\n"); + pswitch(1); + code = -1; + return; + } + if (curtype != prox_type) + changetype(prox_type, 1); + if (command("PORT %s", pasv) != COMPLETE) { + pswitch(1); + return; + } + if (setjmp(ptabort)) + goto abort; + oldintr = signal(SIGINT, abortpt); + if (command("%s %s", cmd, remote) != PRELIM) { + (void) signal(SIGINT, oldintr); + pswitch(1); + return; + } + sleep(2); + pswitch(1); + secndflag++; + if (command("%s %s", cmd2, local) != PRELIM) + goto abort; + ptflag++; + (void) getreply(0); + pswitch(0); + (void) getreply(0); + (void) signal(SIGINT, oldintr); + pswitch(1); + ptflag = 0; + printf("local: %s remote: %s\n", local, remote); + return; +abort: + (void) signal(SIGINT, SIG_IGN); + ptflag = 0; + if (strcmp(cmd, "RETR") && !proxy) + pswitch(1); + else if (!strcmp(cmd, "RETR") && proxy) + pswitch(0); + if (!cpend && !secndflag) { /* only here if cmd = "STOR" (proxy=1) */ + if (command("%s %s", cmd2, local) != PRELIM) { + pswitch(0); + if (cpend) + abort_remote((FILE *) NULL); + } + pswitch(1); + if (ptabflg) + code = -1; + (void) signal(SIGINT, oldintr); + return; + } + if (cpend) + abort_remote((FILE *) NULL); + pswitch(!proxy); + if (!cpend && !secndflag) { /* only if cmd = "RETR" (proxy=1) */ + if (command("%s %s", cmd2, local) != PRELIM) { + pswitch(0); + if (cpend) + abort_remote((FILE *) NULL); + pswitch(1); + if (ptabflg) + code = -1; + (void) signal(SIGINT, oldintr); + return; + } + } + if (cpend) + abort_remote((FILE *) NULL); + pswitch(!proxy); + if (cpend) { + FD_ZERO(&mask); + FD_SET(fileno(cin), &mask); + if ((nfnd = empty(&mask, 10)) <= 0) { + if (nfnd < 0) { + warn("abort"); + } + if (ptabflg) + code = -1; + lostpeer(); + } + (void) getreply(0); + (void) getreply(0); + } + if (proxy) + pswitch(0); + pswitch(1); + if (ptabflg) + code = -1; + (void) signal(SIGINT, oldintr); +} + +void +reset(argc, argv) + int argc; + char *argv[]; +{ + struct fd_set mask; + int nfnd = 1; + + FD_ZERO(&mask); + while (nfnd > 0) { + FD_SET(fileno(cin), &mask); + if ((nfnd = empty(&mask,0)) < 0) { + warn("reset"); + code = -1; + lostpeer(); + } + else if (nfnd) { + (void) getreply(0); + } + } +} + +char * +gunique(local) + char *local; +{ + static char new[MAXPATHLEN]; + char *cp = strrchr(local, '/'); + int d, count=0; + char ext = '1'; + + if (cp) + *cp = '\0'; + d = access(cp ? local : ".", 2); + if (cp) + *cp = '/'; + if (d < 0) { + warn("local: %s", local); + return ((char *) 0); + } + (void) strcpy(new, local); + cp = new + strlen(new); + *cp++ = '.'; + while (!d) { + if (++count == 100) { + printf("runique: can't find unique file name.\n"); + return ((char *) 0); + } + *cp++ = ext; + *cp = '\0'; + if (ext == '9') + ext = '0'; + else + ext++; + if ((d = access(new, 0)) < 0) + break; + if (ext != '0') + cp--; + else if (*(cp - 2) == '.') + *(cp - 1) = '1'; + else { + *(cp - 2) = *(cp - 2) + 1; + cp--; + } + } + return (new); +} + +void +abort_remote(din) + FILE *din; +{ + char buf[BUFSIZ]; + int nfnd; + struct fd_set mask; + + /* + * send IAC in urgent mode instead of DM because 4.3BSD places oob mark + * after urgent byte rather than before as is protocol now + */ + sprintf(buf, "%c%c%c", IAC, IP, IAC); + if (send(fileno(cout), buf, 3, MSG_OOB) != 3) + warn("abort"); + fprintf(cout,"%cABOR\r\n", DM); + (void) fflush(cout); + FD_ZERO(&mask); + FD_SET(fileno(cin), &mask); + if (din) { + FD_SET(fileno(din), &mask); + } + if ((nfnd = empty(&mask, 10)) <= 0) { + if (nfnd < 0) { + warn("abort"); + } + if (ptabflg) + code = -1; + lostpeer(); + } + if (din && FD_ISSET(fileno(din), &mask)) { + while (read(fileno(din), buf, BUFSIZ) > 0) + /* LOOP */; + } + if (getreply(0) == ERROR && code == 552) { + /* 552 needed for nic style abort */ + (void) getreply(0); + } + (void) getreply(0); +} diff --git a/usr.bin/ftp/ftp_var.h b/usr.bin/ftp/ftp_var.h new file mode 100644 index 00000000000..8252f5e3dd1 --- /dev/null +++ b/usr.bin/ftp/ftp_var.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 1985, 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ftp_var.h 8.4 (Berkeley) 10/9/94 + */ + +/* + * FTP global variables. + */ + +#include +#include + +#include "extern.h" + +/* + * Options and other state info. + */ +int trace; /* trace packets exchanged */ +int hash; /* print # for each buffer transferred */ +int sendport; /* use PORT cmd for each data connection */ +int verbose; /* print messages coming back from server */ +int connected; /* connected to server */ +int fromatty; /* input is from a terminal */ +int interactive; /* interactively prompt on m* cmds */ +int debug; /* debugging level */ +int bell; /* ring bell on cmd completion */ +int doglob; /* glob local file names */ +int autologin; /* establish user account on connection */ +int proxy; /* proxy server connection active */ +int proxflag; /* proxy connection exists */ +int sunique; /* store files on server with unique name */ +int runique; /* store local files with unique name */ +int mcase; /* map upper to lower case for mget names */ +int ntflag; /* use ntin ntout tables for name translation */ +int mapflag; /* use mapin mapout templates on file names */ +int code; /* return/reply code for ftp command */ +int crflag; /* if 1, strip car. rets. on ascii gets */ +char pasv[64]; /* passive port for proxy data connection */ +int passivemode; /* passive mode enabled */ +char *altarg; /* argv[1] with no shell-like preprocessing */ +char ntin[17]; /* input translation table */ +char ntout[17]; /* output translation table */ +char mapin[MAXPATHLEN]; /* input map template */ +char mapout[MAXPATHLEN]; /* output map template */ +char typename[32]; /* name of file transfer type */ +int type; /* requested file transfer type */ +int curtype; /* current file transfer type */ +char structname[32]; /* name of file transfer structure */ +int stru; /* file transfer structure */ +char formname[32]; /* name of file transfer format */ +int form; /* file transfer format */ +char modename[32]; /* name of file transfer mode */ +int mode; /* file transfer mode */ +char bytename[32]; /* local byte size in ascii */ +int bytesize; /* local byte size in binary */ + +char *hostname; /* name of host connected to */ +int unix_server; /* server is unix, can use binary for ascii */ +int unix_proxy; /* proxy is unix, can use binary for ascii */ + +struct servent *sp; /* service spec for tcp/ftp */ + +jmp_buf toplevel; /* non-local goto stuff for cmd scanner */ + +char line[200]; /* input line buffer */ +char *stringbase; /* current scan point in line buffer */ +char argbuf[200]; /* argument storage buffer */ +char *argbase; /* current storage point in arg buffer */ +int margc; /* count of arguments on input line */ +char *margv[20]; /* args parsed from input line */ +int cpend; /* flag: if != 0, then pending server reply */ +int mflag; /* flag: if != 0, then active multi command */ + +int options; /* used during socket creation */ + +/* + * Format of command table. + */ +struct cmd { + char *c_name; /* name of command */ + char *c_help; /* help string */ + char c_bell; /* give bell when command completes */ + char c_conn; /* must be connected to use command */ + char c_proxy; /* proxy server may execute */ + void (*c_handler) __P((int, char **)); /* function to call */ +}; + +struct macel { + char mac_name[9]; /* macro name */ + char *mac_start; /* start of macro in macbuf */ + char *mac_end; /* end of macro in macbuf */ +}; + +int macnum; /* number of defined macros */ +struct macel macros[16]; +char macbuf[4096]; diff --git a/usr.bin/ftp/main.c b/usr.bin/ftp/main.c new file mode 100644 index 00000000000..72c63cda435 --- /dev/null +++ b/usr.bin/ftp/main.c @@ -0,0 +1,515 @@ +/* + * Copyright (c) 1985, 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1985, 1989, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 10/9/94"; +#endif /* not lint */ + +/* + * FTP User Program -- Command Interface. + */ +/*#include */ +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ftp_var.h" + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch, top; + struct passwd *pw = NULL; + char *cp, homedir[MAXPATHLEN]; + + sp = getservbyname("ftp", "tcp"); + if (sp == 0) + errx(1, "ftp/tcp: unknown service"); + doglob = 1; + interactive = 1; + autologin = 1; + + while ((ch = getopt(argc, argv, "dgintv")) != EOF) { + switch (ch) { + case 'd': + options |= SO_DEBUG; + debug++; + break; + + case 'g': + doglob = 0; + break; + + case 'i': + interactive = 0; + break; + + case 'n': + autologin = 0; + break; + + case 't': + trace++; + break; + + case 'v': + verbose++; + break; + + default: + (void)fprintf(stderr, + "usage: ftp [-dgintv] [host [port]]\n"); + exit(1); + } + } + argc -= optind; + argv += optind; + + fromatty = isatty(fileno(stdin)); + if (fromatty) + verbose++; + cpend = 0; /* no pending replies */ + proxy = 0; /* proxy not active */ + passivemode = 0; /* passive mode not active */ + crflag = 1; /* strip c.r. on ascii gets */ + sendport = -1; /* not using ports */ + /* + * Set up the home directory in case we're globbing. + */ + cp = getlogin(); + if (cp != NULL) { + pw = getpwnam(cp); + } + if (pw == NULL) + pw = getpwuid(getuid()); + if (pw != NULL) { + home = homedir; + (void) strcpy(home, pw->pw_dir); + } + if (argc > 0) { + char *xargv[5]; + extern char *__progname; + + if (setjmp(toplevel)) + exit(0); + (void) signal(SIGINT, intr); + (void) signal(SIGPIPE, lostpeer); + xargv[0] = __progname; + xargv[1] = argv[0]; + xargv[2] = argv[1]; + xargv[3] = argv[2]; + xargv[4] = NULL; + setpeer(argc+1, xargv); + } + top = setjmp(toplevel) == 0; + if (top) { + (void) signal(SIGINT, intr); + (void) signal(SIGPIPE, lostpeer); + } + for (;;) { + cmdscanner(top); + top = 1; + } +} + +void +intr() +{ + + longjmp(toplevel, 1); +} + +void +lostpeer() +{ + + if (connected) { + if (cout != NULL) { + (void) shutdown(fileno(cout), 1+1); + (void) fclose(cout); + cout = NULL; + } + if (data >= 0) { + (void) shutdown(data, 1+1); + (void) close(data); + data = -1; + } + connected = 0; + } + pswitch(1); + if (connected) { + if (cout != NULL) { + (void) shutdown(fileno(cout), 1+1); + (void) fclose(cout); + cout = NULL; + } + connected = 0; + } + proxflag = 0; + pswitch(0); +} + +/* +char * +tail(filename) + char *filename; +{ + char *s; + + while (*filename) { + s = strrchr(filename, '/'); + if (s == NULL) + break; + if (s[1]) + return (s + 1); + *s = '\0'; + } + return (filename); +} +*/ + +/* + * Command parser. + */ +void +cmdscanner(top) + int top; +{ + struct cmd *c; + int l; + + if (!top) + (void) putchar('\n'); + for (;;) { + if (fromatty) { + printf("ftp> "); + (void) fflush(stdout); + } + if (fgets(line, sizeof line, stdin) == NULL) + quit(0, 0); + l = strlen(line); + if (l == 0) + break; + if (line[--l] == '\n') { + if (l == 0) + break; + line[l] = '\0'; + } else if (l == sizeof(line) - 2) { + printf("sorry, input line too long\n"); + while ((l = getchar()) != '\n' && l != EOF) + /* void */; + break; + } /* else it was a line without a newline */ + makeargv(); + if (margc == 0) { + continue; + } + c = getcmd(margv[0]); + if (c == (struct cmd *)-1) { + printf("?Ambiguous command\n"); + continue; + } + if (c == 0) { + printf("?Invalid command\n"); + continue; + } + if (c->c_conn && !connected) { + printf("Not connected.\n"); + continue; + } + (*c->c_handler)(margc, margv); + if (bell && c->c_bell) + (void) putchar('\007'); + if (c->c_handler != help) + break; + } + (void) signal(SIGINT, intr); + (void) signal(SIGPIPE, lostpeer); +} + +struct cmd * +getcmd(name) + char *name; +{ + char *p, *q; + struct cmd *c, *found; + int nmatches, longest; + + longest = 0; + nmatches = 0; + found = 0; + for (c = cmdtab; p = c->c_name; c++) { + for (q = name; *q == *p++; q++) + if (*q == 0) /* exact match? */ + return (c); + if (!*q) { /* the name was a prefix */ + if (q - name > longest) { + longest = q - name; + nmatches = 1; + found = c; + } else if (q - name == longest) + nmatches++; + } + } + if (nmatches > 1) + return ((struct cmd *)-1); + return (found); +} + +/* + * Slice a string up into argc/argv. + */ + +int slrflag; + +void +makeargv() +{ + char **argp; + + margc = 0; + argp = margv; + stringbase = line; /* scan from first of buffer */ + argbase = argbuf; /* store from first of buffer */ + slrflag = 0; + while (*argp++ = slurpstring()) + margc++; +} + +/* + * Parse string into argbuf; + * implemented with FSM to + * handle quoting and strings + */ +char * +slurpstring() +{ + int got_one = 0; + char *sb = stringbase; + char *ap = argbase; + char *tmp = argbase; /* will return this if token found */ + + if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ + switch (slrflag) { /* and $ as token for macro invoke */ + case 0: + slrflag++; + stringbase++; + return ((*sb == '!') ? "!" : "$"); + /* NOTREACHED */ + case 1: + slrflag++; + altarg = stringbase; + break; + default: + break; + } + } + +S0: + switch (*sb) { + + case '\0': + goto OUT; + + case ' ': + case '\t': + sb++; goto S0; + + default: + switch (slrflag) { + case 0: + slrflag++; + break; + case 1: + slrflag++; + altarg = sb; + break; + default: + break; + } + goto S1; + } + +S1: + switch (*sb) { + + case ' ': + case '\t': + case '\0': + goto OUT; /* end of token */ + + case '\\': + sb++; goto S2; /* slurp next character */ + + case '"': + sb++; goto S3; /* slurp quoted string */ + + default: + *ap++ = *sb++; /* add character to token */ + got_one = 1; + goto S1; + } + +S2: + switch (*sb) { + + case '\0': + goto OUT; + + default: + *ap++ = *sb++; + got_one = 1; + goto S1; + } + +S3: + switch (*sb) { + + case '\0': + goto OUT; + + case '"': + sb++; goto S1; + + default: + *ap++ = *sb++; + got_one = 1; + goto S3; + } + +OUT: + if (got_one) + *ap++ = '\0'; + argbase = ap; /* update storage pointer */ + stringbase = sb; /* update scan pointer */ + if (got_one) { + return (tmp); + } + switch (slrflag) { + case 0: + slrflag++; + break; + case 1: + slrflag++; + altarg = (char *) 0; + break; + default: + break; + } + return ((char *)0); +} + +#define HELPINDENT ((int) sizeof ("directory")) + +/* + * Help command. + * Call each command handler with argc == 0 and argv[0] == name. + */ +void +help(argc, argv) + int argc; + char *argv[]; +{ + struct cmd *c; + + if (argc == 1) { + int i, j, w, k; + int columns, width = 0, lines; + + printf("Commands may be abbreviated. Commands are:\n\n"); + for (c = cmdtab; c < &cmdtab[NCMDS]; c++) { + int len = strlen(c->c_name); + + if (len > width) + width = len; + } + width = (width + 8) &~ 7; + columns = 80 / width; + if (columns == 0) + columns = 1; + lines = (NCMDS + columns - 1) / columns; + for (i = 0; i < lines; i++) { + for (j = 0; j < columns; j++) { + c = cmdtab + j * lines + i; + if (c->c_name && (!proxy || c->c_proxy)) { + printf("%s", c->c_name); + } + else if (c->c_name) { + for (k=0; k < strlen(c->c_name); k++) { + (void) putchar(' '); + } + } + if (c + lines >= &cmdtab[NCMDS]) { + printf("\n"); + break; + } + w = strlen(c->c_name); + while (w < width) { + w = (w + 8) &~ 7; + (void) putchar('\t'); + } + } + } + return; + } + while (--argc > 0) { + char *arg; + arg = *++argv; + c = getcmd(arg); + if (c == (struct cmd *)-1) + printf("?Ambiguous help command %s\n", arg); + else if (c == (struct cmd *)0) + printf("?Invalid help command %s\n", arg); + else + printf("%-*s\t%s\n", HELPINDENT, + c->c_name, c->c_help); + } +} diff --git a/usr.bin/ftp/ruserpass.c b/usr.bin/ftp/ruserpass.c new file mode 100644 index 00000000000..0a13fcd7a33 --- /dev/null +++ b/usr.bin/ftp/ruserpass.c @@ -0,0 +1,281 @@ +/* + * Copyright (c) 1985, 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[] = "@(#)ruserpass.c 8.4 (Berkeley) 4/27/95"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "ftp_var.h" + +static int token __P((void)); +static FILE *cfile; + +#define DEFAULT 1 +#define LOGIN 2 +#define PASSWD 3 +#define ACCOUNT 4 +#define MACDEF 5 +#define ID 10 +#define MACH 11 + +static char tokval[100]; + +static struct toktab { + char *tokstr; + int tval; +} toktab[]= { + { "default", DEFAULT }, + { "login", LOGIN }, + { "password", PASSWD }, + { "passwd", PASSWD }, + { "account", ACCOUNT }, + { "machine", MACH }, + { "macdef", MACDEF }, + { NULL, 0 } +}; + +int +ruserpass(host, aname, apass, aacct) + char *host, **aname, **apass, **aacct; +{ + char *hdir, buf[BUFSIZ], *tmp; + char myname[MAXHOSTNAMELEN], *mydomain; + int t, i, c, usedefault = 0; + struct stat stb; + + hdir = getenv("HOME"); + if (hdir == NULL) + hdir = "."; + (void) sprintf(buf, "%s/.netrc", hdir); + cfile = fopen(buf, "r"); + if (cfile == NULL) { + if (errno != ENOENT) + warn("%s", buf); + return (0); + } + if (gethostname(myname, sizeof(myname)) < 0) + myname[0] = '\0'; + if ((mydomain = strchr(myname, '.')) == NULL) + mydomain = ""; +next: + while ((t = token())) switch(t) { + + case DEFAULT: + usedefault = 1; + /* FALL THROUGH */ + + case MACH: + if (!usedefault) { + if (token() != ID) + continue; + /* + * Allow match either for user's input host name + * or official hostname. Also allow match of + * incompletely-specified host in local domain. + */ + if (strcasecmp(host, tokval) == 0) + goto match; + if (strcasecmp(hostname, tokval) == 0) + goto match; + if ((tmp = strchr(hostname, '.')) != NULL && + strcasecmp(tmp, mydomain) == 0 && + strncasecmp(hostname, tokval, tmp-hostname) == 0 && + tokval[tmp - hostname] == '\0') + goto match; + if ((tmp = strchr(host, '.')) != NULL && + strcasecmp(tmp, mydomain) == 0 && + strncasecmp(host, tokval, tmp - host) == 0 && + tokval[tmp - host] == '\0') + goto match; + continue; + } + match: + while ((t = token()) && t != MACH && t != DEFAULT) switch(t) { + + case LOGIN: + if (token()) + if (*aname == 0) { + *aname = malloc((unsigned) strlen(tokval) + 1); + (void) strcpy(*aname, tokval); + } else { + if (strcmp(*aname, tokval)) + goto next; + } + break; + case PASSWD: + if ((*aname == NULL || strcmp(*aname, "anonymous")) && + fstat(fileno(cfile), &stb) >= 0 && + (stb.st_mode & 077) != 0) { + warnx("Error: .netrc file is readable by others."); + warnx("Remove password or make file unreadable by others."); + goto bad; + } + if (token() && *apass == 0) { + *apass = malloc((unsigned) strlen(tokval) + 1); + (void) strcpy(*apass, tokval); + } + break; + case ACCOUNT: + if (fstat(fileno(cfile), &stb) >= 0 + && (stb.st_mode & 077) != 0) { + warnx("Error: .netrc file is readable by others."); + warnx("Remove account or make file unreadable by others."); + goto bad; + } + if (token() && *aacct == 0) { + *aacct = malloc((unsigned) strlen(tokval) + 1); + (void) strcpy(*aacct, tokval); + } + break; + case MACDEF: + if (proxy) { + (void) fclose(cfile); + return (0); + } + while ((c=getc(cfile)) != EOF && c == ' ' || c == '\t'); + if (c == EOF || c == '\n') { + printf("Missing macdef name argument.\n"); + goto bad; + } + if (macnum == 16) { + printf("Limit of 16 macros have already been defined\n"); + goto bad; + } + tmp = macros[macnum].mac_name; + *tmp++ = c; + for (i=0; i < 8 && (c=getc(cfile)) != EOF && + !isspace(c); ++i) { + *tmp++ = c; + } + if (c == EOF) { + printf("Macro definition missing null line terminator.\n"); + goto bad; + } + *tmp = '\0'; + if (c != '\n') { + while ((c=getc(cfile)) != EOF && c != '\n'); + } + if (c == EOF) { + printf("Macro definition missing null line terminator.\n"); + goto bad; + } + if (macnum == 0) { + macros[macnum].mac_start = macbuf; + } + else { + macros[macnum].mac_start = macros[macnum-1].mac_end + 1; + } + tmp = macros[macnum].mac_start; + while (tmp != macbuf + 4096) { + if ((c=getc(cfile)) == EOF) { + printf("Macro definition missing null line terminator.\n"); + goto bad; + } + *tmp = c; + if (*tmp == '\n') { + if (*(tmp-1) == '\0') { + macros[macnum++].mac_end = tmp - 1; + break; + } + *tmp = '\0'; + } + tmp++; + } + if (tmp == macbuf + 4096) { + printf("4K macro buffer exceeded\n"); + goto bad; + } + break; + default: + warnx("Unknown .netrc keyword %s", tokval); + break; + } + goto done; + } +done: + (void) fclose(cfile); + return (0); +bad: + (void) fclose(cfile); + return (-1); +} + +static int +token() +{ + char *cp; + int c; + struct toktab *t; + + if (feof(cfile) || ferror(cfile)) + return (0); + while ((c = getc(cfile)) != EOF && + (c == '\n' || c == '\t' || c == ' ' || c == ',')) + continue; + if (c == EOF) + return (0); + cp = tokval; + if (c == '"') { + while ((c = getc(cfile)) != EOF && c != '"') { + if (c == '\\') + c = getc(cfile); + *cp++ = c; + } + } else { + *cp++ = c; + while ((c = getc(cfile)) != EOF + && c != '\n' && c != '\t' && c != ' ' && c != ',') { + if (c == '\\') + c = getc(cfile); + *cp++ = c; + } + } + *cp = 0; + if (tokval[0] == 0) + return (0); + for (t = toktab; t->tokstr; t++) + if (!strcmp(t->tokstr, tokval)) + return (t->tval); + return (ID); +} diff --git a/usr.bin/id/id.1 b/usr.bin/id/id.1 new file mode 100644 index 00000000000..3bc454e00f5 --- /dev/null +++ b/usr.bin/id/id.1 @@ -0,0 +1,139 @@ +.\" Copyright (c) 1991, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" 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. +.\" +.\" @(#)id.1 8.2 (Berkeley) 5/5/94 +.\" +.Dd "May 5, 1994" +.Dt ID 1 +.Os BSD 4.4 +.Sh NAME +.Nm id +.Nd return user identity +.Sh SYNOPSIS +.Nm id +.Op Ar user +.Nm id +.Fl G Op Fl n +.Op Ar user +.Nm id +.Fl g Op Fl nr +.Op Ar user +.Nm id +.Fl p +.Nm id +.Fl u Op Fl nr +.Op Ar user +.Sh DESCRIPTION +The +.Nm id +utility displays the user and group names and numeric IDs, of the +calling process, to the standard output. +If the real and effective IDs are different, both are displayed, +otherwise only the real ID is displayed. +.Pp +If a +.Ar user +(login name or user ID) +is specified, the user and group IDs of that user are displayed. +In this case, the real and effective IDs are assumed to be the same. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl G +Display the different group IDs (effective, real and supplementary) +as white-space separated numbers, in no particular order. +.It Fl g +Display the effective group ID as a number. +.It Fl n +Display the name of the user or group ID for the +.Fl G , +.Fl g +and +.Fl u +options instead of the number. +If any of the ID numbers cannot be mapped into names, the number will be +displayed as usual. +.It Fl p +Make the output human-readable. +If the user name returned by +.Xr getlogin 2 +is different from the login name referenced by the user ID, the name +returned by +.Xr getlogin 2 +is displayed, preceded by the keyword ``login''. +The user ID as a name is displayed, preceded by the keyword ``uid''. +If the effective user ID is different from the real user ID, the real user +ID is displayed as a name, preceded by the keyword ``euid''. +If the effective group ID is different from the real group ID, the real group +ID is displayed as a name, preceded by the keyword ``rgid''. +The list of groups to which the user belongs is then displayed as names, +preceded by the keyword ``groups''. +Each display is on a separate line. +.It Fl r +Display the real ID for the +.Fl g +and +.Fl u +options instead of the effective ID. +.It Fl u +Display the effective user ID as a number. +.El +.Pp +The +.Nm id +utility exits 0 on success, and >0 if an error occurs. +.Sh SEE ALSO +.Xr who 1 +.Sh STANDARDS +The +.Nm id +function is expected to conform to +.St -p1003.2 . +.Sh HISTORY +The +historic +.Xr groups 1 +command is equivalent to +.Dq Nm id Fl Gn Op Ar user . +.Pp +The +historic +.Xr whoami 1 +command is equivalent to +.Dq Nm id Fl un . +.Pp +The +.Nm id +command first appeared in +.Bx 4.4 . diff --git a/usr.bin/id/id.c b/usr.bin/id/id.c new file mode 100644 index 00000000000..bf9218e6560 --- /dev/null +++ b/usr.bin/id/id.c @@ -0,0 +1,351 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char 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[] = "@(#)id.c 8.3 (Berkeley) 4/28/95"; +#endif /* not lint */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +void current __P((void)); +void err __P((const char *, ...)); +void pretty __P((struct passwd *)); +void group __P((struct passwd *, int)); +void usage __P((void)); +void user __P((struct passwd *)); +struct passwd * + who __P((char *)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct group *gr; + struct passwd *pw; + int Gflag, ch, gflag, id, nflag, pflag, rflag, uflag; + + Gflag = gflag = nflag = pflag = rflag = uflag = 0; + while ((ch = getopt(argc, argv, "Ggnpru")) != EOF) + switch(ch) { + case 'G': + Gflag = 1; + break; + case 'g': + gflag = 1; + break; + case 'n': + nflag = 1; + break; + case 'p': + pflag = 1; + break; + case 'r': + rflag = 1; + break; + case 'u': + uflag = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + switch(Gflag + gflag + pflag + uflag) { + case 1: + break; + case 0: + if (!nflag && !rflag) + break; + /* FALLTHROUGH */ + default: + usage(); + } + + pw = *argv ? who(*argv) : NULL; + + if (gflag) { + id = pw ? pw->pw_gid : rflag ? getgid() : getegid(); + if (nflag && (gr = getgrgid(id))) + (void)printf("%s\n", gr->gr_name); + else + (void)printf("%u\n", id); + exit(0); + } + + if (uflag) { + id = pw ? pw->pw_uid : rflag ? getuid() : geteuid(); + if (nflag && (pw = getpwuid(id))) + (void)printf("%s\n", pw->pw_name); + else + (void)printf("%u\n", id); + exit(0); + } + + if (Gflag) { + group(pw, nflag); + exit(0); + } + + if (pflag) { + pretty(pw); + exit(0); + } + + if (pw) + user(pw); + else + current(); + exit(0); +} + +void +pretty(pw) + struct passwd *pw; +{ + struct group *gr; + u_int eid, rid; + char *login; + + if (pw) { + (void)printf("uid\t%s\n", pw->pw_name); + (void)printf("groups\t"); + group(pw, 1); + } else { + if ((login = getlogin()) == NULL) + err("getlogin: %s", strerror(errno)); + + pw = getpwuid(rid = getuid()); + if (pw == NULL || strcmp(login, pw->pw_name)) + (void)printf("login\t%s\n", login); + if (pw) + (void)printf("uid\t%s\n", pw->pw_name); + else + (void)printf("uid\t%u\n", rid); + + if ((eid = geteuid()) != rid) + if (pw = getpwuid(eid)) + (void)printf("euid\t%s", pw->pw_name); + else + (void)printf("euid\t%u", eid); + if ((rid = getgid()) != (eid = getegid())) + if (gr = getgrgid(rid)) + (void)printf("rgid\t%s\n", gr->gr_name); + else + (void)printf("rgid\t%u\n", rid); + (void)printf("groups\t"); + group(NULL, 1); + } +} + +void +current() +{ + struct group *gr; + struct passwd *pw; + int cnt, id, eid, lastid, ngroups; + gid_t groups[NGROUPS]; + char *fmt; + + id = getuid(); + (void)printf("uid=%u", id); + if (pw = getpwuid(id)) + (void)printf("(%s)", pw->pw_name); + if ((eid = geteuid()) != id) { + (void)printf(" euid=%u", eid); + if (pw = getpwuid(eid)) + (void)printf("(%s)", pw->pw_name); + } + id = getgid(); + (void)printf(" gid=%u", id); + if (gr = getgrgid(id)) + (void)printf("(%s)", gr->gr_name); + if ((eid = getegid()) != id) { + (void)printf(" egid=%u", eid); + if (gr = getgrgid(eid)) + (void)printf("(%s)", gr->gr_name); + } + if (ngroups = getgroups(NGROUPS, groups)) { + for (fmt = " groups=%u", lastid = -1, cnt = 0; cnt < ngroups; + fmt = ", %u", lastid = id) { + id = groups[cnt++]; + if (lastid == id) + continue; + (void)printf(fmt, id); + if (gr = getgrgid(id)) + (void)printf("(%s)", gr->gr_name); + } + } + (void)printf("\n"); +} + +void +user(pw) + register struct passwd *pw; +{ + register struct group *gr; + register char *fmt, **p; + int cnt, id, lastid, ngroups, groups[NGROUPS + 1]; + + id = pw->pw_uid; + (void)printf("uid=%u(%s)", id, pw->pw_name); + (void)printf(" gid=%u", pw->pw_gid); + if (gr = getgrgid(pw->pw_gid)) + (void)printf("(%s)", gr->gr_name); + ngroups = NGROUPS + 1; + (void) getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups); + fmt = " groups=%u"; + for (lastid = -1, cnt = 0; cnt < ngroups; ++cnt) { + if (lastid == (id = groups[cnt])) + continue; + (void)printf(fmt, id); + fmt = " %u"; + if (gr = getgrgid(id)) + (void)printf("(%s)", gr->gr_name); + lastid = id; + } + (void)printf("\n"); +} + +void +group(pw, nflag) + struct passwd *pw; + int nflag; +{ + struct group *gr; + int cnt, id, lastid, ngroups; + gid_t groups[NGROUPS + 1]; + char *fmt; + + if (pw) { + ngroups = NGROUPS + 1; + (void) getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups); + } else { + groups[0] = getgid(); + ngroups = getgroups(NGROUPS, groups + 1) + 1; + } + fmt = nflag ? "%s" : "%u"; + for (lastid = -1, cnt = 0; cnt < ngroups; ++cnt) { + if (lastid == (id = groups[cnt])) + continue; + if (nflag) { + if (gr = getgrgid(id)) + (void)printf(fmt, gr->gr_name); + else + (void)printf(*fmt == ' ' ? " %u" : "%u", + id); + fmt = " %s"; + } else { + (void)printf(fmt, id); + fmt = " %u"; + } + lastid = id; + } + (void)printf("\n"); +} + +struct passwd * +who(u) + char *u; +{ + struct passwd *pw; + long id; + char *ep; + + /* + * Translate user argument into a pw pointer. First, try to + * get it as specified. If that fails, try it as a number. + */ + if (pw = getpwnam(u)) + return(pw); + id = strtol(u, &ep, 10); + if (*u && !*ep && (pw = getpwuid(id))) + return(pw); + err("%s: No such user", u); + /* NOTREACHED */ +} + +#if __STDC__ +#include +#else +#include +#endif + +void +#if __STDC__ +err(const char *fmt, ...) +#else +err(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)fprintf(stderr, "id: "); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); + exit(1); + /* NOTREACHED */ +} + +void +usage() +{ + (void)fprintf(stderr, "usage: id [user]\n"); + (void)fprintf(stderr, " id -G [-n] [user]\n"); + (void)fprintf(stderr, " id -g [-nr] [user]\n"); + (void)fprintf(stderr, " id -u [-nr] [user]\n"); + exit(1); +} diff --git a/usr.bin/kdump/kdump.c b/usr.bin/kdump/kdump.c new file mode 100644 index 00000000000..5a1f27966e5 --- /dev/null +++ b/usr.bin/kdump/kdump.c @@ -0,0 +1,418 @@ +/*- + * 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[] = "@(#)kdump.c 8.4 (Berkeley) 4/28/95"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#define KERNEL +#include +#undef KERNEL + +#include +#include +#include +#include +#include +#include +#include + +#include "ktrace.h" + +int timestamp, decimal, fancy = 1, tail, maxdata; +char *tracefile = DEF_TRACEFILE; +struct ktr_header ktr_header; + +#define eqs(s1, s2) (strcmp((s1), (s2)) == 0) + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch, ktrlen, size; + register void *m; + int trpoints = ALL_POINTS; + + while ((ch = getopt(argc, argv, "f:dlm:nRTt:")) != -1) + switch (ch) { + case 'f': + tracefile = optarg; + break; + case 'd': + decimal = 1; + break; + case 'l': + tail = 1; + break; + case 'm': + maxdata = atoi(optarg); + break; + case 'n': + fancy = 0; + break; + case 'R': + timestamp = 2; /* relative timestamp */ + break; + case 'T': + timestamp = 1; + break; + case 't': + trpoints = getpoints(optarg); + if (trpoints < 0) + errx(1, "unknown trace point in %s", optarg); + break; + default: + usage(); + } + argv += optind; + argc -= optind; + + if (argc > 1) + usage(); + + m = (void *)malloc(size = 1025); + if (m == NULL) + errx(1, "%s", strerror(ENOMEM)); + if (!freopen(tracefile, "r", stdin)) + err(1, "%s", tracefile); + while (fread_tail(&ktr_header, sizeof(struct ktr_header), 1)) { + if (trpoints & (1< size) { + m = (void *)realloc(m, ktrlen+1); + if (m == NULL) + errx(1, "%s", strerror(ENOMEM)); + size = ktrlen; + } + if (ktrlen && fread_tail(m, ktrlen, 1) == 0) + errx(1, "data too short"); + if ((trpoints & (1<ktr_type) { + case KTR_SYSCALL: + type = "CALL"; + break; + case KTR_SYSRET: + type = "RET "; + break; + case KTR_NAMEI: + type = "NAMI"; + break; + case KTR_GENIO: + type = "GIO "; + break; + case KTR_PSIG: + type = "PSIG"; + break; + case KTR_CSW: + type = "CSW"; + break; + default: + (void)sprintf(unknown, "UNKNOWN(%d)", kth->ktr_type); + type = unknown; + } + + (void)printf("%6d %-8s ", kth->ktr_pid, kth->ktr_comm); + if (timestamp) { + if (timestamp == 2) { + temp = kth->ktr_time; + timevalsub(&kth->ktr_time, &prevtime); + prevtime = temp; + } + (void)printf("%ld.%06ld ", + kth->ktr_time.tv_sec, kth->ktr_time.tv_usec); + } + (void)printf("%s ", type); +} + +#include +#define KTRACE +#include "/sys/kern/syscalls.c" +#undef KTRACE +int nsyscalls = sizeof (syscallnames) / sizeof (syscallnames[0]); + +static char *ptrace_ops[] = { + "PT_TRACE_ME", "PT_READ_I", "PT_READ_D", "PT_READ_U", + "PT_WRITE_I", "PT_WRITE_D", "PT_WRITE_U", "PT_CONTINUE", + "PT_KILL", "PT_STEP", +}; + +ktrsyscall(ktr) + register struct ktr_syscall *ktr; +{ + register argsize = ktr->ktr_argsize; + register register_t *ap; + char *ioctlname(); + + if (ktr->ktr_code >= nsyscalls || ktr->ktr_code < 0) + (void)printf("[%d]", ktr->ktr_code); + else + (void)printf("%s", syscallnames[ktr->ktr_code]); + ap = (register_t *)((char *)ktr + sizeof(struct ktr_syscall)); + if (argsize) { + char c = '('; + if (fancy) { + if (ktr->ktr_code == SYS_ioctl) { + char *cp; + if (decimal) + (void)printf("(%ld", (long)*ap); + else + (void)printf("(%#lx", (long)*ap); + ap++; + argsize -= sizeof(register_t); + if ((cp = ioctlname(*ap)) != NULL) + (void)printf(",%s", cp); + else { + if (decimal) + (void)printf(",%ld", + (long)*ap); + else + (void)printf(",%#lx ", + (long)*ap); + } + c = ','; + ap++; + argsize -= sizeof(register_t); + } else if (ktr->ktr_code == SYS_ptrace) { + if (*ap >= 0 && *ap <= + sizeof(ptrace_ops) / sizeof(ptrace_ops[0])) + (void)printf("(%s", ptrace_ops[*ap]); + else + (void)printf("(%ld", (long)*ap); + c = ','; + ap++; + argsize -= sizeof(register_t); + } + } + while (argsize) { + if (decimal) + (void)printf("%c%ld", c, (long)*ap); + else + (void)printf("%c%#lx", c, (long)*ap); + c = ','; + ap++; + argsize -= sizeof(register_t); + } + (void)putchar(')'); + } + (void)putchar('\n'); +} + +ktrsysret(ktr) + struct ktr_sysret *ktr; +{ + register int ret = ktr->ktr_retval; + register int error = ktr->ktr_error; + register int code = ktr->ktr_code; + + if (code >= nsyscalls || code < 0) + (void)printf("[%d] ", code); + else + (void)printf("%s ", syscallnames[code]); + + if (error == 0) { + if (fancy) { + (void)printf("%d", ret); + if (ret < 0 || ret > 9) + (void)printf("/%#x", ret); + } else { + if (decimal) + (void)printf("%d", ret); + else + (void)printf("%#x", ret); + } + } else if (error == ERESTART) + (void)printf("RESTART"); + else if (error == EJUSTRETURN) + (void)printf("JUSTRETURN"); + else { + (void)printf("-1 errno %d", ktr->ktr_error); + if (fancy) + (void)printf(" %s", strerror(ktr->ktr_error)); + } + (void)putchar('\n'); +} + +ktrnamei(cp, len) + char *cp; +{ + (void)printf("\"%.*s\"\n", len, cp); +} + +ktrgenio(ktr, len) + struct ktr_genio *ktr; +{ + register int datalen = len - sizeof (struct ktr_genio); + register char *dp = (char *)ktr + sizeof (struct ktr_genio); + register char *cp; + register int col = 0; + register width; + char visbuf[5]; + static screenwidth = 0; + + if (screenwidth == 0) { + struct winsize ws; + + if (fancy && ioctl(fileno(stderr), TIOCGWINSZ, &ws) != -1 && + ws.ws_col > 8) + screenwidth = ws.ws_col; + else + screenwidth = 80; + } + printf("fd %d %s %d bytes\n", ktr->ktr_fd, + ktr->ktr_rw == UIO_READ ? "read" : "wrote", datalen); + if (maxdata && datalen > maxdata) + datalen = maxdata; + (void)printf(" \""); + col = 8; + for (; datalen > 0; datalen--, dp++) { + (void) vis(visbuf, *dp, VIS_CSTYLE, *(dp+1)); + cp = visbuf; + /* + * Keep track of printables and + * space chars (like fold(1)). + */ + if (col == 0) { + (void)putchar('\t'); + col = 8; + } + switch(*cp) { + case '\n': + col = 0; + (void)putchar('\n'); + continue; + case '\t': + width = 8 - (col&07); + break; + default: + width = strlen(cp); + } + if (col + width > (screenwidth-2)) { + (void)printf("\\\n\t"); + col = 8; + } + col += width; + do { + (void)putchar(*cp++); + } while (*cp); + } + if (col == 0) + (void)printf(" "); + (void)printf("\"\n"); +} + +ktrpsig(psig) + struct ktr_psig *psig; +{ + (void)printf("SIG%s ", sys_signame[psig->signo]); + if (psig->action == SIG_DFL) + (void)printf("SIG_DFL\n"); + else + (void)printf("caught handler=0x%lx mask=0x%x code=0x%x\n", + (u_long)psig->action, psig->mask, psig->code); +} + +ktrcsw(cs) + struct ktr_csw *cs; +{ + (void)printf("%s %s\n", cs->out ? "stop" : "resume", + cs->user ? "user" : "kernel"); +} + +usage() +{ + (void)fprintf(stderr, + "usage: kdump [-dnlRT] [-f trfile] [-m maxdata] [-t [cnis]]\n"); + exit(1); +} diff --git a/usr.bin/kdump/mkioctls b/usr.bin/kdump/mkioctls new file mode 100644 index 00000000000..b165d05c812 --- /dev/null +++ b/usr.bin/kdump/mkioctls @@ -0,0 +1,68 @@ +#!/bin/sh - +# +# Copyright (c) 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. +# +# @(#)mkioctls 8.2 (Berkeley) 4/28/95 +# +awk ' +BEGIN { + print "#include " + print "#include " + print "#include " + print "#include " + print "#include " + print "#include " + print "#define COMPAT_43" + print "#include " + print "" + print "char *" + print "ioctlname(val)" + print "{" + print "" +} + +/^#[ ]*define[ ]*(TIO|FIO|SIO|OSIO)[A-Z]*[ ]*_IO/ { + + # find where the name starts + for (i = 1; i <= NF; i++) + if ($i ~ /define/) + break; + ++i; + # + printf("\tif (val == %s)\n\t\treturn(\"%s\");\n", $i, $i); + +} +END { + print "\n\treturn(NULL);" + print "}" +} +' /usr/include/sys/ioctl.h /usr/include/sys/ioctl_compat.h diff --git a/usr.bin/ktrace/ktrace.c b/usr.bin/ktrace/ktrace.c new file mode 100644 index 00000000000..f40dbafb420 --- /dev/null +++ b/usr.bin/ktrace/ktrace.c @@ -0,0 +1,182 @@ +/*- + * 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[] = "@(#)ktrace.c 8.2 (Berkeley) 4/28/95"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "ktrace.h" + +void no_ktrace __P((int)); +void usage __P((void)); + +main(argc, argv) + int argc; + char **argv; +{ + enum { NOTSET, CLEAR, CLEARALL } clear; + int append, ch, fd, inherit, ops, pid, pidset, trpoints; + char *tracefile; + + clear = NOTSET; + append = ops = pidset = inherit = 0; + trpoints = DEF_POINTS; + tracefile = DEF_TRACEFILE; + while ((ch = getopt(argc,argv,"aCcdf:g:ip:t:")) != EOF) + switch((char)ch) { + case 'a': + append = 1; + break; + case 'C': + clear = CLEARALL; + pidset = 1; + break; + case 'c': + clear = CLEAR; + break; + case 'd': + ops |= KTRFLAG_DESCEND; + break; + case 'f': + tracefile = optarg; + break; + case 'g': + pid = -rpid(optarg); + pidset = 1; + break; + case 'i': + inherit = 1; + break; + case 'p': + pid = rpid(optarg); + pidset = 1; + break; + case 't': + trpoints = getpoints(optarg); + if (trpoints < 0) { + warnx("unknown facility in %s", optarg); + usage(); + } + break; + default: + usage(); + } + argv += optind; + argc -= optind; + + if (pidset && *argv || !pidset && !*argv) + usage(); + + if (inherit) + trpoints |= KTRFAC_INHERIT; + + (void)signal(SIGSYS, no_ktrace); + if (clear != NOTSET) { + if (clear == CLEARALL) { + ops = KTROP_CLEAR | KTRFLAG_DESCEND; + trpoints = ALL_POINTS; + pid = 1; + } else + ops |= pid ? KTROP_CLEAR : KTROP_CLEARFILE; + + if (ktrace(tracefile, ops, trpoints, pid) < 0) + err(1, tracefile); + exit(0); + } + + if ((fd = open(tracefile, O_CREAT | O_WRONLY | (append ? 0 : O_TRUNC), + DEFFILEMODE)) < 0) + err(1, tracefile); + (void)close(fd); + + if (*argv) { + if (ktrace(tracefile, ops, trpoints, getpid()) < 0) + err(1, tracefile); + execvp(argv[0], &argv[0]); + err(1, "exec of '%s' failed", argv[0]); + } + else if (ktrace(tracefile, ops, trpoints, pid) < 0) + err(1, tracefile); + exit(0); +} + +rpid(p) + char *p; +{ + static int first; + + if (first++) { + warnx("only one -g or -p flag is permitted."); + usage(); + } + if (!*p) { + warnx("illegal process id."); + usage(); + } + return(atoi(p)); +} + +void +usage() +{ + (void)fprintf(stderr, +"usage:\tktrace [-aCcid] [-f trfile] [-g pgid] [-p pid] [-t [acgn]\n\tktrace [-aCcid] [-f trfile] [-t [acgn] command\n"); + exit(1); +} + +void +no_ktrace(sig) + int sig; +{ + (void)fprintf(stderr, +"error:\tktrace() system call not supported in the running kernel\n\tre-compile kernel with 'options KTRACE'\n"); + exit(1); +} diff --git a/usr.bin/ktrace/subr.c b/usr.bin/ktrace/subr.c new file mode 100644 index 00000000000..015801d210c --- /dev/null +++ b/usr.bin/ktrace/subr.c @@ -0,0 +1,109 @@ +/*- + * 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 sccsid[] = "@(#)subr.c 8.2 (Berkeley) 4/28/95"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "ktrace.h" + +getpoints(s) + char *s; +{ + int facs = 0; + + while (*s) { + switch(*s) { + case 'c': + facs |= KTRFAC_SYSCALL | KTRFAC_SYSRET; + break; + case 'n': + facs |= KTRFAC_NAMEI; + break; + case 'i': + facs |= KTRFAC_GENIO; + break; + case 's': + facs |= KTRFAC_PSIG; + break; + case 'w': + facs |= KTRFAC_CSW; + break; + case '+': + facs |= DEF_POINTS; + break; + default: + return (-1); + } + s++; + } + return (facs); +} + +timevaladd(t1, t2) + struct timeval *t1, *t2; +{ + t1->tv_sec += t2->tv_sec; + t1->tv_usec += t2->tv_usec; + timevalfix(t1); +} + +timevalsub(t1, t2) + struct timeval *t1, *t2; +{ + t1->tv_sec -= t2->tv_sec; + t1->tv_usec -= t2->tv_usec; + timevalfix(t1); +} + +timevalfix(t1) + struct timeval *t1; +{ + if (t1->tv_usec < 0) { + t1->tv_sec--; + t1->tv_usec += 1000000; + } + if (t1->tv_usec >= 1000000) { + t1->tv_sec++; + t1->tv_usec -= 1000000; + } +} diff --git a/usr.bin/lastcomm/lastcomm.c b/usr.bin/lastcomm/lastcomm.c new file mode 100644 index 00000000000..749d8cf1cd7 --- /dev/null +++ b/usr.bin/lastcomm/lastcomm.c @@ -0,0 +1,220 @@ +/* + * 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1980, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)lastcomm.c 8.2 (Berkeley) 4/29/95"; +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pathnames.h" + +time_t expand __P((u_int)); +char *flagbits __P((int)); +char *getdev __P((dev_t)); +int requested __P((char *[], struct acct *)); +void usage __P((void)); +char *user_from_uid(); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + register char *p; + struct acct ab; + struct stat sb; + FILE *fp; + off_t size; + time_t t; + int ch; + char *acctfile; + + acctfile = _PATH_ACCT; + while ((ch = getopt(argc, argv, "f:")) != EOF) + switch((char)ch) { + case 'f': + acctfile = optarg; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + /* Open the file. */ + if ((fp = fopen(acctfile, "r")) == NULL || fstat(fileno(fp), &sb)) + err(1, "%s", acctfile); + + /* + * Round off to integral number of accounting records, probably + * not necessary, but it doesn't hurt. + */ + size = sb.st_size - sb.st_size % sizeof(struct acct); + + /* Check if any records to display. */ + if (size < sizeof(struct acct)) + exit(0); + + /* + * Seek to before the last entry in the file; use lseek(2) in case + * the file is bigger than a "long". + */ + size -= sizeof(struct acct); + if (lseek(fileno(fp), size, SEEK_SET) == -1) + err(1, "%s", acctfile); + + for (;;) { + if (fread(&ab, sizeof(struct acct), 1, fp) != 1) + err(1, "%s", acctfile); + + if (fseek(fp, 2 * -(long)sizeof(struct acct), SEEK_CUR) == -1) + err(1, "%s", acctfile); + + if (size == 0) + break; + size -= sizeof(struct acct); + + if (ab.ac_comm[0] == '\0') { + ab.ac_comm[0] = '?'; + ab.ac_comm[1] = '\0'; + } else + for (p = &ab.ac_comm[0]; + p < &ab.ac_comm[fldsiz(acct, ac_comm)] && *p; ++p) + if (!isprint(*p)) + *p = '?'; + if (*argv && !requested(argv, &ab)) + continue; + + t = expand(ab.ac_utime) + expand(ab.ac_stime); + (void)printf("%-*s %-7s %-*s %-*s %6.2f secs %.16s\n", + fldsiz(acct, ac_comm), ab.ac_comm, flagbits(ab.ac_flag), + UT_NAMESIZE, user_from_uid(ab.ac_uid, 0), + UT_LINESIZE, getdev(ab.ac_tty), + t / (double)AHZ, ctime(&ab.ac_btime)); + } + exit(0); +} + +time_t +expand(t) + u_int t; +{ + register time_t nt; + + nt = t & 017777; + t >>= 13; + while (t) { + t--; + nt <<= 3; + } + return (nt); +} + +char * +flagbits(f) + register int f; +{ + static char flags[20] = "-"; + char *p; + +#define BIT(flag, ch) if (f & flag) *p++ = ch + + p = flags + 1; + BIT(ASU, 'S'); + BIT(AFORK, 'F'); + BIT(ACOMPAT, 'C'); + BIT(ACORE, 'D'); + BIT(AXSIG, 'X'); + *p = '\0'; + return (flags); +} + +int +requested(argv, acp) + register char *argv[]; + register struct acct *acp; +{ + do { + if (!strcmp(user_from_uid(acp->ac_uid, 0), *argv)) + return (1); + if (!strcmp(getdev(acp->ac_tty), *argv)) + return (1); + if (!strncmp(acp->ac_comm, *argv, fldsiz(acct, ac_comm))) + return (1); + } while (*++argv); + return (0); +} + +char * +getdev(dev) + dev_t dev; +{ + static dev_t lastdev = (dev_t)-1; + static char *lastname; + + if (dev == NODEV) /* Special case. */ + return ("__"); + if (dev == lastdev) /* One-element cache. */ + return (lastname); + lastdev = dev; + if ((lastname = devname(dev, S_IFCHR)) == NULL) + lastname = "??"; + return (lastname); +} + +void +usage() +{ + (void)fprintf(stderr, + "lastcomm [ -f file ] [command ...] [user ...] [tty ...]\n"); + exit(1); +} diff --git a/usr.bin/ld/Makefile b/usr.bin/ld/Makefile new file mode 100644 index 00000000000..834a5c99eeb --- /dev/null +++ b/usr.bin/ld/Makefile @@ -0,0 +1,7 @@ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 + +PROG= ld +SRCS= ld.c cplus-dem.c +NOMAN= noman + +.include diff --git a/usr.bin/ld/cplus-dem.c b/usr.bin/ld/cplus-dem.c new file mode 100644 index 00000000000..b2e30509638 --- /dev/null +++ b/usr.bin/ld/cplus-dem.c @@ -0,0 +1,970 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + */ + +#ifndef lint +static char sccsid[] = "@(#)cplus-dem.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +/* Demangler for GNU C++ + Copyright (C) 1989 Free Software Foundation, Inc. + written by James Clark (jjc@jclark.uucp) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* This is for g++ 1.36.1 (November 6 version). It will probably + require changes for any other version. + + Modified for g++ 1.36.2 (November 18 version). */ + +/* This file exports one function + + char *cplus_demangle (const char *name) + + If `name' is a mangled function name produced by g++, then + a pointer to a malloced string giving a C++ representation + of the name will be returned; otherwise NULL will be returned. + It is the caller's responsibility to free the string which + is returned. + + For example, + + cplus_demangle ("_foo__1Ai") + + returns + + "A::foo(int)" + + This file imports xmalloc and xrealloc, which are like malloc and + realloc except that they generate a fatal error if there is no + available memory. */ + +/* #define nounderscore 1 /* define this is names don't start with _ */ + +#include +#include + +#ifdef USG +#include +#include +#else +#include +#define memcpy(s1, s2, n) bcopy ((s2), (s1), (n)) +#define memcmp(s1, s2, n) bcmp ((s2), (s1), (n)) +#define strchr index +#define strrchr rindex +#endif + +#ifdef __STDC__ +extern char *cplus_demangle (const char *type); +#else +extern char *cplus_demangle (); +#endif + +#ifdef __STDC__ +extern char *xmalloc (int); +extern char *xrealloc (char *, int); +#else +extern char *xmalloc (); +extern char *xrealloc (); +#endif + +static char **typevec = 0; +static int ntypes = 0; +static int typevec_size = 0; + +static struct { + const char *in; + const char *out; +} optable[] = { + "new", " new", + "delete", " delete", + "ne", "!=", + "eq", "==", + "ge", ">=", + "gt", ">", + "le", "<=", + "lt", "<", + "plus", "+", + "minus", "-", + "mult", "*", + "convert", "+", /* unary + */ + "negate", "-", /* unary - */ + "trunc_mod", "%", + "trunc_div", "/", + "truth_andif", "&&", + "truth_orif", "||", + "truth_not", "!", + "postincrement", "++", + "postdecrement", "--", + "bit_ior", "|", + "bit_xor", "^", + "bit_and", "&", + "bit_not", "~", + "call", "()", + "cond", "?:", + "alshift", "<<", + "arshift", ">>", + "component", "->", + "indirect", "*", + "method_call", "->()", + "addr", "&", /* unary & */ + "array", "[]", + "nop", "", /* for operator= */ +}; + +/* Beware: these aren't '\0' terminated. */ + +typedef struct { + char *b; /* pointer to start of string */ + char *p; /* pointer after last character */ + char *e; /* pointer after end of allocated space */ +} string; + +#ifdef __STDC__ +static void string_need (string *s, int n); +static void string_delete (string *s); +static void string_init (string *s); +static void string_clear (string *s); +static int string_empty (string *s); +static void string_append (string *p, const char *s); +static void string_appends (string *p, string *s); +static void string_appendn (string *p, const char *s, int n); +static void string_prepend (string *p, const char *s); +#if 0 +static void string_prepends (string *p, string *s); +#endif +static void string_prependn (string *p, const char *s, int n); +static int get_count (const char **type, int *count); +static int do_args (const char **type, string *decl); +static int do_type (const char **type, string *result); +static int do_arg (const char **type, string *result); +static int do_args (const char **type, string *decl); +static void munge_function_name (string *name); +static void remember_type (const char *type, int len); +#else +static void string_need (); +static void string_delete (); +static void string_init (); +static void string_clear (); +static int string_empty (); +static void string_append (); +static void string_appends (); +static void string_appendn (); +static void string_prepend (); +static void string_prepends (); +static void string_prependn (); +static int get_count (); +static int do_args (); +static int do_type (); +static int do_arg (); +static int do_args (); +static void munge_function_name (); +static void remember_type (); +#endif + +char * +cplus_demangle (type) + const char *type; +{ + string decl; + int n; + int success = 0; + int constructor = 0; + int const_flag = 0; + int i; + const char *p; +#ifndef LONGERNAMES + const char *premangle; +#endif + + if (type == NULL || *type == '\0') + return NULL; +#ifndef nounderscore + if (*type++ != '_') + return NULL; +#endif + p = type; + while (*p != '\0' && !(*p == '_' && p[1] == '_')) + p++; + if (*p == '\0') + { + /* destructor */ + if (type[0] == '_' && type[1] == '$' && type[2] == '_') + { + int n = (strlen (type) - 3)*2 + 3 + 2 + 1; + char *tem = (char *) xmalloc (n); + strcpy (tem, type + 3); + strcat (tem, "::~"); + strcat (tem, type + 3); + strcat (tem, "()"); + return tem; + } + /* static data member */ + if (*type != '_' && (p = strchr (type, '$')) != NULL) + { + int n = strlen (type) + 2; + char *tem = (char *) xmalloc (n); + memcpy (tem, type, p - type); + strcpy (tem + (p - type), "::"); + strcpy (tem + (p - type) + 2, p + 1); + return tem; + } + /* virtual table */ + if (type[0] == '_' && type[1] == 'v' && type[2] == 't' && type[3] == '$') + { + int n = strlen (type + 4) + 14 + 1; + char *tem = (char *) xmalloc (n); + strcpy (tem, type + 4); + strcat (tem, " virtual table"); + return tem; + } + return NULL; + } + + string_init (&decl); + + if (p == type) + { + if (!isdigit (p[2])) + { + string_delete (&decl); + return NULL; + } + constructor = 1; + } + else + { + string_appendn (&decl, type, p - type); + munge_function_name (&decl); + } + p += 2; + +#ifndef LONGERNAMES + premangle = p; +#endif + switch (*p) + { + case 'C': + /* a const member function */ + if (!isdigit (p[1])) + { + string_delete (&decl); + return NULL; + } + p += 1; + const_flag = 1; + /* fall through */ + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + n = 0; + do + { + n *= 10; + n += *p - '0'; + p += 1; + } + while (isdigit (*p)); + if (strlen (p) < n) + { + string_delete (&decl); + return NULL; + } + if (constructor) + { + string_appendn (&decl, p, n); + string_append (&decl, "::"); + string_appendn (&decl, p, n); + } + else + { + string_prepend (&decl, "::"); + string_prependn (&decl, p, n); + } + p += n; +#ifndef LONGERNAMES + remember_type (premangle, p - premangle); +#endif + success = do_args (&p, &decl); + if (const_flag) + string_append (&decl, " const"); + break; + case 'F': + p += 1; + success = do_args (&p, &decl); + break; + } + + for (i = 0; i < ntypes; i++) + if (typevec[i] != NULL) + free (typevec[i]); + ntypes = 0; + if (typevec != NULL) + { + free ((char *)typevec); + typevec = NULL; + typevec_size = 0; + } + + if (success) + { + string_appendn (&decl, "", 1); + return decl.b; + } + else + { + string_delete (&decl); + return NULL; + } +} + +static int +get_count (type, count) + const char **type; + int *count; +{ + if (!isdigit (**type)) + return 0; + *count = **type - '0'; + *type += 1; + /* see flush_repeats in cplus-method.c */ + if (isdigit (**type)) + { + const char *p = *type; + int n = *count; + do + { + n *= 10; + n += *p - '0'; + p += 1; + } + while (isdigit (*p)); + if (*p == '_') + { + *type = p + 1; + *count = n; + } + } + return 1; +} + +/* result will be initialised here; it will be freed on failure */ + +static int +do_type (type, result) + const char **type; + string *result; +{ + int n; + int done; + int non_empty = 0; + int success; + string decl; + const char *remembered_type; + + string_init (&decl); + string_init (result); + + done = 0; + success = 1; + while (success && !done) + { + int member; + switch (**type) + { + case 'P': + *type += 1; + string_prepend (&decl, "*"); + break; + + case 'R': + *type += 1; + string_prepend (&decl, "&"); + break; + + case 'T': + *type += 1; + if (!get_count (type, &n) || n >= ntypes) + success = 0; + else + { + remembered_type = typevec[n]; + type = &remembered_type; + } + break; + + case 'F': + *type += 1; + if (!string_empty (&decl) && decl.b[0] == '*') + { + string_prepend (&decl, "("); + string_append (&decl, ")"); + } + if (!do_args (type, &decl) || **type != '_') + success = 0; + else + *type += 1; + break; + + case 'M': + case 'O': + { + int constp = 0; + int volatilep = 0; + + member = **type == 'M'; + *type += 1; + if (!isdigit (**type)) + { + success = 0; + break; + } + n = 0; + do + { + n *= 10; + n += **type - '0'; + *type += 1; + } + while (isdigit (**type)); + if (strlen (*type) < n) + { + success = 0; + break; + } + string_append (&decl, ")"); + string_prepend (&decl, "::"); + string_prependn (&decl, *type, n); + string_prepend (&decl, "("); + *type += n; + if (member) + { + if (**type == 'C') + { + *type += 1; + constp = 1; + } + if (**type == 'V') + { + *type += 1; + volatilep = 1; + } + if (*(*type)++ != 'F') + { + success = 0; + break; + } + } + if ((member && !do_args (type, &decl)) || **type != '_') + { + success = 0; + break; + } + *type += 1; + if (constp) + { + if (non_empty) + string_append (&decl, " "); + else + non_empty = 1; + string_append (&decl, "const"); + } + if (volatilep) + { + if (non_empty) + string_append (&decl, " "); + else + non_empty = 1; + string_append (&decl, "volatilep"); + } + break; + } + + case 'C': + if ((*type)[1] == 'P') + { + *type += 1; + if (!string_empty (&decl)) + string_prepend (&decl, " "); + string_prepend (&decl, "const"); + break; + } + + /* fall through */ + default: + done = 1; + break; + } + } + + done = 0; + non_empty = 0; + while (success && !done) + { + switch (**type) + { + case 'C': + *type += 1; + if (non_empty) + string_append (result, " "); + else + non_empty = 1; + string_append (result, "const"); + break; + case 'U': + *type += 1; + if (non_empty) + string_append (result, " "); + else + non_empty = 1; + string_append (result, "unsigned"); + break; + case 'V': + *type += 1; + if (non_empty) + string_append (result, " "); + else + non_empty = 1; + string_append (result, "volatile"); + break; + default: + done = 1; + break; + } + } + + if (success) + switch (**type) + { + case '\0': + case '_': + break; + case 'v': + *type += 1; + if (non_empty) + string_append (result, " "); + string_append (result, "void"); + break; + case 'x': + *type += 1; + if (non_empty) + string_append (result, " "); + string_append (result, "long long"); + break; + case 'l': + *type += 1; + if (non_empty) + string_append (result, " "); + string_append (result, "long"); + break; + case 'i': + *type += 1; + if (non_empty) + string_append (result, " "); + string_append (result, "int"); + break; + case 's': + *type += 1; + if (non_empty) + string_append (result, " "); + string_append (result, "short"); + break; + case 'c': + *type += 1; + if (non_empty) + string_append (result, " "); + string_append (result, "char"); + break; + case 'r': + *type += 1; + if (non_empty) + string_append (result, " "); + string_append (result, "long double"); + break; + case 'd': + *type += 1; + if (non_empty) + string_append (result, " "); + string_append (result, "double"); + break; + case 'f': + *type += 1; + if (non_empty) + string_append (result, " "); + string_append (result, "float"); + break; + case 'G': + *type += 1; + if (!isdigit (**type)) + { + success = 0; + break; + } + /* fall through */ + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + n = 0; + do + { + n *= 10; + n += **type - '0'; + *type += 1; + } + while (isdigit (**type)); + if (strlen (*type) < n) + { + success = 0; + break; + } + if (non_empty) + string_append (result, " "); + string_appendn (result, *type, n); + *type += n; + break; + default: + success = 0; + break; + } + + if (success) + { + if (!string_empty (&decl)) + { + string_append (result, " "); + string_appends (result, &decl); + } + string_delete (&decl); + return 1; + } + else + { + string_delete (&decl); + string_delete (result); + return 0; + } +} + +/* `result' will be initialised in do_type; it will be freed on failure */ + +static int +do_arg (type, result) + const char **type; + string *result; +{ + const char *start = *type; + + if (!do_type (type, result)) + return 0; + remember_type (start, *type - start); + return 1; +} + +static void +remember_type (start, len) + const char *start; + int len; +{ + char *tem; + + if (ntypes >= typevec_size) + { + if (typevec_size == 0) + { + typevec_size = 3; + typevec = (char **) xmalloc (sizeof (char*)*typevec_size); + } + else + { + typevec_size *= 2; + typevec = (char **) xrealloc ((char *)typevec, sizeof (char*)*typevec_size); + } + } + tem = (char *) xmalloc (len + 1); + memcpy (tem, start, len); + tem[len] = '\0'; + typevec[ntypes++] = tem; +} + +/* `decl' must be already initialised, usually non-empty; + it won't be freed on failure */ + +static int +do_args (type, decl) + const char **type; + string *decl; +{ + string arg; + int need_comma = 0; + + string_append (decl, "("); + + while (**type != '_' && **type != '\0' && **type != 'e' && **type != 'v') + { + if (**type == 'N') + { + int r; + int t; + *type += 1; + if (!get_count (type, &r) || !get_count (type, &t) || t >= ntypes) + return 0; + while (--r >= 0) + { + const char *tem = typevec[t]; + if (need_comma) + string_append (decl, ", "); + if (!do_arg (&tem, &arg)) + return 0; + string_appends (decl, &arg); + string_delete (&arg); + need_comma = 1; + } + } + else + { + if (need_comma) + string_append (decl, ", "); + if (!do_arg (type, &arg)) + return 0; + string_appends (decl, &arg); + string_delete (&arg); + need_comma = 1; + } + } + + if (**type == 'v') + *type += 1; + else if (**type == 'e') + { + *type += 1; + if (need_comma) + string_append (decl, ","); + string_append (decl, "..."); + } + + string_append (decl, ")"); + return 1; +} + +static void +munge_function_name (name) + string *name; +{ + if (!string_empty (name) && name->p - name->b >= 3 + && name->b[0] == 'o' && name->b[1] == 'p' && name->b[2] == '$') + { + int i; + /* see if it's an assignment expression */ + if (name->p - name->b >= 10 /* op$assign_ */ + && memcmp (name->b + 3, "assign_", 7) == 0) + { + for (i = 0; i < sizeof (optable)/sizeof (optable[0]); i++) + { + int len = name->p - name->b - 10; + if (strlen (optable[i].in) == len + && memcmp (optable[i].in, name->b + 10, len) == 0) + { + string_clear (name); + string_append (name, "operator"); + string_append (name, optable[i].out); + string_append (name, "="); + return; + } + } + } + else + { + for (i = 0; i < sizeof (optable)/sizeof (optable[0]); i++) + { + int len = name->p - name->b - 3; + if (strlen (optable[i].in) == len + && memcmp (optable[i].in, name->b + 3, len) == 0) + { + string_clear (name); + string_append (name, "operator"); + string_append (name, optable[i].out); + return; + } + } + } + return; + } + else if (!string_empty (name) && name->p - name->b >= 5 + && memcmp (name->b, "type$", 5) == 0) + { + /* type conversion operator */ + string type; + const char *tem = name->b + 5; + if (do_type (&tem, &type)) + { + string_clear (name); + string_append (name, "operator "); + string_appends (name, &type); + string_delete (&type); + return; + } + } +} + +/* a mini string-handling package */ + +static void +string_need (s, n) + string *s; + int n; +{ + if (s->b == NULL) + { + if (n < 32) + n = 32; + s->p = s->b = (char *) xmalloc (n); + s->e = s->b + n; + } + else if (s->e - s->p < n) + { + int tem = s->p - s->b; + n += tem; + n *= 2; + s->b = (char *) xrealloc (s->b, n); + s->p = s->b + tem; + s->e = s->b + n; + } +} + +static void +string_delete (s) + string *s; +{ + if (s->b != NULL) + { + free (s->b); + s->b = s->e = s->p = NULL; + } +} + +static void +string_init (s) + string *s; +{ + s->b = s->p = s->e = NULL; +} + +static void +string_clear (s) + string *s; +{ + s->p = s->b; +} + +static int +string_empty (s) + string *s; +{ + return s->b == s->p; +} + +static void +string_append (p, s) + string *p; + const char *s; +{ + int n; + if (s == NULL || *s == '\0') + return; + n = strlen (s); + string_need (p, n); + memcpy (p->p, s, n); + p->p += n; +} + +static void +string_appends (p, s) + string *p, *s; +{ + int n; + if (s->b == s->p) + return; + n = s->p - s->b; + string_need (p, n); + memcpy (p->p, s->b, n); + p->p += n; +} + +static void +string_appendn (p, s, n) + string *p; + const char *s; + int n; +{ + if (n == 0) + return; + string_need (p, n); + memcpy (p->p, s, n); + p->p += n; +} + +static void +string_prepend (p, s) + string *p; + const char *s; +{ + if (s == NULL || *s == '\0') + return; + string_prependn (p, s, strlen (s)); +} + +#if 0 +static void +string_prepends (p, s) + string *p, *s; +{ + if (s->b == s->p) + return; + string_prependn (p, s->b, s->p - s->b); +} +#endif + +static void +string_prependn (p, s, n) + string *p; + const char *s; + int n; +{ + char *q; + + if (n == 0) + return; + string_need (p, n); + for (q = p->p - 1; q >= p->b; q--) + q[n] = q[0]; + memcpy (p->b, s, n); + p->p += n; +} diff --git a/usr.bin/ld/ld.c b/usr.bin/ld/ld.c new file mode 100644 index 00000000000..f0b35f2edb1 --- /dev/null +++ b/usr.bin/ld/ld.c @@ -0,0 +1,4718 @@ +/*- + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * Modified 1991 by Donn Seeley at UUNET Technologies, Inc. + */ + +#ifndef lint +static char sccsid[] = "@(#)ld.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +/* Linker `ld' for GNU + Copyright (C) 1988 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Written by Richard Stallman with some help from Eric Albert. + Set, indirect, and warning symbol features added by Randy Smith. */ + +/* Define how to initialize system-dependent header fields. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* symseg.h defines the obsolete GNU debugging format; we should nuke it. */ +#define CORE_ADDR unsigned long /* For symseg.h */ +#include "symseg.h" + +#define N_SET_MAGIC(exec, val) ((exec).a_magic = val) + +/* If compiled with GNU C, use the built-in alloca */ +#ifdef __GNUC__ +#define alloca __builtin_alloca +#endif + +#define min(a,b) ((a) < (b) ? (a) : (b)) + +/* Macro to control the number of undefined references printed */ +#define MAX_UREFS_PRINTED 10 + +/* Size of a page; obtained from the operating system. */ + +int page_size; + +/* Name this program was invoked by. */ + +char *progname; + +/* System dependencies */ + +/* Define this to specify the default executable format. */ + +#ifndef DEFAULT_MAGIC +#define DEFAULT_MAGIC ZMAGIC +#endif + +#if defined(hp300) || defined(luna68k) +#define INITIALIZE_HEADER outheader.a_mid = MID_HP300 +#endif + +#ifdef sparc +#ifndef sun +#define sun 1 +#endif +#define INITIALIZE_HEADER \ + (outheader.a_mid = MID_SUN_SPARC, outheader.a_toolversion = 1) +#endif + +/* + * Ok. Following are the relocation information macros. If your + * system should not be able to use the default set (below), you must + * define the following: + + * relocation_info: This must be typedef'd (or #define'd) to the type + * of structure that is stored in the relocation info section of your + * a.out files. Often this is defined in the a.out.h for your system. + * + * RELOC_ADDRESS (rval): Offset into the current section of the + * to be relocated. *Must be an lvalue*. + * + * RELOC_EXTERN_P (rval): Is this relocation entry based on an + * external symbol (1), or was it fully resolved upon entering the + * loader (0) in which case some combination of the value in memory + * (if RELOC_MEMORY_ADD_P) and the extra (if RELOC_ADD_EXTRA) contains + * what the value of the relocation actually was. *Must be an lvalue*. + * + * RELOC_TYPE (rval): If this entry was fully resolved upon + * entering the loader, what type should it be relocated as? + * + * RELOC_SYMBOL (rval): If this entry was not fully resolved upon + * entering the loader, what is the index of it's symbol in the symbol + * table? *Must be a lvalue*. + * + * RELOC_MEMORY_ADD_P (rval): This should return true if the final + * relocation value output here should be added to memory, or if the + * section of memory described should simply be set to the relocation + * value. + * + * RELOC_ADD_EXTRA (rval): (Optional) This macro, if defined, gives + * an extra value to be added to the relocation value based on the + * individual relocation entry. *Must be an lvalue if defined*. + * + * RELOC_PCREL_P (rval): True if the relocation value described is + * pc relative. + * + * RELOC_VALUE_RIGHTSHIFT (rval): Number of bits right to shift the + * final relocation value before putting it where it belongs. + * + * RELOC_TARGET_SIZE (rval): log to the base 2 of the number of + * bytes of size this relocation entry describes; 1 byte == 0; 2 bytes + * == 1; 4 bytes == 2, and etc. This is somewhat redundant (we could + * do everything in terms of the bit operators below), but having this + * macro could end up producing better code on machines without fancy + * bit twiddling. Also, it's easier to understand/code big/little + * endian distinctions with this macro. + * + * RELOC_TARGET_BITPOS (rval): The starting bit position within the + * object described in RELOC_TARGET_SIZE in which the relocation value + * will go. + * + * RELOC_TARGET_BITSIZE (rval): How many bits are to be replaced + * with the bits of the relocation value. It may be assumed by the + * code that the relocation value will fit into this many bits. This + * may be larger than RELOC_TARGET_SIZE if such be useful. + * + * + * Things I haven't implemented + * ---------------------------- + * + * Values for RELOC_TARGET_SIZE other than 0, 1, or 2. + * + * Pc relative relocation for External references. + * + * + */ + +/* The following #if has been modifed for cross compilation */ +/* It originally read: #if defined(sun) && defined(sparc) */ +/* Marc Ullman, Stanford University Nov. 1 1989 */ +#if defined(sun) && (TARGET == SUN4) +/* Sparc (Sun 4) macros */ +#undef relocation_info +#define relocation_info reloc_info_sparc +#define RELOC_ADDRESS(r) ((r)->r_address) +#define RELOC_EXTERN_P(r) ((r)->r_extern) +#define RELOC_TYPE(r) ((r)->r_index) +#define RELOC_SYMBOL(r) ((r)->r_index) +#define RELOC_MEMORY_SUB_P(r) 0 +#define RELOC_MEMORY_ADD_P(r) 0 +#define RELOC_ADD_EXTRA(r) ((r)->r_addend) +#define RELOC_PCREL_P(r) \ + ((r)->r_type >= RELOC_DISP8 && (r)->r_type <= RELOC_WDISP22) +#define RELOC_VALUE_RIGHTSHIFT(r) (reloc_target_rightshift[(r)->r_type]) +#define RELOC_TARGET_SIZE(r) (reloc_target_size[(r)->r_type]) +#define RELOC_TARGET_BITPOS(r) 0 +#define RELOC_TARGET_BITSIZE(r) (reloc_target_bitsize[(r)->r_type]) + +/* Note that these are very dependent on the order of the enums in + enum reloc_type (in a.out.h); if they change the following must be + changed */ +/* Also note that the last few may be incorrect; I have no information */ +static int reloc_target_rightshift[] = { + 0, 0, 0, 0, 0, 0, 2, 2, 10, 0, 0, 0, 0, 0, 0, +}; +static int reloc_target_size[] = { + 0, 1, 2, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +}; +static int reloc_target_bitsize[] = { + 8, 16, 32, 8, 16, 32, 30, 22, 22, 22, 13, 10, 32, 32, 16, +}; + +#define MAX_ALIGNMENT (sizeof (double)) +#endif + +/* Default macros */ +#ifndef RELOC_ADDRESS +#define RELOC_ADDRESS(r) ((r)->r_address) +#define RELOC_EXTERN_P(r) ((r)->r_extern) +#define RELOC_TYPE(r) ((r)->r_symbolnum) +#define RELOC_SYMBOL(r) ((r)->r_symbolnum) +#define RELOC_MEMORY_SUB_P(r) 0 +#define RELOC_MEMORY_ADD_P(r) 1 +#undef RELOC_ADD_EXTRA +#define RELOC_PCREL_P(r) ((r)->r_pcrel) +#define RELOC_VALUE_RIGHTSHIFT(r) 0 +#define RELOC_TARGET_SIZE(r) ((r)->r_length) +#define RELOC_TARGET_BITPOS(r) 0 +#define RELOC_TARGET_BITSIZE(r) 32 +#endif + +#ifndef MAX_ALIGNMENT +#define MAX_ALIGNMENT (sizeof (int)) +#endif + +#ifdef nounderscore +#define LPREFIX '.' +#else +#define LPREFIX 'L' +#endif + +#ifndef TEXT_START +#define TEXT_START(x) N_TXTADDR(x) +#endif + +/* Special global symbol types understood by GNU LD. */ + +/* The following type indicates the definition of a symbol as being + an indirect reference to another symbol. The other symbol + appears as an undefined reference, immediately following this symbol. + + Indirection is asymmetrical. The other symbol's value will be used + to satisfy requests for the indirect symbol, but not vice versa. + If the other symbol does not have a definition, libraries will + be searched to find a definition. + + So, for example, the following two lines placed in an assembler + input file would result in an object file which would direct gnu ld + to resolve all references to symbol "foo" as references to symbol + "bar". + + .stabs "_foo",11,0,0,0 + .stabs "_bar",1,0,0,0 + + Note that (11 == (N_INDR | N_EXT)) and (1 == (N_UNDF | N_EXT)). */ + +#ifndef N_INDR +#define N_INDR 0xa +#endif + +/* The following symbols refer to set elements. These are expected + only in input to the loader; they should not appear in loader + output (unless relocatable output is requested). To be recognized + by the loader, the input symbols must have their N_EXT bit set. + All the N_SET[ATDB] symbols with the same name form one set. The + loader collects all of these elements at load time and outputs a + vector for each name. + Space (an array of 32 bit words) is allocated for the set in the + data section, and the n_value field of each set element value is + stored into one word of the array. + The first word of the array is the length of the set (number of + elements). The last word of the vector is set to zero for possible + use by incremental loaders. The array is ordered by the linkage + order; the first symbols which the linker encounters will be first + in the array. + + In C syntax this looks like: + + struct set_vector { + unsigned int length; + unsigned int vector[length]; + unsigned int always_zero; + }; + + Before being placed into the array, each element is relocated + according to its type. This allows the loader to create an array + of pointers to objects automatically. N_SETA type symbols will not + be relocated. + + The address of the set is made into an N_SETV symbol + whose name is the same as the name of the set. + This symbol acts like a N_DATA global symbol + in that it can satisfy undefined external references. + + For the purposes of determining whether or not to load in a library + file, set element definitions are not considered "real + definitions"; they will not cause the loading of a library + member. + + If relocatable output is requested, none of this processing is + done. The symbols are simply relocated and passed through to the + output file. + + So, for example, the following three lines of assembler code + (whether in one file or scattered between several different ones) + will produce a three element vector (total length is five words; + see above), referenced by the symbol "_xyzzy", which will have the + addresses of the routines _init1, _init2, and _init3. + + *NOTE*: If symbolic addresses are used in the n_value field of the + defining .stabs, those symbols must be defined in the same file as + that containing the .stabs. + + .stabs "_xyzzy",23,0,0,_init1 + .stabs "_xyzzy",23,0,0,_init2 + .stabs "_xyzzy",23,0,0,_init3 + + Note that (23 == (N_SETT | N_EXT)). */ + +#ifndef N_SETA +#define N_SETA 0x14 /* Absolute set element symbol */ +#endif /* This is input to LD, in a .o file. */ + +#ifndef N_SETT +#define N_SETT 0x16 /* Text set element symbol */ +#endif /* This is input to LD, in a .o file. */ + +#ifndef N_SETD +#define N_SETD 0x18 /* Data set element symbol */ +#endif /* This is input to LD, in a .o file. */ + +#ifndef N_SETB +#define N_SETB 0x1A /* Bss set element symbol */ +#endif /* This is input to LD, in a .o file. */ + +/* Macros dealing with the set element symbols defined in a.out.h */ +#define SET_ELEMENT_P(x) ((x)>=N_SETA&&(x)<=(N_SETB|N_EXT)) +#define TYPE_OF_SET_ELEMENT(x) ((x)-N_SETA+N_ABS) + +#ifndef N_SETV +#define N_SETV 0x1C /* Pointer to set vector in data area. */ +#endif /* This is output from LD. */ + +/* If a this type of symbol is encountered, its name is a warning + message to print each time the symbol referenced by the next symbol + table entry is referenced. + + This feature may be used to allow backwards compatibility with + certain functions (eg. gets) but to discourage programmers from + their use. + + So if, for example, you wanted to have ld print a warning whenever + the function "gets" was used in their C program, you would add the + following to the assembler file in which gets is defined: + + .stabs "Obsolete function \"gets\" referenced",30,0,0,0 + .stabs "_gets",1,0,0,0 + + These .stabs do not necessarily have to be in the same file as the + gets function, they simply must exist somewhere in the compilation. */ + +#ifndef N_WARNING +#define N_WARNING 0x1E /* Warning message to print if symbol + included */ +#endif /* This is input to ld */ + +#ifndef __GNU_STAB__ + +/* Line number for the data section. This is to be used to describe + the source location of a variable declaration. */ +#ifndef N_DSLINE +#define N_DSLINE (N_SLINE+N_DATA-N_TEXT) +#endif + +/* Line number for the bss section. This is to be used to describe + the source location of a variable declaration. */ +#ifndef N_BSLINE +#define N_BSLINE (N_SLINE+N_BSS-N_TEXT) +#endif + +#endif /* not __GNU_STAB__ */ + +/* Symbol table */ + +/* Global symbol data is recorded in these structures, + one for each global symbol. + They are found via hashing in 'symtab', which points to a vector of buckets. + Each bucket is a chain of these structures through the link field. */ + +typedef + struct glosym + { + /* Pointer to next symbol in this symbol's hash bucket. */ + struct glosym *link; + /* Name of this symbol. */ + char *name; + /* Value of this symbol as a global symbol. */ + long value; + /* Chain of external 'nlist's in files for this symbol, both defs + and refs. */ + struct nlist *refs; + /* Any warning message that might be associated with this symbol + from an N_WARNING symbol encountered. */ + char *warning; + /* Nonzero means definitions of this symbol as common have been seen, + and the value here is the largest size specified by any of them. */ + int max_common_size; + /* For relocatable_output, records the index of this global sym in the + symbol table to be written, with the first global sym given index 0.*/ + int def_count; + /* Nonzero means a definition of this global symbol is known to exist. + Library members should not be loaded on its account. */ + char defined; + /* Nonzero means a reference to this global symbol has been seen + in a file that is surely being loaded. + A value higher than 1 is the n_type code for the symbol's + definition. */ + char referenced; + /* A count of the number of undefined references printed for a + specific symbol. If a symbol is unresolved at the end of + digest_symbols (and the loading run is supposed to produce + relocatable output) do_file_warnings keeps track of how many + unresolved reference error messages have been printed for + each symbol here. When the number hits MAX_UREFS_PRINTED, + messages stop. */ + unsigned char undef_refs; + /* 1 means that this symbol has multiple definitions. 2 means + that it has multiple definitions, and some of them are set + elements, one of which has been printed out already. */ + unsigned char multiply_defined; + /* Nonzero means print a message at all refs or defs of this symbol */ + char trace; + } + symbol; + +/* Demangler for C++. */ +extern char *cplus_demangle (); + +/* Demangler function to use. */ +char *(*demangler)() = NULL; + +/* Number of buckets in symbol hash table */ +#define TABSIZE 1009 + +/* The symbol hash table: a vector of TABSIZE pointers to struct glosym. */ +symbol *symtab[TABSIZE]; + +/* Number of symbols in symbol hash table. */ +int num_hash_tab_syms = 0; + +/* Count the number of nlist entries that are for local symbols. + This count and the three following counts + are incremented as as symbols are entered in the symbol table. */ +int local_sym_count; + +/* Count number of nlist entries that are for local symbols + whose names don't start with L. */ +int non_L_local_sym_count; + +/* Count the number of nlist entries for debugger info. */ +int debugger_sym_count; + +/* Count the number of global symbols referenced and not defined. */ +int undefined_global_sym_count; + +/* Count the number of global symbols multiply defined. */ +int multiple_def_count; + +/* Count the number of defined global symbols. + Each symbol is counted only once + regardless of how many different nlist entries refer to it, + since the output file will need only one nlist entry for it. + This count is computed by `digest_symbols'; + it is undefined while symbols are being loaded. */ +int defined_global_sym_count; + +/* Count the number of symbols defined through common declarations. + This count is kept in symdef_library, linear_library, and + enter_global_ref. It is incremented when the defined flag is set + in a symbol because of a common definition, and decremented when + the symbol is defined "for real" (ie. by something besides a common + definition). */ +int common_defined_global_count; + +/* Count the number of set element type symbols and the number of + separate vectors which these symbols will fit into. See the + GNU a.out.h for more info. + This count is computed by 'enter_file_symbols' */ +int set_symbol_count; +int set_vector_count; + +/* Define a linked list of strings which define symbols which should + be treated as set elements even though they aren't. Any symbol + with a prefix matching one of these should be treated as a set + element. + + This is to make up for deficiencies in many assemblers which aren't + willing to pass any stabs through to the loader which they don't + understand. */ +struct string_list_element { + char *str; + struct string_list_element *next; +}; + +struct string_list_element *set_element_prefixes; + +/* Count the number of definitions done indirectly (ie. done relative + to the value of some other symbol. */ +int global_indirect_count; + +/* Count the number of warning symbols encountered. */ +int warning_count; + +/* Total number of symbols to be written in the output file. + Computed by digest_symbols from the variables above. */ +int nsyms; + + +/* Nonzero means ptr to symbol entry for symbol to use as start addr. + -e sets this. */ +symbol *entry_symbol; + +symbol *edata_symbol; /* the symbol _edata */ +symbol *etext_symbol; /* the symbol _etext */ +symbol *end_symbol; /* the symbol _end */ + +/* Each input file, and each library member ("subfile") being loaded, + has a `file_entry' structure for it. + + For files specified by command args, these are contained in the vector + which `file_table' points to. + + For library members, they are dynamically allocated, + and chained through the `chain' field. + The chain is found in the `subfiles' field of the `file_entry'. + The `file_entry' objects for the members have `superfile' fields pointing + to the one for the library. */ + +struct file_entry { + /* Name of this file. */ + char *filename; + /* Name to use for the symbol giving address of text start */ + /* Usually the same as filename, but for a file spec'd with -l + this is the -l switch itself rather than the filename. */ + char *local_sym_name; + + /* Describe the layout of the contents of the file */ + + /* The file's a.out header. */ + struct exec header; + /* Offset in file of GDB symbol segment, or 0 if there is none. */ + int symseg_offset; + + /* Describe data from the file loaded into core */ + + /* Symbol table of the file. */ + struct nlist *symbols; + /* Size in bytes of string table. */ + int string_size; + /* Pointer to the string table. + The string table is not kept in core all the time, + but when it is in core, its address is here. */ + char *strings; + + /* Next two used only if `relocatable_output' or if needed for */ + /* output of undefined reference line numbers. */ + + /* Text reloc info saved by `write_text' for `coptxtrel'. */ + struct relocation_info *textrel; + /* Data reloc info saved by `write_data' for `copdatrel'. */ + struct relocation_info *datarel; + + /* Relation of this file's segments to the output file */ + + /* Start of this file's text seg in the output file core image. */ + int text_start_address; + /* Start of this file's data seg in the output file core image. */ + int data_start_address; + /* Start of this file's bss seg in the output file core image. */ + int bss_start_address; + /* Offset in bytes in the output file symbol table + of the first local symbol for this file. Set by `write_file_symbols'. */ + int local_syms_offset; + + /* For library members only */ + + /* For a library, points to chain of entries for the library members. */ + struct file_entry *subfiles; + /* For a library member, offset of the member within the archive. + Zero for files that are not library members. */ + int starting_offset; + /* Size of contents of this file, if library member. */ + int total_size; + /* For library member, points to the library's own entry. */ + struct file_entry *superfile; + /* For library member, points to next entry for next member. */ + struct file_entry *chain; + + /* 1 if file is a library. */ + char library_flag; + + /* 1 if file's header has been read into this structure. */ + char header_read_flag; + + /* 1 means search a set of directories for this file. */ + char search_dirs_flag; + + /* 1 means this is base file of incremental load. + Do not load this file's text or data. + Also default text_start to after this file's bss. */ + char just_syms_flag; +}; + +/* Vector of entries for input files specified by arguments. + These are all the input files except for members of specified libraries. */ +struct file_entry *file_table; + +/* Length of that vector. */ +int number_of_files; + +/* When loading the text and data, we can avoid doing a close + and another open between members of the same library. + + These two variables remember the file that is currently open. + Both are zero if no file is open. + + See `each_file' and `file_close'. */ + +struct file_entry *input_file; +int input_desc; + +/* The name of the file to write; "a.out" by default. */ + +char *output_filename; + +/* Descriptor for writing that file with `mywrite'. */ + +int outdesc; + +/* Header for that file (filled in by `write_header'). */ + +struct exec outheader; + +#ifdef COFF_ENCAPSULATE +struct coffheader coffheader; +int need_coff_header; +#endif + +/* The following are computed by `digest_symbols'. */ + +int text_size; /* total size of text of all input files. */ +int data_size; /* total size of data of all input files. */ +int bss_size; /* total size of bss of all input files. */ +int text_reloc_size; /* total size of text relocation of all input files. */ +int data_reloc_size; /* total size of data relocation of all input */ + /* files. */ + +/* Specifications of start and length of the area reserved at the end + of the text segment for the set vectors. Computed in 'digest_symbols' */ +int set_sect_start; +int set_sect_size; + +/* Pointer for in core storage for the above vectors, before they are + written. */ +unsigned long *set_vectors; + +/* Amount of cleared space to leave between the text and data segments. */ + +int text_pad; + +/* Amount of bss segment to include as part of the data segment. */ + +int data_pad; + +/* Format of __.SYMDEF: + First, a longword containing the size of the 'symdef' data that follows. + Second, zero or more 'symdef' structures. + Third, a longword containing the length of symbol name strings. + Fourth, zero or more symbol name strings (each followed by a null). */ + +struct symdef { + int symbol_name_string_index; + int library_member_offset; +}; + +/* Record most of the command options. */ + +/* Address we assume the text section will be loaded at. + We relocate symbols and text and data for this, but we do not + write any padding in the output file for it. */ +int text_start; + +/* Offset of default entry-pc within the text section. */ +int entry_offset; + +/* Address we decide the data section will be loaded at. */ +int data_start; + +/* `text-start' address is normally this much plus a page boundary. + This is not a user option; it is fixed for each system. */ +int text_start_alignment; + +/* Nonzero if -T was specified in the command line. + This prevents text_start from being set later to default values. */ +int T_flag_specified; + +/* Nonzero if -Tdata was specified in the command line. + This prevents data_start from being set later to default values. */ +int Tdata_flag_specified; + +/* Size to pad data section up to. + We simply increase the size of the data section, padding with zeros, + and reduce the size of the bss section to match. */ +int specified_data_size; + +/* Magic number to use for the output file, set by switch. */ +int magic; + +/* Nonzero means print names of input files as processed. */ +int trace_files; + +/* Which symbols should be stripped (omitted from the output): + none, all, or debugger symbols. */ +enum { STRIP_NONE, STRIP_ALL, STRIP_DEBUGGER } strip_symbols; + +/* Which local symbols should be omitted: + none, all, or those starting with L. + This is irrelevant if STRIP_NONE. */ +enum { DISCARD_NONE, DISCARD_ALL, DISCARD_L } discard_locals; + +/* Do we want to pad the text to a page boundary? */ +int padtext; + +/* 1 => write load map. */ +int write_map; + +/* 1 => write relocation into output file so can re-input it later. */ +int relocatable_output; + +/* 1 => assign space to common symbols even if `relocatable_output'. */ +int force_common_definition; + +/* Standard directories to search for files specified by -l. */ +char *standard_search_dirs[] = +#ifdef STANDARD_SEARCH_DIRS + {STANDARD_SEARCH_DIRS}; +#else +#ifdef NON_NATIVE + {"/usr/local/lib/gnu"}; +#else + {"/lib", "/usr/lib", "/usr/local/lib"}; +#endif +#endif + +/* Actual vector of directories to search; + this contains those specified with -L plus the standard ones. */ +char **search_dirs; + +/* Length of the vector `search_dirs'. */ +int n_search_dirs; + +/* Non zero means to create the output executable. */ +/* Cleared by nonfatal errors. */ +int make_executable; + +/* Force the executable to be output, even if there are non-fatal + errors */ +int force_executable; + +/* Keep a list of any symbols referenced from the command line (so + that error messages for these guys can be generated). This list is + zero terminated. */ +struct glosym **cmdline_references; +int cl_refs_allocated; + +void bcopy (), bzero (); +int malloc (), realloc (); +#ifndef alloca +int alloca (); +#endif +int free (); + +int xmalloc (); +int xrealloc (); +void fatal (); +void fatal_with_file (); +void perror_name (); +void perror_file (); +void error (); + +void digest_symbols (); +void print_symbols (); +void load_symbols (); +void decode_command (); +void list_undefined_symbols (); +void list_unresolved_references (); +void write_output (); +void write_header (); +void write_text (); +void read_file_relocation (); +void write_data (); +void write_rel (); +void write_syms (); +void write_symsegs (); +void mywrite (); +void symtab_init (); +void padfile (); +char *concat (); +char *get_file_name (); +symbol *getsym (), *getsym_soft (); + +int +main (argc, argv) + char **argv; + int argc; +{ +/* Added this to stop ld core-dumping on very large .o files. */ +#ifdef RLIMIT_STACK + /* Get rid of any avoidable limit on stack size. */ + { + struct rlimit rlim; + + /* Set the stack limit huge so that alloca does not fail. */ + getrlimit (RLIMIT_STACK, &rlim); + rlim.rlim_cur = rlim.rlim_max; + setrlimit (RLIMIT_STACK, &rlim); + } +#endif /* RLIMIT_STACK */ + + page_size = getpagesize (); + progname = argv[0]; + + /* Clear the cumulative info on the output file. */ + + text_size = 0; + data_size = 0; + bss_size = 0; + text_reloc_size = 0; + data_reloc_size = 0; + + data_pad = 0; + text_pad = 0; + + /* Initialize the data about options. */ + + specified_data_size = 0; + strip_symbols = STRIP_NONE; + trace_files = 0; + discard_locals = DISCARD_NONE; + padtext = 0; + entry_symbol = 0; + write_map = 0; + relocatable_output = 0; + force_common_definition = 0; + T_flag_specified = 0; + Tdata_flag_specified = 0; + magic = DEFAULT_MAGIC; + make_executable = 1; + force_executable = 0; + set_element_prefixes = 0; + + /* Initialize the cumulative counts of symbols. */ + + local_sym_count = 0; + non_L_local_sym_count = 0; + debugger_sym_count = 0; + undefined_global_sym_count = 0; + set_symbol_count = 0; + set_vector_count = 0; + global_indirect_count = 0; + warning_count = 0; + multiple_def_count = 0; + common_defined_global_count = 0; + + /* Keep a list of symbols referenced from the command line */ + cl_refs_allocated = 10; + cmdline_references + = (struct glosym **) xmalloc (cl_refs_allocated + * sizeof(struct glosym *)); + *cmdline_references = 0; + + /* Completely decode ARGV. */ + + decode_command (argc, argv); + + /* Create the symbols `etext', `edata' and `end'. */ + + if (!relocatable_output) + symtab_init (); + + /* Determine whether to count the header as part of + the text size, and initialize the text size accordingly. + This depends on the kind of system and on the output format selected. */ + + N_SET_MAGIC (outheader, magic); +#ifdef INITIALIZE_HEADER + INITIALIZE_HEADER; +#endif + + text_size = sizeof (struct exec); +#ifdef COFF_ENCAPSULATE + if (relocatable_output == 0 && file_table[0].just_syms_flag == 0) + { + need_coff_header = 1; + /* set this flag now, since it will change the values of N_TXTOFF, etc */ + N_SET_FLAGS (outheader, N_FLAGS_COFF_ENCAPSULATE); + text_size += sizeof (struct coffheader); + } +#endif + + text_size -= N_TXTOFF (outheader); + + if (text_size < 0) + text_size = 0; + entry_offset = text_size; + + if (!T_flag_specified && !relocatable_output) + text_start = TEXT_START (outheader); + + /* The text-start address is normally this far past a page boundary. */ + text_start_alignment = text_start % page_size; + + /* Load symbols of all input files. + Also search all libraries and decide which library members to load. */ + + load_symbols (); + + /* Compute where each file's sections go, and relocate symbols. */ + + digest_symbols (); + + /* Print error messages for any missing symbols, for any warning + symbols, and possibly multiple definitions */ + + do_warnings (stderr); + + /* Print a map, if requested. */ + + if (write_map) print_symbols (stdout); + + /* Write the output file. */ + + if (make_executable || force_executable) + write_output (); + + exit (!make_executable); +} + +void decode_option (); + +/* Analyze a command line argument. + Return 0 if the argument is a filename. + Return 1 if the argument is a option complete in itself. + Return 2 if the argument is a option which uses an argument. + + Thus, the value is the number of consecutive arguments + that are part of options. */ + +int +classify_arg (arg) + register char *arg; +{ + if (*arg != '-') return 0; + switch (arg[1]) + { + case 'A': + case 'D': + case 'e': + case 'L': + case 'l': + case 'o': + case 'u': + case 'V': + case 'y': + if (arg[2]) + return 1; + return 2; + + case 'B': + if (! strcmp (&arg[2], "static")) + return 1; + + case 'T': + if (arg[2] == 0) + return 2; + if (! strcmp (&arg[2], "text")) + return 2; + if (! strcmp (&arg[2], "data")) + return 2; + return 1; + } + + return 1; +} + +/* Process the command arguments, + setting up file_table with an entry for each input file, + and setting variables according to the options. */ + +void +decode_command (argc, argv) + char **argv; + int argc; +{ + register int i; + register struct file_entry *p; + char *cp; + + number_of_files = 0; + output_filename = "a.out"; + + n_search_dirs = 0; + search_dirs = (char **) xmalloc (sizeof (char *)); + + /* First compute number_of_files so we know how long to make file_table. */ + /* Also process most options completely. */ + + for (i = 1; i < argc; i++) + { + register int code = classify_arg (argv[i]); + if (code) + { + if (i + code > argc) + fatal ("no argument following %s\n", argv[i]); + + decode_option (argv[i], argv[i+1]); + + if (argv[i][1] == 'l' || argv[i][1] == 'A') + number_of_files++; + + i += code - 1; + } + else + number_of_files++; + } + + if (!number_of_files) + fatal ("no input files", 0); + + p = file_table + = (struct file_entry *) xmalloc (number_of_files * sizeof (struct file_entry)); + bzero (p, number_of_files * sizeof (struct file_entry)); + + /* Now scan again and fill in file_table. */ + /* All options except -A and -l are ignored here. */ + + for (i = 1; i < argc; i++) + { + register int code = classify_arg (argv[i]); + + if (code) + { + char *string; + if (code == 2) + string = argv[i+1]; + else + string = &argv[i][2]; + + if (argv[i][1] == 'A') + { + if (p != file_table) + fatal ("-A specified before an input file other than the first"); + + p->filename = string; + p->local_sym_name = string; + p->just_syms_flag = 1; + p++; + } + if (argv[i][1] == 'l') + { + if (cp = rindex(string, '/')) + { + *cp++ = '\0'; + cp = concat (string, "/lib", cp); + p->filename = concat (cp, ".a", ""); + } + else + p->filename = concat ("lib", string, ".a"); + + p->local_sym_name = concat ("-l", string, ""); + p->search_dirs_flag = 1; + p++; + } + i += code - 1; + } + else + { + p->filename = argv[i]; + p->local_sym_name = argv[i]; + p++; + } + } + + /* Now check some option settings for consistency. */ + +#ifdef NMAGIC + if ((magic == ZMAGIC || magic == NMAGIC) +#else + if ((magic == ZMAGIC) +#endif + && (text_start - text_start_alignment) & (page_size - 1)) + fatal ("-T argument not multiple of page size, with sharable output", 0); + + /* Append the standard search directories to the user-specified ones. */ + { + int n = sizeof standard_search_dirs / sizeof standard_search_dirs[0]; + n_search_dirs += n; + search_dirs + = (char **) xrealloc (search_dirs, n_search_dirs * sizeof (char *)); + bcopy (standard_search_dirs, &search_dirs[n_search_dirs - n], + n * sizeof (char *)); + } +} + + +void +add_cmdline_ref (sp) + struct glosym *sp; +{ + struct glosym **ptr; + + for (ptr = cmdline_references; + ptr < cmdline_references + cl_refs_allocated && *ptr; + ptr++) + ; + + if (ptr >= cmdline_references + cl_refs_allocated - 1) + { + int diff = ptr - cmdline_references; + + cl_refs_allocated *= 2; + cmdline_references = (struct glosym **) + xrealloc (cmdline_references, + cl_refs_allocated * sizeof (struct glosym *)); + ptr = cmdline_references + diff; + } + + *ptr++ = sp; + *ptr = (struct glosym *) 0; +} + +int +set_element_prefixed_p (name) + char *name; +{ + struct string_list_element *p; + int i; + + for (p = set_element_prefixes; p; p = p->next) + { + for (i = 0; p->str[i] != '\0' && (p->str[i] == name[i]); i++) + ; + + if (p->str[i] == '\0') + return 1; + } + return 0; +} + +int parse (); + +/* Record an option and arrange to act on it later. + ARG should be the following command argument, + which may or may not be used by this option. + + The `l' and `A' options are ignored here since they actually + specify input files. */ + +void +decode_option (swt, arg) + register char *swt, *arg; +{ + /* We get Bstatic from gcc on suns. */ + if (! strcmp (swt + 1, "Bstatic")) + return; + if (! strcmp (swt + 1, "Ttext")) + { + text_start = parse (arg, "%x", "invalid argument to -Ttext"); + T_flag_specified = 1; + return; + } + if (! strcmp (swt + 1, "Tdata")) + { + data_start = parse (arg, "%x", "invalid argument to -Tdata"); + Tdata_flag_specified = 1; + return; + } + if (! strcmp (swt + 1, "noinhibit-exec")) + { + force_executable = 1; + return; + } + + if (swt[2] != 0) + arg = &swt[2]; + + switch (swt[1]) + { + case 'A': + return; + + case 'D': + specified_data_size = parse (arg, "%x", "invalid argument to -D"); + return; + + case 'd': + force_common_definition = 1; + return; + + case 'e': + entry_symbol = getsym (arg); + if (!entry_symbol->defined && !entry_symbol->referenced) + undefined_global_sym_count++; + entry_symbol->referenced = 1; + add_cmdline_ref (entry_symbol); + return; + + case 'l': + /* If linking with libg++, use the C++ demangler. */ + if (arg != NULL && strcmp (arg, "g++") == 0) + demangler = cplus_demangle; + return; + + case 'L': + n_search_dirs++; + search_dirs + = (char **) xrealloc (search_dirs, n_search_dirs * sizeof (char *)); + search_dirs[n_search_dirs - 1] = arg; + return; + + case 'M': + write_map = 1; + return; + + case 'N': + magic = OMAGIC; + return; + +#ifdef NMAGIC + case 'n': + magic = NMAGIC; + return; +#endif + + case 'o': + output_filename = arg; + return; + + case 'p': + padtext = 1; + return; + + case 'r': + relocatable_output = 1; + magic = OMAGIC; + text_start = 0; + return; + + case 'S': + strip_symbols = STRIP_DEBUGGER; + return; + + case 's': + strip_symbols = STRIP_ALL; + return; + + case 'T': + text_start = parse (arg, "%x", "invalid argument to -T"); + T_flag_specified = 1; + return; + + case 't': + trace_files = 1; + return; + + case 'u': + { + register symbol *sp = getsym (arg); + if (!sp->defined && !sp->referenced) + undefined_global_sym_count++; + sp->referenced = 1; + add_cmdline_ref (sp); + } + return; + + case 'V': + { + struct string_list_element *new + = (struct string_list_element *) + xmalloc (sizeof (struct string_list_element)); + + new->str = arg; + new->next = set_element_prefixes; + set_element_prefixes = new; + return; + } + + case 'X': + discard_locals = DISCARD_L; + return; + + case 'x': + discard_locals = DISCARD_ALL; + return; + + case 'y': + { + register symbol *sp = getsym (&swt[2]); + sp->trace = 1; + } + return; + + case 'z': + magic = ZMAGIC; + return; + + default: + fatal ("invalid command option `%s'", swt); + } +} + +/** Convenient functions for operating on one or all files being */ + /** loaded. */ +void print_file_name (); + +/* Call FUNCTION on each input file entry. + Do not call for entries for libraries; + instead, call once for each library member that is being loaded. + + FUNCTION receives two arguments: the entry, and ARG. */ + +void +each_file (function, arg) + register void (*function)(); + register int arg; +{ + register int i; + + for (i = 0; i < number_of_files; i++) + { + register struct file_entry *entry = &file_table[i]; + if (entry->library_flag) + { + register struct file_entry *subentry = entry->subfiles; + for (; subentry; subentry = subentry->chain) + (*function) (subentry, arg); + } + else + (*function) (entry, arg); + } +} + +/* Call FUNCTION on each input file entry until it returns a non-zero + value. Return this value. + Do not call for entries for libraries; + instead, call once for each library member that is being loaded. + + FUNCTION receives two arguments: the entry, and ARG. It must be a + function returning unsigned long (though this can probably be fudged). */ + +unsigned long +check_each_file (function, arg) + register unsigned long (*function)(); + register int arg; +{ + register int i; + register unsigned long return_val; + + for (i = 0; i < number_of_files; i++) + { + register struct file_entry *entry = &file_table[i]; + if (entry->library_flag) + { + register struct file_entry *subentry = entry->subfiles; + for (; subentry; subentry = subentry->chain) + if (return_val = (*function) (subentry, arg)) + return return_val; + } + else + if (return_val = (*function) (entry, arg)) + return return_val; + } + return 0; +} + +/* Like `each_file' but ignore files that were just for symbol definitions. */ + +void +each_full_file (function, arg) + register void (*function)(); + register int arg; +{ + register int i; + + for (i = 0; i < number_of_files; i++) + { + register struct file_entry *entry = &file_table[i]; + if (entry->just_syms_flag) + continue; + if (entry->library_flag) + { + register struct file_entry *subentry = entry->subfiles; + for (; subentry; subentry = subentry->chain) + (*function) (subentry, arg); + } + else + (*function) (entry, arg); + } +} + +/* Close the input file that is now open. */ + +void +file_close () +{ + close (input_desc); + input_desc = 0; + input_file = 0; +} + +/* Open the input file specified by 'entry', and return a descriptor. + The open file is remembered; if the same file is opened twice in a row, + a new open is not actually done. */ + +int +file_open (entry) + register struct file_entry *entry; +{ + register int desc; + + if (entry->superfile) + return file_open (entry->superfile); + + if (entry == input_file) + return input_desc; + + if (input_file) file_close (); + + if (entry->search_dirs_flag) + { + int i; + + for (i = 0; i < n_search_dirs; i++) + { + register char *string + = concat (search_dirs[i], "/", entry->filename); + desc = open (string, O_RDONLY, 0); + if (desc > 0) + { + entry->filename = string; + entry->search_dirs_flag = 0; + break; + } + free (string); + } + } + else + desc = open (entry->filename, O_RDONLY, 0); + + if (desc > 0) + { + input_file = entry; + input_desc = desc; + return desc; + } + + perror_file (entry); + /* NOTREACHED */ +} + +/* Print the filename of ENTRY on OUTFILE (a stdio stream), + and then a newline. */ + +void +prline_file_name (entry, outfile) + struct file_entry *entry; + FILE *outfile; +{ + print_file_name (entry, outfile); + fprintf (outfile, "\n"); +} + +/* Print the filename of ENTRY on OUTFILE (a stdio stream). */ + +void +print_file_name (entry, outfile) + struct file_entry *entry; + FILE *outfile; +{ + if (entry->superfile) + { + print_file_name (entry->superfile, outfile); + fprintf (outfile, "(%s)", entry->filename); + } + else + fprintf (outfile, "%s", entry->filename); +} + +/* Return the filename of entry as a string (malloc'd for the purpose) */ + +char * +get_file_name (entry) + struct file_entry *entry; +{ + char *result, *supfile; + if (entry->superfile) + { + supfile = get_file_name (entry->superfile); + result = (char *) xmalloc (strlen (supfile) + + strlen (entry->filename) + 3); + sprintf (result, "%s(%s)", supfile, entry->filename); + free (supfile); + } + else + { + result = (char *) xmalloc (strlen (entry->filename) + 1); + strcpy (result, entry->filename); + } + return result; +} + +/* Medium-level input routines for rel files. */ + +/* Read a file's header into the proper place in the file_entry. + DESC is the descriptor on which the file is open. + ENTRY is the file's entry. */ + +void +read_header (desc, entry) + int desc; + register struct file_entry *entry; +{ + register int len; + struct exec *loc = (struct exec *) &entry->header; + + lseek (desc, entry->starting_offset, 0); +#ifdef COFF_ENCAPSULATE + if (entry->just_syms_flag) + lseek (desc, sizeof(coffheader), 1); +#endif + len = read (desc, loc, sizeof (struct exec)); + if (len != sizeof (struct exec)) + fatal_with_file ("failure reading header of ", entry); + if (N_BADMAG (*loc)) + fatal_with_file ("bad magic number in ", entry); + + entry->header_read_flag = 1; +} + +/* Read the symbols of file ENTRY into core. + Assume it is already open, on descriptor DESC. + Also read the length of the string table, which follows the symbol table, + but don't read the contents of the string table. */ + +void +read_entry_symbols (desc, entry) + struct file_entry *entry; + int desc; +{ + int str_size; + + if (!entry->header_read_flag) + read_header (desc, entry); + + entry->symbols = (struct nlist *) xmalloc (entry->header.a_syms); + + lseek (desc, N_SYMOFF (entry->header) + entry->starting_offset, 0); + if (entry->header.a_syms != read (desc, entry->symbols, entry->header.a_syms)) + fatal_with_file ("premature end of file in symbols of ", entry); + + lseek (desc, N_STROFF (entry->header) + entry->starting_offset, 0); + if (sizeof str_size != read (desc, &str_size, sizeof str_size)) + fatal_with_file ("bad string table size in ", entry); + + entry->string_size = str_size; +} + +/* Read the string table of file ENTRY into core. + Assume it is already open, on descriptor DESC. + Also record whether a GDB symbol segment follows the string table. */ + +void +read_entry_strings (desc, entry) + struct file_entry *entry; + int desc; +{ + int buffer; + + if (!entry->header_read_flag) + read_header (desc, entry); + + lseek (desc, N_STROFF (entry->header) + entry->starting_offset, 0); + if (entry->string_size != read (desc, entry->strings, entry->string_size)) + fatal_with_file ("premature end of file in strings of ", entry); + + /* While we are here, see if the file has a symbol segment at the end. + For a separate file, just try reading some more. + For a library member, compare current pos against total size. */ + if (entry->superfile) + { + if (entry->total_size == N_STROFF (entry->header) + entry->string_size) + return; + } + else + { + buffer = read (desc, &buffer, sizeof buffer); + if (buffer == 0) + return; + if (buffer != sizeof buffer) + fatal_with_file ("premature end of file in GDB symbol segment of ", entry); + } + /* Don't try to do anything with symsegs. */ + return; +#if 0 + /* eliminate warning of `statement not reached'. */ + entry->symseg_offset = N_STROFF (entry->header) + entry->string_size; +#endif +} + +/* Read in the symbols of all input files. */ + +void read_file_symbols (), read_entry_symbols (), read_entry_strings (); +void enter_file_symbols (), enter_global_ref (), search_library (); + +void +load_symbols () +{ + register int i; + + if (trace_files) fprintf (stderr, "Loading symbols:\n\n"); + + for (i = 0; i < number_of_files; i++) + { + register struct file_entry *entry = &file_table[i]; + read_file_symbols (entry); + } + + if (trace_files) fprintf (stderr, "\n"); +} + +/* If ENTRY is a rel file, read its symbol and string sections into core. + If it is a library, search it and load the appropriate members + (which means calling this function recursively on those members). */ + +void +read_file_symbols (entry) + register struct file_entry *entry; +{ + register int desc; + register int len; + struct exec hdr; + + desc = file_open (entry); + +#ifdef COFF_ENCAPSULATE + if (entry->just_syms_flag) + lseek (desc, sizeof(coffheader),0); +#endif + + len = read (desc, &hdr, sizeof hdr); + if (len != sizeof hdr) + fatal_with_file ("failure reading header of ", entry); + + if (!N_BADMAG (hdr)) + { + read_entry_symbols (desc, entry); + entry->strings = (char *) alloca (entry->string_size); + read_entry_strings (desc, entry); + enter_file_symbols (entry); + entry->strings = 0; + } + else + { + char armag[SARMAG]; + + lseek (desc, 0, 0); + if (SARMAG != read (desc, armag, SARMAG) || strncmp (armag, ARMAG, SARMAG)) + fatal_with_file ("malformed input file (not rel or archive) ", entry); + entry->library_flag = 1; + search_library (desc, entry); + } + + file_close (); +} + +/* Enter the external symbol defs and refs of ENTRY in the hash table. */ + +void +enter_file_symbols (entry) + struct file_entry *entry; +{ + register struct nlist + *p, + *end = entry->symbols + entry->header.a_syms / sizeof (struct nlist); + + if (trace_files) prline_file_name (entry, stderr); + + for (p = entry->symbols; p < end; p++) + { + if (p->n_type == (N_SETV | N_EXT)) continue; + if (set_element_prefixes + && set_element_prefixed_p (p->n_un.n_strx + entry->strings)) + p->n_type += (N_SETA - N_ABS); + + if (SET_ELEMENT_P (p->n_type)) + { + set_symbol_count++; + if (!relocatable_output) + enter_global_ref (p, p->n_un.n_strx + entry->strings, entry); + } + else if (p->n_type == N_WARNING) + { + char *name = p->n_un.n_strx + entry->strings; + + /* Grab the next entry. */ + p++; + if (p->n_type != (N_UNDF | N_EXT)) + { + fprintf (stderr, "%s: Warning symbol found in %s without external reference following.\n", + progname, entry->filename); + make_executable = 0; + p--; /* Process normally. */ + } + else + { + symbol *sp; + char *sname = p->n_un.n_strx + entry->strings; + /* Deal with the warning symbol. */ + enter_global_ref (p, p->n_un.n_strx + entry->strings, entry); + sp = getsym (sname); + sp->warning = (char *) xmalloc (strlen(name) + 1); + strcpy (sp->warning, name); + warning_count++; + } + } + else if (p->n_type & N_EXT) + enter_global_ref (p, p->n_un.n_strx + entry->strings, entry); + else if (p->n_un.n_strx && !(p->n_type & (N_STAB | N_EXT))) + { + if ((p->n_un.n_strx + entry->strings)[0] != LPREFIX) + non_L_local_sym_count++; + local_sym_count++; + } + else debugger_sym_count++; + } + + /* Count one for the local symbol that we generate, + whose name is the file's name (usually) and whose address + is the start of the file's text. */ + + local_sym_count++; + non_L_local_sym_count++; +} + +/* Enter one global symbol in the hash table. + NLIST_P points to the `struct nlist' read from the file + that describes the global symbol. NAME is the symbol's name. + ENTRY is the file entry for the file the symbol comes from. + + The `struct nlist' is modified by placing it on a chain of + all such structs that refer to the same global symbol. + This chain starts in the `refs' field of the symbol table entry + and is chained through the `n_name'. */ + +void +enter_global_ref (nlist_p, name, entry) + register struct nlist *nlist_p; + char *name; + struct file_entry *entry; +{ + register symbol *sp = getsym (name); + register int type = nlist_p->n_type; + int oldref = sp->referenced; + int olddef = sp->defined; + int com = sp->defined && sp->max_common_size; + + nlist_p->n_un.n_name = (char *) sp->refs; + sp->refs = nlist_p; + + sp->referenced = 1; + if (type != (N_UNDF | N_EXT) || nlist_p->n_value) + { + if (!sp->defined || sp->defined == (N_UNDF | N_EXT)) + sp->defined = type; + + if (oldref && !olddef) + /* It used to be undefined and we're defining it. */ + undefined_global_sym_count--; + + if (!olddef && type == (N_UNDF | N_EXT) && nlist_p->n_value) + { + /* First definition and it's common. */ + common_defined_global_count++; + sp->max_common_size = nlist_p->n_value; + } + else if (com && type != (N_UNDF | N_EXT)) + { + /* It used to be common and we're defining it as + something else. */ + common_defined_global_count--; + sp->max_common_size = 0; + } + else if (com && type == (N_UNDF | N_EXT) + && sp->max_common_size < nlist_p->n_value) + /* It used to be common and this is a new common entry to + which we need to pay attention. */ + sp->max_common_size = nlist_p->n_value; + + /* Are we defining it as a set element? */ + if (SET_ELEMENT_P (type) && (!olddef || com)) + set_vector_count++; + /* As an indirection? */ + else if (type == (N_INDR | N_EXT)) + { + /* Indirect symbols value should be modified to point + a symbol being equivalenced to. */ + nlist_p->n_value + = (unsigned int) getsym ((nlist_p + 1)->n_un.n_strx + + entry->strings); + if ((symbol *) nlist_p->n_value == sp) + { + /* Somebody redefined a symbol to be itself. */ + fprintf (stderr, "%s: Symbol %s indirected to itself.\n", + entry->filename, name); + /* Rewrite this symbol as being a global text symbol + with value 0. */ + nlist_p->n_type = sp->defined = N_TEXT | N_EXT; + nlist_p->n_value = 0; + /* Don't make the output executable. */ + make_executable = 0; + } + else + global_indirect_count++; + } + } + else + if (!oldref) +#ifndef DOLLAR_KLUDGE + undefined_global_sym_count++; +#else + { + if (entry->superfile && type == (N_UNDF | N_EXT) && name[1] == '$') + { + /* This is an (ISI?) $-conditional; skip it */ + sp->referenced = 0; + if (sp->trace) + { + fprintf (stderr, "symbol %s is a $-conditional ignored in ", sp->name); + print_file_name (entry, stderr); + fprintf (stderr, "\n"); + } + return; + } + else + undefined_global_sym_count++; + } +#endif + + if (sp == end_symbol && entry->just_syms_flag && !T_flag_specified) + text_start = nlist_p->n_value; + + if (sp->trace) + { + register char *reftype; + switch (type & N_TYPE) + { + case N_UNDF: + if (nlist_p->n_value) + reftype = "defined as common"; + else reftype = "referenced"; + break; + + case N_ABS: + reftype = "defined as absolute"; + break; + + case N_TEXT: + reftype = "defined in text section"; + break; + + case N_DATA: + reftype = "defined in data section"; + break; + + case N_BSS: + reftype = "defined in BSS section"; + break; + + case N_SETT: + reftype = "is a text set element"; + break; + + case N_SETD: + reftype = "is a data set element"; + break; + + case N_SETB: + reftype = "is a BSS set element"; + break; + + case N_SETA: + reftype = "is an absolute set element"; + break; + + case N_SETV: + reftype = "defined in data section as vector"; + break; + + case N_INDR: + reftype = (char *) alloca (23 + + strlen ((nlist_p + 1)->n_un.n_strx + + entry->strings)); + sprintf (reftype, "defined equivalent to %s", + (nlist_p + 1)->n_un.n_strx + entry->strings); + break; + +#ifdef sequent + case N_SHUNDF: + reftype = "shared undf"; + break; + +/* These conflict with cases above. + case N_SHDATA: + reftype = "shared data"; + break; + + case N_SHBSS: + reftype = "shared BSS"; + break; +*/ + default: + reftype = "I don't know this type"; + break; +#endif + } + + fprintf (stderr, "symbol %s %s in ", sp->name, reftype); + print_file_name (entry, stderr); + fprintf (stderr, "\n"); + } +} + +/* This return 0 if the given file entry's symbol table does *not* + contain the nlist point entry, and it returns the files entry + pointer (cast to unsigned long) if it does. */ + +unsigned long +contains_symbol (entry, n_ptr) + struct file_entry *entry; + register struct nlist *n_ptr; +{ + if (n_ptr >= entry->symbols && + n_ptr < (entry->symbols + + (entry->header.a_syms / sizeof (struct nlist)))) + return (unsigned long) entry; + return 0; +} + + +/* Searching libraries */ + +struct file_entry *decode_library_subfile (); +void linear_library (), symdef_library (); + +/* Search the library ENTRY, already open on descriptor DESC. + This means deciding which library members to load, + making a chain of `struct file_entry' for those members, + and entering their global symbols in the hash table. */ + +void +search_library (desc, entry) + int desc; + struct file_entry *entry; +{ + int member_length; + register char *name; + register struct file_entry *subentry; + + if (!undefined_global_sym_count) return; + + /* Examine its first member, which starts SARMAG bytes in. */ + subentry = decode_library_subfile (desc, entry, SARMAG, &member_length); + if (!subentry) return; + + name = subentry->filename; + free (subentry); + + /* Search via __.SYMDEF if that exists, else linearly. */ + + if (!strcmp (name, "__.SYMDEF")) + symdef_library (desc, entry, member_length); + else + linear_library (desc, entry); +} + +/* Construct and return a file_entry for a library member. + The library's file_entry is library_entry, and the library is open on DESC. + SUBFILE_OFFSET is the byte index in the library of this member's header. + We store the length of the member into *LENGTH_LOC. */ + +struct file_entry * +decode_library_subfile (desc, library_entry, subfile_offset, length_loc) + int desc; + struct file_entry *library_entry; + int subfile_offset; + int *length_loc; +{ + int bytes_read; + register int namelen; + int member_length; + register char *name; + struct ar_hdr hdr1; + register struct file_entry *subentry; + + lseek (desc, subfile_offset, 0); + + bytes_read = read (desc, &hdr1, sizeof hdr1); + if (!bytes_read) + return 0; /* end of archive */ + + if (sizeof hdr1 != bytes_read) + fatal_with_file ("malformed library archive ", library_entry); + + if (sscanf (hdr1.ar_size, "%d", &member_length) != 1) + fatal_with_file ("malformatted header of archive member in ", library_entry); + + subentry = (struct file_entry *) xmalloc (sizeof (struct file_entry)); + bzero (subentry, sizeof (struct file_entry)); + + for (namelen = 0; + namelen < sizeof hdr1.ar_name + && hdr1.ar_name[namelen] != 0 && hdr1.ar_name[namelen] != ' ' + && hdr1.ar_name[namelen] != '/'; + namelen++); + + name = (char *) xmalloc (namelen+1); + strncpy (name, hdr1.ar_name, namelen); + name[namelen] = 0; + + subentry->filename = name; + subentry->local_sym_name = name; + subentry->symbols = 0; + subentry->strings = 0; + subentry->subfiles = 0; + subentry->starting_offset = subfile_offset + sizeof hdr1; + subentry->superfile = library_entry; + subentry->library_flag = 0; + subentry->header_read_flag = 0; + subentry->just_syms_flag = 0; + subentry->chain = 0; + subentry->total_size = member_length; + + (*length_loc) = member_length; + + return subentry; +} + +int subfile_wanted_p (); + +/* Search a library that has a __.SYMDEF member. + DESC is a descriptor on which the library is open. + The file pointer is assumed to point at the __.SYMDEF data. + ENTRY is the library's file_entry. + MEMBER_LENGTH is the length of the __.SYMDEF data. */ + +void +symdef_library (desc, entry, member_length) + int desc; + struct file_entry *entry; + int member_length; +{ + int *symdef_data = (int *) xmalloc (member_length); + register struct symdef *symdef_base; + char *sym_name_base; + int number_of_symdefs; + int length_of_strings; + int not_finished; + int bytes_read; + register int i; + struct file_entry *prev = 0; + int prev_offset = 0; + + bytes_read = read (desc, symdef_data, member_length); + if (bytes_read != member_length) + fatal_with_file ("malformatted __.SYMDEF in ", entry); + + number_of_symdefs = *symdef_data / sizeof (struct symdef); + if (number_of_symdefs < 0 || + number_of_symdefs * sizeof (struct symdef) + 2 * sizeof (int) > member_length) + fatal_with_file ("malformatted __.SYMDEF in ", entry); + + symdef_base = (struct symdef *) (symdef_data + 1); + length_of_strings = *(int *) (symdef_base + number_of_symdefs); + + if (length_of_strings < 0 + || number_of_symdefs * sizeof (struct symdef) + length_of_strings + + 2 * sizeof (int) > member_length) + fatal_with_file ("malformatted __.SYMDEF in ", entry); + + sym_name_base = sizeof (int) + (char *) (symdef_base + number_of_symdefs); + + /* Check all the string indexes for validity. */ + + for (i = 0; i < number_of_symdefs; i++) + { + register int index = symdef_base[i].symbol_name_string_index; + if (index < 0 || index >= length_of_strings + || (index && *(sym_name_base + index - 1))) + fatal_with_file ("malformatted __.SYMDEF in ", entry); + } + + /* Search the symdef data for members to load. + Do this until one whole pass finds nothing to load. */ + + not_finished = 1; + while (not_finished) + { + not_finished = 0; + + /* Scan all the symbols mentioned in the symdef for ones that we need. + Load the library members that contain such symbols. */ + + for (i = 0; + (i < number_of_symdefs + && (undefined_global_sym_count || common_defined_global_count)); + i++) + if (symdef_base[i].symbol_name_string_index >= 0) + { + register symbol *sp; + + sp = getsym_soft (sym_name_base + + symdef_base[i].symbol_name_string_index); + + /* If we find a symbol that appears to be needed, think carefully + about the archive member that the symbol is in. */ + + /* + * Per Mike Karels' recommendation, we no longer load library + * files if the only reference(s) that would be satisfied are + * 'common' references. This prevents some problems with name + * pollution (e.g. a global common 'utime' linked to a function). + */ + if (sp && sp->referenced && !sp->defined) + { + int junk; + register int j; + register int offset = symdef_base[i].library_member_offset; + struct file_entry *subentry; + + /* Don't think carefully about any archive member + more than once in a given pass. */ + + if (prev_offset == offset) + continue; + prev_offset = offset; + + /* Read the symbol table of the archive member. */ + + subentry = decode_library_subfile (desc, entry, offset, &junk); + if (subentry == 0) + fatal ("invalid offset for %s in symbol table of %s", + sym_name_base + + symdef_base[i].symbol_name_string_index, + entry->filename); + read_entry_symbols (desc, subentry); + subentry->strings = (char *) malloc (subentry->string_size); + read_entry_strings (desc, subentry); + + /* Now scan the symbol table and decide whether to load. */ + + if (!subfile_wanted_p (subentry)) + { + free (subentry->symbols); + free (subentry); + } + else + { + /* This member is needed; load it. + Since we are loading something on this pass, + we must make another pass through the symdef data. */ + + not_finished = 1; + + enter_file_symbols (subentry); + + if (prev) + prev->chain = subentry; + else entry->subfiles = subentry; + prev = subentry; + + /* Clear out this member's symbols from the symdef data + so that following passes won't waste time on them. */ + + for (j = 0; j < number_of_symdefs; j++) + { + if (symdef_base[j].library_member_offset == offset) + symdef_base[j].symbol_name_string_index = -1; + } + } + + /* We'll read the strings again if we need them again. */ + free (subentry->strings); + subentry->strings = 0; + } + } + } + + free (symdef_data); +} + +/* Search a library that has no __.SYMDEF. + ENTRY is the library's file_entry. + DESC is the descriptor it is open on. */ + +void +linear_library (desc, entry) + int desc; + struct file_entry *entry; +{ + register struct file_entry *prev = 0; + register int this_subfile_offset = SARMAG; + + while (undefined_global_sym_count || common_defined_global_count) + { + int member_length; + register struct file_entry *subentry; + + subentry = decode_library_subfile (desc, entry, this_subfile_offset, + &member_length); + + if (!subentry) return; + + read_entry_symbols (desc, subentry); + subentry->strings = (char *) alloca (subentry->string_size); + read_entry_strings (desc, subentry); + + if (!subfile_wanted_p (subentry)) + { + free (subentry->symbols); + free (subentry); + } + else + { + enter_file_symbols (subentry); + + if (prev) + prev->chain = subentry; + else entry->subfiles = subentry; + prev = subentry; + subentry->strings = 0; /* Since space will dissapear on return */ + } + + this_subfile_offset += member_length + sizeof (struct ar_hdr); + if (this_subfile_offset & 1) this_subfile_offset++; + } +} + +/* ENTRY is an entry for a library member. + Its symbols have been read into core, but not entered. + Return nonzero if we ought to load this member. */ + +int +subfile_wanted_p (entry) + struct file_entry *entry; +{ + register struct nlist *p; + register struct nlist *end + = entry->symbols + entry->header.a_syms / sizeof (struct nlist); +#ifdef DOLLAR_KLUDGE + register int dollar_cond = 0; +#endif + + for (p = entry->symbols; p < end; p++) + { + register int type = p->n_type; + register char *name = p->n_un.n_strx + entry->strings; + + /* If the symbol has an interesting definition, we could + potentially want it. */ + if (type & N_EXT + && (type != (N_UNDF | N_EXT) || p->n_value + +#ifdef DOLLAR_KLUDGE + || name[1] == '$' +#endif + ) + && !SET_ELEMENT_P (type) + && !set_element_prefixed_p (name)) + { + register symbol *sp = getsym_soft (name); + +#ifdef DOLLAR_KLUDGE + if (name[1] == '$') + { + sp = getsym_soft (&name[2]); + dollar_cond = 1; + if (!sp) continue; + if (sp->referenced) + { + if (write_map) + { + print_file_name (entry, stdout); + fprintf (stdout, " needed due to $-conditional %s\n", name); + } + return 1; + } + continue; + } +#endif + + /* If this symbol has not been hashed, we can't be looking for it. */ + + if (!sp) continue; + + /* + * We don't load a file if it merely satisfies a common reference + * (see explanation above in symdef_library()). + */ + if (sp->referenced && !sp->defined) + { + /* This is a symbol we are looking for. It is either + not yet defined or defined as a common. */ +#ifdef DOLLAR_KLUDGE + if (dollar_cond) continue; +#endif + if (type == (N_UNDF | N_EXT)) + { + /* Symbol being defined as common. + Remember this, but don't load subfile just for this. */ + + /* If it didn't used to be common, up the count of + common symbols. */ + if (!sp->max_common_size) + common_defined_global_count++; + + if (sp->max_common_size < p->n_value) + sp->max_common_size = p->n_value; + if (!sp->defined) + undefined_global_sym_count--; + sp->defined = 1; + continue; + } + + if (write_map) + { + print_file_name (entry, stdout); + fprintf (stdout, " needed due to %s\n", sp->name); + } + return 1; + } + } + } + + return 0; +} + +void consider_file_section_lengths (), relocate_file_addresses (); + +/* Having entered all the global symbols and found the sizes of sections + of all files to be linked, make all appropriate deductions from this data. + + We propagate global symbol values from definitions to references. + We compute the layout of the output file and where each input file's + contents fit into it. */ + +void +digest_symbols () +{ + register int i; + int setv_fill_count; + + if (trace_files) + fprintf (stderr, "Digesting symbol information:\n\n"); + + /* Compute total size of sections */ + + each_file (consider_file_section_lengths, 0); + + /* If necessary, pad text section to full page in the file. + Include the padding in the text segment size. */ + + if (magic == ZMAGIC) + { + int text_end = text_size + N_TXTOFF (outheader); + text_pad = ((text_end + page_size - 1) & (- page_size)) - text_end; + text_size += text_pad; + } + if (padtext) + { + int text_end = text_size; + text_pad = ((text_end + page_size - 1) & (- page_size)) - text_end; + text_size += text_pad; + } + +#ifdef _N_BASEADDR + /* SunOS 4.1 N_TXTADDR depends on the value of outheader.a_entry. */ + outheader.a_entry = N_PAGSIZ (outheader); +#endif + + outheader.a_text = text_size; +#ifdef sequent + outheader.a_text += N_ADDRADJ (outheader); +#endif + + /* Make the data segment address start in memory on a suitable boundary. */ + + if (! Tdata_flag_specified) + data_start = N_DATADDR (outheader) + text_start - TEXT_START (outheader); + + /* Set up the set element vector */ + + if (!relocatable_output) + { + /* The set sector size is the number of set elements + a word + for each symbol for the length word at the beginning of the + vector, plus a word for each symbol for a zero at the end of + the vector (for incremental linking). */ + set_sect_size + = (2 * set_symbol_count + set_vector_count) * sizeof (unsigned long); + set_sect_start = data_start + data_size; + data_size += set_sect_size; + set_vectors = (unsigned long *) xmalloc (set_sect_size); + setv_fill_count = 0; + } + + /* Compute start addresses of each file's sections and symbols. */ + + each_full_file (relocate_file_addresses, 0); + + /* Now, for each symbol, verify that it is defined globally at most once. + Put the global value into the symbol entry. + Common symbols are allocated here, in the BSS section. + Each defined symbol is given a '->defined' field + which is the correct N_ code for its definition, + except in the case of common symbols with -r. + Then make all the references point at the symbol entry + instead of being chained together. */ + + defined_global_sym_count = 0; + + for (i = 0; i < TABSIZE; i++) + { + register symbol *sp; + for (sp = symtab[i]; sp; sp = sp->link) + { + /* For each symbol */ + register struct nlist *p, *next; + int defs = 0, com = sp->max_common_size; + struct nlist *first_definition; + for (p = sp->refs; p; p = next) + { + register int type = p->n_type; + + if (SET_ELEMENT_P (type)) + { + if (relocatable_output) + fatal ("internal: global ref to set element with -r"); + if (!defs++) + { + sp->value = set_sect_start + + setv_fill_count++ * sizeof (unsigned long); + sp->defined = N_SETV | N_EXT; + first_definition = p; + } + else if ((sp->defined & ~N_EXT) != N_SETV) + { + sp->multiply_defined = 1; + multiple_def_count++; + } + set_vectors[setv_fill_count++] = p->n_value; + } + else if ((type & N_EXT) && type != (N_UNDF | N_EXT)) + { + /* non-common definition */ + if (defs++ && sp->value != p->n_value) + { + sp->multiply_defined = 1; + multiple_def_count++; + } + sp->value = p->n_value; + sp->defined = type; + first_definition = p; + } + next = (struct nlist *) p->n_un.n_name; + p->n_un.n_name = (char *) sp; + } + /* Allocate as common if defined as common and not defined for real */ + if (com && !defs) + { + if (!relocatable_output || force_common_definition) + { + int align = sizeof (int); + + /* Round up to nearest sizeof (int). I don't know + whether this is necessary or not (given that + alignment is taken care of later), but it's + traditional, so I'll leave it in. Note that if + this size alignment is ever removed, ALIGN above + will have to be initialized to 1 instead of + sizeof (int). */ + + com = (com + sizeof (int) - 1) & (- sizeof (int)); + + while (!(com & align)) + align <<= 1; + + align = align > MAX_ALIGNMENT ? MAX_ALIGNMENT : align; + + bss_size = ((((bss_size + data_size + data_start) + + (align - 1)) & (- align)) + - data_size - data_start); + + sp->value = data_start + data_size + bss_size; + sp->defined = N_BSS | N_EXT; + bss_size += com; + if (write_map) + printf ("Allocating common %s: %x at %x\n", + sp->name, com, sp->value); + } + else + { + sp->defined = 0; + undefined_global_sym_count++; + } + } + /* Set length word at front of vector and zero byte at end. + Reverse the vector itself to put it in file order. */ + if ((sp->defined & ~N_EXT) == N_SETV) + { + unsigned long length_word_index + = (sp->value - set_sect_start) / sizeof (unsigned long); + unsigned long i, tmp; + + set_vectors[length_word_index] + = setv_fill_count - 1 - length_word_index; + + /* Reverse the vector. */ + for (i = 1; + i < (setv_fill_count - length_word_index - 1) / 2 + 1; + i++) + { + tmp = set_vectors[length_word_index + i]; + set_vectors[length_word_index + i] + = set_vectors[setv_fill_count - i]; + set_vectors[setv_fill_count - i] = tmp; + } + + set_vectors[setv_fill_count++] = 0; + } + if (sp->defined) + defined_global_sym_count++; + } + } + + if (end_symbol) /* These are null if -r. */ + { + etext_symbol->value = text_size + text_start; + edata_symbol->value = data_start + data_size; + end_symbol->value = data_start + data_size + bss_size; + } + + /* Figure the data_pad now, so that it overlaps with the bss addresses. */ + + if (specified_data_size && specified_data_size > data_size) + data_pad = specified_data_size - data_size; + + if (magic == ZMAGIC) + data_pad = ((data_pad + data_size + page_size - 1) & (- page_size)) + - data_size; + + bss_size -= data_pad; + if (bss_size < 0) bss_size = 0; + + data_size += data_pad; +} + +/* Accumulate the section sizes of input file ENTRY + into the section sizes of the output file. */ + +void +consider_file_section_lengths (entry) + register struct file_entry *entry; +{ + if (entry->just_syms_flag) + return; + + entry->text_start_address = text_size; + /* If there were any vectors, we need to chop them off */ + text_size += entry->header.a_text; + entry->data_start_address = data_size; + data_size += entry->header.a_data; + entry->bss_start_address = bss_size; + bss_size += entry->header.a_bss; + + text_reloc_size += entry->header.a_trsize; + data_reloc_size += entry->header.a_drsize; +} + +/* Determine where the sections of ENTRY go into the output file, + whose total section sizes are already known. + Also relocate the addresses of the file's local and debugger symbols. */ + +void +relocate_file_addresses (entry) + register struct file_entry *entry; +{ + entry->text_start_address += text_start; + /* Note that `data_start' and `data_size' have not yet been + adjusted for `data_pad'. If they had been, we would get the wrong + results here. */ + entry->data_start_address += data_start; + entry->bss_start_address += data_start + data_size; + + { + register struct nlist *p; + register struct nlist *end + = entry->symbols + entry->header.a_syms / sizeof (struct nlist); + + for (p = entry->symbols; p < end; p++) + { + /* If this belongs to a section, update it by the section's start address */ + register int type = p->n_type & N_TYPE; + + switch (type) + { + case N_TEXT: + case N_SETT: + p->n_value += entry->text_start_address; + break; + case N_DATA: + case N_SETV: + case N_SETD: + /* A symbol whose value is in the data section + is present in the input file as if the data section + started at an address equal to the length of the file's text. */ + p->n_value += entry->data_start_address - entry->header.a_text; + break; + case N_BSS: + case N_SETB: + /* likewise for symbols with value in BSS. */ + p->n_value += entry->bss_start_address + - entry->header.a_text - entry->header.a_data; + break; + } + } + } +} + +void describe_file_sections (), list_file_locals (); + +/* Print a complete or partial map of the output file. */ + +void +print_symbols (outfile) + FILE *outfile; +{ + register int i; + + fprintf (outfile, "\nFiles:\n\n"); + + each_file (describe_file_sections, outfile); + + fprintf (outfile, "\nGlobal symbols:\n\n"); + + for (i = 0; i < TABSIZE; i++) + { + register symbol *sp; + for (sp = symtab[i]; sp; sp = sp->link) + { + if (sp->defined == 1) + fprintf (outfile, " %s: common, length 0x%x\n", sp->name, sp->max_common_size); + if (sp->defined) + fprintf (outfile, " %s: 0x%x\n", sp->name, sp->value); + else if (sp->referenced) + fprintf (outfile, " %s: undefined\n", sp->name); + } + } + + each_file (list_file_locals, outfile); +} + +void +describe_file_sections (entry, outfile) + struct file_entry *entry; + FILE *outfile; +{ + fprintf (outfile, " "); + print_file_name (entry, outfile); + if (entry->just_syms_flag) + fprintf (outfile, " symbols only\n", 0); + else + fprintf (outfile, " text %x(%x), data %x(%x), bss %x(%x) hex\n", + entry->text_start_address, entry->header.a_text, + entry->data_start_address, entry->header.a_data, + entry->bss_start_address, entry->header.a_bss); +} + +void +list_file_locals (entry, outfile) + struct file_entry *entry; + FILE *outfile; +{ + register struct nlist + *p, + *end = entry->symbols + entry->header.a_syms / sizeof (struct nlist); + + entry->strings = (char *) alloca (entry->string_size); + read_entry_strings (file_open (entry), entry); + + fprintf (outfile, "\nLocal symbols of "); + print_file_name (entry, outfile); + fprintf (outfile, ":\n\n"); + + for (p = entry->symbols; p < end; p++) + /* If this is a definition, + update it if necessary by this file's start address. */ + if (!(p->n_type & (N_STAB | N_EXT))) + fprintf (outfile, " %s: 0x%x\n", + entry->strings + p->n_un.n_strx, p->n_value); + + entry->strings = 0; /* All done with them. */ +} + + +/* Static vars for do_warnings and subroutines of it */ +int list_unresolved_refs; /* List unresolved refs */ +int list_warning_symbols; /* List warning syms */ +int list_multiple_defs; /* List multiple definitions */ + +/* + * Structure for communication between do_file_warnings and it's + * helper routines. Will in practice be an array of three of these: + * 0) Current line, 1) Next line, 2) Source file info. + */ +struct line_debug_entry +{ + int line; + char *filename; + struct nlist *sym; +}; + +void qsort (); +/* + * Helper routines for do_file_warnings. + */ + +/* Return an integer less than, equal to, or greater than 0 as per the + relation between the two relocation entries. Used by qsort. */ + +int +relocation_entries_relation (rel1, rel2) + struct relocation_info *rel1, *rel2; +{ + return RELOC_ADDRESS(rel1) - RELOC_ADDRESS(rel2); +} + +/* Moves to the next debugging symbol in the file. USE_DATA_SYMBOLS + determines the type of the debugging symbol to look for (DSLINE or + SLINE). STATE_POINTER keeps track of the old and new locatiosn in + the file. It assumes that state_pointer[1] is valid; ie + that it.sym points into some entry in the symbol table. If + state_pointer[1].sym == 0, this routine should not be called. */ + +int +next_debug_entry (use_data_symbols, state_pointer) + register int use_data_symbols; + /* Next must be passed by reference! */ + struct line_debug_entry state_pointer[3]; +{ + register struct line_debug_entry + *current = state_pointer, + *next = state_pointer + 1, + /* Used to store source file */ + *source = state_pointer + 2; + struct file_entry *entry = (struct file_entry *) source->sym; + + current->sym = next->sym; + current->line = next->line; + current->filename = next->filename; + + while (++(next->sym) < (entry->symbols + + entry->header.a_syms/sizeof (struct nlist))) + { + /* n_type is a char, and N_SOL, N_EINCL and N_BINCL are > 0x80, so + * may look negative...therefore, must mask to low bits + */ + switch (next->sym->n_type & 0xff) + { + case N_SLINE: + if (use_data_symbols) continue; + next->line = next->sym->n_desc; + return 1; + case N_DSLINE: + if (!use_data_symbols) continue; + next->line = next->sym->n_desc; + return 1; +#ifdef HAVE_SUN_STABS + case N_EINCL: + next->filename = source->filename; + continue; +#endif + case N_SO: + source->filename = next->sym->n_un.n_strx + entry->strings; + source->line++; +#ifdef HAVE_SUN_STABS + case N_BINCL: +#endif + case N_SOL: + next->filename + = next->sym->n_un.n_strx + entry->strings; + default: + continue; + } + } + next->sym = (struct nlist *) 0; + return 0; +} + +/* Create a structure to save the state of a scan through the debug + symbols. USE_DATA_SYMBOLS is set if we should be scanning for + DSLINE's instead of SLINE's. entry is the file entry which points + at the symbols to use. */ + +struct line_debug_entry * +init_debug_scan (use_data_symbols, entry) + int use_data_symbols; + struct file_entry *entry; +{ + struct line_debug_entry + *state_pointer + = (struct line_debug_entry *) + xmalloc (3 * sizeof (struct line_debug_entry)); + register struct line_debug_entry + *current = state_pointer, + *next = state_pointer + 1, + *source = state_pointer + 2; /* Used to store source file */ + + struct nlist *tmp; + + for (tmp = entry->symbols; + tmp < (entry->symbols + + entry->header.a_syms/sizeof (struct nlist)); + tmp++) + if (tmp->n_type == (int) N_SO) + break; + + if (tmp >= (entry->symbols + + entry->header.a_syms/sizeof (struct nlist))) + { + /* I believe this translates to "We lose" */ + current->filename = next->filename = entry->filename; + current->line = next->line = -1; + current->sym = next->sym = (struct nlist *) 0; + return state_pointer; + } + + next->line = source->line = 0; + next->filename = source->filename + = (tmp->n_un.n_strx + entry->strings); + source->sym = (struct nlist *) entry; + next->sym = tmp; + + next_debug_entry (use_data_symbols, state_pointer); /* To setup next */ + + if (!next->sym) /* No line numbers for this section; */ + /* setup output results as appropriate */ + { + if (source->line) + { + current->filename = source->filename = entry->filename; + current->line = -1; /* Don't print lineno */ + } + else + { + current->filename = source->filename; + current->line = 0; + } + return state_pointer; + } + + + next_debug_entry (use_data_symbols, state_pointer); /* To setup current */ + + return state_pointer; +} + +/* Takes an ADDRESS (in either text or data space) and a STATE_POINTER + which describes the current location in the implied scan through + the debug symbols within the file which ADDRESS is within, and + returns the source line number which corresponds to ADDRESS. */ + +int +address_to_line (address, state_pointer) + unsigned long address; + /* Next must be passed by reference! */ + struct line_debug_entry state_pointer[3]; +{ + struct line_debug_entry + *current = state_pointer, + *next = state_pointer + 1; + struct line_debug_entry *tmp_pointer; + + int use_data_symbols; + + if (next->sym) + use_data_symbols = (next->sym->n_type & N_TYPE) == N_DATA; + else + return current->line; + + /* Go back to the beginning if we've already passed it. */ + if (current->sym->n_value > address) + { + tmp_pointer = init_debug_scan (use_data_symbols, + (struct file_entry *) + ((state_pointer + 2)->sym)); + state_pointer[0] = tmp_pointer[0]; + state_pointer[1] = tmp_pointer[1]; + state_pointer[2] = tmp_pointer[2]; + free (tmp_pointer); + } + + /* If we're still in a bad way, return -1, meaning invalid line. */ + if (current->sym->n_value > address) + return -1; + + while (next->sym + && next->sym->n_value <= address + && next_debug_entry (use_data_symbols, state_pointer)) + ; + return current->line; +} + + +/* Macros for manipulating bitvectors. */ +#define BIT_SET_P(bv, index) ((bv)[(index) >> 3] & 1 << ((index) & 0x7)) +#define SET_BIT(bv, index) ((bv)[(index) >> 3] |= 1 << ((index) & 0x7)) + +/* This routine will scan through the relocation data of file ENTRY, + printing out references to undefined symbols and references to + symbols defined in files with N_WARNING symbols. If DATA_SEGMENT + is non-zero, it will scan the data relocation segment (and use + N_DSLINE symbols to track line number); otherwise it will scan the + text relocation segment. Warnings will be printed on the output + stream OUTFILE. Eventually, every nlist symbol mapped through will + be marked in the NLIST_BITVECTOR, so we don't repeat ourselves when + we scan the nlists themselves. */ + +do_relocation_warnings (entry, data_segment, outfile, nlist_bitvector) + struct file_entry *entry; + int data_segment; + FILE *outfile; + unsigned char *nlist_bitvector; +{ + struct relocation_info + *reloc_start = data_segment ? entry->datarel : entry->textrel, + *reloc; + int reloc_size + = ((data_segment ? entry->header.a_drsize : entry->header.a_trsize) + / sizeof (struct relocation_info)); + int start_of_segment + = (data_segment ? entry->data_start_address : entry->text_start_address); + struct nlist *start_of_syms = entry->symbols; + struct line_debug_entry *state_pointer + = init_debug_scan (data_segment != 0, entry); + register struct line_debug_entry + *current = state_pointer; + /* Assigned to generally static values; should not be written into. */ + char *errfmt; + /* Assigned to alloca'd values cand copied into; should be freed + when done. */ + char *errmsg; + int invalidate_line_number; + + /* We need to sort the relocation info here. Sheesh, so much effort + for one lousy error optimization. */ + + qsort (reloc_start, reloc_size, sizeof (struct relocation_info), + relocation_entries_relation); + + for (reloc = reloc_start; + reloc < (reloc_start + reloc_size); + reloc++) + { + register struct nlist *s; + register symbol *g; + + /* If the relocation isn't resolved through a symbol, continue */ + if (!RELOC_EXTERN_P(reloc)) + continue; + + s = &(entry->symbols[RELOC_SYMBOL(reloc)]); + + /* Local symbols shouldn't ever be used by relocation info, so + the next should be safe. + This is, of course, wrong. References to local BSS symbols can be + the targets of relocation info, and they can (must) be + resolved through symbols. However, these must be defined properly, + (the assembler would have caught it otherwise), so we can + ignore these cases. */ + if (!(s->n_type & N_EXT)) + continue; + + g = (symbol *) s->n_un.n_name; + errmsg = 0; + + if (!g->defined && list_unresolved_refs) /* Reference */ + { + /* Mark as being noted by relocation warning pass. */ + SET_BIT (nlist_bitvector, s - start_of_syms); + + if (g->undef_refs >= MAX_UREFS_PRINTED) /* Listed too many */ + continue; + + /* Undefined symbol which we should mention */ + + if (++(g->undef_refs) == MAX_UREFS_PRINTED) + { + errfmt = "More undefined symbol %s refs follow"; + invalidate_line_number = 1; + } + else + { + errfmt = "Undefined symbol %s referenced from %s segment"; + invalidate_line_number = 0; + } + } + else /* Defined */ + { + /* Potential symbol warning here */ + if (!g->warning) continue; + + /* Mark as being noted by relocation warning pass. */ + SET_BIT (nlist_bitvector, s - start_of_syms); + + errfmt = 0; + errmsg = g->warning; + invalidate_line_number = 0; + } + + + /* If errfmt == 0, errmsg has already been defined. */ + if (errfmt != 0) + { + char *nm; + + if (demangler == NULL || (nm = (*demangler)(g->name)) == NULL) + nm = g->name; + errmsg = (char *) xmalloc (strlen (errfmt) + strlen (nm) + 1); + sprintf (errmsg, errfmt, nm, data_segment ? "data" : "text"); + if (nm != g->name) + free (nm); + } + + address_to_line (RELOC_ADDRESS (reloc) + start_of_segment, + state_pointer); + + if (current->line >=0) + fprintf (outfile, "%s:%d: %s\n", current->filename, + invalidate_line_number ? 0 : current->line, errmsg); + else + fprintf (outfile, "%s: %s\n", current->filename, errmsg); + + if (errfmt != 0) + free (errmsg); + } + + free (state_pointer); +} + +/* Print on OUTFILE a list of all warnings generated by references + and/or definitions in the file ENTRY. List source file and line + number if possible, just the .o file if not. */ + +void +do_file_warnings (entry, outfile) + struct file_entry *entry; + FILE *outfile; +{ + int number_of_syms = entry->header.a_syms / sizeof (struct nlist); + unsigned char *nlist_bitvector + = (unsigned char *) alloca ((number_of_syms >> 3) + 1); + struct line_debug_entry *text_scan, *data_scan; + int i; + char *errfmt, *file_name; + int line_number; + int dont_allow_symbol_name; + + bzero (nlist_bitvector, (number_of_syms >> 3) + 1); + + /* Read in the files strings if they aren't available */ + if (!entry->strings) + { + int desc; + + entry->strings = (char *) alloca (entry->string_size); + desc = file_open (entry); + read_entry_strings (desc, entry); + } + + read_file_relocation (entry); + + /* Do text warnings based on a scan through the relocation info. */ + do_relocation_warnings (entry, 0, outfile, nlist_bitvector); + + /* Do data warnings based on a scan through the relocation info. */ + do_relocation_warnings (entry, 1, outfile, nlist_bitvector); + + /* Scan through all of the nlist entries in this file and pick up + anything that the scan through the relocation stuff didn't. */ + + text_scan = init_debug_scan (0, entry); + data_scan = init_debug_scan (1, entry); + + for (i = 0; i < number_of_syms; i++) + { + struct nlist *s; + struct glosym *g; + + s = entry->symbols + i; + + if (!(s->n_type & N_EXT)) + continue; + + g = (symbol *) s->n_un.n_name; + dont_allow_symbol_name = 0; + + if (list_multiple_defs && g->multiply_defined) + { + errfmt = "Definition of symbol %s (multiply defined)"; + switch (s->n_type) + { + case N_TEXT | N_EXT: + line_number = address_to_line (s->n_value, text_scan); + file_name = text_scan[0].filename; + break; + case N_DATA | N_EXT: + line_number = address_to_line (s->n_value, data_scan); + file_name = data_scan[0].filename; + break; + case N_SETA | N_EXT: + case N_SETT | N_EXT: + case N_SETD | N_EXT: + case N_SETB | N_EXT: + if (g->multiply_defined == 2) + continue; + errfmt = "First set element definition of symbol %s (multiply defined)"; + break; + default: + continue; /* Don't print out multiple defs + at references. */ + } + } + else if (BIT_SET_P (nlist_bitvector, i)) + continue; + else if (list_unresolved_refs && !g->defined) + { + if (g->undef_refs >= MAX_UREFS_PRINTED) + continue; + + if (++(g->undef_refs) == MAX_UREFS_PRINTED) + errfmt = "More undefined \"%s\" refs follow"; + else + errfmt = "Undefined symbol \"%s\" referenced"; + line_number = -1; + } + else if (g->warning) + { + /* There are two cases in which we don't want to + do this. The first is if this is a definition instead of + a reference. The second is if it's the reference used by + the warning stabs itself. */ + if (s->n_type != (N_EXT | N_UNDF) + || (i && (s-1)->n_type == N_WARNING)) + continue; + + errfmt = g->warning; + line_number = -1; + dont_allow_symbol_name = 1; + } + else + continue; + + if (line_number == -1) + fprintf (outfile, "%s: ", entry->filename); + else + fprintf (outfile, "%s:%d: ", file_name, line_number); + + if (dont_allow_symbol_name) + fprintf (outfile, "%s", errfmt); + else + { + char *nm; + if (demangler != NULL && (nm = (*demangler)(g->name)) != NULL) + { + fprintf (outfile, errfmt, nm); + free (nm); + } + else + fprintf (outfile, errfmt, g->name); + } + + fputc ('\n', outfile); + } + free (text_scan); + free (data_scan); + entry->strings = 0; /* Since it will dissapear anyway. */ +} + +do_warnings (outfile) + FILE *outfile; +{ + list_unresolved_refs = !relocatable_output && undefined_global_sym_count; + list_warning_symbols = warning_count; + list_multiple_defs = multiple_def_count != 0; + + if (!(list_unresolved_refs || + list_warning_symbols || + list_multiple_defs )) + /* No need to run this routine */ + return; + + each_file (do_file_warnings, outfile); + + if (list_unresolved_refs || list_multiple_defs) + make_executable = 0; +} + +/* Write the output file */ + +void +write_output () +{ + struct stat statbuf; + int filemode; + + (void) unlink (output_filename); + outdesc = open (output_filename, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (outdesc < 0) perror_name (output_filename); + + if (fstat (outdesc, &statbuf) < 0) + perror_name (output_filename); + + (void) fchflags(outdesc, statbuf.st_flags | UF_NODUMP); + + filemode = statbuf.st_mode; + (void) fchmod (outdesc, filemode & ~0111); + + /* Output the a.out header. */ + write_header (); + + /* Output the text and data segments, relocating as we go. */ + write_text (); + write_data (); + + /* Output the merged relocation info, if requested with `-r'. */ + if (relocatable_output) + write_rel (); + + /* Output the symbol table (both globals and locals). */ + write_syms (); + + /* Copy any GDB symbol segments from input files. */ + write_symsegs (); + + if (fchmod (outdesc, filemode | 0111) == -1) + perror_name (output_filename); + + close (outdesc); +} + +void modify_location (), perform_relocation (), copy_text (), copy_data (); + +void +write_header () +{ + N_SET_MAGIC (outheader, magic); + outheader.a_text = text_size; +#ifdef sequent + outheader.a_text += N_ADDRADJ (outheader); + if (entry_symbol == 0) + entry_symbol = getsym("start"); +#endif + outheader.a_data = data_size; + outheader.a_bss = bss_size; + outheader.a_entry = (entry_symbol ? entry_symbol->value + : text_start + entry_offset); +#ifdef COFF_ENCAPSULATE + if (need_coff_header) + { + /* We are encapsulating BSD format within COFF format. */ + struct coffscn *tp, *dp, *bp; + + tp = &coffheader.scns[0]; + dp = &coffheader.scns[1]; + bp = &coffheader.scns[2]; + + strcpy (tp->s_name, ".text"); + tp->s_paddr = text_start; + tp->s_vaddr = text_start; + tp->s_size = text_size; + tp->s_scnptr = sizeof (struct coffheader) + sizeof (struct exec); + tp->s_relptr = 0; + tp->s_lnnoptr = 0; + tp->s_nreloc = 0; + tp->s_nlnno = 0; + tp->s_flags = 0x20; + strcpy (dp->s_name, ".data"); + dp->s_paddr = data_start; + dp->s_vaddr = data_start; + dp->s_size = data_size; + dp->s_scnptr = tp->s_scnptr + tp->s_size; + dp->s_relptr = 0; + dp->s_lnnoptr = 0; + dp->s_nreloc = 0; + dp->s_nlnno = 0; + dp->s_flags = 0x40; + strcpy (bp->s_name, ".bss"); + bp->s_paddr = dp->s_vaddr + dp->s_size; + bp->s_vaddr = bp->s_paddr; + bp->s_size = bss_size; + bp->s_scnptr = 0; + bp->s_relptr = 0; + bp->s_lnnoptr = 0; + bp->s_nreloc = 0; + bp->s_nlnno = 0; + bp->s_flags = 0x80; + + coffheader.f_magic = COFF_MAGIC; + coffheader.f_nscns = 3; + /* store an unlikely time so programs can + * tell that there is a bsd header + */ + coffheader.f_timdat = 1; + coffheader.f_symptr = 0; + coffheader.f_nsyms = 0; + coffheader.f_opthdr = 28; + coffheader.f_flags = 0x103; + /* aouthdr */ + coffheader.magic = ZMAGIC; + coffheader.vstamp = 0; + coffheader.tsize = tp->s_size; + coffheader.dsize = dp->s_size; + coffheader.bsize = bp->s_size; + coffheader.entry = outheader.a_entry; + coffheader.text_start = tp->s_vaddr; + coffheader.data_start = dp->s_vaddr; + } +#endif + +#ifdef INITIALIZE_HEADER + INITIALIZE_HEADER; +#endif + + if (strip_symbols == STRIP_ALL) + nsyms = 0; + else + { + nsyms = (defined_global_sym_count + + undefined_global_sym_count); + if (discard_locals == DISCARD_L) + nsyms += non_L_local_sym_count; + else if (discard_locals == DISCARD_NONE) + nsyms += local_sym_count; + /* One extra for following reference on indirects */ + if (relocatable_output) + nsyms += set_symbol_count + global_indirect_count; + } + + if (strip_symbols == STRIP_NONE) + nsyms += debugger_sym_count; + + outheader.a_syms = nsyms * sizeof (struct nlist); + + if (relocatable_output) + { + outheader.a_trsize = text_reloc_size; + outheader.a_drsize = data_reloc_size; + } + else + { + outheader.a_trsize = 0; + outheader.a_drsize = 0; + } + +#ifdef COFF_ENCAPSULATE + if (need_coff_header) + mywrite (&coffheader, sizeof coffheader, 1, outdesc); +#endif + mywrite (&outheader, sizeof (struct exec), 1, outdesc); + + /* Output whatever padding is required in the executable file + between the header and the start of the text. */ + +#ifndef COFF_ENCAPSULATE + padfile (N_TXTOFF (outheader) - sizeof outheader, outdesc); +#endif +} + +/* Relocate the text segment of each input file + and write to the output file. */ + +void +write_text () +{ + if (trace_files) + fprintf (stderr, "Copying and relocating text:\n\n"); + + each_full_file (copy_text, 0); + file_close (); + + if (trace_files) + fprintf (stderr, "\n"); + + padfile (text_pad, outdesc); +} + +int +text_offset (entry) + struct file_entry *entry; +{ + return entry->starting_offset + N_TXTOFF (entry->header); +} + +/* Read in all of the relocation information */ + +void +read_relocation () +{ + each_full_file (read_file_relocation, 0); +} + +/* Read in the relocation sections of ENTRY if necessary */ + +void +read_file_relocation (entry) + struct file_entry *entry; +{ + register struct relocation_info *reloc; + int desc; + int read_return; + + desc = -1; + if (!entry->textrel) + { + reloc = (struct relocation_info *) xmalloc (entry->header.a_trsize); + desc = file_open (entry); + lseek (desc, + text_offset (entry) + entry->header.a_text + entry->header.a_data, + L_SET); + if (entry->header.a_trsize != (read_return = read (desc, reloc, entry->header.a_trsize))) + { + fprintf (stderr, "Return from read: %d\n", read_return); + fatal_with_file ("premature eof in text relocation of ", entry); + } + entry->textrel = reloc; + } + + if (!entry->datarel) + { + reloc = (struct relocation_info *) xmalloc (entry->header.a_drsize); + if (desc == -1) desc = file_open (entry); + lseek (desc, + text_offset (entry) + entry->header.a_text + + entry->header.a_data + entry->header.a_trsize, + L_SET); + if (entry->header.a_drsize != read (desc, reloc, entry->header.a_drsize)) + fatal_with_file ("premature eof in data relocation of ", entry); + entry->datarel = reloc; + } +} + +/* Read the text segment contents of ENTRY, relocate them, + and write the result to the output file. + If `-r', save the text relocation for later reuse. */ + +void +copy_text (entry) + struct file_entry *entry; +{ + register char *bytes; + register int desc; + register struct relocation_info *reloc; + + if (trace_files) + prline_file_name (entry, stderr); + + desc = file_open (entry); + + /* Allocate space for the file's text section */ + + bytes = (char *) alloca (entry->header.a_text); + + /* Deal with relocation information however is appropriate */ + + if (entry->textrel) reloc = entry->textrel; + else if (relocatable_output) + { + read_file_relocation (entry); + reloc = entry->textrel; + } + else + { + reloc = (struct relocation_info *) alloca (entry->header.a_trsize); + lseek (desc, text_offset (entry) + entry->header.a_text + entry->header.a_data, 0); + if (entry->header.a_trsize != read (desc, reloc, entry->header.a_trsize)) + fatal_with_file ("premature eof in text relocation of ", entry); + } + + /* Read the text section into core. */ + + lseek (desc, text_offset (entry), 0); + if (entry->header.a_text != read (desc, bytes, entry->header.a_text)) + fatal_with_file ("premature eof in text section of ", entry); + + + /* Relocate the text according to the text relocation. */ + + perform_relocation (bytes, entry->text_start_address, entry->header.a_text, + reloc, entry->header.a_trsize, entry); + + /* Write the relocated text to the output file. */ + + mywrite (bytes, 1, entry->header.a_text, outdesc); +} + +/* Relocate the data segment of each input file + and write to the output file. */ + +void +write_data () +{ + if (trace_files) + fprintf (stderr, "Copying and relocating data:\n\n"); + + each_full_file (copy_data, 0); + file_close (); + + /* Write out the set element vectors. See digest symbols for + description of length of the set vector section. */ + + if (set_vector_count) + mywrite (set_vectors, 2 * set_symbol_count + set_vector_count, + sizeof (unsigned long), outdesc); + + if (trace_files) + fprintf (stderr, "\n"); + + padfile (data_pad, outdesc); +} + +/* Read the data segment contents of ENTRY, relocate them, + and write the result to the output file. + If `-r', save the data relocation for later reuse. + See comments in `copy_text'. */ + +void +copy_data (entry) + struct file_entry *entry; +{ + register struct relocation_info *reloc; + register char *bytes; + register int desc; + + if (trace_files) + prline_file_name (entry, stderr); + + desc = file_open (entry); + + bytes = (char *) alloca (entry->header.a_data); + + if (entry->datarel) reloc = entry->datarel; + else if (relocatable_output) /* Will need this again */ + { + read_file_relocation (entry); + reloc = entry->datarel; + } + else + { + reloc = (struct relocation_info *) alloca (entry->header.a_drsize); + lseek (desc, text_offset (entry) + entry->header.a_text + + entry->header.a_data + entry->header.a_trsize, + 0); + if (entry->header.a_drsize != read (desc, reloc, entry->header.a_drsize)) + fatal_with_file ("premature eof in data relocation of ", entry); + } + + lseek (desc, text_offset (entry) + entry->header.a_text, 0); + if (entry->header.a_data != read (desc, bytes, entry->header.a_data)) + fatal_with_file ("premature eof in data section of ", entry); + + perform_relocation (bytes, entry->data_start_address - entry->header.a_text, + entry->header.a_data, reloc, entry->header.a_drsize, entry); + + mywrite (bytes, 1, entry->header.a_data, outdesc); +} + +/* Relocate ENTRY's text or data section contents. + DATA is the address of the contents, in core. + DATA_SIZE is the length of the contents. + PC_RELOCATION is the difference between the address of the contents + in the output file and its address in the input file. + RELOC_INFO is the address of the relocation info, in core. + RELOC_SIZE is its length in bytes. */ +/* This version is about to be severly hacked by Randy. Hope it + works afterwards. */ +void +perform_relocation (data, pc_relocation, data_size, reloc_info, reloc_size, entry) + char *data; + struct relocation_info *reloc_info; + struct file_entry *entry; + int pc_relocation; + int data_size; + int reloc_size; +{ + register struct relocation_info *p = reloc_info; + struct relocation_info *end + = reloc_info + reloc_size / sizeof (struct relocation_info); + int text_relocation = entry->text_start_address; + int data_relocation = entry->data_start_address - entry->header.a_text; + int bss_relocation + = entry->bss_start_address - entry->header.a_text - entry->header.a_data; + + for (; p < end; p++) + { + register int relocation = 0; + register int addr = RELOC_ADDRESS(p); + register unsigned int mask = 0; + + if (addr >= data_size) + fatal_with_file ("relocation address out of range in ", entry); + + if (RELOC_EXTERN_P(p)) + { + int symindex = RELOC_SYMBOL (p) * sizeof (struct nlist); + symbol *sp = ((symbol *) + (((struct nlist *) + (((char *)entry->symbols) + symindex)) + ->n_un.n_name)); + +#ifdef N_INDR + /* Resolve indirection */ + if ((sp->defined & ~N_EXT) == N_INDR) + sp = (symbol *) sp->value; +#endif + + if (symindex >= entry->header.a_syms) + fatal_with_file ("relocation symbolnum out of range in ", entry); + + /* If the symbol is undefined, leave it at zero. */ + if (! sp->defined) + relocation = 0; + else + relocation = sp->value; + } + else switch (RELOC_TYPE(p)) + { + case N_TEXT: + case N_TEXT | N_EXT: + relocation = text_relocation; + break; + + case N_DATA: + case N_DATA | N_EXT: + /* A word that points to beginning of the the data section + initially contains not 0 but rather the "address" of that section + in the input file, which is the length of the file's text. */ + relocation = data_relocation; + break; + + case N_BSS: + case N_BSS | N_EXT: + /* Similarly, an input word pointing to the beginning of the bss + initially contains the length of text plus data of the file. */ + relocation = bss_relocation; + break; + + case N_ABS: + case N_ABS | N_EXT: + /* Don't know why this code would occur, but apparently it does. */ + break; + + default: + fatal_with_file ("nonexternal relocation code invalid in ", entry); + } + +#ifdef RELOC_ADD_EXTRA + relocation += RELOC_ADD_EXTRA(p); + if (relocatable_output) + { + /* Non-PC relative relocations which are absolute + or which have become non-external now have fixed + relocations. Set the ADD_EXTRA of this relocation + to be the relocation we have now determined. */ + if (! RELOC_PCREL_P (p)) + { + if ((int)p->r_type <= RELOC_32 + || RELOC_EXTERN_P (p) == 0) + RELOC_ADD_EXTRA (p) = relocation; + } + /* External PC-relative relocations continue to move around; + update their relocations by the amount they have moved + so far. */ + else if (RELOC_EXTERN_P (p)) + RELOC_ADD_EXTRA (p) -= pc_relocation; + continue; + } +#endif + + if (RELOC_PCREL_P(p)) + relocation -= pc_relocation; + + relocation >>= RELOC_VALUE_RIGHTSHIFT(p); + + /* Unshifted mask for relocation */ + mask = 1 << RELOC_TARGET_BITSIZE(p) - 1; + mask |= mask - 1; + relocation &= mask; + + /* Shift everything up to where it's going to be used */ + relocation <<= RELOC_TARGET_BITPOS(p); + mask <<= RELOC_TARGET_BITPOS(p); + + switch (RELOC_TARGET_SIZE(p)) + { + case 0: + if (RELOC_MEMORY_SUB_P(p)) + relocation -= mask & *(char *) (data + addr); + else if (RELOC_MEMORY_ADD_P(p)) + relocation += mask & *(char *) (data + addr); + *(char *) (data + addr) &= ~mask; + *(char *) (data + addr) |= relocation; + break; + + case 1: +#ifdef tahoe + if (((int) data + addr & 1) == 0) + { +#endif + if (RELOC_MEMORY_SUB_P(p)) + relocation -= mask & *(short *) (data + addr); + else if (RELOC_MEMORY_ADD_P(p)) + relocation += mask & *(short *) (data + addr); + *(short *) (data + addr) &= ~mask; + *(short *) (data + addr) |= relocation; +#ifdef tahoe + } + /* + * The CCI Power 6 (aka Tahoe) architecture has byte-aligned + * instruction operands but requires data accesses to be aligned. + * Brain-damage... + */ + else + { + unsigned char *da = (unsigned char *) (data + addr); + unsigned short s = da[0] << 8 | da[1]; + + if (RELOC_MEMORY_SUB_P(p)) + relocation -= mask & s; + else if (RELOC_MEMORY_ADD_P(p)) + relocation += mask & s; + s &= ~mask; + s |= relocation; + da[0] = s >> 8; + da[1] = s; + } +#endif + break; + + case 2: +#ifndef _CROSS_TARGET_ARCH +#ifdef tahoe + if (((int) data + addr & 3) == 0) + { +#endif + if (RELOC_MEMORY_SUB_P(p)) + relocation -= mask & *(long *) (data + addr); + else if (RELOC_MEMORY_ADD_P(p)) + relocation += mask & *(long *) (data + addr); + *(long *) (data + addr) &= ~mask; + *(long *) (data + addr) |= relocation; +#ifdef tahoe + } + else + { + unsigned char *da = (unsigned char *) (data + addr); + unsigned long l = da[0] << 24 | da[1] << 16 | da[2] << 8 | da[3]; + + if (RELOC_MEMORY_SUB_P(p)) + relocation -= mask & l; + else if (RELOC_MEMORY_ADD_P(p)) + relocation += mask & l; + l &= ~mask; + l |= relocation; + da[0] = l >> 24; + da[1] = l >> 16; + da[2] = l >> 8; + da[3] = l; + } +#endif +#else + /* Handle long word alignment requirements of SPARC architecture */ + /* WARNING: This fix makes an assumption on byte ordering */ + /* Marc Ullman, Stanford University Nov. 1 1989 */ + if (RELOC_MEMORY_SUB_P(p)) { + relocation -= mask & + ((*(unsigned short *) (data + addr) << 16) | + *(unsigned short *) (data + addr + 2)); + } else if (RELOC_MEMORY_ADD_P(p)) { + relocation += mask & + ((*(unsigned short *) (data + addr) << 16) | + *(unsigned short *) (data + addr + 2)); + } + *(unsigned short *) (data + addr) &= (~mask >> 16); + *(unsigned short *) (data + addr + 2) &= (~mask & 0xffff); + *(unsigned short *) (data + addr) |= (relocation >> 16); + *(unsigned short *) (data + addr + 2) |= (relocation & 0xffff); +#endif + break; + + default: + fatal_with_file ("Unimplemented relocation field length in ", entry); + } + } +} + +/* For relocatable_output only: write out the relocation, + relocating the addresses-to-be-relocated. */ + +void coptxtrel (), copdatrel (); + +void +write_rel () +{ + register int i; + register int count = 0; + + if (trace_files) + fprintf (stderr, "Writing text relocation:\n\n"); + + /* Assign each global symbol a sequence number, giving the order + in which `write_syms' will write it. + This is so we can store the proper symbolnum fields + in relocation entries we write. */ + + for (i = 0; i < TABSIZE; i++) + { + symbol *sp; + for (sp = symtab[i]; sp; sp = sp->link) + if (sp->referenced || sp->defined) + { + sp->def_count = count++; + /* Leave room for the reference required by N_INDR, if + necessary. */ + if ((sp->defined & ~N_EXT) == N_INDR) + count++; + } + } + /* Correct, because if (relocatable_output), we will also be writing + whatever indirect blocks we have. */ + if (count != defined_global_sym_count + + undefined_global_sym_count + global_indirect_count) + fatal ("internal error"); + + /* Write out the relocations of all files, remembered from copy_text. */ + + each_full_file (coptxtrel, 0); + + if (trace_files) + fprintf (stderr, "\nWriting data relocation:\n\n"); + + each_full_file (copdatrel, 0); + + if (trace_files) + fprintf (stderr, "\n"); +} + +void +coptxtrel (entry) + struct file_entry *entry; +{ + register struct relocation_info *p, *end; + register int reloc = entry->text_start_address; + + p = entry->textrel; + end = (struct relocation_info *) (entry->header.a_trsize + (char *) p); + while (p < end) + { + RELOC_ADDRESS(p) += reloc; + if (RELOC_EXTERN_P(p)) + { + register int symindex = RELOC_SYMBOL(p) * sizeof (struct nlist); + symbol *symptr = ((symbol *) + (((struct nlist *) + (((char *)entry->symbols) + symindex)) + ->n_un.n_name)); + + if (symindex >= entry->header.a_syms) + fatal_with_file ("relocation symbolnum out of range in ", entry); + +#ifdef N_INDR + /* Resolve indirection. */ + if ((symptr->defined & ~N_EXT) == N_INDR) + symptr = (symbol *) symptr->value; +#endif + + /* If the symbol is now defined, change the external relocation + to an internal one. */ + + if (symptr->defined) + { + RELOC_EXTERN_P(p) = 0; + RELOC_SYMBOL(p) = (symptr->defined & N_TYPE); +#ifdef RELOC_ADD_EXTRA + /* If we aren't going to be adding in the value in + memory on the next pass of the loader, then we need + to add it in from the relocation entry. Otherwise + the work we did in this pass is lost. */ + if (!RELOC_MEMORY_ADD_P(p)) + RELOC_ADD_EXTRA (p) += symptr->value; +#endif + } + else + /* Debugger symbols come first, so have to start this + after them. */ + RELOC_SYMBOL(p) = (symptr->def_count + nsyms + - defined_global_sym_count + - undefined_global_sym_count + - global_indirect_count); + } + p++; + } + mywrite (entry->textrel, 1, entry->header.a_trsize, outdesc); +} + +void +copdatrel (entry) + struct file_entry *entry; +{ + register struct relocation_info *p, *end; + /* Relocate the address of the relocation. + Old address is relative to start of the input file's data section. + New address is relative to start of the output file's data section. */ + register int reloc = entry->data_start_address - text_size; + + p = entry->datarel; + end = (struct relocation_info *) (entry->header.a_drsize + (char *) p); + while (p < end) + { + RELOC_ADDRESS(p) += reloc; + if (RELOC_EXTERN_P(p)) + { + register int symindex = RELOC_SYMBOL(p) * sizeof (struct nlist); + symbol *symptr = ((symbol *) + (((struct nlist *) + (((char *)entry->symbols) + symindex)) + ->n_un.n_name)); + int symtype; + + if (symindex >= entry->header.a_syms) + fatal_with_file ("relocation symbolnum out of range in ", entry); + +#ifdef N_INDR + /* Resolve indirection. */ + if ((symptr->defined & ~N_EXT) == N_INDR) + symptr = (symbol *) symptr->value; +#endif + + symtype = symptr->defined & N_TYPE; + + if (force_common_definition + || symtype == N_DATA || symtype == N_TEXT || symtype == N_ABS) + { + RELOC_EXTERN_P(p) = 0; + RELOC_SYMBOL(p) = symtype; + } + else + /* Debugger symbols come first, so have to start this + after them. */ + RELOC_SYMBOL(p) + = (((symbol *) + (((struct nlist *) + (((char *)entry->symbols) + symindex)) + ->n_un.n_name)) + ->def_count + + nsyms - defined_global_sym_count + - undefined_global_sym_count + - global_indirect_count); + } + p++; + } + mywrite (entry->datarel, 1, entry->header.a_drsize, outdesc); +} + +void write_file_syms (); +void write_string_table (); + +/* Offsets and current lengths of symbol and string tables in output file. */ + +int symbol_table_offset; +int symbol_table_len; + +/* Address in output file where string table starts. */ +int string_table_offset; + +/* Offset within string table + where the strings in `strtab_vector' should be written. */ +int string_table_len; + +/* Total size of string table strings allocated so far, + including strings in `strtab_vector'. */ +int strtab_size; + +/* Vector whose elements are strings to be added to the string table. */ +char **strtab_vector; + +/* Vector whose elements are the lengths of those strings. */ +int *strtab_lens; + +/* Index in `strtab_vector' at which the next string will be stored. */ +int strtab_index; + +/* Add the string NAME to the output file string table. + Record it in `strtab_vector' to be output later. + Return the index within the string table that this string will have. */ + +int +assign_string_table_index (name) + char *name; +{ + register int index = strtab_size; + register int len = strlen (name) + 1; + + strtab_size += len; + strtab_vector[strtab_index] = name; + strtab_lens[strtab_index++] = len; + + return index; +} + +FILE *outstream = (FILE *) 0; + +/* Write the contents of `strtab_vector' into the string table. + This is done once for each file's local&debugger symbols + and once for the global symbols. */ + +void +write_string_table () +{ + register int i; + + lseek (outdesc, string_table_offset + string_table_len, 0); + + if (!outstream) + outstream = fdopen (outdesc, "w"); + + for (i = 0; i < strtab_index; i++) + { + fwrite (strtab_vector[i], 1, strtab_lens[i], outstream); + string_table_len += strtab_lens[i]; + } + + fflush (outstream); + + /* Report I/O error such as disk full. */ + if (ferror (outstream)) + perror_name (output_filename); +} + +/* Write the symbol table and string table of the output file. */ + +void +write_syms () +{ + /* Number of symbols written so far. */ + int syms_written = 0; + register int i; + register symbol *sp; + + /* Buffer big enough for all the global symbols. One + extra struct for each indirect symbol to hold the extra reference + following. */ + struct nlist *buf + = (struct nlist *) alloca ((defined_global_sym_count + + undefined_global_sym_count + + global_indirect_count) + * sizeof (struct nlist)); + /* Pointer for storing into BUF. */ + register struct nlist *bufp = buf; + + /* Size of string table includes the bytes that store the size. */ + strtab_size = sizeof strtab_size; + + symbol_table_offset = N_SYMOFF (outheader); + symbol_table_len = 0; + string_table_offset = N_STROFF (outheader); + string_table_len = strtab_size; + + if (strip_symbols == STRIP_ALL) + return; + + /* Write the local symbols defined by the various files. */ + + each_file (write_file_syms, &syms_written); + file_close (); + + /* Now write out the global symbols. */ + + /* Allocate two vectors that record the data to generate the string + table from the global symbols written so far. This must include + extra space for the references following indirect outputs. */ + + strtab_vector = (char **) alloca ((num_hash_tab_syms + + global_indirect_count) * sizeof (char *)); + strtab_lens = (int *) alloca ((num_hash_tab_syms + + global_indirect_count) * sizeof (int)); + strtab_index = 0; + + /* Scan the symbol hash table, bucket by bucket. */ + + for (i = 0; i < TABSIZE; i++) + for (sp = symtab[i]; sp; sp = sp->link) + { + struct nlist nl; + + nl.n_other = 0; + nl.n_desc = 0; + + /* Compute a `struct nlist' for the symbol. */ + + if (sp->defined || sp->referenced) + { + /* common condition needs to be before undefined condition */ + /* because unallocated commons are set undefined in */ + /* digest_symbols */ + if (sp->defined > 1) /* defined with known type */ + { + /* If the target of an indirect symbol has been + defined and we are outputting an executable, + resolve the indirection; it's no longer needed */ + if (!relocatable_output + && ((sp->defined & N_TYPE) == N_INDR) + && (((symbol *) sp->value)->defined > 1)) + { + symbol *newsp = (symbol *) sp->value; + nl.n_type = newsp->defined; + nl.n_value = newsp->value; + } + else + { + nl.n_type = sp->defined; + if (sp->defined != (N_INDR | N_EXT)) + nl.n_value = sp->value; + else + nl.n_value = 0; + } + } + else if (sp->max_common_size) /* defined as common but not allocated. */ + { + /* happens only with -r and not -d */ + /* write out a common definition */ + nl.n_type = N_UNDF | N_EXT; + nl.n_value = sp->max_common_size; + } + else if (!sp->defined) /* undefined -- legit only if -r */ + { + nl.n_type = N_UNDF | N_EXT; + nl.n_value = 0; + } + else + fatal ("internal error: %s defined in mysterious way", sp->name); + + /* Allocate string table space for the symbol name. */ + + nl.n_un.n_strx = assign_string_table_index (sp->name); + + /* Output to the buffer and count it. */ + + *bufp++ = nl; + syms_written++; + if (nl.n_type == (N_INDR | N_EXT)) + { + struct nlist xtra_ref; + xtra_ref.n_type = N_EXT | N_UNDF; + xtra_ref.n_un.n_strx + = assign_string_table_index (((symbol *) sp->value)->name); + xtra_ref.n_other = 0; + xtra_ref.n_desc = 0; + xtra_ref.n_value = 0; + *bufp++ = xtra_ref; + syms_written++; + } + } + } + + /* Output the buffer full of `struct nlist's. */ + + lseek (outdesc, symbol_table_offset + symbol_table_len, 0); + mywrite (buf, sizeof (struct nlist), bufp - buf, outdesc); + symbol_table_len += sizeof (struct nlist) * (bufp - buf); + + if (syms_written != nsyms) + fatal ("internal error: wrong number of symbols written into output file", 0); + + if (symbol_table_offset + symbol_table_len != string_table_offset) + fatal ("internal error: inconsistent symbol table length", 0); + + /* Now the total string table size is known, so write it. + We are already positioned at the right place in the file. */ + + mywrite (&strtab_size, sizeof (int), 1, outdesc); /* we're at right place */ + + /* Write the strings for the global symbols. */ + + write_string_table (); +} + +/* Write the local and debugger symbols of file ENTRY. + Increment *SYMS_WRITTEN_ADDR for each symbol that is written. */ + +/* Note that we do not combine identical names of local symbols. + dbx or gdb would be confused if we did that. */ + +void +write_file_syms (entry, syms_written_addr) + struct file_entry *entry; + int *syms_written_addr; +{ + register struct nlist *p = entry->symbols; + register struct nlist *end = p + entry->header.a_syms / sizeof (struct nlist); + + /* Buffer to accumulate all the syms before writing them. + It has one extra slot for the local symbol we generate here. */ + struct nlist *buf + = (struct nlist *) alloca (entry->header.a_syms + sizeof (struct nlist)); + register struct nlist *bufp = buf; + + /* Upper bound on number of syms to be written here. */ + int max_syms = (entry->header.a_syms / sizeof (struct nlist)) + 1; + + /* Make tables that record, for each symbol, its name and its name's length. + The elements are filled in by `assign_string_table_index'. */ + + strtab_vector = (char **) alloca (max_syms * sizeof (char *)); + strtab_lens = (int *) alloca (max_syms * sizeof (int)); + strtab_index = 0; + + /* Generate a local symbol for the start of this file's text. */ + + if (discard_locals != DISCARD_ALL) + { + struct nlist nl; + + nl.n_type = N_FN | N_EXT; + nl.n_un.n_strx = assign_string_table_index (entry->local_sym_name); + nl.n_value = entry->text_start_address; + nl.n_desc = 0; + nl.n_other = 0; + *bufp++ = nl; + (*syms_written_addr)++; + entry->local_syms_offset = *syms_written_addr * sizeof (struct nlist); + } + + /* Read the file's string table. */ + + entry->strings = (char *) alloca (entry->string_size); + read_entry_strings (file_open (entry), entry); + + for (; p < end; p++) + { + register int type = p->n_type; + register int write = 0; + + /* WRITE gets 1 for a non-global symbol that should be written. */ + + + if (SET_ELEMENT_P (type)) /* This occurs even if global. These */ + /* types of symbols are never written */ + /* globally, though they are stored */ + /* globally. */ + write = relocatable_output; + else if (!(type & (N_STAB | N_EXT))) + /* ordinary local symbol */ + write = ((discard_locals != DISCARD_ALL) + && !(discard_locals == DISCARD_L && + (p->n_un.n_strx + entry->strings)[0] == LPREFIX) + && type != N_WARNING); + else if (!(type & N_EXT)) + /* debugger symbol */ + write = (strip_symbols == STRIP_NONE); + + if (write) + { + /* If this symbol has a name, + allocate space for it in the output string table. */ + + if (p->n_un.n_strx) + p->n_un.n_strx = assign_string_table_index (p->n_un.n_strx + + entry->strings); + + /* Output this symbol to the buffer and count it. */ + + *bufp++ = *p; + (*syms_written_addr)++; + } + } + + /* All the symbols are now in BUF; write them. */ + + lseek (outdesc, symbol_table_offset + symbol_table_len, 0); + mywrite (buf, sizeof (struct nlist), bufp - buf, outdesc); + symbol_table_len += sizeof (struct nlist) * (bufp - buf); + + /* Write the string-table data for the symbols just written, + using the data in vectors `strtab_vector' and `strtab_lens'. */ + + write_string_table (); + entry->strings = 0; /* Since it will dissapear anyway. */ +} + +/* Copy any GDB symbol segments from the input files to the output file. + The contents of the symbol segment is copied without change + except that we store some information into the beginning of it. */ + +void write_file_symseg (); + +void +write_symsegs () +{ + each_file (write_file_symseg, 0); +} + +void +write_file_symseg (entry) + struct file_entry *entry; +{ + char buffer[4096]; + struct symbol_root root; + int indesc; + int len; + + if (entry->symseg_offset == 0) + return; + + /* This entry has a symbol segment. Read the root of the segment. */ + + indesc = file_open (entry); + lseek (indesc, entry->symseg_offset + entry->starting_offset, 0); + if (sizeof root != read (indesc, &root, sizeof root)) + fatal_with_file ("premature end of file in symbol segment of ", entry); + + /* Store some relocation info into the root. */ + + root.ldsymoff = entry->local_syms_offset; + root.textrel = entry->text_start_address; + root.datarel = entry->data_start_address - entry->header.a_text; + root.bssrel = entry->bss_start_address + - entry->header.a_text - entry->header.a_data; + root.databeg = entry->data_start_address - root.datarel; + root.bssbeg = entry->bss_start_address - root.bssrel; + + /* Write the modified root into the output file. */ + + mywrite (&root, sizeof root, 1, outdesc); + + /* Copy the rest of the symbol segment unchanged. */ + + if (entry->superfile) + { + /* Library member: number of bytes to copy is determined + from the member's total size. */ + + int total = entry->total_size - entry->symseg_offset - sizeof root; + + while (total > 0) + { + len = read (indesc, buffer, min (sizeof buffer, total)); + + if (len != min (sizeof buffer, total)) + fatal_with_file ("premature end of file in symbol segment of ", entry); + total -= len; + mywrite (buffer, len, 1, outdesc); + } + } + else + { + /* A separate file: copy until end of file. */ + + while (len = read (indesc, buffer, sizeof buffer)) + { + mywrite (buffer, len, 1, outdesc); + if (len < sizeof buffer) + break; + } + } + + file_close (); +} + +/* Create the symbol table entries for `etext', `edata' and `end'. */ + +void +symtab_init () +{ +#ifndef nounderscore + edata_symbol = getsym ("_edata"); + etext_symbol = getsym ("_etext"); + end_symbol = getsym ("_end"); +#else + edata_symbol = getsym ("edata"); + etext_symbol = getsym ("etext"); + end_symbol = getsym ("end"); +#endif + +#ifdef sun + { + symbol *dynamic_symbol = getsym ("__DYNAMIC"); + dynamic_symbol->defined = N_ABS | N_EXT; + dynamic_symbol->referenced = 1; + dynamic_symbol->value = 0; + } +#endif + +#ifdef sequent + { + symbol *_387_flt_symbol = getsym ("_387_flt"); + _387_flt_symbol->defined = N_ABS | N_EXT; + _387_flt_symbol->referenced = 1; + _387_flt_symbol->value = 0; + } +#endif + + edata_symbol->defined = N_DATA | N_EXT; + etext_symbol->defined = N_TEXT | N_EXT; + end_symbol->defined = N_BSS | N_EXT; + + edata_symbol->referenced = 1; + etext_symbol->referenced = 1; + end_symbol->referenced = 1; +} + +/* Compute the hash code for symbol name KEY. */ + +int +hash_string (key) + char *key; +{ + register char *cp; + register int k; + + cp = key; + k = 0; + while (*cp) + k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff; + + return k; +} + +/* Get the symbol table entry for the global symbol named KEY. + Create one if there is none. */ + +symbol * +getsym (key) + char *key; +{ + register int hashval; + register symbol *bp; + + /* Determine the proper bucket. */ + + hashval = hash_string (key) % TABSIZE; + + /* Search the bucket. */ + + for (bp = symtab[hashval]; bp; bp = bp->link) + if (! strcmp (key, bp->name)) + return bp; + + /* Nothing was found; create a new symbol table entry. */ + + bp = (symbol *) xmalloc (sizeof (symbol)); + bp->refs = 0; + bp->name = (char *) xmalloc (strlen (key) + 1); + strcpy (bp->name, key); + bp->defined = 0; + bp->referenced = 0; + bp->trace = 0; + bp->value = 0; + bp->max_common_size = 0; + bp->warning = 0; + bp->undef_refs = 0; + bp->multiply_defined = 0; + + /* Add the entry to the bucket. */ + + bp->link = symtab[hashval]; + symtab[hashval] = bp; + + ++num_hash_tab_syms; + + return bp; +} + +/* Like `getsym' but return 0 if the symbol is not already known. */ + +symbol * +getsym_soft (key) + char *key; +{ + register int hashval; + register symbol *bp; + + /* Determine which bucket. */ + + hashval = hash_string (key) % TABSIZE; + + /* Search the bucket. */ + + for (bp = symtab[hashval]; bp; bp = bp->link) + if (! strcmp (key, bp->name)) + return bp; + + return 0; +} + +/* Report a fatal error. + STRING is a printf format string and ARG is one arg for it. */ + +void +fatal (string, arg) + char *string, *arg; +{ + fprintf (stderr, "ld: "); + fprintf (stderr, string, arg); + fprintf (stderr, "\n"); + exit (1); +} + +/* Report a fatal error. The error message is STRING + followed by the filename of ENTRY. */ + +void +fatal_with_file (string, entry) + char *string; + struct file_entry *entry; +{ + fprintf (stderr, "ld: "); + fprintf (stderr, string); + print_file_name (entry, stderr); + fprintf (stderr, "\n"); + exit (1); +} + +/* Report a fatal error using the message for the last failed system call, + followed by the string NAME. */ + +void +perror_name (name) + char *name; +{ + extern int errno; + char *s; + + if (errno < sys_nerr) + s = concat ("", sys_errlist[errno], " for %s"); + else + s = "cannot open %s"; + fatal (s, name); +} + +/* Report a fatal error using the message for the last failed system call, + followed by the name of file ENTRY. */ + +void +perror_file (entry) + struct file_entry *entry; +{ + extern int errno; + char *s; + + if (errno < sys_nerr) + s = concat ("", sys_errlist[errno], " for "); + else + s = "cannot open "; + fatal_with_file (s, entry); +} + +/* Report a nonfatal error. + STRING is a format for printf, and ARG1 ... ARG3 are args for it. */ + +void +error (string, arg1, arg2, arg3) + char *string, *arg1, *arg2, *arg3; +{ + fprintf (stderr, "%s: ", progname); + fprintf (stderr, string, arg1, arg2, arg3); + fprintf (stderr, "\n"); +} + + +/* Output COUNT*ELTSIZE bytes of data at BUF + to the descriptor DESC. */ + +void +mywrite (buf, count, eltsize, desc) + char *buf; + int count; + int eltsize; + int desc; +{ + register int val; + register int bytes = count * eltsize; + + while (bytes > 0) + { + val = write (desc, buf, bytes); + if (val <= 0) + perror_name (output_filename); + buf += val; + bytes -= val; + } +} + +/* Output PADDING zero-bytes to descriptor OUTDESC. + PADDING may be negative; in that case, do nothing. */ + +void +padfile (padding, outdesc) + int padding; + int outdesc; +{ + register char *buf; + if (padding <= 0) + return; + + buf = (char *) alloca (padding); + bzero (buf, padding); + mywrite (buf, padding, 1, outdesc); +} + +/* Return a newly-allocated string + whose contents concatenate the strings S1, S2, S3. */ + +char * +concat (s1, s2, s3) + char *s1, *s2, *s3; +{ + register int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3); + register char *result = (char *) xmalloc (len1 + len2 + len3 + 1); + + strcpy (result, s1); + strcpy (result + len1, s2); + strcpy (result + len1 + len2, s3); + result[len1 + len2 + len3] = 0; + + return result; +} + +/* Parse the string ARG using scanf format FORMAT, and return the result. + If it does not parse, report fatal error + generating the error message using format string ERROR and ARG as arg. */ + +int +parse (arg, format, error) + char *arg, *format; +{ + int x; + if (1 != sscanf (arg, format, &x)) + fatal (error, arg); + return x; +} + +/* Like malloc but get fatal error if memory is exhausted. */ + +int +xmalloc (size) + int size; +{ + register int result = malloc (size); + if (!result) + fatal ("virtual memory exhausted", 0); + return result; +} + +/* Like realloc but get fatal error if memory is exhausted. */ + +int +xrealloc (ptr, size) + char *ptr; + int size; +{ + register int result = realloc (ptr, size); + if (!result) + fatal ("virtual memory exhausted", 0); + return result; +} + +#ifdef USG + +void +bzero (p, n) + char *p; +{ + memset (p, 0, n); +} + +void +bcopy (from, to, n) + char *from, *to; +{ + memcpy (to, from, n); +} + +getpagesize () +{ + return (4096); +} + +#endif + +#if defined(sun) && (TARGET == SUN4) + +/* Don't use local pagesize to build for Sparc. */ + +getpagesize () +{ + return (8192); +} +#endif diff --git a/usr.bin/ld/symseg.h b/usr.bin/ld/symseg.h new file mode 100644 index 00000000000..978d8bed947 --- /dev/null +++ b/usr.bin/ld/symseg.h @@ -0,0 +1,358 @@ +/*- + * + * This code is derived from software copyrighted by the Free Software + * Foundation. + * + * @(#)symseg.h 8.1 (Berkeley) 6/6/93 + */ + +/* GDB symbol table format definitions. + Copyright (C) 1987, 1988 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Format of GDB symbol table data. + There is one symbol segment for each source file or + independant compilation. These segments are simply concatenated + to form the GDB symbol table. A zero word where the beginning + of a segment is expected indicates there are no more segments. + +Format of a symbol segment: + + The symbol segment begins with a word containing 1 + if it is in the format described here. Other formats may + be designed, with other code numbers. + + The segment contains many objects which point at each other. + The pointers are offsets in bytes from the beginning of the segment. + Thus, each segment can be loaded into core and its pointers relocated + to make valid in-core pointers. + + All the data objects in the segment can be found indirectly from + one of them, the root object, of type `struct symbol_root'. + It appears at the beginning of the segment. + + The total size of the segment, in bytes, appears as the `length' + field of this object. This size includes the size of the + root object. + + All the object data types are defined here to contain pointer types + appropriate for in-core use on a relocated symbol segment. + Casts to and from type int are required for working with + unrelocated symbol segments such as are found in the file. + + The ldsymaddr word is filled in by the loader to contain + the offset (in bytes) within the ld symbol table + of the first nonglobal symbol from this compilation. + This makes it possible to match those symbols + (which contain line number information) reliably with + the segment they go with. + + Core addresses within the program that appear in the symbol segment + are not relocated by the loader. They are inserted by the assembler + and apply to addresses as output by the assembler, so GDB must + relocate them when it loads the symbol segment. It gets the information + on how to relocate from the textrel, datarel, bssrel, databeg and bssbeg + words of the root object. + + The words textrel, datarel and bssrel + are filled in by ld with the amounts to relocate within-the-file + text, data and bss addresses by; databeg and bssbeg can be + used to tell which kind of relocation an address needs. */ + +enum language {language_c}; + +struct symbol_root +{ + int format; /* Data format version */ + int length; /* # bytes in this symbol segment */ + int ldsymoff; /* Offset in ld symtab of this file's syms */ + int textrel; /* Relocation for text addresses */ + int datarel; /* Relocation for data addresses */ + int bssrel; /* Relocation for bss addresses */ + char *filename; /* Name of main source file compiled */ + char *filedir; /* Name of directory it was reached from */ + struct blockvector *blockvector; /* Vector of all symbol-naming blocks */ + struct typevector *typevector; /* Vector of all data types */ + enum language language; /* Code identifying the language used */ + char *version; /* Version info. Not fully specified */ + char *compilation; /* Compilation info. Not fully specified */ + int databeg; /* Address within the file of data start */ + int bssbeg; /* Address within the file of bss start */ + struct sourcevector *sourcevector; /* Vector of line-number info */ +}; + +/* All data types of symbols in the compiled program + are represented by `struct type' objects. + All of these objects are pointed to by the typevector. + The type vector may have empty slots that contain zero. */ + +struct typevector +{ + int length; /* Number of types described */ + struct type *type[1]; +}; + +/* Different kinds of data types are distinguished by the `code' field. */ + +enum type_code +{ + TYPE_CODE_UNDEF, /* Not used; catches errors */ + TYPE_CODE_PTR, /* Pointer type */ + TYPE_CODE_ARRAY, /* Array type, lower bound zero */ + TYPE_CODE_STRUCT, /* C struct or Pascal record */ + TYPE_CODE_UNION, /* C union or Pascal variant part */ + TYPE_CODE_ENUM, /* Enumeration type */ + TYPE_CODE_FUNC, /* Function type */ + TYPE_CODE_INT, /* Integer type */ + TYPE_CODE_FLT, /* Floating type */ + TYPE_CODE_VOID, /* Void type (values zero length) */ + TYPE_CODE_SET, /* Pascal sets */ + TYPE_CODE_RANGE, /* Range (integers within spec'd bounds) */ + TYPE_CODE_PASCAL_ARRAY, /* Array with explicit type of index */ +}; + +/* This appears in a type's flags word for an unsigned integer type. */ +#define TYPE_FLAG_UNSIGNED 1 + +/* Other flag bits are used with GDB. */ + +struct type +{ + /* Code for kind of type */ + enum type_code code; + /* Name of this type, or zero if none. + This is used for printing only. + Type names specified as input are defined by symbols. */ + char *name; + /* Length in bytes of storage for a value of this type */ + int length; + /* For a pointer type, describes the type of object pointed to. + For an array type, describes the type of the elements. + For a function type, describes the type of the value. + Unused otherwise. */ + struct type *target_type; + /* Type that is a pointer to this type. + Zero if no such pointer-to type is known yet. + The debugger may add the address of such a type + if it has to construct one later. */ + struct type *pointer_type; + /* Type that is a function returning this type. + Zero if no such function type is known here. + The debugger may add the address of such a type + if it has to construct one later. */ + struct type *function_type; + /* Flags about this type. */ + short flags; + /* Number of fields described for this type */ + short nfields; + /* For structure and union types, a description of each field. + For set and pascal array types, there is one "field", + whose type is the domain type of the set or array. + For range types, there are two "fields", + the minimum and maximum values (both inclusive). + For enum types, each possible value is described by one "field". + For range types, there are two "fields", that record constant values + (inclusive) for the minimum and maximum. + + Using a pointer to a separate array of fields + allows all types to have the same size, which is useful + because we can allocate the space for a type before + we know what to put in it. */ + struct field + { + /* Position of this field, counting in bits from start of + containing structure. For a function type, this is the + position in the argument list of this argument. + For a range bound or enum value, this is the value itself. */ + int bitpos; + /* Size of this field, in bits, or zero if not packed. + For an unpacked field, the field's type's length + says how many bytes the field occupies. */ + int bitsize; + /* In a struct or enum type, type of this field. + In a function type, type of this argument. + In an array type, the domain-type of the array. */ + struct type *type; + /* Name of field, value or argument. + Zero for range bounds and array domains. */ + char *name; + } *fields; +}; + +/* All of the name-scope contours of the program + are represented by `struct block' objects. + All of these objects are pointed to by the blockvector. + + Each block represents one name scope. + Each lexical context has its own block. + + The first two blocks in the blockvector are special. + The first one contains all the symbols defined in this compilation + whose scope is the entire program linked together. + The second one contains all the symbols whose scope is the + entire compilation excluding other separate compilations. + In C, these correspond to global symbols and static symbols. + + Each block records a range of core addresses for the code that + is in the scope of the block. The first two special blocks + give, for the range of code, the entire range of code produced + by the compilation that the symbol segment belongs to. + + The blocks appear in the blockvector + in order of increasing starting-address, + and, within that, in order of decreasing ending-address. + + This implies that within the body of one function + the blocks appear in the order of a depth-first tree walk. */ + +struct blockvector +{ + /* Number of blocks in the list. */ + int nblocks; + /* The blocks themselves. */ + struct block *block[1]; +}; + +struct block +{ + /* Addresses in the executable code that are in this block. + Note: in an unrelocated symbol segment in a file, + these are always zero. They can be filled in from the + N_LBRAC and N_RBRAC symbols in the loader symbol table. */ + int startaddr, endaddr; + /* The symbol that names this block, + if the block is the body of a function; + otherwise, zero. + Note: In an unrelocated symbol segment in an object file, + this field may be zero even when the block has a name. + That is because the block is output before the name + (since the name resides in a higher block). + Since the symbol does point to the block (as its value), + it is possible to find the block and set its name properly. */ + struct symbol *function; + /* The `struct block' for the containing block, or 0 if none. */ + /* Note that in an unrelocated symbol segment in an object file + this pointer may be zero when the correct value should be + the second special block (for symbols whose scope is one compilation). + This is because the compiler ouptuts the special blocks at the + very end, after the other blocks. */ + struct block *superblock; + /* Number of local symbols. */ + int nsyms; + /* The symbols. */ + struct symbol *sym[1]; +}; + +/* Represent one symbol name; a variable, constant, function or typedef. */ + +/* Different name spaces for symbols. Looking up a symbol specifies + a namespace and ignores symbol definitions in other name spaces. + + VAR_NAMESPACE is the usual namespace. + In C, this contains variables, function names, typedef names + and enum type values. + + STRUCT_NAMESPACE is used in C to hold struct, union and enum type names. + Thus, if `struct foo' is used in a C program, + it produces a symbol named `foo' in the STRUCT_NAMESPACE. + + LABEL_NAMESPACE may be used for names of labels (for gotos); + currently it is not used and labels are not recorded at all. */ + +/* For a non-global symbol allocated statically, + the correct core address cannot be determined by the compiler. + The compiler puts an index number into the symbol's value field. + This index number can be matched with the "desc" field of + an entry in the loader symbol table. */ + +enum namespace +{ + UNDEF_NAMESPACE, VAR_NAMESPACE, STRUCT_NAMESPACE, LABEL_NAMESPACE, +}; + +/* An address-class says where to find the value of the symbol in core. */ + +enum address_class +{ + LOC_UNDEF, /* Not used; catches errors */ + LOC_CONST, /* Value is constant int */ + LOC_STATIC, /* Value is at fixed address */ + LOC_REGISTER, /* Value is in register */ + LOC_ARG, /* Value is at spec'd position in arglist */ + LOC_LOCAL, /* Value is at spec'd pos in stack frame */ + LOC_TYPEDEF, /* Value not used; definition in SYMBOL_TYPE + Symbols in the namespace STRUCT_NAMESPACE + all have this class. */ + LOC_LABEL, /* Value is address in the code */ + LOC_BLOCK, /* Value is address of a `struct block'. + Function names have this class. */ + LOC_EXTERNAL, /* Value is at address not in this compilation. + This is used for .comm symbols + and for extern symbols within functions. + Inside GDB, this is changed to LOC_STATIC once the + real address is obtained from a loader symbol. */ + LOC_CONST_BYTES /* Value is a constant byte-sequence. */ +}; + +struct symbol +{ + /* Symbol name */ + char *name; + /* Name space code. */ + enum namespace namespace; + /* Address class */ + enum address_class class; + /* Data type of value */ + struct type *type; + /* constant value, or address if static, or register number, + or offset in arguments, or offset in stack frame. */ + union + { + long value; + struct block *block; /* for LOC_BLOCK */ + char *bytes; /* for LOC_CONST_BYTES */ + } + value; +}; + +/* Source-file information. + This describes the relation between source files and line numbers + and addresses in the program text. */ + +struct sourcevector +{ + int length; /* Number of source files described */ + struct source *source[1]; /* Descriptions of the files */ +}; + +/* Line number and address of one line. */ + +struct line +{ + int linenum; + int address; +}; + +/* All the information on one source file. */ + +struct source +{ + char *name; /* Name of file */ + int nlines; /* Number of lines that follow */ + struct line lines[1]; /* Information on each line */ +}; diff --git a/usr.bin/locate/bigram/locate.bigram.c b/usr.bin/locate/bigram/locate.bigram.c new file mode 100644 index 00000000000..fc0cbb707b5 --- /dev/null +++ b/usr.bin/locate/bigram/locate.bigram.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * James A. Woods. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE 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[] = "@(#)locate.bigram.c 8.2 (Berkeley) 4/28/95"; +#endif /* not lint */ + +/* + * bigram < text > bigrams + * + * List bigrams for 'updatedb' script. + * Use 'code' to encode a file using this output. + */ + +#include +#include /* for MAXPATHLEN */ + +char buf1[MAXPATHLEN] = " "; +char buf2[MAXPATHLEN]; + +main ( ) +{ + register char *cp; + register char *oldpath = buf1, *path = buf2; + + while ( fgets ( path, sizeof(buf2), stdin ) != NULL ) { + + /* skip longest common prefix */ + for ( cp = path; *cp == *oldpath; cp++, oldpath++ ) + if ( *oldpath == '\0' ) + break; + /* + * output post-residue bigrams only + */ + while ( *cp != '\0' && *(cp + 1) != '\0' ) { + putchar ( *cp++ ); + putchar ( *cp++ ); + putchar ( '\n' ); + } + if ( path == buf1 ) /* swap pointers */ + path = buf2, oldpath = buf1; + else + path = buf1, oldpath = buf2; + } + return (0); +} diff --git a/usr.bin/locate/code/locate.code.c b/usr.bin/locate/code/locate.code.c new file mode 100644 index 00000000000..5561dea8557 --- /dev/null +++ b/usr.bin/locate/code/locate.code.c @@ -0,0 +1,213 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * James A. Woods. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE 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[] = "@(#)locate.code.c 8.4 (Berkeley) 5/4/95"; +#endif /* not lint */ + +/* + * PURPOSE: sorted list compressor (works with a modified 'find' + * to encode/decode a filename database) + * + * USAGE: bigram < list > bigrams + * process bigrams (see updatedb) > common_bigrams + * code common_bigrams < list > squozen_list + * + * METHOD: Uses 'front compression' (see ";login:", Volume 8, Number 1 + * February/March 1983, p. 8). Output format is, per line, an + * offset differential count byte followed by a partially bigram- + * encoded ascii residue. A bigram is a two-character sequence, + * the first 128 most common of which are encoded in one byte. + * + * EXAMPLE: For simple front compression with no bigram encoding, + * if the input is... then the output is... + * + * /usr/src 0 /usr/src + * /usr/src/cmd/aardvark.c 8 /cmd/aardvark.c + * /usr/src/cmd/armadillo.c 14 armadillo.c + * /usr/tmp/zoo 5 tmp/zoo + * + * The codes are: + * + * 0-28 likeliest differential counts + offset to make nonnegative + * 30 switch code for out-of-range count to follow in next word + * 128-255 bigram codes (128 most common, as determined by 'updatedb') + * 32-127 single character (printable) ascii residue (ie, literal) + * + * SEE ALSO: updatedb.csh, bigram.c + * + * AUTHOR: James A. Woods, Informatics General Corp., + * NASA Ames Research Center, 10/82 + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "locate.h" + +#define BGBUFSIZE (NBG * 2) /* size of bigram buffer */ + +char buf1[MAXPATHLEN] = " "; +char buf2[MAXPATHLEN]; +char bigrams[BGBUFSIZE + 1] = { 0 }; + +int bgindex __P((char *)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + register char *cp, *oldpath, *path; + int ch, code, count, diffcount, oldcount; + FILE *fp; + + while ((ch = getopt(argc, argv, "")) != EOF) + switch(ch) { + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 1) + usage(); + + if ((fp = fopen(argv[0], "r")) == NULL) + err(1, "%s", argv[0]); + + /* First copy bigram array to stdout. */ + (void)fgets(bigrams, BGBUFSIZE + 1, fp); + if (fwrite(bigrams, 1, BGBUFSIZE, stdout) != BGBUFSIZE) + err(1, "stdout"); + (void)fclose(fp); + + oldpath = buf1; + path = buf2; + oldcount = 0; + while (fgets(path, sizeof(buf2), stdin) != NULL) { + /* Truncate newline. */ + cp = path + strlen(path) - 1; + if (cp > path && *cp == '\n') + *cp = '\0'; + + /* Squelch characters that would botch the decoding. */ + for (cp = path; *cp != '\0'; cp++) { + if ((u_char)*cp >= PARITY || *cp <= SWITCH) + *cp = '?'; + } + + /* Skip longest common prefix. */ + for (cp = path; *cp == *oldpath; cp++, oldpath++) + if (*oldpath == '\0') + break; + count = cp - path; + diffcount = count - oldcount + OFFSET; + oldcount = count; + if (diffcount < 0 || diffcount > 2 * OFFSET) { + if (putchar(SWITCH) == EOF || + putw(diffcount, stdout) == EOF) + err(1, "stdout"); + } else + if (putchar(diffcount) == EOF) + err(1, "stdout"); + + while (*cp != '\0') { + if (*(cp + 1) == '\0') { + if (putchar(*cp) == EOF) + err(1, "stdout"); + break; + } + if ((code = bgindex(cp)) < 0) { + if (putchar(*cp++) == EOF || + putchar(*cp++) == EOF) + err(1, "stdout"); + } else { + /* Found, so mark byte with parity bit. */ + if (putchar((code / 2) | PARITY) == EOF) + err(1, "stdout"); + cp += 2; + } + } + if (path == buf1) { /* swap pointers */ + path = buf2; + oldpath = buf1; + } else { + path = buf1; + oldpath = buf2; + } + } + /* Non-zero status if there were errors */ + if (fflush(stdout) != 0 || ferror(stdout)) + exit(1); + exit(0); +} + +int +bgindex(bg) /* Return location of bg in bigrams or -1. */ + char *bg; +{ + register char bg0, bg1, *p; + + bg0 = bg[0]; + bg1 = bg[1]; + for (p = bigrams; *p != '\0'; p++) + if (*p++ == bg0 && *p == bg1) + break; + return (*p == '\0' ? -1 : --p - bigrams); +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: locate.code common_bigrams < list > squozen_list\n"); + exit(1); +} diff --git a/usr.bin/locate/locate/updatedb.csh b/usr.bin/locate/locate/updatedb.csh new file mode 100644 index 00000000000..bca8bab541c --- /dev/null +++ b/usr.bin/locate/locate/updatedb.csh @@ -0,0 +1,77 @@ +#!/bin/csh -f +# +# Copyright (c) 1989, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# James A. Woods. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)updatedb.csh 8.4 (Berkeley) 10/27/94 +# + +set SRCHPATHS = "/" # directories to be put in the database +set LIBDIR = /usr/libexec # for subprograms + # for temp files +if (! $?TMPDIR) setenv TMPDIR /var/tmp +set FCODES = /var/db/locate.database # the database + +set path = ( /bin /usr/bin ) +set bigrams = $TMPDIR/locate.bigrams.$$ +set filelist = $TMPDIR/locate.list.$$ +set errs = $TMPDIR/locate.errs.$$ + +# Make a file list and compute common bigrams. +# Alphabetize '/' before any other char with 'tr'. +# If the system is very short of sort space, 'bigram' can be made +# smarter to accumulate common bigrams directly without sorting +# ('awk', with its associative memory capacity, can do this in several +# lines, but is too slow, and runs out of string space on small machines). + +# search locally or everything +# find ${SRCHPATHS} -print | \ +find ${SRCHPATHS} \! -fstype local -prune -or -print | \ + tr '/' '\001' | \ + (sort -T $TMPDIR -f; echo $status > $errs) | tr '\001' '/' > $filelist + +$LIBDIR/locate.bigram < $filelist | \ + (sort -T /$TMPDIR; echo $status >> $errs) | \ + uniq -c | sort -T /$TMPDIR -nr | \ + awk '{ if (NR <= 128) print $2 }' | tr -d '\012' > $bigrams + +# code the file list + +if { grep -s -v 0 $errs } then + printf 'locate: updatedb failed\n\n' +else + $LIBDIR/locate.code $bigrams < $filelist > $FCODES + chmod 644 $FCODES + rm $bigrams $filelist $errs +endif diff --git a/usr.bin/login/login.1 b/usr.bin/login/login.1 new file mode 100644 index 00000000000..6c78b23216b --- /dev/null +++ b/usr.bin/login/login.1 @@ -0,0 +1,148 @@ +.\" 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. +.\" +.\" @(#)login.1 8.2 (Berkeley) 5/5/94 +.\" +.Dd May 5, 1994 +.Dt LOGIN 1 +.Os BSD 4 +.Sh NAME +.Nm login +.Nd log into the computer +.Sh SYNOPSIS +.Nm login +.Op Fl fp +.Op Fl h Ar hostname +.Op Ar user +.Sh DESCRIPTION +The +.Nm login +utility logs users (and pseudo-users) into the computer system. +.Pp +If no user is specified, or if a user is specified and authentication +of the user fails, +.Nm login +prompts for a user name. +Authentication of users is done via passwords. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl f +The +.Fl f +option is used when a user name is specified to indicate that proper +authentication has already been done and that no password need be +requested. +This option may only be used by the super-user or when an already +logged in user is logging in as themselves. +.It Fl h +The +.Fl h +option specifies the host from which the connection was received. +It is used by various daemons such as +.Xr telnetd 8 . +This option may only be used by the super-user. +.It Fl p +By default, +.Nm login +discards any previous environment. +The +.Fl p +option disables this behavior. +.El +.Pp +If the file +.Pa /etc/nologin +exists, +.Nm login +dislays its contents to the user and exits. +This is used by +.Xr shutdown 8 +to prevent users from logging in when the system is about to go down. +.Pp +Immediately after logging a user in, +.Nm login +displays the system copyright notice, the date and time the user last +logged in, the message of the day as well as other information. +If the file +.Dq Pa .hushlogin +exists in the user's home directory, all of these messages are suppressed. +This is to simplify logins for non-human users, such as +.Xr uucp 1 . +.Nm Login +then records an entry in the +.Xr wtmp 5 +and +.Xr utmp 5 +files and executes the user's command interpretor. +.Pp +Login enters information into the environment (see +.Xr environ 7 ) +specifying the user's home directory (HOME), command interpreter (SHELL), +search path (PATH), terminal type (TERM) and user name (both LOGNAME and +USER). +.Pp +The standard shells, +.Xr csh 1 +and +.Xr sh 1 , +do not fork before executing the +.Nm login +utility. +.Sh FILES +.Bl -tag -width /var/mail/userXXX -compact +.It Pa /etc/motd +message-of-the-day +.It Pa /etc/nologin +disallows logins +.It Pa /var/run/utmp +current logins +.It Pa /var/log/lastlog +last login account records +.It Pa /var/log/wtmp +login account records +.It Pa /var/mail/user +system mailboxes +.It Pa \&.hushlogin +makes login quieter +.El +.Sh SEE ALSO +.Xr chpass 1 , +.Xr passwd 1 , +.Xr rlogin 1 , +.Xr getpass 3 , +.Xr utmp 5 , +.Xr environ 7 , +.Sh HISTORY +A +.Nm login +appeared in +.At v6 . diff --git a/usr.bin/lorder/lorder.1 b/usr.bin/lorder/lorder.1 new file mode 100644 index 00000000000..387481f5d53 --- /dev/null +++ b/usr.bin/lorder/lorder.1 @@ -0,0 +1,73 @@ +.\" 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. +.\" +.\" @(#)lorder.1 8.2 (Berkeley) 4/28/95 +.\" +.Dd April 28, 1995 +.Dt LORDER 1 +.Os +.Sh NAME +.Nm lorder +.Nd list dependencies for object files +.Sh SYNOPSIS +.Nm lorder +.Ar +.Sh DESCRIPTION +The +.Nm lorder +utility uses +.Xr nm 1 +to determine interdependencies in the list of object files +specified on the command line. +.Nm Lorder +outputs a list of file names where the first file contains a symbol +which is defined by the second file. +.Pp +The output is normally used with +.Xr tsort 1 +when a library is created to determine the optimum ordering of the +object modules so that all references may be resolved in a single +pass of the loader. +.Sh EXAMPLES +.Bd -literal -offset indent +ar cr library.a `lorder ${OBJS} | tsort` +.Ed +.Sh SEE ALSO +.Xr ar 1 , +.Xr ld 1 , +.Xr nm 1 , +.Xr ranlib 1 , +.Xr tsort 1 +.Sh HISTORY +An +.Nm lorder +utility appeared in +.At v7 . diff --git a/usr.bin/m4/eval.c b/usr.bin/m4/eval.c new file mode 100644 index 00000000000..aa3da5ed53e --- /dev/null +++ b/usr.bin/m4/eval.c @@ -0,0 +1,790 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ozan Yigit at York University. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)eval.c 8.2 (Berkeley) 4/27/95"; +#endif /* not lint */ + +/* + * eval.c + * Facility: m4 macro processor + * by: oz + */ + +#include +#include +#include +#include +#include +#include +#include "mdef.h" +#include "stdd.h" +#include "extern.h" +#include "pathnames.h" + +/* + * eval - evaluate built-in macros. + * argc - number of elements in argv. + * argv - element vector : + * argv[0] = definition of a user + * macro or nil if built-in. + * argv[1] = name of the macro or + * built-in. + * argv[2] = parameters to user-defined + * . macro or built-in. + * . + * + * Note that the minimum value for argc is 3. A call in the form + * of macro-or-builtin() will result in: + * argv[0] = nullstr + * argv[1] = macro-or-builtin + * argv[2] = nullstr + */ + +void +eval(argv, argc, td) +register char *argv[]; +register int argc; +register int td; +{ + register int c, n; + static int sysval = 0; + +#ifdef DEBUG + printf("argc = %d\n", argc); + for (n = 0; n < argc; n++) + printf("argv[%d] = %s\n", n, argv[n]); +#endif + /* + * if argc == 3 and argv[2] is null, then we + * have macro-or-builtin() type call. We adjust + * argc to avoid further checking.. + */ + if (argc == 3 && !*(argv[2])) + argc--; + + switch (td & ~STATIC) { + + case DEFITYPE: + if (argc > 2) + dodefine(argv[2], (argc > 3) ? argv[3] : null); + break; + + case PUSDTYPE: + if (argc > 2) + dopushdef(argv[2], (argc > 3) ? argv[3] : null); + break; + + case DUMPTYPE: + dodump(argv, argc); + break; + + case EXPRTYPE: + /* + * doexpr - evaluate arithmetic + * expression + */ + if (argc > 2) + pbnum(expr(argv[2])); + break; + + case IFELTYPE: + if (argc > 4) + doifelse(argv, argc); + break; + + case IFDFTYPE: + /* + * doifdef - select one of two + * alternatives based on the existence of + * another definition + */ + if (argc > 3) { + if (lookup(argv[2]) != nil) + pbstr(argv[3]); + else if (argc > 4) + pbstr(argv[4]); + } + break; + + case LENGTYPE: + /* + * dolen - find the length of the + * argument + */ + if (argc > 2) + pbnum((argc > 2) ? strlen(argv[2]) : 0); + break; + + case INCRTYPE: + /* + * doincr - increment the value of the + * argument + */ + if (argc > 2) + pbnum(atoi(argv[2]) + 1); + break; + + case DECRTYPE: + /* + * dodecr - decrement the value of the + * argument + */ + if (argc > 2) + pbnum(atoi(argv[2]) - 1); + break; + + case SYSCTYPE: + /* + * dosys - execute system command + */ + if (argc > 2) + sysval = system(argv[2]); + break; + + case SYSVTYPE: + /* + * dosysval - return value of the last + * system call. + * + */ + pbnum(sysval); + break; + + case INCLTYPE: + if (argc > 2) + if (!doincl(argv[2])) + oops("%s: %s", argv[2], strerror(errno)); + break; + + case SINCTYPE: + if (argc > 2) + (void) doincl(argv[2]); + break; +#ifdef EXTENDED + case PASTTYPE: + if (argc > 2) + if (!dopaste(argv[2])) + oops("%s: %s", argv[2], strerror(errno)); + break; + + case SPASTYPE: + if (argc > 2) + (void) dopaste(argv[2]); + break; +#endif + case CHNQTYPE: + dochq(argv, argc); + break; + + case CHNCTYPE: + dochc(argv, argc); + break; + + case SUBSTYPE: + /* + * dosub - select substring + * + */ + if (argc > 3) + dosub(argv, argc); + break; + + case SHIFTYPE: + /* + * doshift - push back all arguments + * except the first one (i.e. skip + * argv[2]) + */ + if (argc > 3) { + for (n = argc - 1; n > 3; n--) { + putback(rquote); + pbstr(argv[n]); + putback(lquote); + putback(','); + } + putback(rquote); + pbstr(argv[3]); + putback(lquote); + } + break; + + case DIVRTYPE: + if (argc > 2 && (n = atoi(argv[2])) != 0) + dodiv(n); + else { + active = stdout; + oindex = 0; + } + break; + + case UNDVTYPE: + doundiv(argv, argc); + break; + + case DIVNTYPE: + /* + * dodivnum - return the number of + * current output diversion + */ + pbnum(oindex); + break; + + case UNDFTYPE: + /* + * doundefine - undefine a previously + * defined macro(s) or m4 keyword(s). + */ + if (argc > 2) + for (n = 2; n < argc; n++) + remhash(argv[n], ALL); + break; + + case POPDTYPE: + /* + * dopopdef - remove the topmost + * definitions of macro(s) or m4 + * keyword(s). + */ + if (argc > 2) + for (n = 2; n < argc; n++) + remhash(argv[n], TOP); + break; + + case MKTMTYPE: + /* + * dotemp - create a temporary file + */ + if (argc > 2) + pbstr(mktemp(argv[2])); + break; + + case TRNLTYPE: + /* + * dotranslit - replace all characters in + * the source string that appears in the + * "from" string with the corresponding + * characters in the "to" string. + */ + if (argc > 3) { + char temp[MAXTOK]; + if (argc > 4) + map(temp, argv[2], argv[3], argv[4]); + else + map(temp, argv[2], argv[3], null); + pbstr(temp); + } + else if (argc > 2) + pbstr(argv[2]); + break; + + case INDXTYPE: + /* + * doindex - find the index of the second + * argument string in the first argument + * string. -1 if not present. + */ + pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1); + break; + + case ERRPTYPE: + /* + * doerrp - print the arguments to stderr + * file + */ + if (argc > 2) { + for (n = 2; n < argc; n++) + fprintf(stderr, "%s ", argv[n]); + fprintf(stderr, "\n"); + } + break; + + case DNLNTYPE: + /* + * dodnl - eat-up-to and including + * newline + */ + while ((c = gpbc()) != '\n' && c != EOF) + ; + break; + + case M4WRTYPE: + /* + * dom4wrap - set up for + * wrap-up/wind-down activity + */ + m4wraps = (argc > 2) ? xstrdup(argv[2]) : null; + break; + + case EXITTYPE: + /* + * doexit - immediate exit from m4. + */ + killdiv(); + exit((argc > 2) ? atoi(argv[2]) : 0); + break; + + case DEFNTYPE: + if (argc > 2) + for (n = 2; n < argc; n++) + dodefn(argv[n]); + break; + + default: + oops("%s: major botch.", "eval"); + break; + } +} + +char *dumpfmt = "`%s'\t`%s'\n"; /* format string for dumpdef */ + +/* + * expand - user-defined macro expansion + */ +void +expand(argv, argc) +register char *argv[]; +register int argc; +{ + register char *t; + register char *p; + register int n; + register int argno; + + t = argv[0]; /* defn string as a whole */ + p = t; + while (*p) + p++; + p--; /* last character of defn */ + while (p > t) { + if (*(p - 1) != ARGFLAG) + putback(*p); + else { + switch (*p) { + + case '#': + pbnum(argc - 2); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if ((argno = *p - '0') < argc - 1) + pbstr(argv[argno + 1]); + break; + case '*': + for (n = argc - 1; n > 2; n--) { + pbstr(argv[n]); + putback(','); + } + pbstr(argv[2]); + break; + default: + putback(*p); + putback('$'); + break; + } + p--; + } + p--; + } + if (p == t) /* do last character */ + putback(*p); +} + +/* + * dodefine - install definition in the table + */ +void +dodefine(name, defn) +register char *name; +register char *defn; +{ + register ndptr p; + + if (!*name) + oops("null definition."); + if (STREQ(name, defn)) + oops("%s: recursive definition.", name); + if ((p = lookup(name)) == nil) + p = addent(name); + else if (p->defn != null) + free((char *) p->defn); + if (!*defn) + p->defn = null; + else + p->defn = xstrdup(defn); + p->type = MACRTYPE; +} + +/* + * dodefn - push back a quoted definition of + * the given name. + */ +void +dodefn(name) +char *name; +{ + register ndptr p; + + if ((p = lookup(name)) != nil && p->defn != null) { + putback(rquote); + pbstr(p->defn); + putback(lquote); + } +} + +/* + * dopushdef - install a definition in the hash table + * without removing a previous definition. Since + * each new entry is entered in *front* of the + * hash bucket, it hides a previous definition from + * lookup. + */ +void +dopushdef(name, defn) +register char *name; +register char *defn; +{ + register ndptr p; + + if (!*name) + oops("null definition"); + if (STREQ(name, defn)) + oops("%s: recursive definition.", name); + p = addent(name); + if (!*defn) + p->defn = null; + else + p->defn = xstrdup(defn); + p->type = MACRTYPE; +} + +/* + * dodumpdef - dump the specified definitions in the hash + * table to stderr. If nothing is specified, the entire + * hash table is dumped. + */ +void +dodump(argv, argc) +register char *argv[]; +register int argc; +{ + register int n; + ndptr p; + + if (argc > 2) { + for (n = 2; n < argc; n++) + if ((p = lookup(argv[n])) != nil) + fprintf(stderr, dumpfmt, p->name, + p->defn); + } + else { + for (n = 0; n < HASHSIZE; n++) + for (p = hashtab[n]; p != nil; p = p->nxtptr) + fprintf(stderr, dumpfmt, p->name, + p->defn); + } +} + +/* + * doifelse - select one of two alternatives - loop. + */ +void +doifelse(argv, argc) +register char *argv[]; +register int argc; +{ + cycle { + if (STREQ(argv[2], argv[3])) + pbstr(argv[4]); + else if (argc == 6) + pbstr(argv[5]); + else if (argc > 6) { + argv += 3; + argc -= 3; + continue; + } + break; + } +} + +/* + * doinclude - include a given file. + */ +int +doincl(ifile) +char *ifile; +{ + if (ilevel + 1 == MAXINP) + oops("too many include files."); + if ((infile[ilevel + 1] = fopen(ifile, "r")) != NULL) { + ilevel++; + bbase[ilevel] = bufbase = bp; + return (1); + } + else + return (0); +} + +#ifdef EXTENDED +/* + * dopaste - include a given file without any + * macro processing. + */ +int +dopaste(pfile) +char *pfile; +{ + FILE *pf; + register int c; + + if ((pf = fopen(pfile, "r")) != NULL) { + while ((c = getc(pf)) != EOF) + putc(c, active); + (void) fclose(pf); + return (1); + } + else + return (0); +} +#endif + +/* + * dochq - change quote characters + */ +void +dochq(argv, argc) +register char *argv[]; +register int argc; +{ + if (argc > 2) { + if (*argv[2]) + lquote = *argv[2]; + if (argc > 3) { + if (*argv[3]) + rquote = *argv[3]; + } + else + rquote = lquote; + } + else { + lquote = LQUOTE; + rquote = RQUOTE; + } +} + +/* + * dochc - change comment characters + */ +void +dochc(argv, argc) +register char *argv[]; +register int argc; +{ + if (argc > 2) { + if (*argv[2]) + scommt = *argv[2]; + if (argc > 3) { + if (*argv[3]) + ecommt = *argv[3]; + } + else + ecommt = ECOMMT; + } + else { + scommt = SCOMMT; + ecommt = ECOMMT; + } +} + +/* + * dodivert - divert the output to a temporary file + */ +void +dodiv(n) +register int n; +{ + if (n < 0 || n >= MAXOUT) + n = 0; /* bitbucket */ + if (outfile[n] == NULL) { + m4temp[UNIQUE] = n + '0'; + if ((outfile[n] = fopen(m4temp, "w")) == NULL) + oops("%s: cannot divert.", m4temp); + } + oindex = n; + active = outfile[n]; +} + +/* + * doundivert - undivert a specified output, or all + * other outputs, in numerical order. + */ +void +doundiv(argv, argc) +register char *argv[]; +register int argc; +{ + register int ind; + register int n; + + if (argc > 2) { + for (ind = 2; ind < argc; ind++) { + n = atoi(argv[ind]); + if (n > 0 && n < MAXOUT && outfile[n] != NULL) + getdiv(n); + + } + } + else + for (n = 1; n < MAXOUT; n++) + if (outfile[n] != NULL) + getdiv(n); +} + +/* + * dosub - select substring + */ +void +dosub(argv, argc) +register char *argv[]; +register int argc; +{ + register char *ap, *fc, *k; + register int nc; + + if (argc < 5) + nc = MAXTOK; + else +#ifdef EXPR + nc = expr(argv[4]); +#else + nc = atoi(argv[4]); +#endif + ap = argv[2]; /* target string */ +#ifdef EXPR + fc = ap + expr(argv[3]); /* first char */ +#else + fc = ap + atoi(argv[3]); /* first char */ +#endif + if (fc >= ap && fc < ap + strlen(ap)) + for (k = fc + min(nc, strlen(fc)) - 1; k >= fc; k--) + putback(*k); +} + +/* + * map: + * map every character of s1 that is specified in from + * into s3 and replace in s. (source s1 remains untouched) + * + * This is a standard implementation of map(s,from,to) function of ICON + * language. Within mapvec, we replace every character of "from" with + * the corresponding character in "to". If "to" is shorter than "from", + * than the corresponding entries are null, which means that those + * characters dissapear altogether. Furthermore, imagine + * map(dest, "sourcestring", "srtin", "rn..*") type call. In this case, + * `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s' + * ultimately maps to `*'. In order to achieve this effect in an efficient + * manner (i.e. without multiple passes over the destination string), we + * loop over mapvec, starting with the initial source character. if the + * character value (dch) in this location is different than the source + * character (sch), sch becomes dch, once again to index into mapvec, until + * the character value stabilizes (i.e. sch = dch, in other words + * mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary + * character, it will stabilize, since mapvec[0] == 0 at all times. At the + * end, we restore mapvec* back to normal where mapvec[n] == n for + * 0 <= n <= 127. This strategy, along with the restoration of mapvec, is + * about 5 times faster than any algorithm that makes multiple passes over + * destination string. + */ +void +map(dest, src, from, to) +register char *dest; +register char *src; +register char *from; +register char *to; +{ + register char *tmp; + register char sch, dch; + static char mapvec[128] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127 + }; + + if (*src) { + tmp = from; + /* + * create a mapping between "from" and + * "to" + */ + while (*from) + mapvec[*from++] = (*to) ? *to++ : (char) 0; + + while (*src) { + sch = *src++; + dch = mapvec[sch]; + while (dch != sch) { + sch = dch; + dch = mapvec[sch]; + } + if (*dest = dch) + dest++; + } + /* + * restore all the changed characters + */ + while (*tmp) { + mapvec[*tmp] = *tmp; + tmp++; + } + } + *dest = (char) 0; +} diff --git a/usr.bin/m4/expr.c b/usr.bin/m4/expr.c new file mode 100644 index 00000000000..8ee66b6ba49 --- /dev/null +++ b/usr.bin/m4/expr.c @@ -0,0 +1,574 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ozan Yigit at York University. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE 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[] = "@(#)expr.c 8.2 (Berkeley) 4/29/95"; +#endif /* not lint */ + +#include +#include + +/* + * expression evaluator: performs a standard recursive + * descent parse to evaluate any expression permissible + * within the following grammar: + * + * expr : query EOS + * query : lor + * | lor "?" query ":" query + * lor : land { "||" land } + * land : not { "&&" not } + * not : eqrel + * | '!' not + * eqrel : shift { eqrelop shift } + * shift : primary { shop primary } + * primary : term { addop term } + * term : exp { mulop exp } + * exp : unary { expop unary } + * unary : factor + * | unop unary + * factor : constant + * | "(" query ")" + * constant: num + * | "'" CHAR "'" + * num : DIGIT + * | DIGIT num + * shop : "<<" + * | ">>" + * eqrel : "=" + * | "==" + * | "!=" + * | "<" + * | ">" + * | "<=" + * | ">=" + * + * + * This expression evaluator is lifted from a public-domain + * C Pre-Processor included with the DECUS C Compiler distribution. + * It is hacked somewhat to be suitable for m4. + * + * Originally by: Mike Lutz + * Bob Harper + */ + +#define TRUE 1 +#define FALSE 0 +#define EOS (char) 0 +#define EQL 0 +#define NEQ 1 +#define LSS 2 +#define LEQ 3 +#define GTR 4 +#define GEQ 5 +#define OCTAL 8 +#define DECIMAL 10 + +static char *nxtch; /* Parser scan pointer */ + +static int query __P((void)); +static int lor __P((void)); +static int land __P((void)); +static int not __P((void)); +static int eqrel __P((void)); +static int shift __P((void)); +static int primary __P((void)); +static int term __P((void)); +static int exp __P((void)); +static int unary __P((void)); +static int factor __P((void)); +static int constant __P((void)); +static int num __P((void)); +static int geteqrel __P((void)); +static int skipws __P((void)); +static void experr __P((char *)); + +/* + * For longjmp + */ +#include +static jmp_buf expjump; + +/* + * macros: + * ungetch - Put back the last character examined. + * getch - return the next character from expr string. + */ +#define ungetch() nxtch-- +#define getch() *nxtch++ + +int +expr(expbuf) +char *expbuf; +{ + register int rval; + + nxtch = expbuf; + if (setjmp(expjump) != 0) + return FALSE; + + rval = query(); + if (skipws() == EOS) + return rval; + + printf("m4: ill-formed expression.\n"); + return FALSE; +} + +/* + * query : lor | lor '?' query ':' query + */ +static int +query() +{ + register int bool, true_val, false_val; + + bool = lor(); + if (skipws() != '?') { + ungetch(); + return bool; + } + + true_val = query(); + if (skipws() != ':') + experr("bad query"); + + false_val = query(); + return bool ? true_val : false_val; +} + +/* + * lor : land { '||' land } + */ +static int +lor() +{ + register int c, vl, vr; + + vl = land(); + while ((c = skipws()) == '|') { + if (getch() != '|') + ungetch(); + vr = land(); + vl = vl || vr; + } + + ungetch(); + return vl; +} + +/* + * land : not { '&&' not } + */ +static int +land() +{ + register int c, vl, vr; + + vl = not(); + while ((c = skipws()) == '&') { + if (getch() != '&') + ungetch(); + vr = not(); + vl = vl && vr; + } + + ungetch(); + return vl; +} + +/* + * not : eqrel | '!' not + */ +static int +not() +{ + register int val, c; + + if ((c = skipws()) == '!' && getch() != '=') { + ungetch(); + val = not(); + return !val; + } + + if (c == '!') + ungetch(); + ungetch(); + return eqrel(); +} + +/* + * eqrel : shift { eqrelop shift } + */ +static int +eqrel() +{ + register int vl, vr, eqrel; + + vl = shift(); + while ((eqrel = geteqrel()) != -1) { + vr = shift(); + + switch (eqrel) { + + case EQL: + vl = (vl == vr); + break; + case NEQ: + vl = (vl != vr); + break; + + case LEQ: + vl = (vl <= vr); + break; + case LSS: + vl = (vl < vr); + break; + case GTR: + vl = (vl > vr); + break; + case GEQ: + vl = (vl >= vr); + break; + } + } + return vl; +} + +/* + * shift : primary { shop primary } + */ +static int +shift() +{ + register int vl, vr, c; + + vl = primary(); + while (((c = skipws()) == '<' || c == '>') && getch() == c) { + vr = primary(); + + if (c == '<') + vl <<= vr; + else + vl >>= vr; + } + + if (c == '<' || c == '>') + ungetch(); + ungetch(); + return vl; +} + +/* + * primary : term { addop term } + */ +static int +primary() +{ + register int c, vl, vr; + + vl = term(); + while ((c = skipws()) == '+' || c == '-') { + vr = term(); + + if (c == '+') + vl += vr; + else + vl -= vr; + } + + ungetch(); + return vl; +} + +/* + * := { } + */ +static int +term() +{ + register int c, vl, vr; + + vl = exp(); + while ((c = skipws()) == '*' || c == '/' || c == '%') { + vr = exp(); + + switch (c) { + case '*': + vl *= vr; + break; + case '/': + vl /= vr; + break; + case '%': + vl %= vr; + break; + } + } + ungetch(); + return vl; +} + +/* + * := { } + */ +static int +exp() +{ + register c, vl, vr, n; + + vl = unary(); + switch (c = skipws()) { + + case '*': + if (getch() != '*') { + ungetch(); + break; + } + + case '^': + vr = exp(); + n = 1; + while (vr-- > 0) + n *= vl; + return n; + } + + ungetch(); + return vl; +} + +/* + * unary : factor | unop unary + */ +static int +unary() +{ + register int val, c; + + if ((c = skipws()) == '+' || c == '-' || c == '~') { + val = unary(); + + switch (c) { + case '+': + return val; + case '-': + return -val; + case '~': + return ~val; + } + } + + ungetch(); + return factor(); +} + +/* + * factor : constant | '(' query ')' + */ +static int +factor() +{ + register int val; + + if (skipws() == '(') { + val = query(); + if (skipws() != ')') + experr("bad factor"); + return val; + } + + ungetch(); + return constant(); +} + +/* + * constant: num | 'char' + * Note: constant() handles multi-byte constants + */ +static int +constant() +{ + register int i; + register int value; + register char c; + int v[sizeof(int)]; + + if (skipws() != '\'') { + ungetch(); + return num(); + } + for (i = 0; i < sizeof(int); i++) { + if ((c = getch()) == '\'') { + ungetch(); + break; + } + if (c == '\\') { + switch (c = getch()) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + ungetch(); + c = num(); + break; + case 'n': + c = 012; + break; + case 'r': + c = 015; + break; + case 't': + c = 011; + break; + case 'b': + c = 010; + break; + case 'f': + c = 014; + break; + } + } + v[i] = c; + } + if (i == 0 || getch() != '\'') + experr("illegal character constant"); + for (value = 0; --i >= 0;) { + value <<= 8; + value += v[i]; + } + return value; +} + +/* + * num : digit | num digit + */ +static int +num() +{ + register int rval, c, base; + int ndig; + + base = ((c = skipws()) == '0') ? OCTAL : DECIMAL; + rval = 0; + ndig = 0; + while (c >= '0' && c <= (base == OCTAL ? '7' : '9')) { + rval *= base; + rval += (c - '0'); + c = getch(); + ndig++; + } + ungetch(); + + if (ndig == 0) + experr("bad constant"); + + return rval; + +} + +/* + * eqrel : '=' | '==' | '!=' | '<' | '>' | '<=' | '>=' + */ +static int +geteqrel() +{ + register int c1, c2; + + c1 = skipws(); + c2 = getch(); + + switch (c1) { + + case '=': + if (c2 != '=') + ungetch(); + return EQL; + + case '!': + if (c2 == '=') + return NEQ; + ungetch(); + ungetch(); + return -1; + + case '<': + if (c2 == '=') + return LEQ; + ungetch(); + return LSS; + + case '>': + if (c2 == '=') + return GEQ; + ungetch(); + return GTR; + + default: + ungetch(); + ungetch(); + return -1; + } +} + +/* + * Skip over any white space and return terminating char. + */ +static int +skipws() +{ + register char c; + + while ((c = getch()) <= ' ' && c > EOS) + ; + return c; +} + +/* + * resets environment to eval(), prints an error + * and forces eval to return FALSE. + */ +static void +experr(msg) +char *msg; +{ + printf("m4: %s in expr.\n", msg); + longjmp(expjump, -1); +} diff --git a/usr.bin/m4/serv.c b/usr.bin/m4/serv.c new file mode 100644 index 00000000000..54a2e59dce4 --- /dev/null +++ b/usr.bin/m4/serv.c @@ -0,0 +1,475 @@ +/* + * Copyright (c) 1989 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * This code is derived from software contributed to Berkeley by + * Ozan Yigit. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE 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[] = "@(#)serv.c 5.4 (Berkeley) 1/21/94"; +#endif /* not lint */ + +/* + * serv.c + * Facility: m4 macro processor + * by: oz + */ + +#include +#include +#include +#include "mdef.h" +#include "extr.h" +#include "pathnames.h" + +extern ndptr lookup(); +extern ndptr addent(); + +char *dumpfmt = "`%s'\t`%s'\n"; /* format string for dumpdef */ + +/* + * expand - user-defined macro expansion + * + */ +expand(argv, argc) +register char *argv[]; +register int argc; +{ + register char *t; + register char *p; + register int n; + register int argno; + + t = argv[0]; /* defn string as a whole */ + p = t; + while (*p) + p++; + p--; /* last character of defn */ + while (p > t) { + if (*(p-1) != ARGFLAG) + putback(*p); + else { + switch (*p) { + + case '#': + pbnum(argc-2); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if ((argno = *p - '0') < argc-1) + pbstr(argv[argno+1]); + break; + case '*': + for (n = argc - 1; n > 2; n--) { + pbstr(argv[n]); + putback(','); + } + pbstr(argv[2]); + break; + default : + putback(*p); + break; + } + p--; + } + p--; + } + if (p == t) /* do last character */ + putback(*p); +} + +/* + * dodefine - install definition in the table + * + */ +dodefine(name, defn) +register char *name; +register char *defn; +{ + register ndptr p; + + if (!*name) + error("m4: null definition."); + if (strcmp(name, defn) == 0) + error("m4: recursive definition."); + if ((p = lookup(name)) == nil) + p = addent(name); + else if (p->defn != null) + free(p->defn); + if (!*defn) + p->defn = null; + else + p->defn = strdup(defn); + p->type = MACRTYPE; +} + +/* + * dodefn - push back a quoted definition of + * the given name. + */ + +dodefn(name) +char *name; +{ + register ndptr p; + + if ((p = lookup(name)) != nil && p->defn != null) { + putback(rquote); + pbstr(p->defn); + putback(lquote); + } +} + +/* + * dopushdef - install a definition in the hash table + * without removing a previous definition. Since + * each new entry is entered in *front* of the + * hash bucket, it hides a previous definition from + * lookup. + */ +dopushdef(name, defn) +register char *name; +register char *defn; +{ + register ndptr p; + + if (!*name) + error("m4: null definition"); + if (strcmp(name, defn) == 0) + error("m4: recursive definition."); + p = addent(name); + if (!*defn) + p->defn = null; + else + p->defn = strdup(defn); + p->type = MACRTYPE; +} + +/* + * dodumpdef - dump the specified definitions in the hash + * table to stderr. If nothing is specified, the entire + * hash table is dumped. + * + */ +dodump(argv, argc) +register char *argv[]; +register int argc; +{ + register int n; + ndptr p; + + if (argc > 2) { + for (n = 2; n < argc; n++) + if ((p = lookup(argv[n])) != nil) + fprintf(stderr, dumpfmt, p->name, + p->defn); + } + else { + for (n = 0; n < HASHSIZE; n++) + for (p = hashtab[n]; p != nil; p = p->nxtptr) + fprintf(stderr, dumpfmt, p->name, + p->defn); + } +} + +/* + * doifelse - select one of two alternatives - loop. + * + */ +doifelse(argv,argc) +register char *argv[]; +register int argc; +{ + cycle { + if (strcmp(argv[2], argv[3]) == 0) + pbstr(argv[4]); + else if (argc == 6) + pbstr(argv[5]); + else if (argc > 6) { + argv += 3; + argc -= 3; + continue; + } + break; + } +} + +/* + * doinclude - include a given file. + * + */ +doincl(ifile) +char *ifile; +{ + if (ilevel+1 == MAXINP) + error("m4: too many include files."); + if ((infile[ilevel+1] = fopen(ifile, "r")) != NULL) { + ilevel++; + return (1); + } + else + return (0); +} + +#ifdef EXTENDED +/* + * dopaste - include a given file without any + * macro processing. + */ +dopaste(pfile) +char *pfile; +{ + FILE *pf; + register int c; + + if ((pf = fopen(pfile, "r")) != NULL) { + while((c = getc(pf)) != EOF) + putc(c, active); + (void) fclose(pf); + return(1); + } + else + return(0); +} +#endif + +/* + * dochq - change quote characters + * + */ +dochq(argv, argc) +register char *argv[]; +register int argc; +{ + if (argc > 2) { + if (*argv[2]) + lquote = *argv[2]; + if (argc > 3) { + if (*argv[3]) + rquote = *argv[3]; + } + else + rquote = lquote; + } + else { + lquote = LQUOTE; + rquote = RQUOTE; + } +} + +/* + * dochc - change comment characters + * + */ +dochc(argv, argc) +register char *argv[]; +register int argc; +{ + if (argc > 2) { + if (*argv[2]) + scommt = *argv[2]; + if (argc > 3) { + if (*argv[3]) + ecommt = *argv[3]; + } + else + ecommt = ECOMMT; + } + else { + scommt = SCOMMT; + ecommt = ECOMMT; + } +} + +/* + * dodivert - divert the output to a temporary file + * + */ +dodiv(n) +register int n; +{ + if (n < 0 || n >= MAXOUT) + n = 0; /* bitbucket */ + if (outfile[n] == NULL) { + m4temp[UNIQUE] = n + '0'; + if ((outfile[n] = fopen(m4temp, "w")) == NULL) + error("m4: cannot divert."); + } + oindex = n; + active = outfile[n]; +} + +/* + * doundivert - undivert a specified output, or all + * other outputs, in numerical order. + */ +doundiv(argv, argc) +register char *argv[]; +register int argc; +{ + register int ind; + register int n; + + if (argc > 2) { + for (ind = 2; ind < argc; ind++) { + n = atoi(argv[ind]); + if (n > 0 && n < MAXOUT && outfile[n] != NULL) + getdiv(n); + + } + } + else + for (n = 1; n < MAXOUT; n++) + if (outfile[n] != NULL) + getdiv(n); +} + +/* + * dosub - select substring + * + */ +dosub (argv, argc) +register char *argv[]; +register int argc; +{ + register char *ap, *fc, *k; + register int nc; + + if (argc < 5) + nc = MAXTOK; + else +#ifdef EXPR + nc = expr(argv[4]); +#else + nc = atoi(argv[4]); +#endif + ap = argv[2]; /* target string */ +#ifdef EXPR + fc = ap + expr(argv[3]); /* first char */ +#else + fc = ap + atoi(argv[3]); /* first char */ +#endif + if (fc >= ap && fc < ap+strlen(ap)) + for (k = fc+min(nc,strlen(fc))-1; k >= fc; k--) + putback(*k); +} + +/* + * map: + * map every character of s1 that is specified in from + * into s3 and replace in s. (source s1 remains untouched) + * + * This is a standard implementation of map(s,from,to) function of ICON + * language. Within mapvec, we replace every character of "from" with + * the corresponding character in "to". If "to" is shorter than "from", + * than the corresponding entries are null, which means that those + * characters dissapear altogether. Furthermore, imagine + * map(dest, "sourcestring", "srtin", "rn..*") type call. In this case, + * `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s' + * ultimately maps to `*'. In order to achieve this effect in an efficient + * manner (i.e. without multiple passes over the destination string), we + * loop over mapvec, starting with the initial source character. if the + * character value (dch) in this location is different than the source + * character (sch), sch becomes dch, once again to index into mapvec, until + * the character value stabilizes (i.e. sch = dch, in other words + * mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary + * character, it will stabilize, since mapvec[0] == 0 at all times. At the + * end, we restore mapvec* back to normal where mapvec[n] == n for + * 0 <= n <= 127. This strategy, along with the restoration of mapvec, is + * about 5 times faster than any algorithm that makes multiple passes over + * destination string. + * + */ + +map(dest,src,from,to) +register char *dest; +register char *src; +register char *from; +register char *to; +{ + register char *tmp; + register char sch, dch; + static char mapvec[128] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127 + }; + + if (*src) { + tmp = from; + /* + * create a mapping between "from" and "to" + */ + while (*from) + mapvec[*from++] = (*to) ? *to++ : (char) 0; + + while (*src) { + sch = *src++; + dch = mapvec[sch]; + while (dch != sch) { + sch = dch; + dch = mapvec[sch]; + } + if (*dest = dch) + dest++; + } + /* + * restore all the changed characters + */ + while (*tmp) { + mapvec[*tmp] = *tmp; + tmp++; + } + } + *dest = (char) 0; +} diff --git a/usr.bin/man/Makefile b/usr.bin/man/Makefile new file mode 100644 index 00000000000..728d038156f --- /dev/null +++ b/usr.bin/man/Makefile @@ -0,0 +1,8 @@ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 + +PROG= man +SRCS= config.c man.c +MAN1= man.0 +MAN5= man.conf.0 + +.include diff --git a/usr.bin/man/config.c b/usr.bin/man/config.c new file mode 100644 index 00000000000..42780fa77d3 --- /dev/null +++ b/usr.bin/man/config.c @@ -0,0 +1,176 @@ +/* + * Copyright (c) 1989, 1993, 1995 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)config.c 8.8 (Berkeley) 1/31/95"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "pathnames.h" + +struct _head head; + +/* + * config -- + * + * Read the configuration file and build a doubly linked + * list that looks like: + * + * tag1 <-> record <-> record <-> record + * | + * tag2 <-> record <-> record <-> record + */ +void +config(fname) + char *fname; +{ + TAG *tp; + ENTRY *ep; + FILE *cfp; + size_t len; + int lcnt; + char *p, *t; + + if (fname == NULL) + fname = _PATH_MANCONF; + if ((cfp = fopen(fname, "r")) == NULL) + err(1, "%s", fname); + TAILQ_INIT(&head); + for (lcnt = 1; (p = fgetln(cfp, &len)) != NULL; ++lcnt) { + if (len == 1) /* Skip empty lines. */ + continue; + if (p[len - 1] != '\n') { /* Skip corrupted lines. */ + warnx("%s: line %d corrupted", fname, lcnt); + continue; + } + p[len - 1] = '\0'; /* Terminate the line. */ + + /* Skip leading space. */ + for (; *p != '\0' && isspace(*p); ++p); + /* Skip empty/comment lines. */ + if (*p == '\0' || *p == '#') + continue; + /* Find first token. */ + for (t = p; *t && !isspace(*t); ++t); + if (*t == '\0') /* Need more than one token.*/ + continue; + *t = '\0'; + + for (tp = head.tqh_first; /* Find any matching tag. */ + tp != NULL && strcmp(p, tp->s); tp = tp->q.tqe_next); + + if (tp == NULL) /* Create a new tag. */ + tp = addlist(p); + + /* + * Attach new records. The keyword _build takes the rest of + * the line as a single entity, everything else is white + * space separated. The reason we're not just using strtok(3) + * for all of the parsing is so we don't get caught if a line + * has only a single token on it. + */ + if (!strcmp(p, "_build")) { + while (*++t && isspace(*t)); + if ((ep = malloc(sizeof(ENTRY))) == NULL || + (ep->s = strdup(t)) == NULL) + err(1, NULL); + TAILQ_INSERT_TAIL(&tp->list, ep, q); + } else for (++t; (p = strtok(t, " \t\n")) != NULL; t = NULL) { + if ((ep = malloc(sizeof(ENTRY))) == NULL || + (ep->s = strdup(p)) == NULL) + err(1, NULL); + TAILQ_INSERT_TAIL(&tp->list, ep, q); + } + } + + fclose(cfp); +} + +/* + * addlist -- + * Add a tag to the list. + */ +TAG * +addlist(name) + char *name; +{ + TAG *tp; + + if ((tp = calloc(1, sizeof(TAG))) == NULL || + (tp->s = strdup(name)) == NULL) + err(1, NULL); + TAILQ_INIT(&tp->list); + TAILQ_INSERT_TAIL(&head, tp, q); + return (tp); +} + +/* + * getlist -- + * Return the linked list of entries for a tag if it exists. + */ +TAG * +getlist(name) + char *name; +{ + TAG *tp; + + for (tp = head.tqh_first; tp != NULL; tp = tp->q.tqe_next) + if (!strcmp(name, tp->s)) + return (tp); + return (NULL); +} + +void +debug(l) + char *l; +{ + TAG *tp; + ENTRY *ep; + + (void)printf("%s ===============\n", l); + for (tp = head.tqh_first; tp != NULL; tp = tp->q.tqe_next) { + printf("%s\n", tp->s); + for (ep = tp->list.tqh_first; ep != NULL; ep = ep->q.tqe_next) + printf("\t%s\n", ep->s); + } +} diff --git a/usr.bin/man/config.h b/usr.bin/man/config.h new file mode 100644 index 00000000000..52ea969f9e0 --- /dev/null +++ b/usr.bin/man/config.h @@ -0,0 +1,57 @@ +/*- + * 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. + * + * @(#)config.h 8.4 (Berkeley) 12/18/93 + */ + +typedef struct _tag { + TAILQ_ENTRY(_tag) q; /* Queue of tags. */ + + TAILQ_HEAD(tqh, _entry) list; /* Queue of entries. */ + char *s; /* Associated string. */ + size_t len; /* Length of 's'. */ +} TAG; +typedef struct _entry { + TAILQ_ENTRY(_entry) q; /* Queue of entries. */ + + char *s; /* Associated string. */ + size_t len; /* Length of 's'. */ +} ENTRY; + +TAILQ_HEAD(_head, _tag); +extern struct _head head; + +TAG *addlist __P((char *)); +void config __P((char *)); +void debug __P((char *)); +TAG *getlist __P((char *)); diff --git a/usr.bin/man/man.1 b/usr.bin/man/man.1 new file mode 100644 index 00000000000..081e204aee2 --- /dev/null +++ b/usr.bin/man/man.1 @@ -0,0 +1,188 @@ +.\" Copyright (c) 1989, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)man.1 8.2 (Berkeley) 1/2/94 +.\" +.Dd January 2, 1994 +.Dt MAN 1 +.Os BSD 4 +.Sh NAME +.Nm man +.Nd display the on-line manual pages +.Sh SYNOPSIS +.Nm man +.Op Fl achw +.Op Fl C Ar file +.Op Fl M Ar path +.Op Fl m Ar path +.Op Ar section +.Ar name Ar ... +.Sh DESCRIPTION +The +.Nm man +utility +displays the +.Bx +manual pages entitled +.Ar name . +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl a +Display all of the manual pages for a specified +.Ar section +and +.Ar name +combination. +(Normally, only the first manual page found is displayed.) +.It Fl C +Use the specified +.Ar file +instead of the default configuration file. +This permits users to configure their own manual environment. +See +.Xr man.conf 5 +for a description of the contents of this file. +.It Fl c +Copy the manual page to the standard output instead of using +.Xr more 1 +to paginate it. +This is done by default if the standard output is not a terminal device. +.It Fl h +Display only the +.Dq Tn SYNOPSIS +lines of the requested manual pages. +.It Fl M +Override the list of standard directories which +.Nm man +searches for manual pages. +The supplied +.Ar path +must be a colon (``:'') separated list of directories. +This search path may also be set using the environment variable +.Ev MANPATH . +The subdirectories to be searched, and their search order, +is specified by the ``_subdir'' line in the +.Nm man +configuration file. +.It Fl m +Augment the list of standard directories which +.Nm man +searches for manual pages. +The supplied +.Ar path +must be a colon (``:'') separated list of directories. +These directories will be searched before the standard directories or +the directories specified using the +.Fl M +option or the +.Ev MANPATH +environment variable. +The subdirectories to be searched, and their search order, +is specified by the ``_subdir'' line in the +.Nm man +configuration file. +.It Fl w +List the pathnames of the manual pages which +.Nm man +would display for the specified +.Ar section +and +.Ar name +combination. +.El +.Pp +The optional +.Ar section +argument restricts the directories that +.Nm man +will search. +The +.Nm man +configuration file (see +.Xr man.conf 5 ) +specifies the possible +.Ar section +values that are currently available. +If only a single argument is specified or if the first argument is +not a valid section, +.Nm man +assumes that the argument is the name of a manual page to be displayed. +.Sh ENVIRONMENT +.Bl -tag -width MANPATHX +.It Ev MACHINE +As some manual pages are intended only for specific architectures, +.Nm man +searches any subdirectories, +with the same name as the current architecture, +in every directory which it searches. +Machine specific areas are checked before general areas. +The current machine type may be overridden by setting the environment +variable +.Ev MACHINE +to the name of a specific architecture. +.It Ev MANPATH +The standard search path used by +.Nm man +may be overridden by specifying a path in the +.Ev MANPATH +environment +variable. +The format of the path is a colon (``:'') separated list of directories. +The subdirectories to be searched as well as their search order +is specified by the ``_subdir'' line in the +.Nm man +configuration file. +.It Ev PAGER +Any value of the environment variable +.Ev PAGER +will be used instead of the standard pagination program, +.Xr more 1 . +.El +.Sh FILES +.Bl -tag -width /etc/man.conf -compact +.It Pa /etc/man.conf +default man configuration file. +.El +.Sh SEE ALSO +.Xr apropos 1 , +.Xr whatis 1 , +.Xr whereis 1 , +.Xr man.conf 5 +.Sh BUGS +The on-line manual pages are, by necessity, forgiving toward stupid +display devices, causing a few manual pages to not as nicely formatted +as their typeset counterparts. +.Sh HISTORY +A +.Nm +command appeared in +.At v6 . diff --git a/usr.bin/man/man.c b/usr.bin/man/man.c new file mode 100644 index 00000000000..4f3b9e58968 --- /dev/null +++ b/usr.bin/man/man.c @@ -0,0 +1,712 @@ +/* + * Copyright (c) 1987, 1993, 1994, 1995 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1987, 1993, 1994, 1995\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)man.c 8.17 (Berkeley) 1/31/95"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "pathnames.h" + +int f_all, f_where; + +static void build_page __P((char *, char **)); +static void cat __P((char *)); +static char *check_pager __P((char *)); +static int cleanup __P((void)); +static void how __P((char *)); +static void jump __P((char **, char *, char *)); +static int manual __P((char *, TAG *, glob_t *)); +static void onsig __P((int)); +static void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; + extern int optind; + TAG *defp, *defnewp, *section, *sectnewp, *subp; + ENTRY *e_defp, *e_sectp, *e_subp, *ep; + glob_t pg; + size_t len; + int ch, f_cat, f_how, found; + char **ap, *cmd, *machine, *p, *p_add, *p_path, *pager, *slashp; + char *conffile, buf[MAXPATHLEN * 2]; + + f_cat = f_how = 0; + conffile = p_add = p_path = NULL; + while ((ch = getopt(argc, argv, "-aC:cfhkM:m:P:w")) != EOF) + switch (ch) { + case 'a': + f_all = 1; + break; + case 'C': + conffile = optarg; + break; + case 'c': + case '-': /* Deprecated. */ + f_cat = 1; + break; + case 'h': + f_how = 1; + break; + case 'm': + p_add = optarg; + break; + case 'M': + case 'P': /* Backward compatibility. */ + p_path = optarg; + break; + /* + * The -f and -k options are backward compatible, + * undocumented ways of calling whatis(1) and apropos(1). + */ + case 'f': + jump(argv, "-f", "whatis"); + /* NOTREACHED */ + case 'k': + jump(argv, "-k", "apropos"); + /* NOTREACHED */ + case 'w': + f_all = f_where = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (!*argv) + usage(); + + if (!f_cat && !f_how && !f_where) + if (!isatty(1)) + f_cat = 1; + else if ((pager = getenv("PAGER")) != NULL) + pager = check_pager(pager); + else + pager = _PATH_PAGER; + + /* Read the configuration file. */ + config(conffile); + + /* Get the machine type. */ + if ((machine = getenv("MACHINE")) == NULL) + machine = MACHINE; + + /* If there's no _default list, create an empty one. */ + if ((defp = getlist("_default")) == NULL) + defp = addlist("_default"); + + /* + * 1: If the user specified a MANPATH variable, or set the -M + * option, we replace the _default list with the user's list, + * appending the entries in the _subdir list and the machine. + */ + if (p_path == NULL) + p_path = getenv("MANPATH"); + if (p_path != NULL) { + while ((e_defp = defp->list.tqh_first) != NULL) { + free(e_defp->s); + TAILQ_REMOVE(&defp->list, e_defp, q); + } + for (p = strtok(p_path, ":"); + p != NULL; p = strtok(NULL, ":")) { + slashp = p[strlen(p) - 1] == '/' ? "" : "/"; + e_subp = (subp = getlist("_subdir")) == NULL ? + NULL : subp->list.tqh_first; + for (; e_subp != NULL; e_subp = e_subp->q.tqe_next) { + (void)snprintf(buf, sizeof(buf), "%s%s%s{/%s,}", + p, slashp, e_subp->s, machine); + if ((ep = malloc(sizeof(ENTRY))) == NULL || + (ep->s = strdup(buf)) == NULL) + err(1, NULL); + TAILQ_INSERT_TAIL(&defp->list, ep, q); + } + } + } + + /* + * 2: If the user did not specify MANPATH, -M or a section, rewrite + * the _default list to include the _subdir list and the machine. + */ + if (argv[1] == NULL) + section = NULL; + else if ((section = getlist(*argv)) != NULL) + ++argv; + if (p_path == NULL && section == NULL) { + defnewp = addlist("_default_new"); + e_defp = + defp->list.tqh_first == NULL ? NULL : defp->list.tqh_first; + for (; e_defp != NULL; e_defp = e_defp->q.tqe_next) { + slashp = + e_defp->s[strlen(e_defp->s) - 1] == '/' ? "" : "/"; + e_subp = (subp = getlist("_subdir")) == NULL ? + NULL : subp->list.tqh_first; + for (; e_subp != NULL; e_subp = e_subp->q.tqe_next) { + (void)snprintf(buf, sizeof(buf), "%s%s%s{/%s,}", + e_defp->s, slashp, e_subp->s, machine); + if ((ep = malloc(sizeof(ENTRY))) == NULL || + (ep->s = strdup(buf)) == NULL) + err(1, NULL); + TAILQ_INSERT_TAIL(&defnewp->list, ep, q); + } + } + defp = getlist("_default"); + while ((e_defp = defp->list.tqh_first) != NULL) { + free(e_defp->s); + TAILQ_REMOVE(&defp->list, e_defp, q); + } + free(defp->s); + TAILQ_REMOVE(&head, defp, q); + defnewp = getlist("_default_new"); + free(defnewp->s); + defnewp->s = "_default"; + defp = defnewp; + } + + /* + * 3: If the user set the -m option, insert the user's list before + * whatever list we have, again appending the _subdir list and + * the machine. + */ + if (p_add != NULL) + for (p = strtok(p_add, ":"); p != NULL; p = strtok(NULL, ":")) { + slashp = p[strlen(p) - 1] == '/' ? "" : "/"; + e_subp = (subp = getlist("_subdir")) == NULL ? + NULL : subp->list.tqh_first; + for (; e_subp != NULL; e_subp = e_subp->q.tqe_next) { + (void)snprintf(buf, sizeof(buf), "%s%s%s{/%s,}", + p, slashp, e_subp->s, machine); + if ((ep = malloc(sizeof(ENTRY))) == NULL || + (ep->s = strdup(buf)) == NULL) + err(1, NULL); + TAILQ_INSERT_HEAD(&defp->list, ep, q); + } + } + + /* + * 4: If none of MANPATH, -M, or -m were specified, and a section was, + * rewrite the section's paths (if they have a trailing slash) to + * append the _subdir list and the machine. This then becomes the + * _default list. + */ + if (p_path == NULL && p_add == NULL && section != NULL) { + sectnewp = addlist("_section_new"); + for (e_sectp = section->list.tqh_first; + e_sectp != NULL; e_sectp = e_sectp->q.tqe_next) { + if (e_sectp->s[strlen(e_sectp->s) - 1] != '/') { + (void)snprintf(buf, sizeof(buf), + "%s{/%s,}", e_sectp->s, machine); + if ((ep = malloc(sizeof(ENTRY))) == NULL || + (ep->s = strdup(buf)) == NULL) + err(1, NULL); + TAILQ_INSERT_TAIL(§newp->list, ep, q); + continue; + } + e_subp = (subp = getlist("_subdir")) == NULL ? + NULL : subp->list.tqh_first; + for (; e_subp != NULL; e_subp = e_subp->q.tqe_next) { + (void)snprintf(buf, sizeof(buf), "%s%s{/%s,}", + e_sectp->s, e_subp->s, machine); + if ((ep = malloc(sizeof(ENTRY))) == NULL || + (ep->s = strdup(buf)) == NULL) + err(1, NULL); + TAILQ_INSERT_TAIL(§newp->list, ep, q); + } + } + sectnewp->s = section->s; + defp = sectnewp; + TAILQ_REMOVE(&head, section, q); + } + + /* + * 5: Search for the files. Set up an interrupt handler, so the + * temporary files go away. + */ + (void)signal(SIGINT, onsig); + (void)signal(SIGHUP, onsig); + + memset(&pg, 0, sizeof(pg)); + for (found = 0; *argv; ++argv) + if (manual(*argv, defp, &pg)) + found = 1; + + /* 6: If nothing found, we're done. */ + if (!found) { + (void)cleanup(); + exit (1); + } + + /* 7: If it's simple, display it fast. */ + if (f_cat) { + for (ap = pg.gl_pathv; *ap != NULL; ++ap) { + if (**ap == '\0') + continue; + cat(*ap); + } + exit (cleanup()); + } + if (f_how) { + for (ap = pg.gl_pathv; *ap != NULL; ++ap) { + if (**ap == '\0') + continue; + how(*ap); + } + exit(cleanup()); + } + if (f_where) { + for (ap = pg.gl_pathv; *ap != NULL; ++ap) { + if (**ap == '\0') + continue; + (void)printf("%s\n", *ap); + } + exit(cleanup()); + } + + /* + * 8: We display things in a single command; build a list of things + * to display. + */ + for (ap = pg.gl_pathv, len = strlen(pager) + 1; *ap != NULL; ++ap) { + if (**ap == '\0') + continue; + len += strlen(*ap) + 1; + } + if ((cmd = malloc(len)) == NULL) { + warn(NULL); + (void)cleanup(); + exit(1); + } + p = cmd; + len = strlen(pager); + memmove(p, pager, len); + p += len; + *p++ = ' '; + for (ap = pg.gl_pathv; *ap != NULL; ++ap) { + if (**ap == '\0') + continue; + len = strlen(*ap); + memmove(p, *ap, len); + p += len; + *p++ = ' '; + } + *p = '\0'; + + /* Use system(3) in case someone's pager is "pager arg1 arg2". */ + (void)system(cmd); + + exit(cleanup()); +} + +/* + * manual -- + * Search the manuals for the pages. + */ +static int +manual(page, tag, pg) + char *page; + TAG *tag; + glob_t *pg; +{ + ENTRY *ep, *e_sufp, *e_tag; + TAG *missp, *sufp; + int anyfound, cnt, found; + char *p, buf[128]; + + anyfound = 0; + buf[0] = '*'; + + /* For each element in the list... */ + e_tag = tag == NULL ? NULL : tag->list.tqh_first; + for (; e_tag != NULL; e_tag = e_tag->q.tqe_next) { + (void)snprintf(buf, sizeof(buf), "%s/%s.*", e_tag->s, page); + if (glob(buf, + GLOB_APPEND | GLOB_BRACE | GLOB_NOSORT | GLOB_QUOTE, + NULL, pg)) { + warn("globbing"); + (void)cleanup(); + exit(1); + } + if (pg->gl_matchc == 0) + continue; + + /* Find out if it's really a man page. */ + for (cnt = pg->gl_pathc - pg->gl_matchc; + cnt < pg->gl_pathc; ++cnt) { + + /* + * Try the _suffix key words first. + * + * XXX + * Older versions of man.conf didn't have the suffix + * key words, it was assumed that everything was a .0. + * We just test for .0 first, it's fast and probably + * going to hit. + */ + (void)snprintf(buf, sizeof(buf), "*/%s.0", page); + if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) + goto next; + + e_sufp = (sufp = getlist("_suffix")) == NULL ? + NULL : sufp->list.tqh_first; + for (found = 0; + e_sufp != NULL; e_sufp = e_sufp->q.tqe_next) { + (void)snprintf(buf, + sizeof(buf), "*/%s%s", page, e_sufp->s); + if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) { + found = 1; + break; + } + } + if (found) + goto next; + + /* Try the _build key words next. */ + e_sufp = (sufp = getlist("_build")) == NULL ? + NULL : sufp->list.tqh_first; + for (found = 0; + e_sufp != NULL; e_sufp = e_sufp->q.tqe_next) { + for (p = e_sufp->s; + *p != '\0' && !isspace(*p); ++p); + if (*p == '\0') + continue; + *p = '\0'; + (void)snprintf(buf, + sizeof(buf), "*/%s%s", page, e_sufp->s); + if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) { + if (!f_where) + build_page(p + 1, + &pg->gl_pathv[cnt]); + *p = ' '; + found = 1; + break; + } + *p = ' '; + } + if (found) { +next: anyfound = 1; + if (!f_all) { + /* Delete any other matches. */ + while (++cnt< pg->gl_pathc) + pg->gl_pathv[cnt] = ""; + break; + } + continue; + } + + /* It's not a man page, forget about it. */ + pg->gl_pathv[cnt] = ""; + } + + if (anyfound && !f_all) + break; + } + + /* If not found, enter onto the missing list. */ + if (!anyfound) { + if ((missp = getlist("_missing")) == NULL) + missp = addlist("_missing"); + if ((ep = malloc(sizeof(ENTRY))) == NULL || + (ep->s = strdup(page)) == NULL) { + warn(NULL); + (void)cleanup(); + exit(1); + } + TAILQ_INSERT_TAIL(&missp->list, ep, q); + } + return (anyfound); +} + +/* + * build_page -- + * Build a man page for display. + */ +static void +build_page(fmt, pathp) + char *fmt, **pathp; +{ + static int warned; + ENTRY *ep; + TAG *intmpp; + int fd; + char buf[MAXPATHLEN], cmd[MAXPATHLEN], tpath[sizeof(_PATH_TMP)]; + + /* Let the user know this may take awhile. */ + if (!warned) { + warned = 1; + warnx("Formatting manual page..."); + } + + /* Add a remove-when-done list. */ + if ((intmpp = getlist("_intmp")) == NULL) + intmpp = addlist("_intmp"); + + /* Move to the printf(3) format string. */ + for (; *fmt && isspace(*fmt); ++fmt); + + /* + * Get a temporary file and build a version of the file + * to display. Replace the old file name with the new one. + */ + (void)strcpy(tpath, _PATH_TMP); + if ((fd = mkstemp(tpath)) == -1) { + warn("%s", tpath); + (void)cleanup(); + exit(1); + } + (void)snprintf(buf, sizeof(buf), "%s > %s", fmt, tpath); + (void)snprintf(cmd, sizeof(cmd), buf, *pathp); + (void)system(cmd); + (void)close(fd); + if ((*pathp = strdup(tpath)) == NULL) { + warn(NULL); + (void)cleanup(); + exit(1); + } + + /* Link the built file into the remove-when-done list. */ + if ((ep = malloc(sizeof(ENTRY))) == NULL) { + warn(NULL); + (void)cleanup(); + exit(1); + } + ep->s = *pathp; + TAILQ_INSERT_TAIL(&intmpp->list, ep, q); +} + +/* + * how -- + * display how information + */ +static void +how(fname) + char *fname; +{ + FILE *fp; + + int lcnt, print; + char *p, buf[256]; + + if (!(fp = fopen(fname, "r"))) { + warn("%s", fname); + (void)cleanup(); + exit (1); + } +#define S1 "SYNOPSIS" +#define S2 "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS" +#define D1 "DESCRIPTION" +#define D2 "D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN" + for (lcnt = print = 0; fgets(buf, sizeof(buf), fp);) { + if (!strncmp(buf, S1, sizeof(S1) - 1) || + !strncmp(buf, S2, sizeof(S2) - 1)) { + print = 1; + continue; + } else if (!strncmp(buf, D1, sizeof(D1) - 1) || + !strncmp(buf, D2, sizeof(D2) - 1)) + return; + if (!print) + continue; + if (*buf == '\n') + ++lcnt; + else { + for(; lcnt; --lcnt) + (void)putchar('\n'); + for (p = buf; isspace(*p); ++p); + (void)fputs(p, stdout); + } + } + (void)fclose(fp); +} + +/* + * cat -- + * cat out the file + */ +static void +cat(fname) + char *fname; +{ + int fd, n; + char buf[2048]; + + if ((fd = open(fname, O_RDONLY, 0)) < 0) { + warn("%s", fname); + (void)cleanup(); + exit(1); + } + while ((n = read(fd, buf, sizeof(buf))) > 0) + if (write(STDOUT_FILENO, buf, n) != n) { + warn("write"); + (void)cleanup(); + exit (1); + } + if (n == -1) { + warn("read"); + (void)cleanup(); + exit(1); + } + (void)close(fd); +} + +/* + * check_pager -- + * check the user supplied page information + */ +static char * +check_pager(name) + char *name; +{ + char *p, *save; + + /* + * if the user uses "more", we make it "more -s"; watch out for + * PAGER = "mypager /usr/ucb/more" + */ + for (p = name; *p && !isspace(*p); ++p); + for (; p > name && *p != '/'; --p); + if (p != name) + ++p; + + /* make sure it's "more", not "morex" */ + if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))){ + save = name; + /* allocate space to add the "-s" */ + if (!(name = + malloc((u_int)(strlen(save) + sizeof("-s") + 1)))) + err(1, NULL); + (void)sprintf(name, "%s %s", save, "-s"); + } + return(name); +} + +/* + * jump -- + * strip out flag argument and jump + */ +static void +jump(argv, flag, name) + char **argv, *flag, *name; +{ + char **arg; + + argv[0] = name; + for (arg = argv + 1; *arg; ++arg) + if (!strcmp(*arg, flag)) + break; + for (; *arg; ++arg) + arg[0] = arg[1]; + execvp(name, argv); + (void)fprintf(stderr, "%s: Command not found.\n", name); + exit(1); +} + +/* + * onsig -- + * If signaled, delete the temporary files. + */ +static void +onsig(signo) + int signo; +{ + (void)cleanup(); + + (void)signal(signo, SIG_DFL); + (void)kill(getpid(), signo); + + /* NOTREACHED */ + exit (1); +} + +/* + * cleanup -- + * Clean up temporary files, show any error messages. + */ +static int +cleanup() +{ + TAG *intmpp, *missp; + ENTRY *ep; + int rval; + + rval = 0; + ep = (missp = getlist("_missing")) == NULL ? + NULL : missp->list.tqh_first; + if (ep != NULL) + for (; ep != NULL; ep = ep->q.tqe_next) { + warnx("no entry for %s in the manual.", ep->s); + rval = 1; + } + + ep = (intmpp = getlist("_intmp")) == NULL ? + NULL : intmpp->list.tqh_first; + for (; ep != NULL; ep = ep->q.tqe_next) + (void)unlink(ep->s); + return (rval); +} + +/* + * usage -- + * print usage message and die + */ +static void +usage() +{ + (void)fprintf(stderr, + "usage: man [-achw] [-C file] [-M path] [-m path] [section] title ...\n"); + exit(1); +} diff --git a/usr.bin/man/man.conf b/usr.bin/man/man.conf new file mode 100644 index 00000000000..9faad52546e --- /dev/null +++ b/usr.bin/man/man.conf @@ -0,0 +1,46 @@ +# Sheer, raging paranoia... +_version BSD.2 + +# The whatis/apropos database. +_whatdb /usr/share/man/whatis.db + +# Subdirectories for paths ending in '/', IN SEARCH ORDER. +_subdir cat{1,8,6,2,3,4,5,7,3f} + +# Files typed by suffix and their commands. +# Note the order, .Z must come after .[1-9].Z, or it will match first. +_suffix .0 +_build .[1-9] /usr/bin/nroff -man %s +_build .[1-9].Z /usr/bin/zcat %s | /usr/bin/nroff -man +_build .Z /usr/bin/zcat %s +_build .0.Z /usr/bin/zcat %s +_build .gz /usr/contrib/bin/gunzip %s +_build .z /usr/contrib/bin/gunzip %s +_build .nr /usr/bin/nroff -man %s + +# Sections and their directories. +# All paths ending in '/' are the equivalent of entries specifying that +# directory with all of the subdirectories listed for the keyword _subdir. + +# default +_default /usr/{share,X11,contrib,local}/{man,man/old}/ + +# Other sections that represent complete man subdirectories. +X11 /usr/X11R4/man/ +X11R4 /usr/X11R4/man/ +contrib /usr/contrib/man/ +local /usr/local/man/ +new /usr/contrib/man/ +old /usr/share/man/old/ + +# Specific section/directory combinations. +1 /usr/{share,X11R4,contrib,local}/{man/,man/old/}cat1 +2 /usr/{share,X11R4,contrib,local}/{man/,man/old/}cat2 +3 /usr/{share,X11R4,contrib,local}/{man/,man/old/}cat3 +3F /usr/share/man/cat3f +3f /usr/share/man/cat3f +4 /usr/{share,X11R4,contrib,local}/{man/,man/old/}cat4 +5 /usr/{share,X11R4,contrib,local}/{man/,man/old/}cat5 +6 /usr/{share,X11R4,contrib,local}/{man/,man/old/}cat6 +7 /usr/{share,X11R4,contrib,local}/{man/,man/old/}cat7 +8 /usr/{share,X11R4,contrib,local}/{man/,man/old/}cat8 diff --git a/usr.bin/man/man.conf.5 b/usr.bin/man/man.conf.5 new file mode 100644 index 00000000000..353a30236fb --- /dev/null +++ b/usr.bin/man/man.conf.5 @@ -0,0 +1,195 @@ +.\" Copyright (c) 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. +.\" +.\" @(#)man.conf.5 8.5 (Berkeley) 1/2/94 +.\" +.Dd January 2, 1994 +.Dt MAN.CONF 5 +.Os +.Sh NAME +.Nm man.conf +.Nd configuration file for +.Xr man 1 +.Sh DESCRIPTION +The +.Xr man 1 , +.Xr apropos 1 , +and +.Xr whatis 1 +commands +search for manual pages or their database files as specified by the +.Nm man.conf +file. +Manual pages are normally expected to be preformatted (see +.Xr nroff 1 ) +and named with a trailing ``.0''. +.Pp +The +.Nm man.conf +file contains two types of lines. +.Pp +The first type of line is a ``section'' line, which contains a +section name followed by one or more directory paths. +The directory paths may contain the normal shell globbing characters, +including curly braces (``{}''); to escape a shell globbing character, +precede it with a backslash (``\e''). +Lines in this format specify that manual pages for the section +may be found in the following directories. +.Pp +Directories named with a trailing slash character (``/'') are expected +to contain subdirectories of manual pages, (see the keyword ``_subdir'' +below) instead of manual pages. +These subdirectories are searched instead of the directory. +.Pp +Before searching any directory for a manual page, the +.Xr man 1 +command always searches the subdirectory with the same name +as the current machine type, if it exists. +No specification of these subdirectories is necessary in the +.Nm man.conf +file. +.Pp +Section names are unrestricted except for the reserved words specified +below; in general, you should avoid anything with a leading underscore +(``_'') to avoid future incompatibilities. +.Pp +The section named ``_default'' is the list of directories that will +be searched if no section is specified by the user. +.Pp +The second type of line is preceded with a ``keyword''. +The possible keywords and their meanings are as follows: +.Pp +.Bl -tag -width "_version" +.It _build +Man file names, regardless of their format, are expected to end in +a ``.*'' pattern, i.e. a ``.'' followed by some suffix. +The first field of a _build line lists a suffix which indicates +files which need to be reformated or manipulated in some way before +being displayed to the user. +The suffix may contain the normal shell globbing characters (NOT +including curly braces (``{}'')). +The rest of the line must be a shell command line, the standard +output of which is the manual page in a format which may be directly +displayed to the user. +Any occurrences of the string ``%s'' in the shell command line will +be replaced by the name of the file which is being reformatted. +.It _subdir +The list (in search order) of subdirectories which will be searched in +any directory named with a trailing slash (``/'') character. +This list is also used when a path is specified to the +.Xr man 1 +utility by the user, using the +.Ev MANPATH +environment variable or the +.Fl M +and +.Fl m +options. +.It _suffix +Man file names, regardless of their format are expected to end in +a ``.*'' pattern, i.e. a ``.'' followed by some suffix. +Each field of a _suffix line is a suffix which indicates +files which do not need to be reformatted or manipulated +in any way, but which may be directly displayed to the user. +Each suffix may contain the normal shell globbing characters (NOT +including curly braces (``{}'')). +.It _version +The version of the configuration file. +.It _whatdb +The full pathname (not just a directory path) for a database to be used +by the +.Xr apropos 1 +and +.Xr whatis 1 +commands. +.El +.Pp +Multiple specifications for all types of lines are cumulative and the +entries are used in the order listed in the file; multiple entries may +be listed per line, as well. +.Pp +Empty lines or lines whose first non-whitespace character is a hash +mark (``#'') are ignored. +.Sh EXAMPLES +Given the following +.Nm man.conf +file: +.Bd -literal -offset indent +_version BSD.2 +_subdir cat[123] +_suffix .0 +_build .[1-9] nroff -man %s +_build .tbl tbl %s | nroff -man +_default /usr/share/man/ +sect3 /usr/share/man/{old/,}cat3 +.Ed +.Pp +By default, the command +.Dq Li man mktemp +will search for +``mktemp.'' and ``mktemp.tbl'' +in the directories +.Dq Pa /usr/share/man/cat1 , +.Dq Pa /usr/share/man/cat2 , +and +.Dq Pa /usr/share/man/cat3 . +If on a machine of type ``vax'', the subdirectory ``vax'' in each +directory would be searched as well, before the directory was +searched. +.Pp +If ``mktemp.tbl'' was found first, the command +.Dq Li tbl | nroff -man +would be run to build a man page for display to the user. +.Pp +The command +.Dq Li man sect3 mktemp +would search the directories +.Dq Pa /usr/share/man/old/cat3 +and +.Dq Pa /usr/share/man/cat3 , +in that order, for +the mktemp manual page. +If a subdirectory with the same name as the current machine type +existed in any of them, it would be searched as well, before each +of them were searched. +.Sh FILES +.Bl -tag -width /etc/man.conf -compact +.It Pa /etc/man.conf +Standard manual directory search path. +.El +.Sh SEE ALSO +.Xr apropos 1 , +.Xr machine 1 , +.Xr man 1 , +.Xr whatis 1 , +.Xr whereis 1 , +.Xr fnmatch 3 , +.Xr glob 3 diff --git a/usr.bin/man/pathnames.h b/usr.bin/man/pathnames.h new file mode 100644 index 00000000000..17284fe0d87 --- /dev/null +++ b/usr.bin/man/pathnames.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.3 (Berkeley) 1/2/94 + */ + +#define _PATH_MANCONF "/etc/man.conf" +#define _PATH_PAGER "/usr/bin/more -s" +#define _PATH_TMP "/tmp/man.XXXXXX" +#define _PATH_WHATIS "whatis.db" diff --git a/usr.bin/mklocale/Japanese b/usr.bin/mklocale/Japanese new file mode 100644 index 00000000000..55eb1559660 --- /dev/null +++ b/usr.bin/mklocale/Japanese @@ -0,0 +1,158 @@ +# @(#)Japanese 8.1 (Berkeley) 6/6/93 + +/* + * Japanese LOCALE_CTYPE definitions using EUC of JIS character sets + */ + +ENCODING "EUC" + +/* JIS JIS JIS */ +/* X201 X208 X201 */ +/* 00-7f 84-fe */ + +VARIABLE 1 0x0000 2 0x8080 2 0x0080 3 0x8000 0x8080 + +/* + * Code Set 1 + */ +ALPHA 'A' - 'Z' 'a' - 'z' +CONTROL 0x00 - 0x1f 0x7f +DIGIT '0' - '9' +GRAPH 0x21 - 0x7e +LOWER 'a' - 'z' +PUNCT 0x21 - 0x2f 0x3a - 0x40 0x5b - 0x60 0x7b - 0x7e +SPACE 0x09 - 0x0d 0x20 +UPPER 'A' - 'Z' +XDIGIT 'a' - 'f' 'A' - 'F' +BLANK ' ' '\t' +PRINT 0x20 - 0x7e + +MAPLOWER < 'A' - 'Z' : 'a' > +MAPLOWER < 'a' - 'z' : 'a' > +MAPUPPER < 'A' - 'Z' : 'A' > +MAPUPPER < 'a' - 'z' : 'A' > +TODIGIT < '0' - '9' : 0 > +TODIGIT < 'A' - 'F' : 10 > +TODIGIT < 'a' - 'f' : 10 > + +/* + * Code Set 2 + */ + +SPACE 0xa1a1 +PHONOGRAM 0xa1bc +SPECIAL 0xa1a2 - 0xa1fe +PUNCT 0xa1a2 - 0xa1f8 /* A few too many in here... */ + +SPECIAL 0xa2a1 - 0xa2ae 0xa2ba - 0xa2c1 0xa2ca - 0xa2d0 0xa2dc - 0xa2ea +SPECIAL 0xa2f2 - 0xa2f9 0xa2fe + +DIGIT 0xa3b0 - 0xa3b9 +UPPER 0xa3c1 - 0xa3da /* Romaji */ +LOWER 0xa3e1 - 0xa3fa /* Romaji */ +MAPLOWER < 0xa3c1 - 0xa3da : 0xa3e1 > /* English */ +MAPLOWER < 0xa3e1 - 0xa3fa : 0xa3e1 > /* English */ +MAPUPPER < 0xa3c1 - 0xa3da : 0xa3c1 > +MAPUPPER < 0xa3e1 - 0xa3fa : 0xa3c1 > + +XDIGIT 0xa3c1 - 0xa3c6 0xa3e1 - 0xa3e6 + +TODIGIT < 0xa3b0 - 0xa3b9 : 0 > +TODIGIT < 0xa3c1 - 0xa3c6 : 10 > +TODIGIT < 0xa3e1 - 0xa3e6 : 10 > + +PHONOGRAM 0xa4a1 - 0xa4f3 +PHONOGRAM 0xa5a1 - 0xa5f6 + +UPPER 0xa6a1 - 0xa6b8 /* Greek */ +LOWER 0xa6c1 - 0xa6d8 /* Greek */ +MAPLOWER < 0xa6a1 - 0xa6b8 : 0xa6c1 > +MAPLOWER < 0xa6c1 - 0xa6d8 : 0xa6c1 > +MAPUPPER < 0xa6a1 - 0xa6b8 : 0xa6a1 > +MAPUPPER < 0xa6c1 - 0xa6d8 : 0xa6a1 > + +UPPER 0xa7a1 - 0xa7c1 /* Cyrillic */ +LOWER 0xa7d1 - 0xa7f1 /* Cyrillic */ +MAPLOWER < 0xa7a1 - 0xa7c1 : 0xa7d1 > +MAPLOWER < 0xa7d1 - 0xa7f1 : 0xa7d1 > +MAPUPPER < 0xa7a1 - 0xa7c1 : 0xa7a1 > +MAPUPPER < 0xa7d1 - 0xa7f1 : 0xa7a1 > + +SPECIAL 0xa8a1 - 0xa8c0 + +IDEOGRAM 0xb0a1 - 0xb0fe +IDEOGRAM 0xb1a1 - 0xb1fe +IDEOGRAM 0xb2a1 - 0xb2fe +IDEOGRAM 0xb3a1 - 0xb3fe +IDEOGRAM 0xb4a1 - 0xb4fe +IDEOGRAM 0xb5a1 - 0xb5fe +IDEOGRAM 0xb6a1 - 0xb6fe +IDEOGRAM 0xb7a1 - 0xb7fe +IDEOGRAM 0xb8a1 - 0xb8fe +IDEOGRAM 0xb9a1 - 0xb9fe +IDEOGRAM 0xbaa1 - 0xbafe +IDEOGRAM 0xbba1 - 0xbbfe +IDEOGRAM 0xbca1 - 0xbcfe +IDEOGRAM 0xbda1 - 0xbdfe +IDEOGRAM 0xbea1 - 0xbefe +IDEOGRAM 0xbfa1 - 0xbffe +IDEOGRAM 0xc0a1 - 0xc0fe +IDEOGRAM 0xc1a1 - 0xc1fe +IDEOGRAM 0xc2a1 - 0xc2fe +IDEOGRAM 0xc3a1 - 0xc3fe +IDEOGRAM 0xc4a1 - 0xc4fe +IDEOGRAM 0xc5a1 - 0xc5fe +IDEOGRAM 0xc6a1 - 0xc6fe +IDEOGRAM 0xc7a1 - 0xc7fe +IDEOGRAM 0xc8a1 - 0xc8fe +IDEOGRAM 0xc9a1 - 0xc9fe +IDEOGRAM 0xcaa1 - 0xcafe +IDEOGRAM 0xcba1 - 0xcbfe +IDEOGRAM 0xcca1 - 0xccfe +IDEOGRAM 0xcda1 - 0xcdfe +IDEOGRAM 0xcea1 - 0xcefe +IDEOGRAM 0xcfa1 - 0xcfd3 +IDEOGRAM 0xd0a1 - 0xd0fe +IDEOGRAM 0xd1a1 - 0xd1fe +IDEOGRAM 0xd2a1 - 0xd2fe +IDEOGRAM 0xd3a1 - 0xd3fe +IDEOGRAM 0xd4a1 - 0xd4fe +IDEOGRAM 0xd5a1 - 0xd5fe +IDEOGRAM 0xd6a1 - 0xd6fe +IDEOGRAM 0xd7a1 - 0xd7fe +IDEOGRAM 0xd8a1 - 0xd8fe +IDEOGRAM 0xd9a1 - 0xd9fe +IDEOGRAM 0xdaa1 - 0xdafe +IDEOGRAM 0xdba1 - 0xdbfe +IDEOGRAM 0xdca1 - 0xdcfe +IDEOGRAM 0xdda1 - 0xddfe +IDEOGRAM 0xdea1 - 0xdefe +IDEOGRAM 0xdfa1 - 0xdffe +IDEOGRAM 0xe0a1 - 0xe0fe +IDEOGRAM 0xe1a1 - 0xe1fe +IDEOGRAM 0xe2a1 - 0xe2fe +IDEOGRAM 0xe3a1 - 0xe3fe +IDEOGRAM 0xe4a1 - 0xe4fe +IDEOGRAM 0xe5a1 - 0xe5fe +IDEOGRAM 0xe6a1 - 0xe6fe +IDEOGRAM 0xe7a1 - 0xe7fe +IDEOGRAM 0xe8a1 - 0xe8fe +IDEOGRAM 0xe9a1 - 0xe9fe +IDEOGRAM 0xeaa1 - 0xeafe +IDEOGRAM 0xeba1 - 0xebfe +IDEOGRAM 0xeca1 - 0xecfe +IDEOGRAM 0xeda1 - 0xedfe +IDEOGRAM 0xeea1 - 0xeefe +IDEOGRAM 0xefa1 - 0xeffe +IDEOGRAM 0xf0a1 - 0xf0fe +IDEOGRAM 0xf1a1 - 0xf1fe +IDEOGRAM 0xf2a1 - 0xf2fe +IDEOGRAM 0xf3a1 - 0xf3fe +IDEOGRAM 0xf4a1 - 0xf4a4 + +/* + * This is for Code Set 3, half-width kana + */ +SPECIAL 0xa1 - 0xdf +PHONOGRAM 0xa1 - 0xdf +CONTROL 0x84 - 0x97 0x9b - 0x9f 0xe0 - 0xfe diff --git a/usr.bin/more/output.c b/usr.bin/more/output.c new file mode 100644 index 00000000000..21514ede1bb --- /dev/null +++ b/usr.bin/more/output.c @@ -0,0 +1,254 @@ +/* + * Copyright (c) 1988 Mark Nudleman + * 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 sccsid[] = "@(#)output.c 8.2 (Berkeley) 4/27/95"; +#endif /* not lint */ + +/* + * High level routines dealing with the output to the screen. + */ + +#include +#include + +int errmsgs; /* Count of messages displayed by error() */ + +extern int sigs; +extern int sc_width, sc_height; +extern int ul_width, ue_width; +extern int so_width, se_width; +extern int bo_width, be_width; +extern int tabstop; +extern int screen_trashed; +extern int any_display; +extern char *line; + +/* display the line which is in the line buffer. */ +put_line() +{ + register char *p; + register int c; + register int column; + extern int auto_wrap, ignaw; + + if (sigs) + { + /* + * Don't output if a signal is pending. + */ + screen_trashed = 1; + return; + } + + if (line == NULL) + line = ""; + + column = 0; + for (p = line; *p != '\0'; p++) + { + switch (c = *p) + { + case UL_CHAR: + ul_enter(); + column += ul_width +1; + break; + case UE_CHAR: + ul_exit(); + column += ue_width; + break; + case BO_CHAR: + bo_enter(); + column += bo_width +1; + break; + case BE_CHAR: + bo_exit(); + column += be_width; + break; + case '\t': + do + { + putchr(' '); + column++; + } while ((column % tabstop) != 0); + break; + case '\b': + putbs(); + column--; + break; + default: + if (c & 0200) + { + /* + * Control characters arrive here as the + * normal character [CARAT_CHAR(c)] with + * the 0200 bit set. See pappend(). + */ + putchr('^'); + putchr(c & 0177); + column += 2; + } else + { + putchr(c); + column++; + } + } + } + if (column < sc_width || !auto_wrap || ignaw) + putchr('\n'); +} + +static char obuf[1024]; +static char *ob = obuf; + +/* + * Flush buffered output. + */ +flush() +{ + register int n; + + n = ob - obuf; + if (n == 0) + return; + if (write(1, obuf, n) != n) + screen_trashed = 1; + ob = obuf; +} + +/* + * Purge any pending output. + */ +purge() +{ + + ob = obuf; +} + +/* + * Output a character. + */ +putchr(c) + int c; +{ + if (ob >= &obuf[sizeof(obuf)]) + flush(); + *ob++ = c; +} + +/* + * Output a string. + */ +putstr(s) + register char *s; +{ + while (*s != '\0') + putchr(*s++); +} + +int cmdstack; +static char return_to_continue[] = "(press RETURN)"; + +/* + * Output a message in the lower left corner of the screen + * and wait for carriage return. + */ +error(s) + char *s; +{ + int ch; + + ++errmsgs; + if (!any_display) { + /* + * Nothing has been displayed yet. Output this message on + * error output (file descriptor 2) and don't wait for a + * keystroke to continue. + * + * This has the desirable effect of producing all error + * messages on error output if standard output is directed + * to a file. It also does the same if we never produce + * any real output; for example, if the input file(s) cannot + * be opened. If we do eventually produce output, code in + * edit() makes sure these messages can be seen before they + * are overwritten or scrolled away. + */ + if (s != NULL) { + (void)write(2, s, strlen(s)); + (void)write(2, "\n", 1); + } + return; + } + + lower_left(); + clear_eol(); + so_enter(); + if (s != NULL) { + putstr(s); + putstr(" "); + } + putstr(return_to_continue); + so_exit(); + + if ((ch = getchr()) != '\n') { + if (ch == 'q') + quit(); + cmdstack = ch; + } + lower_left(); + + if ((s != NULL ? strlen(s) : 0) + sizeof(return_to_continue) + + so_width + se_width + 1 > sc_width) + /* + * Printing the message has probably scrolled the screen. + * {{ Unless the terminal doesn't have auto margins, + * in which case we just hammered on the right margin. }} + */ + repaint(); + flush(); +} + +static char intr_to_abort[] = "... (interrupt to abort)"; + +ierror(s) + char *s; +{ + lower_left(); + clear_eol(); + so_enter(); + putstr(s); + putstr(intr_to_abort); + so_exit(); + flush(); +} diff --git a/usr.bin/msgs/Makefile b/usr.bin/msgs/Makefile new file mode 100644 index 00000000000..23989078f33 --- /dev/null +++ b/usr.bin/msgs/Makefile @@ -0,0 +1,7 @@ +# @(#)Makefile 8.2 (Berkeley) 4/28/95 + +PROG= msgs +DPADD= ${LIBTERM} +LDADD= -ltermlib + +.include diff --git a/usr.bin/msgs/msgs.1 b/usr.bin/msgs/msgs.1 new file mode 100644 index 00000000000..fe61924a6f1 --- /dev/null +++ b/usr.bin/msgs/msgs.1 @@ -0,0 +1,214 @@ +.\" 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. +.\" +.\" @(#)msgs.1 8.2 (Berkeley) 4/28/95 +.\" +.Dd April 28, 1995 +.Dt MSGS 1 +.Os BSD 4 +.Sh NAME +.Nm msgs +.Nd system messages and junk mail program +.Sh SYNOPSIS +.Nm msgs +.Op Fl fhlpq +.Op Ar number +.Op Ar \-number +.Nm msgs +.Op Fl s +.Nm msgs +.Op Fl c +.Op \-days +.Sh DESCRIPTION +.Nm Msgs +is used to read system messages. +These messages are +sent by mailing to the login `msgs' and should be short +pieces of information which are suitable to be read once by most users +of the system. +.Pp +.Nm Msgs +is normally invoked each time you login, by placing it in the file +.Pa .login +(or +.Pa .profile +if you use +.Xr sh 1 ) . +It will then prompt you with the source and subject of each new message. +If there is no subject line, the first few non-blank lines of the +message will be displayed. +If there is more to the message, you will be told how +long it is and asked whether you wish to see the rest of the message. +The possible responses are: +.Bl -tag -width Fl +.It Fl y +Type the rest of the message. +.It Ic RETURN +Synonym for y. +.It Fl n +Skip this message +and go on to the next message. +.It Fl +Redisplay the last message. +.It Fl q +Drop out of +.Nm msgs ; +the next time +.Nm msgs +will pick up where it last left off. +.It Fl s +Append the current message to the file ``Messages'' in the current directory; +`s\-' will save the previously displayed message. A `s' or `s\-' may +be followed by a space and a file name to receive the message replacing +the default ``Messages''. +.It Fl m +A copy of the specified message is placed in a temporary +mailbox and +.Xr mail 1 +is invoked on that mailbox. +Both `m' and `s' accept a numeric argument in place of the `\-'. +.El +.Pp +.Nm Msgs +keeps track of the next message you will see by a number in the file +.Pa \&.msgsrc +in your home directory. +In the directory +.Pa /var/msgs +it keeps a set of files whose names are the (sequential) numbers +of the messages they represent. +The file +.Pa /var/msgs/bounds +shows the low and high number of the messages in the directory +so that +.Nm msgs +can quickly determine if there are no messages for you. +If the contents of +.Pa bounds +is incorrect it can be fixed by removing it; +.Nm msgs +will make a new +.Pa bounds +file the next time it is run. +.Pp +The +.Fl s +option is used for setting up the posting of messages. The line +.Pp +.Dl msgs: \&"\&| /usr/bin/msgs \-s\&" +.Pp +should be included in +.Pa /etc/aliases +(see +.Xr newaliases 1 ) +to enable posting of messages. +.Pp +The +.Fl c +option is used for performing cleanup on +.Pa /var/msgs. +An entry with the +.Fl c +option should be placed in +.Pa /etc/crontab +to run every night. This will remove all messages over 21 days old. +A different expiration may be specified on the command line to override +the default. +.Pp +Options when reading messages include: +.Bl -tag -width Fl +.It Fl f +Do not to say ``No new messages.''. +This is useful in a +.Pa .login +file since this is often the case here. +.It Fl q +Queries whether there are messages, printing +``There are new messages.'' if there are. +The command ``msgs \-q'' is often used in login scripts. +.It Fl h +Print the first part of messages only. +.It Fl l +Option causes only locally originated messages to be reported. +.It Ar num +A message number can be given +on the command line, causing +.Nm msgs +to start at the specified message rather than at the next message +indicated by your +.Pa \&.msgsrc +file. +Thus +.Pp +.Dl msgs \-h 1 +.Pp +prints the first part of all messages. +.It Ar \-number +Start +.Ar number +messages back from the one indicated in the +.Pa \&.msgsrc +file, useful for reviews of recent messages. +.It Fl p +Pipe long messages through +.Xr more 1 . +.El +.Pp +Within +.Nm msgs +you can also go to any specific message by typing its number when +.Nm msgs +requests input as to what to do. +.Sh ENVIRONMENT +.Nm Msgs +uses the +.Ev HOME +and +.Ev TERM +environment variables for the default home directory and +terminal type. +.Sh FILES +.Bl -tag -width /usr/msgs/* -compact +.It Pa /usr/msgs/* +database +.It ~/.msgsrc +number of next message to be presented +.El +.Sh SEE ALSO +.Xr aliases 5 , +.\".Xr crontab 5 , +.Xr mail 1 , +.Xr more 1 +.Sh HISTORY +The +.Nm msgs +command appeared in +.Bx 3.0 . diff --git a/usr.bin/msgs/msgs.c b/usr.bin/msgs/msgs.c new file mode 100644 index 00000000000..68042821357 --- /dev/null +++ b/usr.bin/msgs/msgs.c @@ -0,0 +1,862 @@ +/*- + * 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1980, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)msgs.c 8.2 (Berkeley) 4/28/95"; +#endif /* not lint */ + +/* + * msgs - a user bulletin board program + * + * usage: + * msgs [fhlopq] [[-]number] to read messages + * msgs -s to place messages + * msgs -c [-days] to clean up the bulletin board + * + * prompt commands are: + * y print message + * n flush message, go to next message + * q flush message, quit + * p print message, turn on 'pipe thru more' mode + * P print message, turn off 'pipe thru more' mode + * - reprint last message + * s[-][] [] save message + * m[-][] mail with message in temp mbox + * x exit without flushing this message + * print message number + */ + +#define V7 /* will look for TERM in the environment */ +#define OBJECT /* will object to messages without Subjects */ +/* #define REJECT /* will reject messages without Subjects + (OBJECT must be defined also) */ +/* #define UNBUFFERED /* use unbuffered output */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pathnames.h" + +#define CMODE 0666 /* bounds file creation mode */ +#define NO 0 +#define YES 1 +#define SUPERUSER 0 /* superuser uid */ +#define DAEMON 1 /* daemon uid */ +#define NLINES 24 /* default number of lines/crt screen */ +#define NDAYS 21 /* default keep time for messages */ +#define DAYS *24*60*60 /* seconds/day */ +#define MSGSRC ".msgsrc" /* user's rc file */ +#define BOUNDS "bounds" /* message bounds file */ +#define NEXT "Next message? [yq]" +#define MORE "More? [ynq]" +#define NOMORE "(No more) [q] ?" + +typedef char bool; + +FILE *msgsrc; +FILE *newmsg; +char *sep = "-"; +char inbuf[BUFSIZ]; +char fname[128]; +char cmdbuf[128]; +char subj[128]; +char from[128]; +char date[128]; +char *ptr; +char *in; +bool local; +bool ruptible; +bool totty; +bool seenfrom; +bool seensubj; +bool blankline; +bool printing = NO; +bool mailing = NO; +bool quitit = NO; +bool sending = NO; +bool intrpflg = NO; +int uid; +int msg; +int prevmsg; +int lct; +int nlines; +int Lpp = 0; +time_t t; +time_t keep; + +char *mktemp(); +char *nxtfld(); +void onintr(); +void onsusp(); + +/* option initialization */ +bool hdrs = NO; +bool qopt = NO; +bool hush = NO; +bool send_msg = NO; +bool locomode = NO; +bool use_pager = NO; +bool clean = NO; +bool lastcmd = NO; +jmp_buf tstpbuf; + +main(argc, argv) +int argc; char *argv[]; +{ + bool newrc, already; + int rcfirst = 0; /* first message to print (from .rc) */ + int rcback = 0; /* amount to back off of rcfirst */ + int firstmsg, nextmsg, lastmsg = 0; + int blast = 0; + FILE *bounds; + +#ifdef UNBUFFERED + setbuf(stdout, NULL); +#endif + + time(&t); + setuid(uid = getuid()); + ruptible = (signal(SIGINT, SIG_IGN) == SIG_DFL); + if (ruptible) + signal(SIGINT, SIG_DFL); + + argc--, argv++; + while (argc > 0) { + if (isdigit(argv[0][0])) { /* starting message # */ + rcfirst = atoi(argv[0]); + } + else if (isdigit(argv[0][1])) { /* backward offset */ + rcback = atoi( &( argv[0][1] ) ); + } + else { + ptr = *argv; + while (*ptr) switch (*ptr++) { + + case '-': + break; + + case 'c': + if (uid != SUPERUSER && uid != DAEMON) { + fprintf(stderr, "Sorry\n"); + exit(1); + } + clean = YES; + break; + + case 'f': /* silently */ + hush = YES; + break; + + case 'h': /* headers only */ + hdrs = YES; + break; + + case 'l': /* local msgs only */ + locomode = YES; + break; + + case 'o': /* option to save last message */ + lastcmd = YES; + break; + + case 'p': /* pipe thru 'more' during long msgs */ + use_pager = YES; + break; + + case 'q': /* query only */ + qopt = YES; + break; + + case 's': /* sending TO msgs */ + send_msg = YES; + break; + + default: + fprintf(stderr, + "usage: msgs [fhlopq] [[-]number]\n"); + exit(1); + } + } + argc--, argv++; + } + + /* + * determine current message bounds + */ + sprintf(fname, "%s/%s", _PATH_MSGS, BOUNDS); + bounds = fopen(fname, "r"); + + if (bounds != NULL) { + fscanf(bounds, "%d %d\n", &firstmsg, &lastmsg); + fclose(bounds); + blast = lastmsg; /* save upper bound */ + } + + if (clean) + keep = t - (rcback? rcback : NDAYS) DAYS; + + if (clean || bounds == NULL) { /* relocate message bounds */ + struct direct *dp; + struct stat stbuf; + bool seenany = NO; + DIR *dirp; + + dirp = opendir(_PATH_MSGS); + if (dirp == NULL) { + perror(_PATH_MSGS); + exit(errno); + } + + firstmsg = 32767; + lastmsg = 0; + + for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)){ + register char *cp = dp->d_name; + register int i = 0; + + if (dp->d_ino == 0) + continue; + if (dp->d_namlen == 0) + continue; + + if (clean) + sprintf(inbuf, "%s/%s", _PATH_MSGS, cp); + + while (isdigit(*cp)) + i = i * 10 + *cp++ - '0'; + if (*cp) + continue; /* not a message! */ + + if (clean) { + if (stat(inbuf, &stbuf) != 0) + continue; + if (stbuf.st_mtime < keep + && stbuf.st_mode&S_IWRITE) { + unlink(inbuf); + continue; + } + } + + if (i > lastmsg) + lastmsg = i; + if (i < firstmsg) + firstmsg = i; + seenany = YES; + } + closedir(dirp); + + if (!seenany) { + if (blast != 0) /* never lower the upper bound! */ + lastmsg = blast; + firstmsg = lastmsg + 1; + } + else if (blast > lastmsg) + lastmsg = blast; + + if (!send_msg) { + bounds = fopen(fname, "w"); + if (bounds == NULL) { + perror(fname); + exit(errno); + } + chmod(fname, CMODE); + fprintf(bounds, "%d %d\n", firstmsg, lastmsg); + fclose(bounds); + } + } + + if (send_msg) { + /* + * Send mode - place msgs in _PATH_MSGS + */ + bounds = fopen(fname, "w"); + if (bounds == NULL) { + perror(fname); + exit(errno); + } + + nextmsg = lastmsg + 1; + sprintf(fname, "%s/%d", _PATH_MSGS, nextmsg); + newmsg = fopen(fname, "w"); + if (newmsg == NULL) { + perror(fname); + exit(errno); + } + chmod(fname, 0644); + + fprintf(bounds, "%d %d\n", firstmsg, nextmsg); + fclose(bounds); + + sending = YES; + if (ruptible) + signal(SIGINT, onintr); + + if (isatty(fileno(stdin))) { + ptr = getpwuid(uid)->pw_name; + printf("Message %d:\nFrom %s %sSubject: ", + nextmsg, ptr, ctime(&t)); + fflush(stdout); + fgets(inbuf, sizeof inbuf, stdin); + putchar('\n'); + fflush(stdout); + fprintf(newmsg, "From %s %sSubject: %s\n", + ptr, ctime(&t), inbuf); + blankline = seensubj = YES; + } + else + blankline = seensubj = NO; + for (;;) { + fgets(inbuf, sizeof inbuf, stdin); + if (feof(stdin) || ferror(stdin)) + break; + blankline = (blankline || (inbuf[0] == '\n')); + seensubj = (seensubj || (!blankline && (strncmp(inbuf, "Subj", 4) == 0))); + fputs(inbuf, newmsg); + } +#ifdef OBJECT + if (!seensubj) { + printf("NOTICE: Messages should have a Subject field!\n"); +#ifdef REJECT + unlink(fname); +#endif + exit(1); + } +#endif + exit(ferror(stdin)); + } + if (clean) + exit(0); + + /* + * prepare to display messages + */ + totty = (isatty(fileno(stdout)) != 0); + use_pager = use_pager && totty; + + sprintf(fname, "%s/%s", getenv("HOME"), MSGSRC); + msgsrc = fopen(fname, "r"); + if (msgsrc) { + newrc = NO; + fscanf(msgsrc, "%d\n", &nextmsg); + fclose(msgsrc); + if (nextmsg > lastmsg+1) { + printf("Warning: bounds have been reset (%d, %d)\n", + firstmsg, lastmsg); + truncate(fname, (off_t)0); + newrc = YES; + } + else if (!rcfirst) + rcfirst = nextmsg - rcback; + } + else + newrc = YES; + msgsrc = fopen(fname, "r+"); + if (msgsrc == NULL) + msgsrc = fopen(fname, "w"); + if (msgsrc == NULL) { + perror(fname); + exit(errno); + } + if (rcfirst) { + if (rcfirst > lastmsg+1) { + printf("Warning: the last message is number %d.\n", + lastmsg); + rcfirst = nextmsg; + } + if (rcfirst > firstmsg) + firstmsg = rcfirst; /* don't set below first msg */ + } + if (newrc) { + nextmsg = firstmsg; + fseek(msgsrc, 0L, 0); + fprintf(msgsrc, "%d\n", nextmsg); + fflush(msgsrc); + } + +#ifdef V7 + if (totty) { + struct winsize win; + if (ioctl(fileno(stdout), TIOCGWINSZ, &win) != -1) + Lpp = win.ws_row; + if (Lpp <= 0) { + if (tgetent(inbuf, getenv("TERM")) <= 0 + || (Lpp = tgetnum("li")) <= 0) { + Lpp = NLINES; + } + } + } +#endif + Lpp -= 6; /* for headers, etc. */ + + already = NO; + prevmsg = firstmsg; + printing = YES; + if (ruptible) + signal(SIGINT, onintr); + + /* + * Main program loop + */ + for (msg = firstmsg; msg <= lastmsg; msg++) { + + sprintf(fname, "%s/%d", _PATH_MSGS, msg); + newmsg = fopen(fname, "r"); + if (newmsg == NULL) + continue; + + gfrsub(newmsg); /* get From and Subject fields */ + if (locomode && !local) { + fclose(newmsg); + continue; + } + + if (qopt) { /* This has to be located here */ + printf("There are new messages.\n"); + exit(0); + } + + if (already && !hdrs) + putchar('\n'); + + /* + * Print header + */ + if (totty) + signal(SIGTSTP, onsusp); + (void) setjmp(tstpbuf); + already = YES; + nlines = 2; + if (seenfrom) { + printf("Message %d:\nFrom %s %s", msg, from, date); + nlines++; + } + if (seensubj) { + printf("Subject: %s", subj); + nlines++; + } + else { + if (seenfrom) { + putchar('\n'); + nlines++; + } + while (nlines < 6 + && fgets(inbuf, sizeof inbuf, newmsg) + && inbuf[0] != '\n') { + fputs(inbuf, stdout); + nlines++; + } + } + + lct = linecnt(newmsg); + if (lct) + printf("(%d%slines) ", lct, seensubj? " " : " more "); + + if (hdrs) { + printf("\n-----\n"); + fclose(newmsg); + continue; + } + + /* + * Ask user for command + */ + if (totty) + ask(lct? MORE : (msg==lastmsg? NOMORE : NEXT)); + else + inbuf[0] = 'y'; + if (totty) + signal(SIGTSTP, SIG_DFL); +cmnd: + in = inbuf; + switch (*in) { + case 'x': + case 'X': + exit(0); + + case 'q': + case 'Q': + quitit = YES; + printf("--Postponed--\n"); + exit(0); + /* intentional fall-thru */ + case 'n': + case 'N': + if (msg >= nextmsg) sep = "Flushed"; + prevmsg = msg; + break; + + case 'p': + case 'P': + use_pager = (*in++ == 'p'); + /* intentional fallthru */ + case '\n': + case 'y': + default: + if (*in == '-') { + msg = prevmsg-1; + sep = "replay"; + break; + } + if (isdigit(*in)) { + msg = next(in); + sep = in; + break; + } + + prmesg(nlines + lct + (seensubj? 1 : 0)); + prevmsg = msg; + + } + + printf("--%s--\n", sep); + sep = "-"; + if (msg >= nextmsg) { + nextmsg = msg + 1; + fseek(msgsrc, 0L, 0); + fprintf(msgsrc, "%d\n", nextmsg); + fflush(msgsrc); + } + if (newmsg) + fclose(newmsg); + if (quitit) + break; + } + + /* + * Make sure .rc file gets updated + */ + if (--msg >= nextmsg) { + nextmsg = msg + 1; + fseek(msgsrc, 0L, 0); + fprintf(msgsrc, "%d\n", nextmsg); + fflush(msgsrc); + } + if (already && !quitit && lastcmd && totty) { + /* + * save or reply to last message? + */ + msg = prevmsg; + ask(NOMORE); + if (inbuf[0] == '-' || isdigit(inbuf[0])) + goto cmnd; + } + if (!(already || hush || qopt)) + printf("No new messages.\n"); + exit(0); +} + +prmesg(length) +int length; +{ + FILE *outf; + + if (use_pager && length > Lpp) { + signal(SIGPIPE, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + sprintf(cmdbuf, _PATH_PAGER, Lpp); + outf = popen(cmdbuf, "w"); + if (!outf) + outf = stdout; + else + setbuf(outf, (char *)NULL); + } + else + outf = stdout; + + if (seensubj) + putc('\n', outf); + + while (fgets(inbuf, sizeof inbuf, newmsg)) { + fputs(inbuf, outf); + if (ferror(outf)) { + clearerr(outf); + break; + } + } + + if (outf != stdout) { + pclose(outf); + signal(SIGPIPE, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + } + else { + fflush(stdout); + } + + /* trick to force wait on output */ + tcdrain(fileno(stdout)); +} + +void +onintr() +{ + signal(SIGINT, onintr); + if (mailing) + unlink(fname); + if (sending) { + unlink(fname); + puts("--Killed--"); + exit(1); + } + if (printing) { + putchar('\n'); + if (hdrs) + exit(0); + sep = "Interrupt"; + if (newmsg) + fseek(newmsg, 0L, 2); + intrpflg = YES; + } +} + +/* + * We have just gotten a susp. Suspend and prepare to resume. + */ +void +onsusp() +{ + + signal(SIGTSTP, SIG_DFL); + sigsetmask(0); + kill(0, SIGTSTP); + signal(SIGTSTP, onsusp); + if (!mailing) + longjmp(tstpbuf, 0); +} + +linecnt(f) +FILE *f; +{ + off_t oldpos = ftell(f); + int l = 0; + char lbuf[BUFSIZ]; + + while (fgets(lbuf, sizeof lbuf, f)) + l++; + clearerr(f); + fseek(f, oldpos, 0); + return (l); +} + +next(buf) +char *buf; +{ + int i; + sscanf(buf, "%d", &i); + sprintf(buf, "Goto %d", i); + return(--i); +} + +ask(prompt) +char *prompt; +{ + char inch; + int n, cmsg; + off_t oldpos; + FILE *cpfrom, *cpto; + + printf("%s ", prompt); + fflush(stdout); + intrpflg = NO; + (void) fgets(inbuf, sizeof inbuf, stdin); + if ((n = strlen(inbuf)) > 0 && inbuf[n - 1] == '\n') + inbuf[n - 1] = '\0'; + if (intrpflg) + inbuf[0] = 'x'; + + /* + * Handle 'mail' and 'save' here. + */ + if ((inch = inbuf[0]) == 's' || inch == 'm') { + if (inbuf[1] == '-') + cmsg = prevmsg; + else if (isdigit(inbuf[1])) + cmsg = atoi(&inbuf[1]); + else + cmsg = msg; + sprintf(fname, "%s/%d", _PATH_MSGS, cmsg); + + oldpos = ftell(newmsg); + + cpfrom = fopen(fname, "r"); + if (!cpfrom) { + printf("Message %d not found\n", cmsg); + ask (prompt); + return; + } + + if (inch == 's') { + in = nxtfld(inbuf); + if (*in) { + for (n=0; in[n] > ' '; n++) { /* sizeof fname? */ + fname[n] = in[n]; + } + fname[n] = NULL; + } + else + strcpy(fname, "Messages"); + } + else { + strcpy(fname, _PATH_TMP); + mktemp(fname); + sprintf(cmdbuf, _PATH_MAIL, fname); + mailing = YES; + } + cpto = fopen(fname, "a"); + if (!cpto) { + perror(fname); + mailing = NO; + fseek(newmsg, oldpos, 0); + ask(prompt); + return; + } + + while (n = fread(inbuf, 1, sizeof inbuf, cpfrom)) + fwrite(inbuf, 1, n, cpto); + + fclose(cpfrom); + fclose(cpto); + fseek(newmsg, oldpos, 0); /* reposition current message */ + if (inch == 's') + printf("Message %d saved in \"%s\"\n", cmsg, fname); + else { + system(cmdbuf); + unlink(fname); + mailing = NO; + } + ask(prompt); + } +} + +gfrsub(infile) +FILE *infile; +{ + off_t frompos; + + seensubj = seenfrom = NO; + local = YES; + subj[0] = from[0] = date[0] = NULL; + + /* + * Is this a normal message? + */ + if (fgets(inbuf, sizeof inbuf, infile)) { + if (strncmp(inbuf, "From", 4)==0) { + /* + * expected form starts with From + */ + seenfrom = YES; + frompos = ftell(infile); + ptr = from; + in = nxtfld(inbuf); + if (*in) while (*in && *in > ' ') { + if (*in == ':' || *in == '@' || *in == '!') + local = NO; + *ptr++ = *in++; + /* what about sizeof from ? */ + } + *ptr = NULL; + if (*(in = nxtfld(in))) + strncpy(date, in, sizeof date); + else { + date[0] = '\n'; + date[1] = NULL; + } + } + else { + /* + * not the expected form + */ + fseek(infile, 0L, 0); + return; + } + } + else + /* + * empty file ? + */ + return; + + /* + * look for Subject line until EOF or a blank line + */ + while (fgets(inbuf, sizeof inbuf, infile) + && !(blankline = (inbuf[0] == '\n'))) { + /* + * extract Subject line + */ + if (!seensubj && strncmp(inbuf, "Subj", 4)==0) { + seensubj = YES; + frompos = ftell(infile); + strncpy(subj, nxtfld(inbuf), sizeof subj); + } + } + if (!blankline) + /* + * ran into EOF + */ + fseek(infile, frompos, 0); + + if (!seensubj) + /* + * for possible use with Mail + */ + strncpy(subj, "(No Subject)\n", sizeof subj); +} + +char * +nxtfld(s) +char *s; +{ + if (*s) while (*s && *s > ' ') s++; /* skip over this field */ + if (*s) while (*s && *s <= ' ') s++; /* find start of next field */ + return (s); +} diff --git a/usr.bin/nfsstat/nfsstat.c b/usr.bin/nfsstat/nfsstat.c new file mode 100644 index 00000000000..c7ef75d6144 --- /dev/null +++ b/usr.bin/nfsstat/nfsstat.c @@ -0,0 +1,404 @@ +/* + * Copyright (c) 1983, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Rick Macklem at The University of Guelph. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE 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, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)nfsstat.c 8.2 (Berkeley) 3/31/95"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct nlist nl[] = { +#define N_NFSSTAT 0 + { "_nfsstats" }, + "", +}; +kvm_t *kd; + +static int deadkernel = 0; + +void intpr __P((void)); +void printhdr __P((void)); +void sidewaysintpr __P((u_int)); +void usage __P((void)); + +main(argc, argv) + int argc; + char **argv; +{ + extern int optind; + extern char *optarg; + u_int interval; + int ch; + char *memf, *nlistf; + char errbuf[80]; + + interval = 0; + memf = nlistf = NULL; + while ((ch = getopt(argc, argv, "M:N:w:")) != EOF) + switch(ch) { + case 'M': + memf = optarg; + break; + case 'N': + nlistf = optarg; + break; + case 'w': + interval = atoi(optarg); + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + +#define BACKWARD_COMPATIBILITY +#ifdef BACKWARD_COMPATIBILITY + if (*argv) { + interval = atoi(*argv); + if (*++argv) { + nlistf = *argv; + if (*++argv) + memf = *argv; + } + } +#endif + /* + * Discard setgid privileges if not the running kernel so that bad + * guys can't print interesting stuff from kernel memory. + */ + if (nlistf != NULL || memf != NULL) { + setgid(getgid()); + deadkernel = 1; + + if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, + errbuf)) == 0) { + errx(1, "kvm_openfiles: %s", errbuf); + } + if (kvm_nlist(kd, nl) != 0) { + errx(1, "kvm_nlist: can't get names"); + } + } + + if (interval) + sidewaysintpr(interval); + else + intpr(); + exit(0); +} + +/* + * Read the nfs stats using sysctl(3) for live kernels, or kvm_read + * for dead ones. + */ +void +readstats(stp) + struct nfsstats *stp; +{ + if(deadkernel) { + if(kvm_read(kd, (u_long)nl[N_NFSSTAT].n_value, stp, + sizeof *stp) < 0) { + err(1, "kvm_read"); + } + } else { + int name[3]; + size_t buflen = sizeof *stp; + struct vfsconf vfc; + + if (getvfsbyname("nfs", &vfc) < 0) + err(1, "getvfsbyname: NFS not compiled into kernel"); + name[0] = CTL_VFS; + name[1] = vfc.vfc_typenum; + name[2] = NFS_NFSSTATS; + if (sysctl(name, 3, stp, &buflen, (void *)0, (size_t)0) < 0) { + err(1, "sysctl"); + } + } +} + +/* + * Print a description of the nfs stats. + */ +void +intpr() +{ + struct nfsstats nfsstats; + + readstats(&nfsstats); + + printf("Client Info:\n"); + printf("Rpc Counts:\n"); + printf("%9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s\n", + "Getattr", "Setattr", "Lookup", "Readlink", "Read", + "Write", "Create", "Remove"); + printf("%9d %9d %9d %9d %9d %9d %9d %9d\n", + nfsstats.rpccnt[NFSPROC_GETATTR], + nfsstats.rpccnt[NFSPROC_SETATTR], + nfsstats.rpccnt[NFSPROC_LOOKUP], + nfsstats.rpccnt[NFSPROC_READLINK], + nfsstats.rpccnt[NFSPROC_READ], + nfsstats.rpccnt[NFSPROC_WRITE], + nfsstats.rpccnt[NFSPROC_CREATE], + nfsstats.rpccnt[NFSPROC_REMOVE]); + printf("%9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s\n", + "Rename", "Link", "Symlink", "Mkdir", "Rmdir", + "Readdir", "RdirPlus", "Access"); + printf("%9d %9d %9d %9d %9d %9d %9d %9d\n", + nfsstats.rpccnt[NFSPROC_RENAME], + nfsstats.rpccnt[NFSPROC_LINK], + nfsstats.rpccnt[NFSPROC_SYMLINK], + nfsstats.rpccnt[NFSPROC_MKDIR], + nfsstats.rpccnt[NFSPROC_RMDIR], + nfsstats.rpccnt[NFSPROC_READDIR], + nfsstats.rpccnt[NFSPROC_READDIRPLUS], + nfsstats.rpccnt[NFSPROC_ACCESS]); + printf("%9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s\n", + "Mknod", "Fsstat", "Fsinfo", "PathConf", "Commit", + "GLease", "Vacate", "Evict"); + printf("%9d %9d %9d %9d %9d %9d %9d %9d\n", + nfsstats.rpccnt[NFSPROC_MKNOD], + nfsstats.rpccnt[NFSPROC_FSSTAT], + nfsstats.rpccnt[NFSPROC_FSINFO], + nfsstats.rpccnt[NFSPROC_PATHCONF], + nfsstats.rpccnt[NFSPROC_COMMIT], + nfsstats.rpccnt[NQNFSPROC_GETLEASE], + nfsstats.rpccnt[NQNFSPROC_VACATED], + nfsstats.rpccnt[NQNFSPROC_EVICTED]); + printf("Rpc Info:\n"); + printf("%9.9s %9.9s %9.9s %9.9s %9.9s\n", + "TimedOut", "Invalid", "X Replies", "Retries", "Requests"); + printf("%9d %9d %9d %9d %9d\n", + nfsstats.rpctimeouts, + nfsstats.rpcinvalid, + nfsstats.rpcunexpected, + nfsstats.rpcretries, + nfsstats.rpcrequests); + printf("Cache Info:\n"); + printf("%9.9s %9.9s %9.9s %9.9s", + "Attr Hits", "Misses", "Lkup Hits", "Misses"); + printf(" %9.9s %9.9s %9.9s %9.9s\n", + "BioR Hits", "Misses", "BioW Hits", "Misses"); + printf("%9d %9d %9d %9d", + nfsstats.attrcache_hits, nfsstats.attrcache_misses, + nfsstats.lookupcache_hits, nfsstats.lookupcache_misses); + printf(" %9d %9d %9d %9d\n", + nfsstats.biocache_reads-nfsstats.read_bios, + nfsstats.read_bios, + nfsstats.biocache_writes-nfsstats.write_bios, + nfsstats.write_bios); + printf("%9.9s %9.9s %9.9s %9.9s", + "BioRLHits", "Misses", "BioD Hits", "Misses"); + printf(" %9.9s %9.9s\n", "DirE Hits", "Misses"); + printf("%9d %9d %9d %9d", + nfsstats.biocache_readlinks-nfsstats.readlink_bios, + nfsstats.readlink_bios, + nfsstats.biocache_readdirs-nfsstats.readdir_bios, + nfsstats.readdir_bios); + printf(" %9d %9d\n", + nfsstats.direofcache_hits, nfsstats.direofcache_misses); + printf("\nServer Info:\n"); + printf("%9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s\n", + "Getattr", "Setattr", "Lookup", "Readlink", "Read", + "Write", "Create", "Remove"); + printf("%9d %9d %9d %9d %9d %9d %9d %9d\n", + nfsstats.srvrpccnt[NFSPROC_GETATTR], + nfsstats.srvrpccnt[NFSPROC_SETATTR], + nfsstats.srvrpccnt[NFSPROC_LOOKUP], + nfsstats.srvrpccnt[NFSPROC_READLINK], + nfsstats.srvrpccnt[NFSPROC_READ], + nfsstats.srvrpccnt[NFSPROC_WRITE], + nfsstats.srvrpccnt[NFSPROC_CREATE], + nfsstats.srvrpccnt[NFSPROC_REMOVE]); + printf("%9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s\n", + "Rename", "Link", "Symlink", "Mkdir", "Rmdir", + "Readdir", "RdirPlus", "Access"); + printf("%9d %9d %9d %9d %9d %9d %9d %9d\n", + nfsstats.srvrpccnt[NFSPROC_RENAME], + nfsstats.srvrpccnt[NFSPROC_LINK], + nfsstats.srvrpccnt[NFSPROC_SYMLINK], + nfsstats.srvrpccnt[NFSPROC_MKDIR], + nfsstats.srvrpccnt[NFSPROC_RMDIR], + nfsstats.srvrpccnt[NFSPROC_READDIR], + nfsstats.srvrpccnt[NFSPROC_READDIRPLUS], + nfsstats.srvrpccnt[NFSPROC_ACCESS]); + printf("%9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s\n", + "Mknod", "Fsstat", "Fsinfo", "PathConf", "Commit", + "GLease", "Vacate", "Evict"); + printf("%9d %9d %9d %9d %9d %9d %9d %9d\n", + nfsstats.srvrpccnt[NFSPROC_MKNOD], + nfsstats.srvrpccnt[NFSPROC_FSSTAT], + nfsstats.srvrpccnt[NFSPROC_FSINFO], + nfsstats.srvrpccnt[NFSPROC_PATHCONF], + nfsstats.srvrpccnt[NFSPROC_COMMIT], + nfsstats.srvrpccnt[NQNFSPROC_GETLEASE], + nfsstats.srvrpccnt[NQNFSPROC_VACATED], + nfsstats.srvrpccnt[NQNFSPROC_EVICTED]); + printf("Server Ret-Failed\n"); + printf("%17d\n", nfsstats.srvrpc_errs); + printf("Server Faults\n"); + printf("%13d\n", nfsstats.srv_errs); + printf("Server Cache Stats:\n"); + printf("%9.9s %9.9s %9.9s %9.9s\n", + "Inprog", "Idem", "Non-idem", "Misses"); + printf("%9d %9d %9d %9d\n", + nfsstats.srvcache_inproghits, + nfsstats.srvcache_idemdonehits, + nfsstats.srvcache_nonidemdonehits, + nfsstats.srvcache_misses); + printf("Server Lease Stats:\n"); + printf("%9.9s %9.9s %9.9s\n", + "Leases", "PeakL", "GLeases"); + printf("%9d %9d %9d\n", + nfsstats.srvnqnfs_leases, + nfsstats.srvnqnfs_maxleases, + nfsstats.srvnqnfs_getleases); + printf("Server Write Gathering:\n"); + printf("%9.9s %9.9s %9.9s\n", + "WriteOps", "WriteRPC", "Opsaved"); + printf("%9d %9d %9d\n", + nfsstats.srvvop_writes, + nfsstats.srvrpccnt[NFSPROC_WRITE], + nfsstats.srvrpccnt[NFSPROC_WRITE] - nfsstats.srvvop_writes); +} + +u_char signalled; /* set if alarm goes off "early" */ + +/* + * Print a running summary of nfs statistics. + * Repeat display every interval seconds, showing statistics + * collected over that interval. Assumes that interval is non-zero. + * First line printed at top of screen is always cumulative. + */ +void +sidewaysintpr(interval) + u_int interval; +{ + struct nfsstats nfsstats, lastst; + int hdrcnt, oldmask; + void catchalarm(); + + (void)signal(SIGALRM, catchalarm); + signalled = 0; + (void)alarm(interval); + bzero((caddr_t)&lastst, sizeof(lastst)); + + for (hdrcnt = 1;;) { + if (!--hdrcnt) { + printhdr(); + hdrcnt = 20; + } + readstats(&nfsstats); + printf("Client: %8d %8d %8d %8d %8d %8d %8d %8d\n", + nfsstats.rpccnt[NFSPROC_GETATTR]-lastst.rpccnt[NFSPROC_GETATTR], + nfsstats.rpccnt[NFSPROC_LOOKUP]-lastst.rpccnt[NFSPROC_LOOKUP], + nfsstats.rpccnt[NFSPROC_READLINK]-lastst.rpccnt[NFSPROC_READLINK], + nfsstats.rpccnt[NFSPROC_READ]-lastst.rpccnt[NFSPROC_READ], + nfsstats.rpccnt[NFSPROC_WRITE]-lastst.rpccnt[NFSPROC_WRITE], + nfsstats.rpccnt[NFSPROC_RENAME]-lastst.rpccnt[NFSPROC_RENAME], + nfsstats.rpccnt[NFSPROC_ACCESS]-lastst.rpccnt[NFSPROC_ACCESS], + (nfsstats.rpccnt[NFSPROC_READDIR]-lastst.rpccnt[NFSPROC_READDIR]) + +(nfsstats.rpccnt[NFSPROC_READDIRPLUS]-lastst.rpccnt[NFSPROC_READDIRPLUS])); + printf("Server: %8d %8d %8d %8d %8d %8d %8d %8d\n", + nfsstats.srvrpccnt[NFSPROC_GETATTR]-lastst.srvrpccnt[NFSPROC_GETATTR], + nfsstats.srvrpccnt[NFSPROC_LOOKUP]-lastst.srvrpccnt[NFSPROC_LOOKUP], + nfsstats.srvrpccnt[NFSPROC_READLINK]-lastst.srvrpccnt[NFSPROC_READLINK], + nfsstats.srvrpccnt[NFSPROC_READ]-lastst.srvrpccnt[NFSPROC_READ], + nfsstats.srvrpccnt[NFSPROC_WRITE]-lastst.srvrpccnt[NFSPROC_WRITE], + nfsstats.srvrpccnt[NFSPROC_RENAME]-lastst.srvrpccnt[NFSPROC_RENAME], + nfsstats.srvrpccnt[NFSPROC_ACCESS]-lastst.srvrpccnt[NFSPROC_ACCESS], + (nfsstats.srvrpccnt[NFSPROC_READDIR]-lastst.srvrpccnt[NFSPROC_READDIR]) + +(nfsstats.srvrpccnt[NFSPROC_READDIRPLUS]-lastst.srvrpccnt[NFSPROC_READDIRPLUS])); + lastst = nfsstats; + fflush(stdout); + oldmask = sigblock(sigmask(SIGALRM)); + if (!signalled) + sigpause(0); + sigsetmask(oldmask); + signalled = 0; + (void)alarm(interval); + } + /*NOTREACHED*/ +} + +void +printhdr() +{ + printf(" %8.8s %8.8s %8.8s %8.8s %8.8s %8.8s %8.8s %8.8s\n", + "Getattr", "Lookup", "Readlink", "Read", "Write", "Rename", + "Access", "Readdir"); + fflush(stdout); +} + +/* + * Called if an interval expires before sidewaysintpr has completed a loop. + * Sets a flag to not wait for the alarm. + */ +void +catchalarm() +{ + signalled = 1; +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: nfsstat [-M core] [-N system] [-w interval]\n"); + exit(1); +} diff --git a/usr.bin/nice/nice.c b/usr.bin/nice/nice.c new file mode 100644 index 00000000000..9805bfa9971 --- /dev/null +++ b/usr.bin/nice/nice.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1989, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)nice.c 8.3 (Berkeley) 4/28/95"; +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define DEFNICE 10 + +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int niceness = DEFNICE; + + if (argv[1] == NULL) + usage(); + if (argv[1][0] == '-') + if (argv[1][1] == '-' || isdigit(argv[1][1])) { + niceness = atoi(argv[1] + 1); + ++argv; + if (argv[1] == NULL) + usage(); + } else + errx(1, "illegal option -- %s", argv[1]); + + errno = 0; + niceness += getpriority(PRIO_PROCESS, 0); + if (errno) + err(1, "getpriority"); + if (setpriority(PRIO_PROCESS, 0, niceness)) + err(1, "setpriority"); + execvp(argv[1], &argv[1]); + err(1, "%s", argv[1]); +} + +void +usage() +{ + (void)fprintf(stderr, + "nice [ -# ] command [ options ] [ operands ]\n"); + exit(1); +} diff --git a/usr.bin/passwd/kpasswd_proto.h b/usr.bin/passwd/kpasswd_proto.h new file mode 100644 index 00000000000..465d4c7ad88 --- /dev/null +++ b/usr.bin/passwd/kpasswd_proto.h @@ -0,0 +1,54 @@ +/*- + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)kpasswd_proto.h 8.1 (Berkeley) 6/6/93 + */ + +/* + * kpasswd_proto + * + * definitions for the kpasswd "protocol" + * (We hope this to be temporary until a real admin protocol is worked out.) + */ + +struct kpasswd_data { + des_cblock random_key; + char secure_msg[_PASSWORD_LEN]; +}; + +struct update_data { + char pw[_PASSWORD_LEN]; + char secure_msg[_PASSWORD_LEN]; +}; +#define SERVICE "kpasswd" +#define SECURE_STRING \ + "Kerberos password update program -- 12/9/88 UC Berkeley" diff --git a/usr.bin/passwd/krb_passwd.c b/usr.bin/passwd/krb_passwd.c new file mode 100644 index 00000000000..d4a0f15be7b --- /dev/null +++ b/usr.bin/passwd/krb_passwd.c @@ -0,0 +1,319 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)krb_passwd.c 8.3 (Berkeley) 4/2/94"; +#endif /* not lint */ + +#ifdef KERBEROS + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kpasswd_proto.h" + +#include "extern.h" + +#define PROTO "tcp" + +static void send_update __P((int, char *, char *)); +static void recv_ack __P((int)); +static void cleanup __P((void)); +static void finish __P((void)); + +static struct timeval timeout = { CLIENT_KRB_TIMEOUT, 0 }; +static struct kpasswd_data proto_data; +static des_cblock okey; +static Key_schedule osched; +static KTEXT_ST ticket; +static Key_schedule random_schedule; +static long authopts; +static char realm[REALM_SZ], krbhst[MAX_HSTNM]; +static int sock; + +int +krb_passwd() +{ + struct servent *se; + struct hostent *host; + struct sockaddr_in sin; + CREDENTIALS cred; + fd_set readfds; + int rval; + char pass[_PASSWORD_LEN], password[_PASSWORD_LEN]; + static void finish(); + + static struct rlimit rl = { 0, 0 }; + + (void)signal(SIGHUP, SIG_IGN); + (void)signal(SIGINT, SIG_IGN); + (void)signal(SIGTSTP, SIG_IGN); + + if (setrlimit(RLIMIT_CORE, &rl) < 0) { + warn("setrlimit"); + return (1); + } + + if ((se = getservbyname(SERVICE, PROTO)) == NULL) { + warnx("couldn't find entry for service %s/%s", + SERVICE, PROTO); + return (1); + } + + if ((rval = krb_get_lrealm(realm,1)) != KSUCCESS) { + warnx("couldn't get local Kerberos realm: %s", + krb_err_txt[rval]); + return (1); + } + + if ((rval = krb_get_krbhst(krbhst, realm, 1)) != KSUCCESS) { + warnx("couldn't get Kerberos host: %s", + krb_err_txt[rval]); + return (1); + } + + if ((host = gethostbyname(krbhst)) == NULL) { + warnx("couldn't get host entry for krb host %s", + krbhst); + return (1); + } + + sin.sin_family = host->h_addrtype; + memmove((char *) &sin.sin_addr, host->h_addr, host->h_length); + sin.sin_port = se->s_port; + + if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { + warn("socket"); + return (1); + } + + if (connect(sock, (struct sockaddr *) &sin, sizeof(sin)) < 0) { + warn("connect"); + (void)close(sock); + return (1); + } + + rval = krb_sendauth( + authopts, /* NOT mutual */ + sock, + &ticket, /* (filled in) */ + SERVICE, + krbhst, /* instance (krbhst) */ + realm, /* dest realm */ + (u_long) getpid(), /* checksum */ + NULL, /* msg data */ + NULL, /* credentials */ + NULL, /* schedule */ + NULL, /* local addr */ + NULL, /* foreign addr */ + "KPWDV0.1" + ); + + if (rval != KSUCCESS) { + warnx("Kerberos sendauth error: %s", krb_err_txt[rval]); + return (1); + } + + krb_get_cred("krbtgt", realm, realm, &cred); + + (void)printf("Changing Kerberos password for %s.%s@%s.\n", + cred.pname, cred.pinst, realm); + + if (des_read_pw_string(pass, + sizeof(pass)-1, "Old Kerberos password:", 0)) { + warnx("error reading old Kerberos password"); + return (1); + } + + (void)des_string_to_key(pass, okey); + (void)des_key_sched(okey, osched); + (void)des_set_key(okey, osched); + + /* wait on the verification string */ + + FD_ZERO(&readfds); + FD_SET(sock, &readfds); + + rval = + select(sock + 1, &readfds, (fd_set *) 0, (fd_set *) 0, &timeout); + + if ((rval < 1) || !FD_ISSET(sock, &readfds)) { + if(rval == 0) { + warnx("timed out (aborted)"); + cleanup(); + return (1); + } + warnx("select failed (aborted)"); + cleanup(); + return (1); + } + + /* read verification string */ + + if (des_read(sock, &proto_data, sizeof(proto_data)) != + sizeof(proto_data)) { + warnx("couldn't read verification string (aborted)"); + cleanup(); + return (1); + } + + (void)signal(SIGHUP, finish); + (void)signal(SIGINT, finish); + + if (strcmp(SECURE_STRING, proto_data.secure_msg) != 0) { + cleanup(); + /* don't complain loud if user just hit return */ + if (pass == NULL || (!*pass)) + return (0); + (void)fprintf(stderr, "Sorry\n"); + return (1); + } + + (void)des_key_sched(proto_data.random_key, random_schedule); + (void)des_set_key(proto_data.random_key, random_schedule); + (void)memset(pass, 0, sizeof(pass)); + + if (des_read_pw_string(pass, + sizeof(pass)-1, "New Kerberos password:", 0)) { + warnx("error reading new Kerberos password (aborted)"); + cleanup(); + return (1); + } + + if (des_read_pw_string(password, + sizeof(password)-1, "Retype new Kerberos password:", 0)) { + warnx("error reading new Kerberos password (aborted)"); + cleanup(); + return (1); + } + + if (strcmp(password, pass) != 0) { + warnx("password mismatch (aborted)"); + cleanup(); + return (1); + } + + if (strlen(pass) == 0) + (void)printf("using NULL password\n"); + + send_update(sock, password, SECURE_STRING); + + /* wait for ACK */ + + FD_ZERO(&readfds); + FD_SET(sock, &readfds); + + rval = + select(sock + 1, &readfds, (fd_set *) 0, (fd_set *) 0, &timeout); + if ((rval < 1) || !FD_ISSET(sock, &readfds)) { + if(rval == 0) { + warnx("timed out reading ACK (aborted)"); + cleanup(); + exit(1); + } + warnx("select failed (aborted)"); + cleanup(); + exit(1); + } + recv_ack(sock); + cleanup(); + return (0); +} + +static void +send_update(dest, pwd, str) + int dest; + char *pwd, *str; +{ + static struct update_data ud; + + (void)strncpy(ud.secure_msg, str, _PASSWORD_LEN); + (void)strncpy(ud.pw, pwd, sizeof(ud.pw)); + if (des_write(dest, &ud, sizeof(ud)) != sizeof(ud)) { + warnx("couldn't write pw update (abort)"); + memset((char *)&ud, 0, sizeof(ud)); + cleanup(); + exit(1); + } +} + +static void +recv_ack(remote) + int remote; +{ + int cc; + char buf[BUFSIZ]; + + cc = des_read(remote, buf, sizeof(buf)); + if (cc <= 0) { + warnx("error reading acknowledgement (aborted)"); + cleanup(); + exit(1); + } + (void)printf("%s", buf); +} + +static void +cleanup() +{ + + (void)memset((char *)&proto_data, 0, sizeof(proto_data)); + (void)memset((char *)okey, 0, sizeof(okey)); + (void)memset((char *)osched, 0, sizeof(osched)); + (void)memset((char *)random_schedule, 0, sizeof(random_schedule)); +} + +static void +finish() +{ + + (void)close(sock); + exit(1); +} + +#endif /* KERBEROS */ diff --git a/usr.bin/patch/EXTERN.h b/usr.bin/patch/EXTERN.h new file mode 100644 index 00000000000..0271074bbec --- /dev/null +++ b/usr.bin/patch/EXTERN.h @@ -0,0 +1,15 @@ +/* $Header: EXTERN.h,v 2.0 86/09/17 15:35:37 lwall Exp $ + * + * $Log: EXTERN.h,v $ + * Revision 2.0 86/09/17 15:35:37 lwall + * Baseline for netwide release. + * + */ + +#undef EXT +#define EXT extern + +#undef INIT +#define INIT(x) + +#undef DOINIT diff --git a/usr.bin/patch/INTERN.h b/usr.bin/patch/INTERN.h new file mode 100644 index 00000000000..8bf16f521b7 --- /dev/null +++ b/usr.bin/patch/INTERN.h @@ -0,0 +1,15 @@ +/* $Header: INTERN.h,v 2.0 86/09/17 15:35:58 lwall Exp $ + * + * $Log: INTERN.h,v $ + * Revision 2.0 86/09/17 15:35:58 lwall + * Baseline for netwide release. + * + */ + +#undef EXT +#define EXT + +#undef INIT +#define INIT(x) = x + +#define DOINIT diff --git a/usr.bin/patch/Makefile b/usr.bin/patch/Makefile new file mode 100644 index 00000000000..d6db9f70219 --- /dev/null +++ b/usr.bin/patch/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 + +PROG= patch +SRCS= patch.c pch.c inp.c version.c util.c + +.include diff --git a/usr.bin/patch/README b/usr.bin/patch/README new file mode 100644 index 00000000000..017b1a006ad --- /dev/null +++ b/usr.bin/patch/README @@ -0,0 +1,79 @@ + +The Makefile and config.h files in this directory work with the current +BSD release. Don't run the Configure script, you'll get wrong results. + +Keith Bostic 1/10/88 +----------------------------------------------------------------------------- + + Patch Kit, Version 2.0 + + Copyright (c) 1986, Larry Wall + +You may copy the patch kit in whole or in part as long as you don't try to +make money off it, or pretend that you wrote it. +-------------------------------------------------------------------------- + +Please read all the directions below before you proceed any further, and +then follow them carefully. Failure to do so may void your warranty. :-) + +After you have unpacked your kit, you should have all the files listed +in MANIFEST. + +Installation + +1) Run Configure. This will figure out various things about your system. + Some things Configure will figure out for itself, other things it will + ask you about. It will then proceed to make config.h, config.sh, and + Makefile. + + You might possibly have to trim # comments from the front of Configure + if your sh doesn't handle them, but all other # comments will be taken + care of. + + If you don't have sh, you'll have to rip the prototype of config.h out + of Configure and generate the defines by hand. + +2) Glance through config.h to make sure system dependencies are correct. + Most of them should have been taken care of by running the Configure script. + + If you have any additional changes to make to the C definitions, they + can be done in the Makefile, or in config.h. Bear in mind that they may + get undone next time you run Configure. + +3) make + + This will attempt to make patch in the current directory. + +4) make install + + This will put patch into a public directory (normally /usr/local/bin). + It will also try to put the man pages in a reasonable place. It will not + nroff the man page, however. + +5) Read the manual entry before running patch. + +6) IMPORTANT! Help save the world! Communicate any problems and + suggested patches to me, lwall@sdcrdcf.UUCP (Larry Wall), so we can + keep the world in sync. If you have a problem, there's someone else + out there who either has had or will have the same problem. + + If possible, send in patches such that the patch program will apply them. + Context diffs are the best, then normal diffs. Don't send ed scripts-- + I've probably changed my copy since the version you have. + + Watch for patch patches in net.sources.bugs. Patches will generally be + in a form usable by the patch program. If you are just now bringing up + patch and aren't sure how many patches there are, write to me and I'll + send any you don't have. Your current patch level is shown in patchlevel.h. + + +NEW FEATURES IN THIS RELEASE + +(Correct) support for 4.3bsd-style context diffs. +Files can be created from scratch. +You can specify a fuzz-factor for context matching. +You can force patch to ask no questions. +You can specify how much of the leading pathname to strip off filenames. +Uses a Configure script for greater portability. +You are now asked if you want to apply a reversed patch. +No limit (apart from memory) on the size of hunks. diff --git a/usr.bin/patch/common.h b/usr.bin/patch/common.h new file mode 100644 index 00000000000..42d688312e3 --- /dev/null +++ b/usr.bin/patch/common.h @@ -0,0 +1,138 @@ +/* $Header: common.h,v 2.0 86/09/17 15:36:39 lwall Exp $ + * + * $Log: common.h,v $ + * Revision 2.0 86/09/17 15:36:39 lwall + * Baseline for netwide release. + * + */ + +#define DEBUGGING + +#include "config.h" + +/* shut lint up about the following when return value ignored */ + +#define Signal (void)signal +#define Unlink (void)unlink +#define Lseek (void)lseek +#define Fseek (void)fseek +#define Fstat (void)fstat +#define Pclose (void)pclose +#define Close (void)close +#define Fclose (void)fclose +#define Fflush (void)fflush +#define Sprintf (void)sprintf +#define Mktemp (void)mktemp +#define Strcpy (void)strcpy +#define Strcat (void)strcat + +#include +#include +#include +#include +#include +#include + +/* constants */ + +#define TRUE (1) +#define FALSE (0) + +#define MAXHUNKSIZE 100000 /* is this enough lines? */ +#define INITHUNKMAX 125 /* initial dynamic allocation size */ +#define MAXLINELEN 1024 +#define BUFFERSIZE 1024 +#define ORIGEXT ".orig" +#define SCCSPREFIX "s." +#define GET "get -e %s" +#define RCSSUFFIX ",v" +#define CHECKOUT "co -l %s" + +/* handy definitions */ + +#define Null(t) ((t)0) +#define Nullch Null(char *) +#define Nullfp Null(FILE *) +#define Nulline Null(LINENUM) + +#define Ctl(ch) ((ch) & 037) + +#define strNE(s1,s2) (strcmp(s1, s2)) +#define strEQ(s1,s2) (!strcmp(s1, s2)) +#define strnNE(s1,s2,l) (strncmp(s1, s2, l)) +#define strnEQ(s1,s2,l) (!strncmp(s1, s2, l)) + +/* typedefs */ + +typedef char bool; +typedef long LINENUM; /* must be signed */ +typedef unsigned MEM; /* what to feed malloc */ + +/* globals */ + +EXT int Argc; /* guess */ +EXT char **Argv; +EXT int Argc_last; /* for restarting plan_b */ +EXT char **Argv_last; + +EXT struct stat filestat; /* file statistics area */ +EXT int filemode INIT(0644); + +EXT char buf[MAXLINELEN]; /* general purpose buffer */ +EXT FILE *ofp INIT(Nullfp); /* output file pointer */ +EXT FILE *rejfp INIT(Nullfp); /* reject file pointer */ + +EXT bool using_plan_a INIT(TRUE); /* try to keep everything in memory */ +EXT bool out_of_mem INIT(FALSE); /* ran out of memory in plan a */ + +#define MAXFILEC 2 +EXT int filec INIT(0); /* how many file arguments? */ +EXT char *filearg[MAXFILEC]; +EXT bool ok_to_create_file INIT(FALSE); +EXT char *bestguess INIT(Nullch); /* guess at correct filename */ + +EXT char *outname INIT(Nullch); +EXT char rejname[128]; + +EXT char *origext INIT(Nullch); + +EXT char TMPOUTNAME[] INIT("/tmp/patchoXXXXXX"); +EXT char TMPINNAME[] INIT("/tmp/patchiXXXXXX"); /* might want /usr/tmp here */ +EXT char TMPREJNAME[] INIT("/tmp/patchrXXXXXX"); +EXT char TMPPATNAME[] INIT("/tmp/patchpXXXXXX"); +EXT bool toutkeep INIT(FALSE); +EXT bool trejkeep INIT(FALSE); + +EXT LINENUM last_offset INIT(0); +#ifdef DEBUGGING +EXT int debug INIT(0); +#endif +EXT LINENUM maxfuzz INIT(2); +EXT bool force INIT(FALSE); +EXT bool verbose INIT(TRUE); +EXT bool reverse INIT(FALSE); +EXT bool noreverse INIT(FALSE); +EXT bool skip_rest_of_patch INIT(FALSE); +EXT int strippath INIT(957); +EXT bool canonicalize INIT(FALSE); + +#define CONTEXT_DIFF 1 +#define NORMAL_DIFF 2 +#define ED_DIFF 3 +#define NEW_CONTEXT_DIFF 4 +EXT int diff_type INIT(0); + +EXT bool do_defines INIT(FALSE); /* patch using ifdef, ifndef, etc. */ +EXT char if_defined[128]; /* #ifdef xyzzy */ +EXT char not_defined[128]; /* #ifndef xyzzy */ +EXT char else_defined[] INIT("#else\n");/* #else */ +EXT char end_defined[128]; /* #endif xyzzy */ + +EXT char *revision INIT(Nullch); /* prerequisite revision, if any */ + +char *malloc(); +char *realloc(); +char *strcpy(); +char *strcat(); +long atol(); +char *mktemp(); diff --git a/usr.bin/patch/config.h b/usr.bin/patch/config.h new file mode 100644 index 00000000000..9318fe7cec1 --- /dev/null +++ b/usr.bin/patch/config.h @@ -0,0 +1,16 @@ +#define Reg1 register /**/ +#define Reg2 register /**/ +#define Reg3 register /**/ +#define Reg4 register /**/ +#define Reg5 register /**/ +#define Reg6 register /**/ +#define Reg7 register /**/ +#define Reg8 register /**/ +#define Reg9 register /**/ +#define Reg10 register /**/ +#define Reg11 /**/ +#define Reg12 /**/ +#define Reg13 /**/ +#define Reg14 /**/ +#define Reg15 /**/ +#define Reg16 /**/ diff --git a/usr.bin/patch/inp.c b/usr.bin/patch/inp.c new file mode 100644 index 00000000000..a3eeb904363 --- /dev/null +++ b/usr.bin/patch/inp.c @@ -0,0 +1,313 @@ +/* $Header: inp.c,v 2.0 86/09/17 15:37:02 lwall Exp $ + * + * $Log: inp.c,v $ + * Revision 2.0 86/09/17 15:37:02 lwall + * Baseline for netwide release. + * + */ + +#include "EXTERN.h" +#include "common.h" +#include "util.h" +#include "pch.h" +#include "INTERN.h" +#include "inp.h" + +/* Input-file-with-indexable-lines abstract type */ + +static long i_size; /* size of the input file */ +static char *i_womp; /* plan a buffer for entire file */ +static char **i_ptr; /* pointers to lines in i_womp */ + +static int tifd = -1; /* plan b virtual string array */ +static char *tibuf[2]; /* plan b buffers */ +static LINENUM tiline[2] = {-1, -1}; /* 1st line in each buffer */ +static LINENUM lines_per_buf; /* how many lines per buffer */ +static int tireclen; /* length of records in tmp file */ + +/* New patch--prepare to edit another file. */ + +void +re_input() +{ + if (using_plan_a) { + i_size = 0; +#ifndef lint + if (i_ptr != Null(char**)) + free((char *)i_ptr); +#endif + if (i_womp != Nullch) + free(i_womp); + i_womp = Nullch; + i_ptr = Null(char **); + } + else { + using_plan_a = TRUE; /* maybe the next one is smaller */ + Close(tifd); + tifd = -1; + free(tibuf[0]); + free(tibuf[1]); + tibuf[0] = tibuf[1] = Nullch; + tiline[0] = tiline[1] = -1; + tireclen = 0; + } +} + +/* Constuct the line index, somehow or other. */ + +void +scan_input(filename) +char *filename; +{ + if (!plan_a(filename)) + plan_b(filename); + if (verbose) { + say3("Patching file %s using Plan %s...\n", filename, + (using_plan_a ? "A" : "B") ); + } +} + +/* Try keeping everything in memory. */ + +bool +plan_a(filename) +char *filename; +{ + int ifd; + Reg1 char *s; + Reg2 LINENUM iline; + + if (ok_to_create_file && stat(filename, &filestat) < 0) { + if (verbose) + say2("(Creating file %s...)\n",filename); + makedirs(filename, TRUE); + close(creat(filename, 0666)); + } + if (stat(filename, &filestat) < 0) { + Sprintf(buf, "RCS/%s%s", filename, RCSSUFFIX); + if (stat(buf, &filestat) >= 0 || stat(buf+4, &filestat) >= 0) { + Sprintf(buf, CHECKOUT, filename); + if (verbose) + say2("Can't find %s--attempting to check it out from RCS.\n", + filename); + if (system(buf) || stat(filename, &filestat)) + fatal2("Can't check out %s.\n", filename); + } + else { + Sprintf(buf, "SCCS/%s%s", SCCSPREFIX, filename); + if (stat(buf, &filestat) >= 0 || stat(buf+5, &filestat) >= 0) { + Sprintf(buf, GET, filename); + if (verbose) + say2("Can't find %s--attempting to get it from SCCS.\n", + filename); + if (system(buf) || stat(filename, &filestat)) + fatal2("Can't get %s.\n", filename); + } + else + fatal2("Can't find %s.\n", filename); + } + } + filemode = filestat.st_mode; + if ((filemode & S_IFMT) & ~S_IFREG) + fatal2("%s is not a normal file--can't patch.\n", filename); + i_size = filestat.st_size; + if (out_of_mem) { + set_hunkmax(); /* make sure dynamic arrays are allocated */ + out_of_mem = FALSE; + return FALSE; /* force plan b because plan a bombed */ + } +#ifdef lint + i_womp = Nullch; +#else + i_womp = malloc((MEM)(i_size+2)); /* lint says this may alloc less than */ + /* i_size, but that's okay, I think. */ +#endif + if (i_womp == Nullch) + return FALSE; + if ((ifd = open(filename, 0)) < 0) + fatal2("Can't open file %s\n", filename); +#ifndef lint + if (read(ifd, i_womp, (int)i_size) != i_size) { + Close(ifd); /* probably means i_size > 15 or 16 bits worth */ + free(i_womp); /* at this point it doesn't matter if i_womp was */ + return FALSE; /* undersized. */ + } +#endif + Close(ifd); + if (i_size && i_womp[i_size-1] != '\n') + i_womp[i_size++] = '\n'; + i_womp[i_size] = '\0'; + + /* count the lines in the buffer so we know how many pointers we need */ + + iline = 0; + for (s=i_womp; *s; s++) { + if (*s == '\n') + iline++; + } +#ifdef lint + i_ptr = Null(char**); +#else + i_ptr = (char **)malloc((MEM)((iline + 2) * sizeof(char *))); +#endif + if (i_ptr == Null(char **)) { /* shucks, it was a near thing */ + free((char *)i_womp); + return FALSE; + } + + /* now scan the buffer and build pointer array */ + + iline = 1; + i_ptr[iline] = i_womp; + for (s=i_womp; *s; s++) { + if (*s == '\n') + i_ptr[++iline] = s+1; /* these are NOT null terminated */ + } + input_lines = iline - 1; + + /* now check for revision, if any */ + + if (revision != Nullch) { + if (!rev_in_string(i_womp)) { + if (force) { + if (verbose) + say2("\ +Warning: this file doesn't appear to be the %s version--patching anyway.\n", + revision); + } + else { + ask2("\ +This file doesn't appear to be the %s version--patch anyway? [n] ", + revision); + if (*buf != 'y') + fatal1("Aborted.\n"); + } + } + else if (verbose) + say2("Good. This file appears to be the %s version.\n", + revision); + } + return TRUE; /* plan a will work */ +} + +/* Keep (virtually) nothing in memory. */ + +void +plan_b(filename) +char *filename; +{ + Reg3 FILE *ifp; + Reg1 int i = 0; + Reg2 int maxlen = 1; + Reg4 bool found_revision = (revision == Nullch); + + using_plan_a = FALSE; + if ((ifp = fopen(filename, "r")) == Nullfp) + fatal2("Can't open file %s\n", filename); + if ((tifd = creat(TMPINNAME, 0666)) < 0) + fatal2("Can't open file %s\n", TMPINNAME); + while (fgets(buf, sizeof buf, ifp) != Nullch) { + if (revision != Nullch && !found_revision && rev_in_string(buf)) + found_revision = TRUE; + if ((i = strlen(buf)) > maxlen) + maxlen = i; /* find longest line */ + } + if (revision != Nullch) { + if (!found_revision) { + if (force) { + if (verbose) + say2("\ +Warning: this file doesn't appear to be the %s version--patching anyway.\n", + revision); + } + else { + ask2("\ +This file doesn't appear to be the %s version--patch anyway? [n] ", + revision); + if (*buf != 'y') + fatal1("Aborted.\n"); + } + } + else if (verbose) + say2("Good. This file appears to be the %s version.\n", + revision); + } + Fseek(ifp, 0L, 0); /* rewind file */ + lines_per_buf = BUFFERSIZE / maxlen; + tireclen = maxlen; + tibuf[0] = malloc((MEM)(BUFFERSIZE + 1)); + tibuf[1] = malloc((MEM)(BUFFERSIZE + 1)); + if (tibuf[1] == Nullch) + fatal1("Can't seem to get enough memory.\n"); + for (i=1; ; i++) { + if (! (i % lines_per_buf)) /* new block */ + if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE) + fatal1("patch: can't write temp file.\n"); + if (fgets(tibuf[0] + maxlen * (i%lines_per_buf), maxlen + 1, ifp) + == Nullch) { + input_lines = i - 1; + if (i % lines_per_buf) + if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE) + fatal1("patch: can't write temp file.\n"); + break; + } + } + Fclose(ifp); + Close(tifd); + if ((tifd = open(TMPINNAME, 0)) < 0) { + fatal2("Can't reopen file %s\n", TMPINNAME); + } +} + +/* Fetch a line from the input file, \n terminated, not necessarily \0. */ + +char * +ifetch(line,whichbuf) +Reg1 LINENUM line; +int whichbuf; /* ignored when file in memory */ +{ + if (line < 1 || line > input_lines) + return ""; + if (using_plan_a) + return i_ptr[line]; + else { + LINENUM offline = line % lines_per_buf; + LINENUM baseline = line - offline; + + if (tiline[0] == baseline) + whichbuf = 0; + else if (tiline[1] == baseline) + whichbuf = 1; + else { + tiline[whichbuf] = baseline; +#ifndef lint /* complains of long accuracy */ + Lseek(tifd, (off_t)baseline / lines_per_buf * BUFFERSIZE, 0); +#endif + if (read(tifd, tibuf[whichbuf], BUFFERSIZE) < 0) + fatal2("Error reading tmp file %s.\n", TMPINNAME); + } + return tibuf[whichbuf] + (tireclen*offline); + } +} + +/* True if the string argument contains the revision number we want. */ + +bool +rev_in_string(string) +char *string; +{ + Reg1 char *s; + Reg2 int patlen; + + if (revision == Nullch) + return TRUE; + patlen = strlen(revision); + for (s = string; *s; s++) { + if (isspace(*s) && strnEQ(s+1, revision, patlen) && + isspace(s[patlen+1] )) { + return TRUE; + } + } + return FALSE; +} + diff --git a/usr.bin/patch/inp.h b/usr.bin/patch/inp.h new file mode 100644 index 00000000000..c6d2a917d5c --- /dev/null +++ b/usr.bin/patch/inp.h @@ -0,0 +1,18 @@ +/* $Header: inp.h,v 2.0 86/09/17 15:37:25 lwall Exp $ + * + * $Log: inp.h,v $ + * Revision 2.0 86/09/17 15:37:25 lwall + * Baseline for netwide release. + * + */ + +EXT LINENUM input_lines INIT(0); /* how long is input file in lines */ +EXT LINENUM last_frozen_line INIT(0); /* how many input lines have been */ + /* irretractibly output */ + +bool rev_in_string(); +void scan_input(); +bool plan_a(); /* returns false if insufficient memory */ +void plan_b(); +char *ifetch(); + diff --git a/usr.bin/patch/patch.1 b/usr.bin/patch/patch.1 new file mode 100644 index 00000000000..3e4a12e020a --- /dev/null +++ b/usr.bin/patch/patch.1 @@ -0,0 +1,446 @@ +''' $Header: patch.man,v 2.0 86/09/17 15:39:09 lwall Exp $ +''' +''' $Log: patch.man,v $ +''' Revision 2.0 86/09/17 15:39:09 lwall +''' Baseline for netwide release. +''' +''' Revision 1.4 86/08/01 19:23:22 lwall +''' Documented -v, -p, -F. +''' Added notes to patch senders. +''' +''' Revision 1.3 85/03/26 15:11:06 lwall +''' Frozen. +''' +''' Revision 1.2.1.4 85/03/12 16:14:27 lwall +''' Documented -p. +''' +''' Revision 1.2.1.3 85/03/12 16:09:41 lwall +''' Documented -D. +''' +''' Revision 1.2.1.2 84/12/05 11:06:55 lwall +''' Added -l switch, and noted bistability bug. +''' +''' Revision 1.2.1.1 84/12/04 17:23:39 lwall +''' Branch for sdcrdcf changes. +''' +''' Revision 1.2 84/12/04 17:22:02 lwall +''' Baseline version. +''' +.de Sh +.br +.ne 5 +.PP +\fB\\$1\fR +.PP +.. +.de Sp +.if t .sp .5v +.if n .sp +.. +''' +''' Set up \*(-- to give an unbreakable dash; +''' string Tr holds user defined translation string. +''' Bell System Logo is used as a dummy character. +''' +.ie n \{\ +.tr \(bs-\*(Tr +.ds -- \(bs- +.if (\n(.H=4u)&(1m=24u) .ds -- \(bs\h'-12u'\(bs\h'-12u'-\" diablo 10 pitch +.if (\n(.H=4u)&(1m=20u) .ds -- \(bs\h'-12u'\(bs\h'-8u'-\" diablo 12 pitch +.ds L" "" +.ds R" "" +.ds L' ' +.ds R' ' +'br\} +.el\{\ +.ds -- \(em\| +.tr \*(Tr +.ds L" `` +.ds R" '' +.ds L' ` +.ds R' ' +'br\} +.TH PATCH 1 "June 30, 1993" +.SH NAME +patch \- a program for applying a diff file to an original +.SH SYNOPSIS +.B patch +[options] orig patchfile [+ [options] orig] +.sp +but usually just +.sp +.B patch + +sets the maximum fuzz factor. +This switch only applied to context diffs, and causes +.I patch +to ignore up to that many lines in looking for places to install a hunk. +Note that a larger fuzz factor increases the odds of a faulty patch. +The default fuzz factor is 2, and it may not be set to more than +the number of lines of context in the context diff, ordinarily 3. +.TP 5 +.B \-l +causes the pattern matching to be done loosely, in case the tabs and +spaces have been munged in your input file. +Any sequence of whitespace in the pattern line will match any sequence +in the input file. +Normal characters must still match exactly. +Each line of the context must still match a line in the input file. +.TP 5 +.B \-n +forces +.I patch +to interpret the patch file as a normal diff. +.TP 5 +.B \-N +causes +.I patch +to ignore patches that it thinks are reversed or already applied. +See also +.B \-R . +.TP 5 +.B \-o +causes the next argument to be interpreted as the output file name. +.TP 5 +.B \-p +sets the pathname strip count, +which controls how pathnames found in the patch file are treated, in case +the you keep your files in a different directory than the person who sent +out the patch. +The strip count specifies how many backslashes are to be stripped from +the front of the pathname. +(Any intervening directory names also go away.) +For example, supposing the filename in the patch file was +.sp + /u/howard/src/blurfl/blurfl.c +.sp +setting +.B \-p +or +.B \-p0 +gives the entire pathname unmodified, +.B \-p1 +gives +.sp + u/howard/src/blurfl/blurfl.c +.sp +without the leading slash, +.B \-p4 +gives +.sp + blurfl/blurfl.c +.sp +and not specifying +.B \-p +at all just gives you "blurfl.c". +Whatever you end up with is looked for either in the current directory, +or the directory specified by the +.B \-d +switch. +.TP 5 +.B \-r +causes the next argument to be interpreted as the reject file name. +.TP 5 +.B \-R +tells +.I patch +that this patch was created with the old and new files swapped. +(Yes, I'm afraid that does happen occasionally, human nature being what it +is.) +.I Patch +will attempt to swap each hunk around before applying it. +Rejects will come out in the swapped format. +The +.B \-R +switch will not work with ed diff scripts because there is too little +information to reconstruct the reverse operation. +.Sp +If the first hunk of a patch fails, +.I patch +will reverse the hunk to see if it can be applied that way. +If it can, you will be asked if you want to have the +.B \-R +switch set. +If it can't, the patch will continue to be applied normally. +(Note: this method cannot detect a reversed patch if it is a normal diff +and if the first command is an append (i.e. it should have been a delete) +since appends always succeed, due to the fact that a null context will match +anywhere. +Luckily, most patches add or change lines rather than delete them, so most +reversed normal diffs will begin with a delete, which will fail, triggering +the heuristic.) +.TP 5 +.B \-s +makes +.I patch +do its work silently, unless an error occurs. +.TP 5 +.B \-S +causes +.I patch +to ignore this patch from the patch file, but continue on looking +for the next patch in the file. +Thus +.sp + patch -S + -S + +sets internal debugging flags, and is of interest only to +.I patch +patchers. +.SH ENVIRONMENT +No environment variables are used by +.IR patch . +.SH FILES +/tmp/patch* +.SH SEE ALSO +diff(1) +.SH NOTES FOR PATCH SENDERS +There are several things you should bear in mind if you are going to +be sending out patches. +First, you can save people a lot of grief by keeping a patchlevel.h file +which is patched to increment the patch level as the first diff in the +patch file you send out. +If you put a Prereq: line in with the patch, it won't let them apply +patches out of order without some warning. +Second, make sure you've specified the filenames right, either in a +context diff header, or with an Index: line. +If you are patching something in a subdirectory, be sure to tell the patch +user to specify a +.B \-p +switch as needed. +Third, you can create a file by sending out a diff that compares a +null file to the file you want to create. +This will only work if the file you want to create doesn't exist already in +the target directory. +Fourth, take care not to send out reversed patches, since it makes people wonder +whether they already applied the patch. +Fifth, while you may be able to get away with putting 582 diff listings into +one file, it is probably wiser to group related patches into separate files in +case something goes haywire. +.SH DIAGNOSTICS +Too many to list here, but generally indicative that +.I patch +couldn't parse your patch file. +.PP +The message \*(L"Hmm...\*(R" indicates that there is unprocessed text in +the patch file and that +.I patch +is attempting to intuit whether there is a patch in that text and, if so, +what kind of patch it is. +.SH CAVEATS +.I Patch +cannot tell if the line numbers are off in an ed script, and can only detect +bad line numbers in a normal diff when it finds a \*(L"change\*(R" or +a \*(L"delete\*(R" command. +A context diff using fuzz factor 3 may have the same problem. +Until a suitable interactive interface is added, you should probably do +a context diff in these cases to see if the changes made sense. +Of course, compiling without errors is a pretty good indication that the patch +worked, but not always. +.PP +.I Patch +usually produces the correct results, even when it has to do a lot of +guessing. +However, the results are guaranteed to be correct only when the patch is +applied to exactly the same version of the file that the patch was +generated from. +.SH BUGS +Could be smarter about partial matches, excessively \&deviant offsets and +swapped code, but that would take an extra pass. +.PP +If code has been duplicated (for instance with #ifdef OLDCODE ... #else ... +#endif), +.I patch +is incapable of patching both versions, and, if it works at all, will likely +patch the wrong one, and tell you that it succeeded to boot. +.PP +If you apply a patch you've already applied, +.I patch +will think it is a reversed patch, and offer to un-apply the patch. +This could be construed as a feature. diff --git a/usr.bin/patch/patch.c b/usr.bin/patch/patch.c new file mode 100644 index 00000000000..0f91c5cd7fd --- /dev/null +++ b/usr.bin/patch/patch.c @@ -0,0 +1,800 @@ +#ifndef lint +static char sccsid[] = "@(#)patch.c 8.1 (Berkeley) 6/6/93"; +#endif not lint + +char rcsid[] = + "$Header: patch.c,v 2.0.1.4 87/02/16 14:00:04 lwall Exp $"; + +/* patch - a program to apply diffs to original files + * + * Copyright 1986, Larry Wall + * + * This program may be copied as long as you don't try to make any + * money off of it, or pretend that you wrote it. + * + * $Log: patch.c,v $ + * Revision 2.0.1.4 87/02/16 14:00:04 lwall + * Short replacement caused spurious "Out of sync" message. + * + * Revision 2.0.1.3 87/01/30 22:45:50 lwall + * Improved diagnostic on sync error. + * Moved do_ed_script() to pch.c. + * + * Revision 2.0.1.2 86/11/21 09:39:15 lwall + * Fuzz factor caused offset of installed lines. + * + * Revision 2.0.1.1 86/10/29 13:10:22 lwall + * Backwards search could terminate prematurely. + * + * Revision 2.0 86/09/17 15:37:32 lwall + * Baseline for netwide release. + * + * Revision 1.5 86/08/01 20:53:24 lwall + * Changed some %d's to %ld's. + * Linted. + * + * Revision 1.4 86/08/01 19:17:29 lwall + * Fixes for machines that can't vararg. + * Added fuzz factor. + * Generalized -p. + * General cleanup. + * + * 85/08/15 van%ucbmonet@berkeley + * Changes for 4.3bsd diff -c. + * + * Revision 1.3 85/03/26 15:07:43 lwall + * Frozen. + * + * Revision 1.2.1.9 85/03/12 17:03:35 lwall + * Changed pfp->_file to fileno(pfp). + * + * Revision 1.2.1.8 85/03/12 16:30:43 lwall + * Check i_ptr and i_womp to make sure they aren't null before freeing. + * Also allow ed output to be suppressed. + * + * Revision 1.2.1.7 85/03/12 15:56:13 lwall + * Added -p option from jromine@uci-750a. + * + * Revision 1.2.1.6 85/03/12 12:12:51 lwall + * Now checks for normalness of file to patch. + * + * Revision 1.2.1.5 85/03/12 11:52:12 lwall + * Added -D (#ifdef) option from joe@fluke. + * + * Revision 1.2.1.4 84/12/06 11:14:15 lwall + * Made smarter about SCCS subdirectories. + * + * Revision 1.2.1.3 84/12/05 11:18:43 lwall + * Added -l switch to do loose string comparison. + * + * Revision 1.2.1.2 84/12/04 09:47:13 lwall + * Failed hunk count not reset on multiple patch file. + * + * Revision 1.2.1.1 84/12/04 09:42:37 lwall + * Branch for sdcrdcf changes. + * + * Revision 1.2 84/11/29 13:29:51 lwall + * Linted. Identifiers uniqified. Fixed i_ptr malloc() bug. Fixed + * multiple calls to mktemp(). Will now work on machines that can only + * read 32767 chars. Added -R option for diffs with new and old swapped. + * Various cosmetic changes. + * + * Revision 1.1 84/11/09 17:03:58 lwall + * Initial revision + * + */ + +#include "INTERN.h" +#include "common.h" +#include "EXTERN.h" +#include "version.h" +#include "util.h" +#include "pch.h" +#include "inp.h" + +/* procedures */ + +void reinitialize_almost_everything(); +void get_some_switches(); +LINENUM locate_hunk(); +void abort_hunk(); +void apply_hunk(); +void init_output(); +void init_reject(); +void copy_till(); +void spew_output(); +void dump_line(); +bool patch_match(); +bool similar(); +void re_input(); +void my_exit(); + +/* Apply a set of diffs as appropriate. */ + +main(argc,argv) +int argc; +char **argv; +{ + LINENUM where; + LINENUM newwhere; + LINENUM fuzz; + LINENUM mymaxfuzz; + int hunk = 0; + int failed = 0; + int i; + + setbuf(stderr, serrbuf); + for (i = 0; i= 2) + fatal1("You may not change to a different patch file.\n"); +} + +/* Process switches and filenames up to next '+' or end of list. */ + +void +get_some_switches() +{ + Reg1 char *s; + + rejname[0] = '\0'; + Argc_last = Argc; + Argv_last = Argv; + if (!Argc) + return; + for (Argc--,Argv++; Argc; Argc--,Argv++) { + s = Argv[0]; + if (strEQ(s, "+")) { + return; /* + will be skipped by for loop */ + } + if (*s != '-' || !s[1]) { + if (filec == MAXFILEC) + fatal1("Too many file arguments.\n"); + filearg[filec++] = savestr(s); + } + else { + switch (*++s) { + case 'b': + origext = savestr(Argv[1]); + Argc--,Argv++; + break; + case 'c': + diff_type = CONTEXT_DIFF; + break; + case 'd': + if (!*++s) { + Argc--,Argv++; + s = Argv[0]; + } + if (chdir(s) < 0) + fatal2("Can't cd to %s.\n", s); + break; + case 'D': + do_defines = TRUE; + if (!*++s) { + Argc--,Argv++; + s = Argv[0]; + } + Sprintf(if_defined, "#ifdef %s\n", s); + Sprintf(not_defined, "#ifndef %s\n", s); + Sprintf(end_defined, "#endif /* %s */\n", s); + break; + case 'e': + diff_type = ED_DIFF; + break; + case 'f': + force = TRUE; + break; + case 'F': + if (*++s == '=') + s++; + maxfuzz = atoi(s); + break; + case 'l': + canonicalize = TRUE; + break; + case 'n': + diff_type = NORMAL_DIFF; + break; + case 'N': + noreverse = TRUE; + break; + case 'o': + outname = savestr(Argv[1]); + Argc--,Argv++; + break; + case 'p': + if (*++s == '=') + s++; + strippath = atoi(s); + break; + case 'r': + Strcpy(rejname, Argv[1]); + Argc--,Argv++; + break; + case 'R': + reverse = TRUE; + break; + case 's': + verbose = FALSE; + break; + case 'S': + skip_rest_of_patch = TRUE; + break; + case 'v': + version(); + break; +#ifdef DEBUGGING + case 'x': + debug = atoi(s+1); + break; +#endif + default: + fatal2("Unrecognized switch: %s\n", Argv[0]); + } + } + } +} + +/* Attempt to find the right place to apply this hunk of patch. */ + +LINENUM +locate_hunk(fuzz) +LINENUM fuzz; +{ + Reg1 LINENUM first_guess = pch_first() + last_offset; + Reg2 LINENUM offset; + LINENUM pat_lines = pch_ptrn_lines(); + Reg3 LINENUM max_pos_offset = input_lines - first_guess + - pat_lines + 1; + Reg4 LINENUM max_neg_offset = first_guess - last_frozen_line - 1 + + pch_context(); + + if (!pat_lines) /* null range matches always */ + return first_guess; + if (max_neg_offset >= first_guess) /* do not try lines < 0 */ + max_neg_offset = first_guess - 1; + if (first_guess <= input_lines && patch_match(first_guess, Nulline, fuzz)) + return first_guess; + for (offset = 1; ; offset++) { + Reg5 bool check_after = (offset <= max_pos_offset); + Reg6 bool check_before = (offset <= max_neg_offset); + + if (check_after && patch_match(first_guess, offset, fuzz)) { +#ifdef DEBUGGING + if (debug & 1) + say3("Offset changing from %ld to %ld\n", last_offset, offset); +#endif + last_offset = offset; + return first_guess+offset; + } + else if (check_before && patch_match(first_guess, -offset, fuzz)) { +#ifdef DEBUGGING + if (debug & 1) + say3("Offset changing from %ld to %ld\n", last_offset, -offset); +#endif + last_offset = -offset; + return first_guess-offset; + } + else if (!check_before && !check_after) + return Nulline; + } +} + +/* We did not find the pattern, dump out the hunk so they can handle it. */ + +void +abort_hunk() +{ + Reg1 LINENUM i; + Reg2 LINENUM pat_end = pch_end(); + /* add in last_offset to guess the same as the previous successful hunk */ + LINENUM oldfirst = pch_first() + last_offset; + LINENUM newfirst = pch_newfirst() + last_offset; + LINENUM oldlast = oldfirst + pch_ptrn_lines() - 1; + LINENUM newlast = newfirst + pch_repl_lines() - 1; + char *stars = (diff_type == NEW_CONTEXT_DIFF ? " ****" : ""); + char *minuses = (diff_type == NEW_CONTEXT_DIFF ? " ----" : " -----"); + + fprintf(rejfp, "***************\n"); + for (i=0; i<=pat_end; i++) { + switch (pch_char(i)) { + case '*': + if (oldlast < oldfirst) + fprintf(rejfp, "*** 0%s\n", stars); + else if (oldlast == oldfirst) + fprintf(rejfp, "*** %ld%s\n", oldfirst, stars); + else + fprintf(rejfp, "*** %ld,%ld%s\n", oldfirst, oldlast, stars); + break; + case '=': + if (newlast < newfirst) + fprintf(rejfp, "--- 0%s\n", minuses); + else if (newlast == newfirst) + fprintf(rejfp, "--- %ld%s\n", newfirst, minuses); + else + fprintf(rejfp, "--- %ld,%ld%s\n", newfirst, newlast, minuses); + break; + case '\n': + fprintf(rejfp, "%s", pfetch(i)); + break; + case ' ': case '-': case '+': case '!': + fprintf(rejfp, "%c %s", pch_char(i), pfetch(i)); + break; + default: + say1("Fatal internal error in abort_hunk().\n"); + abort(); + } + } +} + +/* We found where to apply it (we hope), so do it. */ + +void +apply_hunk(where) +LINENUM where; +{ + Reg1 LINENUM old = 1; + Reg2 LINENUM lastline = pch_ptrn_lines(); + Reg3 LINENUM new = lastline+1; +#define OUTSIDE 0 +#define IN_IFNDEF 1 +#define IN_IFDEF 2 +#define IN_ELSE 3 + Reg4 int def_state = OUTSIDE; + Reg5 bool R_do_defines = do_defines; + Reg6 LINENUM pat_end = pch_end(); + + where--; + while (pch_char(new) == '=' || pch_char(new) == '\n') + new++; + + while (old <= lastline) { + if (pch_char(old) == '-') { + copy_till(where + old - 1); + if (R_do_defines) { + if (def_state == OUTSIDE) { + fputs(not_defined, ofp); + def_state = IN_IFNDEF; + } + else if (def_state == IN_IFDEF) { + fputs(else_defined, ofp); + def_state = IN_ELSE; + } + fputs(pfetch(old), ofp); + } + last_frozen_line++; + old++; + } + else if (new > pat_end) + break; + else if (pch_char(new) == '+') { + copy_till(where + old - 1); + if (R_do_defines) { + if (def_state == IN_IFNDEF) { + fputs(else_defined, ofp); + def_state = IN_ELSE; + } + else if (def_state == OUTSIDE) { + fputs(if_defined, ofp); + def_state = IN_IFDEF; + } + } + fputs(pfetch(new), ofp); + new++; + } + else { + if (pch_char(new) != pch_char(old)) { + say3("Out-of-sync patch, lines %ld,%ld--mangled text or line numbers, maybe?\n", + pch_hunk_beg() + old, + pch_hunk_beg() + new); +#ifdef DEBUGGING + say3("oldchar = '%c', newchar = '%c'\n", + pch_char(old), pch_char(new)); +#endif + my_exit(1); + } + if (pch_char(new) == '!') { + copy_till(where + old - 1); + if (R_do_defines) { + fputs(not_defined, ofp); + def_state = IN_IFNDEF; + } + while (pch_char(old) == '!') { + if (R_do_defines) { + fputs(pfetch(old), ofp); + } + last_frozen_line++; + old++; + } + if (R_do_defines) { + fputs(else_defined, ofp); + def_state = IN_ELSE; + } + while (pch_char(new) == '!') { + fputs(pfetch(new), ofp); + new++; + } + if (R_do_defines) { + fputs(end_defined, ofp); + def_state = OUTSIDE; + } + } + else { + assert(pch_char(new) == ' '); + old++; + new++; + } + } + } + if (new <= pat_end && pch_char(new) == '+') { + copy_till(where + old - 1); + if (R_do_defines) { + if (def_state == OUTSIDE) { + fputs(if_defined, ofp); + def_state = IN_IFDEF; + } + else if (def_state == IN_IFNDEF) { + fputs(else_defined, ofp); + def_state = IN_ELSE; + } + } + while (new <= pat_end && pch_char(new) == '+') { + fputs(pfetch(new), ofp); + new++; + } + } + if (R_do_defines && def_state != OUTSIDE) { + fputs(end_defined, ofp); + } +} + +/* Open the new file. */ + +void +init_output(name) +char *name; +{ + ofp = fopen(name, "w"); + if (ofp == Nullfp) + fatal2("patch: can't create %s.\n", name); +} + +/* Open a file to put hunks we can't locate. */ + +void +init_reject(name) +char *name; +{ + rejfp = fopen(name, "w"); + if (rejfp == Nullfp) + fatal2("patch: can't create %s.\n", name); +} + +/* Copy input file to output, up to wherever hunk is to be applied. */ + +void +copy_till(lastline) +Reg1 LINENUM lastline; +{ + Reg2 LINENUM R_last_frozen_line = last_frozen_line; + + if (R_last_frozen_line > lastline) + say1("patch: misordered hunks! output will be garbled.\n"); + while (R_last_frozen_line < lastline) { + dump_line(++R_last_frozen_line); + } + last_frozen_line = R_last_frozen_line; +} + +/* Finish copying the input file to the output file. */ + +void +spew_output() +{ +#ifdef DEBUGGING + if (debug & 256) + say3("il=%ld lfl=%ld\n",input_lines,last_frozen_line); +#endif + if (input_lines) + copy_till(input_lines); /* dump remainder of file */ + Fclose(ofp); + ofp = Nullfp; +} + +/* Copy one line from input to output. */ + +void +dump_line(line) +LINENUM line; +{ + Reg1 char *s; + Reg2 char R_newline = '\n'; + + /* Note: string is not null terminated. */ + for (s=ifetch(line, 0); putc(*s, ofp) != R_newline; s++) ; +} + +/* Does the patch pattern match at line base+offset? */ + +bool +patch_match(base, offset, fuzz) +LINENUM base; +LINENUM offset; +LINENUM fuzz; +{ + Reg1 LINENUM pline = 1 + fuzz; + Reg2 LINENUM iline; + Reg3 LINENUM pat_lines = pch_ptrn_lines() - fuzz; + + for (iline=base+offset+fuzz; pline <= pat_lines; pline++,iline++) { + if (canonicalize) { + if (!similar(ifetch(iline, (offset >= 0)), + pfetch(pline), + pch_line_len(pline) )) + return FALSE; + } + else if (strnNE(ifetch(iline, (offset >= 0)), + pfetch(pline), + pch_line_len(pline) )) + return FALSE; + } + return TRUE; +} + +/* Do two lines match with canonicalized white space? */ + +bool +similar(a,b,len) +Reg1 char *a; +Reg2 char *b; +Reg3 int len; +{ + while (len) { + if (isspace(*b)) { /* whitespace (or \n) to match? */ + if (!isspace(*a)) /* no corresponding whitespace? */ + return FALSE; + while (len && isspace(*b) && *b != '\n') + b++,len--; /* skip pattern whitespace */ + while (isspace(*a) && *a != '\n') + a++; /* skip target whitespace */ + if (*a == '\n' || *b == '\n') + return (*a == *b); /* should end in sync */ + } + else if (*a++ != *b++) /* match non-whitespace chars */ + return FALSE; + else + len--; /* probably not necessary */ + } + return TRUE; /* actually, this is not reached */ + /* since there is always a \n */ +} + +/* Exit with cleanup. */ + +void +my_exit(status) +int status; +{ + Unlink(TMPINNAME); + if (!toutkeep) { + Unlink(TMPOUTNAME); + } + if (!trejkeep) { + Unlink(TMPREJNAME); + } + Unlink(TMPPATNAME); + exit(status); +} diff --git a/usr.bin/patch/patchlevel.h b/usr.bin/patch/patchlevel.h new file mode 100644 index 00000000000..618bca4808a --- /dev/null +++ b/usr.bin/patch/patchlevel.h @@ -0,0 +1 @@ +#define PATCHLEVEL 9 diff --git a/usr.bin/patch/pch.c b/usr.bin/patch/pch.c new file mode 100644 index 00000000000..88372123353 --- /dev/null +++ b/usr.bin/patch/pch.c @@ -0,0 +1,1108 @@ +/* $Header: pch.c,v 2.0.1.6 87/06/04 16:18:13 lwall Exp $ + * + * $Log: pch.c,v $ + * Revision 2.0.1.6 87/06/04 16:18:13 lwall + * pch_swap didn't swap p_bfake and p_efake. + * + * Revision 2.0.1.5 87/01/30 22:47:42 lwall + * Improved responses to mangled patches. + * + * Revision 2.0.1.4 87/01/05 16:59:53 lwall + * New-style context diffs caused double call to free(). + * + * Revision 2.0.1.3 86/11/14 10:08:33 lwall + * Fixed problem where a long pattern wouldn't grow the hunk. + * Also restored p_input_line when backtracking so error messages are right. + * + * Revision 2.0.1.2 86/11/03 17:49:52 lwall + * New-style delete triggers spurious assertion error. + * + * Revision 2.0.1.1 86/10/29 15:52:08 lwall + * Could falsely report new-style context diff. + * + * Revision 2.0 86/09/17 15:39:37 lwall + * Baseline for netwide release. + * + */ + +#include "EXTERN.h" +#include "common.h" +#include "util.h" +#include "INTERN.h" +#include "pch.h" + +/* Patch (diff listing) abstract type. */ + +static long p_filesize; /* size of the patch file */ +static LINENUM p_first; /* 1st line number */ +static LINENUM p_newfirst; /* 1st line number of replacement */ +static LINENUM p_ptrn_lines; /* # lines in pattern */ +static LINENUM p_repl_lines; /* # lines in replacement text */ +static LINENUM p_end = -1; /* last line in hunk */ +static LINENUM p_max; /* max allowed value of p_end */ +static LINENUM p_context = 3; /* # of context lines */ +static LINENUM p_input_line = 0; /* current line # from patch file */ +static char **p_line = Null(char**); /* the text of the hunk */ +static short *p_len = Null(short*); /* length of each line */ +static char *p_char = Nullch; /* +, -, and ! */ +static int hunkmax = INITHUNKMAX; /* size of above arrays to begin with */ +static int p_indent; /* indent to patch */ +static LINENUM p_base; /* where to intuit this time */ +static LINENUM p_bline; /* line # of p_base */ +static LINENUM p_start; /* where intuit found a patch */ +static LINENUM p_sline; /* and the line number for it */ +static LINENUM p_hunk_beg; /* line number of current hunk */ +static LINENUM p_efake = -1; /* end of faked up lines--don't free */ +static LINENUM p_bfake = -1; /* beg of faked up lines */ + +/* Prepare to look for the next patch in the patch file. */ + +void +re_patch() +{ + p_first = Nulline; + p_newfirst = Nulline; + p_ptrn_lines = Nulline; + p_repl_lines = Nulline; + p_end = (LINENUM)-1; + p_max = Nulline; + p_indent = 0; +} + +/* Open the patch file at the beginning of time. */ + +void +open_patch_file(filename) +char *filename; +{ + if (filename == Nullch || !*filename || strEQ(filename, "-")) { + pfp = fopen(TMPPATNAME, "w"); + if (pfp == Nullfp) + fatal2("patch: can't create %s.\n", TMPPATNAME); + while (fgets(buf, sizeof buf, stdin) != Nullch) + fputs(buf, pfp); + Fclose(pfp); + filename = TMPPATNAME; + } + pfp = fopen(filename, "r"); + if (pfp == Nullfp) + fatal2("patch file %s not found\n", filename); + Fstat(fileno(pfp), &filestat); + p_filesize = filestat.st_size; + next_intuit_at(0L,1L); /* start at the beginning */ + set_hunkmax(); +} + +/* Make sure our dynamically realloced tables are malloced to begin with. */ + +void +set_hunkmax() +{ +#ifndef lint + if (p_line == Null(char**)) + p_line = (char**) malloc((MEM)hunkmax * sizeof(char *)); + if (p_len == Null(short*)) + p_len = (short*) malloc((MEM)hunkmax * sizeof(short)); +#endif + if (p_char == Nullch) + p_char = (char*) malloc((MEM)hunkmax * sizeof(char)); +} + +/* Enlarge the arrays containing the current hunk of patch. */ + +void +grow_hunkmax() +{ + hunkmax *= 2; + /* + * Note that on most systems, only the p_line array ever gets fresh memory + * since p_len can move into p_line's old space, and p_char can move into + * p_len's old space. Not on PDP-11's however. But it doesn't matter. + */ + assert(p_line != Null(char**) && p_len != Null(short*) && p_char != Nullch); +#ifndef lint + p_line = (char**) realloc((char*)p_line, (MEM)hunkmax * sizeof(char *)); + p_len = (short*) realloc((char*)p_len, (MEM)hunkmax * sizeof(short)); + p_char = (char*) realloc((char*)p_char, (MEM)hunkmax * sizeof(char)); +#endif + if (p_line != Null(char**) && p_len != Null(short*) && p_char != Nullch) + return; + if (!using_plan_a) + fatal1("patch: out of memory (grow_hunkmax)\n"); + out_of_mem = TRUE; /* whatever is null will be allocated again */ + /* from within plan_a(), of all places */ +} + +/* True if the remainder of the patch file contains a diff of some sort. */ + +bool +there_is_another_patch() +{ + if (p_base != 0L && p_base >= p_filesize) { + if (verbose) + say1("done\n"); + return FALSE; + } + if (verbose) + say1("Hmm..."); + diff_type = intuit_diff_type(); + if (!diff_type) { + if (p_base != 0L) { + if (verbose) + say1(" Ignoring the trailing garbage.\ndone\n"); + } + else + say1(" I can't seem to find a patch in there anywhere.\n"); + return FALSE; + } + if (verbose) + say3(" %sooks like %s to me...\n", + (p_base == 0L ? "L" : "The next patch l"), + diff_type == CONTEXT_DIFF ? "a context diff" : + diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff" : + diff_type == NORMAL_DIFF ? "a normal diff" : + "an ed script" ); + if (p_indent && verbose) + say3("(Patch is indented %d space%s.)\n", p_indent, p_indent==1?"":"s"); + skip_to(p_start,p_sline); + while (filearg[0] == Nullch) { + if (force) { + say1("No file to patch. Skipping...\n"); + filearg[0] = savestr(bestguess); + return TRUE; + } + ask1("File to patch: "); + if (*buf != '\n') { + if (bestguess) + free(bestguess); + bestguess = savestr(buf); + filearg[0] = fetchname(buf, 0, FALSE); + } + if (filearg[0] == Nullch) { + ask1("No file found--skip this patch? [n] "); + if (*buf != 'y') { + continue; + } + if (verbose) + say1("Skipping patch...\n"); + filearg[0] = fetchname(bestguess, 0, TRUE); + skip_rest_of_patch = TRUE; + return TRUE; + } + } + return TRUE; +} + +/* Determine what kind of diff is in the remaining part of the patch file. */ + +int +intuit_diff_type() +{ + Reg4 long this_line = 0; + Reg5 long previous_line; + Reg6 long first_command_line = -1; + long fcl_line; + Reg7 bool last_line_was_command = FALSE; + Reg8 bool this_is_a_command = FALSE; + Reg9 bool stars_last_line = FALSE; + Reg10 bool stars_this_line = FALSE; + Reg3 int indent; + Reg1 char *s; + Reg2 char *t; + char *indtmp = Nullch; + char *oldtmp = Nullch; + char *newtmp = Nullch; + char *indname = Nullch; + char *oldname = Nullch; + char *newname = Nullch; + Reg11 int retval; + bool no_filearg = (filearg[0] == Nullch); + + ok_to_create_file = FALSE; + Fseek(pfp, p_base, 0); + p_input_line = p_bline - 1; + for (;;) { + previous_line = this_line; + last_line_was_command = this_is_a_command; + stars_last_line = stars_this_line; + this_line = ftell(pfp); + indent = 0; + p_input_line++; + if (fgets(buf, sizeof buf, pfp) == Nullch) { + if (first_command_line >= 0L) { + /* nothing but deletes!? */ + p_start = first_command_line; + p_sline = fcl_line; + retval = ED_DIFF; + goto scan_exit; + } + else { + p_start = this_line; + p_sline = p_input_line; + retval = 0; + goto scan_exit; + } + } + for (s = buf; *s == ' ' || *s == '\t'; s++) { + if (*s == '\t') + indent += 8 - (indent % 8); + else + indent++; + } + for (t=s; isdigit(*t) || *t == ','; t++) ; + this_is_a_command = (isdigit(*s) && + (*t == 'd' || *t == 'c' || *t == 'a') ); + if (first_command_line < 0L && this_is_a_command) { + first_command_line = this_line; + fcl_line = p_input_line; + p_indent = indent; /* assume this for now */ + } + if (!stars_last_line && strnEQ(s, "*** ", 4)) + oldtmp = savestr(s+4); + else if (strnEQ(s, "--- ", 4)) + newtmp = savestr(s+4); + else if (strnEQ(s, "Index:", 6)) + indtmp = savestr(s+6); + else if (strnEQ(s, "Prereq:", 7)) { + for (t=s+7; isspace(*t); t++) ; + revision = savestr(t); + for (t=revision; *t && !isspace(*t); t++) ; + *t = '\0'; + if (!*revision) { + free(revision); + revision = Nullch; + } + } + if ((!diff_type || diff_type == ED_DIFF) && + first_command_line >= 0L && + strEQ(s, ".\n") ) { + p_indent = indent; + p_start = first_command_line; + p_sline = fcl_line; + retval = ED_DIFF; + goto scan_exit; + } + stars_this_line = strnEQ(s, "********", 8); + if ((!diff_type || diff_type == CONTEXT_DIFF) && stars_last_line && + strnEQ(s, "*** ", 4)) { + if (!atol(s+4)) + ok_to_create_file = TRUE; + /* if this is a new context diff the character just before */ + /* the newline is a '*'. */ + while (*s != '\n') + s++; + p_indent = indent; + p_start = previous_line; + p_sline = p_input_line - 1; + retval = (*(s-1) == '*' ? NEW_CONTEXT_DIFF : CONTEXT_DIFF); + goto scan_exit; + } + if ((!diff_type || diff_type == NORMAL_DIFF) && + last_line_was_command && + (strnEQ(s, "< ", 2) || strnEQ(s, "> ", 2)) ) { + p_start = previous_line; + p_sline = p_input_line - 1; + p_indent = indent; + retval = NORMAL_DIFF; + goto scan_exit; + } + } + scan_exit: + if (no_filearg) { + if (indtmp != Nullch) + indname = fetchname(indtmp, strippath, ok_to_create_file); + if (oldtmp != Nullch) + oldname = fetchname(oldtmp, strippath, ok_to_create_file); + if (newtmp != Nullch) + newname = fetchname(newtmp, strippath, ok_to_create_file); + if (oldname && newname) { + if (strlen(oldname) < strlen(newname)) + filearg[0] = savestr(oldname); + else + filearg[0] = savestr(newname); + } + else if (oldname) + filearg[0] = savestr(oldname); + else if (newname) + filearg[0] = savestr(newname); + else if (indname) + filearg[0] = savestr(indname); + } + if (bestguess) { + free(bestguess); + bestguess = Nullch; + } + if (filearg[0] != Nullch) + bestguess = savestr(filearg[0]); + else if (indtmp != Nullch) + bestguess = fetchname(indtmp, strippath, TRUE); + else { + if (oldtmp != Nullch) + oldname = fetchname(oldtmp, strippath, TRUE); + if (newtmp != Nullch) + newname = fetchname(newtmp, strippath, TRUE); + if (oldname && newname) { + if (strlen(oldname) < strlen(newname)) + bestguess = savestr(oldname); + else + bestguess = savestr(newname); + } + else if (oldname) + bestguess = savestr(oldname); + else if (newname) + bestguess = savestr(newname); + } + if (indtmp != Nullch) + free(indtmp); + if (oldtmp != Nullch) + free(oldtmp); + if (newtmp != Nullch) + free(newtmp); + if (indname != Nullch) + free(indname); + if (oldname != Nullch) + free(oldname); + if (newname != Nullch) + free(newname); + return retval; +} + +/* Remember where this patch ends so we know where to start up again. */ + +void +next_intuit_at(file_pos,file_line) +long file_pos; +long file_line; +{ + p_base = file_pos; + p_bline = file_line; +} + +/* Basically a verbose fseek() to the actual diff listing. */ + +void +skip_to(file_pos,file_line) +long file_pos; +long file_line; +{ + char *ret; + + assert(p_base <= file_pos); + if (verbose && p_base < file_pos) { + Fseek(pfp, p_base, 0); + say1("The text leading up to this was:\n--------------------------\n"); + while (ftell(pfp) < file_pos) { + ret = fgets(buf, sizeof buf, pfp); + assert(ret != Nullch); + say2("|%s", buf); + } + say1("--------------------------\n"); + } + else + Fseek(pfp, file_pos, 0); + p_input_line = file_line - 1; +} + +/* True if there is more of the current diff listing to process. */ + +bool +another_hunk() +{ + Reg1 char *s; + Reg8 char *ret; + Reg2 int context = 0; + + while (p_end >= 0) { + if (p_end == p_efake) + p_end = p_bfake; /* don't free twice */ + else + free(p_line[p_end]); + p_end--; + } + assert(p_end == -1); + p_efake = -1; + + p_max = hunkmax; /* gets reduced when --- found */ + if (diff_type == CONTEXT_DIFF || diff_type == NEW_CONTEXT_DIFF) { + long line_beginning = ftell(pfp); + /* file pos of the current line */ + LINENUM repl_beginning = 0; /* index of --- line */ + Reg4 LINENUM fillcnt = 0; /* #lines of missing ptrn or repl */ + Reg5 LINENUM fillsrc; /* index of first line to copy */ + Reg6 LINENUM filldst; /* index of first missing line */ + bool ptrn_spaces_eaten = FALSE; /* ptrn was slightly misformed */ + Reg9 bool repl_could_be_missing = TRUE; + /* no + or ! lines in this hunk */ + bool repl_missing = FALSE; /* we are now backtracking */ + long repl_backtrack_position = 0; + /* file pos of first repl line */ + LINENUM repl_patch_line; /* input line number for same */ + Reg7 LINENUM ptrn_copiable = 0; + /* # of copiable lines in ptrn */ + + ret = pgets(buf, sizeof buf, pfp); + p_input_line++; + if (ret == Nullch || strnNE(buf, "********", 8)) { + next_intuit_at(line_beginning,p_input_line); + return FALSE; + } + p_context = 100; + p_hunk_beg = p_input_line + 1; + while (p_end < p_max) { + line_beginning = ftell(pfp); + ret = pgets(buf, sizeof buf, pfp); + p_input_line++; + if (ret == Nullch) { + if (p_max - p_end < 4) + Strcpy(buf, " \n"); /* assume blank lines got chopped */ + else { + if (repl_beginning && repl_could_be_missing) { + repl_missing = TRUE; + goto hunk_done; + } + fatal1("Unexpected end of file in patch.\n"); + } + } + p_end++; + assert(p_end < hunkmax); + p_char[p_end] = *buf; + p_line[p_end] = Nullch; + switch (*buf) { + case '*': + if (strnEQ(buf, "********", 8)) { + if (repl_beginning && repl_could_be_missing) { + repl_missing = TRUE; + goto hunk_done; + } + else + fatal2("Unexpected end of hunk at line %ld.\n", + p_input_line); + } + if (p_end != 0) { + if (repl_beginning && repl_could_be_missing) { + repl_missing = TRUE; + goto hunk_done; + } + fatal3("Unexpected *** at line %ld: %s", p_input_line, buf); + } + context = 0; + p_line[p_end] = savestr(buf); + if (out_of_mem) { + p_end--; + return FALSE; + } + for (s=buf; *s && !isdigit(*s); s++) ; + if (!*s) + goto malformed; + p_first = (LINENUM) atol(s); + while (isdigit(*s)) s++; + if (*s == ',') { + for (; *s && !isdigit(*s); s++) ; + if (!*s) + goto malformed; + p_ptrn_lines = ((LINENUM)atol(s)) - p_first + 1; + } + else if (p_first) + p_ptrn_lines = 1; + else { + p_ptrn_lines = 0; + p_first = 1; + } + p_max = p_ptrn_lines + 6; /* we need this much at least */ + while (p_max >= hunkmax) + grow_hunkmax(); + p_max = hunkmax; + break; + case '-': + if (buf[1] == '-') { + if (repl_beginning || + (p_end != p_ptrn_lines + 1 + (p_char[p_end-1] == '\n'))) + { + if (p_end == 1) { + /* `old' lines were omitted - set up to fill */ + /* them in from 'new' context lines. */ + p_end = p_ptrn_lines + 1; + fillsrc = p_end + 1; + filldst = 1; + fillcnt = p_ptrn_lines; + } + else { + if (repl_beginning) { + if (repl_could_be_missing){ + repl_missing = TRUE; + goto hunk_done; + } + fatal3( +"Duplicate \"---\" at line %ld--check line numbers at line %ld.\n", + p_input_line, p_hunk_beg + repl_beginning); + } + else { + fatal4( +"%s \"---\" at line %ld--check line numbers at line %ld.\n", + (p_end <= p_ptrn_lines + ? "Premature" + : "Overdue" ), + p_input_line, p_hunk_beg); + } + } + } + repl_beginning = p_end; + repl_backtrack_position = ftell(pfp); + repl_patch_line = p_input_line; + p_line[p_end] = savestr(buf); + if (out_of_mem) { + p_end--; + return FALSE; + } + p_char[p_end] = '='; + for (s=buf; *s && !isdigit(*s); s++) ; + if (!*s) + goto malformed; + p_newfirst = (LINENUM) atol(s); + while (isdigit(*s)) s++; + if (*s == ',') { + for (; *s && !isdigit(*s); s++) ; + if (!*s) + goto malformed; + p_repl_lines = ((LINENUM)atol(s)) - p_newfirst + 1; + } + else if (p_newfirst) + p_repl_lines = 1; + else { + p_repl_lines = 0; + p_newfirst = 1; + } + p_max = p_repl_lines + p_end; + if (p_max > MAXHUNKSIZE) + fatal4("Hunk too large (%ld lines) at line %ld: %s", + p_max, p_input_line, buf); + while (p_max >= hunkmax) + grow_hunkmax(); + if (p_repl_lines != ptrn_copiable) + repl_could_be_missing = FALSE; + break; + } + goto change_line; + case '+': case '!': + repl_could_be_missing = FALSE; + change_line: + if (!isspace(buf[1]) && buf[1] != '>' && buf[1] != '<' && + repl_beginning && repl_could_be_missing) { + repl_missing = TRUE; + goto hunk_done; + } + if (context > 0) { + if (context < p_context) + p_context = context; + context = -1000; + } + p_line[p_end] = savestr(buf+2); + if (out_of_mem) { + p_end--; + return FALSE; + } + break; + case '\t': case '\n': /* assume the 2 spaces got eaten */ + if (repl_beginning && repl_could_be_missing && + (!ptrn_spaces_eaten || diff_type == NEW_CONTEXT_DIFF) ) { + repl_missing = TRUE; + goto hunk_done; + } + p_line[p_end] = savestr(buf); + if (out_of_mem) { + p_end--; + return FALSE; + } + if (p_end != p_ptrn_lines + 1) { + ptrn_spaces_eaten |= (repl_beginning != 0); + context++; + if (!repl_beginning) + ptrn_copiable++; + p_char[p_end] = ' '; + } + break; + case ' ': + if (!isspace(buf[1]) && + repl_beginning && repl_could_be_missing) { + repl_missing = TRUE; + goto hunk_done; + } + context++; + if (!repl_beginning) + ptrn_copiable++; + p_line[p_end] = savestr(buf+2); + if (out_of_mem) { + p_end--; + return FALSE; + } + break; + default: + if (repl_beginning && repl_could_be_missing) { + repl_missing = TRUE; + goto hunk_done; + } + goto malformed; + } + /* set up p_len for strncmp() so we don't have to */ + /* assume null termination */ + if (p_line[p_end]) + p_len[p_end] = strlen(p_line[p_end]); + else + p_len[p_end] = 0; + } + + hunk_done: + if (p_end >=0 && !repl_beginning) + fatal2("No --- found in patch at line %ld\n", pch_hunk_beg()); + + if (repl_missing) { + + /* reset state back to just after --- */ + p_input_line = repl_patch_line; + for (p_end--; p_end > repl_beginning; p_end--) + free(p_line[p_end]); + Fseek(pfp, repl_backtrack_position, 0); + + /* redundant 'new' context lines were omitted - set */ + /* up to fill them in from the old file context */ + fillsrc = 1; + filldst = repl_beginning+1; + fillcnt = p_repl_lines; + p_end = p_max; + } + + if (diff_type == CONTEXT_DIFF && + (fillcnt || (p_first > 1 && ptrn_copiable > 2*p_context)) ) { + if (verbose) + say1("\ +(Fascinating--this is really a new-style context diff but without the telltale\n\ +extra asterisks on the *** line that usually indicate the new style...)\n"); + diff_type = NEW_CONTEXT_DIFF; + } + + /* if there were omitted context lines, fill them in now */ + if (fillcnt) { + p_bfake = filldst; /* remember where not to free() */ + p_efake = filldst + fillcnt - 1; + while (fillcnt-- > 0) { + while (fillsrc <= p_end && p_char[fillsrc] != ' ') + fillsrc++; + if (fillsrc > p_end) + fatal2("Replacement text or line numbers mangled in hunk at line %ld\n", + p_hunk_beg); + p_line[filldst] = p_line[fillsrc]; + p_char[filldst] = p_char[fillsrc]; + p_len[filldst] = p_len[fillsrc]; + fillsrc++; filldst++; + } + while (fillsrc <= p_end && fillsrc != repl_beginning && + p_char[fillsrc] != ' ') + fillsrc++; +#ifdef DEBUGGING + if (debug & 64) + printf("fillsrc %ld, filldst %ld, rb %ld, e+1 %ld\n", + fillsrc,filldst,repl_beginning,p_end+1); +#endif + assert(fillsrc==p_end+1 || fillsrc==repl_beginning); + assert(filldst==p_end+1 || filldst==repl_beginning); + } + } + else { /* normal diff--fake it up */ + char hunk_type; + Reg3 int i; + LINENUM min, max; + long line_beginning = ftell(pfp); + + p_context = 0; + ret = pgets(buf, sizeof buf, pfp); + p_input_line++; + if (ret == Nullch || !isdigit(*buf)) { + next_intuit_at(line_beginning,p_input_line); + return FALSE; + } + p_first = (LINENUM)atol(buf); + for (s=buf; isdigit(*s); s++) ; + if (*s == ',') { + p_ptrn_lines = (LINENUM)atol(++s) - p_first + 1; + while (isdigit(*s)) s++; + } + else + p_ptrn_lines = (*s != 'a'); + hunk_type = *s; + if (hunk_type == 'a') + p_first++; /* do append rather than insert */ + min = (LINENUM)atol(++s); + for (; isdigit(*s); s++) ; + if (*s == ',') + max = (LINENUM)atol(++s); + else + max = min; + if (hunk_type == 'd') + min++; + p_end = p_ptrn_lines + 1 + max - min + 1; + if (p_end > MAXHUNKSIZE) + fatal4("Hunk too large (%ld lines) at line %ld: %s", + p_end, p_input_line, buf); + while (p_end >= hunkmax) + grow_hunkmax(); + p_newfirst = min; + p_repl_lines = max - min + 1; + Sprintf(buf, "*** %ld,%ld\n", p_first, p_first + p_ptrn_lines - 1); + p_line[0] = savestr(buf); + if (out_of_mem) { + p_end = -1; + return FALSE; + } + p_char[0] = '*'; + for (i=1; i<=p_ptrn_lines; i++) { + ret = pgets(buf, sizeof buf, pfp); + p_input_line++; + if (ret == Nullch) + fatal2("Unexpected end of file in patch at line %ld.\n", + p_input_line); + if (*buf != '<') + fatal2("< expected at line %ld of patch.\n", p_input_line); + p_line[i] = savestr(buf+2); + if (out_of_mem) { + p_end = i-1; + return FALSE; + } + p_len[i] = strlen(p_line[i]); + p_char[i] = '-'; + } + if (hunk_type == 'c') { + ret = pgets(buf, sizeof buf, pfp); + p_input_line++; + if (ret == Nullch) + fatal2("Unexpected end of file in patch at line %ld.\n", + p_input_line); + if (*buf != '-') + fatal2("--- expected at line %ld of patch.\n", p_input_line); + } + Sprintf(buf, "--- %ld,%ld\n", min, max); + p_line[i] = savestr(buf); + if (out_of_mem) { + p_end = i-1; + return FALSE; + } + p_char[i] = '='; + for (i++; i<=p_end; i++) { + ret = pgets(buf, sizeof buf, pfp); + p_input_line++; + if (ret == Nullch) + fatal2("Unexpected end of file in patch at line %ld.\n", + p_input_line); + if (*buf != '>') + fatal2("> expected at line %ld of patch.\n", p_input_line); + p_line[i] = savestr(buf+2); + if (out_of_mem) { + p_end = i-1; + return FALSE; + } + p_len[i] = strlen(p_line[i]); + p_char[i] = '+'; + } + } + if (reverse) /* backwards patch? */ + if (!pch_swap()) + say1("Not enough memory to swap next hunk!\n"); +#ifdef DEBUGGING + if (debug & 2) { + int i; + char special; + + for (i=0; i <= p_end; i++) { + if (i == p_ptrn_lines) + special = '^'; + else + special = ' '; + fprintf(stderr, "%3d %c %c %s", i, p_char[i], special, p_line[i]); + Fflush(stderr); + } + } +#endif + if (p_end+1 < hunkmax) /* paranoia reigns supreme... */ + p_char[p_end+1] = '^'; /* add a stopper for apply_hunk */ + return TRUE; + +malformed: + fatal3("Malformed patch at line %ld: %s", p_input_line, buf); + /* about as informative as "Syntax error" in C */ + return FALSE; /* for lint */ +} + +/* Input a line from the patch file, worrying about indentation. */ + +char * +pgets(bf,sz,fp) +char *bf; +int sz; +FILE *fp; +{ + char *ret = fgets(bf, sz, fp); + Reg1 char *s; + Reg2 int indent = 0; + + if (p_indent && ret != Nullch) { + for (s=buf; indent < p_indent && (*s == ' ' || *s == '\t'); s++) { + if (*s == '\t') + indent += 8 - (indent % 7); + else + indent++; + } + if (buf != s) + Strcpy(buf, s); + } + return ret; +} + +/* Reverse the old and new portions of the current hunk. */ + +bool +pch_swap() +{ + char **tp_line; /* the text of the hunk */ + short *tp_len; /* length of each line */ + char *tp_char; /* +, -, and ! */ + Reg1 LINENUM i; + Reg2 LINENUM n; + bool blankline = FALSE; + Reg3 char *s; + + i = p_first; + p_first = p_newfirst; + p_newfirst = i; + + /* make a scratch copy */ + + tp_line = p_line; + tp_len = p_len; + tp_char = p_char; + p_line = Null(char**); /* force set_hunkmax to allocate again */ + p_len = Null(short*); + p_char = Nullch; + set_hunkmax(); + if (p_line == Null(char**) || p_len == Null(short*) || p_char == Nullch) { +#ifndef lint + if (p_line == Null(char**)) + free((char*)p_line); + p_line = tp_line; + if (p_len == Null(short*)) + free((char*)p_len); + p_len = tp_len; +#endif + if (p_char == Nullch) + free((char*)p_char); + p_char = tp_char; + return FALSE; /* not enough memory to swap hunk! */ + } + + /* now turn the new into the old */ + + i = p_ptrn_lines + 1; + if (tp_char[i] == '\n') { /* account for possible blank line */ + blankline = TRUE; + i++; + } + if (p_efake >= 0) { /* fix non-freeable ptr range */ + n = p_end - i + 1; + if (p_efake > i) + n = -n; + p_efake += n; + p_bfake += n; + } + for (n=0; i <= p_end; i++,n++) { + p_line[n] = tp_line[i]; + p_char[n] = tp_char[i]; + if (p_char[n] == '+') + p_char[n] = '-'; + p_len[n] = tp_len[i]; + } + if (blankline) { + i = p_ptrn_lines + 1; + p_line[n] = tp_line[i]; + p_char[n] = tp_char[i]; + p_len[n] = tp_len[i]; + n++; + } + assert(p_char[0] == '='); + p_char[0] = '*'; + for (s=p_line[0]; *s; s++) + if (*s == '-') + *s = '*'; + + /* now turn the old into the new */ + + assert(tp_char[0] == '*'); + tp_char[0] = '='; + for (s=tp_line[0]; *s; s++) + if (*s == '*') + *s = '-'; + for (i=0; n <= p_end; i++,n++) { + p_line[n] = tp_line[i]; + p_char[n] = tp_char[i]; + if (p_char[n] == '-') + p_char[n] = '+'; + p_len[n] = tp_len[i]; + } + assert(i == p_ptrn_lines + 1); + i = p_ptrn_lines; + p_ptrn_lines = p_repl_lines; + p_repl_lines = i; +#ifndef lint + if (tp_line == Null(char**)) + free((char*)tp_line); + if (tp_len == Null(short*)) + free((char*)tp_len); +#endif + if (tp_char == Nullch) + free((char*)tp_char); + return TRUE; +} + +/* Return the specified line position in the old file of the old context. */ + +LINENUM +pch_first() +{ + return p_first; +} + +/* Return the number of lines of old context. */ + +LINENUM +pch_ptrn_lines() +{ + return p_ptrn_lines; +} + +/* Return the probable line position in the new file of the first line. */ + +LINENUM +pch_newfirst() +{ + return p_newfirst; +} + +/* Return the number of lines in the replacement text including context. */ + +LINENUM +pch_repl_lines() +{ + return p_repl_lines; +} + +/* Return the number of lines in the whole hunk. */ + +LINENUM +pch_end() +{ + return p_end; +} + +/* Return the number of context lines before the first changed line. */ + +LINENUM +pch_context() +{ + return p_context; +} + +/* Return the length of a particular patch line. */ + +short +pch_line_len(line) +LINENUM line; +{ + return p_len[line]; +} + +/* Return the control character (+, -, *, !, etc) for a patch line. */ + +char +pch_char(line) +LINENUM line; +{ + return p_char[line]; +} + +/* Return a pointer to a particular patch line. */ + +char * +pfetch(line) +LINENUM line; +{ + return p_line[line]; +} + +/* Return where in the patch file this hunk began, for error messages. */ + +LINENUM +pch_hunk_beg() +{ + return p_hunk_beg; +} + +/* Apply an ed script by feeding ed itself. */ + +void +do_ed_script() +{ + Reg1 char *t; + Reg2 long beginning_of_this_line; + Reg3 bool this_line_is_command = FALSE; + Reg4 FILE *pipefp; + FILE *popen(); + + if (!skip_rest_of_patch) { + Unlink(TMPOUTNAME); + copy_file(filearg[0], TMPOUTNAME); + if (verbose) + Sprintf(buf, "/bin/ed %s", TMPOUTNAME); + else + Sprintf(buf, "/bin/ed - %s", TMPOUTNAME); + pipefp = popen(buf, "w"); + } + for (;;) { + beginning_of_this_line = ftell(pfp); + if (pgets(buf, sizeof buf, pfp) == Nullch) { + next_intuit_at(beginning_of_this_line,p_input_line); + break; + } + p_input_line++; + for (t=buf; isdigit(*t) || *t == ','; t++) ; + this_line_is_command = (isdigit(*buf) && + (*t == 'd' || *t == 'c' || *t == 'a') ); + if (this_line_is_command) { + if (!skip_rest_of_patch) + fputs(buf, pipefp); + if (*t != 'd') { + while (pgets(buf, sizeof buf, pfp) != Nullch) { + p_input_line++; + if (!skip_rest_of_patch) + fputs(buf, pipefp); + if (strEQ(buf, ".\n")) + break; + } + } + } + else { + next_intuit_at(beginning_of_this_line,p_input_line); + break; + } + } + if (skip_rest_of_patch) + return; + fprintf(pipefp, "w\n"); + fprintf(pipefp, "q\n"); + Fflush(pipefp); + Pclose(pipefp); + ignore_signals(); + if (move_file(TMPOUTNAME, outname) < 0) { + toutkeep = TRUE; + chmod(TMPOUTNAME, filemode); + } + else + chmod(outname, filemode); + set_signals(); +} diff --git a/usr.bin/patch/pch.h b/usr.bin/patch/pch.h new file mode 100644 index 00000000000..97a5b28a338 --- /dev/null +++ b/usr.bin/patch/pch.h @@ -0,0 +1,36 @@ +/* $Header: pch.h,v 2.0.1.1 87/01/30 22:47:16 lwall Exp $ + * + * $Log: pch.h,v $ + * Revision 2.0.1.1 87/01/30 22:47:16 lwall + * Added do_ed_script(). + * + * Revision 2.0 86/09/17 15:39:57 lwall + * Baseline for netwide release. + * + */ + +EXT FILE *pfp INIT(Nullfp); /* patch file pointer */ + +void re_patch(); +void open_patch_file(); +void set_hunkmax(); +void grow_hunkmax(); +bool there_is_another_patch(); +int intuit_diff_type(); +void next_intuit_at(); +void skip_to(); +bool another_hunk(); +bool pch_swap(); +char *pfetch(); +short pch_line_len(); +LINENUM pch_first(); +LINENUM pch_ptrn_lines(); +LINENUM pch_newfirst(); +LINENUM pch_repl_lines(); +LINENUM pch_end(); +LINENUM pch_context(); +LINENUM pch_hunk_beg(); +char pch_char(); +char *pfetch(); +char *pgets(); +void do_ed_script(); diff --git a/usr.bin/patch/util.c b/usr.bin/patch/util.c new file mode 100644 index 00000000000..5582d188337 --- /dev/null +++ b/usr.bin/patch/util.c @@ -0,0 +1,339 @@ +#include "EXTERN.h" +#include "common.h" +#include "INTERN.h" +#include "util.h" + +/* Rename a file, copying it if necessary. */ + +int +move_file(from,to) +char *from, *to; +{ + char bakname[512]; + Reg1 char *s; + Reg2 int i; + Reg3 int fromfd; + + /* to stdout? */ + + if (strEQ(to, "-")) { +#ifdef DEBUGGING + if (debug & 4) + say2("Moving %s to stdout.\n", from); +#endif + fromfd = open(from, 0); + if (fromfd < 0) + fatal2("patch: internal error, can't reopen %s\n", from); + while ((i=read(fromfd, buf, sizeof buf)) > 0) + if (write(1, buf, i) != 1) + fatal1("patch: write failed\n"); + Close(fromfd); + return 0; + } + + Strcpy(bakname, to); + Strcat(bakname, origext?origext:ORIGEXT); + if (stat(to, &filestat) >= 0) { /* output file exists */ + dev_t to_device = filestat.st_dev; + ino_t to_inode = filestat.st_ino; + char *simplename = bakname; + + for (s=bakname; *s; s++) { + if (*s == '/') + simplename = s+1; + } + /* find a backup name that is not the same file */ + while (stat(bakname, &filestat) >= 0 && + to_device == filestat.st_dev && to_inode == filestat.st_ino) { + for (s=simplename; *s && !islower(*s); s++) ; + if (*s) + *s = toupper(*s); + else + Strcpy(simplename, simplename+1); + } + while (unlink(bakname) >= 0) ; /* while() is for benefit of Eunice */ +#ifdef DEBUGGING + if (debug & 4) + say3("Moving %s to %s.\n", to, bakname); +#endif + if (link(to, bakname) < 0) { + say3("patch: can't backup %s, output is in %s\n", + to, from); + return -1; + } + while (unlink(to) >= 0) ; + } +#ifdef DEBUGGING + if (debug & 4) + say3("Moving %s to %s.\n", from, to); +#endif + if (link(from, to) < 0) { /* different file system? */ + Reg4 int tofd; + + tofd = creat(to, 0666); + if (tofd < 0) { + say3("patch: can't create %s, output is in %s.\n", + to, from); + return -1; + } + fromfd = open(from, 0); + if (fromfd < 0) + fatal2("patch: internal error, can't reopen %s\n", from); + while ((i=read(fromfd, buf, sizeof buf)) > 0) + if (write(tofd, buf, i) != i) + fatal1("patch: write failed\n"); + Close(fromfd); + Close(tofd); + } + Unlink(from); + return 0; +} + +/* Copy a file. */ + +void +copy_file(from,to) +char *from, *to; +{ + Reg3 int tofd; + Reg2 int fromfd; + Reg1 int i; + + tofd = creat(to, 0666); + if (tofd < 0) + fatal2("patch: can't create %s.\n", to); + fromfd = open(from, 0); + if (fromfd < 0) + fatal2("patch: internal error, can't reopen %s\n", from); + while ((i=read(fromfd, buf, sizeof buf)) > 0) + if (write(tofd, buf, i) != i) + fatal2("patch: write (%s) failed\n", to); + Close(fromfd); + Close(tofd); +} + +/* Allocate a unique area for a string. */ + +char * +savestr(s) +Reg1 char *s; +{ + Reg3 char *rv; + Reg2 char *t; + + if (!s) + s = "Oops"; + t = s; + while (*t++); + rv = malloc((MEM) (t - s)); + if (rv == Nullch) { + if (using_plan_a) + out_of_mem = TRUE; + else + fatal1("patch: out of memory (savestr)\n"); + } + else { + t = rv; + while (*t++ = *s++); + } + return rv; +} + +#if defined(lint) && defined(CANVARARG) + +/*VARARGS ARGSUSED*/ +say(pat) char *pat; { ; } +/*VARARGS ARGSUSED*/ +fatal(pat) char *pat; { ; } +/*VARARGS ARGSUSED*/ +ask(pat) char *pat; { ; } + +#else + +/* Vanilla terminal output (buffered). */ + +void +say(pat,arg1,arg2,arg3) +char *pat; +int arg1,arg2,arg3; +{ + fprintf(stderr, pat, arg1, arg2, arg3); + Fflush(stderr); +} + +/* Terminal output, pun intended. */ + +void /* very void */ +fatal(pat,arg1,arg2,arg3) +char *pat; +int arg1,arg2,arg3; +{ + void my_exit(); + + say(pat, arg1, arg2, arg3); + my_exit(1); +} + +/* Get a response from the user, somehow or other. */ + +void +ask(pat,arg1,arg2,arg3) +char *pat; +int arg1,arg2,arg3; +{ + int ttyfd; + int r; + bool tty2 = isatty(2); + + Sprintf(buf, pat, arg1, arg2, arg3); + Fflush(stderr); + write(2, buf, strlen(buf)); + if (tty2) { /* might be redirected to a file */ + r = read(2, buf, sizeof buf); + } + else if (isatty(1)) { /* this may be new file output */ + Fflush(stdout); + write(1, buf, strlen(buf)); + r = read(1, buf, sizeof buf); + } + else if ((ttyfd = open("/dev/tty", 2)) >= 0 && isatty(ttyfd)) { + /* might be deleted or unwriteable */ + write(ttyfd, buf, strlen(buf)); + r = read(ttyfd, buf, sizeof buf); + Close(ttyfd); + } + else if (isatty(0)) { /* this is probably patch input */ + Fflush(stdin); + write(0, buf, strlen(buf)); + r = read(0, buf, sizeof buf); + } + else { /* no terminal at all--default it */ + buf[0] = '\n'; + r = 1; + } + if (r <= 0) + buf[0] = 0; + else + buf[r] = '\0'; + if (!tty2) + say1(buf); +} +#endif lint + +/* How to handle certain events when not in a critical region. */ + +void +set_signals() +{ + void my_exit(); + +#ifndef lint + if (signal(SIGHUP, SIG_IGN) != SIG_IGN) + Signal(SIGHUP, my_exit); + if (signal(SIGINT, SIG_IGN) != SIG_IGN) + Signal(SIGINT, my_exit); +#endif +} + +/* How to handle certain events when in a critical region. */ + +void +ignore_signals() +{ +#ifndef lint + Signal(SIGHUP, SIG_IGN); + Signal(SIGINT, SIG_IGN); +#endif +} + +/* Make sure we'll have the directories to create a file. */ + +void +makedirs(filename,striplast) +Reg1 char *filename; +bool striplast; +{ + char tmpbuf[256]; + Reg2 char *s = tmpbuf; + char *dirv[20]; + Reg3 int i; + Reg4 int dirvp = 0; + + while (*filename) { + if (*filename == '/') { + filename++; + dirv[dirvp++] = s; + *s++ = '\0'; + } + else { + *s++ = *filename++; + } + } + *s = '\0'; + dirv[dirvp] = s; + if (striplast) + dirvp--; + if (dirvp < 0) + return; + strcpy(buf, "mkdir"); + s = buf; + for (i=0; i<=dirvp; i++) { + while (*s) s++; + *s++ = ' '; + strcpy(s, tmpbuf); + *dirv[i] = '/'; + } + system(buf); +} + +/* Make filenames more reasonable. */ + +char * +fetchname(at,strip_leading,assume_exists) +char *at; +int strip_leading; +int assume_exists; +{ + char *s; + char *name; + Reg1 char *t; + char tmpbuf[200]; + + if (!at) + return Nullch; + s = savestr(at); + for (t=s; isspace(*t); t++) ; + name = t; +#ifdef DEBUGGING + if (debug & 128) + say4("fetchname %s %d %d\n",name,strip_leading,assume_exists); +#endif + if (strnEQ(name, "/dev/null", 9)) /* so files can be created by diffing */ + return Nullch; /* against /dev/null. */ + for (; *t && !isspace(*t); t++) + if (*t == '/') + if (--strip_leading >= 0) + name = t+1; + *t = '\0'; + if (name != s && *s != '/') { + name[-1] = '\0'; + if (stat(s, &filestat) && filestat.st_mode & S_IFDIR) { + name[-1] = '/'; + name=s; + } + } + name = savestr(name); + Sprintf(tmpbuf, "RCS/%s", name); + free(s); + if (stat(name, &filestat) < 0 && !assume_exists) { + Strcat(tmpbuf, RCSSUFFIX); + if (stat(tmpbuf, &filestat) < 0 && stat(tmpbuf+4, &filestat) < 0) { + Sprintf(tmpbuf, "SCCS/%s%s", SCCSPREFIX, name); + if (stat(tmpbuf, &filestat) < 0 && stat(tmpbuf+5, &filestat) < 0) { + free(name); + name = Nullch; + } + } + } + return name; +} diff --git a/usr.bin/patch/util.h b/usr.bin/patch/util.h new file mode 100644 index 00000000000..9896c63bc08 --- /dev/null +++ b/usr.bin/patch/util.h @@ -0,0 +1,74 @@ +/* $Header: util.h,v 2.0 86/09/17 15:40:06 lwall Exp $ + * + * $Log: util.h,v $ + * Revision 2.0 86/09/17 15:40:06 lwall + * Baseline for netwide release. + * + */ + +/* and for those machine that can't handle a variable argument list */ + +#ifdef CANVARARG + +#define say1 say +#define say2 say +#define say3 say +#define say4 say +#define ask1 ask +#define ask2 ask +#define ask3 ask +#define ask4 ask +#define fatal1 fatal +#define fatal2 fatal +#define fatal3 fatal +#define fatal4 fatal + +#else /* hope they allow multi-line macro actual arguments */ + +#ifdef lint + +#define say1(a) say(a, 0, 0, 0) +#define say2(a,b) say(a, (b)==(b), 0, 0) +#define say3(a,b,c) say(a, (b)==(b), (c)==(c), 0) +#define say4(a,b,c,d) say(a, (b)==(b), (c)==(c), (d)==(d)) +#define ask1(a) ask(a, 0, 0, 0) +#define ask2(a,b) ask(a, (b)==(b), 0, 0) +#define ask3(a,b,c) ask(a, (b)==(b), (c)==(c), 0) +#define ask4(a,b,c,d) ask(a, (b)==(b), (c)==(c), (d)==(d)) +#define fatal1(a) fatal(a, 0, 0, 0) +#define fatal2(a,b) fatal(a, (b)==(b), 0, 0) +#define fatal3(a,b,c) fatal(a, (b)==(b), (c)==(c), 0) +#define fatal4(a,b,c,d) fatal(a, (b)==(b), (c)==(c), (d)==(d)) + +#else /* lint */ + /* if this doesn't work, try defining CANVARARG above */ +#define say1(a) say(a, Nullch, Nullch, Nullch) +#define say2(a,b) say(a, b, Nullch, Nullch) +#define say3(a,b,c) say(a, b, c, Nullch) +#define say4 say +#define ask1(a) ask(a, Nullch, Nullch, Nullch) +#define ask2(a,b) ask(a, b, Nullch, Nullch) +#define ask3(a,b,c) ask(a, b, c, Nullch) +#define ask4 ask +#define fatal1(a) fatal(a, Nullch, Nullch, Nullch) +#define fatal2(a,b) fatal(a, b, Nullch, Nullch) +#define fatal3(a,b,c) fatal(a, b, c, Nullch) +#define fatal4 fatal + +#endif /* lint */ + +/* if neither of the above work, join all multi-line macro calls. */ +#endif + +EXT char serrbuf[BUFSIZ]; /* buffer for stderr */ + +char *fetchname(); +int move_file(); +void copy_file(); +void say(); +void fatal(); +void ask(); +char *savestr(); +void set_signals(); +void ignore_signals(); +void makedirs(); diff --git a/usr.bin/patch/version.c b/usr.bin/patch/version.c new file mode 100644 index 00000000000..17dfb81bfa4 --- /dev/null +++ b/usr.bin/patch/version.c @@ -0,0 +1,28 @@ +/* $Header: version.c,v 2.0 86/09/17 15:40:11 lwall Exp $ + * + * $Log: version.c,v $ + * Revision 2.0 86/09/17 15:40:11 lwall + * Baseline for netwide release. + * + */ + +#include "EXTERN.h" +#include "common.h" +#include "util.h" +#include "INTERN.h" +#include "patchlevel.h" +#include "version.h" + +/* Print out the version number and die. */ + +void +version() +{ + extern char rcsid[]; + +#ifdef lint + rcsid[0] = rcsid[0]; +#else + fatal3("%s\nPatch level: %d\n", rcsid, PATCHLEVEL); +#endif +} diff --git a/usr.bin/patch/version.h b/usr.bin/patch/version.h new file mode 100644 index 00000000000..08fe68d9738 --- /dev/null +++ b/usr.bin/patch/version.h @@ -0,0 +1,9 @@ +/* $Header: version.h,v 2.0 86/09/17 15:40:14 lwall Exp $ + * + * $Log: version.h,v $ + * Revision 2.0 86/09/17 15:40:14 lwall + * Baseline for netwide release. + * + */ + +void version(); diff --git a/usr.bin/pr/pr.c b/usr.bin/pr/pr.c new file mode 100644 index 00000000000..0c2b6f3a966 --- /dev/null +++ b/usr.bin/pr/pr.c @@ -0,0 +1,1807 @@ +/*- + * Copyright (c) 1991 Keith Muller. + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)pr.c 8.3 (Berkeley) 10/9/94"; +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "pr.h" +#include "extern.h" + +/* + * pr: a printing and pagination filter. If multiple input files + * are specified, each is read, formatted, and written to standard + * output. By default, input is seperated into 66-line pages, each + * with a header that includes the page number, date, time and the + * files pathname. + * + * Complies with posix P1003.2/D11 + */ + +/* + * parameter variables + */ +int pgnm; /* starting page number */ +int clcnt; /* number of columns */ +int colwd; /* column data width - multiple columns */ +int across; /* mult col flag; write across page */ +int dspace; /* double space flag */ +char inchar; /* expand input char */ +int ingap; /* expand input gap */ +int formfeed; /* use formfeed as trailer */ +char *header; /* header name instead of file name */ +char ochar; /* contract output char */ +int ogap; /* contract output gap */ +int lines; /* number of lines per page */ +int merge; /* merge multiple files in output */ +char nmchar; /* line numbering append char */ +int nmwd; /* width of line number field */ +int offst; /* number of page offset spaces */ +int nodiag; /* do not report file open errors */ +char schar; /* text column separation character */ +int sflag; /* -s option for multiple columns */ +int nohead; /* do not write head and trailer */ +int pgwd; /* page width with multiple col output */ +char *timefrmt; /* time conversion string */ + +/* + * misc globals + */ +FILE *errf; /* error message file pointer */ +int addone; /* page length is odd with double space */ +int errcnt; /* error count on file processing */ +char digs[] = "0123456789"; /* page number translation map */ + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ret_val; + + if (signal(SIGINT, SIG_IGN) != SIG_IGN) + (void)signal(SIGINT, terminate); + ret_val = setup(argc, argv); + if (!ret_val) { + /* + * select the output format based on options + */ + if (merge) + ret_val = mulfile(argc, argv); + else if (clcnt == 1) + ret_val = onecol(argc, argv); + else if (across) + ret_val = horzcol(argc, argv); + else + ret_val = vertcol(argc, argv); + } else + usage(); + flsh_errs(); + if (errcnt || ret_val) + exit(1); + return(0); +} + +/* + * onecol: print files with only one column of output. + * Line length is unlimited. + */ +int +onecol(argc, argv) + int argc; + char *argv[]; +{ + register int cnt = -1; + register int off; + register int lrgln; + register int linecnt; + register int num; + int lncnt; + int pagecnt; + int ips; + int ops; + int cps; + char *obuf; + char *lbuf; + char *nbuf; + char *hbuf; + char *ohbuf; + FILE *inf; + char *fname; + int mor; + + if (nmwd) + num = nmwd + 1; + else + num = 0; + off = num + offst; + + /* + * allocate line buffer + */ + if ((obuf = malloc((unsigned)(LBUF + off)*sizeof(char))) == NULL) { + mfail(); + return(1); + } + /* + * allocate header buffer + */ + if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL) { + mfail(); + return(1); + } + + ohbuf = hbuf + offst; + nbuf = obuf + offst; + lbuf = nbuf + num; + if (num) + nbuf[--num] = nmchar; + if (offst) { + (void)memset(obuf, (int)' ', offst); + (void)memset(hbuf, (int)' ', offst); + } + + /* + * loop by file + */ + while ((inf = nxtfile(argc, argv, &fname, ohbuf, 0)) != NULL) { + if (pgnm) { + /* + * skip to specified page + */ + if (inskip(inf, pgnm, lines)) + continue; + pagecnt = pgnm; + } else + pagecnt = 1; + lncnt = 0; + + /* + * loop by page + */ + for(;;) { + linecnt = 0; + lrgln = 0; + ops = 0; + ips = 0; + cps = 0; + + /* + * loop by line + */ + while (linecnt < lines) { + /* + * input next line + */ + if ((cnt = inln(inf,lbuf,LBUF,&cps,0,&mor)) < 0) + break; + if (!linecnt && !nohead && + prhead(hbuf, fname, pagecnt)) + return(1); + + /* + * start of new line. + */ + if (!lrgln) { + if (num) + addnum(nbuf, num, ++lncnt); + if (otln(obuf,cnt+off, &ips, &ops, mor)) + return(1); + } else if (otln(lbuf, cnt, &ips, &ops, mor)) + return(1); + + /* + * if line bigger than buffer, get more + */ + if (mor) { + lrgln = 1; + continue; + } + + /* + * whole line rcvd. reset tab proc. state + */ + ++linecnt; + lrgln = 0; + ops = 0; + ips = 0; + } + + /* + * fill to end of page + */ + if (linecnt && prtail(lines-linecnt-lrgln, lrgln)) + return(1); + + /* + * On EOF go to next file + */ + if (cnt < 0) + break; + ++pagecnt; + } + if (inf != stdin) + (void)fclose(inf); + } + if (eoptind < argc) + return(1); + return(0); +} + +/* + * vertcol: print files with more than one column of output down a page + */ +int +vertcol(argc, argv) + int argc; + char *argv[]; +{ + register char *ptbf; + register char **lstdat; + register int i; + register int j; + register int cnt = -1; + register int pln; + register int *indy; + int cvc; + int *lindy; + int lncnt; + int stp; + int pagecnt; + int col = colwd + 1; + int mxlen = pgwd + offst + 1; + int mclcnt = clcnt - 1; + struct vcol *vc; + int mvc; + int tvc; + int cw = nmwd + 1; + int fullcol; + char *buf; + char *hbuf; + char *ohbuf; + char *fname; + FILE *inf; + int ips = 0; + int cps = 0; + int ops = 0; + int mor = 0; + + /* + * allocate page buffer + */ + if ((buf = malloc((unsigned)lines*mxlen*sizeof(char))) == NULL) { + mfail(); + return(1); + } + + /* + * allocate page header + */ + if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL) { + mfail(); + return(1); + } + ohbuf = hbuf + offst; + if (offst) + (void)memset(hbuf, (int)' ', offst); + + /* + * col pointers when no headers + */ + mvc = lines * clcnt; + if ((vc = + (struct vcol *)malloc((unsigned)mvc*sizeof(struct vcol))) == NULL) { + mfail(); + return(1); + } + + /* + * pointer into page where last data per line is located + */ + if ((lstdat = (char **)malloc((unsigned)lines*sizeof(char *))) == NULL){ + mfail(); + return(1); + } + + /* + * fast index lookups to locate start of lines + */ + if ((indy = (int *)malloc((unsigned)lines*sizeof(int))) == NULL) { + mfail(); + return(1); + } + if ((lindy = (int *)malloc((unsigned)lines*sizeof(int))) == NULL) { + mfail(); + return(1); + } + + if (nmwd) + fullcol = col + cw; + else + fullcol = col; + + /* + * initialize buffer lookup indexes and offset area + */ + for (j = 0; j < lines; ++j) { + lindy[j] = j * mxlen; + indy[j] = lindy[j] + offst; + if (offst) { + ptbf = buf + lindy[j]; + (void)memset(ptbf, (int)' ', offst); + ptbf += offst; + } else + ptbf = buf + indy[j]; + lstdat[j] = ptbf; + } + + /* + * loop by file + */ + while ((inf = nxtfile(argc, argv, &fname, ohbuf, 0)) != NULL) { + if (pgnm) { + /* + * skip to requested page + */ + if (inskip(inf, pgnm, lines)) + continue; + pagecnt = pgnm; + } else + pagecnt = 1; + lncnt = 0; + + /* + * loop by page + */ + for(;;) { + /* + * loop by column + */ + cvc = 0; + for (i = 0; i < clcnt; ++i) { + j = 0; + /* + * if last column, do not pad + */ + if (i == mclcnt) + stp = 1; + else + stp = 0; + /* + * loop by line + */ + for(;;) { + /* + * is this first column + */ + if (!i) { + ptbf = buf + indy[j]; + lstdat[j] = ptbf; + } else + ptbf = lstdat[j]; + vc[cvc].pt = ptbf; + + /* + * add number + */ + if (nmwd) { + addnum(ptbf, nmwd, ++lncnt); + ptbf += nmwd; + *ptbf++ = nmchar; + } + + /* + * input next line + */ + cnt = inln(inf,ptbf,colwd,&cps,1,&mor); + vc[cvc++].cnt = cnt; + if (cnt < 0) + break; + ptbf += cnt; + + /* + * pad all but last column on page + */ + if (!stp) { + /* + * pad to end of column + */ + if (sflag) + *ptbf++ = schar; + else if ((pln = col-cnt) > 0) { + (void)memset(ptbf, + (int)' ',pln); + ptbf += pln; + } + } + /* + * remember last char in line + */ + lstdat[j] = ptbf; + if (++j >= lines) + break; + } + if (cnt < 0) + break; + } + + /* + * when -t (no header) is specified the spec requires + * the min number of lines. The last page may not have + * balanced length columns. To fix this we must reorder + * the columns. This is a very slow technique so it is + * only used under limited conditions. Without -t, the + * balancing of text columns is unspecified. To NOT + * balance the last page, add the global variable + * nohead to the if statement below e.g. + * + * if ((cnt < 0) && nohead && cvc ...... + */ + --cvc; + + /* + * check to see if last page needs to be reordered + */ + if ((cnt < 0) && cvc && ((mvc-cvc) >= clcnt)){ + pln = cvc/clcnt; + if (cvc % clcnt) + ++pln; + + /* + * print header + */ + if (!nohead && prhead(hbuf, fname, pagecnt)) + return(1); + for (i = 0; i < pln; ++i) { + ips = 0; + ops = 0; + if (offst&& otln(buf,offst,&ips,&ops,1)) + return(1); + tvc = i; + + for (j = 0; j < clcnt; ++j) { + /* + * determine column length + */ + if (j == mclcnt) { + /* + * last column + */ + cnt = vc[tvc].cnt; + if (nmwd) + cnt += cw; + } else if (sflag) { + /* + * single ch between + */ + cnt = vc[tvc].cnt + 1; + if (nmwd) + cnt += cw; + } else + cnt = fullcol; + if (otln(vc[tvc].pt, cnt, &ips, + &ops, 1)) + return(1); + tvc += pln; + if (tvc >= cvc) + break; + } + /* + * terminate line + */ + if (otln(buf, 0, &ips, &ops, 0)) + return(1); + } + /* + * pad to end of page + */ + if (prtail((lines - pln), 0)) + return(1); + /* + * done with output, go to next file + */ + break; + } + + /* + * determine how many lines to output + */ + if (i > 0) + pln = lines; + else + pln = j; + + /* + * print header + */ + if (pln && !nohead && prhead(hbuf, fname, pagecnt)) + return(1); + + /* + * output each line + */ + for (i = 0; i < pln; ++i) { + ptbf = buf + lindy[i]; + if ((j = lstdat[i] - ptbf) <= offst) + break; + if (otln(ptbf, j, &ips, &ops, 0)) + return(1); + } + + /* + * pad to end of page + */ + if (pln && prtail((lines - pln), 0)) + return(1); + + /* + * if EOF go to next file + */ + if (cnt < 0) + break; + ++pagecnt; + } + if (inf != stdin) + (void)fclose(inf); + } + if (eoptind < argc) + return(1); + return(0); +} + +/* + * horzcol: print files with more than one column of output across a page + */ +int +horzcol(argc, argv) + int argc; + char *argv[]; +{ + register char *ptbf; + register int pln; + register int cnt = -1; + register char *lstdat; + register int col = colwd + 1; + register int j; + register int i; + int lncnt; + int pagecnt; + char *buf; + char *hbuf; + char *ohbuf; + char *fname; + FILE *inf; + int ips = 0; + int cps = 0; + int ops = 0; + int mor = 0; + + if ((buf = malloc((unsigned)(pgwd+offst+1)*sizeof(char))) == NULL) { + mfail(); + return(1); + } + + /* + * page header + */ + if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL) { + mfail(); + return(1); + } + ohbuf = hbuf + offst; + if (offst) { + (void)memset(buf, (int)' ', offst); + (void)memset(hbuf, (int)' ', offst); + } + + /* + * loop by file + */ + while ((inf = nxtfile(argc, argv, &fname, ohbuf, 0)) != NULL) { + if (pgnm) { + if (inskip(inf, pgnm, lines)) + continue; + pagecnt = pgnm; + } else + pagecnt = 1; + lncnt = 0; + + /* + * loop by page + */ + for(;;) { + /* + * loop by line + */ + for (i = 0; i < lines; ++i) { + ptbf = buf + offst; + lstdat = ptbf; + j = 0; + /* + * loop by col + */ + for(;;) { + if (nmwd) { + /* + * add number to column + */ + addnum(ptbf, nmwd, ++lncnt); + ptbf += nmwd; + *ptbf++ = nmchar; + } + /* + * input line + */ + if ((cnt = inln(inf,ptbf,colwd,&cps,1, + &mor)) < 0) + break; + ptbf += cnt; + lstdat = ptbf; + + /* + * if last line skip padding + */ + if (++j >= clcnt) + break; + + /* + * pad to end of column + */ + if (sflag) + *ptbf++ = schar; + else if ((pln = col - cnt) > 0) { + (void)memset(ptbf,(int)' ',pln); + ptbf += pln; + } + } + + /* + * determine line length + */ + if ((j = lstdat - buf) <= offst) + break; + if (!i && !nohead && + prhead(hbuf, fname, pagecnt)) + return(1); + /* + * output line + */ + if (otln(buf, j, &ips, &ops, 0)) + return(1); + } + + /* + * pad to end of page + */ + if (i && prtail(lines-i, 0)) + return(1); + + /* + * if EOF go to next file + */ + if (cnt < 0) + break; + ++pagecnt; + } + if (inf != stdin) + (void)fclose(inf); + } + if (eoptind < argc) + return(1); + return(0); +} + +/* + * mulfile: print files with more than one column of output and + * more than one file concurrently + */ +int +mulfile(argc, argv) + int argc; + char *argv[]; +{ + register char *ptbf; + register int j; + register int pln; + register int cnt; + register char *lstdat; + register int i; + FILE **fbuf; + int actf; + int lncnt; + int col; + int pagecnt; + int fproc; + char *buf; + char *hbuf; + char *ohbuf; + char *fname; + int ips = 0; + int cps = 0; + int ops = 0; + int mor = 0; + + /* + * array of FILE *, one for each operand + */ + if ((fbuf = (FILE **)malloc((unsigned)clcnt*sizeof(FILE *))) == NULL) { + mfail(); + return(1); + } + + /* + * page header + */ + if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL) { + mfail(); + return(1); + } + ohbuf = hbuf + offst; + + /* + * do not know how many columns yet. The number of operands provide an + * upper bound on the number of columns. We use the number of files + * we can open successfully to set the number of columns. The operation + * of the merge operation (-m) in relation to unsuccesful file opens + * is unspecified by posix. + */ + j = 0; + while (j < clcnt) { + if ((fbuf[j] = nxtfile(argc, argv, &fname, ohbuf, 1)) == NULL) + break; + if (pgnm && (inskip(fbuf[j], pgnm, lines))) + fbuf[j] = NULL; + ++j; + } + + /* + * if no files, exit + */ + if (!j) + return(1); + + /* + * calculate page boundries based on open file count + */ + clcnt = j; + if (nmwd) { + colwd = (pgwd - clcnt - nmwd)/clcnt; + pgwd = ((colwd + 1) * clcnt) - nmwd - 2; + } else { + colwd = (pgwd + 1 - clcnt)/clcnt; + pgwd = ((colwd + 1) * clcnt) - 1; + } + if (colwd < 1) { + (void)fprintf(errf, + "pr: page width too small for %d columns\n", clcnt); + return(1); + } + actf = clcnt; + col = colwd + 1; + + /* + * line buffer + */ + if ((buf = malloc((unsigned)(pgwd+offst+1)*sizeof(char))) == NULL) { + mfail(); + return(1); + } + if (offst) { + (void)memset(buf, (int)' ', offst); + (void)memset(hbuf, (int)' ', offst); + } + if (pgnm) + pagecnt = pgnm; + else + pagecnt = 1; + lncnt = 0; + + /* + * continue to loop while any file still has data + */ + while (actf > 0) { + /* + * loop by line + */ + for (i = 0; i < lines; ++i) { + ptbf = buf + offst; + lstdat = ptbf; + if (nmwd) { + /* + * add line number to line + */ + addnum(ptbf, nmwd, ++lncnt); + ptbf += nmwd; + *ptbf++ = nmchar; + } + j = 0; + fproc = 0; + + /* + * loop by column + */ + for (j = 0; j < clcnt; ++j) { + if (fbuf[j] == NULL) { + /* + * empty column; EOF + */ + cnt = 0; + } else if ((cnt = inln(fbuf[j], ptbf, colwd, + &cps, 1, &mor)) < 0) { + /* + * EOF hit; no data + */ + if (fbuf[j] != stdin) + (void)fclose(fbuf[j]); + fbuf[j] = NULL; + --actf; + cnt = 0; + } else { + /* + * process file data + */ + ptbf += cnt; + lstdat = ptbf; + fproc++; + } + + /* + * if last ACTIVE column, done with line + */ + if (fproc >= actf) + break; + + /* + * pad to end of column + */ + if (sflag) { + *ptbf++ = schar; + } else if ((pln = col - cnt) > 0) { + (void)memset(ptbf, (int)' ', pln); + ptbf += pln; + } + } + + /* + * calculate data in line + */ + if ((j = lstdat - buf) <= offst) + break; + + if (!i && !nohead && prhead(hbuf, fname, pagecnt)) + return(1); + + /* + * output line + */ + if (otln(buf, j, &ips, &ops, 0)) + return(1); + + /* + * if no more active files, done + */ + if (actf <= 0) { + ++i; + break; + } + } + + /* + * pad to end of page + */ + if (i && prtail(lines-i, 0)) + return(1); + ++pagecnt; + } + if (eoptind < argc) + return(1); + return(0); +} + +/* + * inln(): input a line of data (unlimited length lines supported) + * Input is optionally expanded to spaces + * + * inf: file + * buf: buffer + * lim: buffer length + * cps: column positon 1st char in buffer (large line support) + * trnc: throw away data more than lim up to \n + * mor: set if more data in line (not truncated) + */ +int +inln(inf, buf, lim, cps, trnc, mor) + FILE *inf; + char *buf; + register int lim; + int *cps; + int trnc; + int *mor; +{ + register int col; + register int gap = ingap; + register int ch = EOF; + register char *ptbuf; + register int chk = (int)inchar; + + ptbuf = buf; + + if (gap) { + /* + * expanding input option + */ + while ((--lim >= 0) && ((ch = getc(inf)) != EOF)) { + /* + * is this the input "tab" char + */ + if (ch == chk) { + /* + * expand to number of spaces + */ + col = (ptbuf - buf) + *cps; + col = gap - (col % gap); + + /* + * if more than this line, push back + */ + if ((col > lim) && (ungetc(ch, inf) == EOF)) + return(1); + + /* + * expand to spaces + */ + while ((--col >= 0) && (--lim >= 0)) + *ptbuf++ = ' '; + continue; + } + if (ch == '\n') + break; + *ptbuf++ = ch; + } + } else { + /* + * no expansion + */ + while ((--lim >= 0) && ((ch = getc(inf)) != EOF)) { + if (ch == '\n') + break; + *ptbuf++ = ch; + } + } + col = ptbuf - buf; + if (ch == EOF) { + *mor = 0; + *cps = 0; + if (!col) + return(-1); + return(col); + } + if (ch == '\n') { + /* + * entire line processed + */ + *mor = 0; + *cps = 0; + return(col); + } + + /* + * line was larger than limit + */ + if (trnc) { + /* + * throw away rest of line + */ + while ((ch = getc(inf)) != EOF) { + if (ch == '\n') + break; + } + *cps = 0; + *mor = 0; + } else { + /* + * save column offset if not truncated + */ + *cps += col; + *mor = 1; + } + + return(col); +} + +/* + * otln(): output a line of data. (Supports unlimited length lines) + * output is optionally contracted to tabs + * + * buf: output buffer with data + * cnt: number of chars of valid data in buf + * svips: buffer input column position (for large lines) + * svops: buffer output column position (for large lines) + * mor: output line not complete in this buf; more data to come. + * 1 is more, 0 is complete, -1 is no \n's + */ +int +otln(buf, cnt, svips, svops, mor) + register char *buf; + int cnt; + int *svops; + int *svips; + int mor; +{ + register int ops; /* last col output */ + register int ips; /* last col in buf examined */ + register int gap = ogap; + register int tbps; + register char *endbuf; + + if (ogap) { + /* + * contracting on output + */ + endbuf = buf + cnt; + ops = *svops; + ips = *svips; + while (buf < endbuf) { + /* + * count number of spaces and ochar in buffer + */ + if (*buf == ' ') { + ++ips; + ++buf; + continue; + } + + /* + * simulate ochar processing + */ + if (*buf == ochar) { + ips += gap - (ips % gap); + ++buf; + continue; + } + + /* + * got a non space char; contract out spaces + */ + while (ops < ips) { + /* + * use as many ochar as will fit + */ + if ((tbps = ops + gap - (ops % gap)) > ips) + break; + if (putchar(ochar) == EOF) { + pfail(); + return(1); + } + ops = tbps; + } + + while (ops < ips) { + /* + * finish off with spaces + */ + if (putchar(' ') == EOF) { + pfail(); + return(1); + } + ++ops; + } + + /* + * output non space char + */ + if (putchar(*buf++) == EOF) { + pfail(); + return(1); + } + ++ips; + ++ops; + } + + if (mor > 0) { + /* + * if incomplete line, save position counts + */ + *svops = ops; + *svips = ips; + return(0); + } + + if (mor < 0) { + while (ops < ips) { + /* + * use as many ochar as will fit + */ + if ((tbps = ops + gap - (ops % gap)) > ips) + break; + if (putchar(ochar) == EOF) { + pfail(); + return(1); + } + ops = tbps; + } + while (ops < ips) { + /* + * finish off with spaces + */ + if (putchar(' ') == EOF) { + pfail(); + return(1); + } + ++ops; + } + return(0); + } + } else { + /* + * output is not contracted + */ + if (cnt && (fwrite(buf, sizeof(char), cnt, stdout) <= 0)) { + pfail(); + return(1); + } + if (mor != 0) + return(0); + } + + /* + * process line end and double space as required + */ + if ((putchar('\n') == EOF) || (dspace && (putchar('\n') == EOF))) { + pfail(); + return(1); + } + return(0); +} + +/* + * inskip(): skip over pgcnt pages with lncnt lines per page + * file is closed at EOF (if not stdin). + * + * inf FILE * to read from + * pgcnt number of pages to skip + * lncnt number of lines per page + */ +int +inskip(inf, pgcnt, lncnt) + FILE *inf; + register int pgcnt; + register int lncnt; +{ + register int c; + register int cnt; + + while(--pgcnt > 0) { + cnt = lncnt; + while ((c = getc(inf)) != EOF) { + if ((c == '\n') && (--cnt == 0)) + break; + } + if (c == EOF) { + if (inf != stdin) + (void)fclose(inf); + return(1); + } + } + return(0); +} + +/* + * nxtfile: returns a FILE * to next file in arg list and sets the + * time field for this file (or current date). + * + * buf array to store proper date for the header. + * dt if set skips the date processing (used with -m) + */ +FILE * +nxtfile(argc, argv, fname, buf, dt) + int argc; + char **argv; + char **fname; + char *buf; + int dt; +{ + FILE *inf = NULL; + struct timeval tv; + struct timezone tz; + struct tm *timeptr = NULL; + struct stat statbuf; + static int twice = -1; + + ++twice; + if (eoptind >= argc) { + /* + * no file listed; default, use standard input + */ + if (twice) + return(NULL); + clearerr(stdin); + inf = stdin; + if (header != NULL) + *fname = header; + else + *fname = FNAME; + if (nohead) + return(inf); + if (gettimeofday(&tv, &tz) < 0) { + ++errcnt; + (void)fprintf(errf, "pr: cannot get time of day, %s\n", + strerror(errno)); + eoptind = argc - 1; + return(NULL); + } + timeptr = localtime(&(tv.tv_sec)); + } + for (; eoptind < argc; ++eoptind) { + if (strcmp(argv[eoptind], "-") == 0) { + /* + * process a "-" for filename + */ + clearerr(stdin); + inf = stdin; + if (header != NULL) + *fname = header; + else + *fname = FNAME; + ++eoptind; + if (nohead || (dt && twice)) + return(inf); + if (gettimeofday(&tv, &tz) < 0) { + ++errcnt; + (void)fprintf(errf, + "pr: cannot get time of day, %s\n", + strerror(errno)); + return(NULL); + } + timeptr = localtime(&(tv.tv_sec)); + } else { + /* + * normal file processing + */ + if ((inf = fopen(argv[eoptind], "r")) == NULL) { + ++errcnt; + if (nodiag) + continue; + (void)fprintf(errf, "pr: Cannot open %s, %s\n", + argv[eoptind], strerror(errno)); + continue; + } + if (header != NULL) + *fname = header; + else if (dt) + *fname = FNAME; + else + *fname = argv[eoptind]; + ++eoptind; + if (nohead || (dt && twice)) + return(inf); + + if (dt) { + if (gettimeofday(&tv, &tz) < 0) { + ++errcnt; + (void)fprintf(errf, + "pr: cannot get time of day, %s\n", + strerror(errno)); + return(NULL); + } + timeptr = localtime(&(tv.tv_sec)); + } else { + if (fstat(fileno(inf), &statbuf) < 0) { + ++errcnt; + (void)fclose(inf); + (void)fprintf(errf, + "pr: Cannot stat %s, %s\n", + argv[eoptind], strerror(errno)); + return(NULL); + } + timeptr = localtime(&(statbuf.st_mtime)); + } + } + break; + } + if (inf == NULL) + return(NULL); + + /* + * set up time field used in header + */ + if (strftime(buf, HDBUF, timefrmt, timeptr) <= 0) { + ++errcnt; + if (inf != stdin) + (void)fclose(inf); + (void)fputs("pr: time conversion failed\n", errf); + return(NULL); + } + return(inf); +} + +/* + * addnum(): adds the line number to the column + * Truncates from the front or pads with spaces as required. + * Numbers are right justified. + * + * buf buffer to store the number + * wdth width of buffer to fill + * line line number + * + * NOTE: numbers occupy part of the column. The posix + * spec does not specify if -i processing should or should not + * occur on number padding. The spec does say it occupies + * part of the column. The usage of addnum currently treats + * numbers as part of the column so spaces may be replaced. + */ +void +addnum(buf, wdth, line) + register char *buf; + register int wdth; + register int line; +{ + register char *pt = buf + wdth; + + do { + *--pt = digs[line % 10]; + line /= 10; + } while (line && (pt > buf)); + + /* + * pad with space as required + */ + while (pt > buf) + *--pt = ' '; +} + +/* + * prhead(): prints the top of page header + * + * buf buffer with time field (and offset) + * cnt number of chars in buf + * fname fname field for header + * pagcnt page number + */ +int +prhead(buf, fname, pagcnt) + char *buf; + char *fname; + int pagcnt; +{ + int ips = 0; + int ops = 0; + + if ((putchar('\n') == EOF) || (putchar('\n') == EOF)) { + pfail(); + return(1); + } + /* + * posix is not clear if the header is subject to line length + * restrictions. The specification for header line format + * in the spec clearly does not limit length. No pr currently + * restricts header length. However if we need to truncate in + * an reasonable way, adjust the length of the printf by + * changing HDFMT to allow a length max as an arguement printf. + * buf (which contains the offset spaces and time field could + * also be trimmed + * + * note only the offset (if any) is processed for tab expansion + */ + if (offst && otln(buf, offst, &ips, &ops, -1)) + return(1); + (void)printf(HDFMT,buf+offst, fname, pagcnt); + return(0); +} + +/* + * prtail(): pad page with empty lines (if required) and print page trailer + * if requested + * + * cnt number of lines of padding needed + * incomp was a '\n' missing from last line output + */ +int +prtail(cnt, incomp) + register int cnt; + int incomp; +{ + if (nohead) { + /* + * only pad with no headers when incomplete last line + */ + if (!incomp) + return(0); + if ((dspace && (putchar('\n') == EOF)) || + (putchar('\n') == EOF)) { + pfail(); + return(1); + } + return(0); + } + + /* + * if double space output two \n + */ + if (dspace) + cnt *= 2; + + /* + * if an odd number of lines per page, add an extra \n + */ + if (addone) + ++cnt; + + /* + * pad page + */ + if (formfeed) { + if ((incomp && (putchar('\n') == EOF)) || + (putchar('\f') == EOF)) { + pfail(); + return(1); + } + return(0); + } + cnt += TAILLEN; + while (--cnt >= 0) { + if (putchar('\n') == EOF) { + pfail(); + return(1); + } + } + return(0); +} + +/* + * terminate(): when a SIGINT is recvd + */ +void +terminate(which_sig) + int which_sig; +{ + flsh_errs(); + exit(1); +} + + +/* + * flsh_errs(): output saved up diagnostic messages after all normal + * processing has completed + */ +void +flsh_errs() +{ + char buf[BUFSIZ]; + + (void)fflush(stdout); + (void)fflush(errf); + if (errf == stderr) + return; + rewind(errf); + while (fgets(buf, BUFSIZ, errf) != NULL) + (void)fputs(buf, stderr); +} + +void +mfail() +{ + (void)fputs("pr: memory allocation failed\n", errf); +} + +void +pfail() +{ + (void)fprintf(errf, "pr: write failure, %s\n", strerror(errno)); +} + +void +usage() +{ + (void)fputs( + "usage: pr [+page] [-col] [-adFmrt] [-e[ch][gap]] [-h header]\n", + errf); + (void)fputs( + " [-i[ch][gap]] [-l line] [-n[ch][width]] [-o offset]\n", + errf); + (void)fputs( + " [-s[ch]] [-w width] [-] [file ...]\n", errf); +} + +/* + * setup: Validate command args, initialize and perform sanity + * checks on options + */ +int +setup(argc, argv) + register int argc; + register char **argv; +{ + register int c; + int eflag = 0; + int iflag = 0; + int wflag = 0; + int cflag = 0; + + if (isatty(fileno(stdout))) { + /* + * defer diagnostics until processing is done + */ + if ((errf = tmpfile()) == NULL) { + (void)fputs("Cannot defer diagnostic messages\n",stderr); + return(1); + } + } else + errf = stderr; + while ((c = egetopt(argc, argv, "#adFmrte?h:i?l:n?o:s?w:")) != EOF) { + switch (c) { + case '+': + if ((pgnm = atoi(eoptarg)) < 1) { + (void)fputs("pr: +page number must be 1 or more\n", + errf); + return(1); + } + break; + case '-': + if ((clcnt = atoi(eoptarg)) < 1) { + (void)fputs("pr: -columns must be 1 or more\n", + errf); + return(1); + } + if (clcnt > 1) + ++cflag; + break; + case 'a': + ++across; + break; + case 'd': + ++dspace; + break; + case 'e': + ++eflag; + if ((eoptarg != NULL) && !isdigit(*eoptarg)) + inchar = *eoptarg++; + else + inchar = INCHAR; + if ((eoptarg != NULL) && isdigit(*eoptarg)) { + if ((ingap = atoi(eoptarg)) < 0) { + (void)fputs( + "pr: -e gap must be 0 or more\n", errf); + return(1); + } + if (ingap == 0) + ingap = INGAP; + } else if ((eoptarg != NULL) && (*eoptarg != '\0')) { + (void)fprintf(errf, + "pr: invalid value for -e %s\n", eoptarg); + return(1); + } else + ingap = INGAP; + break; + case 'F': + ++formfeed; + break; + case 'h': + header = eoptarg; + break; + case 'i': + ++iflag; + if ((eoptarg != NULL) && !isdigit(*eoptarg)) + ochar = *eoptarg++; + else + ochar = OCHAR; + if ((eoptarg != NULL) && isdigit(*eoptarg)) { + if ((ogap = atoi(eoptarg)) < 0) { + (void)fputs( + "pr: -i gap must be 0 or more\n", errf); + return(1); + } + if (ogap == 0) + ogap = OGAP; + } else if ((eoptarg != NULL) && (*eoptarg != '\0')) { + (void)fprintf(errf, + "pr: invalid value for -i %s\n", eoptarg); + return(1); + } else + ogap = OGAP; + break; + case 'l': + if (!isdigit(*eoptarg) || ((lines=atoi(eoptarg)) < 1)) { + (void)fputs( + "pr: Number of lines must be 1 or more\n",errf); + return(1); + } + break; + case 'm': + ++merge; + break; + case 'n': + if ((eoptarg != NULL) && !isdigit(*eoptarg)) + nmchar = *eoptarg++; + else + nmchar = NMCHAR; + if ((eoptarg != NULL) && isdigit(*eoptarg)) { + if ((nmwd = atoi(eoptarg)) < 1) { + (void)fputs( + "pr: -n width must be 1 or more\n",errf); + return(1); + } + } else if ((eoptarg != NULL) && (*eoptarg != '\0')) { + (void)fprintf(errf, + "pr: invalid value for -n %s\n", eoptarg); + return(1); + } else + nmwd = NMWD; + break; + case 'o': + if (!isdigit(*eoptarg) || ((offst = atoi(eoptarg))< 1)){ + (void)fputs("pr: -o offset must be 1 or more\n", + errf); + return(1); + } + break; + case 'r': + ++nodiag; + break; + case 's': + ++sflag; + if (eoptarg == NULL) + schar = SCHAR; + else + schar = *eoptarg++; + if (*eoptarg != '\0') { + (void)fprintf(errf, + "pr: invalid value for -s %s\n", eoptarg); + return(1); + } + break; + case 't': + ++nohead; + break; + case 'w': + ++wflag; + if (!isdigit(*eoptarg) || ((pgwd = atoi(eoptarg)) < 1)){ + (void)fputs( + "pr: -w width must be 1 or more \n",errf); + return(1); + } + break; + case '?': + default: + return(1); + } + } + + /* + * default and sanity checks + */ + if (!clcnt) { + if (merge) { + if ((clcnt = argc - eoptind) <= 1) { + clcnt = CLCNT; + merge = 0; + } + } else + clcnt = CLCNT; + } + if (across) { + if (clcnt == 1) { + (void)fputs("pr: -a flag requires multiple columns\n", + errf); + return(1); + } + if (merge) { + (void)fputs("pr: -m cannot be used with -a\n", errf); + return(1); + } + } + if (!wflag) { + if (sflag) + pgwd = SPGWD; + else + pgwd = PGWD; + } + if (cflag || merge) { + if (!eflag) { + inchar = INCHAR; + ingap = INGAP; + } + if (!iflag) { + ochar = OCHAR; + ogap = OGAP; + } + } + if (cflag) { + if (merge) { + (void)fputs( + "pr: -m cannot be used with multiple columns\n", errf); + return(1); + } + if (nmwd) { + colwd = (pgwd + 1 - (clcnt * (nmwd + 2)))/clcnt; + pgwd = ((colwd + nmwd + 2) * clcnt) - 1; + } else { + colwd = (pgwd + 1 - clcnt)/clcnt; + pgwd = ((colwd + 1) * clcnt) - 1; + } + if (colwd < 1) { + (void)fprintf(errf, + "pr: page width is too small for %d columns\n",clcnt); + return(1); + } + } + if (!lines) + lines = LINES; + + /* + * make sure long enough for headers. if not disable + */ + if (lines <= HEADLEN + TAILLEN) + ++nohead; + else if (!nohead) + lines -= HEADLEN + TAILLEN; + + /* + * adjust for double space on odd length pages + */ + if (dspace) { + if (lines == 1) + dspace = 0; + else { + if (lines & 1) + ++addone; + lines /= 2; + } + } + + if ((timefrmt = getenv("LC_TIME")) == NULL) + timefrmt = TIMEFMT; + return(0); +} diff --git a/usr.bin/printf/printf.c b/usr.bin/printf/printf.c new file mode 100644 index 00000000000..82ffbdafb7b --- /dev/null +++ b/usr.bin/printf/printf.c @@ -0,0 +1,410 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if !defined(BUILTIN) && !defined(SHELL) +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ +#endif + +#ifndef lint +static char sccsid[] = "@(#)printf.c 8.2 (Berkeley) 3/22/95"; +#endif /* not lint */ + +#include + +#include +#include +#include +#ifdef SHELL +#define EOF -1 +#else +#include +#endif +#include +#include + +/* + * XXX + * This *has* to go away. TK. + */ +#ifdef SHELL +#define main printfcmd +#define warnx(a, b, c) { \ + char buf[64]; \ + (void)sprintf(buf, sizeof(buf), a, b, c); \ + error(buf); \ +} +#include "../../bin/sh/bltin/bltin.h" +#endif + +#define PF(f, func) { \ + if (fieldwidth) \ + if (precision) \ + (void)printf(f, fieldwidth, precision, func); \ + else \ + (void)printf(f, fieldwidth, func); \ + else if (precision) \ + (void)printf(f, precision, func); \ + else \ + (void)printf(f, func); \ +} + +static int asciicode __P((void)); +static void escape __P((char *)); +static int getchr __P((void)); +static double getdouble __P((void)); +static int getint __P((int *)); +static int getlong __P((long *)); +static char *getstr __P((void)); +static char *mklong __P((char *, int)); +static void usage __P((void)); + +static char **gargv; + +int +#ifdef BUILTIN +progprintf(argc, argv) +#else +main(argc, argv) +#endif + int argc; + char *argv[]; +{ + extern int optind; + static char *skip1, *skip2; + int ch, end, fieldwidth, precision; + char convch, nextch, *format, *fmt, *start; + + while ((ch = getopt(argc, argv, "")) != EOF) + switch (ch) { + case '?': + default: + usage(); + return (1); + } + argc -= optind; + argv += optind; + + if (argc < 1) { + usage(); + return (1); + } + + /* + * Basic algorithm is to scan the format string for conversion + * specifications -- once one is found, find out if the field + * width or precision is a '*'; if it is, gather up value. Note, + * format strings are reused as necessary to use up the provided + * arguments, arguments of zero/null string are provided to use + * up the format string. + */ + skip1 = "#-+ 0"; + skip2 = "*0123456789"; + + escape(fmt = format = *argv); /* backslash interpretation */ + gargv = ++argv; + for (;;) { + end = 0; + /* find next format specification */ +next: for (start = fmt;; ++fmt) { + if (!*fmt) { + /* avoid infinite loop */ + if (end == 1) { + warnx("missing format character", + NULL, NULL); + return (1); + } + end = 1; + if (fmt > start) + (void)printf("%s", start); + if (!*gargv) + return (0); + fmt = format; + goto next; + } + /* %% prints a % */ + if (*fmt == '%') { + if (*++fmt != '%') + break; + *fmt++ = '\0'; + (void)printf("%s", start); + goto next; + } + } + + /* skip to field width */ + for (; strchr(skip1, *fmt); ++fmt); + if (*fmt == '*') { + if (getint(&fieldwidth)) + return (1); + } else + fieldwidth = 0; + + /* skip to possible '.', get following precision */ + for (; strchr(skip2, *fmt); ++fmt); + if (*fmt == '.') + ++fmt; + if (*fmt == '*') { + if (getint(&precision)) + return (1); + } else + precision = 0; + + /* skip to conversion char */ + for (; strchr(skip2, *fmt); ++fmt); + if (!*fmt) { + warnx("missing format character", NULL, NULL); + return (1); + } + + convch = *fmt; + nextch = *++fmt; + *fmt = '\0'; + switch(convch) { + case 'c': { + char p; + + p = getchr(); + PF(start, p); + break; + } + case 's': { + char *p; + + p = getstr(); + PF(start, p); + break; + } + case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': { + long p; + char *f; + + if ((f = mklong(start, convch)) == NULL) + return (1); + if (getlong(&p)) + return (1); + PF(f, p); + break; + } + case 'e': case 'E': case 'f': case 'g': case 'G': { + double p; + + p = getdouble(); + PF(start, p); + break; + } + default: + warnx("illegal format character", NULL, NULL); + return (1); + } + *fmt = nextch; + } + /* NOTREACHED */ +} + +static char * +mklong(str, ch) + char *str; + int ch; +{ + static char copy[64]; + int len; + + len = strlen(str) + 2; + memmove(copy, str, len - 3); + copy[len - 3] = 'l'; + copy[len - 2] = ch; + copy[len - 1] = '\0'; + return (copy); +} + +static void +escape(fmt) + register char *fmt; +{ + register char *store; + register int value, c; + + for (store = fmt; (c = *fmt) != '\0'; ++fmt, ++store) { + if (c != '\\') { + *store = c; + continue; + } + switch (*++fmt) { + case '\0': /* EOS, user error */ + *store = '\\'; + *++store = '\0'; + return; + case '\\': /* backslash */ + case '\'': /* single quote */ + *store = *fmt; + break; + case 'a': /* bell/alert */ + *store = '\7'; + break; + case 'b': /* backspace */ + *store = '\b'; + break; + case 'f': /* form-feed */ + *store = '\f'; + break; + case 'n': /* newline */ + *store = '\n'; + break; + case 'r': /* carriage-return */ + *store = '\r'; + break; + case 't': /* horizontal tab */ + *store = '\t'; + break; + case 'v': /* vertical tab */ + *store = '\13'; + break; + /* octal constant */ + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + for (c = 3, value = 0; + c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) { + value <<= 3; + value += *fmt - '0'; + } + --fmt; + *store = value; + break; + default: + *store = *fmt; + break; + } + } + *store = '\0'; +} + +static int +getchr() +{ + if (!*gargv) + return ('\0'); + return ((int)**gargv++); +} + +static char * +getstr() +{ + if (!*gargv) + return (""); + return (*gargv++); +} + +static char *Number = "+-.0123456789"; +static int +getint(ip) + int *ip; +{ + long val; + + if (getlong(&val)) + return (1); + if (val > INT_MAX) { + warnx("%s: %s", *gargv, strerror(ERANGE)); + return (1); + } + *ip = val; + return (0); +} + +static int +getlong(lp) + long *lp; +{ + long val; + char *ep; + + if (!*gargv) { + *lp = 0; + return (0); + } + if (strchr(Number, **gargv)) { + errno = 0; + val = strtol(*gargv, &ep, 0); + if (*ep != '\0') { + warnx("%s: illegal number", *gargv, NULL); + return (1); + } + if (errno == ERANGE) + if (val == LONG_MAX) { + warnx("%s: %s", *gargv, strerror(ERANGE)); + return (1); + } + if (val == LONG_MIN) { + warnx("%s: %s", *gargv, strerror(ERANGE)); + return (1); + } + + *lp = val; + ++gargv; + return (0); + } + *lp = (long)asciicode(); + return (0); +} + +static double +getdouble() +{ + if (!*gargv) + return ((double)0); + if (strchr(Number, **gargv)) + return (atof(*gargv++)); + return ((double)asciicode()); +} + +static int +asciicode() +{ + register int ch; + + ch = **gargv; + if (ch == '\'' || ch == '"') + ch = (*gargv)[1]; + ++gargv; + return (ch); +} + +static void +usage() +{ + (void)fprintf(stderr, "usage: printf format [arg ...]\n"); +} diff --git a/usr.bin/quota/quota.c b/usr.bin/quota/quota.c new file mode 100644 index 00000000000..f7fadc22cb2 --- /dev/null +++ b/usr.bin/quota/quota.c @@ -0,0 +1,516 @@ +/* + * 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 + * Robert Elz at The University of Melbourne. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1980, 1990, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)quota.c 8.4 (Berkeley) 4/28/95"; +#endif /* not lint */ + +/* + * Disk quota reporting program. + */ +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +char *qfname = QUOTAFILENAME; +char *qfextension[] = INITQFNAMES; + +struct quotause { + struct quotause *next; + long flags; + struct dqblk dqblk; + char fsname[MAXPATHLEN + 1]; +} *getprivs(); +#define FOUND 0x01 + +int qflag; +int vflag; + +main(argc, argv) + char *argv[]; +{ + int ngroups; + gid_t gidset[NGROUPS]; + int i, gflag = 0, uflag = 0; + char ch; + extern char *optarg; + extern int optind, errno; + + if (quotactl("/", 0, 0, (caddr_t)0) < 0 && errno == EOPNOTSUPP) { + fprintf(stderr, "There are no quotas on this system\n"); + exit(0); + } + while ((ch = getopt(argc, argv, "ugvq")) != EOF) { + switch(ch) { + case 'g': + gflag++; + break; + case 'u': + uflag++; + break; + case 'v': + vflag++; + break; + case 'q': + qflag++; + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + if (!uflag && !gflag) + uflag++; + if (argc == 0) { + if (uflag) + showuid(getuid()); + if (gflag) { + ngroups = getgroups(NGROUPS, gidset); + if (ngroups < 0) { + perror("quota: getgroups"); + exit(1); + } + for (i = 1; i < ngroups; i++) + showgid(gidset[i]); + } + exit(0); + } + if (uflag && gflag) + usage(); + if (uflag) { + for (; argc > 0; argc--, argv++) { + if (alldigits(*argv)) + showuid(atoi(*argv)); + else + showusrname(*argv); + } + exit(0); + } + if (gflag) { + for (; argc > 0; argc--, argv++) { + if (alldigits(*argv)) + showgid(atoi(*argv)); + else + showgrpname(*argv); + } + exit(0); + } +} + +usage() +{ + + fprintf(stderr, "%s\n%s\n%s\n", + "Usage: quota [-guqv]", + "\tquota [-qv] -u username ...", + "\tquota [-qv] -g groupname ..."); + exit(1); +} + +/* + * Print out quotas for a specified user identifier. + */ +showuid(uid) + u_long uid; +{ + struct passwd *pwd = getpwuid(uid); + u_long myuid; + char *name; + + if (pwd == NULL) + name = "(no account)"; + else + name = pwd->pw_name; + myuid = getuid(); + if (uid != myuid && myuid != 0) { + printf("quota: %s (uid %d): permission denied\n", name, uid); + return; + } + showquotas(USRQUOTA, uid, name); +} + +/* + * Print out quotas for a specifed user name. + */ +showusrname(name) + char *name; +{ + struct passwd *pwd = getpwnam(name); + u_long myuid; + + if (pwd == NULL) { + fprintf(stderr, "quota: %s: unknown user\n", name); + return; + } + myuid = getuid(); + if (pwd->pw_uid != myuid && myuid != 0) { + fprintf(stderr, "quota: %s (uid %d): permission denied\n", + name, pwd->pw_uid); + return; + } + showquotas(USRQUOTA, pwd->pw_uid, name); +} + +/* + * Print out quotas for a specified group identifier. + */ +showgid(gid) + u_long gid; +{ + struct group *grp = getgrgid(gid); + int ngroups; + gid_t gidset[NGROUPS]; + register int i; + char *name; + + if (grp == NULL) + name = "(no entry)"; + else + name = grp->gr_name; + ngroups = getgroups(NGROUPS, gidset); + if (ngroups < 0) { + perror("quota: getgroups"); + return; + } + for (i = 1; i < ngroups; i++) + if (gid == gidset[i]) + break; + if (i >= ngroups && getuid() != 0) { + fprintf(stderr, "quota: %s (gid %d): permission denied\n", + name, gid); + return; + } + showquotas(GRPQUOTA, gid, name); +} + +/* + * Print out quotas for a specifed group name. + */ +showgrpname(name) + char *name; +{ + struct group *grp = getgrnam(name); + int ngroups; + gid_t gidset[NGROUPS]; + register int i; + + if (grp == NULL) { + fprintf(stderr, "quota: %s: unknown group\n", name); + return; + } + ngroups = getgroups(NGROUPS, gidset); + if (ngroups < 0) { + perror("quota: getgroups"); + return; + } + for (i = 1; i < ngroups; i++) + if (grp->gr_gid == gidset[i]) + break; + if (i >= ngroups && getuid() != 0) { + fprintf(stderr, "quota: %s (gid %d): permission denied\n", + name, grp->gr_gid); + return; + } + showquotas(GRPQUOTA, grp->gr_gid, name); +} + +showquotas(type, id, name) + int type; + u_long id; + char *name; +{ + register struct quotause *qup; + struct quotause *quplist, *getprivs(); + char *msgi, *msgb, *timeprt(); + int myuid, fd, lines = 0; + static int first; + static time_t now; + + if (now == 0) + time(&now); + quplist = getprivs(id, type); + for (qup = quplist; qup; qup = qup->next) { + if (!vflag && + qup->dqblk.dqb_isoftlimit == 0 && + qup->dqblk.dqb_ihardlimit == 0 && + qup->dqblk.dqb_bsoftlimit == 0 && + qup->dqblk.dqb_bhardlimit == 0) + continue; + msgi = (char *)0; + if (qup->dqblk.dqb_ihardlimit && + qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_ihardlimit) + msgi = "File limit reached on"; + else if (qup->dqblk.dqb_isoftlimit && + qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_isoftlimit) + if (qup->dqblk.dqb_itime > now) + msgi = "In file grace period on"; + else + msgi = "Over file quota on"; + msgb = (char *)0; + if (qup->dqblk.dqb_bhardlimit && + qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bhardlimit) + msgb = "Block limit reached on"; + else if (qup->dqblk.dqb_bsoftlimit && + qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bsoftlimit) + if (qup->dqblk.dqb_btime > now) + msgb = "In block grace period on"; + else + msgb = "Over block quota on"; + if (qflag) { + if ((msgi != (char *)0 || msgb != (char *)0) && + lines++ == 0) + heading(type, id, name, ""); + if (msgi != (char *)0) + printf("\t%s %s\n", msgi, qup->fsname); + if (msgb != (char *)0) + printf("\t%s %s\n", msgb, qup->fsname); + continue; + } + if (vflag || + qup->dqblk.dqb_curblocks || + qup->dqblk.dqb_curinodes) { + if (lines++ == 0) + heading(type, id, name, ""); + printf("%15s%8d%c%7d%8d%8s" + , qup->fsname + , dbtob(qup->dqblk.dqb_curblocks) / 1024 + , (msgb == (char *)0) ? ' ' : '*' + , dbtob(qup->dqblk.dqb_bsoftlimit) / 1024 + , dbtob(qup->dqblk.dqb_bhardlimit) / 1024 + , (msgb == (char *)0) ? "" + : timeprt(qup->dqblk.dqb_btime)); + printf("%8d%c%7d%8d%8s\n" + , qup->dqblk.dqb_curinodes + , (msgi == (char *)0) ? ' ' : '*' + , qup->dqblk.dqb_isoftlimit + , qup->dqblk.dqb_ihardlimit + , (msgi == (char *)0) ? "" + : timeprt(qup->dqblk.dqb_itime) + ); + continue; + } + } + if (!qflag && lines == 0) + heading(type, id, name, "none"); +} + +heading(type, id, name, tag) + int type; + u_long id; + char *name, *tag; +{ + + printf("Disk quotas for %s %s (%cid %d): %s\n", qfextension[type], + name, *qfextension[type], id, tag); + if (!qflag && tag[0] == '\0') { + printf("%15s%8s %7s%8s%8s%8s %7s%8s%8s\n" + , "Filesystem" + , "blocks" + , "quota" + , "limit" + , "grace" + , "files" + , "quota" + , "limit" + , "grace" + ); + } +} + +/* + * Calculate the grace period and return a printable string for it. + */ +char * +timeprt(seconds) + time_t seconds; +{ + time_t hours, minutes; + static char buf[20]; + static time_t now; + + if (now == 0) + time(&now); + if (now > seconds) + return ("none"); + seconds -= now; + minutes = (seconds + 30) / 60; + hours = (minutes + 30) / 60; + if (hours >= 36) { + sprintf(buf, "%ddays", (hours + 12) / 24); + return (buf); + } + if (minutes >= 60) { + sprintf(buf, "%2d:%d", minutes / 60, minutes % 60); + return (buf); + } + sprintf(buf, "%2d", minutes); + return (buf); +} + +/* + * Collect the requested quota information. + */ +struct quotause * +getprivs(id, quotatype) + register long id; + int quotatype; +{ + register struct fstab *fs; + register struct quotause *qup, *quptail; + struct quotause *quphead; + char *qfpathname; + int qcmd, fd; + + setfsent(); + quphead = (struct quotause *)0; + qcmd = QCMD(Q_GETQUOTA, quotatype); + while (fs = getfsent()) { + if (strcmp(fs->fs_vfstype, "ufs")) + continue; + if (!hasquota(fs, quotatype, &qfpathname)) + continue; + if ((qup = (struct quotause *)malloc(sizeof *qup)) == NULL) { + fprintf(stderr, "quota: out of memory\n"); + exit(2); + } + if (quotactl(fs->fs_file, qcmd, id, &qup->dqblk) != 0) { + if ((fd = open(qfpathname, O_RDONLY)) < 0) { + perror(qfpathname); + free(qup); + continue; + } + lseek(fd, (off_t)(id * sizeof(struct dqblk)), L_SET); + switch (read(fd, &qup->dqblk, sizeof(struct dqblk))) { + case 0: /* EOF */ + /* + * Convert implicit 0 quota (EOF) + * into an explicit one (zero'ed dqblk) + */ + bzero((caddr_t)&qup->dqblk, + sizeof(struct dqblk)); + break; + + case sizeof(struct dqblk): /* OK */ + break; + + default: /* ERROR */ + fprintf(stderr, "quota: read error"); + perror(qfpathname); + close(fd); + free(qup); + continue; + } + close(fd); + } + strcpy(qup->fsname, fs->fs_file); + if (quphead == NULL) + quphead = qup; + else + quptail->next = qup; + quptail = qup; + qup->next = 0; + } + endfsent(); + return (quphead); +} + +/* + * Check to see if a particular quota is to be enabled. + */ +hasquota(fs, type, qfnamep) + register struct fstab *fs; + int type; + char **qfnamep; +{ + register char *opt; + char *cp, *index(), *strtok(); + static char initname, usrname[100], grpname[100]; + static char buf[BUFSIZ]; + + if (!initname) { + sprintf(usrname, "%s%s", qfextension[USRQUOTA], qfname); + sprintf(grpname, "%s%s", qfextension[GRPQUOTA], qfname); + initname = 1; + } + strcpy(buf, fs->fs_mntops); + for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { + if (cp = index(opt, '=')) + *cp++ = '\0'; + if (type == USRQUOTA && strcmp(opt, usrname) == 0) + break; + if (type == GRPQUOTA && strcmp(opt, grpname) == 0) + break; + } + if (!opt) + return (0); + if (cp) { + *qfnamep = cp; + return (1); + } + (void) sprintf(buf, "%s/%s.%s", fs->fs_file, qfname, qfextension[type]); + *qfnamep = buf; + return (1); +} + +alldigits(s) + register char *s; +{ + register c; + + c = *s++; + do { + if (!isdigit(c)) + return (0); + } while (c = *s++); + return (1); +} diff --git a/usr.bin/ranlib/ranlib.5.5 b/usr.bin/ranlib/ranlib.5.5 new file mode 100644 index 00000000000..e953c51056d --- /dev/null +++ b/usr.bin/ranlib/ranlib.5.5 @@ -0,0 +1,70 @@ +.\" Copyright (c) 1990, 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. +.\" +.\" @(#)ranlib.5.5 8.1 (Berkeley) 6/6/93 +.\" +.Dd June 6, 1993 +.Dt RANLIB 5 +.Os +.Sh NAME +.Nm ranlib +.Nd archive (library) table-of-contents format +.Sh SYNOPSIS +.Fd #include +.Sh DESCRIPTION +The archive table-of-contents command +.Nm ranlib +creates a table of contents for archives, containing object files, to +be used by the link-editor +.Xr ld 1 . +It operates on archives created with the utility +.Xr ar 1 . +.Pp +The +.Nm Ranlib +function +prepends a new file to the archive which has three separate parts. +The first part is a standard archive header, which has a special name +field, "__.SYMDEF". +.Pp +The second part is a ``long'' followed by a list of ranlib structures. +The long is the size, in bytes, of the list of ranlib structures. +Each of the ranlib structures consists of a zero based offset into the +next section (a string table of symbols) and an offset from the beginning +of the archive to the start of the archive file which defines the symbol. +The actual number of ranlib structures is this number divided by the size +of an individual ranlib structure. +.Pp +The third part is a ``long'' followed by a string table. +The long is the size, in bytes of the string table. +.Sh SEE ALSO +.Xr ar 1 , +.Xr ranlib 1 diff --git a/usr.bin/rlogin/des_rw.c b/usr.bin/rlogin/des_rw.c new file mode 100644 index 00000000000..dbe47f0a67d --- /dev/null +++ b/usr.bin/rlogin/des_rw.c @@ -0,0 +1,203 @@ +/*- + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)des_rw.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#ifdef CRYPT +#ifdef KERBEROS +#include + +#include +#include + +#include +#include +#include +#include + +static unsigned char des_inbuf[10240], storage[10240], *store_ptr; +static bit_64 *key; +static u_char *key_schedule; + +/* XXX these should be in a kerberos include file */ +int krb_net_read __P((int, char *, int)); +#ifdef notdef +/* XXX too hard to make this work */ +int des_pcbc_encrypt __P((des_cblock *, des_cblock *, long, + des_key_schedule, des_cblock *, int)); +#endif + +/* + * NB: These routines will not function properly if NBIO + * is set + */ + +/* + * des_set_key + * + * Set des encryption/decryption key for use by the des_read and + * des_write routines + * + * The inkey parameter is actually the DES initial vector, + * and the insched is the DES Key unwrapped for faster decryption + */ + +void +des_set_key(inkey, insched) + bit_64 *inkey; + u_char *insched; +{ + key = inkey; + key_schedule = insched; +} + +void +des_clear_key() +{ + bzero((char *) key, sizeof(C_Block)); + bzero((char *) key_schedule, sizeof(Key_schedule)); +} + + +int +des_read(fd, buf, len) + int fd; + register char *buf; + int len; +{ + int nreturned = 0; + long net_len, rd_len; + int nstored = 0; + + if (nstored >= len) { + (void) bcopy(store_ptr, buf, len); + store_ptr += len; + nstored -= len; + return(len); + } else if (nstored) { + (void) bcopy(store_ptr, buf, nstored); + nreturned += nstored; + buf += nstored; + len -= nstored; + nstored = 0; + } + + if (krb_net_read(fd, (char *)&net_len, sizeof(net_len)) != + sizeof(net_len)) { + /* XXX can't read enough, pipe + must have closed */ + return(0); + } + net_len = ntohl(net_len); + if (net_len <= 0 || net_len > sizeof(des_inbuf)) { + /* preposterous length; assume out-of-sync; only + recourse is to close connection, so return 0 */ + return(0); + } + /* the writer tells us how much real data we are getting, but + we need to read the pad bytes (8-byte boundary) */ + rd_len = roundup(net_len, 8); + if (krb_net_read(fd, (char *)des_inbuf, rd_len) != rd_len) { + /* pipe must have closed, return 0 */ + return(0); + } + (void) des_pcbc_encrypt(des_inbuf, /* inbuf */ + storage, /* outbuf */ + net_len, /* length */ + key_schedule, /* DES key */ + key, /* IV */ + DECRYPT); /* direction */ + + if(net_len < 8) + store_ptr = storage + 8 - net_len; + else + store_ptr = storage; + + nstored = net_len; + if (nstored > len) { + (void) bcopy(store_ptr, buf, len); + nreturned += len; + store_ptr += len; + nstored -= len; + } else { + (void) bcopy(store_ptr, buf, nstored); + nreturned += nstored; + nstored = 0; + } + + return(nreturned); +} + +static unsigned char des_outbuf[10240]; /* > longest write */ + +int +des_write(fd, buf, len) + int fd; + char *buf; + int len; +{ + static int seeded = 0; + static char garbage_buf[8]; + long net_len, garbage; + + if(len < 8) { + if(!seeded) { + seeded = 1; + srandom((int) time((long *)0)); + } + garbage = random(); + /* insert random garbage */ + (void) bcopy(&garbage, garbage_buf, MIN(sizeof(long),8)); + /* this "right-justifies" the data in the buffer */ + (void) bcopy(buf, garbage_buf + 8 - len, len); + } + /* pcbc_encrypt outputs in 8-byte (64 bit) increments */ + + (void) des_pcbc_encrypt((len < 8) ? garbage_buf : buf, + des_outbuf, + (len < 8) ? 8 : len, + key_schedule, /* DES key */ + key, /* IV */ + ENCRYPT); + + /* tell the other end the real amount, but send an 8-byte padded + packet */ + net_len = htonl(len); + (void) write(fd, &net_len, sizeof(net_len)); + (void) write(fd, des_outbuf, roundup(len,8)); + return(len); +} +#endif /* KERBEROS */ +#endif /* CRYPT */ diff --git a/usr.bin/rlogin/rlogin.1 b/usr.bin/rlogin/rlogin.1 new file mode 100644 index 00000000000..979658a8321 --- /dev/null +++ b/usr.bin/rlogin/rlogin.1 @@ -0,0 +1,188 @@ +.\" Copyright (c) 1983, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)rlogin.1 8.2 (Berkeley) 4/29/95 +.\" +.Dd April 29, 1995 +.Dt RLOGIN 1 +.Os BSD 4.2 +.Sh NAME +.Nm rlogin +.Nd remote login +.Sh SYNOPSIS +.Nm rlogin +.Op Fl 8EKLdx +.Op Fl e Ar char +.Op Fl k Ar realm +.Op Fl l Ar username +.Ar host +.Nm rlogin +.Op Fl 8EKLdx +.Op Fl e Ar char +.Op Fl k Ar realm +.Ar username@host +.Sh DESCRIPTION +.Nm Rlogin +starts a terminal session on a remote host +.Ar host . +.Pp +.Nm Rlogin +first attempts to use the Kerberos authorization mechanism, described below. +If the remote host does not supporting Kerberos the standard Berkeley +.Pa rhosts +authorization mechanism is used. +The options are as follows: +.Bl -tag -width flag +.It Fl 8 +The +.Fl 8 +option allows an eight-bit input data path at all times; otherwise +parity bits are stripped except when the remote side's stop and start +characters are other than +^S/^Q . +.It Fl E +The +.Fl E +option stops any character from being recognized as an escape character. +When used with the +.Fl 8 +option, this provides a completely transparent connection. +.It Fl K +The +.Fl K +option turns off all Kerberos authentication. +.It Fl L +The +.Fl L +option allows the rlogin session to be run in ``litout'' (see +.Xr tty 4 ) +mode. +.It Fl d +The +.Fl d +option turns on socket debugging (see +.Xr setsockopt 2 ) +on the TCP sockets used for communication with the remote host. +.It Fl e +The +.Fl e +option allows user specification of the escape character, which is +``~'' by default. +This specification may be as a literal character, or as an octal +value in the form \ennn. +.It Fl k +The +.FL k +option requests rlogin to obtain tickets for the remote host +in realm +.Ar realm +instead of the remote host's realm as determined by +.Xr krb_realmofhost 3 . +.It Fl x +The +.Fl x +option turns on +.Tn DES +encryption for all data passed via the +rlogin session. +This may impact response time and +.Tn CPU +utilization, but provides +increased security. +.El +.Pp +A line of the form ``.'' disconnects from the remote host. +Similarly, the line ``^Z'' will suspend the +.Nm rlogin +session, and ``'' suspends the +send portion of the rlogin, but allows output from the remote system. +By default, the tilde (``~'') character is the escape character, and +normally control-Y (``^Y'') is the delayed-suspend character. +.Pp +All echoing takes place at the remote site, so that (except for delays) +the +.Nm rlogin +is transparent. +Flow control via ^S/^Q and flushing of input and output on interrupts +are handled properly. +.Sh KERBEROS AUTHENTICATION +Each user may have a private authorization list in the file +.Pa .klogin +in their home directory. +Each line in this file should contain a Kerberos principal name of the +form +.Ar principal.instance@realm . +If the originating user is authenticated to one of the principals named +in +.Pa .klogin , +access is granted to the account. +The principal +.Ar accountname.@localrealm +is granted access if +there is no +.Pa .klogin +file. +Otherwise a login and password will be prompted for on the remote machine +as in +.Xr login 1 . +To avoid certain security problems, the +.Pa .klogin +file must be owned by +the remote user. +.Pp +If Kerberos authentication fails, a warning message is printed and the +standard Berkeley +.Nm rlogin +is used instead. +.Sh ENVIRONMENT +The following environment variable is utilized by +.Nm rlogin : +.Bl -tag -width TERM +.It Ev TERM +Determines the user's terminal type. +.El +.Sh SEE ALSO +.Xr rsh 1 , +.Xr kerberos 3 , +.Xr krb_sendauth 3 , +.Xr krb_realmofhost 3 +.Sh HISTORY +The +.Nm rlogin +command appeared in +.Bx 4.2 . +.Sh BUGS +.Nm Rlogin +will be replaced by +.Xr telnet 1 +in the near future. +.Pp +More of the environment should be propagated. diff --git a/usr.bin/rlogin/rlogin.c b/usr.bin/rlogin/rlogin.c new file mode 100644 index 00000000000..4aee20e8b58 --- /dev/null +++ b/usr.bin/rlogin/rlogin.c @@ -0,0 +1,991 @@ +/* + * Copyright (c) 1983, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1990, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)rlogin.c 8.4 (Berkeley) 4/29/95"; +#endif /* not lint */ + +/* + * rlogin - remote login + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __STDC__ +#include +#else +#include +#endif + +#ifdef KERBEROS +#include +#include + +#include "krb.h" + +CREDENTIALS cred; +Key_schedule schedule; +int use_kerberos = 1, doencrypt; +char dst_realm_buf[REALM_SZ], *dest_realm = NULL; +#endif + +#ifndef TIOCPKT_WINDOW +#define TIOCPKT_WINDOW 0x80 +#endif + +/* concession to Sun */ +#ifndef SIGUSR1 +#define SIGUSR1 30 +#endif + +int eight, litout, rem; + +int noescape; +u_char escapechar = '~'; + +#ifdef OLDSUN +struct winsize { + unsigned short ws_row, ws_col; + unsigned short ws_xpixel, ws_ypixel; +}; +#else +#define get_window_size(fd, wp) ioctl(fd, TIOCGWINSZ, wp) +#endif +struct winsize winsize; + +void catch_child __P((int)); +void copytochild __P((int)); +__dead void doit __P((sigset_t *)); +__dead void done __P((int)); +void echo __P((char)); +u_int getescape __P((char *)); +void lostpeer __P((int)); +void mode __P((int)); +void msg __P((char *)); +void oob __P((int)); +int reader __P((sigset_t *)); +void sendwindow __P((void)); +void setsignal __P((int)); +int speed __P((int)); +void sigwinch __P((int)); +void stop __P((char)); +__dead void usage __P((void)); +void writer __P((void)); +void writeroob __P((int)); + +#ifdef KERBEROS +void warning __P((const char *, ...)); +#endif +#ifdef OLDSUN +int get_window_size __P((int, struct winsize *)); +#endif + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct passwd *pw; + struct servent *sp; + sigset_t smask; + uid_t uid; + int argoff, ch, dflag, one; + char *host, *p, *user, term[1024]; + struct sigaction sa; + + argoff = dflag = 0; + one = 1; + host = user = NULL; + + if (p = strrchr(argv[0], '/')) + ++p; + else + p = argv[0]; + + if (strcmp(p, "rlogin") != 0) + host = p; + + /* handle "rlogin host flags" */ + if (!host && argc > 2 && argv[1][0] != '-') { + host = argv[1]; + argoff = 1; + } + +#ifdef KERBEROS +#define OPTIONS "8EKLde:k:l:x" +#else +#define OPTIONS "8EKLde:l:" +#endif + while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != EOF) + switch(ch) { + case '8': + eight = 1; + break; + case 'E': + noescape = 1; + break; + case 'K': +#ifdef KERBEROS + use_kerberos = 0; +#endif + break; + case 'L': + litout = 1; + break; + case 'd': + dflag = 1; + break; + case 'e': + noescape = 0; + escapechar = getescape(optarg); + break; +#ifdef KERBEROS + case 'k': + dest_realm = dst_realm_buf; + (void)strncpy(dest_realm, optarg, REALM_SZ); + break; +#endif + case 'l': + user = optarg; + break; +#ifdef CRYPT +#ifdef KERBEROS + case 'x': + doencrypt = 1; + des_set_key(cred.session, schedule); + break; +#endif +#endif + case '?': + default: + usage(); + } + optind += argoff; + argc -= optind; + argv += optind; + + /* if haven't gotten a host yet, do so */ + if (!host && !(host = *argv++)) + usage(); + + if (*argv) + usage(); + + if (!(pw = getpwuid(uid = getuid()))) + errx(1, "unknown user id."); + /* Accept user1@host format, though "-l user2" overrides user1 */ + p = strchr(host, '@'); + if (p) { + *p = '\0'; + if (!user && p > host) + user = host; + host = p + 1; + if (*host == '\0') + usage(); + } + if (!user) + user = pw->pw_name; + + sp = NULL; +#ifdef KERBEROS + if (use_kerberos) { + sp = getservbyname((doencrypt ? "eklogin" : "klogin"), "tcp"); + if (sp == NULL) { + use_kerberos = 0; + warning("can't get entry for %s/tcp service", + doencrypt ? "eklogin" : "klogin"); + } + } +#endif + if (sp == NULL) + sp = getservbyname("login", "tcp"); + if (sp == NULL) + errx(1, "login/tcp: unknown service."); + + (void)snprintf(term, sizeof(term), "%s/%d", + ((p = getenv("TERM")) ? p : "network"), + speed(0)); + + (void)get_window_size(0, &winsize); + + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sa.sa_handler = lostpeer; + (void)sigaction(SIGPIPE, &sa, (struct sigaction *) 0); + /* will use SIGUSR1 for window size hack, so hold it off */ + sigemptyset(&smask); + sigaddset(&smask, SIGURG); + sigaddset(&smask, SIGUSR1); + (void)sigprocmask(SIG_SETMASK, &smask, &smask); + /* + * We set SIGURG and SIGUSR1 below so that an + * incoming signal will be held pending rather than being + * discarded. Note that these routines will be ready to get + * a signal by the time that they are unblocked below. + */ + sa.sa_handler = copytochild; + (void)sigaction(SIGURG, &sa, (struct sigaction *) 0); + sa.sa_handler = writeroob; + (void)sigaction(SIGUSR1, &sa, (struct sigaction *) 0); + +#ifdef KERBEROS +try_connect: + if (use_kerberos) { + struct hostent *hp; + + /* Fully qualify hostname (needed for krb_realmofhost). */ + hp = gethostbyname(host); + if (hp != NULL && !(host = strdup(hp->h_name))) + errx(1, "%s", strerror(ENOMEM)); + + rem = KSUCCESS; + errno = 0; + if (dest_realm == NULL) + dest_realm = krb_realmofhost(host); + +#ifdef CRYPT + if (doencrypt) + rem = krcmd_mutual(&host, sp->s_port, user, term, 0, + dest_realm, &cred, schedule); + else +#endif /* CRYPT */ + rem = krcmd(&host, sp->s_port, user, term, 0, + dest_realm); + if (rem < 0) { + use_kerberos = 0; + sp = getservbyname("login", "tcp"); + if (sp == NULL) + errx(1, "unknown service login/tcp."); + if (errno == ECONNREFUSED) + warning("remote host doesn't support Kerberos"); + if (errno == ENOENT) + warning("can't provide Kerberos auth data"); + goto try_connect; + } + } else { +#ifdef CRYPT + if (doencrypt) + errx(1, "the -x flag requires Kerberos authentication."); +#endif /* CRYPT */ + rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0); + } +#else + rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0); +#endif /* KERBEROS */ + + if (rem < 0) + exit(1); + + if (dflag && + setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0) + warn("setsockopt DEBUG (ignored)"); + one = IPTOS_LOWDELAY; + if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one, sizeof(int)) < 0) + warn("setsockopt TOS (ignored)"); + + (void)setuid(uid); + doit(&smask); + /*NOTREACHED*/ +} + +#if BSD >= 198810 +int +speed(fd) + int fd; +{ + struct termios tt; + + (void)tcgetattr(fd, &tt); + + return ((int) cfgetispeed(&tt)); +} +#else +int speeds[] = { /* for older systems, B0 .. EXTB */ + 0, 50, 75, 110, + 134, 150, 200, 300, + 600, 1200, 1800, 2400, + 4800, 9600, 19200, 38400 +}; + +int +speed(fd) + int fd; +{ + struct termios tt; + + (void)tcgetattr(fd, &tt); + + return (speeds[(int)cfgetispeed(&tt)]); +} +#endif + +pid_t child; +struct termios deftt; +struct termios nott; + +void +doit(smask) + sigset_t *smask; +{ + int i; + struct sigaction sa; + + for (i = 0; i < NCCS; i++) + nott.c_cc[i] = _POSIX_VDISABLE; + tcgetattr(0, &deftt); + nott.c_cc[VSTART] = deftt.c_cc[VSTART]; + nott.c_cc[VSTOP] = deftt.c_cc[VSTOP]; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sa.sa_handler = SIG_IGN; + (void)sigaction(SIGINT, &sa, (struct sigaction *) 0); + setsignal(SIGHUP); + setsignal(SIGQUIT); + child = fork(); + if (child == -1) { + warn("fork"); + done(1); + } + if (child == 0) { + mode(1); + if (reader(smask) == 0) { + msg("connection closed."); + exit(0); + } + sleep(1); + msg("\007connection closed."); + exit(1); + } + + /* + * We may still own the socket, and may have a pending SIGURG (or might + * receive one soon) that we really want to send to the reader. When + * one of these comes in, the trap copytochild simply copies such + * signals to the child. We can now unblock SIGURG and SIGUSR1 + * that were set above. + */ + (void)sigprocmask(SIG_SETMASK, smask, (sigset_t *) 0); + sa.sa_handler = catch_child; + (void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0); + writer(); + msg("closed connection."); + done(0); +} + +/* trap a signal, unless it is being ignored. */ +void +setsignal(sig) + int sig; +{ + struct sigaction sa; + sigset_t sigs; + + sigemptyset(&sigs); + sigaddset(&sigs, sig); + sigprocmask(SIG_BLOCK, &sigs, &sigs); + + sigemptyset(&sa.sa_mask); + sa.sa_handler = exit; + sa.sa_flags = SA_RESTART; + (void)sigaction(sig, &sa, &sa); + if (sa.sa_handler == SIG_IGN) + (void)sigaction(sig, &sa, (struct sigaction *) 0); + + (void)sigprocmask(SIG_SETMASK, &sigs, (sigset_t *) 0); +} + +__dead void +done(status) + int status; +{ + pid_t w; + int wstatus; + struct sigaction sa; + + mode(0); + if (child > 0) { + /* make sure catch_child does not snap it up */ + sigemptyset(&sa.sa_mask); + sa.sa_handler = SIG_DFL; + sa.sa_flags = 0; + (void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0); + if (kill(child, SIGKILL) >= 0) + while ((w = wait(&wstatus)) > 0 && w != child) + continue; + } + exit(status); +} + +int dosigwinch; + +/* + * This is called when the reader process gets the out-of-band (urgent) + * request to turn on the window-changing protocol. + */ +void +writeroob(signo) + int signo; +{ + struct sigaction sa; + + if (dosigwinch == 0) { + sendwindow(); + sigemptyset(&sa.sa_mask); + sa.sa_handler = sigwinch; + sa.sa_flags = SA_RESTART; + (void)sigaction(SIGWINCH, &sa, (struct sigaction *) 0); + } + dosigwinch = 1; +} + +void +catch_child(signo) + int signo; +{ + int status; + pid_t pid; + + for (;;) { + pid = waitpid(-1, &status, WNOHANG|WUNTRACED); + if (pid == 0) + return; + /* if the child (reader) dies, just quit */ + if (pid < 0 || (pid == child && !WIFSTOPPED(status))) + done(WEXITSTATUS(status) | WTERMSIG(status)); + } + /* NOTREACHED */ +} + +/* + * writer: write to remote: 0 -> line. + * ~. terminate + * ~^Z suspend rlogin process. + * ~ suspend rlogin process, but leave reader alone. + */ +void +writer() +{ + register int bol, local, n; + char c; + + bol = 1; /* beginning of line */ + local = 0; + for (;;) { + n = read(STDIN_FILENO, &c, 1); + if (n <= 0) { + if (n < 0 && errno == EINTR) + continue; + break; + } + /* + * If we're at the beginning of the line and recognize a + * command character, then we echo locally. Otherwise, + * characters are echo'd remotely. If the command character + * is doubled, this acts as a force and local echo is + * suppressed. + */ + if (bol) { + bol = 0; + if (!noescape && c == escapechar) { + local = 1; + continue; + } + } else if (local) { + local = 0; + if (c == '.' || c == deftt.c_cc[VEOF]) { + echo(c); + break; + } + if (c == deftt.c_cc[VSUSP] || c == deftt.c_cc[VDSUSP]) { + bol = 1; + echo(c); + stop(c); + continue; + } + if (c != escapechar) +#ifdef CRYPT +#ifdef KERBEROS + if (doencrypt) + (void)des_write(rem, + (char *)&escapechar, 1); + else +#endif +#endif + (void)write(rem, &escapechar, 1); + } + +#ifdef CRYPT +#ifdef KERBEROS + if (doencrypt) { + if (des_write(rem, &c, 1) == 0) { + msg("line gone"); + break; + } + } else +#endif +#endif + if (write(rem, &c, 1) == 0) { + msg("line gone"); + break; + } + bol = c == deftt.c_cc[VKILL] || c == deftt.c_cc[VEOF] || + c == deftt.c_cc[VINTR] || c == deftt.c_cc[VSUSP] || + c == '\r' || c == '\n'; + } +} + +void +#if __STDC__ +echo(register char c) +#else +echo(c) + register char c; +#endif +{ + register char *p; + char buf[8]; + + p = buf; + c &= 0177; + *p++ = escapechar; + if (c < ' ') { + *p++ = '^'; + *p++ = c + '@'; + } else if (c == 0177) { + *p++ = '^'; + *p++ = '?'; + } else + *p++ = c; + *p++ = '\r'; + *p++ = '\n'; + (void)write(STDOUT_FILENO, buf, p - buf); +} + +void +#if __STDC__ +stop(char cmdc) +#else +stop(cmdc) + char cmdc; +#endif +{ + struct sigaction sa; + + mode(0); + sigemptyset(&sa.sa_mask); + sa.sa_handler = SIG_IGN; + sa.sa_flags = SA_RESTART; + (void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0); + (void)kill(cmdc == deftt.c_cc[VSUSP] ? 0 : getpid(), SIGTSTP); + sa.sa_handler = catch_child; + (void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0); + mode(1); + sigwinch(0); /* check for size changes */ +} + +void +sigwinch(signo) + int signo; +{ + struct winsize ws; + + if (dosigwinch && get_window_size(0, &ws) == 0 && + memcmp(&ws, &winsize, sizeof(ws))) { + winsize = ws; + sendwindow(); + } +} + +/* + * Send the window size to the server via the magic escape + */ +void +sendwindow() +{ + struct winsize *wp; + char obuf[4 + sizeof (struct winsize)]; + + wp = (struct winsize *)(obuf+4); + obuf[0] = 0377; + obuf[1] = 0377; + obuf[2] = 's'; + obuf[3] = 's'; + wp->ws_row = htons(winsize.ws_row); + wp->ws_col = htons(winsize.ws_col); + wp->ws_xpixel = htons(winsize.ws_xpixel); + wp->ws_ypixel = htons(winsize.ws_ypixel); + +#ifdef CRYPT +#ifdef KERBEROS + if(doencrypt) + (void)des_write(rem, obuf, sizeof(obuf)); + else +#endif +#endif + (void)write(rem, obuf, sizeof(obuf)); +} + +/* + * reader: read from remote: line -> 1 + */ +#define READING 1 +#define WRITING 2 + +jmp_buf rcvtop; +pid_t ppid; +int rcvcnt, rcvstate; +char rcvbuf[8 * 1024]; + +void +oob(signo) + int signo; +{ + struct termios tt; + int atmark, n, out, rcvd; + char waste[BUFSIZ], mark; + + out = O_RDWR; + rcvd = 0; + while (recv(rem, &mark, 1, MSG_OOB) < 0) { + switch (errno) { + case EWOULDBLOCK: + /* + * Urgent data not here yet. It may not be possible + * to send it yet if we are blocked for output and + * our input buffer is full. + */ + if (rcvcnt < sizeof(rcvbuf)) { + n = read(rem, rcvbuf + rcvcnt, + sizeof(rcvbuf) - rcvcnt); + if (n <= 0) + return; + rcvd += n; + } else { + n = read(rem, waste, sizeof(waste)); + if (n <= 0) + return; + } + continue; + default: + return; + } + } + if (mark & TIOCPKT_WINDOW) { + /* Let server know about window size changes */ + (void)kill(ppid, SIGUSR1); + } + if (!eight && (mark & TIOCPKT_NOSTOP)) { + tcgetattr(0, &tt); + tt.c_iflag &= ~(IXON | IXOFF); + tt.c_cc[VSTOP] = _POSIX_VDISABLE; + tt.c_cc[VSTART] = _POSIX_VDISABLE; + tcsetattr(0, TCSANOW, &tt); + } + if (!eight && (mark & TIOCPKT_DOSTOP)) { + tcgetattr(0, &tt); + tt.c_iflag |= (IXON|IXOFF); + tt.c_cc[VSTOP] = deftt.c_cc[VSTOP]; + tt.c_cc[VSTART] = deftt.c_cc[VSTART]; + tcsetattr(0, TCSANOW, &tt); + } + if (mark & TIOCPKT_FLUSHWRITE) { + (void)ioctl(1, TIOCFLUSH, (char *)&out); + for (;;) { + if (ioctl(rem, SIOCATMARK, &atmark) < 0) { + warn("ioctl SIOCATMARK (ignored)"); + break; + } + if (atmark) + break; + n = read(rem, waste, sizeof (waste)); + if (n <= 0) + break; + } + /* + * Don't want any pending data to be output, so clear the recv + * buffer. If we were hanging on a write when interrupted, + * don't want it to restart. If we were reading, restart + * anyway. + */ + rcvcnt = 0; + longjmp(rcvtop, 1); + } + + /* oob does not do FLUSHREAD (alas!) */ + + /* + * If we filled the receive buffer while a read was pending, longjmp + * to the top to restart appropriately. Don't abort a pending write, + * however, or we won't know how much was written. + */ + if (rcvd && rcvstate == READING) + longjmp(rcvtop, 1); +} + +/* reader: read from remote: line -> 1 */ +int +reader(smask) + sigset_t *smask; +{ + pid_t pid; + int n, remaining; + char *bufp; + struct sigaction sa; + +#if BSD >= 43 || defined(SUNOS4) + pid = getpid(); /* modern systems use positives for pid */ +#else + pid = -getpid(); /* old broken systems use negatives */ +#endif + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sa.sa_handler = SIG_IGN; + (void)sigaction(SIGTTOU, &sa, (struct sigaction *) 0); + sa.sa_handler = oob; + (void)sigaction(SIGURG, &sa, (struct sigaction *) 0); + ppid = getppid(); + (void)fcntl(rem, F_SETOWN, pid); + (void)setjmp(rcvtop); + (void)sigprocmask(SIG_SETMASK, smask, (sigset_t *) 0); + bufp = rcvbuf; + for (;;) { + while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) { + rcvstate = WRITING; + n = write(STDOUT_FILENO, bufp, remaining); + if (n < 0) { + if (errno != EINTR) + return (-1); + continue; + } + bufp += n; + } + bufp = rcvbuf; + rcvcnt = 0; + rcvstate = READING; + +#ifdef CRYPT +#ifdef KERBEROS + if (doencrypt) + rcvcnt = des_read(rem, rcvbuf, sizeof(rcvbuf)); + else +#endif +#endif + rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf)); + if (rcvcnt == 0) + return (0); + if (rcvcnt < 0) { + if (errno == EINTR) + continue; + warn("read"); + return (-1); + } + } +} + +void +mode(f) + int f; +{ + struct termios tt; + + switch (f) { + case 0: + tcsetattr(0, TCSADRAIN, &deftt); + break; + case 1: + tt = deftt; + tt.c_oflag &= ~(OPOST); + tt.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); + tt.c_iflag &= ~(ICRNL); + tt.c_cc[VMIN] = 1; + tt.c_cc[VTIME] = 0; + if (eight) { + tt.c_iflag &= ~(IXON | IXOFF | ISTRIP); + tt.c_cc[VSTOP] = _POSIX_VDISABLE; + tt.c_cc[VSTART] = _POSIX_VDISABLE; + } + /*if (litout) + lflags |= LLITOUT;*/ + tcsetattr(0, TCSADRAIN, &tt); + break; + + default: + return; + } +} + +void +lostpeer(signo) + int signo; +{ + struct sigaction sa; + + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sa.sa_handler = SIG_IGN; + (void)sigaction(SIGPIPE, &sa, (struct sigaction *) 0); + msg("\007connection closed."); + done(1); +} + +/* copy SIGURGs to the child process. */ +void +copytochild(signo) + int signo; +{ + + (void)kill(child, SIGURG); +} + +void +msg(str) + char *str; +{ + + (void)fprintf(stderr, "rlogin: %s\r\n", str); +} + +#ifdef KERBEROS +/* VARARGS */ +void +#if __STDC__ +warning(const char *fmt, ...) +#else +warning(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; + + (void)fprintf(stderr, "rlogin: warning, using standard rlogin: "); +#ifdef __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, ".\n"); +} +#endif + +__dead void +usage() +{ + (void)fprintf(stderr, + "usage: rlogin [ -%s]%s[-e char] [ -l username ] [username@]host\n", +#ifdef KERBEROS +#ifdef CRYPT + "8EKLx", " [-k realm] "); +#else + "8EKL", " [-k realm] "); +#endif +#else + "8EL", " "); +#endif + exit(1); +} + +/* + * The following routine provides compatibility (such as it is) between older + * Suns and others. Suns have only a `ttysize', so we convert it to a winsize. + */ +#ifdef OLDSUN +int +get_window_size(fd, wp) + int fd; + struct winsize *wp; +{ + struct ttysize ts; + int error; + + if ((error = ioctl(0, TIOCGSIZE, &ts)) != 0) + return (error); + wp->ws_row = ts.ts_lines; + wp->ws_col = ts.ts_cols; + wp->ws_xpixel = 0; + wp->ws_ypixel = 0; + return (0); +} +#endif + +u_int +getescape(p) + register char *p; +{ + long val; + int len; + + if ((len = strlen(p)) == 1) /* use any single char, including '\' */ + return ((u_int)*p); + /* otherwise, \nnn */ + if (*p == '\\' && len >= 2 && len <= 4) { + val = strtol(++p, NULL, 8); + for (;;) { + if (!*++p) + return ((u_int)val); + if (*p < '0' || *p > '8') + break; + } + } + msg("illegal option value -- e"); + usage(); + /* NOTREACHED */ +} diff --git a/usr.bin/rsh/rsh.1 b/usr.bin/rsh/rsh.1 new file mode 100644 index 00000000000..78ec2f10808 --- /dev/null +++ b/usr.bin/rsh/rsh.1 @@ -0,0 +1,187 @@ +.\" Copyright (c) 1983, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)rsh.1 8.2 (Berkeley) 4/29/95 +.\" +.Dd April 29, 1995 +.Dt RSH 1 +.Os BSD 4.2 +.Sh NAME +.Nm rsh +.Nd remote shell +.Sh SYNOPSIS +.Nm rsh +.Op Fl Kdnx +.Op Fl k Ar realm +.Op Fl l Ar username +.Ar host +.Nm rsh +.Op Fl Kdnx +.Op Fl k Ar realm +.Ar username@host +.Op command +.Sh DESCRIPTION +.Nm Rsh +executes +.Ar command +on +.Ar host . +.Pp +.Nm Rsh +copies its standard input to the remote command, the standard +output of the remote command to its standard output, and the +standard error of the remote command to its standard error. +Interrupt, quit and terminate signals are propagated to the remote +command; +.Nm rsh +normally terminates when the remote command does. +The options are as follows: +.Bl -tag -width flag +.It Fl K +The +.Fl K +option turns off all Kerberos authentication. +.It Fl d +The +.Fl d +option turns on socket debugging (using +.Xr setsockopt 2 ) +on the +.Tn TCP +sockets used for communication with the remote host. +.It Fl k +The +.Fl k +option causes +.Nm rsh +to obtain tickets for the remote host in +.Ar realm +instead of the remote host's realm as determined by +.Xr krb_realmofhost 3 . +.It Fl l +By default, the remote username is the same as the local username. +The +.Fl l +option or the +.Pa username@host +format allow the remote name to be specified. +Kerberos authentication is used, and authorization is determined +as in +.Xr rlogin 1 . +.It Fl n +The +.Fl n +option redirects input from the special device +.Pa /dev/null +(see the +.Sx BUGS +section of this manual page). +.It Fl x +The +.Fl x +option turns on +.Tn DES +encryption for all data exchange. +This may introduce a significant delay in response time. +.El +.Pp +If no +.Ar command +is specified, you will be logged in on the remote host using +.Xr rlogin 1 . +.Pp +Shell metacharacters which are not quoted are interpreted on local machine, +while quoted metacharacters are interpreted on the remote machine. +For example, the command +.Pp +.Dl rsh otherhost cat remotefile >> localfile +.Pp +appends the remote file +.Ar remotefile +to the local file +.Ar localfile , +while +.Pp +.Dl rsh otherhost cat remotefile \&">>\&" other_remotefile +.Pp +appends +.Ar remotefile +to +.Ar other_remotefile . +.\" .Pp +.\" Many sites specify a large number of host names as commands in the +.\" directory /usr/hosts. +.\" If this directory is included in your search path, you can use the +.\" shorthand ``host command'' for the longer form ``rsh host command''. +.Sh FILES +.Bl -tag -width /etc/hosts -compact +.It Pa /etc/hosts +.El +.Sh SEE ALSO +.Xr rlogin 1 , +.Xr kerberos 3 , +.Xr krb_sendauth 3 , +.Xr krb_realmofhost 3 +.Sh HISTORY +The +.Nm rsh +command appeared in +.Bx 4.2 . +.Sh BUGS +If you are using +.Xr csh 1 +and put a +.Nm rsh +in the background without redirecting its input away from the terminal, +it will block even if no reads are posted by the remote command. +If no input is desired you should redirect the input of +.Nm rsh +to +.Pa /dev/null +using the +.Fl n +option. +.Pp +You cannot run an interactive command +(like +.Xr rogue 6 +or +.Xr vi 1 ) +using +.Nm rsh ; +use +.Xr rlogin 1 +instead. +.Pp +Stop signals stop the local +.Nm rsh +process only; this is arguably wrong, but currently hard to fix for reasons +too complicated to explain here. diff --git a/usr.bin/rsh/rsh.c b/usr.bin/rsh/rsh.c new file mode 100644 index 00000000000..7e3a5300092 --- /dev/null +++ b/usr.bin/rsh/rsh.c @@ -0,0 +1,490 @@ +/*- + * Copyright (c) 1983, 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) 1983, 1990, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)rsh.c 8.4 (Berkeley) 4/29/95"; +#endif /* not lint */ + +/* + * $Source: mit/rsh/RCS/rsh.c,v $ + * $Header: mit/rsh/RCS/rsh.c,v 5.1 89/07/31 19:28:59 kfall Exp Locker: kfall $ + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pathnames.h" + +#ifdef KERBEROS +#include +#include + +CREDENTIALS cred; +Key_schedule schedule; +int use_kerberos = 1, doencrypt; +char dst_realm_buf[REALM_SZ], *dest_realm; +extern char *krb_realmofhost(); +#endif + +/* + * rsh - remote shell + */ +int rfd2; + +char *copyargs __P((char **)); +void sendsig __P((int)); +void talk __P((int, long, pid_t, int)); +void usage __P((void)); +void warning __P(()); + +int +main(argc, argv) + int argc; + char **argv; +{ + struct passwd *pw; + struct servent *sp; + long omask; + int argoff, asrsh, ch, dflag, nflag, one, rem; + pid_t pid; + uid_t uid; + char *args, *host, *p, *user; + + argoff = asrsh = dflag = nflag = 0; + one = 1; + host = user = NULL; + + /* if called as something other than "rsh", use it as the host name */ + if (p = strrchr(argv[0], '/')) + ++p; + else + p = argv[0]; + if (strcmp(p, "rsh")) + host = p; + else + asrsh = 1; + + /* handle "rsh host flags" */ + if (!host && argc > 2 && argv[1][0] != '-') { + host = argv[1]; + argoff = 1; + } + +#ifdef KERBEROS +#ifdef CRYPT +#define OPTIONS "8KLdek:l:nwx" +#else +#define OPTIONS "8KLdek:l:nw" +#endif +#else +#define OPTIONS "8KLdel:nw" +#endif + while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != EOF) + switch(ch) { + case 'K': +#ifdef KERBEROS + use_kerberos = 0; +#endif + break; + case 'L': /* -8Lew are ignored to allow rlogin aliases */ + case 'e': + case 'w': + case '8': + break; + case 'd': + dflag = 1; + break; + case 'l': + user = optarg; + break; +#ifdef KERBEROS + case 'k': + dest_realm = dst_realm_buf; + strncpy(dest_realm, optarg, REALM_SZ); + break; +#endif + case 'n': + nflag = 1; + break; +#ifdef KERBEROS +#ifdef CRYPT + case 'x': + doencrypt = 1; + des_set_key(cred.session, schedule); + break; +#endif +#endif + case '?': + default: + usage(); + } + optind += argoff; + + /* if haven't gotten a host yet, do so */ + if (!host && !(host = argv[optind++])) + usage(); + + /* if no further arguments, must have been called as rlogin. */ + if (!argv[optind]) { + if (asrsh) + *argv = "rlogin"; + execv(_PATH_RLOGIN, argv); + err(1, "can't exec %s", _PATH_RLOGIN); + } + + argc -= optind; + argv += optind; + + if (!(pw = getpwuid(uid = getuid()))) + errx(1, "unknown user id"); + /* Accept user1@host format, though "-l user2" overrides user1 */ + p = strchr(host, '@'); + if (p) { + *p = '\0'; + if (!user && p > host) + user = host; + host = p + 1; + if (*host == '\0') + usage(); + } + if (!user) + user = pw->pw_name; + +#ifdef KERBEROS +#ifdef CRYPT + /* -x turns off -n */ + if (doencrypt) + nflag = 0; +#endif +#endif + + args = copyargs(argv); + + sp = NULL; +#ifdef KERBEROS + if (use_kerberos) { + sp = getservbyname((doencrypt ? "ekshell" : "kshell"), "tcp"); + if (sp == NULL) { + use_kerberos = 0; + warning("can't get entry for %s/tcp service", + doencrypt ? "ekshell" : "kshell"); + } + } +#endif + if (sp == NULL) + sp = getservbyname("shell", "tcp"); + if (sp == NULL) + errx(1, "shell/tcp: unknown service"); + +#ifdef KERBEROS +try_connect: + if (use_kerberos) { + struct hostent *hp; + + /* fully qualify hostname (needed for krb_realmofhost) */ + hp = gethostbyname(host); + if (hp != NULL && !(host = strdup(hp->h_name))) + err(1, NULL); + + rem = KSUCCESS; + errno = 0; + if (dest_realm == NULL) + dest_realm = krb_realmofhost(host); + +#ifdef CRYPT + if (doencrypt) + rem = krcmd_mutual(&host, sp->s_port, user, args, + &rfd2, dest_realm, &cred, schedule); + else +#endif + rem = krcmd(&host, sp->s_port, user, args, &rfd2, + dest_realm); + if (rem < 0) { + use_kerberos = 0; + sp = getservbyname("shell", "tcp"); + if (sp == NULL) + errx(1, "shell/tcp: unknown service"); + if (errno == ECONNREFUSED) + warning("remote host doesn't support Kerberos"); + if (errno == ENOENT) + warning("can't provide Kerberos auth data"); + goto try_connect; + } + } else { + if (doencrypt) + errx(1, "the -x flag requires Kerberos authentication"); + rem = rcmd(&host, sp->s_port, pw->pw_name, user, args, &rfd2); + } +#else + rem = rcmd(&host, sp->s_port, pw->pw_name, user, args, &rfd2); +#endif + + if (rem < 0) + exit(1); + + if (rfd2 < 0) + errx(1, "can't establish stderr"); + if (dflag) { + if (setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, + sizeof(one)) < 0) + warn("setsockopt"); + if (setsockopt(rfd2, SOL_SOCKET, SO_DEBUG, &one, + sizeof(one)) < 0) + warn("setsockopt"); + } + + (void)setuid(uid); + omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGTERM)); + if (signal(SIGINT, SIG_IGN) != SIG_IGN) + (void)signal(SIGINT, sendsig); + if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) + (void)signal(SIGQUIT, sendsig); + if (signal(SIGTERM, SIG_IGN) != SIG_IGN) + (void)signal(SIGTERM, sendsig); + + if (!nflag) { + pid = fork(); + if (pid < 0) + err(1, "fork"); + } + +#ifdef KERBEROS +#ifdef CRYPT + if (!doencrypt) +#endif +#endif + { + (void)ioctl(rfd2, FIONBIO, &one); + (void)ioctl(rem, FIONBIO, &one); + } + + talk(nflag, omask, pid, rem); + + if (!nflag) + (void)kill(pid, SIGKILL); + exit(0); +} + +void +talk(nflag, omask, pid, rem) + int nflag; + long omask; + pid_t pid; + int rem; +{ + int cc, wc; + fd_set readfrom, ready, rembits; + char *bp, buf[BUFSIZ]; + + if (!nflag && pid == 0) { + (void)close(rfd2); + +reread: errno = 0; + if ((cc = read(0, buf, sizeof buf)) <= 0) + goto done; + bp = buf; + +rewrite: + FD_ZERO(&rembits); + FD_SET(rem, &rembits); + if (select(16, 0, &rembits, 0, 0) < 0) { + if (errno != EINTR) + err(1, "select"); + goto rewrite; + } + if (!FD_ISSET(rem, &rembits)) + goto rewrite; +#ifdef KERBEROS +#ifdef CRYPT + if (doencrypt) + wc = des_write(rem, bp, cc); + else +#endif +#endif + wc = write(rem, bp, cc); + if (wc < 0) { + if (errno == EWOULDBLOCK) + goto rewrite; + goto done; + } + bp += wc; + cc -= wc; + if (cc == 0) + goto reread; + goto rewrite; +done: + (void)shutdown(rem, 1); + exit(0); + } + + (void)sigsetmask(omask); + FD_ZERO(&readfrom); + FD_SET(rfd2, &readfrom); + FD_SET(rem, &readfrom); + do { + ready = readfrom; + if (select(16, &ready, 0, 0, 0) < 0) { + if (errno != EINTR) + err(1, "select"); + continue; + } + if (FD_ISSET(rfd2, &ready)) { + errno = 0; +#ifdef KERBEROS +#ifdef CRYPT + if (doencrypt) + cc = des_read(rfd2, buf, sizeof buf); + else +#endif +#endif + cc = read(rfd2, buf, sizeof buf); + if (cc <= 0) { + if (errno != EWOULDBLOCK) + FD_CLR(rfd2, &readfrom); + } else + (void)write(2, buf, cc); + } + if (FD_ISSET(rem, &ready)) { + errno = 0; +#ifdef KERBEROS +#ifdef CRYPT + if (doencrypt) + cc = des_read(rem, buf, sizeof buf); + else +#endif +#endif + cc = read(rem, buf, sizeof buf); + if (cc <= 0) { + if (errno != EWOULDBLOCK) + FD_CLR(rem, &readfrom); + } else + (void)write(1, buf, cc); + } + } while (FD_ISSET(rfd2, &readfrom) || FD_ISSET(rem, &readfrom)); +} + +void +sendsig(sig) + int sig; +{ + char signo; + + signo = sig; +#ifdef KERBEROS +#ifdef CRYPT + if (doencrypt) + (void)des_write(rfd2, &signo, 1); + else +#endif +#endif + (void)write(rfd2, &signo, 1); +} + +#ifdef KERBEROS +/* VARARGS */ +void +warning(va_alist) +va_dcl +{ + va_list ap; + char *fmt; + + (void)fprintf(stderr, "rsh: warning, using standard rsh: "); + va_start(ap); + fmt = va_arg(ap, char *); + vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, ".\n"); +} +#endif + +char * +copyargs(argv) + char **argv; +{ + int cc; + char **ap, *args, *p; + + cc = 0; + for (ap = argv; *ap; ++ap) + cc += strlen(*ap) + 1; + if (!(args = malloc((u_int)cc))) + err(1, NULL); + for (p = args, ap = argv; *ap; ++ap) { + (void)strcpy(p, *ap); + for (p = strcpy(p, *ap); *p; ++p); + if (ap[1]) + *p++ = ' '; + } + return (args); +} + +void +usage() +{ + + (void)fprintf(stderr, + "usage: rsh [-nd%s]%s[-l login] [login@]host [command]\n", +#ifdef KERBEROS +#ifdef CRYPT + "x", " [-k realm] "); +#else + "", " [-k realm] "); +#endif +#else + "", " "); +#endif + exit(1); +} diff --git a/usr.bin/sccs/Makefile b/usr.bin/sccs/Makefile new file mode 100644 index 00000000000..0ee9d1b59e2 --- /dev/null +++ b/usr.bin/sccs/Makefile @@ -0,0 +1,5 @@ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 + +PROG= sccs + +.include diff --git a/usr.bin/sccs/PSD.doc/Makefile b/usr.bin/sccs/PSD.doc/Makefile new file mode 100644 index 00000000000..4e8ebba0962 --- /dev/null +++ b/usr.bin/sccs/PSD.doc/Makefile @@ -0,0 +1,7 @@ +# @(#)Makefile 8.2 (Berkeley) 5/23/94 + +DIR= psd/14.sccs +SRCS= sccs.me +MACROS= -me + +.include diff --git a/usr.bin/sccs/PSD.doc/sccs.me b/usr.bin/sccs/PSD.doc/sccs.me new file mode 100644 index 00000000000..16dc3fbfb55 --- /dev/null +++ b/usr.bin/sccs/PSD.doc/sccs.me @@ -0,0 +1,1609 @@ +.\" Copyright (c) 1986, 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. +.\" +.\" @(#)sccs.me 8.2 (Berkeley) 6/1/94 +.\" +.eh '\fRPSD:14-%\fP''\fRAn Introduction to the Source Code Control System\fP' +.oh '\fRAn Introduction to the Source Code Control System\fP''\fRPSD:14-%\fP' +.ds S \s-1SCCS\s0 +.ds I \s-1SID\s0 +.nr bi 8n +.ev 1 \" only for keeps +.ss 16 +.ev +.\".he '\*S Introduction''%' +.+c +.(l C +.sz 14 +.b +An Introduction to the +Source Code Control System +.sz +.r +.sp +Eric Allman +.i "Project Ingres" +.i "University of California at Berkeley" +.)l +.sp 3 +.pp +.(f +This is version 1.21 of this document. +It was last modified on 12/5/80. +.)f +This document gives a quick introduction +to using the Source Code Control System +(\*S). +The presentation is geared to programmers +who are more concerned with +what +to do to get a task done +rather than how it works; +for this reason some of the examples +are not well explained. +For details of what the magic options do, +see the section on +.q "Further Information" . +.(l F +This is a working document. +Please send any comments or suggestions +to eric@Berkeley.Edu. +.)l +.sh 1 "Introduction" +.pp +\*S is a source management system. +Such a system maintains a record of versions of a system; +a record is kept with each set of changes +of what the changes are, +why they were made, +and who made them and when. +Old versions can be recovered, +and different versions can be maintained simultaneously. +In projects with more than one person, +\*S will insure that two people are not +editing the same file at the same time. +.pp +All versions of your program, +plus the log and other information, +is kept in a file called the +.q "s-file" . +There are three major operations +that can be performed on the s-file: +.np +Get a file for compilation (not for editing). +This operation retrieves a version of the file +from the s-file. +By default, the latest version is retrieved. +This file is intended for compilation, printing, or whatever; +it is specifically NOT intended to be edited +or changed in any way; +any changes made to a file retrieved +in this way will probably be lost. +.np +Get a file for editing. +This operation also retrieves a version of the file +from the s-file, +but this file is intended to be edited and then +incorporated back into the s-file. +Only one person may be editing a file at one time. +.np +Merge a file back into the s-file. +This is the companion operation to (2). +A new version number is assigned, +and comments are saved explaining why this change was made. +.sh 1 "Learning the Lingo" +.pp +There are a number of terms that are worth learning +before we go any farther. +.sh 2 "S-file" +.pp +The s-file +is a single file that holds all the different versions +of your file. +The s-file is stored in +differential format; +.i i.e. , +only the differences between versions are stored, +rather than the entire text of the new version. +This saves disk space +and allows selective changes to be removed later. +Also included in the s-file +is some header information for each version, +including the comments given by the person who +created the version explaining why the changes were made. +.sh 2 "Deltas" +.pp +Each set of changes to the s-file +(which is approximately [but not exactly!] equivalent +to a version of the file) +is called a +.i delta . +Although technically a delta only includes the +.i changes +made, +in practice +it is usual for +each delta to be made with respect to +all the deltas that have occurred before\**. +.(f +\**This matches normal usage, where the previous changes are not saved +at all, +so all changes are automatically based on all other changes +that have happened through history. +.)f +However, +it is possible to get a version of the file +that has selected deltas removed out of the middle +of the list of changes \*- +equivalent to removing your changes later. +.sh 2 "\*I's (or, version numbers)" +.pp +A \*I +(\*S Id) +is a number that represents a delta. +This is normally a two-part number +consisting of a +.q release +number and a +.q level +number. +Normally the release number stays the same, +however, +it is possible to move into a new release +if some major change is being made. +.pp +Since all past deltas are normally applied, +the \*I of the final delta applied +can be used to represent a version number of the file +as a whole. +.sh 2 "Id keywords" +.pp +When you get a version of a file +with intent to compile and install it +(\c +.i i.e. , +something other than edit it), +some special keywords are expanded inline +by \*S. +These +.i "Id Keywords" +can be used to include the current version number +or other information into the file. +All id keywords are of the form +.b % \c +.i x \c +.b % , +where +.i x +is an upper case letter. +For example, +.b %\&I\&% +is the \*I of the latest delta applied, +.b %\&W\&% +includes the module name, +\*I, +and a mark that makes it findable by a program, +and +.b %\&G\&% +is the date of the latest delta applied. +There are many others, +most of which are of dubious usefulness. +.pp +When you get a file for editing, +the id keywords are not expanded; +this is so that after you put them back in to the s-file, +they will be expanded automatically on each new version. +But notice: if you were to get them +expanded accidently, +then your file would appear to be the same version +forever more, +which would of course defeat the purpose. +Also, +if you should install a version of the program +without expanding the id keywords, +it will be impossible to tell what version it is +(since all it will have is +.q %\&W\&% +or whatever). +.sh 1 "Creating \*S Files" +.pp +To put source files +into +\*S +format, run the following shell script from csh: +.(b +mkdir SCCS save +foreach i (*.[ch]) + sccs admin \-i$i $i + mv $i save/$i +end +.)b +This will put the named files +into s-files +in the subdirectory +.q SCCS +The files will be removed from the current directory +and hidden away in the directory +.q save , +so the next thing you will probably want to do +is to get all the files +(described below). +When you are convinced that +\*S has correctly created the s-files, +you should remove the directory +.q save . +.pp +If you want to have id keywords in the files, +it is best to put them in before you create the s-files. +If you do not, +.i admin +will print +.q "No Id Keywords (cm7)" , +which is a warning message only. +.sh 1 "Getting Files for Compilation" +.pp +To get a copy of the latest version +of a file, +run +.(b +sccs get prog.c +.)b +\*S will respond: +.(b +1.1 +87 lines +.)b +meaning that version 1.1 was retrieved\** +.(f +\**Actually, +the \*I of the final delta applied was 1.1. +.)f +and that it has 87 lines. +The file +.i prog.c +will be created +in the current directory. +The file will be read-only +to remind you that you are not +supposed to change it. +.pp +This copy of the file +should not be changed, +since \*S is unable +to merge the changes +back into the s-file. +If you do make changes, +they will be lost the next time +someone does a +.i get . +.sh 1 "Changing Files (or, Creating Deltas)" +.sh 2 "Getting a copy to edit" +.pp +To edit a source file, +you must first get it, +requesting permission to edit it\**: +.(f +\**The +.q "edit" +command is equivalent to using the \-e +flag to +.i "get" , +as: +.(l +sccs get \-e prog.c +.)l +Keep this in mind when reading other documentation. +.)f +.(b +sccs edit prog.c +.)b +The response will be the same as with +.i get +except that it will also say: +.(b +New delta 1.2 +.)b +You then edit it, +using a standard text editor: +.(b +vi prog.c +.)b +.sh 2 "Merging the changes back into the s-file" +.pp +When the desired changes are made, +you can put your changes into the +\*S +file using the +.i delta +command: +.(b +sccs delta prog.c +.)b +.pp +Delta will prompt you for +.q "comments?" +before it merges the changes in. +At this prompt you should type a one-line description +of what the changes mean +(more lines can be entered by ending each line +except the last with a backslash\**). +.(f +\**Yes, this is a stupid default. +.)f +.i Delta +will then type: +.(b +1.2 +5 inserted +3 deleted +84 unchanged +.)b +saying that delta 1.2 was created, +and it inserted five lines, +removed three lines, +and left 84 lines unchanged\**. +.(f +\**Changes to a line are counted as a line deleted +and a line inserted. +.)f +The +.i prog.c +file will be removed; +it can be retrieved +using +.i get . +.sh 2 "When to make deltas" +.pp +It is probably unwise to make a delta +before every recompilation or test; +otherwise, +you tend to get a lot of deltas with comments like +.q "fixed compilation problem in previous delta" +or +.q "fixed botch in 1.3" . +However, +it is very important to delta everything +before installing a module for general use. +A good technique is to edit the files you need, +make all necessary changes and tests, +compiling and editing as often as necessary +without making deltas. +When you are satisfied that you have a working version, +delta everything being edited, +re-get them, +and recompile everything. +.sh 2 "What's going on: the info command" +.pp +To find out what files where being edited, +you can use: +.(b +sccs info +.)b +to print out all the files being edited +and other information such as the name of the user +who did the edit. +Also, +the command: +.(b +sccs check +.)b +is nearly equivalent to the +.i info +command, +except that it is silent if nothing is being edited, +and returns non-zero exit status if anything is being edited; +it can be used in an +.q install +entry in a makefile +to abort the install +if anything has not been properly deltaed. +.pp +If you know that everything being edited should be deltaed, +you can use: +.(b +sccs delta \`sccs tell\` +.)b +The +.i tell +command is similar to +.i info +except that only the names of files being edited +are output, +one per line. +.pp +All of these commands take a +.b \-b +flag +to ignore +.q branches +(alternate versions, described later) +and the +.b \-u +flag to only give files being edited by you. +The +.b \-u +flag takes an optional +.i user +argument, +giving only files being edited by that user. +For example, +.(b +sccs info \-ujohn +.)b +gives a listing of files being edited by john. +.sh 2 "ID keywords" +.pp +Id keywords can be inserted into your file +that will be expanded automatically by +.i get . +For example, +a line such as: +.(b +static char SccsId[] = "%\&W\&%\et%\&G\&%"; +.)b +will be replaced with something like: +.(b +static char SccsId[] = "@\&(#)prog.c 1.2 08/29/80"; +.)b +This tells you +the name and version +of the source file +and the time the delta was created. +The string +.q "@\&(#)" +is a special string +which signals the beginning +of an +\*S +Id keyword. +.sh 3 "The what command" +.pp +To find out what version of a program +is being run, +use: +.(b +sccs what prog.c /usr/bin/prog +.)b +which will print all strings +it finds that +begin with +.q "@\&(#)" . +This works on all types of files, +including binaries and libraries. +For example, the above command will output something like: +.(b +prog.c: + prog.c 1.2 08/29/80 +/usr/bin/prog: + prog.c 1.1 02/05/79 +.)b +From this I can see +that the source that I have in prog.c +will not compile into the same version +as the binary in /usr/bin/prog. +.sh 3 "Where to put id keywords" +.pp +ID keywords can be inserted anywhere, +including in comments, +but +Id Keywords that are compiled into the object module +are especially useful, +since it lets you find out what version of +the object is being run, +as well as the source. +However, +there is a cost: +data space is used up to store +the keywords, +and on small address space machines +this may be prohibitive. +.pp +When you put id keywords into header files, +it is important that you assign them to different variables. +For example, you might use: +.(b +static char AccessSid[] = "%\&W\&% %\&G\&%"; +.)b +in the file +.i access.h +and: +.(b +static char OpsysSid[] = "%\&W\&% %\&G\&%"; +.)b +in the file +.i opsys.h . +Otherwise, +you will get compilation errors because +.q SccsId +is redefined. +The problem with this is that if the header file +is included by many modules that are loaded together, +the version number of that header file is included +in the object module many times; +you may find it more to your taste +to put id keywords in header files +in comments. +.sh 2 "Keeping \*I's consistent across files" +.pp +With some care, +it is possible to keep the \*I's consistent +in multi-file systems. +The trick here is to always +.i edit +all files +at once. +The changes can then be made +to whatever files are necessary +and then all files +(even those not changed) +are redeltaed. +This can be done fairly easily +by just specifying the name of the directory +that the \*S files are in: +.(b +sccs edit SCCS +.)b +which will +.i edit +all files in that directory. +To make the delta, use: +.(b +sccs delta SCCS +.)b +You will be prompted for comments only once. +.sh 2 "Creating new releases" +.pp +When you want to create a new release +of a program, +you can specify the release number you want to create +on the +.i edit +command. +For example: +.(b +sccs edit \-r2 prog.c +.)b +will cause the next delta to be in release two +(that is, +it will be numbered 2.1). +Future deltas will automatically be in release two. +To change the release number +of an entire system, +use: +.(b +sccs edit \-r2 SCCS +.)b +.sh 1 "Restoring Old Versions" +.sh 2 "Reverting to old versions" +.pp +Suppose that after delta 1.2 +was stable +you made and released a delta 1.3. +But this introduced a bug, +so you made a delta 1.4 to correct it. +But 1.4 was still buggy, +and you decided you wanted to go back +to the old version. +You could +revert to delta 1.2 +by choosing the \*I in a get: +.(b +sccs get \-r1.2 prog.c +.)b +This will produce a version of +.i prog.c +that is delta 1.2 +that can be reinstalled so that work can proceed. +.pp +In some cases you don't know +what the \*I of the delta you want is. +However, +you can revert to the version of the program +that was running as of a certain date +by using the +.b \-c +(cutoff) flag. +For example, +.(b +sccs get \-c800722120000 prog.c +.)b +will retrieve whatever version was current +as of July 22, 1980 +at 12:00 noon. +Trailing components can be stripped off +(defaulting to their highest legal value), +and punctuation can be inserted in the obvious +places; +for example, +the above line could be equivalently stated: +.(b +sccs get \-c"80/07/22 12:00:00" prog.c +.)b +.sh 2 "Selectively deleting old deltas" +.pp +Suppose that you later decided +that you liked the changes in delta 1.4, +but that delta 1.3 should be removed. +You could do this by +.i excluding +delta 1.3: +.(b +sccs edit \-x1.3 prog.c +.)b +.ne 1i +When delta 1.5 is made, +it will include the changes made +in delta 1.4, +but will exclude the changes made +in delta 1.3. +You can exclude a range of deltas +using a dash. +For example, +if you want to get rid of 1.3 and 1.4 +you can use: +.(b +sccs edit \-x1.3\-1.4 prog.c +.)b +which will exclude all deltas from 1.3 to 1.4. +Alternatively, +.(b +sccs edit \-x1.3\-1 prog.c +.)b +will exclude a range of deltas +from 1.3 to the current highest delta in release 1. +.pp +In certain cases when using +.b \-x +(or +.b \-i ; +see below) +there will be conflicts +between versions; +for example, it may be necessary +to both include and delete +a particular line. +If this happens, +\*S always prints out a message +telling the range of lines effected; +these lines should then be examined very carefully +to see if the version \*S got +is ok. +.pp +Since each delta +(in the sense of +.q "a set of changes" ) +can be excluded at will, +that this makes it most useful +to put each semantically distinct change +into its own delta. +.sh 1 "Auditing Changes" +.sh 2 "The prt command" +.pp +When you created a delta, +you presumably gave a reason for the delta +to the +.q "comments?" +prompt. +To print out these comments later, +use: +.(b +sccs prt prog.c +.)b +This will produce +a report +for each delta +of the \*I, +time and date of creation, +user who created the delta, +number of lines inserted, deleted, and unchanged, +and the comments associated with the delta. +For example, the output of the above command might be: +.(b +D 1.2 80/08/29 12:35:31 bill 2 1 00005/00003/00084 +removed "-q" option +.sp \n(psu +D 1.1 79/02/05 00:19:31 eric 1 0 00087/00000/00000 +date and time created 80/06/10 00:19:31 by eric +.)b +.sh 2 "Finding why lines were inserted" +.pp +To find out +why you inserted lines, +you can get a copy of the file +with each line +preceded by the \*I that created it: +.(b +sccs get \-m prog.c +.)b +You can then find out +what this delta did +by printing the comments using +.i prt . +.pp +To find out what lines are associated with a particular delta +(\c +.i e.g. , +1.3), +use: +.(b +sccs get \-m \-p prog.c \(bv grep \'^1.3\' +.)b +The +.b \-p +flag causes \*S to output the generated source +to the standard output rather than to a file. +.sh 2 "Finding what changes you have made" +.pp +When you are editing a file, +you can find out what changes you have made using: +.(b +sccs diffs prog.c +.)b +Most of the ``diff'' flags can be used. +To pass the +.b \-c +flag, +use +.b \-C . +.pp +To compare two versions that are in deltas, +use: +.(b +sccs sccsdiff -r1.3 -r1.6 prog.c +.)b +to see the differences between delta 1.3 and delta 1.6. +.sh 1 "Shorthand Notations" +.pp +There are several sequences of commands that get +executed frequently. +.i Sccs +tries to make it easy to do these. +.sh 2 "Delget" +.pp +A frequent requirement is to make a delta of some file +and then get that file. +This can be done by using: +.(b +sccs delget prog.c +.)b +which is entirely equivalent to using: +.(b +sccs delta prog.c +sccs get prog.c +.)b +The +.q deledit +command is equivalent to +.q delget +except that the +.q edit +command is used +instead of the +.q get +command. +.sh 2 "Fix" +.pp +Frequently, there are small bugs +in deltas, +e.g., compilation errors, +for which there is no reason to maintain an audit trail. +To +.i replace +a delta, use: +.(b +sccs fix \-r1.4 prog.c +.)b +This will get a copy of delta 1.4 of prog.c for you to edit +and then delete delta 1.4 from the \*S file. +When you do a delta of prog.c, +it will be delta 1.4 again. +The \-r flag must be specified, +and the delta that is specified must be a leaf delta, +i.e., no other deltas may have been made subsequent +to the creation of that delta. +.sh 2 "Unedit" +.pp +If you found you edited a file +that you did not want to edit, +you can back out by using: +.(b +sccs unedit prog.c +.)b +.sh 2 "The \-d flag" +.pp +If you are working on a project +where the \*S code is in a directory somewhere, +you may be able to simplify things +by using a shell alias. +For example, +the alias: +.(b +alias syssccs sccs \-d/usr/src +.)b +will allow you to issue commands such as: +.(b +syssccs edit cmd/who.c +.)b +which will look for the file +.q "/usr/src/cmd/SCCS/who.c" . +The file +.q who.c +will always be created in your current directory +regardless of the value of the \-d flag. +.sh 1 "Using \*S on a Project" +.pp +Working on a project with several people +has its own set of special problems. +The main problem occurs when two people +modify a file at the same time. +\*S prevents this by locking an s-file +while it is being edited. +.pp +As a result, +files should not be reserved for editing +unless they are actually being edited at the time, +since this will prevent other people on the project +from making necessary changes. +For example, +a good scenario for working might be: +.(b +sccs edit a.c g.c t.c +vi a.c g.c t.c +# do testing of the (experimental) version +sccs delget a.c g.c t.c +sccs info +# should respond "Nothing being edited" +make install +.)b +.pp +As a general rule, +all source files should be deltaed +before installing the program for general use. +This will insure that it is possible +to restore any version in use at any time. +.sh 1 "Saving Yourself" +.sh 2 "Recovering a munged edit file" +.pp +Sometimes you may find +that you have destroyed or trashed +a file that you were trying to edit\**. +.(f +\**Or given up and decided to start over. +.)f +Unfortunately, +you can't just remove it +and re-\c +.i edit +it; +\*S keeps track +of the fact +that someone is trying to edit it, +so it won't let you do it again. +Neither can you just get it using +.i get , +since that would expand the Id keywords. +Instead, +you can say: +.(b +sccs get \-k prog.c +.)b +This will not expand the Id keywords, +so it is safe to do a delta +with it. +.pp +Alternately, +you can +.i unedit +and +.i edit +the file. +.sh 2 "Restoring the s-file" +.pp +In particularly bad circumstances, +the \*S file itself +may get munged. +The most common way this happens +is that it gets edited. +Since \*S keeps a checksum, +you will get errors every time you read the file. +To fix this checksum, use: +.(b +sccs admin \-z prog.c +.)b +.sh 1 "Using the Admin Command" +.pp +There are a number of parameters that can be set +using the +.i admin +command. +The most interesting of these are flags. +Flags can be added by using the +.b \-f +flag. +For example: +.(b +sccs admin \-fd1 prog.c +.)b +sets the +.q d +flag to the value +.q 1 . +This flag can be deleted by using: +.(b +sccs admin \-dd prog.c +.)b +The most useful flags are: +.nr ii 7n +.ip "b" +Allow branches to be made using the +\-b +flag to +.i edit . +.ip "d\fISID\fP" +Default \*I to be used on a +.i get +or +.i edit . +If this is just a release number +it constrains the +version +to a particular release only. +.ip "i" +Give a fatal error +if there are no Id Keywords in a file. +This is useful to guarantee that a version of the +file does not get merged into the s-file +that has the Id Keywords inserted as constants +instead of internal forms. +.ip "y" +The +.q type +of the module. +Actually, +the value of this flag is unused by \*S +except that it replaces the +.b %\&Y\&% +keyword. +.pp +The +.b \-t\fIfile\fR +flag can be used +to store descriptive text +from +.i file . +This descriptive text might be the documentation +or a design and implementation document. +Using the +.b \-t +flag insures that if the \*S file is sent, +the documentation will be sent also. +If +.i file +is omitted, +the descriptive text is deleted. +To see the descriptive text, +use +.q "prt \-t" . +.pp +The +.i admin +command can be used safely +any number of times on files. +A file need not be gotten +for +.i admin +to work. +.sh 1 "Maintaining Different Versions (Branches)" +.pp +Sometimes it is convenient +to maintain an experimental version of a program +for an extended period +while normal maintenance continues +on the version in production. +This can be done using a +.q branch. +Normally deltas continue in a straight line, +each depending on the delta before. +Creating a branch +.q "forks off" +a version of the program. +.pp +The ability to create branches +must be enabled in advance using: +.(b +sccs admin \-fb prog.c +.)b +The +.b \-fb +flag can be specified when the +\*S file is first created. +.sh 2 "Creating a branch" +.pp +To create a branch, use: +.(b +sccs edit \-b prog.c +.)b +This will create a branch +with (for example) \*I 1.5.1.1. +The deltas for this version +will be numbered +1.5.1.\c +.i n . +.sh 2 "Getting from a branch" +.pp +Deltas in a branch are normally not included +when you do a get. +To get these versions, +you will have to say: +.(b +sccs get \-r1.5.1 prog.c +.)b +.sh 2 "Merging a branch back into the main trunk" +.pp +At some point you will have finished the experiment, +and if it was successful +you will want to incorporate it into the release version. +But in the meantime +someone may have created a delta 1.6 +that you don't want to lose. +The commands: +.(b +sccs edit \-i1.5.1.1\-1.5.1 prog.c +sccs delta prog.c +.)b +will merge all of your changes +into the release system. +If some of the changes conflict, +get will print an error; +the generated result +should be carefully examined +before the delta is made. +.sh 2 "A more detailed example" +.pp +The following technique might be used +to maintain a different version of a program. +First, +create a directory to contain the new version: +.(b +mkdir ../newxyz +cd ../newxyz +.)b +Edit a copy of the program +on a branch: +.(b +sccs \-d../xyz edit prog.c +.)b +When using the old version, +be sure to use the +.b \-b +flag to info, check, tell, and clean +to avoid confusion. +For example, use: +.(b +sccs info \-b +.)b +when in the directory +.q xyz . +.pp +If you want to save a copy of the program +(still on the branch) +back in the s-file, +you can use: +.(b +sccs -d../xyz deledit prog.c +.)b +which will do a delta on the branch +and reedit it for you. +.pp +When the experiment is complete, merge it back into the s-file +using delta: +.(b +sccs -d../xyz delta prog.c +.)b +At this point you must decide whether this version +should be merged back into the trunk +(\c +.i i.e. +the default version), +which may have undergone changes. +If so, it can be merged using the +.b \-i +flag to +.i edit +as described above. +.sh 2 "A warning" +.pp +Branches should be kept to a minimum. +After the first branch from the trunk, +\*I's are assigned rather haphazardly, +and the structure gets complex fast. +.sh 1 "Using \*S with Make" +.pp +\*S and make can be made to work together +with a little care. +A few sample makefiles +for common applications are shown. +.pp +There are a few basic entries that every makefile +ought to have. +These are: +.nr ii 1i +.ip a.out +(or whatever the makefile generates.) +This entry regenerates whatever this makefile is +supposed to regenerate. +If the makefile regenerates many things, +this should be called +.q all +and should in turn +have dependencies on everything +the makefile can generate. +.ip install +Moves the objects to the final +resting place, +doing any special +.i chmod 's +or +.i ranlib 's +as appropriate. +.ip sources +Creates all the source files from \*S files. +.ip clean +Removes all files from the current directory +that can be regenerated from \*S files. +.ip print +Prints the contents of the directory. +.lp +The examples shown below are only partial examples, +and may omit some of these entries +when they are deemed to be obvious. +.pp +The +.i clean +entry should not remove files that can be +regenerated from the \*S files. +It is sufficiently important to have the +source files around at all times +that the only time they should be removed +is when the directory is being mothballed. +To do this, the command: +.(b +sccs clean +.)b +can be used. +This will remove all files for which an s-file +exists, +but which is not being edited. +.sh 2 "To maintain single programs" +.pp +Frequently there are directories with several +largely unrelated programs +(such as simple commands). +These can be put into a single makefile: +.(b +LDFLAGS= \-i \-s +.sp \n(psu +prog: prog.o + $(CC) $(LDFLAGS) \-o prog prog.o +prog.o: prog.c prog.h +.sp \n(psu +example: example.o + $(CC) $(LDFLAGS) \-o example example.o +example.o: example.c +.sp \n(psu +\&.DEFAULT: + sccs get $< +.)b +The trick here +is that the .DEFAULT rule +is called every time +something is needed +that does not exist, +and no other rule exists to make it. +The explicit dependency of the +.b \&.o +file on the +.b \&.c +file is important. +Another way of doing the same thing is: +.(b +SRCS= prog.c prog.h example.c +.sp \n(psu +LDFLAGS= \-i \-s +.sp \n(psu +prog: prog.o + $(CC) $(LDFLAGS) \-o prog prog.o +prog.o: prog.h +.sp \n(psu +example: example.o + $(CC) $(LDFLAGS) \-o example example.o +.sp \n(psu +sources: $(SRCS) +$(SRCS): + sccs get $@ +.)b +There are a couple of advantages to this approach: +(1) the explicit dependencies of the .o on the .c files are +not needed, +(2) there is an entry called "sources" so if you want to get +all the sources you can just say +.q "make sources" , +and +(3) the makefile is less likely to do confusing things +since it won't try to +.i get +things that do not exist. +.sh 2 "To maintain a library" +.pp +Libraries that are largely static +are best updated using explicit commands, +since +.i make +doesn't know about updating them properly. +However, +libraries that are in the process of being developed +can be handled quite adequately. +The problem is that the .o files +have to be kept out of the library +as well as in the library. +.(b +# configuration information +OBJS= a.o b.o c.o d.o +SRCS= a.c b.c c.c d.s x.h y.h z.h +TARG= /usr/lib +.sp \n(psu +# programs +GET= sccs get +REL= +AR= \-ar +RANLIB= ranlib +.sp \n(psu +lib.a: $(OBJS) + $(AR) rvu lib.a $(OBJS) + $(RANLIB) lib.a +.sp \n(psu +install: lib.a + sccs check + cp lib.a $(TARG)/lib.a + $(RANLIB) $(TARG)/lib.a +.sp \n(psu +sources: $(SRCS) +$(SRCS): + $(GET) $(REL) $@ +.sp \n(psu +print: sources + pr *.h *.[cs] +clean: + rm \-f *.o + rm \-f core a.out $(LIB) +.)b +.pp +The +.q "$(REL)" +in the get +can be used to get old versions +easily; for example: +.(b +make b.o REL=\-r1.3 +.)b +.pp +The +.i install +entry includes the line +.q "sccs check" +before anything else. +This guarantees that all the s-files +are up to date +(\c +.i i.e. , +nothing is being edited), +and will abort the +.i make +if this condition is not met. +.sh 2 "To maintain a large program" +.(b +OBJS= a.o b.o c.o d.o +SRCS= a.c b.c c.y d.s x.h y.h z.h +.sp \n(psu +GET= sccs get +REL= +.sp \n(psu +a.out: $(OBJS) + $(CC) $(LDFLAGS) $(OBJS) $(LIBS) +.sp \n(psu +sources: $(SRCS) +$(SRCS): + $(GET) $(REL) $@ +.)b +(The +.i print +and +.i clean +entries are identical to the previous case.) +This makefile requires copies of the source and object files +to be kept during development. +It is probably also wise to include lines of the form: +.(b +a.o: x.h y.h +b.o: z.h +c.o: x.h y.h z.h +z.h: x.h +.)b +so that modules will be recompiled +if header files change. +.pp +Since +.i make +does not do transitive closure on dependencies, +you may find in some makefiles lines like: +.(b +z.h: x.h + touch z.h +.)b +This would be used in cases where file z.h +has a line: +.(b +#include "x.h" +.)b +in order to bring the mod date of z.h in line +with the mod date of x.h. +When you have a makefile such as above, +the +.i touch +command can be removed completely; +the equivalent effect will be achieved +by doing an automatic +.i get +on z.h. +.sh 1 "Further Information" +.pp +The +.i "SCCS/PWB User's Manual" +gives a deeper description +of how to use \*S. +Of particular interest +are the numbering of branches, +the l-file, +which gives a description of what deltas were used on a get, +and certain other \*S commands. +.pp +The \*S manual pages +are a good last resort. +These should be read by software managers +and by people who want to know +everything about everything. +.pp +Both of these documents were written without the +.i sccs +front end in mind, +so most of the examples are slightly different from those +in this document. +.bp +.sz 12 +.ce +.b "Quick Reference" +.sz +.sp 2 +.sh 1 Commands 1 +.pp +The following commands should all be preceded with +.q sccs . +This list is not exhaustive; +for more options see +.i "Further Information" . +.ip get 9n +Gets files for compilation (not for editing). +Id keywords are expanded. +.ba 9n +.nr ii 8n +.ip \-r\fI\*I\fP +Version to get. +.ip \-p +Send to standard output rather than to the actual file. +.ip \-k +Don't expand id keywords. +.ip \-i\fIlist\fP +List of deltas to include. +.ip \-x\fIlist\fP +List of deltas to exclude. +.ip \-m +Precede each line with \*I of creating delta. +.ip \-c\fIdate\fP +Don't apply any deltas created after +.i date. +.ba +.ip edit 9n +Gets files for editing. +Id keywords are not expanded. +Should be matched with a +.i delta +command. +.ba 9n +.nr ii 8n +.ip \-r\fI\*I\fP +Same as +.i get . +If +.i \*I +specifies a release that does not yet exist, +the highest numbered delta is retrieved +and the new delta is numbered with +.i \*I . +.ip \-b +Create a branch. +.ip \-i\fIlist\fP +Same as +.i get . +.ip \-x\fIlist\fP +Same as +.i get . +.ba +.ip delta 9n +Merge a file gotten using +.i edit +back into the s-file. +Collect comments about why this delta was made. +.ip unedit 9n +Remove a file that has been edited previously +without merging the changes into the s-file. +.ip prt 9n +Produce a report of changes. +.ba 9n +.nr ii 5n +.ip \-t +Print the descriptive text. +.ip \-e +Print (nearly) everything. +.ba +.ip info 9n +Give a list of all files being edited. +.ba 9n +.nr ii 5n +.ip \-b +Ignore branches. +.ip \-u[\fIuser\fP] +Ignore files not being edited by +.i user . +.ba +.ip check 9n +Same as +.i info , +except that nothing is printed if nothing is being edited +and exit status is returned. +.ip tell 9n +Same as +.i info , +except that one line is produced per file being edited containing +only the file name. +.ip clean 9n +Remove all files that can be regenerated from the +s-file. +.ip what 9n +Find and print id keywords. +.ip admin 9n +Create or set parameters on s-files. +.ba 9n +.nr ii 8n +.ip \-i\fIfile\fP +Create, using +.i file +as the initial contents. +.ip \-z +Rebuild the checksum in case +the file has been trashed. +.ip \-f\fIflag\fP +Turn on the +.i flag . +.ip \-d\fIflag\fP +Turn off (delete) the +.i flag . +.ip \-t\fIfile\fP +Replace the descriptive text +in the s-file with the contents of +.i file . +If +.i file +is omitted, +the text is deleted. +Useful for storing documentation +or +.q "design & implementation" +documents to insure they get distributed with the +s-file. +.lp +Useful flags are: +.ip b +Allow branches to be made using the \-b flag to +.i edit. +.ip d\fI\*I\fP +Default \*I to be used +on a +.i get +or +.i edit . +.ip i +Cause +.q "No Id Keywords" +error message +to be a fatal error rather than a warning. +.ip t +The module +.q type ; +the value of this flag replaces the +.b %\&Y\&% +keyword. +.ba +.ip fix 9n +Remove a delta and reedit it. +.ip delget 9n +Do a +.i delta +followed by a +.i get . +.ip deledit 9n +Do a +.i delta +followed by an +.i edit . +.sh 1 "Id Keywords" +.nr ii 6n +.ip "%\&Z\&%" +Expands to +.q @\&(#) +for the +.i what +command to find. +.ip "%\&M\&%" +The current module name, +.i e.g., +.q prog.c . +.ip "%\&I\&%" +The highest \*I applied. +.ip "%\&W\&%" +A shorthand for +.q "%\&Z\&%%\&M\&% %\&I\&%" . +.ip "%\&G\&%" +The date of the delta +corresponding to the +.q "%\&I\&%" +keyword. +.ip "%\&R\&%" +The current release number, +.i i.e. , +the first component of the +.q "%\&I\&%" +keyword. +.ip "%\&Y\&%" +Replaced by the value of the +.b t +flag +(set by +.i admin ). diff --git a/usr.bin/sccs/PSD.doc/spell.ok b/usr.bin/sccs/PSD.doc/spell.ok new file mode 100644 index 00000000000..fb2fe247b9b --- /dev/null +++ b/usr.bin/sccs/PSD.doc/spell.ok @@ -0,0 +1,77 @@ +AccessSid +Admin +Allman +Berkeley.Edu +Delget +Ingres +LDFLAGS +LIB +LIBS +OBJS +OpsysSid +PS1:14 +PWB +REL +SCCS +SID +SRCS +Sccs +SccsId +System''PS1:14 +TARG +a.c +a.o +a.out +access.h +admin +b.c +b.o +backslash +bi +c.c +c.o +c.y +ch +cm7 +cmd +cs +d.o +d.s +deledit +delget +eric +example.c +example.o +fb +fd1 +foreach +g.c +info +inline +john +lib +lib.a +makefile +makefiles +mod +mothballed +newxyz +ok +opsys.h +prog +prog.c +prog.h +prog.o +prt +rvu +sccs +sccsdiff +src +syssccs +t.c +ujohn +who.c +x.h +xyz +y.h +z.h diff --git a/usr.bin/sccs/pathnames.h b/usr.bin/sccs/pathnames.h new file mode 100644 index 00000000000..4da6874d334 --- /dev/null +++ b/usr.bin/sccs/pathnames.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/6/93 + */ + +#include + +#define _PATH_SCCSADMIN "/usr/local/bin/admin" +#define _PATH_SCCSBDIFF "/usr/local/bin/bdiff" +#define _PATH_SCCSCOMB "/usr/local/bin/comb" +#define _PATH_SCCSDELTA "/usr/local/bin/delta" +#define _PATH_SCCSDIFF "/usr/local/bin/sccsdiff" +#define _PATH_SCCSGET "/usr/local/bin/get" +#define _PATH_SCCSHELP "/usr/local/bin/help" +#define _PATH_SCCSPRS "/usr/local/bin/prs" +#define _PATH_SCCSPRT "/usr/local/bin/prt" +#define _PATH_SCCSRMDEL "/usr/local/bin/rmdel" +#define _PATH_SCCSVAL "/usr/local/bin/val" +#define _PATH_SCCSWHAT "/usr/local/bin/what" +#undef _PATH_TMP +#define _PATH_TMP "/tmp/sccsXXXXX" diff --git a/usr.bin/sccs/sccs.1 b/usr.bin/sccs/sccs.1 new file mode 100644 index 00000000000..7f4990c34d1 --- /dev/null +++ b/usr.bin/sccs/sccs.1 @@ -0,0 +1,398 @@ +.\" Copyright (c) 1983, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)sccs.1 8.1 (Berkeley) 6/6/93 +.\" +.Dd June 6, 1993 +.Dt SCCS 1 +.Os BSD 4.2 +.Sh NAME +.Nm sccs +.Nd front end for the +.Li SCCS +subsystem +.Sh SYNOPSIS +.Nm sccs +.Op Fl r +.Op Fl d Ar path +.Op Fl p Ar path +.Ar command +.Op flags +.Op Ar +.Sh DESCRIPTION +.Nm Sccs +is a front end to the +.Li SCCS +programs +that +helps them mesh more cleanly +with +the rest of UNIX. +It +also includes the capability to run +.Dq set user id +to another user +to +provide additional protection. +.Pp +Basically, +.Nm sccs +runs the command with the specified +.Ar flags +and +.Ar args . +Each argument is normally modified to be prepended with +.Dq Li SCCS/s. . +.Pp +Flags to be interpreted by the +.Nm sccs +program must be before the +.Ar command +argument. +Flags to be passed to the actual +.Li SCCS +program must come after the +.Ar command +argument. +These flags are specific to the command and +are discussed in the documentation for that command. +.Pp +Besides the usual +.Li SCCS +commands, +several +.Dq pseudo-commands +can be issued. +These are: +.Bl -tag -width deledit +.It Cm edit +Equivalent +to +.Dq Li get \-e . +.It Cm delget +Perform a delta on the named files and +then get new versions. +The new versions will have id keywords expanded, and +will not be editable. +The +.Fl m , +.Fl p , +.Fl r , +.Fl s , +and +.Fl y +flags will be passed to +.Nm delta , +and the +.Fl b, +.Fl c , +.Fl e , +.Fl i , +.Fl k , +.Fl l , +.Fl s , +.\" anybody who has a bad xterm which is almost anyone +and +.Fl x +flags will be passed to get. +.It Cm deledit +Equivalent +to +.Nm delget +except that the +.Nm get +phase includes the +.Fl e +flag. +This +option is useful for making a +.Em checkpoint +of your current editing phase. The same flags will be passed to delta +as described above, and +all the flags listed for +.om get +above except +.Fl e +and +.Fl k +are +passed to +.Nm edit . +.It Cm create +Creates +an +.Li SCCS +file , +taking +the initial contents from the file of the same name. +Any +flags to +.Nm admin +are accepted. If the creation is successful, +the files are renamed with a comma on the front. +These should be removed when you are convinced that the +.Li SCCS +files +have been created successfully. +.It Cm fix +Must +be followed by a +.Fl r +flag. +This command essentially removes the named delta, but +leaves you with a copy of the delta +with the changes that were in it. It +is useful for fixing small compiler bugs, etc. +Since it doesn't leave audit trails, it should be used carefully. +.It Cm clean +This routine removes everything from the current directory +that can be recreated from SCCS files. +It will not remove any files being edited. +If the +.Fl b +flag is given, branches are ignored in the determination of +whether they are being edited; this +is dangerous if you are keeping the branches in the +same directory. +.It Cm unedit +This +is the opposite of an +.Nm edit +or +a +.Dq Li get \-e . +It should be used with extreme caution, since +any changes you made since the get will be irretrievably lost. +.It Cm info +Gives a listing of all files being edited. +If the +.Fl b +flag +is given, branches (i.e., +.Li SID Ns \&\'s +with two or fewer components) +are ignored. If the +.Fl u +flag is given (with an optional argument) then +only files being edited by you (or the named user) are listed. +.It Cm check +Like +.Nm info +except that nothing is printed if nothing is being edited, and +a non-zero exit status is returned if anything is being edited. +The intent is to have this included in an +.Em install +entry in a makefile to insure that everything is included into the +.Li SCCS +file before a version is installed. +.It Cm tell +Gives a newline-separated list of the files being edited +on the standard output. Takes the +.Fl b +and +.Fl u +flags like +.Nm info +and +.Nm check . +.It Cm diffs +Gives a +.Nm diff +listing between the current version of the +program(s) you have out for editing and the versions in +.Li SCCS +format. +The +.Fl r , +.Fl c , +.Fl i , +.Fl x , +and +.Fl t +flags are passed to +.if n \{\ +. br +.\} +.Nm get ; +the +.Fl l , +.Fl s , +.Fl e , +.Fl f , +.Fl h , +and +.Fl b +options are passed to +.if n \{\ +. br +.\} +.Nm diff . +The +.Fl C +flag is passed to +.Nm diff +as +.Fl c . +.It Cm print +This command prints out verbose information +about the named files. +.Pp +.It Fl r +Runs +.Nm sccs +as the real user rather than as whatever effective user +.Nm sccs +is +.Dq Li set user id +to. +.It Fl d +Specifies a root directory for the +.Li SCCS +files. +The default is the current directory. +If environment variable +.Ev PROJECT +is set, +it will be used to determine the +.Fl d +flag. +.It Fl p +Defines the pathname of the directory in which the +.Li SCCS +files will be found; +.Dq Li SCCS +is the default. +The +.Fl p +flag +differs from the +.Fl d +flag +in that the +.Fl d +argument is prepended to the entire pathname and the +.Fl p +argument is inserted before the final component of the pathname. +For example, +.Dq Li sccs \-d/x \-py get a/b +will convert to +.Dq Li get /x/a/y/s.b . +The intent here is to create aliases such as +.Dq Li alias syssccs sccs -d/usr/src +which +will be used as +.Dq Li syssccs get cmd/who.c . +.Pp +Certain +commands (such as +.Nm admin ) +cannot be run +.Dq Li set user id +by all users, since this would allow anyone to change the authorizations. +These commands are always run as the real user. +.Sh EXAMPLES +To get a file for editing, +edit it, +and produce a new delta: +.Pp +.Dl sccs get \-e file.c +.Dl ex file.c +.Dl sccs delta file.c +.Pp +To get a file from another directory: +.Pp +.Dl sccs \-p/usr/src/sccs/s. get cc.c +.Pp +or +.Pp +.Dl sccs get /usr/src/sccs/s.cc.c +.Pp +To make a delta of a large number of files +in the current directory: +.Pp +.Dl sccs delta *.c +.Pp +To get a list of files being edited that are not on branches: +.Pp +.Dl sccs info \-b +.Pp +To delta everything being edited by you: +.Pp +.Dl sccs delta \`sccs tell \-u\` +.Pp +In a makefile, to get source files +from an +.Li SCCS +file if it does not already exist: +.Pp +.Dl SRCS = +.Dl $(SRCS): +.Dl \&\tsccs get $(REL) $@ +.Sh ENVIRONMENT +.Bl -tag -width Ar +.It Ev PROJECT +The PROJECT environment variable is checked by the +.Fl d +flag. If +it begins with a slash, it is taken directly; otherwise, +the home directory of a user of that name is +examined for a subdirectory +.Dq Li src +or +.Dq Li source . +If such a directory is found, it is used. +.El +.Sh SEE ALSO +.Xr what 1 +.Xr admin SCCS , +.Xr chghist SCCS , +.Xr comb SCCS , +.Xr delta SCCS , +.Xr get SCCS , +.Xr help SCCS , +.Xr prt SCCS , +.Xr rmdel SCCS , +.Xr sccsdiff SCCS , +.Rs +.%A Eric Allman +.%T "An Introduction to the Source Code Control System" +.Re +.Sh HISTORY +The +.Nm sccs +command +appeared in +.Bx 4.3 . +.Sh BUGS +It should be able to take directory arguments on pseudo-commands +like the +.Li SCCS +commands do. diff --git a/usr.bin/sccs/sccs.c b/usr.bin/sccs/sccs.c new file mode 100644 index 00000000000..2dfd76d4bd4 --- /dev/null +++ b/usr.bin/sccs/sccs.c @@ -0,0 +1,1621 @@ +/* + * 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1980, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)sccs.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pathnames.h" + +/* +** SCCS.C -- human-oriented front end to the SCCS system. +** +** Without trying to add any functionality to speak of, this +** program tries to make SCCS a little more accessible to human +** types. The main thing it does is automatically put the +** string "SCCS/s." on the front of names. Also, it has a +** couple of things that are designed to shorten frequent +** combinations, e.g., "delget" which expands to a "delta" +** and a "get". +** +** This program can also function as a setuid front end. +** To do this, you should copy the source, renaming it to +** whatever you want, e.g., "syssccs". Change any defaults +** in the program (e.g., syssccs might default -d to +** "/usr/src/sys"). Then recompile and put the result +** as setuid to whomever you want. In this mode, sccs +** knows to not run setuid for certain programs in order +** to preserve security, and so forth. +** +** Usage: +** sccs [flags] command [args] +** +** Flags: +** -d represents a directory to search +** out of. It should be a full pathname +** for general usage. E.g., if is +** "/usr/src/sys", then a reference to the +** file "dev/bio.c" becomes a reference to +** "/usr/src/sys/dev/bio.c". +** -p prepends to the final component +** of the pathname. By default, this is +** "SCCS". For example, in the -d example +** above, the path then gets modified to +** "/usr/src/sys/dev/SCCS/s.bio.c". In +** more common usage (without the -d flag), +** "prog.c" would get modified to +** "SCCS/s.prog.c". In both cases, the +** "s." gets automatically prepended. +** -r run as the real user. +** +** Commands: +** admin, +** get, +** delta, +** rmdel, +** cdc, +** etc. Straight out of SCCS; only difference +** is that pathnames get modified as +** described above. +** enter Front end doing "sccs admin -i " +** create Macro for "enter" followed by "get". +** edit Macro for "get -e". +** unedit Removes a file being edited, knowing +** about p-files, etc. +** delget Macro for "delta" followed by "get". +** deledit Macro for "delta" followed by "get -e". +** branch Macro for "get -b -e", followed by "delta +** -s -n", followd by "get -e -t -g". +** diffs "diff" the specified version of files +** and the checked-out version. +** print Macro for "prs -e" followed by "get -p -m". +** tell List what files are being edited. +** info Print information about files being edited. +** clean Remove all files that can be +** regenerated from SCCS files. +** check Like info, but return exit status, for +** use in makefiles. +** fix Remove a top delta & reedit, but save +** the previous changes in that delta. +** +** Compilation Flags: +** UIDUSER -- determine who the user is by looking at the +** uid rather than the login name -- for machines +** where SCCS gets the user in this way. +** SCCSDIR -- if defined, forces the -d flag to take on +** this value. This is so that the setuid +** aspects of this program cannot be abused. +** This flag also disables the -p flag. +** SCCSPATH -- the default for the -p flag. +** MYNAME -- the title this program should print when it +** gives error messages. +** +** Compilation Instructions: +** cc -O -n -s sccs.c +** The flags listed above can be -D defined to simplify +** recompilation for variant versions. +** +** Author: +** Eric Allman, UCB/INGRES +** Copyright 1980 Regents of the University of California +*/ + + +/******************* Configuration Information ********************/ + +# ifndef SCCSPATH +# define SCCSPATH "SCCS" /* pathname in which to find s-files */ +# endif NOT SCCSPATH + +# ifndef MYNAME +# define MYNAME "sccs" /* name used for printing errors */ +# endif NOT MYNAME + +/**************** End of Configuration Information ****************/ + +typedef char bool; +# define TRUE 1 +# define FALSE 0 + +# define bitset(bit, word) ((bool) ((bit) & (word))) + +struct sccsprog +{ + char *sccsname; /* name of SCCS routine */ + short sccsoper; /* opcode, see below */ + short sccsflags; /* flags, see below */ + char *sccspath; /* pathname of binary implementing */ +}; + +/* values for sccsoper */ +# define PROG 0 /* call a program */ +# define CMACRO 1 /* command substitution macro */ +# define FIX 2 /* fix a delta */ +# define CLEAN 3 /* clean out recreatable files */ +# define UNEDIT 4 /* unedit a file */ +# define SHELL 5 /* call a shell file (like PROG) */ +# define DIFFS 6 /* diff between sccs & file out */ +# define DODIFF 7 /* internal call to diff program */ +# define ENTER 8 /* enter new files */ + +/* bits for sccsflags */ +# define NO_SDOT 0001 /* no s. on front of args */ +# define REALUSER 0002 /* protected (e.g., admin) */ + +/* modes for the "clean", "info", "check" ops */ +# define CLEANC 0 /* clean command */ +# define INFOC 1 /* info command */ +# define CHECKC 2 /* check command */ +# define TELLC 3 /* give list of files being edited */ + +/* +** Description of commands known to this program. +** First argument puts the command into a class. Second arg is +** info regarding treatment of this command. Third arg is a +** list of flags this command accepts from macros, etc. Fourth +** arg is the pathname of the implementing program, or the +** macro definition, or the arg to a sub-algorithm. +*/ + +struct sccsprog SccsProg[] = { + "admin", PROG, REALUSER, _PATH_SCCSADMIN, + "cdc", PROG, 0, _PATH_SCCSRMDEL, + "comb", PROG, 0, _PATH_SCCSCOMB, + "delta", PROG, 0, _PATH_SCCSDELTA, + "get", PROG, 0, _PATH_SCCSGET, + "help", PROG, NO_SDOT, _PATH_SCCSHELP, + "prs", PROG, 0, _PATH_SCCSPRS, + "prt", PROG, 0, _PATH_SCCSPRT, + "rmdel", PROG, REALUSER, _PATH_SCCSRMDEL, + "val", PROG, 0, _PATH_SCCSVAL, + "what", PROG, NO_SDOT, _PATH_SCCSWHAT, + "sccsdiff", SHELL, REALUSER, _PATH_SCCSDIFF, + "edit", CMACRO, NO_SDOT, "get -e", + "delget", CMACRO, NO_SDOT, "delta:mysrp/get:ixbeskcl -t", + "deledit", CMACRO, NO_SDOT, + "delta:mysrp -n/get:ixbskcl -e -t -g", + "fix", FIX, NO_SDOT, NULL, + "clean", CLEAN, REALUSER|NO_SDOT, + (char *) CLEANC, + "info", CLEAN, REALUSER|NO_SDOT, + (char *) INFOC, + "check", CLEAN, REALUSER|NO_SDOT, + (char *) CHECKC, + "tell", CLEAN, REALUSER|NO_SDOT, + (char *) TELLC, + "unedit", UNEDIT, NO_SDOT, NULL, + "diffs", DIFFS, NO_SDOT|REALUSER, + NULL, + "-diff", DODIFF, NO_SDOT|REALUSER, + _PATH_SCCSBDIFF, + "print", CMACRO, 0, "prs -e/get -p -m -s", + "branch", CMACRO, NO_SDOT, + "get:ixrc -e -b/delta: -s -n -ybranch-place-holder/get:pl -e -t -g", + "enter", ENTER, NO_SDOT, NULL, + "create", CMACRO, NO_SDOT, "enter/get:ixbeskcl -t", + NULL, -1, 0, NULL +}; + +/* one line from a p-file */ +struct pfile +{ + char *p_osid; /* old SID */ + char *p_nsid; /* new SID */ + char *p_user; /* user who did edit */ + char *p_date; /* date of get */ + char *p_time; /* time of get */ + char *p_aux; /* extra info at end */ +}; + +char *SccsPath = SCCSPATH; /* pathname of SCCS files */ +# ifdef SCCSDIR +char *SccsDir = SCCSDIR; /* directory to begin search from */ +# else +char *SccsDir = ""; +# endif +char MyName[] = MYNAME; /* name used in messages */ +int OutFile = -1; /* override output file for commands */ +bool RealUser; /* if set, running as real user */ +# ifdef DEBUG +bool Debug; /* turn on tracing */ +# endif +# ifndef V6 +extern char *getenv(); +# endif V6 + +char *gstrcat(), *strcat(); +char *gstrncat(), *strncat(); +char *gstrcpy(), *strcpy(); +#define FBUFSIZ BUFSIZ +#define PFILELG 120 + +main(argc, argv) + int argc; + char **argv; +{ + register char *p; + extern struct sccsprog *lookup(); + register int i; +# ifndef V6 +# ifndef SCCSDIR + register struct passwd *pw; + extern struct passwd *getpwnam(); + char buf[FBUFSIZ]; + + /* pull "SccsDir" out of the environment (possibly) */ + p = getenv("PROJECTDIR"); + if (p != NULL && p[0] != '\0') + { + if (p[0] == '/') + SccsDir = p; + else + { + pw = getpwnam(p); + if (pw == NULL) + { + usrerr("user %s does not exist", p); + exit(EX_USAGE); + } + gstrcpy(buf, pw->pw_dir, sizeof(buf)); + gstrcat(buf, "/src", sizeof(buf)); + if (access(buf, 0) < 0) + { + gstrcpy(buf, pw->pw_dir, sizeof(buf)); + gstrcat(buf, "/source", sizeof(buf)); + if (access(buf, 0) < 0) + { + usrerr("project %s has no source!", p); + exit(EX_USAGE); + } + } + SccsDir = buf; + } + } +# endif SCCSDIR +# endif V6 + + /* + ** Detect and decode flags intended for this program. + */ + + if (argc < 2) + { + fprintf(stderr, "Usage: %s [flags] command [flags]\n", MyName); + exit(EX_USAGE); + } + argv[argc] = NULL; + + if (lookup(argv[0]) == NULL) + { + while ((p = *++argv) != NULL) + { + if (*p != '-') + break; + switch (*++p) + { + case 'r': /* run as real user */ + setuid(getuid()); + RealUser++; + break; + +# ifndef SCCSDIR + case 'p': /* path of sccs files */ + SccsPath = ++p; + if (SccsPath[0] == '\0' && argv[1] != NULL) + SccsPath = *++argv; + break; + + case 'd': /* directory to search from */ + SccsDir = ++p; + if (SccsDir[0] == '\0' && argv[1] != NULL) + SccsDir = *++argv; + break; +# endif + +# ifdef DEBUG + case 'T': /* trace */ + Debug++; + break; +# endif + + default: + usrerr("unknown option -%s", p); + break; + } + } + if (SccsPath[0] == '\0') + SccsPath = "."; + } + + i = command(argv, FALSE, ""); + exit(i); +} + +/* +** COMMAND -- look up and perform a command +** +** This routine is the guts of this program. Given an +** argument vector, it looks up the "command" (argv[0]) +** in the configuration table and does the necessary stuff. +** +** Parameters: +** argv -- an argument vector to process. +** forkflag -- if set, fork before executing the command. +** editflag -- if set, only include flags listed in the +** sccsklets field of the command descriptor. +** arg0 -- a space-seperated list of arguments to insert +** before argv. +** +** Returns: +** zero -- command executed ok. +** else -- error status. +** +** Side Effects: +** none. +*/ + +command(argv, forkflag, arg0) + char **argv; + bool forkflag; + char *arg0; +{ + register struct sccsprog *cmd; + register char *p; + char buf[FBUFSIZ]; + extern struct sccsprog *lookup(); + char *nav[1000]; + char **np; + register char **ap; + register int i; + register char *q; + extern bool unedit(); + int rval = 0; + extern char *index(); + extern char *makefile(); + char *editchs; + extern char *tail(); + +# ifdef DEBUG + if (Debug) + { + printf("command:\n\t\"%s\"\n", arg0); + for (np = argv; *np != NULL; np++) + printf("\t\"%s\"\n", *np); + } +# endif + + /* + ** Copy arguments. + ** Copy from arg0 & if necessary at most one arg + ** from argv[0]. + */ + + np = ap = &nav[1]; + editchs = NULL; + for (p = arg0, q = buf; *p != '\0' && *p != '/'; ) + { + *np++ = q; + while (*p == ' ') + p++; + while (*p != ' ' && *p != '\0' && *p != '/' && *p != ':') + *q++ = *p++; + *q++ = '\0'; + if (*p == ':') + { + editchs = q; + while (*++p != '\0' && *p != '/' && *p != ' ') + *q++ = *p; + *q++ = '\0'; + } + } + *np = NULL; + if (*ap == NULL) + *np++ = *argv++; + + /* + ** Look up command. + ** At this point, *ap is the command name. + */ + + cmd = lookup(*ap); + if (cmd == NULL) + { + usrerr("Unknown command \"%s\"", *ap); + return (EX_USAGE); + } + + /* + ** Copy remaining arguments doing editing as appropriate. + */ + + for (; *argv != NULL; argv++) + { + p = *argv; + if (*p == '-') + { + if (p[1] == '\0' || editchs == NULL || index(editchs, p[1]) != NULL) + *np++ = p; + } + else + { + if (!bitset(NO_SDOT, cmd->sccsflags)) + p = makefile(p); + if (p != NULL) + *np++ = p; + } + } + *np = NULL; + + /* + ** Interpret operation associated with this command. + */ + + switch (cmd->sccsoper) + { + case SHELL: /* call a shell file */ + *ap = cmd->sccspath; + *--ap = "sh"; + rval = callprog(_PATH_BSHELL, cmd->sccsflags, ap, forkflag); + break; + + case PROG: /* call an sccs prog */ + rval = callprog(cmd->sccspath, cmd->sccsflags, ap, forkflag); + break; + + case CMACRO: /* command macro */ + /* step through & execute each part of the macro */ + for (p = cmd->sccspath; *p != '\0'; p++) + { + q = p; + while (*p != '\0' && *p != '/') + p++; + rval = command(&ap[1], *p != '\0', q); + if (rval != 0) + break; + } + break; + + case FIX: /* fix a delta */ + if (ap[1]==0 || strncmp(ap[1], "-r", 2)!=0) + { + usrerr("-r flag needed for fix command"); + rval = EX_USAGE; + break; + } + + /* get the version with all changes */ + rval = command(&ap[1], TRUE, "get -k"); + + /* now remove that version from the s-file */ + if (rval == 0) + rval = command(&ap[1], TRUE, "rmdel:r"); + + /* and edit the old version (but don't clobber new vers) */ + if (rval == 0) + rval = command(&ap[2], FALSE, "get -e -g"); + break; + + case CLEAN: + rval = clean((int) cmd->sccspath, ap); + break; + + case UNEDIT: + for (argv = np = &ap[1]; *argv != NULL; argv++) + { + if (unedit(*argv)) + *np++ = *argv; + } + *np = NULL; + + /* get all the files that we unedited successfully */ + if (np > &ap[1]) + rval = command(&ap[1], FALSE, "get"); + break; + + case DIFFS: /* diff between s-file & edit file */ + /* find the end of the flag arguments */ + for (np = &ap[1]; *np != NULL && **np == '-'; np++) + continue; + argv = np; + + /* for each file, do the diff */ + p = argv[1]; + while (*np != NULL) + { + /* messy, but we need a null terminated argv */ + *argv = *np++; + argv[1] = NULL; + i = dodiff(ap, tail(*argv)); + if (rval == 0) + rval = i; + argv[1] = p; + } + break; + + case DODIFF: /* internal diff call */ + setuid(getuid()); + for (np = ap; *np != NULL; np++) + { + if ((*np)[0] == '-' && (*np)[1] == 'C') + (*np)[1] = 'c'; + } + + /* insert "-" argument */ + np[1] = NULL; + np[0] = np[-1]; + np[-1] = "-"; + + /* execute the diff program of choice */ +# ifndef V6 + execvp("diff", ap); +# endif + execv(cmd->sccspath, argv); + syserr("cannot exec %s", cmd->sccspath); + exit(EX_OSERR); + + case ENTER: /* enter new sccs files */ + /* skip over flag arguments */ + for (np = &ap[1]; *np != NULL && **np == '-'; np++) + continue; + argv = np; + + /* do an admin for each file */ + p = argv[1]; + while (*np != NULL) + { + printf("\n%s:\n", *np); + strcpy(buf, "-i"); + gstrcat(buf, *np, sizeof(buf)); + ap[0] = buf; + argv[0] = tail(*np); + argv[1] = NULL; + rval = command(ap, TRUE, "admin"); + argv[1] = p; + if (rval == 0) + { + strcpy(buf, ","); + gstrcat(buf, tail(*np), sizeof(buf)); + if (link(*np, buf) >= 0) + unlink(*np); + } + np++; + } + break; + + default: + syserr("oper %d", cmd->sccsoper); + exit(EX_SOFTWARE); + } +# ifdef DEBUG + if (Debug) + printf("command: rval=%d\n", rval); +# endif + return (rval); +} + +/* +** LOOKUP -- look up an SCCS command name. +** +** Parameters: +** name -- the name of the command to look up. +** +** Returns: +** ptr to command descriptor for this command. +** NULL if no such entry. +** +** Side Effects: +** none. +*/ + +struct sccsprog * +lookup(name) + char *name; +{ + register struct sccsprog *cmd; + + for (cmd = SccsProg; cmd->sccsname != NULL; cmd++) + { + if (strcmp(cmd->sccsname, name) == 0) + return (cmd); + } + return (NULL); +} + +/* +** CALLPROG -- call a program +** +** Used to call the SCCS programs. +** +** Parameters: +** progpath -- pathname of the program to call. +** flags -- status flags from the command descriptors. +** argv -- an argument vector to pass to the program. +** forkflag -- if true, fork before calling, else just +** exec. +** +** Returns: +** The exit status of the program. +** Nothing if forkflag == FALSE. +** +** Side Effects: +** Can exit if forkflag == FALSE. +*/ + +callprog(progpath, flags, argv, forkflag) + char *progpath; + short flags; + char **argv; + bool forkflag; +{ + register int i; + register int wpid; + auto int st; + register int sigcode; + register int coredumped; + register const char *sigmsg; + char sigmsgbuf[10+1]; /* "Signal 127" + terminating '\0' */ + +# ifdef DEBUG + if (Debug) + { + printf("callprog:\n"); + for (i = 0; argv[i] != NULL; i++) + printf("\t\"%s\"\n", argv[i]); + } +# endif + + if (*argv == NULL) + return (-1); + + /* + ** Fork if appropriate. + */ + + if (forkflag) + { +# ifdef DEBUG + if (Debug) + printf("Forking\n"); +# endif + i = fork(); + if (i < 0) + { + syserr("cannot fork"); + exit(EX_OSERR); + } + else if (i > 0) + { + while ((wpid = wait(&st)) != -1 && wpid != i) + ; + if ((sigcode = st & 0377) == 0) + st = (st >> 8) & 0377; + else + { + coredumped = sigcode & 0200; + sigcode &= 0177; + if (sigcode != SIGINT && sigcode != SIGPIPE) + { + if (sigcode < NSIG) + sigmsg = sys_siglist[sigcode]; + else + { + sprintf(sigmsgbuf, "Signal %d", + sigcode); + sigmsg = sigmsgbuf; + } + fprintf(stderr, "sccs: %s: %s%s", argv[0], + sigmsg, + coredumped ? " - core dumped": ""); + } + st = EX_SOFTWARE; + } + if (OutFile >= 0) + { + close(OutFile); + OutFile = -1; + } + return (st); + } + } + else if (OutFile >= 0) + { + syserr("callprog: setting stdout w/o forking"); + exit(EX_SOFTWARE); + } + + /* set protection as appropriate */ + if (bitset(REALUSER, flags)) + setuid(getuid()); + + /* change standard input & output if needed */ + if (OutFile >= 0) + { + close(1); + dup(OutFile); + close(OutFile); + } + + /* call real SCCS program */ + execv(progpath, argv); + syserr("cannot execute %s", progpath); + exit(EX_UNAVAILABLE); + /*NOTREACHED*/ +} + +/* +** MAKEFILE -- make filename of SCCS file +** +** If the name passed is already the name of an SCCS file, +** just return it. Otherwise, munge the name into the name +** of the actual SCCS file. +** +** There are cases when it is not clear what you want to +** do. For example, if SccsPath is an absolute pathname +** and the name given is also an absolute pathname, we go +** for SccsPath (& only use the last component of the name +** passed) -- this is important for security reasons (if +** sccs is being used as a setuid front end), but not +** particularly intuitive. +** +** Parameters: +** name -- the file name to be munged. +** +** Returns: +** The pathname of the sccs file. +** NULL on error. +** +** Side Effects: +** none. +*/ + +char * +makefile(name) + char *name; +{ + register char *p; + char buf[3*FBUFSIZ]; + extern char *malloc(); + extern char *rindex(); + extern bool safepath(); + extern bool isdir(); + register char *q; + + p = rindex(name, '/'); + if (p == NULL) + p = name; + else + p++; + + /* + ** Check to see that the path is "safe", i.e., that we + ** are not letting some nasty person use the setuid part + ** of this program to look at or munge some presumably + ** hidden files. + */ + + if (SccsDir[0] == '/' && !safepath(name)) + return (NULL); + + /* + ** Create the base pathname. + */ + + /* first the directory part */ + if (SccsDir[0] != '\0' && name[0] != '/' && strncmp(name, "./", 2) != 0) + { + gstrcpy(buf, SccsDir, sizeof(buf)); + gstrcat(buf, "/", sizeof(buf)); + } + else + gstrcpy(buf, "", sizeof(buf)); + + /* then the head of the pathname */ + gstrncat(buf, name, p - name, sizeof(buf)); + q = &buf[strlen(buf)]; + + /* now copy the final part of the name, in case useful */ + gstrcpy(q, p, sizeof(buf)); + + /* so is it useful? */ + if (strncmp(p, "s.", 2) != 0 && !isdir(buf)) + { + /* sorry, no; copy the SCCS pathname & the "s." */ + gstrcpy(q, SccsPath, sizeof(buf)); + gstrcat(buf, "/s.", sizeof(buf)); + + /* and now the end of the name */ + gstrcat(buf, p, sizeof(buf)); + } + + /* if i haven't changed it, why did I do all this? */ + if (strcmp(buf, name) == 0) + p = name; + else + { + /* but if I have, squirrel it away */ + p = malloc(strlen(buf) + 1); + if (p == NULL) + { + perror("Sccs: no mem"); + exit(EX_OSERR); + } + strcpy(p, buf); + } + + return (p); +} + +/* +** ISDIR -- return true if the argument is a directory. +** +** Parameters: +** name -- the pathname of the file to check. +** +** Returns: +** TRUE if 'name' is a directory, FALSE otherwise. +** +** Side Effects: +** none. +*/ + +bool +isdir(name) + char *name; +{ + struct stat stbuf; + + return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR); +} + +/* +** SAFEPATH -- determine whether a pathname is "safe" +** +** "Safe" pathnames only allow you to get deeper into the +** directory structure, i.e., full pathnames and ".." are +** not allowed. +** +** Parameters: +** p -- the name to check. +** +** Returns: +** TRUE -- if the path is safe. +** FALSE -- if the path is not safe. +** +** Side Effects: +** Prints a message if the path is not safe. +*/ + +bool +safepath(p) + register char *p; +{ + extern char *index(); + + if (*p != '/') + { + while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0) + { + p = index(p, '/'); + if (p == NULL) + return (TRUE); + p++; + } + } + + printf("You may not use full pathnames or \"..\"\n"); + return (FALSE); +} + +/* +** CLEAN -- clean out recreatable files +** +** Any file for which an "s." file exists but no "p." file +** exists in the current directory is purged. +** +** Parameters: +** mode -- tells whether this came from a "clean", "info", or +** "check" command. +** argv -- the rest of the argument vector. +** +** Returns: +** none. +** +** Side Effects: +** Removes files in the current directory. +** Prints information regarding files being edited. +** Exits if a "check" command. +*/ + +clean(mode, argv) + int mode; + char **argv; +{ + struct direct *dir; + char buf[FBUFSIZ]; + char *bufend; + register DIR *dirp; + register char *basefile; + bool gotedit; + bool gotpfent; + FILE *pfp; + bool nobranch = FALSE; + extern struct pfile *getpfent(); + register struct pfile *pf; + register char **ap; + extern char *username(); + char *usernm = NULL; + char *subdir = NULL; + char *cmdname; + + /* + ** Process the argv + */ + + cmdname = *argv; + for (ap = argv; *++ap != NULL; ) + { + if (**ap == '-') + { + /* we have a flag */ + switch ((*ap)[1]) + { + case 'b': + nobranch = TRUE; + break; + + case 'u': + if ((*ap)[2] != '\0') + usernm = &(*ap)[2]; + else if (ap[1] != NULL && ap[1][0] != '-') + usernm = *++ap; + else + usernm = username(); + break; + } + } + else + { + if (subdir != NULL) + usrerr("too many args"); + else + subdir = *ap; + } + } + + /* + ** Find and open the SCCS directory. + */ + + gstrcpy(buf, SccsDir, sizeof(buf)); + if (buf[0] != '\0') + gstrcat(buf, "/", sizeof(buf)); + if (subdir != NULL) + { + gstrcat(buf, subdir, sizeof(buf)); + gstrcat(buf, "/", sizeof(buf)); + } + gstrcat(buf, SccsPath, sizeof(buf)); + bufend = &buf[strlen(buf)]; + + dirp = opendir(buf); + if (dirp == NULL) + { + usrerr("cannot open %s", buf); + return (EX_NOINPUT); + } + + /* + ** Scan the SCCS directory looking for s. files. + ** gotedit tells whether we have tried to clean any + ** files that are being edited. + */ + + gotedit = FALSE; + while (dir = readdir(dirp)) { + if (strncmp(dir->d_name, "s.", 2) != 0) + continue; + + /* got an s. file -- see if the p. file exists */ + gstrcpy(bufend, "/p.", sizeof(buf)); + basefile = bufend + 3; + gstrcpy(basefile, &dir->d_name[2], sizeof(buf)); + + /* + ** open and scan the p-file. + ** 'gotpfent' tells if we have found a valid p-file + ** entry. + */ + + pfp = fopen(buf, "r"); + gotpfent = FALSE; + if (pfp != NULL) + { + /* the file exists -- report it's contents */ + while ((pf = getpfent(pfp)) != NULL) + { + if (nobranch && isbranch(pf->p_nsid)) + continue; + if (usernm != NULL && strcmp(usernm, pf->p_user) != 0 && mode != CLEANC) + continue; + gotedit = TRUE; + gotpfent = TRUE; + if (mode == TELLC) + { + printf("%s\n", basefile); + break; + } + printf("%12s: being edited: ", basefile); + putpfent(pf, stdout); + } + fclose(pfp); + } + + /* the s. file exists and no p. file exists -- unlink the g-file */ + if (mode == CLEANC && !gotpfent) + { + char unlinkbuf[FBUFSIZ]; + gstrcpy(unlinkbuf, &dir->d_name[2], sizeof(unlinkbuf)); + unlink(unlinkbuf); + } + } + + /* cleanup & report results */ + closedir(dirp); + if (!gotedit && mode == INFOC) + { + printf("Nothing being edited"); + if (nobranch) + printf(" (on trunk)"); + if (usernm == NULL) + printf("\n"); + else + printf(" by %s\n", usernm); + } + if (mode == CHECKC) + exit(gotedit); + return (EX_OK); +} + +/* +** ISBRANCH -- is the SID a branch? +** +** Parameters: +** sid -- the sid to check. +** +** Returns: +** TRUE if the sid represents a branch. +** FALSE otherwise. +** +** Side Effects: +** none. +*/ + +isbranch(sid) + char *sid; +{ + register char *p; + int dots; + + dots = 0; + for (p = sid; *p != '\0'; p++) + { + if (*p == '.') + dots++; + if (dots > 1) + return (TRUE); + } + return (FALSE); +} + +/* +** UNEDIT -- unedit a file +** +** Checks to see that the current user is actually editting +** the file and arranges that s/he is not editting it. +** +** Parameters: +** fn -- the name of the file to be unedited. +** +** Returns: +** TRUE -- if the file was successfully unedited. +** FALSE -- if the file was not unedited for some +** reason. +** +** Side Effects: +** fn is removed +** entries are removed from pfile. +*/ + +bool +unedit(fn) + char *fn; +{ + register FILE *pfp; + char *cp, *pfn; + static char tfn[] = _PATH_TMP; + FILE *tfp; + register char *q; + bool delete = FALSE; + bool others = FALSE; + char *myname; + extern char *username(); + struct pfile *pent; + extern struct pfile *getpfent(); + char buf[PFILELG]; + extern char *makefile(), *rindex(), *tail(); + + /* make "s." filename & find the trailing component */ + pfn = makefile(fn); + if (pfn == NULL) + return (FALSE); + q = rindex(pfn, '/'); + if (q == NULL) + q = &pfn[-1]; + if (q[1] != 's' || q[2] != '.') + { + usrerr("bad file name \"%s\"", fn); + return (FALSE); + } + + /* turn "s." into "p." & try to open it */ + *++q = 'p'; + + pfp = fopen(pfn, "r"); + if (pfp == NULL) + { + printf("%12s: not being edited\n", fn); + return (FALSE); + } + + /* create temp file for editing p-file */ + mktemp(tfn); + tfp = fopen(tfn, "w"); + if (tfp == NULL) + { + usrerr("cannot create \"%s\"", tfn); + exit(EX_OSERR); + } + + /* figure out who I am */ + myname = username(); + + /* + ** Copy p-file to temp file, doing deletions as needed. + */ + + while ((pent = getpfent(pfp)) != NULL) + { + if (strcmp(pent->p_user, myname) == 0) + { + /* a match */ + delete++; + } + else + { + /* output it again */ + putpfent(pent, tfp); + others++; + } + } + + /* + * Before changing anything, make sure we can remove + * the file in question (assuming it exists). + */ + if (delete) { + extern int errno; + + cp = tail(fn); + errno = 0; + if (access(cp, 0) < 0 && errno != ENOENT) + goto bad; + if (errno == 0) + /* + * This is wrong, but the rest of the program + * has built in assumptions about "." as well, + * so why make unedit a special case? + */ + if (access(".", 2) < 0) { + bad: + printf("%12s: can't remove\n", cp); + fclose(tfp); + fclose(pfp); + unlink(tfn); + return (FALSE); + } + } + /* do final cleanup */ + if (others) + { + /* copy it back (perhaps it should be linked?) */ + if (freopen(tfn, "r", tfp) == NULL) + { + syserr("cannot reopen \"%s\"", tfn); + exit(EX_OSERR); + } + if (freopen(pfn, "w", pfp) == NULL) + { + usrerr("cannot create \"%s\"", pfn); + return (FALSE); + } + while (fgets(buf, sizeof buf, tfp) != NULL) + fputs(buf, pfp); + } + else + { + /* it's empty -- remove it */ + unlink(pfn); + } + fclose(tfp); + fclose(pfp); + unlink(tfn); + + /* actually remove the g-file */ + if (delete) + { + /* + * Since we've checked above, we can + * use the return from unlink to + * determine if the file existed or not. + */ + if (unlink(cp) >= 0) + printf("%12s: removed\n", cp); + return (TRUE); + } + else + { + printf("%12s: not being edited by you\n", fn); + return (FALSE); + } +} + +/* +** DODIFF -- diff an s-file against a g-file +** +** Parameters: +** getv -- argv for the 'get' command. +** gfile -- name of the g-file to diff against. +** +** Returns: +** Result of get. +** +** Side Effects: +** none. +*/ + +dodiff(getv, gfile) + char **getv; + char *gfile; +{ + int pipev[2]; + int rval; + register int i; + register int pid; + auto int st; + extern int errno; + sig_t osig; + + printf("\n------- %s -------\n", gfile); + fflush(stdout); + + /* create context for diff to run in */ + if (pipe(pipev) < 0) + { + syserr("dodiff: pipe failed"); + exit(EX_OSERR); + } + if ((pid = fork()) < 0) + { + syserr("dodiff: fork failed"); + exit(EX_OSERR); + } + else if (pid > 0) + { + /* in parent; run get */ + OutFile = pipev[1]; + close(pipev[0]); + rval = command(&getv[1], TRUE, "get:rcixt -s -k -p"); + osig = signal(SIGINT, SIG_IGN); + while (((i = wait(&st)) >= 0 && i != pid) || errno == EINTR) + errno = 0; + signal(SIGINT, osig); + /* ignore result of diff */ + } + else + { + /* in child, run diff */ + if (close(pipev[1]) < 0 || close(0) < 0 || + dup(pipev[0]) != 0 || close(pipev[0]) < 0) + { + syserr("dodiff: magic failed"); + exit(EX_OSERR); + } + command(&getv[1], FALSE, "-diff:elsfhbC"); + } + return (rval); +} + +/* +** TAIL -- return tail of filename. +** +** Parameters: +** fn -- the filename. +** +** Returns: +** a pointer to the tail of the filename; e.g., given +** "cmd/ls.c", "ls.c" is returned. +** +** Side Effects: +** none. +*/ + +char * +tail(fn) + register char *fn; +{ + register char *p; + + for (p = fn; *p != 0; p++) + if (*p == '/' && p[1] != '\0' && p[1] != '/') + fn = &p[1]; + return (fn); +} + +/* +** GETPFENT -- get an entry from the p-file +** +** Parameters: +** pfp -- p-file file pointer +** +** Returns: +** pointer to p-file struct for next entry +** NULL on EOF or error +** +** Side Effects: +** Each call wipes out results of previous call. +*/ + +struct pfile * +getpfent(pfp) + FILE *pfp; +{ + static struct pfile ent; + static char buf[PFILELG]; + register char *p; + extern char *nextfield(); + + if (fgets(buf, sizeof buf, pfp) == NULL) + return (NULL); + + ent.p_osid = p = buf; + ent.p_nsid = p = nextfield(p); + ent.p_user = p = nextfield(p); + ent.p_date = p = nextfield(p); + ent.p_time = p = nextfield(p); + ent.p_aux = p = nextfield(p); + + return (&ent); +} + + +char * +nextfield(p) + register char *p; +{ + if (p == NULL || *p == '\0') + return (NULL); + while (*p != ' ' && *p != '\n' && *p != '\0') + p++; + if (*p == '\n' || *p == '\0') + { + *p = '\0'; + return (NULL); + } + *p++ = '\0'; + return (p); +} + /* +** PUTPFENT -- output a p-file entry to a file +** +** Parameters: +** pf -- the p-file entry +** f -- the file to put it on. +** +** Returns: +** none. +** +** Side Effects: +** pf is written onto file f. +*/ + +putpfent(pf, f) + register struct pfile *pf; + register FILE *f; +{ + fprintf(f, "%s %s %s %s %s", pf->p_osid, pf->p_nsid, + pf->p_user, pf->p_date, pf->p_time); + if (pf->p_aux != NULL) + fprintf(f, " %s", pf->p_aux); + else + fprintf(f, "\n"); +} + +/* +** USRERR -- issue user-level error +** +** Parameters: +** f -- format string. +** p1-p3 -- parameters to a printf. +** +** Returns: +** -1 +** +** Side Effects: +** none. +*/ + +/*VARARGS1*/ +usrerr(f, p1, p2, p3) + char *f; +{ + fprintf(stderr, "\n%s: ", MyName); + fprintf(stderr, f, p1, p2, p3); + fprintf(stderr, "\n"); + + return (-1); +} + +/* +** SYSERR -- print system-generated error. +** +** Parameters: +** f -- format string to a printf. +** p1, p2, p3 -- parameters to f. +** +** Returns: +** never. +** +** Side Effects: +** none. +*/ + +/*VARARGS1*/ +syserr(f, p1, p2, p3) + char *f; +{ + extern int errno; + + fprintf(stderr, "\n%s SYSERR: ", MyName); + fprintf(stderr, f, p1, p2, p3); + fprintf(stderr, "\n"); + if (errno == 0) + exit(EX_SOFTWARE); + else + { + perror(NULL); + exit(EX_OSERR); + } +} + /* +** USERNAME -- return name of the current user +** +** Parameters: +** none +** +** Returns: +** name of current user +** +** Side Effects: +** none +*/ + +char * +username() +{ +# ifdef UIDUSER + extern struct passwd *getpwuid(); + register struct passwd *pw; + + pw = getpwuid(getuid()); + if (pw == NULL) + { + syserr("who are you? (uid=%d)", getuid()); + exit(EX_OSERR); + } + return (pw->pw_name); +# else + extern char *getlogin(); + register char *p; + + p = getenv("USER"); + if (p == NULL || p[0] == '\0') + p = getlogin(); + return (p); +# endif UIDUSER +} + +/* +** Guarded string manipulation routines; the last argument +** is the length of the buffer into which the strcpy or strcat +** is to be done. +*/ +char *gstrcat(to, from, length) + char *to, *from; + int length; +{ + if (strlen(from) + strlen(to) >= length) { + gstrbotch(to, from); + } + return(strcat(to, from)); +} + +char *gstrncat(to, from, n, length) + char *to, *from; + int n; + int length; +{ + if (n + strlen(to) >= length) { + gstrbotch(to, from); + } + return(strncat(to, from, n)); +} + +char *gstrcpy(to, from, length) + char *to, *from; + int length; +{ + if (strlen(from) >= length) { + gstrbotch(from, (char *)0); + } + return(strcpy(to, from)); +} +gstrbotch(str1, str2) + char *str1, *str2; +{ + usrerr("Filename(s) too long: %s %s", str1, str2); +} diff --git a/usr.bin/sed/compile.c b/usr.bin/sed/compile.c new file mode 100644 index 00000000000..8ad1b2dd279 --- /dev/null +++ b/usr.bin/sed/compile.c @@ -0,0 +1,779 @@ +/*- + * Copyright (c) 1992 Diomidis Spinellis. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Diomidis Spinellis of Imperial College, University of London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE 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[] = "@(#)compile.c 8.2 (Berkeley) 4/28/95"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "defs.h" +#include "extern.h" + +#define LHSZ 128 +#define LHMASK (LHSZ - 1) +static struct labhash { + struct labhash *lh_next; + u_int lh_hash; + struct s_command *lh_cmd; + int lh_ref; +} *labels[LHSZ]; + +static char *compile_addr __P((char *, struct s_addr *)); +static char *compile_delimited __P((char *, char *)); +static char *compile_flags __P((char *, struct s_subst *)); +static char *compile_re __P((char *, regex_t **)); +static char *compile_subst __P((char *, struct s_subst *)); +static char *compile_text __P((void)); +static char *compile_tr __P((char *, char **)); +static struct s_command + **compile_stream __P((char *, struct s_command **, char *)); +static char *duptoeol __P((char *, char *)); +static void enterlabel __P((struct s_command *)); +static struct s_command + *findlabel __P((char *)); +static void fixuplabel __P((struct s_command *, struct s_command *)); +static void uselabel __P((void)); + +/* + * Command specification. This is used to drive the command parser. + */ +struct s_format { + char code; /* Command code */ + int naddr; /* Number of address args */ + enum e_args args; /* Argument type */ +}; + +static struct s_format cmd_fmts[] = { + {'{', 2, GROUP}, + {'a', 1, TEXT}, + {'b', 2, BRANCH}, + {'c', 2, TEXT}, + {'d', 2, EMPTY}, + {'D', 2, EMPTY}, + {'g', 2, EMPTY}, + {'G', 2, EMPTY}, + {'h', 2, EMPTY}, + {'H', 2, EMPTY}, + {'i', 1, TEXT}, + {'l', 2, EMPTY}, + {'n', 2, EMPTY}, + {'N', 2, EMPTY}, + {'p', 2, EMPTY}, + {'P', 2, EMPTY}, + {'q', 1, EMPTY}, + {'r', 1, RFILE}, + {'s', 2, SUBST}, + {'t', 2, BRANCH}, + {'w', 2, WFILE}, + {'x', 2, EMPTY}, + {'y', 2, TR}, + {'!', 2, NONSEL}, + {':', 0, LABEL}, + {'#', 0, COMMENT}, + {'=', 1, EMPTY}, + {'\0', 0, COMMENT}, +}; + +/* The compiled program. */ +struct s_command *prog; + +/* + * Compile the program into prog. + * Initialise appends. + */ +void +compile() +{ + *compile_stream(NULL, &prog, NULL) = NULL; + fixuplabel(prog, NULL); + uselabel(); + appends = xmalloc(sizeof(struct s_appends) * appendnum); + match = xmalloc((maxnsub + 1) * sizeof(regmatch_t)); +} + +#define EATSPACE() do { \ + if (p) \ + while (*p && isascii(*p) && isspace(*p)) \ + p++; \ + } while (0) + +static struct s_command ** +compile_stream(terminator, link, p) + char *terminator; + struct s_command **link; + register char *p; +{ + static char lbuf[_POSIX2_LINE_MAX + 1]; /* To save stack */ + struct s_command *cmd, *cmd2; + struct s_format *fp; + int naddr; /* Number of addresses */ + + if (p != NULL) + goto semicolon; + for (;;) { + if ((p = cu_fgets(lbuf, sizeof(lbuf))) == NULL) { + if (terminator != NULL) + err(COMPILE, "unexpected EOF (pending }'s)"); + return (link); + } + +semicolon: EATSPACE(); + if (p && (*p == '#' || *p == '\0')) + continue; + if (*p == '}') { + if (terminator == NULL) + err(COMPILE, "unexpected }"); + return (link); + } + *link = cmd = xmalloc(sizeof(struct s_command)); + link = &cmd->next; + cmd->nonsel = cmd->inrange = 0; + /* First parse the addresses */ + naddr = 0; + +/* Valid characters to start an address */ +#define addrchar(c) (strchr("0123456789/\\$", (c))) + if (addrchar(*p)) { + naddr++; + cmd->a1 = xmalloc(sizeof(struct s_addr)); + p = compile_addr(p, cmd->a1); + EATSPACE(); /* EXTENSION */ + if (*p == ',') { + p++; + EATSPACE(); /* EXTENSION */ + naddr++; + cmd->a2 = xmalloc(sizeof(struct s_addr)); + p = compile_addr(p, cmd->a2); + EATSPACE(); + } else + cmd->a2 = 0; + } else + cmd->a1 = cmd->a2 = 0; + +nonsel: /* Now parse the command */ + if (!*p) + err(COMPILE, "command expected"); + cmd->code = *p; + for (fp = cmd_fmts; fp->code; fp++) + if (fp->code == *p) + break; + if (!fp->code) + err(COMPILE, "invalid command code %c", *p); + if (naddr > fp->naddr) + err(COMPILE, +"command %c expects up to %d address(es), found %d", *p, fp->naddr, naddr); + switch (fp->args) { + case NONSEL: /* ! */ + p++; + EATSPACE(); + cmd->nonsel = ! cmd->nonsel; + goto nonsel; + case GROUP: /* { */ + p++; + EATSPACE(); + if (!*p) + p = NULL; + cmd2 = xmalloc(sizeof(struct s_command)); + cmd2->code = '}'; + *compile_stream("}", &cmd->u.c, p) = cmd2; + cmd->next = cmd2; + link = &cmd2->next; + /* + * Short-circuit command processing, since end of + * group is really just a noop. + */ + cmd2->nonsel = 1; + cmd2->a1 = cmd2->a2 = 0; + break; + case EMPTY: /* d D g G h H l n N p P q x = \0 */ + p++; + EATSPACE(); + if (*p == ';') { + p++; + link = &cmd->next; + goto semicolon; + } + if (*p) + err(COMPILE, +"extra characters at the end of %c command", cmd->code); + break; + case TEXT: /* a c i */ + p++; + EATSPACE(); + if (*p != '\\') + err(COMPILE, +"command %c expects \\ followed by text", cmd->code); + p++; + EATSPACE(); + if (*p) + err(COMPILE, +"extra characters after \\ at the end of %c command", cmd->code); + cmd->t = compile_text(); + break; + case COMMENT: /* \0 # */ + break; + case WFILE: /* w */ + p++; + EATSPACE(); + if (*p == '\0') + err(COMPILE, "filename expected"); + cmd->t = duptoeol(p, "w command"); + if (aflag) + cmd->u.fd = -1; + else if ((cmd->u.fd = open(p, + O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, + DEFFILEMODE)) == -1) + err(FATAL, "%s: %s\n", p, strerror(errno)); + break; + case RFILE: /* r */ + p++; + EATSPACE(); + if (*p == '\0') + err(COMPILE, "filename expected"); + else + cmd->t = duptoeol(p, "read command"); + break; + case BRANCH: /* b t */ + p++; + EATSPACE(); + if (*p == '\0') + cmd->t = NULL; + else + cmd->t = duptoeol(p, "branch"); + break; + case LABEL: /* : */ + p++; + EATSPACE(); + cmd->t = duptoeol(p, "label"); + if (strlen(p) == 0) + err(COMPILE, "empty label"); + enterlabel(cmd); + break; + case SUBST: /* s */ + p++; + if (*p == '\0' || *p == '\\') + err(COMPILE, +"substitute pattern can not be delimited by newline or backslash"); + cmd->u.s = xmalloc(sizeof(struct s_subst)); + p = compile_re(p, &cmd->u.s->re); + if (p == NULL) + err(COMPILE, "unterminated substitute pattern"); + --p; + p = compile_subst(p, cmd->u.s); + p = compile_flags(p, cmd->u.s); + EATSPACE(); + if (*p == ';') { + p++; + link = &cmd->next; + goto semicolon; + } + break; + case TR: /* y */ + p++; + p = compile_tr(p, (char **)&cmd->u.y); + EATSPACE(); + if (*p == ';') { + p++; + link = &cmd->next; + goto semicolon; + } + if (*p) + err(COMPILE, +"extra text at the end of a transform command"); + break; + } + } +} + +/* + * Get a delimited string. P points to the delimeter of the string; d points + * to a buffer area. Newline and delimiter escapes are processed; other + * escapes are ignored. + * + * Returns a pointer to the first character after the final delimiter or NULL + * in the case of a non-terminated string. The character array d is filled + * with the processed string. + */ +static char * +compile_delimited(p, d) + char *p, *d; +{ + char c; + + c = *p++; + if (c == '\0') + return (NULL); + else if (c == '\\') + err(COMPILE, "\\ can not be used as a string delimiter"); + else if (c == '\n') + err(COMPILE, "newline can not be used as a string delimiter"); + while (*p) { + if (*p == '\\' && p[1] == c) + p++; + else if (*p == '\\' && p[1] == 'n') { + *d++ = '\n'; + p += 2; + continue; + } else if (*p == '\\' && p[1] == '\\') + *d++ = *p++; + else if (*p == c) { + *d = '\0'; + return (p + 1); + } + *d++ = *p++; + } + return (NULL); +} + +/* + * Get a regular expression. P points to the delimiter of the regular + * expression; repp points to the address of a regexp pointer. Newline + * and delimiter escapes are processed; other escapes are ignored. + * Returns a pointer to the first character after the final delimiter + * or NULL in the case of a non terminated regular expression. The regexp + * pointer is set to the compiled regular expression. + * Cflags are passed to regcomp. + */ +static char * +compile_re(p, repp) + char *p; + regex_t **repp; +{ + int eval; + char re[_POSIX2_LINE_MAX + 1]; + + p = compile_delimited(p, re); + if (p && strlen(re) == 0) { + *repp = NULL; + return (p); + } + *repp = xmalloc(sizeof(regex_t)); + if (p && (eval = regcomp(*repp, re, 0)) != 0) + err(COMPILE, "RE error: %s", strregerror(eval, *repp)); + if (maxnsub < (*repp)->re_nsub) + maxnsub = (*repp)->re_nsub; + return (p); +} + +/* + * Compile the substitution string of a regular expression and set res to + * point to a saved copy of it. Nsub is the number of parenthesized regular + * expressions. + */ +static char * +compile_subst(p, s) + char *p; + struct s_subst *s; +{ + static char lbuf[_POSIX2_LINE_MAX + 1]; + int asize, ref, size; + char c, *text, *op, *sp; + + c = *p++; /* Terminator character */ + if (c == '\0') + return (NULL); + + s->maxbref = 0; + s->linenum = linenum; + asize = 2 * _POSIX2_LINE_MAX + 1; + text = xmalloc(asize); + size = 0; + do { + op = sp = text + size; + for (; *p; p++) { + if (*p == '\\') { + p++; + if (strchr("123456789", *p) != NULL) { + *sp++ = '\\'; + ref = *p - '0'; + if (s->re != NULL && + ref > s->re->re_nsub) + err(COMPILE, +"\\%c not defined in the RE", *p); + if (s->maxbref < ref) + s->maxbref = ref; + } else if (*p == '&' || *p == '\\') + *sp++ = '\\'; + } else if (*p == c) { + p++; + *sp++ = '\0'; + size += sp - op; + s->new = xrealloc(text, size); + return (p); + } else if (*p == '\n') { + err(COMPILE, +"unescaped newline inside substitute pattern"); + /* NOTREACHED */ + } + *sp++ = *p; + } + size += sp - op; + if (asize - size < _POSIX2_LINE_MAX + 1) { + asize *= 2; + text = xmalloc(asize); + } + } while (cu_fgets(p = lbuf, sizeof(lbuf))); + err(COMPILE, "unterminated substitute in regular expression"); + /* NOTREACHED */ +} + +/* + * Compile the flags of the s command + */ +static char * +compile_flags(p, s) + char *p; + struct s_subst *s; +{ + int gn; /* True if we have seen g or n */ + char wfile[_POSIX2_LINE_MAX + 1], *q; + + s->n = 1; /* Default */ + s->p = 0; + s->wfile = NULL; + s->wfd = -1; + for (gn = 0;;) { + EATSPACE(); /* EXTENSION */ + switch (*p) { + case 'g': + if (gn) + err(COMPILE, +"more than one number or 'g' in substitute flags"); + gn = 1; + s->n = 0; + break; + case '\0': + case '\n': + case ';': + return (p); + case 'p': + s->p = 1; + break; + case '1': case '2': case '3': + case '4': case '5': case '6': + case '7': case '8': case '9': + if (gn) + err(COMPILE, +"more than one number or 'g' in substitute flags"); + gn = 1; + /* XXX Check for overflow */ + s->n = (int)strtol(p, &p, 10); + break; + case 'w': + p++; +#ifdef HISTORIC_PRACTICE + if (*p != ' ') { + err(WARNING, "space missing before w wfile"); + return (p); + } +#endif + EATSPACE(); + q = wfile; + while (*p) { + if (*p == '\n') + break; + *q++ = *p++; + } + *q = '\0'; + if (q == wfile) + err(COMPILE, "no wfile specified"); + s->wfile = strdup(wfile); + if (!aflag && (s->wfd = open(wfile, + O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, + DEFFILEMODE)) == -1) + err(FATAL, "%s: %s\n", wfile, strerror(errno)); + return (p); + default: + err(COMPILE, + "bad flag in substitute command: '%c'", *p); + break; + } + p++; + } +} + +/* + * Compile a translation set of strings into a lookup table. + */ +static char * +compile_tr(p, transtab) + char *p; + char **transtab; +{ + int i; + char *lt, *op, *np; + char old[_POSIX2_LINE_MAX + 1]; + char new[_POSIX2_LINE_MAX + 1]; + + if (*p == '\0' || *p == '\\') + err(COMPILE, +"transform pattern can not be delimited by newline or backslash"); + p = compile_delimited(p, old); + if (p == NULL) { + err(COMPILE, "unterminated transform source string"); + return (NULL); + } + p = compile_delimited(--p, new); + if (p == NULL) { + err(COMPILE, "unterminated transform target string"); + return (NULL); + } + EATSPACE(); + if (strlen(new) != strlen(old)) { + err(COMPILE, "transform strings are not the same length"); + return (NULL); + } + /* We assume characters are 8 bits */ + lt = xmalloc(UCHAR_MAX); + for (i = 0; i <= UCHAR_MAX; i++) + lt[i] = (char)i; + for (op = old, np = new; *op; op++, np++) + lt[(u_char)*op] = *np; + *transtab = lt; + return (p); +} + +/* + * Compile the text following an a or i command. + */ +static char * +compile_text() +{ + int asize, size; + char *text, *p, *op, *s; + char lbuf[_POSIX2_LINE_MAX + 1]; + + asize = 2 * _POSIX2_LINE_MAX + 1; + text = xmalloc(asize); + size = 0; + while (cu_fgets(lbuf, sizeof(lbuf))) { + op = s = text + size; + p = lbuf; + EATSPACE(); + for (; *p; p++) { + if (*p == '\\') + p++; + *s++ = *p; + } + size += s - op; + if (p[-2] != '\\') { + *s = '\0'; + break; + } + if (asize - size < _POSIX2_LINE_MAX + 1) { + asize *= 2; + text = xmalloc(asize); + } + } + return (xrealloc(text, size + 1)); +} + +/* + * Get an address and return a pointer to the first character after + * it. Fill the structure pointed to according to the address. + */ +static char * +compile_addr(p, a) + char *p; + struct s_addr *a; +{ + char *end; + + switch (*p) { + case '\\': /* Context address */ + ++p; + /* FALLTHROUGH */ + case '/': /* Context address */ + p = compile_re(p, &a->u.r); + if (p == NULL) + err(COMPILE, "unterminated regular expression"); + a->type = AT_RE; + return (p); + + case '$': /* Last line */ + a->type = AT_LAST; + return (p + 1); + /* Line number */ + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + a->type = AT_LINE; + a->u.l = strtol(p, &end, 10); + return (end); + default: + err(COMPILE, "expected context address"); + return (NULL); + } +} + +/* + * duptoeol -- + * Return a copy of all the characters up to \n or \0. + */ +static char * +duptoeol(s, ctype) + register char *s; + char *ctype; +{ + size_t len; + int ws; + char *start; + + ws = 0; + for (start = s; *s != '\0' && *s != '\n'; ++s) + ws = isspace(*s); + *s = '\0'; + if (ws) + err(WARNING, "whitespace after %s", ctype); + len = s - start + 1; + return (memmove(xmalloc(len), start, len)); +} + +/* + * Convert goto label names to addresses, and count a and r commands, in + * the given subset of the script. Free the memory used by labels in b + * and t commands (but not by :). + * + * TODO: Remove } nodes + */ +static void +fixuplabel(cp, end) + struct s_command *cp, *end; +{ + + for (; cp != end; cp = cp->next) + switch (cp->code) { + case 'a': + case 'r': + appendnum++; + break; + case 'b': + case 't': + /* Resolve branch target. */ + if (cp->t == NULL) { + cp->u.c = NULL; + break; + } + if ((cp->u.c = findlabel(cp->t)) == NULL) + err(COMPILE2, "undefined label '%s'", cp->t); + free(cp->t); + break; + case '{': + /* Do interior commands. */ + fixuplabel(cp->u.c, cp->next); + break; + } +} + +/* + * Associate the given command label for later lookup. + */ +static void +enterlabel(cp) + struct s_command *cp; +{ + register struct labhash **lhp, *lh; + register u_char *p; + register u_int h, c; + + for (h = 0, p = (u_char *)cp->t; (c = *p) != 0; p++) + h = (h << 5) + h + c; + lhp = &labels[h & LHMASK]; + for (lh = *lhp; lh != NULL; lh = lh->lh_next) + if (lh->lh_hash == h && strcmp(cp->t, lh->lh_cmd->t) == 0) + err(COMPILE2, "duplicate label '%s'", cp->t); + lh = xmalloc(sizeof *lh); + lh->lh_next = *lhp; + lh->lh_hash = h; + lh->lh_cmd = cp; + lh->lh_ref = 0; + *lhp = lh; +} + +/* + * Find the label contained in the command l in the command linked + * list cp. L is excluded from the search. Return NULL if not found. + */ +static struct s_command * +findlabel(name) + char *name; +{ + register struct labhash *lh; + register u_char *p; + register u_int h, c; + + for (h = 0, p = (u_char *)name; (c = *p) != 0; p++) + h = (h << 5) + h + c; + for (lh = labels[h & LHMASK]; lh != NULL; lh = lh->lh_next) { + if (lh->lh_hash == h && strcmp(name, lh->lh_cmd->t) == 0) { + lh->lh_ref = 1; + return (lh->lh_cmd); + } + } + return (NULL); +} + +/* + * Warn about any unused labels. As a side effect, release the label hash + * table space. + */ +static void +uselabel() +{ + register struct labhash *lh, *next; + register int i; + + for (i = 0; i < LHSZ; i++) { + for (lh = labels[i]; lh != NULL; lh = next) { + next = lh->lh_next; + if (!lh->lh_ref) + err(WARNING, "unused label '%s'", + lh->lh_cmd->t); + free(lh); + } + } +} diff --git a/usr.bin/showmount/showmount.8 b/usr.bin/showmount/showmount.8 new file mode 100644 index 00000000000..b4d1be68866 --- /dev/null +++ b/usr.bin/showmount/showmount.8 @@ -0,0 +1,93 @@ +.\" Copyright (c) 1989, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Rick Macklem at The University of Guelph. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)showmount.8 8.3 (Berkeley) 3/29/95 +.\" +.Dd March 29, 1995 +.Dt SHOWMOUNT 8 +.Os BSD 4 +.Sh NAME +.Nm showmount +.Nd show remote nfs mounts on host +.Sh SYNOPSIS +.Nm showmount +.Op Fl ade3 +.Op Ar host +.Sh DESCRIPTION +.Nm Showmount +shows status information about the +.Tn NFS +server on +.Ar host . +By default it prints the names of all hosts that have +.Tn NFS +file systems mounted +on the host. See +.%T "NFS: Network File System Protocol Specification" , +RFC 1094, +Appendix A, +and +.%T "NFS: Network File System Version 3 Protocol Specification" , +Appendix I, +for a detailed description of the protocol. +.Bl -tag -width Ds +.It Fl a +List all mount points in the form: +.Bd -ragged -offset indent -compact +.Ar host : Ns Ar dirpath +.Ed +.It Fl d +List directory paths of mount points instead of hosts +.It Fl e +Show the +.Ar host Ns 's +exports list +.It Fl 3 +Use mount protocol Version 3, compatible with NFS Version 3. +.El +.Sh SEE ALSO +.Xr mount 1 , +.Xr mountd 8 +.Sh BUGS +The mount daemon running on the server only has an idea of the actual mounts, +since the +.Tn NFS +server is stateless. +.Nm Showmount +will only display the information +as accurately as the mount daemon reports it. +.Sh HISTORY +The +.Nm showmount +utility first appeared in 4.4BSD. diff --git a/usr.bin/showmount/showmount.c b/usr.bin/showmount/showmount.c new file mode 100644 index 00000000000..2eb07c289be --- /dev/null +++ b/usr.bin/showmount/showmount.c @@ -0,0 +1,363 @@ +/* + * Copyright (c) 1989, 1993, 1995 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Rick Macklem at The University of Guelph. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE 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, 1995\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif not lint + +#ifndef lint +static char sccsid[] = "@(#)showmount.c 8.3 (Berkeley) 3/29/95"; +#endif not lint + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* Constant defs */ +#define ALL 1 +#define DIRS 2 + +#define DODUMP 0x1 +#define DOEXPORTS 0x2 + +struct mountlist { + struct mountlist *ml_left; + struct mountlist *ml_right; + char ml_host[RPCMNT_NAMELEN+1]; + char ml_dirp[RPCMNT_PATHLEN+1]; +}; + +struct grouplist { + struct grouplist *gr_next; + char gr_name[RPCMNT_NAMELEN+1]; +}; + +struct exportslist { + struct exportslist *ex_next; + struct grouplist *ex_groups; + char ex_dirp[RPCMNT_PATHLEN+1]; +}; + +static struct mountlist *mntdump; +static struct exportslist *exports; +static int type = 0; + +void print_dump __P((struct mountlist *)); +void usage __P((void)); +int xdr_mntdump __P((XDR *, struct mountlist **)); +int xdr_exports __P((XDR *, struct exportslist **)); + +/* + * This command queries the NFS mount daemon for it's mount list and/or + * it's exports list and prints them out. + * See "NFS: Network File System Protocol Specification, RFC1094, Appendix A" + * and the "Network File System Protocol XXX.." + * for detailed information on the protocol. + */ +int +main(argc, argv) + int argc; + char **argv; +{ + struct exportslist *exp; + struct grouplist *grp; + int estat, rpcs = 0, mntvers = 1; + char ch, *host; + + while ((ch = getopt(argc, argv, "ade3")) != EOF) + switch((char)ch) { + case 'a': + if (type == 0) { + type = ALL; + rpcs |= DODUMP; + } else + usage(); + break; + case 'd': + if (type == 0) { + type = DIRS; + rpcs |= DODUMP; + } else + usage(); + break; + case 'e': + rpcs |= DOEXPORTS; + break; + case '3': + mntvers = 3; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc > 0) + host = *argv; + else + host = "localhost"; + + if (rpcs == 0) + rpcs = DODUMP; + + if (rpcs & DODUMP) + if ((estat = callrpc(host, RPCPROG_MNT, mntvers, + RPCMNT_DUMP, xdr_void, (char *)0, + xdr_mntdump, (char *)&mntdump)) != 0) { + clnt_perrno(estat); + fprintf(stderr, ": Can't do Mountdump rpc\n"); + exit(1); + } + if (rpcs & DOEXPORTS) + if ((estat = callrpc(host, RPCPROG_MNT, mntvers, + RPCMNT_EXPORT, xdr_void, (char *)0, + xdr_exports, (char *)&exports)) != 0) { + clnt_perrno(estat); + fprintf(stderr, ": Can't do Exports rpc\n"); + exit(1); + } + + /* Now just print out the results */ + if (rpcs & DODUMP) { + switch (type) { + case ALL: + printf("All mount points on %s:\n", host); + break; + case DIRS: + printf("Directories on %s:\n", host); + break; + default: + printf("Hosts on %s:\n", host); + break; + }; + print_dump(mntdump); + } + if (rpcs & DOEXPORTS) { + printf("Exports list on %s:\n", host); + exp = exports; + while (exp) { + printf("%-35s", exp->ex_dirp); + grp = exp->ex_groups; + if (grp == NULL) { + printf("Everyone\n"); + } else { + while (grp) { + printf("%s ", grp->gr_name); + grp = grp->gr_next; + } + printf("\n"); + } + exp = exp->ex_next; + } + } + + exit(0); +} + +/* + * Xdr routine for retrieving the mount dump list + */ +int +xdr_mntdump(xdrsp, mlp) + XDR *xdrsp; + struct mountlist **mlp; +{ + struct mountlist *mp, **otp, *tp; + int bool, val, val2; + char *strp; + + *mlp = (struct mountlist *)0; + if (!xdr_bool(xdrsp, &bool)) + return (0); + while (bool) { + mp = (struct mountlist *)malloc(sizeof(struct mountlist)); + if (mp == NULL) + return (0); + mp->ml_left = mp->ml_right = (struct mountlist *)0; + strp = mp->ml_host; + if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN)) + return (0); + strp = mp->ml_dirp; + if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN)) + return (0); + + /* + * Build a binary tree on sorted order of either host or dirp. + * Drop any duplications. + */ + if (*mlp == NULL) { + *mlp = mp; + } else { + tp = *mlp; + while (tp) { + val = strcmp(mp->ml_host, tp->ml_host); + val2 = strcmp(mp->ml_dirp, tp->ml_dirp); + switch (type) { + case ALL: + if (val == 0) { + if (val2 == 0) { + free((caddr_t)mp); + goto next; + } + val = val2; + } + break; + case DIRS: + if (val2 == 0) { + free((caddr_t)mp); + goto next; + } + val = val2; + break; + default: + if (val == 0) { + free((caddr_t)mp); + goto next; + } + break; + }; + if (val < 0) { + otp = &tp->ml_left; + tp = tp->ml_left; + } else { + otp = &tp->ml_right; + tp = tp->ml_right; + } + } + *otp = mp; + } +next: + if (!xdr_bool(xdrsp, &bool)) + return (0); + } + return (1); +} + +/* + * Xdr routine to retrieve exports list + */ +int +xdr_exports(xdrsp, exp) + XDR *xdrsp; + struct exportslist **exp; +{ + struct exportslist *ep; + struct grouplist *gp; + int bool, grpbool; + char *strp; + + *exp = (struct exportslist *)0; + if (!xdr_bool(xdrsp, &bool)) + return (0); + while (bool) { + ep = (struct exportslist *)malloc(sizeof(struct exportslist)); + if (ep == NULL) + return (0); + ep->ex_groups = (struct grouplist *)0; + strp = ep->ex_dirp; + if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN)) + return (0); + if (!xdr_bool(xdrsp, &grpbool)) + return (0); + while (grpbool) { + gp = (struct grouplist *)malloc(sizeof(struct grouplist)); + if (gp == NULL) + return (0); + strp = gp->gr_name; + if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN)) + return (0); + gp->gr_next = ep->ex_groups; + ep->ex_groups = gp; + if (!xdr_bool(xdrsp, &grpbool)) + return (0); + } + ep->ex_next = *exp; + *exp = ep; + if (!xdr_bool(xdrsp, &bool)) + return (0); + } + return (1); +} + +void +usage() +{ + + fprintf(stderr, "usage: showmount [-ade] host\n"); + exit(1); +} + +/* + * Print the binary tree in inorder so that output is sorted. + */ +void +print_dump(mp) + struct mountlist *mp; +{ + + if (mp == NULL) + return; + if (mp->ml_left) + print_dump(mp->ml_left); + switch (type) { + case ALL: + printf("%s:%s\n", mp->ml_host, mp->ml_dirp); + break; + case DIRS: + printf("%s\n", mp->ml_dirp); + break; + default: + printf("%s\n", mp->ml_host); + break; + }; + if (mp->ml_right) + print_dump(mp->ml_right); +} diff --git a/usr.bin/sort/sort.1 b/usr.bin/sort/sort.1 new file mode 100644 index 00000000000..574efc35dd6 --- /dev/null +++ b/usr.bin/sort/sort.1 @@ -0,0 +1,310 @@ +.\" Copyright (c) 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)sort.1 8.2 (Berkeley) 5/4/95 +.\" +.Dd May 4, 1995 +.Dt SORT 1 +.Os +.Sh NAME +.Nm sort +.Nd sort or merge text files +.Sh SYNOPSIS +.Nm sort +.Op Fl mubdfinrtx +.Oo +.Cm \(pl Ns Ar pos1 +.Op Fl Ns Ar pos2 +.Oc +.Ar ... +.Op Fl o Ar output +.Op Fl T Ar directory +.Op Ar file +.Ar ... +.Sh DESCRIPTION +The +.Nm sort +utility +sorts text files by lines. +Comparisons are based on one or more sort keys (or fields) extracted +from each line of input, and are performed +lexicographically. By default, if keys are not given, +.Nm sort +regards each input line as a single field. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl c +Check that the single input file is sorted lexicographically. +If the file is not sorted, +.Nm sort +sorts it and writes the sorted output to the standard output or the +filename specified by the +.Fl o +option. +.It Fl m +Merge only; the input files are assumed to be pre-sorted. +.It Fl o Ar output +The argument given is the name of an +.Ar output +file to +be used instead of the standard output. +This file +can be the same as one of the input files. +.It Fl T Ar directory +The argument +.Ar directory +is used for creating temporary files. +.It Fl u +Unique: suppress all but one in each set of lines +having equal keys. +If used with the +.Fl c +option, +check that there are no lines with duplicate keys. +.El +.Pp +The following options override the default ordering rules. +When ordering options appear independent of key field +specifications, the requested field ordering rules are +applied globally to all sort keys. +.\" When attached to a +.\" specific key +.\" (see +.\" .Fl k ) , +.\" the specified ordering options override +.\" all global ordering options for that key. +.Bl -tag -width indent +.It Fl d +Only blank space and alphanumeric characters +.\" according +.\" to the current setting of LC_CTYPE +are used +in making comparisons. +.It Fl f +Considers all lowercase characters that have uppercase +equivalents to be the same for purposes of +comparison. +.It Fl i +Ignore all non-printable characters. +.It Fl n +An initial numeric string, consisting of optional +blank space, optional minus sign, and zero or more +digits (including decimal point) +.\" with +.\" optional radix character and thousands +.\" separator +.\" (as defined in the current locale), +is sorted by arithmetic value. +The +.Fl n +option implies +the +.Fl b +option. (See below.) +Note that the +.Fl b +option +is only effective when key fields have been specified +and that +.Fl \&0 +is considered equal to zero. +.It Fl r +Reverse the sense of comparisons. +.El +.Pp +The treatment of field separators can be altered using the +options: +.Bl -tag -width indent +.It Fl b +Leading blank spaces are ignored when determining the starting +ending positions of a restricted sort key. +If the +.Fl b +option is specified before the first +.Cm \(pl Ns Ar pos1 +argument, it shall be applied to all +.Cm \(pl Ns Ar pos1 +arguments. +Otherwise, the +.Fl b +option can be +attached independently to each +.Cm \(pl Ns Ar pos1 +or +.Fl Ar pos2 +argument (see below). +.It Fl t Ar char +.Ar Char +is used as the field separator character; +.Ar char +is not considered to be part of a field (although it +can be included in a sort key). +Each occurrence of +.Ar char +is significant (for example, +.Dq Ar charchar +delimits an empty field). +If +.Fl t +is not specified, +blank space characters are used as default field +separators. +.It Cm \(pl Ns Ar pos1 +Designates the start position of a key field. +.It Fl Ns Ar pos1 +Designates the end position of a key field. +.El +.Pp +The following operands are available: +.Bl -tag -width indent +.Ar file +The pathname of a file to be sorted, merged, or checked. +If no file +operands are specified, or if +a file operand is +.Fl , +the standard input is used. +.Pp +A field is +defined as a minimal sequence of characters followed by a +field separator or a newline character. +By default, the first +blank space of a sequence of blank spaces acts as the field separator. +All blank spaces in a sequence of blank spaces are considered +to be part of the next field; for example, all blank spaces at +the beginning of a line are considered to be part of the +first field. +.Pp +Fields are specified +by the +.Cm \(pl Ns Ar pos1 +and +.Fl Ar pos2 +arguments. A missing +.Cm \(pl Ns Ar pos1 +argument defaults to the beginning of a line. +A missing +.Fl Ar pos2 +argument defaults to the end of a line. +.Pp +The arguments +.Cm \(pl Ns Ar pos1 +and +.Fl Ar pos2 +have the form +.Em m.n +followed by one or more of the options +.Fl b , d , f , i , +.Fl n , r . +A +.Cm \(pl Ns Ar pos1 +position specified by +.Em m.n +is interpreted to +mean the +.Em n Ns th +character in the +.Em m Ns \(pl1th +field. +A missing +.Em \&.n +means +.Ql \&.0 , +indicating the first character of the +.Em m Ns \(pl1th +field. +If the +.Fl b +option is in effect, +.Em n +is counted from the first +non-blank character in the +.Em m Ns \(pl1th +field; +.Em m Ns \&.0b +refers to the first +non-blank character in the +.Em m Ns \(pl1th +field. +.Pp +A +.Fl Ar pos2 +position specified by +.Em m.n +is interpreted to mean +the +.Em n Ns th +character (including separators) after the last +character of the +.Em m Ns th +field. +A missing +.Em \&.n +means +.Ql \&.0 , +indicating +the last character of the +.Em m Ns th +field. +If the +.Fl b +option +is in effect, +.Em n +is counted from the last leading blank character in +the +.Em m Ns \(pl1th +field; +.Em m Ns \&.1b +refers to the first non-blank character in the +.Em m Ns \(pl1th +field. +.Sh FILES +.Bl -tag -width Pa -compact +.It Pa /var/tmp/stm*, /tmp/* +Default temporary directories (in order of search). +.El +.Sh SEE ALSO +.Xr comm 1 , +.Xr uniq 1 , +.Xr join 1 +.Sh DIAGNOSTICS +.Sh BUGS +Lines which are longer than 4096 are discarded and processing continues. +.Sh HISTORY +A +.Nm +command appeared in +.At v6 . diff --git a/usr.bin/split/split.c b/usr.bin/split/split.c new file mode 100644 index 00000000000..221ee31c0fd --- /dev/null +++ b/usr.bin/split/split.c @@ -0,0 +1,288 @@ +/* + * Copyright (c) 1987, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1987, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)split.c 8.3 (Berkeley) 4/25/94"; +#endif /* not lint */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define DEFLINE 1000 /* Default num lines per file. */ + +long bytecnt; /* Byte count to split on. */ +long numlines; /* Line count to split on. */ +int file_open; /* If a file open. */ +int ifd = -1, ofd = -1; /* Input/output file descriptors. */ +char bfr[MAXBSIZE]; /* I/O buffer. */ +char fname[MAXPATHLEN]; /* File name prefix. */ + +void newfile __P((void)); +void split1 __P((void)); +void split2 __P((void)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch; + char *ep, *p; + + while ((ch = getopt(argc, argv, "-0123456789b:l:")) != EOF) + switch (ch) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + /* + * Undocumented kludge: split was originally designed + * to take a number after a dash. + */ + if (numlines == 0) { + p = argv[optind - 1]; + if (p[0] == '-' && p[1] == ch && !p[2]) + numlines = strtol(++p, &ep, 10); + else + numlines = + strtol(argv[optind] + 1, &ep, 10); + if (numlines <= 0 || *ep) + errx(1, + "%s: illegal line count.", optarg); + } + break; + case '-': /* Undocumented: historic stdin flag. */ + if (ifd != -1) + usage(); + ifd = 0; + break; + case 'b': /* Byte count. */ + if ((bytecnt = strtol(optarg, &ep, 10)) <= 0 || + *ep != '\0' && *ep != 'k' && *ep != 'm') + errx(1, "%s: illegal byte count.", optarg); + if (*ep == 'k') + bytecnt *= 1024; + else if (*ep == 'm') + bytecnt *= 1048576; + break; + case 'l': /* Line count. */ + if (numlines != 0) + usage(); + if ((numlines = strtol(optarg, &ep, 10)) <= 0 || *ep) + errx(1, "%s: illegal line count.", optarg); + break; + default: + usage(); + } + argv += optind; + argc -= optind; + + if (*argv != NULL) + if (ifd == -1) { /* Input file. */ + if ((ifd = open(*argv, O_RDONLY, 0)) < 0) + err(1, "%s", *argv); + ++argv; + } + if (*argv != NULL) /* File name prefix. */ + (void)strcpy(fname, *argv++); + if (*argv != NULL) + usage(); + + if (numlines == 0) + numlines = DEFLINE; + else if (bytecnt) + usage(); + + if (ifd == -1) /* Stdin by default. */ + ifd = 0; + + if (bytecnt) { + split1(); + exit (0); + } + split2(); + exit(0); +} + +/* + * split1 -- + * Split the input by bytes. + */ +void +split1() +{ + long bcnt; + int dist, len; + char *C; + + for (bcnt = 0;;) + switch (len = read(ifd, bfr, MAXBSIZE)) { + case 0: + exit(0); + case -1: + err(1, "read"); + /* NOTREACHED */ + default: + if (!file_open) { + newfile(); + file_open = 1; + } + if (bcnt + len >= bytecnt) { + dist = bytecnt - bcnt; + if (write(ofd, bfr, dist) != dist) + err(1, "write"); + len -= dist; + for (C = bfr + dist; len >= bytecnt; + len -= bytecnt, C += bytecnt) { + newfile(); + if (write(ofd, + C, (int)bytecnt) != bytecnt) + err(1, "write"); + } + if (len) { + newfile(); + if (write(ofd, C, len) != len) + err(1, "write"); + } else + file_open = 0; + bcnt = len; + } else { + bcnt += len; + if (write(ofd, bfr, len) != len) + err(1, "write"); + } + } +} + +/* + * split2 -- + * Split the input by lines. + */ +void +split2() +{ + long lcnt; + int len, bcnt; + char *Ce, *Cs; + + for (lcnt = 0;;) + switch (len = read(ifd, bfr, MAXBSIZE)) { + case 0: + exit(0); + case -1: + err(1, "read"); + /* NOTREACHED */ + default: + if (!file_open) { + newfile(); + file_open = 1; + } + for (Cs = Ce = bfr; len--; Ce++) + if (*Ce == '\n' && ++lcnt == numlines) { + bcnt = Ce - Cs + 1; + if (write(ofd, Cs, bcnt) != bcnt) + err(1, "write"); + lcnt = 0; + Cs = Ce + 1; + if (len) + newfile(); + else + file_open = 0; + } + if (Cs < Ce) { + bcnt = Ce - Cs; + if (write(ofd, Cs, bcnt) != bcnt) + err(1, "write"); + } + } +} + +/* + * newfile -- + * Open a new output file. + */ +void +newfile() +{ + static long fnum; + static int defname; + static char *fpnt; + + if (ofd == -1) { + if (fname[0] == '\0') { + fname[0] = 'x'; + fpnt = fname + 1; + defname = 1; + } else { + fpnt = fname + strlen(fname); + defname = 0; + } + ofd = fileno(stdout); + } + /* + * Hack to increase max files; original code wandered through + * magic characters. Maximum files is 3 * 26 * 26 == 2028 + */ +#define MAXFILES 676 + if (fnum == MAXFILES) { + if (!defname || fname[0] == 'z') + errx(1, "too many files."); + ++fname[0]; + fnum = 0; + } + fpnt[0] = fnum / 26 + 'a'; + fpnt[1] = fnum % 26 + 'a'; + ++fnum; + if (!freopen(fname, "w", stdout)) + err(1, "%s", fname); +} + +void +usage() +{ + (void)fprintf(stderr, +"usage: split [-b byte_count] [-l line_count] [file [prefix]]\n"); + exit(1); +} diff --git a/usr.bin/strip/strip.c b/usr.bin/strip/strip.c new file mode 100644 index 00000000000..114d82300f3 --- /dev/null +++ b/usr.bin/strip/strip.c @@ -0,0 +1,236 @@ +/* + * 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[] = "@(#)strip.c 8.3 (Berkeley) 5/16/95"; +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct exec EXEC; +typedef struct nlist NLIST; + +#define strx n_un.n_strx + +void s_stab __P((const char *, int, EXEC *)); +void s_sym __P((const char *, int, EXEC *)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + register int fd, nb; + EXEC head; + void (*sfcn)__P((const char *, int, EXEC *)); + int ch, eval; + char *fn; + + sfcn = s_sym; + while ((ch = getopt(argc, argv, "d")) != EOF) + switch(ch) { + case 'd': + sfcn = s_stab; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + for (eval = 0; (fn = *argv++) != NULL;) { + if ((fd = open(fn, O_RDWR)) < 0) { + warn("%s", fn); + eval = 1; + continue; + } + if ((nb = read(fd, &head, sizeof(EXEC))) == -1) { + warn("%s", fn); + (void)close(fd); + eval = 1; + continue; + } + if (nb != sizeof(EXEC) || N_BADMAG(head)) { + warnx("%s: %s", fn, strerror(EFTYPE)); + (void)close(fd); + eval = 1; + continue; + } + sfcn(fn, fd, &head); + if (close(fd)) { + warn("%s", fn); + eval = 1; + } + } + exit(eval); +} + +void +s_sym(fn, fd, ep) + const char *fn; + int fd; + register EXEC *ep; +{ + register off_t fsize; + + /* If no symbols or data/text relocation info, quit. */ + if (!ep->a_syms && !ep->a_trsize && !ep->a_drsize) + return; + + /* + * New file size is the header plus text and data segments. + */ + fsize = N_DATOFF(*ep) + ep->a_data; + + /* Set symbol size and relocation info values to 0. */ + ep->a_syms = ep->a_trsize = ep->a_drsize = 0; + + /* Rewrite the header and truncate the file. */ + if (lseek(fd, (off_t)0, SEEK_SET) == -1 || + write(fd, ep, sizeof(EXEC)) != sizeof(EXEC) || + ftruncate(fd, fsize)) + err(0, "%s: %s", fn, strerror(errno)); +} + +void +s_stab(fn, fd, ep) + const char *fn; + int fd; + EXEC *ep; +{ + register int cnt, len; + register char *nstr, *nstrbase, *p, *strbase; + register NLIST *sym, *nsym; + struct stat sb; + NLIST *symbase; + + /* Quit if no symbols. */ + if (ep->a_syms == 0) + return; + + /* Stat the file. */ + if (fstat(fd, &sb) < 0) { + err(0, "%s: %s", fn, strerror(errno)); + return; + } + + /* Check size. */ + if (sb.st_size > SIZE_T_MAX) { + err(0, "%s: %s", fn, strerror(EFBIG)); + return; + } + + /* Map the file. */ + if ((ep = (EXEC *)mmap(NULL, (size_t)sb.st_size, + PROT_READ | PROT_WRITE, MAP_SHARED, fd, (off_t)0)) == (EXEC *)-1) { + err(0, "%s: %s", fn, strerror(errno)); + return; + } + + /* + * Initialize old and new symbol pointers. They both point to the + * beginning of the symbol table in memory, since we're deleting + * entries. + */ + sym = nsym = symbase = (NLIST *)((char *)ep + N_SYMOFF(*ep)); + + /* + * Allocate space for the new string table, initialize old and + * new string pointers. Handle the extra long at the beginning + * of the string table. + */ + strbase = (char *)ep + N_STROFF(*ep); + if ((nstrbase = malloc((u_int)*(u_long *)strbase)) == NULL) + err(1, "%s", strerror(errno)); + nstr = nstrbase + sizeof(u_long); + + /* + * Read through the symbol table. For each non-debugging symbol, + * copy it and save its string in the new string table. Keep + * track of the number of symbols. + */ + for (cnt = ep->a_syms / sizeof(NLIST); cnt--; ++sym) + if (!(sym->n_type & N_STAB) && sym->strx) { + *nsym = *sym; + nsym->strx = nstr - nstrbase; + p = strbase + sym->strx; + len = strlen(p) + 1; + bcopy(p, nstr, len); + nstr += len; + ++nsym; + } + + /* Fill in new symbol table size. */ + ep->a_syms = (nsym - symbase) * sizeof(NLIST); + + /* Fill in the new size of the string table. */ + *(u_long *)nstrbase = len = nstr - nstrbase; + + /* + * Copy the new string table into place. Nsym should be pointing + * at the address past the last symbol entry. + */ + bcopy(nstrbase, (void *)nsym, len); + + /* Truncate to the current length. */ + if (ftruncate(fd, (char *)nsym + len - (char *)ep)) + err(0, "%s: %s", fn, strerror(errno)); + munmap((caddr_t)ep, (size_t)sb.st_size); +} + +void +usage() +{ + (void)fprintf(stderr, "usage: strip [-d] file ...\n"); + exit(1); +} diff --git a/usr.bin/tcopy/tcopy.c b/usr.bin/tcopy/tcopy.c new file mode 100644 index 00000000000..d54fc684626 --- /dev/null +++ b/usr.bin/tcopy/tcopy.c @@ -0,0 +1,331 @@ +/* + * Copyright (c) 1985, 1987, 1993, 1995 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1985, 1987, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)tcopy.c 8.3 (Berkeley) 1/23/95"; +#endif /* not lint */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pathnames.h" + +#define MAXREC (64 * 1024) +#define NOCOUNT (-2) + +int filen, guesslen, maxblk = MAXREC; +long lastrec, record; +off_t size, tsize; +FILE *msg = stdout; + +void *getspace __P((int)); +void intr __P((int)); +void usage __P((void)); +void verify __P((int, int, char *)); +void writeop __P((int, int)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch, needeof, nw, inp, outp; + ssize_t lastnread, nread; + enum {READ, VERIFY, COPY, COPYVERIFY} op = READ; + sig_t oldsig; + char *buff, *inf; + + guesslen = 1; + while ((ch = getopt(argc, argv, "cs:vx")) != EOF) + switch((char)ch) { + case 'c': + op = COPYVERIFY; + break; + case 's': + maxblk = atoi(optarg); + if (maxblk <= 0) { + warnx("illegal block size"); + usage(); + } + guesslen = 0; + break; + case 'v': + op = VERIFY; + break; + case 'x': + msg = stderr; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + switch(argc) { + case 0: + if (op != READ) + usage(); + inf = _PATH_DEFTAPE; + break; + case 1: + if (op != READ) + usage(); + inf = argv[0]; + break; + case 2: + if (op == READ) + op = COPY; + inf = argv[0]; + if ((outp = open(argv[1], op == VERIFY ? O_RDONLY : + op == COPY ? O_WRONLY : O_RDWR, DEFFILEMODE)) < 0) { + err(3, argv[1]); + } + break; + default: + usage(); + } + + if ((inp = open(inf, O_RDONLY, 0)) < 0) + err(1, inf); + + buff = getspace(maxblk); + + if (op == VERIFY) { + verify(inp, outp, buff); + exit(0); + } + + if ((oldsig = signal(SIGINT, SIG_IGN)) != SIG_IGN) + (void) signal(SIGINT, intr); + + needeof = 0; + for (lastnread = NOCOUNT;;) { + if ((nread = read(inp, buff, maxblk)) == -1) { + while (errno == EINVAL && (maxblk -= 1024)) { + nread = read(inp, buff, maxblk); + if (nread >= 0) + goto r1; + } + err(1, "read error, file %d, record %ld", + filen, record); + } else if (nread != lastnread) { + if (lastnread != 0 && lastnread != NOCOUNT) { + if (lastrec == 0 && nread == 0) + fprintf(msg, "%ld records\n", record); + else if (record - lastrec > 1) + fprintf(msg, "records %ld to %ld\n", + lastrec, record); + else + fprintf(msg, "record %ld\n", lastrec); + } + if (nread != 0) + fprintf(msg, "file %d: block size %d: ", + filen, nread); + (void) fflush(stdout); + lastrec = record; + } +r1: guesslen = 0; + if (nread > 0) { + if (op == COPY || op == COPYVERIFY) { + if (needeof) { + writeop(outp, MTWEOF); + needeof = 0; + } + nw = write(outp, buff, nread); + if (nw != nread) { + int error = errno; + fprintf(stderr, + "write error, file %d, record %ld: ", + filen, record); + if (nw == -1) + fprintf(stderr, + ": %s", strerror(error)); + else + fprintf(stderr, + "write (%d) != read (%d)\n", + nw, nread); + fprintf(stderr, "copy aborted\n"); + exit(5); + } + } + size += nread; + record++; + } else { + if (lastnread <= 0 && lastnread != NOCOUNT) { + fprintf(msg, "eot\n"); + break; + } + fprintf(msg, + "file %d: eof after %ld records: %qd bytes\n", + filen, record, size); + needeof = 1; + filen++; + tsize += size; + size = record = lastrec = 0; + lastnread = 0; + } + lastnread = nread; + } + fprintf(msg, "total length: %qd bytes\n", tsize); + (void)signal(SIGINT, oldsig); + if (op == COPY || op == COPYVERIFY) { + writeop(outp, MTWEOF); + writeop(outp, MTWEOF); + if (op == COPYVERIFY) { + writeop(outp, MTREW); + writeop(inp, MTREW); + verify(inp, outp, buff); + } + } + exit(0); +} + +void +verify(inp, outp, outb) + int inp, outp; + char *outb; +{ + int eot, inmaxblk, inn, outmaxblk, outn; + char *inb; + + inb = getspace(maxblk); + inmaxblk = outmaxblk = maxblk; + for (eot = 0;; guesslen = 0) { + if ((inn = read(inp, inb, inmaxblk)) == -1) { + if (guesslen) + while (errno == EINVAL && (inmaxblk -= 1024)) { + inn = read(inp, inb, inmaxblk); + if (inn >= 0) + goto r1; + } + warn("read error"); + break; + } +r1: if ((outn = read(outp, outb, outmaxblk)) == -1) { + if (guesslen) + while (errno == EINVAL && (outmaxblk -= 1024)) { + outn = read(outp, outb, outmaxblk); + if (outn >= 0) + goto r2; + } + warn("read error"); + break; + } +r2: if (inn != outn) { + fprintf(msg, + "%s: tapes have different block sizes; %d != %d.\n", + "tcopy", inn, outn); + break; + } + if (!inn) { + if (eot++) { + fprintf(msg, "%s: tapes are identical.\n", + "tcopy"); + return; + } + } else { + if (bcmp(inb, outb, inn)) { + fprintf(msg, + "%s: tapes have different data.\n", + "tcopy"); + break; + } + eot = 0; + } + } + exit(1); +} + +void +intr(signo) + int signo; +{ + if (record) + if (record - lastrec > 1) + fprintf(msg, "records %ld to %ld\n", lastrec, record); + else + fprintf(msg, "record %ld\n", lastrec); + fprintf(msg, "interrupt at file %d: record %ld\n", filen, record); + fprintf(msg, "total length: %qd bytes\n", tsize + size); + exit(1); +} + +void * +getspace(blk) + int blk; +{ + void *bp; + + if ((bp = malloc((size_t)blk)) == NULL) + errx(11, "no memory"); + + return (bp); +} + +void +writeop(fd, type) + int fd, type; +{ + struct mtop op; + + op.mt_op = type; + op.mt_count = (daddr_t)1; + if (ioctl(fd, MTIOCTOP, (char *)&op) < 0) + err(6, "tape op"); +} + +void +usage() +{ + + fprintf(stderr, "usage: tcopy [-cvx] [-s maxblk] src [dest]\n"); + exit(1); +} diff --git a/usr.bin/tip/acu.c b/usr.bin/tip/acu.c new file mode 100644 index 00000000000..f7bde997241 --- /dev/null +++ b/usr.bin/tip/acu.c @@ -0,0 +1,196 @@ +/* + * 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 sccsid[] = "@(#)acu.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#include "tip.h" + +static acu_t *acu = NOACU; +static int conflag; +static void acuabort(); +static acu_t *acutype(); +static jmp_buf jmpbuf; +/* + * Establish connection for tip + * + * If DU is true, we should dial an ACU whose type is AT. + * The phone numbers are in PN, and the call unit is in CU. + * + * If the PN is an '@', then we consult the PHONES file for + * the phone numbers. This file is /etc/phones, unless overriden + * by an exported shell variable. + * + * The data base files must be in the format: + * host-name[ \t]*phone-number + * with the possibility of multiple phone numbers + * for a single host acting as a rotary (in the order + * found in the file). + */ +char * +connect() +{ + register char *cp = PN; + char *phnum, string[256]; + FILE *fd; + int tried = 0; + + if (!DU) { /* regular connect message */ + if (CM != NOSTR) + pwrite(FD, CM, size(CM)); + logent(value(HOST), "", DV, "call completed"); + return (NOSTR); + } + /* + * @ =>'s use data base in PHONES environment variable + * otherwise, use /etc/phones + */ + signal(SIGINT, acuabort); + signal(SIGQUIT, acuabort); + if (setjmp(jmpbuf)) { + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + printf("\ncall aborted\n"); + logent(value(HOST), "", "", "call aborted"); + if (acu != NOACU) { + boolean(value(VERBOSE)) = FALSE; + if (conflag) + disconnect(NOSTR); + else + (*acu->acu_abort)(); + } + return ("interrupt"); + } + if ((acu = acutype(AT)) == NOACU) + return ("unknown ACU type"); + if (*cp != '@') { + while (*cp) { + for (phnum = cp; *cp && *cp != ','; cp++) + ; + if (*cp) + *cp++ = '\0'; + + if (conflag = (*acu->acu_dialer)(phnum, CU)) { + if (CM != NOSTR) + pwrite(FD, CM, size(CM)); + logent(value(HOST), phnum, acu->acu_name, + "call completed"); + return (NOSTR); + } else + logent(value(HOST), phnum, acu->acu_name, + "call failed"); + tried++; + } + } else { + if ((fd = fopen(PH, "r")) == NOFILE) { + printf("%s: ", PH); + return ("can't open phone number file"); + } + while (fgets(string, sizeof(string), fd) != NOSTR) { + for (cp = string; !any(*cp, " \t\n"); cp++) + ; + if (*cp == '\n') { + fclose(fd); + return ("unrecognizable host name"); + } + *cp++ = '\0'; + if (strcmp(string, value(HOST))) + continue; + while (any(*cp, " \t")) + cp++; + if (*cp == '\n') { + fclose(fd); + return ("missing phone number"); + } + for (phnum = cp; *cp && *cp != ',' && *cp != '\n'; cp++) + ; + if (*cp) + *cp++ = '\0'; + + if (conflag = (*acu->acu_dialer)(phnum, CU)) { + fclose(fd); + if (CM != NOSTR) + pwrite(FD, CM, size(CM)); + logent(value(HOST), phnum, acu->acu_name, + "call completed"); + return (NOSTR); + } else + logent(value(HOST), phnum, acu->acu_name, + "call failed"); + tried++; + } + fclose(fd); + } + if (!tried) + logent(value(HOST), "", acu->acu_name, "missing phone number"); + else + (*acu->acu_abort)(); + return (tried ? "call failed" : "missing phone number"); +} + +disconnect(reason) + char *reason; +{ + if (!conflag) { + logent(value(HOST), "", DV, "call terminated"); + return; + } + if (reason == NOSTR) { + logent(value(HOST), "", acu->acu_name, "call terminated"); + if (boolean(value(VERBOSE))) + printf("\r\ndisconnecting..."); + } else + logent(value(HOST), "", acu->acu_name, reason); + (*acu->acu_disconnect)(); +} + +static void +acuabort(s) +{ + signal(s, SIG_IGN); + longjmp(jmpbuf, 1); +} + +static acu_t * +acutype(s) + register char *s; +{ + register acu_t *p; + extern acu_t acutable[]; + + for (p = acutable; p->acu_name != '\0'; p++) + if (!strcmp(s, p->acu_name)) + return (p); + return (NOACU); +} diff --git a/usr.bin/tip/aculib/biz22.c b/usr.bin/tip/aculib/biz22.c new file mode 100644 index 00000000000..93c5e53fc24 --- /dev/null +++ b/usr.bin/tip/aculib/biz22.c @@ -0,0 +1,187 @@ +/* + * 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 sccsid[] = "@(#)biz22.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#include "tip.h" + +#define DISCONNECT_CMD "\20\04" /* disconnection string */ + +static void sigALRM(); +static int timeout = 0; +static jmp_buf timeoutbuf; + +/* + * Dial up on a BIZCOMP Model 1022 with either + * tone dialing (mod = "V") + * pulse dialing (mod = "W") + */ +static int +biz_dialer(num, mod) + char *num, *mod; +{ + register int connected = 0; + char cbuf[40]; + static int cmd(), detect(); + + if (boolean(value(VERBOSE))) + printf("\nstarting call..."); + /* + * Disable auto-answer and configure for tone/pulse + * dialing + */ + if (cmd("\02K\r")) { + printf("can't initialize bizcomp..."); + return (0); + } + strcpy(cbuf, "\02.\r"); + cbuf[1] = *mod; + if (cmd(cbuf)) { + printf("can't set dialing mode..."); + return (0); + } + strcpy(cbuf, "\02D"); + strcat(cbuf, num); + strcat(cbuf, "\r"); + write(FD, cbuf, strlen(cbuf)); + if (!detect("7\r")) { + printf("can't get dial tone..."); + return (0); + } + if (boolean(value(VERBOSE))) + printf("ringing..."); + /* + * The reply from the BIZCOMP should be: + * 2 \r or 7 \r failure + * 1 \r success + */ + connected = detect("1\r"); +#ifdef ACULOG + if (timeout) { + char line[80]; + + sprintf(line, "%d second dial timeout", + number(value(DIALTIMEOUT))); + logent(value(HOST), num, "biz1022", line); + } +#endif + if (timeout) + biz22_disconnect(); /* insurance */ + return (connected); +} + +biz22w_dialer(num, acu) + char *num, *acu; +{ + + return (biz_dialer(num, "W")); +} + +biz22f_dialer(num, acu) + char *num, *acu; +{ + + return (biz_dialer(num, "V")); +} + +biz22_disconnect() +{ + int rw = 2; + + write(FD, DISCONNECT_CMD, 4); + sleep(2); + ioctl(FD, TIOCFLUSH, &rw); +} + +biz22_abort() +{ + + write(FD, "\02", 1); +} + +static void +sigALRM() +{ + + timeout = 1; + longjmp(timeoutbuf, 1); +} + +static int +cmd(s) + register char *s; +{ + sig_t f; + char c; + + write(FD, s, strlen(s)); + f = signal(SIGALRM, sigALRM); + if (setjmp(timeoutbuf)) { + biz22_abort(); + signal(SIGALRM, f); + return (1); + } + alarm(number(value(DIALTIMEOUT))); + read(FD, &c, 1); + alarm(0); + signal(SIGALRM, f); + c &= 0177; + return (c != '\r'); +} + +static int +detect(s) + register char *s; +{ + sig_t f; + char c; + + f = signal(SIGALRM, sigALRM); + timeout = 0; + while (*s) { + if (setjmp(timeoutbuf)) { + biz22_abort(); + break; + } + alarm(number(value(DIALTIMEOUT))); + read(FD, &c, 1); + alarm(0); + c &= 0177; + if (c != *s++) + return (0); + } + signal(SIGALRM, f); + return (timeout == 0); +} diff --git a/usr.bin/tip/aculib/biz31.c b/usr.bin/tip/aculib/biz31.c new file mode 100644 index 00000000000..412974de6e9 --- /dev/null +++ b/usr.bin/tip/aculib/biz31.c @@ -0,0 +1,248 @@ +/* + * 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 sccsid[] = "@(#)biz31.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#include "tip.h" + +#define MAXRETRY 3 /* sync up retry count */ +#define DISCONNECT_CMD "\21\25\11\24" /* disconnection string */ + +static void sigALRM(); +static int timeout = 0; +static jmp_buf timeoutbuf; + +/* + * Dial up on a BIZCOMP Model 1031 with either + * tone dialing (mod = "f") + * pulse dialing (mod = "w") + */ +static int +biz_dialer(num, mod) + char *num, *mod; +{ + register int connected = 0; + + if (!bizsync(FD)) { + logent(value(HOST), "", "biz", "out of sync"); + printf("bizcomp out of sync\n"); + delock(uucplock); + exit(0); + } + if (boolean(value(VERBOSE))) + printf("\nstarting call..."); + echo("#\rk$\r$\n"); /* disable auto-answer */ + echo("$>$.$ #\r"); /* tone/pulse dialing */ + echo(mod); + echo("$\r$\n"); + echo("$>$.$ #\re$ "); /* disconnection sequence */ + echo(DISCONNECT_CMD); + echo("\r$\n$\r$\n"); + echo("$>$.$ #\rr$ "); /* repeat dial */ + echo(num); + echo("\r$\n"); + if (boolean(value(VERBOSE))) + printf("ringing..."); + /* + * The reply from the BIZCOMP should be: + * `^G NO CONNECTION\r\n^G\r\n' failure + * ` CONNECTION\r\n^G' success + */ + connected = detect(" "); +#ifdef ACULOG + if (timeout) { + char line[80]; + + sprintf(line, "%d second dial timeout", + number(value(DIALTIMEOUT))); + logent(value(HOST), num, "biz", line); + } +#endif + if (!connected) + flush(" NO CONNECTION\r\n\07\r\n"); + else + flush("CONNECTION\r\n\07"); + if (timeout) + biz31_disconnect(); /* insurance */ + return (connected); +} + +biz31w_dialer(num, acu) + char *num, *acu; +{ + + return (biz_dialer(num, "w")); +} + +biz31f_dialer(num, acu) + char *num, *acu; +{ + + return (biz_dialer(num, "f")); +} + +biz31_disconnect() +{ + + write(FD, DISCONNECT_CMD, 4); + sleep(2); + ioctl(FD, TIOCFLUSH); +} + +biz31_abort() +{ + + write(FD, "\33", 1); +} + +static int +echo(s) + register char *s; +{ + char c; + + while (c = *s++) switch (c) { + + case '$': + read(FD, &c, 1); + s++; + break; + + case '#': + c = *s++; + write(FD, &c, 1); + break; + + default: + write(FD, &c, 1); + read(FD, &c, 1); + } +} + +static void +sigALRM() +{ + + timeout = 1; + longjmp(timeoutbuf, 1); +} + +static int +detect(s) + register char *s; +{ + sig_t f; + char c; + + f = signal(SIGALRM, sigALRM); + timeout = 0; + while (*s) { + if (setjmp(timeoutbuf)) { + printf("\07timeout waiting for reply\n"); + biz31_abort(); + break; + } + alarm(number(value(DIALTIMEOUT))); + read(FD, &c, 1); + alarm(0); + if (c != *s++) + break; + } + signal(SIGALRM, f); + return (timeout == 0); +} + +static int +flush(s) + register char *s; +{ + sig_t f; + char c; + + f = signal(SIGALRM, sigALRM); + while (*s++) { + if (setjmp(timeoutbuf)) + break; + alarm(10); + read(FD, &c, 1); + alarm(0); + } + signal(SIGALRM, f); + timeout = 0; /* guard against disconnection */ +} + +/* + * This convoluted piece of code attempts to get + * the bizcomp in sync. If you don't have the capacity or nread + * call there are gory ways to simulate this. + */ +static int +bizsync(fd) +{ +#ifdef FIOCAPACITY + struct capacity b; +# define chars(b) ((b).cp_nbytes) +# define IOCTL FIOCAPACITY +#endif +#ifdef FIONREAD + long b; +# define chars(b) (b) +# define IOCTL FIONREAD +#endif + register int already = 0; + char buf[10]; + +retry: + if (ioctl(fd, IOCTL, (caddr_t)&b) >= 0 && chars(b) > 0) + ioctl(fd, TIOCFLUSH); + write(fd, "\rp>\r", 4); + sleep(1); + if (ioctl(fd, IOCTL, (caddr_t)&b) >= 0) { + if (chars(b) != 10) { + nono: + if (already > MAXRETRY) + return (0); + write(fd, DISCONNECT_CMD, 4); + sleep(2); + already++; + goto retry; + } else { + read(fd, buf, 10); + if (strncmp(buf, "p >\r\n\r\n>", 8)) + goto nono; + } + } + return (1); +} diff --git a/usr.bin/tip/aculib/courier.c b/usr.bin/tip/aculib/courier.c new file mode 100644 index 00000000000..85f7b0dd9b0 --- /dev/null +++ b/usr.bin/tip/aculib/courier.c @@ -0,0 +1,380 @@ +/* + * Copyright (c) 1986, 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[] = "@(#)courier.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +/* + * Routines for calling up on a Courier modem. + * Derived from Hayes driver. + */ +#include "tip.h" +#include + +#define MAXRETRY 5 + +static void sigALRM(); +static int timeout = 0; +static int connected = 0; +static jmp_buf timeoutbuf, intbuf; +static int coursync(); + +cour_dialer(num, acu) + register char *num; + char *acu; +{ + register char *cp; +#ifdef ACULOG + char line[80]; +#endif + static int cour_connect(), cour_swallow(); + + if (boolean(value(VERBOSE))) + printf("Using \"%s\"\n", acu); + + ioctl(FD, TIOCHPCL, 0); + /* + * Get in synch. + */ + if (!coursync()) { +badsynch: + printf("can't synchronize with courier\n"); +#ifdef ACULOG + logent(value(HOST), num, "courier", "can't synch up"); +#endif + return (0); + } + cour_write(FD, "AT E0\r", 6); /* turn off echoing */ + sleep(1); +#ifdef DEBUG + if (boolean(value(VERBOSE))) + cour_verbose_read(); +#endif + ioctl(FD, TIOCFLUSH, 0); /* flush any clutter */ + cour_write(FD, "AT C1 E0 H0 Q0 X6 V1\r", 21); + if (!cour_swallow("\r\nOK\r\n")) + goto badsynch; + fflush(stdout); + cour_write(FD, "AT D", 4); + for (cp = num; *cp; cp++) + if (*cp == '=') + *cp = ','; + cour_write(FD, num, strlen(num)); + cour_write(FD, "\r", 1); + connected = cour_connect(); +#ifdef ACULOG + if (timeout) { + sprintf(line, "%d second dial timeout", + number(value(DIALTIMEOUT))); + logent(value(HOST), num, "cour", line); + } +#endif + if (timeout) + cour_disconnect(); + return (connected); +} + +cour_disconnect() +{ + /* first hang up the modem*/ + ioctl(FD, TIOCCDTR, 0); + sleep(1); + ioctl(FD, TIOCSDTR, 0); + coursync(); /* reset */ + close(FD); +} + +cour_abort() +{ + cour_write(FD, "\r", 1); /* send anything to abort the call */ + cour_disconnect(); +} + +static void +sigALRM() +{ + printf("\07timeout waiting for reply\n"); + timeout = 1; + longjmp(timeoutbuf, 1); +} + +static int +cour_swallow(match) + register char *match; + { + sig_t f; + char c; + + f = signal(SIGALRM, sigALRM); + timeout = 0; + do { + if (*match =='\0') { + signal(SIGALRM, f); + return (1); + } + if (setjmp(timeoutbuf)) { + signal(SIGALRM, f); + return (0); + } + alarm(number(value(DIALTIMEOUT))); + read(FD, &c, 1); + alarm(0); + c &= 0177; +#ifdef DEBUG + if (boolean(value(VERBOSE))) + putchar(c); +#endif + } while (c == *match++); +#ifdef DEBUG + if (boolean(value(VERBOSE))) + fflush(stdout); +#endif + signal(SIGALRM, SIG_DFL); + return (0); +} + +struct baud_msg { + char *msg; + int baud; +} baud_msg[] = { + "", B300, + " 1200", B1200, + " 2400", B2400, + " 9600", B9600, + " 9600/ARQ", B9600, + 0, 0, +}; + +static int +cour_connect() +{ + char c; + int nc, nl, n; + struct sgttyb sb; + char dialer_buf[64]; + struct baud_msg *bm; + sig_t f; + + if (cour_swallow("\r\n") == 0) + return (0); + f = signal(SIGALRM, sigALRM); +again: + nc = 0; nl = sizeof(dialer_buf)-1; + bzero(dialer_buf, sizeof(dialer_buf)); + timeout = 0; + for (nc = 0, nl = sizeof(dialer_buf)-1 ; nl > 0 ; nc++, nl--) { + if (setjmp(timeoutbuf)) + break; + alarm(number(value(DIALTIMEOUT))); + n = read(FD, &c, 1); + alarm(0); + if (n <= 0) + break; + c &= 0x7f; + if (c == '\r') { + if (cour_swallow("\n") == 0) + break; + if (!dialer_buf[0]) + goto again; + if (strcmp(dialer_buf, "RINGING") == 0 && + boolean(value(VERBOSE))) { +#ifdef DEBUG + printf("%s\r\n", dialer_buf); +#endif + goto again; + } + if (strncmp(dialer_buf, "CONNECT", + sizeof("CONNECT")-1) != 0) + break; + for (bm = baud_msg ; bm->msg ; bm++) + if (strcmp(bm->msg, + dialer_buf+sizeof("CONNECT")-1) == 0) { + if (ioctl(FD, TIOCGETP, &sb) < 0) { + perror("TIOCGETP"); + goto error; + } + sb.sg_ispeed = sb.sg_ospeed = bm->baud; + if (ioctl(FD, TIOCSETP, &sb) < 0) { + perror("TIOCSETP"); + goto error; + } + signal(SIGALRM, f); +#ifdef DEBUG + if (boolean(value(VERBOSE))) + printf("%s\r\n", dialer_buf); +#endif + return (1); + } + break; + } + dialer_buf[nc] = c; +#ifdef notdef + if (boolean(value(VERBOSE))) + putchar(c); +#endif + } +error1: + printf("%s\r\n", dialer_buf); +error: + signal(SIGALRM, f); + return (0); +} + +/* + * This convoluted piece of code attempts to get + * the courier in sync. + */ +static int +coursync() +{ + int already = 0; + int len; + char buf[40]; + + while (already++ < MAXRETRY) { + ioctl(FD, TIOCFLUSH, 0); /* flush any clutter */ + cour_write(FD, "\rAT Z\r", 6); /* reset modem */ + bzero(buf, sizeof(buf)); + sleep(1); + ioctl(FD, FIONREAD, &len); + if (len) { + len = read(FD, buf, sizeof(buf)); +#ifdef DEBUG + buf[len] = '\0'; + printf("coursync: (\"%s\")\n\r", buf); +#endif + if (index(buf, '0') || + (index(buf, 'O') && index(buf, 'K'))) + return(1); + } + /* + * If not strapped for DTR control, + * try to get command mode. + */ + sleep(1); + cour_write(FD, "+++", 3); + sleep(1); + /* + * Toggle DTR to force anyone off that might have left + * the modem connected. + */ + ioctl(FD, TIOCCDTR, 0); + sleep(1); + ioctl(FD, TIOCSDTR, 0); + } + cour_write(FD, "\rAT Z\r", 6); + return (0); +} + +cour_write(fd, cp, n) +int fd; +char *cp; +int n; +{ + struct sgttyb sb; +#ifdef notdef + if (boolean(value(VERBOSE))) + write(1, cp, n); +#endif + ioctl(fd, TIOCGETP, &sb); + ioctl(fd, TIOCSETP, &sb); + cour_nap(); + for ( ; n-- ; cp++) { + write(fd, cp, 1); + ioctl(fd, TIOCGETP, &sb); + ioctl(fd, TIOCSETP, &sb); + cour_nap(); + } +} + +#ifdef DEBUG +cour_verbose_read() +{ + int n = 0; + char buf[BUFSIZ]; + + if (ioctl(FD, FIONREAD, &n) < 0) + return; + if (n <= 0) + return; + if (read(FD, buf, n) != n) + return; + write(1, buf, n); +} +#endif + +/* + * Code stolen from /usr/src/lib/libc/gen/sleep.c + */ +#define mask(s) (1<<((s)-1)) +#define setvec(vec, a) \ + vec.sv_handler = a; vec.sv_mask = vec.sv_onstack = 0 + +static napms = 50; /* Give the courier 50 milliseconds between characters */ + +static int ringring; + +cour_nap() +{ + + static void cour_napx(); + int omask; + struct itimerval itv, oitv; + register struct itimerval *itp = &itv; + struct sigvec vec, ovec; + + timerclear(&itp->it_interval); + timerclear(&itp->it_value); + if (setitimer(ITIMER_REAL, itp, &oitv) < 0) + return; + setvec(ovec, SIG_DFL); + omask = sigblock(mask(SIGALRM)); + itp->it_value.tv_sec = napms/1000; + itp->it_value.tv_usec = ((napms%1000)*1000); + setvec(vec, cour_napx); + ringring = 0; + (void) sigvec(SIGALRM, &vec, &ovec); + (void) setitimer(ITIMER_REAL, itp, (struct itimerval *)0); + while (!ringring) + sigpause(omask &~ mask(SIGALRM)); + (void) sigvec(SIGALRM, &ovec, (struct sigvec *)0); + (void) setitimer(ITIMER_REAL, &oitv, (struct itimerval *)0); + (void) sigsetmask(omask); +} + +static void +cour_napx() +{ + ringring = 1; +} diff --git a/usr.bin/tip/aculib/df.c b/usr.bin/tip/aculib/df.c new file mode 100644 index 00000000000..5f294f99eb5 --- /dev/null +++ b/usr.bin/tip/aculib/df.c @@ -0,0 +1,132 @@ +/* + * 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 sccsid[] = "@(#)df.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +/* + * Dial the DF02-AC or DF03-AC + */ + +#include "tip.h" + +static jmp_buf Sjbuf; +static void timeout(); + +df02_dialer(num, acu) + char *num, *acu; +{ + + return (df_dialer(num, acu, 0)); +} + +df03_dialer(num, acu) + char *num, *acu; +{ + + return (df_dialer(num, acu, 1)); +} + +df_dialer(num, acu, df03) + char *num, *acu; + int df03; +{ + register int f = FD; + struct sgttyb buf; + int speed = 0, rw = 2; + char c = '\0'; + + ioctl(f, TIOCHPCL, 0); /* make sure it hangs up when done */ + if (setjmp(Sjbuf)) { + printf("connection timed out\r\n"); + df_disconnect(); + return (0); + } + if (boolean(value(VERBOSE))) + printf("\ndialing..."); + fflush(stdout); +#ifdef TIOCMSET + if (df03) { + int st = TIOCM_ST; /* secondary Transmit flag */ + + ioctl(f, TIOCGETP, &buf); + if (buf.sg_ospeed != B1200) { /* must dial at 1200 baud */ + speed = buf.sg_ospeed; + buf.sg_ospeed = buf.sg_ispeed = B1200; + ioctl(f, TIOCSETP, &buf); + ioctl(f, TIOCMBIC, &st); /* clear ST for 300 baud */ + } else + ioctl(f, TIOCMBIS, &st); /* set ST for 1200 baud */ + } +#endif + signal(SIGALRM, timeout); + alarm(5 * strlen(num) + 10); + ioctl(f, TIOCFLUSH, &rw); + write(f, "\001", 1); + sleep(1); + write(f, "\002", 1); + write(f, num, strlen(num)); + read(f, &c, 1); +#ifdef TIOCMSET + if (df03 && speed) { + buf.sg_ispeed = buf.sg_ospeed = speed; + ioctl(f, TIOCSETP, &buf); + } +#endif + return (c == 'A'); +} + +df_disconnect() +{ + int rw = 2; + + write(FD, "\001", 1); + sleep(1); + ioctl(FD, TIOCFLUSH, &rw); +} + + +df_abort() +{ + + df_disconnect(); +} + + +static void +timeout() +{ + + longjmp(Sjbuf, 1); +} diff --git a/usr.bin/tip/aculib/dn11.c b/usr.bin/tip/aculib/dn11.c new file mode 100644 index 00000000000..152b376b036 --- /dev/null +++ b/usr.bin/tip/aculib/dn11.c @@ -0,0 +1,142 @@ +/* + * 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 sccsid[] = "@(#)dn11.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +/* + * Routines for dialing up on DN-11 + */ +#include "tip.h" + +int dn_abort(); +void alarmtr(); +static jmp_buf jmpbuf; +static int child = -1, dn; + +dn_dialer(num, acu) + char *num, *acu; +{ + extern errno; + char *p, *q, phone[40]; + int lt, nw, connected = 1; + register int timelim; + + if (boolean(value(VERBOSE))) + printf("\nstarting call..."); + if ((dn = open(acu, 1)) < 0) { + if (errno == EBUSY) + printf("line busy..."); + else + printf("acu open error..."); + return (0); + } + if (setjmp(jmpbuf)) { + kill(child, SIGKILL); + close(dn); + return (0); + } + signal(SIGALRM, alarmtr); + timelim = 5 * strlen(num); + alarm(timelim < 30 ? 30 : timelim); + if ((child = fork()) == 0) { + /* + * ignore this stuff for aborts + */ + signal(SIGALRM, SIG_IGN); + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + sleep(2); + nw = write(dn, num, lt = strlen(num)); + exit(nw != lt); + } + /* + * open line - will return on carrier + */ + if ((FD = open(DV, 2)) < 0) { + if (errno == EIO) + printf("lost carrier..."); + else + printf("dialup line open failed..."); + alarm(0); + kill(child, SIGKILL); + close(dn); + return (0); + } + alarm(0); + ioctl(dn, TIOCHPCL, 0); + signal(SIGALRM, SIG_DFL); + while ((nw = wait(<)) != child && nw != -1) + ; + fflush(stdout); + close(dn); + if (lt != 0) { + close(FD); + return (0); + } + return (1); +} + +void +alarmtr() +{ + alarm(0); + longjmp(jmpbuf, 1); +} + +/* + * Insurance, for some reason we don't seem to be + * hanging up... + */ +dn_disconnect() +{ + + sleep(2); + if (FD > 0) + ioctl(FD, TIOCCDTR, 0); + close(FD); +} + +dn_abort() +{ + + sleep(2); + if (child > 0) + kill(child, SIGKILL); + if (dn > 0) + close(dn); + if (FD > 0) + ioctl(FD, TIOCCDTR, 0); + close(FD); +} diff --git a/usr.bin/tip/aculib/hayes.c b/usr.bin/tip/aculib/hayes.c new file mode 100644 index 00000000000..a2196f4f780 --- /dev/null +++ b/usr.bin/tip/aculib/hayes.c @@ -0,0 +1,305 @@ +/* + * 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 sccsid[] = "@(#)hayes.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +/* + * Routines for calling up on a Hayes Modem + * (based on the old VenTel driver). + * The modem is expected to be strapped for "echo". + * Also, the switches enabling the DTR and CD lines + * must be set correctly. + * NOTICE: + * The easy way to hang up a modem is always simply to + * clear the DTR signal. However, if the +++ sequence + * (which switches the modem back to local mode) is sent + * before modem is hung up, removal of the DTR signal + * has no effect (except that it prevents the modem from + * recognizing commands). + * (by Helge Skrivervik, Calma Company, Sunnyvale, CA. 1984) + */ +/* + * TODO: + * It is probably not a good idea to switch the modem + * state between 'verbose' and terse (status messages). + * This should be kicked out and we should use verbose + * mode only. This would make it consistent with normal + * interactive use thru the command 'tip dialer'. + */ +#include "tip.h" + +#define min(a,b) ((a < b) ? a : b) + +static void sigALRM(); +static int timeout = 0; +static jmp_buf timeoutbuf; +static char gobble(); +#define DUMBUFLEN 40 +static char dumbuf[DUMBUFLEN]; + +#define DIALING 1 +#define IDLE 2 +#define CONNECTED 3 +#define FAILED 4 +static int state = IDLE; + +hay_dialer(num, acu) + register char *num; + char *acu; +{ + register char *cp; + register int connected = 0; + char dummy; +#ifdef ACULOG + char line[80]; +#endif + if (hay_sync() == 0) /* make sure we can talk to the modem */ + return(0); + if (boolean(value(VERBOSE))) + printf("\ndialing..."); + fflush(stdout); + ioctl(FD, TIOCHPCL, 0); + ioctl(FD, TIOCFLUSH, 0); /* get rid of garbage */ + write(FD, "ATv0\r", 5); /* tell modem to use short status codes */ + gobble("\r"); + gobble("\r"); + write(FD, "ATTD", 4); /* send dial command */ + write(FD, num, strlen(num)); + state = DIALING; + write(FD, "\r", 1); + connected = 0; + if (gobble("\r")) { + if ((dummy = gobble("01234")) != '1') + error_rep(dummy); + else + connected = 1; + } + if (connected) + state = CONNECTED; + else { + state = FAILED; + return (connected); /* lets get out of here.. */ + } + ioctl(FD, TIOCFLUSH, 0); +#ifdef ACULOG + if (timeout) { + sprintf(line, "%d second dial timeout", + number(value(DIALTIMEOUT))); + logent(value(HOST), num, "hayes", line); + } +#endif + if (timeout) + hay_disconnect(); /* insurance */ + return (connected); +} + + +hay_disconnect() +{ + char c; + int len, rlen; + + /* first hang up the modem*/ +#ifdef DEBUG + printf("\rdisconnecting modem....\n\r"); +#endif + ioctl(FD, TIOCCDTR, 0); + sleep(1); + ioctl(FD, TIOCSDTR, 0); + goodbye(); +} + +hay_abort() +{ + + char c; + + write(FD, "\r", 1); /* send anything to abort the call */ + hay_disconnect(); +} + +static void +sigALRM() +{ + + printf("\07timeout waiting for reply\n\r"); + timeout = 1; + longjmp(timeoutbuf, 1); +} + +static char +gobble(match) + register char *match; +{ + char c; + sig_t f; + int i, status = 0; + + f = signal(SIGALRM, sigALRM); + timeout = 0; +#ifdef DEBUG + printf("\ngobble: waiting for %s\n", match); +#endif + do { + if (setjmp(timeoutbuf)) { + signal(SIGALRM, f); + return (0); + } + alarm(number(value(DIALTIMEOUT))); + read(FD, &c, 1); + alarm(0); + c &= 0177; +#ifdef DEBUG + printf("%c 0x%x ", c, c); +#endif + for (i = 0; i < strlen(match); i++) + if (c == match[i]) + status = c; + } while (status == 0); + signal(SIGALRM, SIG_DFL); +#ifdef DEBUG + printf("\n"); +#endif + return (status); +} + +error_rep(c) + register char c; +{ + printf("\n\r"); + switch (c) { + + case '0': + printf("OK"); + break; + + case '1': + printf("CONNECT"); + break; + + case '2': + printf("RING"); + break; + + case '3': + printf("NO CARRIER"); + break; + + case '4': + printf("ERROR in input"); + break; + + case '5': + printf("CONNECT 1200"); + break; + + default: + printf("Unknown Modem error: %c (0x%x)", c, c); + } + printf("\n\r"); + return; +} + +/* + * set modem back to normal verbose status codes. + */ +goodbye() +{ + int len, rlen; + char c; + + ioctl(FD, TIOCFLUSH, &len); /* get rid of trash */ + if (hay_sync()) { + sleep(1); +#ifndef DEBUG + ioctl(FD, TIOCFLUSH, 0); +#endif + write(FD, "ATH0\r", 5); /* insurance */ +#ifndef DEBUG + c = gobble("03"); + if (c != '0' && c != '3') { + printf("cannot hang up modem\n\r"); + printf("please use 'tip dialer' to make sure the line is hung up\n\r"); + } +#endif + sleep(1); + ioctl(FD, FIONREAD, &len); +#ifdef DEBUG + printf("goodbye1: len=%d -- ", len); + rlen = read(FD, dumbuf, min(len, DUMBUFLEN)); + dumbuf[rlen] = '\0'; + printf("read (%d): %s\r\n", rlen, dumbuf); +#endif + write(FD, "ATv1\r", 5); + sleep(1); +#ifdef DEBUG + ioctl(FD, FIONREAD, &len); + printf("goodbye2: len=%d -- ", len); + rlen = read(FD, dumbuf, min(len, DUMBUFLEN)); + dumbuf[rlen] = '\0'; + printf("read (%d): %s\r\n", rlen, dumbuf); +#endif + } + ioctl(FD, TIOCFLUSH, 0); /* clear the input buffer */ + ioctl(FD, TIOCCDTR, 0); /* clear DTR (insurance) */ + close(FD); +} + +#define MAXRETRY 5 + +hay_sync() +{ + int len, retry = 0; + + while (retry++ <= MAXRETRY) { + write(FD, "AT\r", 3); + sleep(1); + ioctl(FD, FIONREAD, &len); + if (len) { + len = read(FD, dumbuf, min(len, DUMBUFLEN)); + if (index(dumbuf, '0') || + (index(dumbuf, 'O') && index(dumbuf, 'K'))) + return(1); +#ifdef DEBUG + dumbuf[len] = '\0'; + printf("hay_sync: (\"%s\") %d\n\r", dumbuf, retry); +#endif + } + ioctl(FD, TIOCCDTR, 0); + ioctl(FD, TIOCSDTR, 0); + } + printf("Cannot synchronize with hayes...\n\r"); + return(0); +} diff --git a/usr.bin/tip/aculib/t3000.c b/usr.bin/tip/aculib/t3000.c new file mode 100644 index 00000000000..5e07359d4d0 --- /dev/null +++ b/usr.bin/tip/aculib/t3000.c @@ -0,0 +1,408 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)t3000.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +/* + * Routines for calling up on a Telebit T3000 modem. + * Derived from Courier driver. + */ +#include "tip.h" +#include + +#define MAXRETRY 5 + +static void sigALRM(); +static int timeout = 0; +static int connected = 0; +static jmp_buf timeoutbuf, intbuf; +static int t3000_sync(); + +t3000_dialer(num, acu) + register char *num; + char *acu; +{ + register char *cp; +#ifdef ACULOG + char line[80]; +#endif + static int t3000_connect(), t3000_swallow(); + + if (boolean(value(VERBOSE))) + printf("Using \"%s\"\n", acu); + + ioctl(FD, TIOCHPCL, 0); + /* + * Get in synch. + */ + if (!t3000_sync()) { +badsynch: + printf("can't synchronize with t3000\n"); +#ifdef ACULOG + logent(value(HOST), num, "t3000", "can't synch up"); +#endif + return (0); + } + t3000_write(FD, "AT E0\r", 6); /* turn off echoing */ + sleep(1); +#ifdef DEBUG + if (boolean(value(VERBOSE))) + t3000_verbose_read(); +#endif + ioctl(FD, TIOCFLUSH, 0); /* flush any clutter */ + t3000_write(FD, "AT E0 H0 Q0 X4 V1\r", 18); + if (!t3000_swallow("\r\nOK\r\n")) + goto badsynch; + fflush(stdout); + t3000_write(FD, "AT D", 4); + for (cp = num; *cp; cp++) + if (*cp == '=') + *cp = ','; + t3000_write(FD, num, strlen(num)); + t3000_write(FD, "\r", 1); + connected = t3000_connect(); +#ifdef ACULOG + if (timeout) { + sprintf(line, "%d second dial timeout", + number(value(DIALTIMEOUT))); + logent(value(HOST), num, "t3000", line); + } +#endif + if (timeout) + t3000_disconnect(); + return (connected); +} + +t3000_disconnect() +{ + /* first hang up the modem*/ + ioctl(FD, TIOCCDTR, 0); + sleep(1); + ioctl(FD, TIOCSDTR, 0); + t3000_sync(); /* reset */ + close(FD); +} + +t3000_abort() +{ + t3000_write(FD, "\r", 1); /* send anything to abort the call */ + t3000_disconnect(); +} + +static void +sigALRM() +{ + printf("\07timeout waiting for reply\n"); + timeout = 1; + longjmp(timeoutbuf, 1); +} + +static int +t3000_swallow(match) + register char *match; + { + sig_t f; + char c; + + f = signal(SIGALRM, sigALRM); + timeout = 0; + do { + if (*match =='\0') { + signal(SIGALRM, f); + return (1); + } + if (setjmp(timeoutbuf)) { + signal(SIGALRM, f); + return (0); + } + alarm(number(value(DIALTIMEOUT))); + read(FD, &c, 1); + alarm(0); + c &= 0177; +#ifdef DEBUG + if (boolean(value(VERBOSE))) + putchar(c); +#endif + } while (c == *match++); +#ifdef DEBUG + if (boolean(value(VERBOSE))) + fflush(stdout); +#endif + signal(SIGALRM, SIG_DFL); + return (0); +} + +#ifndef B19200 /* XXX */ +#define B19200 EXTA +#define B38400 EXTB +#endif + +struct tbaud_msg { + char *msg; + int baud; + int baud2; +} tbaud_msg[] = { + "", B300, 0, + " 1200", B1200, 0, + " 2400", B2400, 0, + " 4800", B4800, 0, + " 9600", B9600, 0, + " 14400", B19200, B9600, + " 19200", B19200, B9600, + " 38400", B38400, B9600, + " 57600", B38400, B9600, + " 7512", B9600, 0, + " 1275", B2400, 0, + " 7200", B9600, 0, + " 12000", B19200, B9600, + 0, 0, 0, +}; + +static int +t3000_connect() +{ + char c; + int nc, nl, n; + struct sgttyb sb; + char dialer_buf[64]; + struct tbaud_msg *bm; + sig_t f; + + if (t3000_swallow("\r\n") == 0) + return (0); + f = signal(SIGALRM, sigALRM); +again: + nc = 0; nl = sizeof(dialer_buf)-1; + bzero(dialer_buf, sizeof(dialer_buf)); + timeout = 0; + for (nc = 0, nl = sizeof(dialer_buf)-1 ; nl > 0 ; nc++, nl--) { + if (setjmp(timeoutbuf)) + break; + alarm(number(value(DIALTIMEOUT))); + n = read(FD, &c, 1); + alarm(0); + if (n <= 0) + break; + c &= 0x7f; + if (c == '\r') { + if (t3000_swallow("\n") == 0) + break; + if (!dialer_buf[0]) + goto again; + if (strcmp(dialer_buf, "RINGING") == 0 && + boolean(value(VERBOSE))) { +#ifdef DEBUG + printf("%s\r\n", dialer_buf); +#endif + goto again; + } + if (strncmp(dialer_buf, "CONNECT", + sizeof("CONNECT")-1) != 0) + break; + for (bm = tbaud_msg ; bm->msg ; bm++) + if (strcmp(bm->msg, + dialer_buf+sizeof("CONNECT")-1) == 0) { + if (ioctl(FD, TIOCGETP, &sb) < 0) { + perror("TIOCGETP"); + goto error; + } + sb.sg_ispeed = sb.sg_ospeed = bm->baud; + if (ioctl(FD, TIOCSETP, &sb) < 0) { + if (bm->baud2) { + sb.sg_ispeed = + sb.sg_ospeed = + bm->baud2; + if (ioctl(FD, + TIOCSETP, + &sb) >= 0) + goto isok; + } + perror("TIOCSETP"); + goto error; + } +isok: + signal(SIGALRM, f); +#ifdef DEBUG + if (boolean(value(VERBOSE))) + printf("%s\r\n", dialer_buf); +#endif + return (1); + } + break; + } + dialer_buf[nc] = c; +#ifdef notdef + if (boolean(value(VERBOSE))) + putchar(c); +#endif + } +error1: + printf("%s\r\n", dialer_buf); +error: + signal(SIGALRM, f); + return (0); +} + +/* + * This convoluted piece of code attempts to get + * the t3000 in sync. + */ +static int +t3000_sync() +{ + int already = 0; + int len; + char buf[40]; + + while (already++ < MAXRETRY) { + ioctl(FD, TIOCFLUSH, 0); /* flush any clutter */ + t3000_write(FD, "\rAT Z\r", 6); /* reset modem */ + bzero(buf, sizeof(buf)); + sleep(2); + ioctl(FD, FIONREAD, &len); +#if 1 +if (len == 0) len = 1; +#endif + if (len) { + len = read(FD, buf, sizeof(buf)); +#ifdef DEBUG + buf[len] = '\0'; + printf("t3000_sync: (\"%s\")\n\r", buf); +#endif + if (index(buf, '0') || + (index(buf, 'O') && index(buf, 'K'))) + return(1); + } + /* + * If not strapped for DTR control, + * try to get command mode. + */ + sleep(1); + t3000_write(FD, "+++", 3); + sleep(1); + /* + * Toggle DTR to force anyone off that might have left + * the modem connected. + */ + ioctl(FD, TIOCCDTR, 0); + sleep(1); + ioctl(FD, TIOCSDTR, 0); + } + t3000_write(FD, "\rAT Z\r", 6); + return (0); +} + +t3000_write(fd, cp, n) +int fd; +char *cp; +int n; +{ + struct sgttyb sb; + +#ifdef notdef + if (boolean(value(VERBOSE))) + write(1, cp, n); +#endif + ioctl(fd, TIOCGETP, &sb); + ioctl(fd, TIOCSETP, &sb); + t3000_nap(); + for ( ; n-- ; cp++) { + write(fd, cp, 1); + ioctl(fd, TIOCGETP, &sb); + ioctl(fd, TIOCSETP, &sb); + t3000_nap(); + } +} + +#ifdef DEBUG +t3000_verbose_read() +{ + int n = 0; + char buf[BUFSIZ]; + + if (ioctl(FD, FIONREAD, &n) < 0) + return; + if (n <= 0) + return; + if (read(FD, buf, n) != n) + return; + write(1, buf, n); +} +#endif + +/* + * Code stolen from /usr/src/lib/libc/gen/sleep.c + */ +#define mask(s) (1<<((s)-1)) +#define setvec(vec, a) \ + vec.sv_handler = a; vec.sv_mask = vec.sv_onstack = 0 + +static napms = 50; /* Give the t3000 50 milliseconds between characters */ + +static int ringring; + +t3000_nap() +{ + + static void t3000_napx(); + int omask; + struct itimerval itv, oitv; + register struct itimerval *itp = &itv; + struct sigvec vec, ovec; + + timerclear(&itp->it_interval); + timerclear(&itp->it_value); + if (setitimer(ITIMER_REAL, itp, &oitv) < 0) + return; + setvec(ovec, SIG_DFL); + omask = sigblock(mask(SIGALRM)); + itp->it_value.tv_sec = napms/1000; + itp->it_value.tv_usec = ((napms%1000)*1000); + setvec(vec, t3000_napx); + ringring = 0; + (void) sigvec(SIGALRM, &vec, &ovec); + (void) setitimer(ITIMER_REAL, itp, (struct itimerval *)0); + while (!ringring) + sigpause(omask &~ mask(SIGALRM)); + (void) sigvec(SIGALRM, &ovec, (struct sigvec *)0); + (void) setitimer(ITIMER_REAL, &oitv, (struct itimerval *)0); + (void) sigsetmask(omask); +} + +static void +t3000_napx() +{ + ringring = 1; +} diff --git a/usr.bin/tip/aculib/v3451.c b/usr.bin/tip/aculib/v3451.c new file mode 100644 index 00000000000..1623a58458f --- /dev/null +++ b/usr.bin/tip/aculib/v3451.c @@ -0,0 +1,214 @@ +/* + * 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 sccsid[] = "@(#)v3451.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +/* + * Routines for calling up on a Vadic 3451 Modem + */ +#include "tip.h" + +static jmp_buf Sjbuf; + +v3451_dialer(num, acu) + register char *num; + char *acu; +{ + sig_t func; + int ok; + int slow = number(value(BAUDRATE)) < 1200, rw = 2; + char phone[50]; +#ifdef ACULOG + char line[80]; +#endif + static int expect(); + static void vawrite(); + + /* + * Get in synch + */ + vawrite("I\r", 1 + slow); + vawrite("I\r", 1 + slow); + vawrite("I\r", 1 + slow); + vawrite("\005\r", 2 + slow); + if (!expect("READY")) { + printf("can't synchronize with vadic 3451\n"); +#ifdef ACULOG + logent(value(HOST), num, "vadic", "can't synch up"); +#endif + return (0); + } + ioctl(FD, TIOCHPCL, 0); + sleep(1); + vawrite("D\r", 2 + slow); + if (!expect("NUMBER?")) { + printf("Vadic will not accept dial command\n"); +#ifdef ACULOG + logent(value(HOST), num, "vadic", "will not accept dial"); +#endif + return (0); + } + strcpy(phone, num); + strcat(phone, "\r"); + vawrite(phone, 1 + slow); + if (!expect(phone)) { + printf("Vadic will not accept phone number\n"); +#ifdef ACULOG + logent(value(HOST), num, "vadic", "will not accept number"); +#endif + return (0); + } + func = signal(SIGINT,SIG_IGN); + /* + * You cannot interrupt the Vadic when its dialing; + * even dropping DTR does not work (definitely a + * brain damaged design). + */ + vawrite("\r", 1 + slow); + vawrite("\r", 1 + slow); + if (!expect("DIALING:")) { + printf("Vadic failed to dial\n"); +#ifdef ACULOG + logent(value(HOST), num, "vadic", "failed to dial"); +#endif + return (0); + } + if (boolean(value(VERBOSE))) + printf("\ndialing..."); + ok = expect("ON LINE"); + signal(SIGINT, func); + if (!ok) { + printf("call failed\n"); +#ifdef ACULOG + logent(value(HOST), num, "vadic", "call failed"); +#endif + return (0); + } + ioctl(FD, TIOCFLUSH, &rw); + return (1); +} + +v3451_disconnect() +{ + + close(FD); +} + +v3451_abort() +{ + + close(FD); +} + +static void +vawrite(cp, delay) + register char *cp; + int delay; +{ + + for (; *cp; sleep(delay), cp++) + write(FD, cp, 1); +} + +static +expect(cp) + register char *cp; +{ + char buf[300]; + register char *rp = buf; + int timeout = 30, online = 0; + static int notin(); + static void alarmtr(); + + if (strcmp(cp, "\"\"") == 0) + return (1); + *rp = 0; + /* + * If we are waiting for the Vadic to complete + * dialing and get a connection, allow more time + * Unfortunately, the Vadic times out 24 seconds after + * the last digit is dialed + */ + online = strcmp(cp, "ON LINE") == 0; + if (online) + timeout = number(value(DIALTIMEOUT)); + signal(SIGALRM, alarmtr); + if (setjmp(Sjbuf)) + return (0); + alarm(timeout); + while (notin(cp, buf) && rp < buf + sizeof (buf) - 1) { + if (online && notin("FAILED CALL", buf) == 0) + return (0); + if (read(FD, rp, 1) < 0) { + alarm(0); + return (0); + } + if (*rp &= 0177) + rp++; + *rp = '\0'; + } + alarm(0); + return (1); +} + +static void +alarmtr() +{ + longjmp(Sjbuf, 1); +} + +static int +notin(sh, lg) + char *sh, *lg; +{ + static int prefix(); + + for (; *lg; lg++) + if (prefix(sh, lg)) + return (0); + return (1); +} + +static +prefix(s1, s2) + register char *s1, *s2; +{ + register char c; + + while ((c = *s1++) == *s2++) + if (c == '\0') + return (1); + return (c == '\0'); +} diff --git a/usr.bin/tip/aculib/v831.c b/usr.bin/tip/aculib/v831.c new file mode 100644 index 00000000000..38aa23045ec --- /dev/null +++ b/usr.bin/tip/aculib/v831.c @@ -0,0 +1,259 @@ +/* + * 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 sccsid[] = "@(#)v831.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +/* + * Routines for dialing up on Vadic 831 + */ +#include "tip.h" + +int v831_abort(); +static void alarmtr(); +extern int errno; + +static jmp_buf jmpbuf; +static int child = -1; + +v831_dialer(num, acu) + char *num, *acu; +{ + int status, pid, connected = 1; + register int timelim; + static int dialit(); + + if (boolean(value(VERBOSE))) + printf("\nstarting call..."); +#ifdef DEBUG + printf ("(acu=%s)\n", acu); +#endif + if ((AC = open(acu, O_RDWR)) < 0) { + if (errno == EBUSY) + printf("line busy..."); + else + printf("acu open error..."); + return (0); + } + if (setjmp(jmpbuf)) { + kill(child, SIGKILL); + close(AC); + return (0); + } + signal(SIGALRM, alarmtr); + timelim = 5 * strlen(num); + alarm(timelim < 30 ? 30 : timelim); + if ((child = fork()) == 0) { + /* + * ignore this stuff for aborts + */ + signal(SIGALRM, SIG_IGN); + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + sleep(2); + exit(dialit(num, acu) != 'A'); + } + /* + * open line - will return on carrier + */ + if ((FD = open(DV, O_RDWR)) < 0) { +#ifdef DEBUG + printf("(after open, errno=%d)\n", errno); +#endif + if (errno == EIO) + printf("lost carrier..."); + else + printf("dialup line open failed..."); + alarm(0); + kill(child, SIGKILL); + close(AC); + return (0); + } + alarm(0); +#ifdef notdef + ioctl(AC, TIOCHPCL, 0); +#endif + signal(SIGALRM, SIG_DFL); + while ((pid = wait(&status)) != child && pid != -1) + ; + if (status) { + close(AC); + return (0); + } + return (1); +} + +static void +alarmtr() +{ + alarm(0); + longjmp(jmpbuf, 1); +} + +/* + * Insurance, for some reason we don't seem to be + * hanging up... + */ +v831_disconnect() +{ + struct sgttyb cntrl; + + sleep(2); +#ifdef DEBUG + printf("[disconnect: FD=%d]\n", FD); +#endif + if (FD > 0) { + ioctl(FD, TIOCCDTR, 0); + ioctl(FD, TIOCGETP, &cntrl); + cntrl.sg_ispeed = cntrl.sg_ospeed = 0; + ioctl(FD, TIOCSETP, &cntrl); + ioctl(FD, TIOCNXCL, (struct sgttyb *)NULL); + } + close(FD); +} + +v831_abort() +{ + +#ifdef DEBUG + printf("[abort: AC=%d]\n", AC); +#endif + sleep(2); + if (child > 0) + kill(child, SIGKILL); + if (AC > 0) + ioctl(FD, TIOCNXCL, (struct sgttyb *)NULL); + close(AC); + if (FD > 0) + ioctl(FD, TIOCCDTR, 0); + close(FD); +} + +/* + * Sigh, this probably must be changed at each site. + */ +struct vaconfig { + char *vc_name; + char vc_rack; + char vc_modem; +} vaconfig[] = { + { "/dev/cua0",'4','0' }, + { "/dev/cua1",'4','1' }, + { 0 } +}; + +#define pc(x) (c = x, write(AC,&c,1)) +#define ABORT 01 +#define SI 017 +#define STX 02 +#define ETX 03 + +static int +dialit(phonenum, acu) + register char *phonenum; + char *acu; +{ + register struct vaconfig *vp; + struct sgttyb cntrl; + char c; + int i, two = 2; + static char *sanitize(); + + phonenum = sanitize(phonenum); +#ifdef DEBUG + printf ("(dial phonenum=%s)\n", phonenum); +#endif + if (*phonenum == '<' && phonenum[1] == 0) + return ('Z'); + for (vp = vaconfig; vp->vc_name; vp++) + if (strcmp(vp->vc_name, acu) == 0) + break; + if (vp->vc_name == 0) { + printf("Unable to locate dialer (%s)\n", acu); + return ('K'); + } + ioctl(AC, TIOCGETP, &cntrl); + cntrl.sg_ispeed = cntrl.sg_ospeed = B2400; + cntrl.sg_flags = RAW | EVENP | ODDP; + ioctl(AC, TIOCSETP, &cntrl); + ioctl(AC, TIOCFLUSH, &two); + pc(STX); + pc(vp->vc_rack); + pc(vp->vc_modem); + while (*phonenum && *phonenum != '<') + pc(*phonenum++); + pc(SI); + pc(ETX); + sleep(1); + i = read(AC, &c, 1); +#ifdef DEBUG + printf("read %d chars, char=%c, errno %d\n", i, c, errno); +#endif + if (i != 1) + c = 'M'; + if (c == 'B' || c == 'G') { + char cc, oc = c; + + pc(ABORT); + read(AC, &cc, 1); +#ifdef DEBUG + printf("abort response=%c\n", cc); +#endif + c = oc; + v831_disconnect(); + } + close(AC); +#ifdef DEBUG + printf("dialit: returns %c\n", c); +#endif + return (c); +} + +static char * +sanitize(s) + register char *s; +{ + static char buf[128]; + register char *cp; + + for (cp = buf; *s; s++) { + if (!isdigit(*s) && *s == '<' && *s != '_') + continue; + if (*s == '_') + *s = '='; + *cp++ = *s; + } + *cp++ = 0; + return (buf); +} diff --git a/usr.bin/tip/aculib/ventel.c b/usr.bin/tip/aculib/ventel.c new file mode 100644 index 00000000000..28b0d282865 --- /dev/null +++ b/usr.bin/tip/aculib/ventel.c @@ -0,0 +1,251 @@ +/* + * 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 sccsid[] = "@(#)ventel.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +/* + * Routines for calling up on a Ventel Modem + * The Ventel is expected to be strapped for local echo (just like uucp) + */ +#include "tip.h" + +#define MAXRETRY 5 + +static void sigALRM(); +static int timeout = 0; +static jmp_buf timeoutbuf; + +/* + * some sleep calls have been replaced by this macro + * because some ventel modems require two s in less than + * a second in order to 'wake up'... yes, it is dirty... + */ +#define delay(num,denom) busyloop(CPUSPEED*num/denom) +#define CPUSPEED 1000000 /* VAX 780 is 1MIPS */ +#define DELAY(n) { register long N = (n); while (--N > 0); } +busyloop(n) { DELAY(n); } + +ven_dialer(num, acu) + register char *num; + char *acu; +{ + register char *cp; + register int connected = 0; + char *msg, *index(), line[80]; + static int gobble(), vensync(); + static void echo(); + + /* + * Get in synch with a couple of carriage returns + */ + if (!vensync(FD)) { + printf("can't synchronize with ventel\n"); +#ifdef ACULOG + logent(value(HOST), num, "ventel", "can't synch up"); +#endif + return (0); + } + if (boolean(value(VERBOSE))) + printf("\ndialing..."); + fflush(stdout); + ioctl(FD, TIOCHPCL, 0); + echo("#k$\r$\n$D$I$A$L$:$ "); + for (cp = num; *cp; cp++) { + delay(1, 10); + write(FD, cp, 1); + } + delay(1, 10); + write(FD, "\r", 1); + gobble('\n', line); + if (gobble('\n', line)) + connected = gobble('!', line); + ioctl(FD, TIOCFLUSH); +#ifdef ACULOG + if (timeout) { + sprintf(line, "%d second dial timeout", + number(value(DIALTIMEOUT))); + logent(value(HOST), num, "ventel", line); + } +#endif + if (timeout) + ven_disconnect(); /* insurance */ + if (connected || timeout || !boolean(value(VERBOSE))) + return (connected); + /* call failed, parse response for user */ + cp = index(line, '\r'); + if (cp) + *cp = '\0'; + for (cp = line; cp = index(cp, ' '); cp++) + if (cp[1] == ' ') + break; + if (cp) { + while (*cp == ' ') + cp++; + msg = cp; + while (*cp) { + if (isupper(*cp)) + *cp = tolower(*cp); + cp++; + } + printf("%s...", msg); + } + return (connected); +} + +ven_disconnect() +{ + + close(FD); +} + +ven_abort() +{ + + write(FD, "\03", 1); + close(FD); +} + +static void +echo(s) + register char *s; +{ + char c; + + while (c = *s++) switch (c) { + + case '$': + read(FD, &c, 1); + s++; + break; + + case '#': + c = *s++; + write(FD, &c, 1); + break; + + default: + write(FD, &c, 1); + read(FD, &c, 1); + } +} + +static void +sigALRM() +{ + printf("\07timeout waiting for reply\n"); + timeout = 1; + longjmp(timeoutbuf, 1); +} + +static int +gobble(match, response) + register char match; + char response[]; +{ + register char *cp = response; + sig_t f; + char c; + + f = signal(SIGALRM, sigALRM); + timeout = 0; + do { + if (setjmp(timeoutbuf)) { + signal(SIGALRM, f); + *cp = '\0'; + return (0); + } + alarm(number(value(DIALTIMEOUT))); + read(FD, cp, 1); + alarm(0); + c = (*cp++ &= 0177); +#ifdef notdef + if (boolean(value(VERBOSE))) + putchar(c); +#endif + } while (c != '\n' && c != match); + signal(SIGALRM, SIG_DFL); + *cp = '\0'; + return (c == match); +} + +#define min(a,b) ((a)>(b)?(b):(a)) +/* + * This convoluted piece of code attempts to get + * the ventel in sync. If you don't have FIONREAD + * there are gory ways to simulate this. + */ +static int +vensync(fd) +{ + int already = 0, nread; + char buf[60]; + + /* + * Toggle DTR to force anyone off that might have left + * the modem connected, and insure a consistent state + * to start from. + * + * If you don't have the ioctl calls to diddle directly + * with DTR, you can always try setting the baud rate to 0. + */ + ioctl(FD, TIOCCDTR, 0); + sleep(1); + ioctl(FD, TIOCSDTR, 0); + while (already < MAXRETRY) { + /* + * After reseting the modem, send it two \r's to + * autobaud on. Make sure to delay between them + * so the modem can frame the incoming characters. + */ + write(fd, "\r", 1); + delay(1,10); + write(fd, "\r", 1); + sleep(2); + if (ioctl(fd, FIONREAD, (caddr_t)&nread) < 0) { + perror("tip: ioctl"); + continue; + } + while (nread > 0) { + read(fd, buf, min(nread, 60)); + if ((buf[nread - 1] & 0177) == '$') + return (1); + nread -= min(nread, 60); + } + sleep(1); + already++; + } + return (0); +} + diff --git a/usr.bin/tip/acutab.c b/usr.bin/tip/acutab.c new file mode 100644 index 00000000000..112b43e4fe9 --- /dev/null +++ b/usr.bin/tip/acutab.c @@ -0,0 +1,97 @@ +/* + * 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 sccsid[] = "@(#)acutab.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#include "tip.h" + +extern int df02_dialer(), df03_dialer(), df_disconnect(), df_abort(), + biz31f_dialer(), biz31_disconnect(), biz31_abort(), + biz31w_dialer(), + biz22f_dialer(), biz22_disconnect(), biz22_abort(), + biz22w_dialer(), + ven_dialer(), ven_disconnect(), ven_abort(), + hay_dialer(), hay_disconnect(), hay_abort(), + cour_dialer(), cour_disconnect(), cour_abort(), + t3000_dialer(), t3000_disconnect(), t3000_abort(), + v3451_dialer(), v3451_disconnect(), v3451_abort(), + v831_dialer(), v831_disconnect(), v831_abort(), + dn_dialer(), dn_disconnect(), dn_abort(); + +acu_t acutable[] = { +#if BIZ1031 + "biz31f", biz31f_dialer, biz31_disconnect, biz31_abort, + "biz31w", biz31w_dialer, biz31_disconnect, biz31_abort, +#endif +#if BIZ1022 + "biz22f", biz22f_dialer, biz22_disconnect, biz22_abort, + "biz22w", biz22w_dialer, biz22_disconnect, biz22_abort, +#endif +#if DF02 + "df02", df02_dialer, df_disconnect, df_abort, +#endif +#if DF03 + "df03", df03_dialer, df_disconnect, df_abort, +#endif +#if DN11 + "dn11", dn_dialer, dn_disconnect, dn_abort, +#endif +#ifdef VENTEL + "ventel",ven_dialer, ven_disconnect, ven_abort, +#endif +#ifdef HAYES + "hayes",hay_dialer, hay_disconnect, hay_abort, +#endif +#ifdef COURIER + "courier",cour_dialer, cour_disconnect, cour_abort, +#endif +#ifdef T3000 + "t3000",t3000_dialer, t3000_disconnect, t3000_abort, +#endif +#ifdef V3451 +#ifndef V831 + "vadic",v3451_dialer, v3451_disconnect, v3451_abort, +#endif + "v3451",v3451_dialer, v3451_disconnect, v3451_abort, +#endif +#ifdef V831 +#ifndef V3451 + "vadic",v831_dialer, v831_disconnect, v831_abort, +#endif + "v831",v831_dialer, v831_disconnect, v831_abort, +#endif + 0, 0, 0, 0 +}; + diff --git a/usr.bin/tip/cmds.c b/usr.bin/tip/cmds.c new file mode 100644 index 00000000000..63bfee2c52c --- /dev/null +++ b/usr.bin/tip/cmds.c @@ -0,0 +1,888 @@ +/* + * 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 sccsid[] = "@(#)cmds.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#include "tip.h" +#include "pathnames.h" + +/* + * tip + * + * miscellaneous commands + */ + +int quant[] = { 60, 60, 24 }; + +char null = '\0'; +char *sep[] = { "second", "minute", "hour" }; +static char *argv[10]; /* argument vector for take and put */ + +void timeout(); /* timeout function called on alarm */ +void stopsnd(); /* SIGINT handler during file transfers */ +void intcopy(); /* interrupt routine for file transfers */ + +/* + * FTP - remote ==> local + * get a file from the remote host + */ +getfl(c) + char c; +{ + char buf[256], *cp, *expand(); + + putchar(c); + /* + * get the UNIX receiving file's name + */ + if (prompt("Local file name? ", copyname)) + return; + cp = expand(copyname); + if ((sfd = creat(cp, 0666)) < 0) { + printf("\r\n%s: cannot creat\r\n", copyname); + return; + } + + /* + * collect parameters + */ + if (prompt("List command for remote system? ", buf)) { + unlink(copyname); + return; + } + transfer(buf, sfd, value(EOFREAD)); +} + +/* + * Cu-like take command + */ +cu_take(cc) + char cc; +{ + int fd, argc; + char line[BUFSIZ], *expand(), *cp; + + if (prompt("[take] ", copyname)) + return; + if ((argc = args(copyname, argv)) < 1 || argc > 2) { + printf("usage: from [to]\r\n"); + return; + } + if (argc == 1) + argv[1] = argv[0]; + cp = expand(argv[1]); + if ((fd = creat(cp, 0666)) < 0) { + printf("\r\n%s: cannot create\r\n", argv[1]); + return; + } + sprintf(line, "cat %s;echo \01", argv[0]); + transfer(line, fd, "\01"); +} + +static jmp_buf intbuf; +/* + * Bulk transfer routine -- + * used by getfl(), cu_take(), and pipefile() + */ +transfer(buf, fd, eofchars) + char *buf, *eofchars; +{ + register int ct; + char c, buffer[BUFSIZ]; + register char *p = buffer; + register int cnt, eof; + time_t start; + sig_t f; + char r; + + pwrite(FD, buf, size(buf)); + quit = 0; + kill(pid, SIGIOT); + read(repdes[0], (char *)&ccc, 1); /* Wait until read process stops */ + + /* + * finish command + */ + r = '\r'; + pwrite(FD, &r, 1); + do + read(FD, &c, 1); + while ((c&0177) != '\n'); + ioctl(0, TIOCSETC, &defchars); + + (void) setjmp(intbuf); + f = signal(SIGINT, intcopy); + start = time(0); + for (ct = 0; !quit;) { + eof = read(FD, &c, 1) <= 0; + c &= 0177; + if (quit) + continue; + if (eof || any(c, eofchars)) + break; + if (c == 0) + continue; /* ignore nulls */ + if (c == '\r') + continue; + *p++ = c; + + if (c == '\n' && boolean(value(VERBOSE))) + printf("\r%d", ++ct); + if ((cnt = (p-buffer)) == number(value(FRAMESIZE))) { + if (write(fd, buffer, cnt) != cnt) { + printf("\r\nwrite error\r\n"); + quit = 1; + } + p = buffer; + } + } + if (cnt = (p-buffer)) + if (write(fd, buffer, cnt) != cnt) + printf("\r\nwrite error\r\n"); + + if (boolean(value(VERBOSE))) + prtime(" lines transferred in ", time(0)-start); + ioctl(0, TIOCSETC, &tchars); + write(fildes[1], (char *)&ccc, 1); + signal(SIGINT, f); + close(fd); +} + +/* + * FTP - remote ==> local process + * send remote input to local process via pipe + */ +pipefile() +{ + int cpid, pdes[2]; + char buf[256]; + int status, p; + extern int errno; + + if (prompt("Local command? ", buf)) + return; + + if (pipe(pdes)) { + printf("can't establish pipe\r\n"); + return; + } + + if ((cpid = fork()) < 0) { + printf("can't fork!\r\n"); + return; + } else if (cpid) { + if (prompt("List command for remote system? ", buf)) { + close(pdes[0]), close(pdes[1]); + kill (cpid, SIGKILL); + } else { + close(pdes[0]); + signal(SIGPIPE, intcopy); + transfer(buf, pdes[1], value(EOFREAD)); + signal(SIGPIPE, SIG_DFL); + while ((p = wait(&status)) > 0 && p != cpid) + ; + } + } else { + register int f; + + dup2(pdes[0], 0); + close(pdes[0]); + for (f = 3; f < 20; f++) + close(f); + execute(buf); + printf("can't execl!\r\n"); + exit(0); + } +} + +/* + * Interrupt service routine for FTP + */ +void +stopsnd() +{ + + stop = 1; + signal(SIGINT, SIG_IGN); +} + +/* + * FTP - local ==> remote + * send local file to remote host + * terminate transmission with pseudo EOF sequence + */ +sendfile(cc) + char cc; +{ + FILE *fd; + char *fnamex; + char *expand(); + + putchar(cc); + /* + * get file name + */ + if (prompt("Local file name? ", fname)) + return; + + /* + * look up file + */ + fnamex = expand(fname); + if ((fd = fopen(fnamex, "r")) == NULL) { + printf("%s: cannot open\r\n", fname); + return; + } + transmit(fd, value(EOFWRITE), NULL); + if (!boolean(value(ECHOCHECK))) { + struct sgttyb buf; + + ioctl(FD, TIOCGETP, &buf); /* this does a */ + ioctl(FD, TIOCSETP, &buf); /* wflushtty */ + } +} + +/* + * Bulk transfer routine to remote host -- + * used by sendfile() and cu_put() + */ +transmit(fd, eofchars, command) + FILE *fd; + char *eofchars, *command; +{ + char *pc, lastc; + int c, ccount, lcount; + time_t start_t, stop_t; + sig_t f; + + kill(pid, SIGIOT); /* put TIPOUT into a wait state */ + stop = 0; + f = signal(SIGINT, stopsnd); + ioctl(0, TIOCSETC, &defchars); + read(repdes[0], (char *)&ccc, 1); + if (command != NULL) { + for (pc = command; *pc; pc++) + send(*pc); + if (boolean(value(ECHOCHECK))) + read(FD, (char *)&c, 1); /* trailing \n */ + else { + struct sgttyb buf; + + ioctl(FD, TIOCGETP, &buf); /* this does a */ + ioctl(FD, TIOCSETP, &buf); /* wflushtty */ + sleep(5); /* wait for remote stty to take effect */ + } + } + lcount = 0; + lastc = '\0'; + start_t = time(0); + while (1) { + ccount = 0; + do { + c = getc(fd); + if (stop) + goto out; + if (c == EOF) + goto out; + if (c == 0177 && !boolean(value(RAWFTP))) + continue; + lastc = c; + if (c < 040) { + if (c == '\n') { + if (!boolean(value(RAWFTP))) + c = '\r'; + } + else if (c == '\t') { + if (!boolean(value(RAWFTP))) { + if (boolean(value(TABEXPAND))) { + send(' '); + while ((++ccount % 8) != 0) + send(' '); + continue; + } + } + } else + if (!boolean(value(RAWFTP))) + continue; + } + send(c); + } while (c != '\r' && !boolean(value(RAWFTP))); + if (boolean(value(VERBOSE))) + printf("\r%d", ++lcount); + if (boolean(value(ECHOCHECK))) { + timedout = 0; + alarm((int)value(ETIMEOUT)); + do { /* wait for prompt */ + read(FD, (char *)&c, 1); + if (timedout || stop) { + if (timedout) + printf("\r\ntimed out at eol\r\n"); + alarm(0); + goto out; + } + } while ((c&0177) != character(value(PROMPT))); + alarm(0); + } + } +out: + if (lastc != '\n' && !boolean(value(RAWFTP))) + send('\r'); + for (pc = eofchars; *pc; pc++) + send(*pc); + stop_t = time(0); + fclose(fd); + signal(SIGINT, f); + if (boolean(value(VERBOSE))) + if (boolean(value(RAWFTP))) + prtime(" chars transferred in ", stop_t-start_t); + else + prtime(" lines transferred in ", stop_t-start_t); + write(fildes[1], (char *)&ccc, 1); + ioctl(0, TIOCSETC, &tchars); +} + +/* + * Cu-like put command + */ +cu_put(cc) + char cc; +{ + FILE *fd; + char line[BUFSIZ]; + int argc; + char *expand(); + char *copynamex; + + if (prompt("[put] ", copyname)) + return; + if ((argc = args(copyname, argv)) < 1 || argc > 2) { + printf("usage: from [to]\r\n"); + return; + } + if (argc == 1) + argv[1] = argv[0]; + copynamex = expand(argv[0]); + if ((fd = fopen(copynamex, "r")) == NULL) { + printf("%s: cannot open\r\n", copynamex); + return; + } + if (boolean(value(ECHOCHECK))) + sprintf(line, "cat>%s\r", argv[1]); + else + sprintf(line, "stty -echo;cat>%s;stty echo\r", argv[1]); + transmit(fd, "\04", line); +} + +/* + * FTP - send single character + * wait for echo & handle timeout + */ +send(c) + char c; +{ + char cc; + int retry = 0; + + cc = c; + pwrite(FD, &cc, 1); +#ifdef notdef + if (number(value(CDELAY)) > 0 && c != '\r') + nap(number(value(CDELAY))); +#endif + if (!boolean(value(ECHOCHECK))) { +#ifdef notdef + if (number(value(LDELAY)) > 0 && c == '\r') + nap(number(value(LDELAY))); +#endif + return; + } +tryagain: + timedout = 0; + alarm((int)value(ETIMEOUT)); + read(FD, &cc, 1); + alarm(0); + if (timedout) { + printf("\r\ntimeout error (%s)\r\n", ctrl(c)); + if (retry++ > 3) + return; + pwrite(FD, &null, 1); /* poke it */ + goto tryagain; + } +} + +void +timeout() +{ + signal(SIGALRM, timeout); + timedout = 1; +} + +/* + * Stolen from consh() -- puts a remote file on the output of a local command. + * Identical to consh() except for where stdout goes. + */ +pipeout(c) +{ + char buf[256]; + int cpid, status, p; + time_t start; + + putchar(c); + if (prompt("Local command? ", buf)) + return; + kill(pid, SIGIOT); /* put TIPOUT into a wait state */ + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + ioctl(0, TIOCSETC, &defchars); + read(repdes[0], (char *)&ccc, 1); + /* + * Set up file descriptors in the child and + * let it go... + */ + if ((cpid = fork()) < 0) + printf("can't fork!\r\n"); + else if (cpid) { + start = time(0); + while ((p = wait(&status)) > 0 && p != cpid) + ; + } else { + register int i; + + dup2(FD, 1); + for (i = 3; i < 20; i++) + close(i); + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + execute(buf); + printf("can't find `%s'\r\n", buf); + exit(0); + } + if (boolean(value(VERBOSE))) + prtime("away for ", time(0)-start); + write(fildes[1], (char *)&ccc, 1); + ioctl(0, TIOCSETC, &tchars); + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); +} + +#ifdef CONNECT +/* + * Fork a program with: + * 0 <-> remote tty in + * 1 <-> remote tty out + * 2 <-> local tty out + */ +consh(c) +{ + char buf[256]; + int cpid, status, p; + time_t start; + + putchar(c); + if (prompt("Local command? ", buf)) + return; + kill(pid, SIGIOT); /* put TIPOUT into a wait state */ + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + ioctl(0, TIOCSETC, &defchars); + read(repdes[0], (char *)&ccc, 1); + /* + * Set up file descriptors in the child and + * let it go... + */ + if ((cpid = fork()) < 0) + printf("can't fork!\r\n"); + else if (cpid) { + start = time(0); + while ((p = wait(&status)) > 0 && p != cpid) + ; + } else { + register int i; + + dup2(FD, 0); + dup2(3, 1); + for (i = 3; i < 20; i++) + close(i); + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + execute(buf); + printf("can't find `%s'\r\n", buf); + exit(0); + } + if (boolean(value(VERBOSE))) + prtime("away for ", time(0)-start); + write(fildes[1], (char *)&ccc, 1); + ioctl(0, TIOCSETC, &tchars); + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); +} +#endif + +/* + * Escape to local shell + */ +shell() +{ + int shpid, status; + extern char **environ; + char *cp; + + printf("[sh]\r\n"); + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + unraw(); + if (shpid = fork()) { + while (shpid != wait(&status)); + raw(); + printf("\r\n!\r\n"); + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + return; + } else { + signal(SIGQUIT, SIG_DFL); + signal(SIGINT, SIG_DFL); + if ((cp = rindex(value(SHELL), '/')) == NULL) + cp = value(SHELL); + else + cp++; + shell_uid(); + execl(value(SHELL), cp, 0); + printf("\r\ncan't execl!\r\n"); + exit(1); + } +} + +/* + * TIPIN portion of scripting + * initiate the conversation with TIPOUT + */ +setscript() +{ + char c; + /* + * enable TIPOUT side for dialogue + */ + kill(pid, SIGEMT); + if (boolean(value(SCRIPT))) + write(fildes[1], value(RECORD), size(value(RECORD))); + write(fildes[1], "\n", 1); + /* + * wait for TIPOUT to finish + */ + read(repdes[0], &c, 1); + if (c == 'n') + printf("can't create %s\r\n", value(RECORD)); +} + +/* + * Change current working directory of + * local portion of tip + */ +chdirectory() +{ + char dirname[80]; + register char *cp = dirname; + + if (prompt("[cd] ", dirname)) { + if (stoprompt) + return; + cp = value(HOME); + } + if (chdir(cp) < 0) + printf("%s: bad directory\r\n", cp); + printf("!\r\n"); +} + +tipabort(msg) + char *msg; +{ + + kill(pid, SIGTERM); + disconnect(msg); + if (msg != NOSTR) + printf("\r\n%s", msg); + printf("\r\n[EOT]\r\n"); + daemon_uid(); + (void)uu_unlock(uucplock); + unraw(); + exit(0); +} + +finish() +{ + char *dismsg; + + if ((dismsg = value(DISCONNECT)) != NOSTR) { + write(FD, dismsg, strlen(dismsg)); + sleep(5); + } + tipabort(NOSTR); +} + +void +intcopy() +{ + raw(); + quit = 1; + longjmp(intbuf, 1); +} + +execute(s) + char *s; +{ + register char *cp; + + if ((cp = rindex(value(SHELL), '/')) == NULL) + cp = value(SHELL); + else + cp++; + shell_uid(); + execl(value(SHELL), cp, "-c", s, 0); +} + +args(buf, a) + char *buf, *a[]; +{ + register char *p = buf, *start; + register char **parg = a; + register int n = 0; + + do { + while (*p && (*p == ' ' || *p == '\t')) + p++; + start = p; + if (*p) + *parg = p; + while (*p && (*p != ' ' && *p != '\t')) + p++; + if (p != start) + parg++, n++; + if (*p) + *p++ = '\0'; + } while (*p); + + return(n); +} + +prtime(s, a) + char *s; + time_t a; +{ + register i; + int nums[3]; + + for (i = 0; i < 3; i++) { + nums[i] = (int)(a % quant[i]); + a /= quant[i]; + } + printf("%s", s); + while (--i >= 0) + if (nums[i] || i == 0 && nums[1] == 0 && nums[2] == 0) + printf("%d %s%c ", nums[i], sep[i], + nums[i] == 1 ? '\0' : 's'); + printf("\r\n!\r\n"); +} + +variable() +{ + char buf[256]; + + if (prompt("[set] ", buf)) + return; + vlex(buf); + if (vtable[BEAUTIFY].v_access&CHANGED) { + vtable[BEAUTIFY].v_access &= ~CHANGED; + kill(pid, SIGSYS); + } + if (vtable[SCRIPT].v_access&CHANGED) { + vtable[SCRIPT].v_access &= ~CHANGED; + setscript(); + /* + * So that "set record=blah script" doesn't + * cause two transactions to occur. + */ + if (vtable[RECORD].v_access&CHANGED) + vtable[RECORD].v_access &= ~CHANGED; + } + if (vtable[RECORD].v_access&CHANGED) { + vtable[RECORD].v_access &= ~CHANGED; + if (boolean(value(SCRIPT))) + setscript(); + } + if (vtable[TAND].v_access&CHANGED) { + vtable[TAND].v_access &= ~CHANGED; + if (boolean(value(TAND))) + tandem("on"); + else + tandem("off"); + } + if (vtable[LECHO].v_access&CHANGED) { + vtable[LECHO].v_access &= ~CHANGED; + HD = boolean(value(LECHO)); + } + if (vtable[PARITY].v_access&CHANGED) { + vtable[PARITY].v_access &= ~CHANGED; + setparity(); + } +} + +/* + * Turn tandem mode on or off for remote tty. + */ +tandem(option) + char *option; +{ + struct sgttyb rmtty; + + ioctl(FD, TIOCGETP, &rmtty); + if (strcmp(option,"on") == 0) { + rmtty.sg_flags |= TANDEM; + arg.sg_flags |= TANDEM; + } else { + rmtty.sg_flags &= ~TANDEM; + arg.sg_flags &= ~TANDEM; + } + ioctl(FD, TIOCSETP, &rmtty); + ioctl(0, TIOCSETP, &arg); +} + +/* + * Send a break. + */ +genbrk() +{ + + ioctl(FD, TIOCSBRK, NULL); + sleep(1); + ioctl(FD, TIOCCBRK, NULL); +} + +/* + * Suspend tip + */ +suspend(c) + char c; +{ + + unraw(); + kill(c == CTRL('y') ? getpid() : 0, SIGTSTP); + raw(); +} + +/* + * expand a file name if it includes shell meta characters + */ + +char * +expand(name) + char name[]; +{ + static char xname[BUFSIZ]; + char cmdbuf[BUFSIZ]; + register int pid, l, rc; + register char *cp, *Shell; + int s, pivec[2], (*sigint)(); + + if (!anyof(name, "~{[*?$`'\"\\")) + return(name); + /* sigint = signal(SIGINT, SIG_IGN); */ + if (pipe(pivec) < 0) { + perror("pipe"); + /* signal(SIGINT, sigint) */ + return(name); + } + sprintf(cmdbuf, "echo %s", name); + if ((pid = vfork()) == 0) { + Shell = value(SHELL); + if (Shell == NOSTR) + Shell = _PATH_BSHELL; + close(pivec[0]); + close(1); + dup(pivec[1]); + close(pivec[1]); + close(2); + shell_uid(); + execl(Shell, Shell, "-c", cmdbuf, 0); + _exit(1); + } + if (pid == -1) { + perror("fork"); + close(pivec[0]); + close(pivec[1]); + return(NOSTR); + } + close(pivec[1]); + l = read(pivec[0], xname, BUFSIZ); + close(pivec[0]); + while (wait(&s) != pid); + ; + s &= 0377; + if (s != 0 && s != SIGPIPE) { + fprintf(stderr, "\"Echo\" failed\n"); + return(NOSTR); + } + if (l < 0) { + perror("read"); + return(NOSTR); + } + if (l == 0) { + fprintf(stderr, "\"%s\": No match\n", name); + return(NOSTR); + } + if (l == BUFSIZ) { + fprintf(stderr, "Buffer overflow expanding \"%s\"\n", name); + return(NOSTR); + } + xname[l] = 0; + for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--) + ; + *++cp = '\0'; + return(xname); +} + +/* + * Are any of the characters in the two strings the same? + */ + +anyof(s1, s2) + register char *s1, *s2; +{ + register int c; + + while (c = *s1++) + if (any(c, s2)) + return(1); + return(0); +} diff --git a/usr.bin/tip/cmdtab.c b/usr.bin/tip/cmdtab.c new file mode 100644 index 00000000000..f6bcb60210f --- /dev/null +++ b/usr.bin/tip/cmdtab.c @@ -0,0 +1,64 @@ +/* + * 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 sccsid[] = "@(#)cmdtab.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#include "tip.h" + +extern int shell(), getfl(), sendfile(), chdirectory(); +extern int finish(), help(), pipefile(), pipeout(), consh(), variable(); +extern int cu_take(), cu_put(), dollar(), genbrk(), suspend(); + +esctable_t etable[] = { + { '!', NORM, "shell", shell }, + { '<', NORM, "receive file from remote host", getfl }, + { '>', NORM, "send file to remote host", sendfile }, + { 't', NORM, "take file from remote UNIX", cu_take }, + { 'p', NORM, "put file to remote UNIX", cu_put }, + { '|', NORM, "pipe remote file", pipefile }, + { '$', NORM, "pipe local command to remote host", pipeout }, +#ifdef CONNECT + { 'C', NORM, "connect program to remote host",consh }, +#endif + { 'c', NORM, "change directory", chdirectory }, + { '.', NORM, "exit from tip", finish }, + {CTRL('d'),NORM,"exit from tip", finish }, + {CTRL('y'),NORM,"suspend tip (local+remote)", suspend }, + {CTRL('z'),NORM,"suspend tip (local only)", suspend }, + { 's', NORM, "set variable", variable }, + { '?', NORM, "get this summary", help }, + { '#', NORM, "send break", genbrk }, + { 0, 0, 0 } +}; diff --git a/usr.bin/tip/cu.c b/usr.bin/tip/cu.c new file mode 100644 index 00000000000..fae2b3cc752 --- /dev/null +++ b/usr.bin/tip/cu.c @@ -0,0 +1,132 @@ +/* + * 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 sccsid[] = "@(#)cu.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#include "tip.h" + +void cleanup(); + +/* + * Botch the interface to look like cu's + */ +cumain(argc, argv) + char *argv[]; +{ + register int i; + static char sbuf[12]; + + if (argc < 2) { + printf("usage: cu telno [-t] [-s speed] [-a acu] [-l line] [-#]\n"); + exit(8); + } + CU = DV = NOSTR; + BR = DEFBR; + for (; argc > 1; argv++, argc--) { + if (argv[1][0] != '-') + PN = argv[1]; + else switch (argv[1][1]) { + + case 't': + HW = 1, DU = -1; + --argc; + continue; + + case 'a': + CU = argv[2]; ++argv; --argc; + break; + + case 's': + if (argc < 3 || speed(atoi(argv[2])) == 0) { + fprintf(stderr, "cu: unsupported speed %s\n", + argv[2]); + exit(3); + } + BR = atoi(argv[2]); ++argv; --argc; + break; + + case 'l': + DV = argv[2]; ++argv; --argc; + break; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + if (CU) + CU[strlen(CU)-1] = argv[1][1]; + if (DV) + DV[strlen(DV)-1] = argv[1][1]; + break; + + default: + printf("Bad flag %s", argv[1]); + break; + } + } + signal(SIGINT, cleanup); + signal(SIGQUIT, cleanup); + signal(SIGHUP, cleanup); + signal(SIGTERM, cleanup); + + /* + * The "cu" host name is used to define the + * attributes of the generic dialer. + */ + (void)sprintf(sbuf, "cu%d", BR); + if ((i = hunt(sbuf)) == 0) { + printf("all ports busy\n"); + exit(3); + } + if (i == -1) { + printf("link down\n"); + (void)uu_unlock(uucplock); + exit(3); + } + setbuf(stdout, NULL); + loginit(); + user_uid(); + vinit(); + setparity("none"); + boolean(value(VERBOSE)) = 0; + if (HW) + ttysetup(speed(BR)); + if (connect()) { + printf("Connect failed\n"); + daemon_uid(); + (void)uu_unlock(uucplock); + exit(1); + } + if (!HW) + ttysetup(speed(BR)); +} diff --git a/usr.bin/tip/hunt.c b/usr.bin/tip/hunt.c new file mode 100644 index 00000000000..650ac2601fd --- /dev/null +++ b/usr.bin/tip/hunt.c @@ -0,0 +1,93 @@ +/* + * 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 sccsid[] = "@(#)hunt.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#include "tip.h" + +extern char *getremote(); +extern char *rindex(); + +static jmp_buf deadline; +static int deadfl; + +void +dead() +{ + deadfl = 1; + longjmp(deadline, 1); +} + +hunt(name) + char *name; +{ + register char *cp; + sig_t f; + + f = signal(SIGALRM, dead); + while (cp = getremote(name)) { + deadfl = 0; + uucplock = rindex(cp, '/')+1; + if (uu_lock(uucplock) < 0) + continue; + /* + * Straight through call units, such as the BIZCOMP, + * VADIC and the DF, must indicate they're hardwired in + * order to get an open file descriptor placed in FD. + * Otherwise, as for a DN-11, the open will have to + * be done in the "open" routine. + */ + if (!HW) + break; + if (setjmp(deadline) == 0) { + alarm(10); + FD = open(cp, O_RDWR); + } + alarm(0); + if (FD < 0) { + perror(cp); + deadfl = 1; + } + if (!deadfl) { + ioctl(FD, TIOCEXCL, 0); + ioctl(FD, TIOCHPCL, 0); + signal(SIGALRM, SIG_DFL); + return ((int)cp); + } + (void)uu_unlock(uucplock); + } + signal(SIGALRM, f); + return (deadfl ? -1 : (int)cp); +} diff --git a/usr.bin/tip/log.c b/usr.bin/tip/log.c new file mode 100644 index 00000000000..5da2c45e4d5 --- /dev/null +++ b/usr.bin/tip/log.c @@ -0,0 +1,86 @@ +/* + * 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 sccsid[] = "@(#)log.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#include "tip.h" + +#ifdef ACULOG +static FILE *flog = NULL; + +/* + * Log file maintenance routines + */ + +logent(group, num, acu, message) + char *group, *num, *acu, *message; +{ + char *user, *timestamp; + struct passwd *pwd; + long t; + + if (flog == NULL) + return; + if (flock(fileno(flog), LOCK_EX) < 0) { + perror("tip: flock"); + return; + } + if ((user = getlogin()) == NOSTR) + if ((pwd = getpwuid(getuid())) == NOPWD) + user = "???"; + else + user = pwd->pw_name; + t = time(0); + timestamp = ctime(&t); + timestamp[24] = '\0'; + fprintf(flog, "%s (%s) <%s, %s, %s> %s\n", + user, timestamp, group, +#ifdef PRISTINE + "", +#else + num, +#endif + acu, message); + (void) fflush(flog); + (void) flock(fileno(flog), LOCK_UN); +} + +loginit() +{ + flog = fopen(value(LOG), "a"); + if (flog == NULL) + fprintf(stderr, "can't open log file %s.\r\n", value(LOG)); +} +#endif diff --git a/usr.bin/tip/partab.c b/usr.bin/tip/partab.c new file mode 100644 index 00000000000..1da4e239881 --- /dev/null +++ b/usr.bin/tip/partab.c @@ -0,0 +1,58 @@ +/* + * 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 sccsid[] = "@(#)partab.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +/* + * Even parity table for 0-0177 + */ +char evenpartab[] = { + 0000,0201,0202,0003,0204,0005,0006,0207, + 0210,0011,0012,0213,0014,0215,0216,0017, + 0220,0021,0022,0223,0024,0225,0226,0027, + 0030,0231,0232,0033,0234,0035,0036,0237, + 0240,0041,0042,0243,0044,0245,0246,0047, + 0050,0251,0252,0053,0254,0055,0056,0257, + 0060,0261,0262,0063,0264,0065,0066,0267, + 0270,0071,0072,0273,0074,0275,0276,0077, + 0300,0101,0102,0303,0104,0305,0306,0107, + 0110,0311,0312,0113,0314,0115,0116,0317, + 0120,0321,0322,0123,0324,0125,0126,0327, + 0330,0131,0132,0333,0134,0335,0336,0137, + 0140,0341,0342,0143,0344,0145,0146,0347, + 0350,0151,0152,0353,0154,0355,0356,0157, + 0360,0161,0162,0363,0164,0365,0366,0167, + 0170,0371,0372,0173,0374,0175,0176,0377, +}; diff --git a/usr.bin/tip/remcap.c b/usr.bin/tip/remcap.c new file mode 100644 index 00000000000..7a7e0c2732c --- /dev/null +++ b/usr.bin/tip/remcap.c @@ -0,0 +1,426 @@ +/* + * 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 sccsid[] = "@(#)remcap.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +/* + * remcap - routines for dealing with the remote host data base + * + * derived from termcap + */ +#include +#include +#include +#include +#include "pathnames.h" + +#ifndef BUFSIZ +#define BUFSIZ 1024 +#endif +#define MAXHOP 32 /* max number of tc= indirections */ + +#define tgetent rgetent +#define tnchktc rnchktc +#define tnamatch rnamatch +#define tgetnum rgetnum +#define tgetflag rgetflag +#define tgetstr rgetstr +#define E_TERMCAP RM = _PATH_REMOTE +#define V_TERMCAP "REMOTE" +#define V_TERM "HOST" + +char *RM; + +/* + * termcap - routines for dealing with the terminal capability data base + * + * BUG: Should use a "last" pointer in tbuf, so that searching + * for capabilities alphabetically would not be a n**2/2 + * process when large numbers of capabilities are given. + * Note: If we add a last pointer now we will screw up the + * tc capability. We really should compile termcap. + * + * Essentially all the work here is scanning and decoding escapes + * in string capabilities. We don't use stdio because the editor + * doesn't, and because living w/o it is not hard. + */ + +static char *tbuf; +static int hopcount; /* detect infinite loops in termcap, init 0 */ +static char *tskip(); +char *tgetstr(); +static char *tdecode(); +static char *remotefile; + +/* + * Get an entry for terminal name in buffer bp, + * from the termcap file. Parse is very rudimentary; + * we just notice escaped newlines. + */ +tgetent(bp, name) + char *bp, *name; +{ + char lbuf[BUFSIZ], *cp, *p; + int rc1, rc2; + + remotefile = cp = getenv(V_TERMCAP); + if (cp == (char *)0 || strcmp(cp, _PATH_REMOTE) == 0) { + remotefile = cp = _PATH_REMOTE; + return (getent(bp, name, cp)); + } else { + if ((rc1 = getent(bp, name, cp)) != 1) + *bp = '\0'; + remotefile = cp = _PATH_REMOTE; + rc2 = getent(lbuf, name, cp); + if (rc1 != 1 && rc2 != 1) + return (rc2); + if (rc2 == 1) { + p = lbuf; + if (rc1 == 1) + while (*p++ != ':') + ; + if (strlen(bp) + strlen(p) > BUFSIZ) { + write(2, "Remcap entry too long\n", 23); + return (-1); + } + strcat(bp, p); + } + tbuf = bp; + return (1); + } +} + +getent(bp, name, cp) + char *bp, *name, *cp; +{ + register int c; + register int i = 0, cnt = 0; + char ibuf[BUFSIZ], *cp2; + int tf; + + tbuf = bp; + tf = 0; + /* + * TERMCAP can have one of two things in it. It can be the + * name of a file to use instead of /etc/termcap. In this + * case it better start with a "/". Or it can be an entry to + * use so we don't have to read the file. In this case it + * has to already have the newlines crunched out. + */ + if (cp && *cp) { + if (*cp!='/') { + cp2 = getenv(V_TERM); + if (cp2 == (char *)0 || strcmp(name,cp2) == 0) { + strcpy(bp,cp); + return (tnchktc()); + } else + tf = open(E_TERMCAP, O_RDONLY); + } else + tf = open(RM = cp, O_RDONLY); + } + if (tf == 0) + tf = open(E_TERMCAP, O_RDONLY); + if (tf < 0) + return (-1); + for (;;) { + cp = bp; + for (;;) { + if (i == cnt) { + cnt = read(tf, ibuf, BUFSIZ); + if (cnt <= 0) { + close(tf); + return (0); + } + i = 0; + } + c = ibuf[i++]; + if (c == '\n') { + if (cp > bp && cp[-1] == '\\') { + cp--; + continue; + } + break; + } + if (cp >= bp+BUFSIZ) { + write(2,"Remcap entry too long\n", 23); + break; + } else + *cp++ = c; + } + *cp = 0; + + /* + * The real work for the match. + */ + if (tnamatch(name)) { + close(tf); + return (tnchktc()); + } + } +} + +/* + * tnchktc: check the last entry, see if it's tc=xxx. If so, + * recursively find xxx and append that entry (minus the names) + * to take the place of the tc=xxx entry. This allows termcap + * entries to say "like an HP2621 but doesn't turn on the labels". + * Note that this works because of the left to right scan. + */ +tnchktc() +{ + register char *p, *q; + char tcname[16]; /* name of similar terminal */ + char tcbuf[BUFSIZ]; + char *holdtbuf = tbuf; + int l; + char *cp; + + p = tbuf + strlen(tbuf) - 2; /* before the last colon */ + while (*--p != ':') + if (p MAXHOP) { + write(2, "Infinite tc= loop\n", 18); + return (0); + } + if (getent(tcbuf, tcname, remotefile) != 1) { + if (strcmp(remotefile, _PATH_REMOTE) == 0) + return (0); + else if (getent(tcbuf, tcname, _PATH_REMOTE) != 1) + return (0); + } + for (q = tcbuf; *q++ != ':'; ) + ; + l = p - holdtbuf + strlen(q); + if (l > BUFSIZ) { + write(2, "Remcap entry too long\n", 23); + q[BUFSIZ - (p-holdtbuf)] = 0; + } + strcpy(p, q); + tbuf = holdtbuf; + return (1); +} + +/* + * Tnamatch deals with name matching. The first field of the termcap + * entry is a sequence of names separated by |'s, so we compare + * against each such name. The normal : terminator after the last + * name (before the first field) stops us. + */ +tnamatch(np) + char *np; +{ + register char *Np, *Bp; + + Bp = tbuf; + if (*Bp == '#') + return (0); + for (;;) { + for (Np = np; *Np && *Bp == *Np; Bp++, Np++) + continue; + if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) + return (1); + while (*Bp && *Bp != ':' && *Bp != '|') + Bp++; + if (*Bp == 0 || *Bp == ':') + return (0); + Bp++; + } +} + +/* + * Skip to the next field. Notice that this is very dumb, not + * knowing about \: escapes or any such. If necessary, :'s can be put + * into the termcap file in octal. + */ +static char * +tskip(bp) + register char *bp; +{ + + while (*bp && *bp != ':') + bp++; + if (*bp == ':') + bp++; + return (bp); +} + +/* + * Return the (numeric) option id. + * Numeric options look like + * li#80 + * i.e. the option string is separated from the numeric value by + * a # character. If the option is not found we return -1. + * Note that we handle octal numbers beginning with 0. + */ +tgetnum(id) + char *id; +{ + register int i, base; + register char *bp = tbuf; + + for (;;) { + bp = tskip(bp); + if (*bp == 0) + return (-1); + if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) + continue; + if (*bp == '@') + return (-1); + if (*bp != '#') + continue; + bp++; + base = 10; + if (*bp == '0') + base = 8; + i = 0; + while (isdigit(*bp)) + i *= base, i += *bp++ - '0'; + return (i); + } +} + +/* + * Handle a flag option. + * Flag options are given "naked", i.e. followed by a : or the end + * of the buffer. Return 1 if we find the option, or 0 if it is + * not given. + */ +tgetflag(id) + char *id; +{ + register char *bp = tbuf; + + for (;;) { + bp = tskip(bp); + if (!*bp) + return (0); + if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) { + if (!*bp || *bp == ':') + return (1); + else if (*bp == '@') + return (0); + } + } +} + +/* + * Get a string valued option. + * These are given as + * cl=^Z + * Much decoding is done on the strings, and the strings are + * placed in area, which is a ref parameter which is updated. + * No checking on area overflow. + */ +char * +tgetstr(id, area) + char *id, **area; +{ + register char *bp = tbuf; + + for (;;) { + bp = tskip(bp); + if (!*bp) + return (0); + if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) + continue; + if (*bp == '@') + return (0); + if (*bp != '=') + continue; + bp++; + return (tdecode(bp, area)); + } +} + +/* + * Tdecode does the grung work to decode the + * string capability escapes. + */ +static char * +tdecode(str, area) + register char *str; + char **area; +{ + register char *cp; + register int c; + register char *dp; + int i; + + cp = *area; + while ((c = *str++) && c != ':') { + switch (c) { + + case '^': + c = *str++ & 037; + break; + + case '\\': + dp = "E\033^^\\\\::n\nr\rt\tb\bf\f"; + c = *str++; +nextc: + if (*dp++ == c) { + c = *dp++; + break; + } + dp++; + if (*dp) + goto nextc; + if (isdigit(c)) { + c -= '0', i = 2; + do + c <<= 3, c |= *str++ - '0'; + while (--i && isdigit(*str)); + } + break; + } + *cp++ = c; + } + *cp++ = 0; + str = *area; + *area = cp; + return (str); +} diff --git a/usr.bin/tip/remote.c b/usr.bin/tip/remote.c new file mode 100644 index 00000000000..9b860664137 --- /dev/null +++ b/usr.bin/tip/remote.c @@ -0,0 +1,226 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char 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[] = "@(#)remote.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#include +#include + +#include "pathnames.h" +#include "tip.h" + +/* + * Attributes to be gleened from remote host description + * data base. + */ +static char **caps[] = { + &AT, &DV, &CM, &CU, &EL, &IE, &OE, &PN, &PR, &DI, + &ES, &EX, &FO, &RC, &RE, &PA +}; + +static char *capstrings[] = { + "at", "dv", "cm", "cu", "el", "ie", "oe", "pn", "pr", + "di", "es", "ex", "fo", "rc", "re", "pa", 0 +}; + +static char *db_array[3] = { _PATH_REMOTE, 0, 0 }; + +#define cgetflag(f) (cgetcap(bp, f, ':') != NULL) + +static +getremcap(host) + register char *host; +{ + register char **p, ***q; + char *bp; + char *rempath; + int stat; + + rempath = getenv("REMOTE"); + if (rempath != NULL) + if (*rempath != '/') + /* we have an entry */ + cgetset(rempath); + else { /* we have a path */ + db_array[1] = rempath; + db_array[2] = _PATH_REMOTE; + } + + if ((stat = cgetent(&bp, db_array, host)) < 0) { + if (DV || + host[0] == '/' && access(DV = host, R_OK | W_OK) == 0) { + CU = DV; + HO = host; + HW = 1; + DU = 0; + if (!BR) + BR = DEFBR; + FS = DEFFS; + return; + } + switch(stat) { + case -1: + fprintf(stderr, "tip: unknown host %s\n", host); + break; + case -2: + fprintf(stderr, + "tip: can't open host description file\n"); + break; + case -3: + fprintf(stderr, + "tip: possible reference loop in host description file\n"); + break; + } + exit(3); + } + + for (p = capstrings, q = caps; *p != NULL; p++, q++) + if (**q == NULL) + cgetstr(bp, *p, *q); + if (!BR && (cgetnum(bp, "br", &BR) == -1)) + BR = DEFBR; + if (cgetnum(bp, "fs", &FS) == -1) + FS = DEFFS; + if (DU < 0) + DU = 0; + else + DU = cgetflag("du"); + if (DV == NOSTR) { + fprintf(stderr, "%s: missing device spec\n", host); + exit(3); + } + if (DU && CU == NOSTR) + CU = DV; + if (DU && PN == NOSTR) { + fprintf(stderr, "%s: missing phone number\n", host); + exit(3); + } + + HD = cgetflag("hd"); + + /* + * This effectively eliminates the "hw" attribute + * from the description file + */ + if (!HW) + HW = (CU == NOSTR) || (DU && equal(DV, CU)); + HO = host; + /* + * see if uppercase mode should be turned on initially + */ + if (cgetflag("ra")) + boolean(value(RAISE)) = 1; + if (cgetflag("ec")) + boolean(value(ECHOCHECK)) = 1; + if (cgetflag("be")) + boolean(value(BEAUTIFY)) = 1; + if (cgetflag("nb")) + boolean(value(BEAUTIFY)) = 0; + if (cgetflag("sc")) + boolean(value(SCRIPT)) = 1; + if (cgetflag("tb")) + boolean(value(TABEXPAND)) = 1; + if (cgetflag("vb")) + boolean(value(VERBOSE)) = 1; + if (cgetflag("nv")) + boolean(value(VERBOSE)) = 0; + if (cgetflag("ta")) + boolean(value(TAND)) = 1; + if (cgetflag("nt")) + boolean(value(TAND)) = 0; + if (cgetflag("rw")) + boolean(value(RAWFTP)) = 1; + if (cgetflag("hd")) + boolean(value(HALFDUPLEX)) = 1; + if (RE == NOSTR) + RE = (char *)"tip.record"; + if (EX == NOSTR) + EX = (char *)"\t\n\b\f"; + if (ES != NOSTR) + vstring("es", ES); + if (FO != NOSTR) + vstring("fo", FO); + if (PR != NOSTR) + vstring("pr", PR); + if (RC != NOSTR) + vstring("rc", RC); + if (cgetnum(bp, "dl", &DL) == -1) + DL = 0; + if (cgetnum(bp, "cl", &CL) == -1) + CL = 0; + if (cgetnum(bp, "et", &ET) == -1) + ET = 10; +} + +char * +getremote(host) + char *host; +{ + register char *cp; + static char *next; + static int lookedup = 0; + + if (!lookedup) { + if (host == NOSTR && (host = getenv("HOST")) == NOSTR) { + fprintf(stderr, "tip: no host specified\n"); + exit(3); + } + getremcap(host); + next = DV; + lookedup++; + } + /* + * We return a new device each time we're called (to allow + * a rotary action to be simulated) + */ + if (next == NOSTR) + return (NOSTR); + if ((cp = index(next, ',')) == NULL) { + DV = next; + next = NOSTR; + } else { + *cp++ = '\0'; + DV = next; + next = cp; + } + return (DV); +} diff --git a/usr.bin/tip/tip.1 b/usr.bin/tip/tip.1 new file mode 100644 index 00000000000..10b8a3ef4c9 --- /dev/null +++ b/usr.bin/tip/tip.1 @@ -0,0 +1,451 @@ +.\" 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. +.\" +.\" @(#)tip.1 8.4 (Berkeley) 4/18/94 +.\" +.Dd April 18, 1994 +.Dt TIP 1 +.Os BSD 4 +.Sh NAME +.Nm tip , +.Nm cu +.Nd connect to a remote system +.Sh SYNOPSIS +.Nm tip +.Op Fl v +.Fl Ns Ns Ar speed +.Ar system\-name +.Nm tip +.Op Fl v +.Fl Ns Ns Ar speed +.Ar phone\-number +.Nm cu +.Ar phone\-number +.Op Fl t +.Op Fl s Ar speed +.Op Fl a Ar acu +.Op Fl l Ar line +.Op Fl # +.Sh DESCRIPTION +.Nm Tip +and +.Ar cu +establish a full-duplex connection to another machine, +giving the appearance of being logged in directly on the +remote cpu. It goes without saying that you must have a login +on the machine (or equivalent) to which you wish to connect. +The preferred interface is +.Nm tip . +The +.Ar cu +interface is included for those people attached to the +``call +.Ux Ns '' +command of version 7. This manual page +describes only +.Nm tip . +.Pp +Available Option: +.Bl -tag -width indent +.It Fl v +Set verbose mode. +.El +.Pp +Typed characters are normally transmitted directly to the remote +machine (which does the echoing as well). A tilde (`~') appearing +as the first character of a line is an escape signal; the following +are recognized: +.Bl -tag -width flag +.It Ic \&~^D No or Ic \&~ . +Drop the connection and exit +(you may still be logged in on the +remote machine). +.It Ic \&~c Op Ar name +Change directory to +.Ar name +(no argument +implies change to your home directory). +.It Ic \&~! +Escape to a shell (exiting the shell will +return you to tip). +.It Ic \&~> +Copy file from local to remote. +.Nm Tip +prompts for the name of a local file to transmit. +.It Ic \&~< +Copy file from remote to local. +.Nm Tip +prompts first for the name of the file to be sent, then for +a command to be executed on the remote machine. +.It Ic \&~p Ar from Op Ar to +Send a file to a remote +.Ux +host. The put command causes the remote +.Ux +system to run the command string ``cat > 'to''', while +.Nm tip +sends it the ``from'' +file. If the ``to'' file isn't specified the ``from'' file name is used. +This command is actually a +.Ux +specific version of the ``~>'' command. +.It Ic \&~t Ar from Op Ar to +Take a file from a remote +.Ux +host. +As in the put command the ``to'' file +defaults to the ``from'' file name if it isn't specified. +The remote host +executes the command string ``cat 'from';echo ^A'' to send the file to +.Nm tip . +.It Ic \&~| +Pipe the output from a remote command to a local +.Ux +process. +The command string sent to the local +.Ux +system is processed by the shell. +.It Ic \&~$ +Pipe the output from a local +.Ux +process to the remote host. +The command string sent to the local +.Ux +system is processed by the shell. +.It Ic \&~C +Fork a child process on the local system to perform special protocols +such as \s-1XMODEM\s+1. The child program will be run with the following +somewhat unusual arrangement of file descriptors: +.nf +.in +1i +0 <-> local tty in +1 <-> local tty out +2 <-> local tty out +3 <-> remote tty in +4 <-> remote tty out +.in -1i +.fi +.It Ic \&~# +Send a +.Dv BREAK +to the remote system. +For systems which don't support the +necessary +.Ar ioctl +call the break is simulated by a sequence of line speed changes +and +.Dv DEL +characters. +.It Ic \&~s +Set a variable (see the discussion below). +.It Ic \&~^Z +Stop +.Nm tip +(only available with job control). +.It Ic \&~^Y +Stop only the ``local side'' of +.Nm tip +(only available with job control); +the ``remote side'' of +.Nm tip , +the side that displays output from the remote host, is left running. +.It Ic \&~? +Get a summary of the tilde escapes +.El +.Pp +.Nm Tip +uses the file +.Pa /etc/remote +to find how to reach a particular +system and to find out how it should operate while talking +to the system; +refer to +.Xr remote 5 +for a full description. +Each system has a default baud rate with which to +establish a connection. If this value is not suitable, the baud rate +to be used may be specified on the command line, e.g. +.Ql "tip -300 mds" . +.Pp +When +.Nm tip +establishes a connection it sends out a +connection message to the remote system; the default value, if any, +is defined in +.Pa /etc/remote +(see +.Xr remote 5 ) . +.Pp +When +.Nm tip +prompts for an argument (e.g. during setup of +a file transfer) the line typed may be edited with the standard +erase and kill characters. A null line in response to a prompt, +or an interrupt, will abort the dialogue and return you to the +remote machine. +.Pp +.Nm Tip +guards against multiple users connecting to a remote system +by opening modems and terminal lines with exclusive access, +and by honoring the locking protocol used by +.Xr uucico 8 . +.Pp +During file transfers +.Nm tip +provides a running count of the number of lines transferred. +When using the ~> and ~< commands, the ``eofread'' and ``eofwrite'' +variables are used to recognize end-of-file when reading, and +specify end-of-file when writing (see below). File transfers +normally depend on tandem mode for flow control. If the remote +system does not support tandem mode, ``echocheck'' may be set +to indicate +.Nm tip +should synchronize with the remote system on the echo of each +transmitted character. +.Pp +When +.Nm tip +must dial a phone number to connect to a system it will print +various messages indicating its actions. +.Nm Tip +supports the +.Tn DEC DN Ns-11 +and +Racal-Vadic 831 auto-call-units; +the +.Tn DEC DF Ns \&02 +and +.Tn DF Ns \&03 , +Ventel 212+, Racal-Vadic 3451, and +Bizcomp 1031 and 1032 integral call unit/modems. +.Ss VARIABLES +.Nm Tip +maintains a set of +.Ar variables +which control its operation. +Some of these variables are read-only to normal users (root is allowed +to change anything of interest). Variables may be displayed +and set through the ``s'' escape. The syntax for variables is patterned +after +.Xr vi 1 +and +.Xr Mail 1 . +Supplying ``all'' +as an argument to the set command displays all variables readable by +the user. Alternatively, the user may request display of a particular +variable by attaching a `?' to the end. For example ``escape?'' +displays the current escape character. +.Pp +Variables are numeric, string, character, or boolean values. Boolean +variables are set merely by specifying their name; they may be reset +by prepending a `!' to the name. Other variable types are set by +concatenating an `=' and the value. The entire assignment must not +have any blanks in it. A single set command may be used to interrogate +as well as set a number of variables. +Variables may be initialized at run time by placing set commands +(without the ``~s'' prefix in a file +.Pa .tiprc +in one's home directory). The +.Fl v +option causes +.Nm tip +to display the sets as they are made. +Certain common variables have abbreviations. +The following is a list of common variables, +their abbreviations, and their default values. +.Bl -tag -width Ar +.It Ar beautify +(bool) Discard unprintable characters when a session is being scripted; +abbreviated +.Ar be . +.It Ar baudrate +(num) The baud rate at which the connection was established; +abbreviated +.Ar ba . +.It Ar dialtimeout +(num) When dialing a phone number, the time (in seconds) +to wait for a connection to be established; abbreviated +.Ar dial . +.It Ar echocheck +(bool) Synchronize with the remote host during file transfer by +waiting for the echo of the last character transmitted; default is +.Ar off . +.It Ar eofread +(str) The set of characters which signify an end-of-transmission +during a ~< file transfer command; abbreviated +.Ar eofr . +.It Ar eofwrite +(str) The string sent to indicate end-of-transmission during +a ~> file transfer command; abbreviated +.Ar eofw . +.It Ar eol +(str) The set of characters which indicate an end-of-line. +.Nm Tip +will recognize escape characters only after an end-of-line. +.It Ar escape +(char) The command prefix (escape) character; abbreviated +.Ar es ; +default value is `~'. +.It Ar exceptions +(str) The set of characters which should not be discarded +due to the beautification switch; abbreviated +.Ar ex ; +default value is ``\et\en\ef\eb''. +.It Ar force +(char) The character used to force literal data transmission; +abbreviated +.Ar fo ; +default value is `^P'. +.It Ar framesize +(num) The amount of data (in bytes) to buffer between file system +writes when receiving files; abbreviated +.Ar fr . +.It Ar host +(str) The name of the host to which you are connected; abbreviated +.Ar ho . +.It Ar prompt +(char) The character which indicates an end-of-line on the remote +host; abbreviated +.Ar pr ; +default value is `\en'. This value is used to synchronize during +data transfers. The count of lines transferred during a file transfer +command is based on receipt of this character. +.It Ar raise +(bool) Upper case mapping mode; abbreviated +.Ar ra ; +default value is +.Ar off . +When this mode is enabled, all lower case letters will be mapped to +upper case by +.Nm tip +for transmission to the remote machine. +.It Ar raisechar +(char) The input character used to toggle upper case mapping mode; +abbreviated +.Ar rc ; +default value is `^A'. +.It Ar record +(str) The name of the file in which a session script is recorded; +abbreviated +.Ar rec ; +default value is ``tip.record''. +.It Ar script +(bool) Session scripting mode; abbreviated +.Ar sc ; +default is +.Ar off . +When +.Ar script +is +.Li true , +.Nm tip +will record everything transmitted by the remote machine in +the script record file specified in +.Ar record . +If the +.Ar beautify +switch is on, only printable +.Tn ASCII +characters will be included in +the script file (those characters betwee 040 and 0177). The +variable +.Ar exceptions +is used to indicate characters which are an exception to the normal +beautification rules. +.It Ar tabexpand +(bool) Expand tabs to spaces during file transfers; abbreviated +.Ar tab ; +default value is +.Ar false . +Each tab is expanded to 8 spaces. +.It Ar verbose +(bool) Verbose mode; abbreviated +.Ar verb ; +default is +.Ar true . +When verbose mode is enabled, +.Nm tip +prints messages while dialing, shows the current number +of lines transferred during a file transfer operations, +and more. +.El +.Sh ENVIRONMENT +.Nm Tip +uses the following environment variables: +.Bl -tag -width Fl +.It Ev SHELL +(str) The name of the shell to use for the ~! command; default +value is ``/bin/sh'', or taken from the environment. +.It Ev HOME +(str) The home directory to use for the ~c command; default +value is taken from the environment. +.It Ev HOST +Check for a default host if none specified. +.El +.Pp +The variables +.Ev ${REMOTE} +and +.Ev ${PHONES} +are also exported. +.Sh FILES +.Bl -tag -width /var/spool/uucp/LCK..* -compact +.It Pa /etc/remote +Global system descriptions. +.It Pa /etc/phones +Global phone number data base. +.It ${REMOTE} +Private system descriptions. +.It ${PHONES} +Private phone numbers. +.It ~/.tiprc +Initialization file. +.It Pa tip.record +Record file. +.It /var/log/aculog +Line access log. +.It Pa /var/spool/uucp/LCK..* +Lock file to avoid conflicts with +.Xr uucp . +.El +.Sh DIAGNOSTICS +Diagnostics are, hopefully, self explanatory. +.Sh SEE ALSO +.Xr remote 5 , +.Xr phones 5 +.Sh HISTORY +The +.Nm tip +appeared command in +.Bx 4.2 . +.Sh BUGS +The full set of variables is undocumented and should, probably, be +pared down. diff --git a/usr.bin/tip/value.c b/usr.bin/tip/value.c new file mode 100644 index 00000000000..ce29a21d647 --- /dev/null +++ b/usr.bin/tip/value.c @@ -0,0 +1,353 @@ +/* + * 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 sccsid[] = "@(#)value.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#include "tip.h" + +#define MIDDLE 35 + +static value_t *vlookup(); +static int col = 0; + +/* + * Variable manipulation + */ +vinit() +{ + register value_t *p; + register char *cp; + FILE *f; + char file[256]; + + for (p = vtable; p->v_name != NULL; p++) { + if (p->v_type&ENVIRON) + if (cp = getenv(p->v_name)) + p->v_value = cp; + if (p->v_type&IREMOTE) + number(p->v_value) = *address(p->v_value); + } + /* + * Read the .tiprc file in the HOME directory + * for sets + */ + strcpy(file, value(HOME)); + strcat(file, "/.tiprc"); + if ((f = fopen(file, "r")) != NULL) { + register char *tp; + + while (fgets(file, sizeof(file)-1, f) != NULL) { + if (vflag) + printf("set %s", file); + if (tp = rindex(file, '\n')) + *tp = '\0'; + vlex(file); + } + fclose(f); + } + /* + * To allow definition of exception prior to fork + */ + vtable[EXCEPTIONS].v_access &= ~(WRITE<v_access, WRITE)) { + printf("access denied\r\n"); + return; + } + switch (p->v_type&TMASK) { + + case STRING: + if (p->v_value && equal(p->v_value, v)) + return; + if (!(p->v_type&(ENVIRON|INIT))) + free(p->v_value); + if ((p->v_value = malloc(size(v)+1)) == NOSTR) { + printf("out of core\r\n"); + return; + } + p->v_type &= ~(ENVIRON|INIT); + strcpy(p->v_value, v); + break; + + case NUMBER: + if (number(p->v_value) == number(v)) + return; + number(p->v_value) = number(v); + break; + + case BOOL: + if (boolean(p->v_value) == (*v != '!')) + return; + boolean(p->v_value) = (*v != '!'); + break; + + case CHAR: + if (character(p->v_value) == *v) + return; + character(p->v_value) = *v; + } + p->v_access |= CHANGED; +} + +static void vprint(); + +vlex(s) + register char *s; +{ + register value_t *p; + static void vtoken(); + + if (equal(s, "all")) { + for (p = vtable; p->v_name; p++) + if (vaccess(p->v_access, READ)) + vprint(p); + } else { + register char *cp; + + do { + if (cp = vinterp(s, ' ')) + cp++; + vtoken(s); + s = cp; + } while (s); + } + if (col > 0) { + printf("\r\n"); + col = 0; + } +} + +static void +vtoken(s) + register char *s; +{ + register value_t *p; + register char *cp; + char *expand(); + + if (cp = index(s, '=')) { + *cp = '\0'; + if (p = vlookup(s)) { + cp++; + if (p->v_type&NUMBER) + vassign(p, atoi(cp)); + else { + if (strcmp(s, "record") == 0) + cp = expand(cp); + vassign(p, cp); + } + return; + } + } else if (cp = index(s, '?')) { + *cp = '\0'; + if ((p = vlookup(s)) && vaccess(p->v_access, READ)) { + vprint(p); + return; + } + } else { + if (*s != '!') + p = vlookup(s); + else + p = vlookup(s+1); + if (p != NOVAL) { + vassign(p, s); + return; + } + } + printf("%s: unknown variable\r\n", s); +} + +static void +vprint(p) + register value_t *p; +{ + register char *cp; + extern char *interp(), *ctrl(); + + if (col > 0 && col < MIDDLE) + while (col++ < MIDDLE) + putchar(' '); + col += size(p->v_name); + switch (p->v_type&TMASK) { + + case BOOL: + if (boolean(p->v_value) == FALSE) { + col++; + putchar('!'); + } + printf("%s", p->v_name); + break; + + case STRING: + printf("%s=", p->v_name); + col++; + if (p->v_value) { + cp = interp(p->v_value, NULL); + col += size(cp); + printf("%s", cp); + } + break; + + case NUMBER: + col += 6; + printf("%s=%-5d", p->v_name, number(p->v_value)); + break; + + case CHAR: + printf("%s=", p->v_name); + col++; + if (p->v_value) { + cp = ctrl(character(p->v_value)); + col += size(cp); + printf("%s", cp); + } + break; + } + if (col >= MIDDLE) { + col = 0; + printf("\r\n"); + return; + } +} + + +static int +vaccess(mode, rw) + register unsigned mode, rw; +{ + if (mode & (rw<v_name; p++) + if (equal(p->v_name, s) || (p->v_abrev && equal(p->v_abrev, s))) + return (p); + return (NULL); +} + +char * +vinterp(s, stop) + register char *s; + char stop; +{ + register char *p = s, c; + int num; + + while ((c = *s++) && c != stop) + switch (c) { + + case '^': + if (*s) + *p++ = *s++ - 0100; + else + *p++ = c; + break; + + case '\\': + num = 0; + c = *s++; + if (c >= '0' && c <= '7') + num = (num<<3)+(c-'0'); + else { + register char *q = "n\nr\rt\tb\bf\f"; + + for (; *q; q++) + if (c == *q++) { + *p++ = *q; + goto cont; + } + *p++ = c; + cont: + break; + } + if ((c = *s++) >= '0' && c <= '7') { + num = (num<<3)+(c-'0'); + if ((c = *s++) >= '0' && c <= '7') + num = (num<<3)+(c-'0'); + else + s--; + } else + s--; + *p++ = num; + break; + + default: + *p++ = c; + } + *p = '\0'; + return (c == stop ? s-1 : NULL); +} + +/* + * assign variable s with value v (for NUMBER or STRING or CHAR types) + */ + +vstring(s,v) + register char *s; + register char *v; +{ + register value_t *p; + char *expand(); + + p = vlookup(s); + if (p == 0) + return (1); + if (p->v_type&NUMBER) + vassign(p, atoi(v)); + else { + if (strcmp(s, "record") == 0) + v = expand(v); + vassign(p, v); + } + return (0); +} diff --git a/usr.bin/tip/vars.c b/usr.bin/tip/vars.c new file mode 100644 index 00000000000..debe01bed05 --- /dev/null +++ b/usr.bin/tip/vars.c @@ -0,0 +1,112 @@ +/* + * 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 sccsid[] = "@(#)vars.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#include "tip.h" +#include "pathnames.h" + +/* + * Definition of variables + */ +value_t vtable[] = { + { "beautify", BOOL, (READ|WRITE)<0 if an error occurs. +.Sh SEE ALSO +.Xr utimes 2 +.Sh COMPATIBILITY +The obsolescent form of +.Nm touch , +where a time format is specified as the first argument, is supported. +When no +.Fl r +or +.Fl t +option is specified, there are at least two arguments, and the first +argument is a string of digits either eight or ten characters in length, +the first argument is interpreted as a time specification of the form +.Dq MMDDhhmm[YY] . +.Pp +The +.Dq MM , +.Dq DD , +.Dq hh +and +.Dq mm +letter pairs are treated as their counterparts specified to the +.Fl t +option. +If the +.Dq YY +letter pair is in the range 69 to 99, the year is set to 1969 to 1999, +otherwise, the year is set in the 21st century. +.Sh HISTORY +A +.Nm touch +command appeared in +.At v7 . +.Sh STANDARDS +The +.Nm touch +function is expected to be a superset of the +.St -p1003.2 +specification. diff --git a/usr.bin/touch/touch.c b/usr.bin/touch/touch.c new file mode 100644 index 00000000000..afaa5376928 --- /dev/null +++ b/usr.bin/touch/touch.c @@ -0,0 +1,342 @@ +/* + * 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 copyright[] = +"@(#) Copyright (c) 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)touch.c 8.2 (Berkeley) 4/28/95"; +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +int rw __P((char *, struct stat *, int)); +void stime_arg1 __P((char *, struct timeval *)); +void stime_arg2 __P((char *, int, struct timeval *)); +void stime_file __P((char *, struct timeval *)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct stat sb; + struct timeval tv[2]; + int aflag, cflag, fflag, mflag, ch, fd, len, rval, timeset; + char *p; + + aflag = cflag = fflag = mflag = timeset = 0; + if (gettimeofday(&tv[0], NULL)) + err(1, "gettimeofday"); + + while ((ch = getopt(argc, argv, "acfmr:t:")) != EOF) + switch(ch) { + case 'a': + aflag = 1; + break; + case 'c': + cflag = 1; + break; + case 'f': + fflag = 1; + break; + case 'm': + mflag = 1; + break; + case 'r': + timeset = 1; + stime_file(optarg, tv); + break; + case 't': + timeset = 1; + stime_arg1(optarg, tv); + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + /* Default is both -a and -m. */ + if (aflag == 0 && mflag == 0) + aflag = mflag = 1; + + /* + * If no -r or -t flag, at least two operands, the first of which + * is an 8 or 10 digit number, use the obsolete time specification. + */ + if (!timeset && argc > 1) { + (void)strtol(argv[0], &p, 10); + len = p - argv[0]; + if (*p == '\0' && (len == 8 || len == 10)) { + timeset = 1; + stime_arg2(*argv++, len == 10, tv); + } + } + + /* Otherwise use the current time of day. */ + if (!timeset) + tv[1] = tv[0]; + + if (*argv == NULL) + usage(); + + for (rval = 0; *argv; ++argv) { + /* See if the file exists. */ + if (stat(*argv, &sb)) + if (!cflag) { + /* Create the file. */ + fd = open(*argv, + O_WRONLY | O_CREAT, DEFFILEMODE); + if (fd == -1 || fstat(fd, &sb) || close(fd)) { + rval = 1; + warn("%s", *argv); + continue; + } + + /* If using the current time, we're done. */ + if (!timeset) + continue; + } else + continue; + + if (!aflag) + TIMESPEC_TO_TIMEVAL(&tv[0], &sb.st_atimespec); + if (!mflag) + TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtimespec); + + /* Try utimes(2). */ + if (!utimes(*argv, tv)) + continue; + + /* If the user specified a time, nothing else we can do. */ + if (timeset) { + rval = 1; + warn("%s", *argv); + } + + /* + * System V and POSIX 1003.1 require that a NULL argument + * set the access/modification times to the current time. + * The permission checks are different, too, in that the + * ability to write the file is sufficient. Take a shot. + */ + if (!utimes(*argv, NULL)) + continue; + + /* Try reading/writing. */ + if (rw(*argv, &sb, fflag)) + rval = 1; + } + exit(rval); +} + +#define ATOI2(ar) ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2; + +void +stime_arg1(arg, tvp) + char *arg; + struct timeval *tvp; +{ + struct tm *t; + int yearset; + char *p; + /* Start with the current time. */ + if ((t = localtime(&tvp[0].tv_sec)) == NULL) + err(1, "localtime"); + /* [[CC]YY]MMDDhhmm[.SS] */ + if ((p = strchr(arg, '.')) == NULL) + t->tm_sec = 0; /* Seconds defaults to 0. */ + else { + if (strlen(p + 1) != 2) + goto terr; + *p++ = '\0'; + t->tm_sec = ATOI2(p); + } + + yearset = 0; + switch(strlen(arg)) { + case 12: /* CCYYMMDDhhmm */ + t->tm_year = ATOI2(arg); + t->tm_year *= 100; + yearset = 1; + /* FALLTHOUGH */ + case 10: /* YYMMDDhhmm */ + if (yearset) { + yearset = ATOI2(arg); + t->tm_year += yearset; + } else { + yearset = ATOI2(arg); + if (yearset < 69) + t->tm_year = yearset + 2000; + else + t->tm_year = yearset + 1900; + } + t->tm_year -= 1900; /* Convert to UNIX time. */ + /* FALLTHROUGH */ + case 8: /* MMDDhhmm */ + t->tm_mon = ATOI2(arg); + --t->tm_mon; /* Convert from 01-12 to 00-11 */ + t->tm_mday = ATOI2(arg); + t->tm_hour = ATOI2(arg); + t->tm_min = ATOI2(arg); + break; + default: + goto terr; + } + + t->tm_isdst = -1; /* Figure out DST. */ + tvp[0].tv_sec = tvp[1].tv_sec = mktime(t); + if (tvp[0].tv_sec == -1) +terr: errx(1, + "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]"); + + tvp[0].tv_usec = tvp[1].tv_usec = 0; +} + +void +stime_arg2(arg, year, tvp) + char *arg; + int year; + struct timeval *tvp; +{ + struct tm *t; + /* Start with the current time. */ + if ((t = localtime(&tvp[0].tv_sec)) == NULL) + err(1, "localtime"); + + t->tm_mon = ATOI2(arg); /* MMDDhhmm[yy] */ + --t->tm_mon; /* Convert from 01-12 to 00-11 */ + t->tm_mday = ATOI2(arg); + t->tm_hour = ATOI2(arg); + t->tm_min = ATOI2(arg); + if (year) + t->tm_year = ATOI2(arg); + + t->tm_isdst = -1; /* Figure out DST. */ + tvp[0].tv_sec = tvp[1].tv_sec = mktime(t); + if (tvp[0].tv_sec == -1) + errx(1, + "out of range or illegal time specification: MMDDhhmm[yy]"); + + tvp[0].tv_usec = tvp[1].tv_usec = 0; +} + +void +stime_file(fname, tvp) + char *fname; + struct timeval *tvp; +{ + struct stat sb; + + if (stat(fname, &sb)) + err(1, "%s", fname); + TIMESPEC_TO_TIMEVAL(tvp, &sb.st_atimespec); + TIMESPEC_TO_TIMEVAL(tvp + 1, &sb.st_mtimespec); +} + +int +rw(fname, sbp, force) + char *fname; + struct stat *sbp; + int force; +{ + int fd, needed_chmod, rval; + u_char byte; + + /* Try regular files and directories. */ + if (!S_ISREG(sbp->st_mode) && !S_ISDIR(sbp->st_mode)) { + warnx("%s: %s", fname, strerror(EFTYPE)); + return (1); + } + + needed_chmod = rval = 0; + if ((fd = open(fname, O_RDWR, 0)) == -1) { + if (!force || chmod(fname, DEFFILEMODE)) + goto err; + if ((fd = open(fname, O_RDWR, 0)) == -1) + goto err; + needed_chmod = 1; + } + + if (sbp->st_size != 0) { + if (read(fd, &byte, sizeof(byte)) != sizeof(byte)) + goto err; + if (lseek(fd, (off_t)0, SEEK_SET) == -1) + goto err; + if (write(fd, &byte, sizeof(byte)) != sizeof(byte)) + goto err; + } else { + if (write(fd, &byte, sizeof(byte)) != sizeof(byte)) { +err: rval = 1; + warn("%s", fname); + } else if (ftruncate(fd, (off_t)0)) { + rval = 1; + warn("%s: file modified", fname); + } + } + + if (close(fd) && rval != 1) { + rval = 1; + warn("%s", fname); + } + if (needed_chmod && chmod(fname, sbp->st_mode) && rval != 1) { + rval = 1; + warn("%s: permissions modified", fname); + } + return (rval); +} + +__dead void +usage() +{ + (void)fprintf(stderr, + "usage: touch [-acfm] [-r file] [-t time] file ...\n"); + exit(1); +} diff --git a/usr.bin/tput/tput.c b/usr.bin/tput/tput.c new file mode 100644 index 00000000000..f5874927097 --- /dev/null +++ b/usr.bin/tput/tput.c @@ -0,0 +1,226 @@ +/*- + * Copyright (c) 1980, 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) 1980, 1988, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)tput.c 8.3 (Berkeley) 4/28/95"; +#endif /* not lint */ + +#include + +#include +#include +#include +#include +#include + +static void prlongname __P((char *)); +static void setospeed __P((void)); +static void outc __P((int)); +static void usage __P((void)); +static char **process __P((char *, char *, char **)); + +int +main(argc, argv) + int argc; + char **argv; +{ + extern char *optarg; + extern int optind; + int ch, exitval, n; + char *cptr, *p, *term, buf[1024], tbuf[1024]; + + term = NULL; + while ((ch = getopt(argc, argv, "T:")) != EOF) + switch(ch) { + case 'T': + term = optarg; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (!term && !(term = getenv("TERM"))) +errx(2, "no terminal type specified and no TERM environmental variable."); + if (tgetent(tbuf, term) != 1) + err(2, "tgetent failure"); + setospeed(); + for (exitval = 0; (p = *argv) != NULL; ++argv) { + switch (*p) { + case 'c': + if (!strcmp(p, "clear")) + p = "cl"; + break; + case 'i': + if (!strcmp(p, "init")) + p = "is"; + break; + case 'l': + if (!strcmp(p, "longname")) + prlongname(tbuf); + continue; + case 'r': + if (!strcmp(p, "reset")) + p = "rs"; + break; + } + cptr = buf; + if (tgetstr(p, &cptr)) + argv = process(p, buf, argv); + else if ((n = tgetnum(p)) != -1) + (void)printf("%d\n", n); + else + exitval = !tgetflag(p); + } + exit(exitval); +} + +static void +prlongname(buf) + char *buf; +{ + int savech; + char *p, *savep; + + for (p = buf; *p && *p != ':'; ++p); + savech = *(savep = p); + for (*p = '\0'; p >= buf && *p != '|'; --p); + (void)printf("%s\n", p + 1); + *savep = savech; +} + +static char ** +process(cap, str, argv) + char *cap, *str, **argv; +{ + static char errfew[] = + "not enough arguments (%d) for capability `%s'"; + static char errmany[] = + "too many arguments (%d) for capability `%s'"; + static char erresc[] = + "unknown %% escape `%c' for capability `%s'"; + char *cp; + int arg_need, arg_rows, arg_cols; + + /* Count how many values we need for this capability. */ + for (cp = str, arg_need = 0; *cp != '\0'; cp++) + if (*cp == '%') + switch (*++cp) { + case 'd': + case '2': + case '3': + case '.': + case '+': + arg_need++; + break; + case '%': + case '>': + case 'i': + case 'r': + case 'n': + case 'B': + case 'D': + break; + default: + /* + * hpux has lot's of them, but we complain + */ + errx(2, erresc, *cp, cap); + } + + /* And print them. */ + switch (arg_need) { + case 0: + (void)tputs(str, 1, outc); + break; + case 1: + arg_cols = 0; + + if (*++argv == NULL || *argv[0] == '\0') + errx(2, errfew, 1, cap); + arg_rows = atoi(*argv); + + (void)tputs(tgoto(str, arg_cols, arg_rows), 1, outc); + break; + case 2: + if (*++argv == NULL || *argv[0] == '\0') + errx(2, errfew, 2, cap); + arg_rows = atoi(*argv); + + if (*++argv == NULL || *argv[0] == '\0') + errx(2, errfew, 2, cap); + arg_cols = atoi(*argv); + + (void) tputs(tgoto(str, arg_cols, arg_rows), arg_rows, outc); + break; + + default: + errx(2, errmany, arg_need, cap); + } + return (argv); +} + +static void +setospeed() +{ +#undef ospeed + extern short ospeed; + struct termios t; + + if (tcgetattr(STDOUT_FILENO, &t) != -1) + ospeed = 0; + else + ospeed = cfgetospeed(&t); +} + +static void +outc(c) + int c; +{ + (void)putchar(c); +} + +static void +usage() +{ + (void)fprintf(stderr, "usage: tput [-T term] attribute ...\n"); + exit(1); +} diff --git a/usr.bin/uucp/acucntrl/acucntrl.8 b/usr.bin/uucp/acucntrl/acucntrl.8 new file mode 100644 index 00000000000..7b6753db7be --- /dev/null +++ b/usr.bin/uucp/acucntrl/acucntrl.8 @@ -0,0 +1,165 @@ +.\" Copyright (c) 1985, 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. +.\" +.\" @(#)acucntrl.8 8.3 (Berkeley) 6/1/94 +.\" +.TH ACUCNTRL 8 "June 1, 1994" +.UC 6 +.SH NAME +acucntrl \- turn around tty line between dialin and dialout +.SH SYNOPSIS +.B /usr/lib/uucp/acucntrl +keyword ttyline +.SH DESCRIPTION +.PP +.I Acucntrl +turns around the terminal line, +enabling it to be used for both dialin and dialout. +On dialin a terminal line is assumed to have modem control enabled and a getty +process in existence waiting for logins. On dialout modem control is disabled +and there is no getty process. +.PP +This program must be run setuid to root. +.PP +.I keyword +is chosen from the list: +.I disable +or +.IR dialout , +to condition a line for dialout; +and +.I enable +or +.IR dialin , +to condition a line for dialin. +.PP +When the line is conditioned for dialing out, the login name of the real uid +of the process is placed in /etc/utmp in capitals. +This declares that the line is in use and acts as an additional locking +mechanism. +.I Acucntrl +will refuse to act if the /etc/utmp entry for the line is not null, +is not the the user's login name (capitalized or not), +and if the process is not running as the superuser. +The last condition is to allow the superuser to clear the state of the line. +.PP +Turning modem control on or off is handled by poking into /dev/kmem. +It is currently implemented for dz, dh, and dmf lines. +.PP +Under 4.2 BSD the program will also refuse to disable a line if carrier is +sensed on it. This is to avoid the dead period where someone has just dialed +in and made the connection but has not yet logged in. +.PP +.I Ttyline +can be either of the form tty* or /dev/tty*. +Enabling/disabling a line whose name does not begin with ttyd? is prohibited +unless the real uid of the process is 0 or if the login name corresponding to +the real uid is uucp. This is a security precaution. +.PP +Steps taken when disabling +.RI ( i . e . +setup for dialing out) +.IP 1) +check input arguments +.IP 2) +look in /etc/utmp to check that the line is not in use by another user +.IP 3) +disable modem control on line +.IP 4) +check for carrier on device +.IP 5) +change owner of device to real uid +.IP 6) +edit /etc/ttys, changing the first character of the appropriate line to 0 +.IP 7) +send a hangup to process 1 to poke init to disable getty +.IP 8) +post uid name in capitals in /etc/utmp to let world know device has been grabbed +.IP 9) +make sure that DTR is on +.PP +Steps taken when enabling +.RI ( i . e . +setup for dialing in) +.IP 1) +check input arguments +.IP 2) +look in /etc/utmp to check that the line is not in use by another user +.IP 3) +make sure modem control on line is disabled +.IP 4) +turn off DTR to make sure line is hung up +.IP 5) +condition line: clear exclusive use and set hangup on close modes +.IP 6) +turn on modem control +.IP 7) +edit /etc/ttys, changing the first character of the appropriate line to 1 +.ne 1i +.IP 8) +send a hangup to process 1 to poke init to enable getty +.IP 9) +clear uid name for /etc/utmp +.SH HISTORY +.PP +First written by Allan Wilkes (fisher!allan) +.PP +Modified June 8,1983 by W.Sebok (astrovax!wls) to poke the kernel rather +than use a kernel hack to turn on/off modem control, using a subroutine +stolen from a program written by Tsutomu Shimomura {astrovax,escher}!tsutomu +.PP +Worked over many times by W.Sebok +.RI ( i . e . +hacked to death) +.SH FILES +/dev/kmem, /vmunix, /etc/ttys, /etc/utmp, /dev/tty* +.SH BUGS +.PP +Sensing carrier requires the 4.2 BSD TIOCMGET ioctl call. Unfortunately this +ioctl is not implemented in the vanilla 4.2 BSD dh driver even though the +dz and dmf drivers use an emulation of the DH11's modem control bits. This +has been fixed here. +.PP +Some time (currently 2 seconds) is required between disabling modem control +and opening the device. This is probably because of a race with getty whose +open is finally being allowed to complete. This time interval may not be +enough on a loaded system. Because of this problem and the above problem with +the dh driver there is deliberately no error message given when the TIOCMGET +ioctl fails. +.PP +Previously there were similar synchronization problems with the init process. +When dialins are disabled the capitalized name of the process cannot be posted +into /etc/utmp until init has finished clearing /etc/utmp. However one does +not know how long that will take, and, on a loaded system, it can take quite +a while. This was solved by the strategy of 1) posting the name, 2) poking +init, 3) going into a loop where the process repeatedly waits a second and +checks whether the entry has been cleared from /etc/utmp, and 4) posting the +name again. diff --git a/usr.bin/uucp/acucntrl/acucntrl.c b/usr.bin/uucp/acucntrl/acucntrl.c new file mode 100644 index 00000000000..deba6bac7da --- /dev/null +++ b/usr.bin/uucp/acucntrl/acucntrl.c @@ -0,0 +1,814 @@ +/*- + * Copyright (c) 1985, 1986, 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, 1986, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)acucntrl.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +/* acucntrl - turn around tty line between dialin and dialout + * + * Usage: acucntrl {enable,disable} /dev/ttydX + * + * History: + * First written by Allan Wilkes (fisher!allan) + * + * Modified June 8,1983 by W.Sebok (astrovax!wls) to poke kernel rather + * than use kernel hack to turn on/off modem control, using subroutine + * stolen from program written by Tsutomu Shimomura + * {astrovax,escher}!tsutomu + * + * Worked over many times by W.Sebok (i.e. hacked to death) + * + * Operation: + * disable (i.e. setup for dialing out) + * (1) check input arguments + * (2) look in _PATH_UTMP to check that the line is not in use by another + * (3) disable modem control on terminal + * (4) check for carrier on device + * (5) change owner of device to real id + * (6) edit _PATH_TTYS, changing the first character of the appropriate + * line to 0 + * (7) send a hangup to process 1 to poke init to disable getty + * (8) post uid name in capitals in _PATH_UTMP to let world know device + * has been grabbed + * (9) make sure that DTR is on + * + * enable (i.e.) restore for dialin + * (1) check input arguments + * (2) look in _PATH_UTMP to check that the line is not in use by another + * (3) make sure modem control on terminal is disabled + * (4) turn off DTR to make sure line is hung up + * (5) condition line: clear exclusive use and set hangup on close modes + * (6) turn on modem control + * (7) edit _PATH_TTYS, changing the first character of the appropriate + * line to 1 + * (8) send a hangup to process 1 to poke init to enable getty + * (9) clear uid name for _PATH_UTMP + */ + +/* #define SENSECARRIER */ + +#include "uucp.h" +#ifdef DIALINOUT +#include +#include +#include +#ifdef vax +#ifdef BSD4_2 +#include +#else +#include +#endif +#endif /* vax */ +#include +#include +#include +#include +#include +#include +#include +#include "pathnames.h" + +#define NDZLINE 8 /* lines/dz */ +#define NDHLINE 16 /* lines/dh */ +#define NDMFLINE 8 /* lines/dmf */ + +#define DZ11 1 +#define DH11 2 +#define DMF 3 + +#define NLVALUE(val) (nl[val].n_value) + +struct nlist nl[] = { +#define CDEVSW 0 + { "_cdevsw" }, + +#define DZOPEN 1 + { "_dzopen" }, +#define DZINFO 2 + { "_dzinfo" }, +#define NDZ11 3 + { "_dz_cnt" }, +#define DZSCAR 4 + { "_dzsoftCAR" }, + +#define DHOPEN 5 + { "_dhopen" }, +#define DHINFO 6 + { "_dhinfo" }, +#define NDH11 7 + { "_ndh11" }, +#define DHSCAR 8 + { "_dhsoftCAR" }, + +#define DMFOPEN 9 + { "_dmfopen" }, +#define DMFINFO 10 + { "_dmfinfo" }, +#define NDMF 11 + { "_ndmf" }, +#define DMFSCAR 12 + { "_dmfsoftCAR" }, + + { "\0" } +}; + +#define ENABLE 1 +#define DISABLE 0 + +char Etcttys[] = _PATH_TTYS; +#ifdef BSD4_3 +FILE *ttysfile, *nttysfile; +char NEtcttys[] = _PATH_NEWTTYS; +extern long ftell(); +#endif BSD4_3 +char Devhome[] = _PATH_DEV; + +char usage[] = "Usage: acucntrl {dis|en}able ttydX\n"; + +struct utmp utmp; +char resettty, resetmodem; +int etcutmp; +off_t utmploc; +off_t ttyslnbeg; +extern int errno; +extern char *sys_errlist[]; +off_t lseek(); + +#define NAMSIZ sizeof(utmp.ut_name) +#define LINSIZ sizeof(utmp.ut_line) + +main(argc, argv) +int argc; char *argv[]; +{ + register char *p; + register int i; + char uname[NAMSIZ], Uname[NAMSIZ]; + int enable ; + char *device; + int devfile; + int uid, gid; + struct passwd *getpwuid(); + char *rindex(); + + /* check input arguments */ + if (argc!=3 && argc != 4) { + fprintf(stderr, usage); + exit(1); + } + + /* interpret command type */ + if (prefix(argv[1], "disable") || strcmp(argv[1], "dialout")==0) + enable = 0; + else if (prefix(argv[1], "enable") || strcmp(argv[1], "dialin")==0) + enable = 1; + else { + fprintf(stderr, usage); + exit(1); + } + + device = rindex(argv[2], '/'); + device = (device == NULL) ? argv[2]: device+1; + + opnttys(device); + +#ifdef vax + /* Get nlist info */ + nlist(_PATH_UNIX, nl); +#endif vax + + /* Chdir to /dev */ + if(chdir(Devhome) < 0) { + fprintf(stderr, "Cannot chdir to %s: %s\r\n", + Devhome, sys_errlist[errno]); + exit(1); + } + + /* Get uid information */ + uid = getuid(); + gid = getgid(); + + p = getpwuid(uid)->pw_name; + if (p==NULL) { + fprintf(stderr, "cannot get uid name\n"); + exit(1); + } + + if (strcmp(p, "uucp") == 0 && argc == 4) + p = argv[3]; + + /* to upper case */ + i = 0; + do { + uname[i] = *p; + Uname[i++] = (*p>='a' && *p<='z') ? (*p - ('a'-'A')) : *p; + } while (*p++ && i= 0 ; str++) + if (*str>='a' && *str<='z') + return(0); + return(1); +} + +/* Post name to public */ +post(device, name) +char *device, *name; +{ + (void)time((time_t *)&utmp.ut_time); + strncpy(utmp.ut_line, device, LINSIZ); + strncpy(utmp.ut_name, name, NAMSIZ); + if (lseek(etcutmp, utmploc, 0) < 0) + fprintf(stderr, "on lseek in %s: %s", + _PATH_UTMP, sys_errlist[errno]); + if (write(etcutmp, (char *)&utmp, sizeof(utmp)) < 0) + fprintf(stderr, "on write in %s: %s", + _PATH_UTMP, sys_errlist[errno]); +} + +/* poke process 1 and wait for it to do its thing */ +pokeinit(device, uname, enable) +char *uname, *device; int enable; +{ + struct utmp utmp; + register int i; + + post(device, uname); + + /* poke init */ + if (kill(1, SIGHUP)) { + fprintf(stderr, + "Cannot send hangup to init process: %s\n", + sys_errlist[errno]); + (void)settys(resettty); + (void)setmodem(device, resetmodem); + exit(1); + } + + if (enable) + return; + + /* wait till init has responded, clearing the utmp entry */ + i = 100; + do { + sleep(1); + if (lseek(etcutmp, utmploc, 0) < 0) + fprintf(stderr, "On lseek in %s: %s", + _PATH_UTMP, sys_errlist[errno]); + if (read(etcutmp, (char *)&utmp, sizeof utmp) < 0) + fprintf(stderr, "On read from %s: %s", + _PATH_UTMP, sys_errlist[errno]); + } while (utmp.ut_name[0] != '\0' && --i > 0); +} + +#ifdef BSD4_3 +/* identify terminal line in ttys */ +opnttys(device) +char *device; +{ + register int ndevice; + register char *p; + char *index(); + char linebuf[BUFSIZ]; + + ttysfile = NULL; + do { + if (ttysfile != NULL) { + fclose(ttysfile); + sleep(5); + } + ttysfile = fopen(Etcttys, "r"); + if(ttysfile == NULL) { + fprintf(stderr, "Cannot open %s: %s\n", Etcttys, + sys_errlist[errno]); + exit(1); + } + } while (flock(fileno(ttysfile), LOCK_NB|LOCK_EX) < 0); + nttysfile = fopen(NEtcttys, "w"); + if(nttysfile == NULL) { + fprintf(stderr, "Cannot open %s: %s\n", Etcttys, + sys_errlist[errno]); + exit(1); + } + + ndevice = strlen(device); +#ifndef BRL4_2 + utmploc = sizeof(utmp); +#else BRL4_2 + utmploc = 0; +#endif BRL4_2 + + while(fgets(linebuf, sizeof(linebuf) - 1, ttysfile) != NULL) { + if(strncmp(device, linebuf, ndevice) == 0) + return; + ttyslnbeg += strlen(linebuf); + if (linebuf[0] != '#' && linebuf[0] != '\0') + utmploc += sizeof(utmp); + if (fputs(linebuf, nttysfile) == NULL) { + fprintf(stderr, "On %s write: %s\n", + Etcttys, sys_errlist[errno]); + exit(1); + } + + } + fprintf(stderr, "%s not found in %s\n", device, Etcttys); + exit(1); +} + +/* modify appropriate line in _PATH_TTYS to turn on/off the device */ +settys(enable) +int enable; +{ + register char *cp, *cp2; + char lbuf[BUFSIZ]; + int i; + char c1, c2; + + (void) fseek(ttysfile, ttyslnbeg, 0); + if(fgets(lbuf, BUFSIZ, ttysfile) == NULL) { + fprintf(stderr, "On %s read: %s\n", + Etcttys, sys_errlist[errno]); + exit(1); + } + /* format is now */ + /* ttyd0 std.100 dialup on secure # comment */ + /* except, 2nd item may have embedded spaces inside quotes, Hubert */ + cp = lbuf; + for (i=0;*cp && i<3;i++) { + if (*cp == '"') { + cp++; + while (*cp && *cp != '"') + cp++; + if (*cp != '\0') + cp++; + }else { + while (*cp && *cp != ' ' && *cp != '\t') + cp++; + } + while (*cp && (*cp == ' ' || *cp == '\t')) + cp++; + } + if (*cp == '\0') { + fprintf(stderr,"Badly formatted line in %s:\n%s", + _PATH_TTYS, lbuf); + exit(1); + } + c1 = *--cp; + *cp++ = '\0'; + cp2 = cp; + while (*cp && *cp != ' ' && *cp != '\t' && *cp != '\n') + cp++; + if (*cp == '\0') { + fprintf(stderr,"Badly formatted line in %s:\n%s", + _PATH_TTYS, lbuf); + exit(1); + } + c2 = *cp; + *cp++ = '\0'; + while (*cp && (*cp == ' ' || *cp == '\t')) + cp++; + resettty = strcmp("on", cp2) != 0; + fprintf(nttysfile,"%s%c%s%c%s", lbuf, c1, enable ? "on" : "off", c2, cp); + if (ferror(nttysfile)) { + fprintf(stderr, "On %s fprintf: %s\n", + NEtcttys, sys_errlist[errno]); + exit(1); + } + while(fgets(lbuf, sizeof(lbuf) - 1, ttysfile) != NULL) { + if (fputs(lbuf, nttysfile) == NULL) { + fprintf(stderr, "On %s write: %s\n", + NEtcttys, sys_errlist[errno]); + exit(1); + } + } + + if (enable^resettty) + (void) unlink(NEtcttys); + else { + struct stat statb; + if (stat(Etcttys, &statb) == 0) { + fchmod(fileno(nttysfile) ,statb.st_mode); + fchown(fileno(nttysfile), statb.st_uid, statb.st_gid); + } + (void) rename(NEtcttys, Etcttys); + } + (void) fclose(nttysfile); + (void) fclose(ttysfile); + return enable^resettty; +} + +#else !BSD4_3 + +/* identify terminal line in ttys */ +opnttys(device) +char *device; +{ + register FILE *ttysfile; + register int ndevice, lnsiz; + register char *p; + char *index(); + char linebuf[BUFSIZ]; + + ttysfile = fopen(Etcttys, "r"); + if(ttysfile == NULL) { + fprintf(stderr, "Cannot open %s: %s\n", Etcttys, + sys_errlist[errno]); + exit(1); + } + + ndevice = strlen(device); + ttyslnbeg = 0; + utmploc = 0; + + while(fgets(linebuf, sizeof(linebuf) - 1, ttysfile) != NULL) { + lnsiz = strlen(linebuf); + if ((p = index(linebuf, '\n')) != NULL) + *p = '\0'; + if(strncmp(device, &linebuf[2], ndevice) == 0) { + (void)fclose(ttysfile); +#ifdef sequent + /* Why is the sequent off by one? */ + utmploc += sizeof(utmp); +#endif sequent + return; + } + ttyslnbeg += lnsiz; + utmploc += sizeof(utmp); + } + fprintf(stderr, "%s not found in %s\n", device, Etcttys); + exit(1); +} + +/* modify appropriate line in _PATH_TTYS to turn on/off the device */ +settys(enable) +int enable; +{ + int ittysfil; + char out, in; + + ittysfil = open(Etcttys, 2); + if(ittysfil < 0) { + fprintf(stderr, "Cannot open %s for output: %s\n", + Etcttys, sys_errlist[errno]); + exit(1); + } + (void)lseek(ittysfil, ttyslnbeg, 0); + if(read(ittysfil, &in, 1)<0) { + fprintf(stderr, "On %s write: %s\n", + Etcttys, sys_errlist[errno]); + exit(1); + } + resettty = (in == '1'); + out = enable ? '1' : '0'; + (void)lseek(ittysfil, ttyslnbeg, 0); + if(write(ittysfil, &out, 1)<0) { + fprintf(stderr, "On %s write: %s\n", + Etcttys, sys_errlist[errno]); + exit(1); + } + (void)close(ittysfil); + return(in==out); +} +#endif !BSD4_3 + +#ifdef sequent +setmodem(ttyline, enable) +char *ttyline; int enable; +{ + char *sysbuf[BUFSIZ]; + sprintf(sysbuf,"/etc/ttyconfig /dev/%s -special %s", ttyline, + enable ? "-carrier" : "-nocarrier"); + system(sysbuf); +} +#endif /* sequent */ +#ifdef vax +/* + * Excerpted from (June 8, 1983 W.Sebok) + * > ttymodem.c - enable/disable modem control for tty lines. + * > + * > Knows about DZ11s and DH11/DM11s. + * > 23.3.83 - TS + * > modified to know about DMF's (hasn't been tested) Nov 8, 1984 - WLS + */ + + +setmodem(ttyline, enable) +char *ttyline; int enable; +{ + dev_t dev; + int kmem; + int unit, line, nlines, addr, tflags; + int devtype=0; + char cflags; short sflags; +#ifdef BSD4_2 + int flags; +#else + short flags; +#endif + struct uba_device *ubinfo; + struct stat statb; + struct cdevsw cdevsw; + + if(nl[CDEVSW].n_type == 0) { + fprintf(stderr, "No namelist.\n"); + return(-1); + } + + if((kmem = open(_PATH_KMEM, 2)) < 0) { + fprintf(stderr, "%s open: %s\n", _PATH_KMEM, + sys_errlist[errno]); + return(-1); + } + + if(stat(ttyline, &statb) < 0) { + fprintf(stderr, "%s stat: %s\n", ttyline, sys_errlist[errno]); + return(-1); + } + + if((statb.st_mode&S_IFMT) != S_IFCHR) { + fprintf(stderr, "%s is not a character device.\n",ttyline); + return(-1); + } + + dev = statb.st_rdev; + (void)lseek(kmem, + (off_t) &(((struct cdevsw *)NLVALUE(CDEVSW))[major(dev)]),0); + (void)read(kmem, (char *) &cdevsw, sizeof cdevsw); + + if((int)(cdevsw.d_open) == NLVALUE(DZOPEN)) { + devtype = DZ11; + unit = minor(dev) / NDZLINE; + line = minor(dev) % NDZLINE; + addr = (int) &(((int *)NLVALUE(DZINFO))[unit]); + (void)lseek(kmem, (off_t) NLVALUE(NDZ11), 0); + } else if((int)(cdevsw.d_open) == NLVALUE(DHOPEN)) { + devtype = DH11; + unit = minor(dev) / NDHLINE; + line = minor(dev) % NDHLINE; + addr = (int) &(((int *)NLVALUE(DHINFO))[unit]); + (void)lseek(kmem, (off_t) NLVALUE(NDH11), 0); + } else if((int)(cdevsw.d_open) == NLVALUE(DMFOPEN)) { + devtype = DMF; + unit = minor(dev) / NDMFLINE; + line = minor(dev) % NDMFLINE; + addr = (int) &(((int *)NLVALUE(DMFINFO))[unit]); + (void)lseek(kmem, (off_t) NLVALUE(NDMF), 0); + } else { + fprintf(stderr, "Device %s (%d/%d) unknown.\n", ttyline, + major(dev), minor(dev)); + return(-1); + } + + (void)read(kmem, (char *) &nlines, sizeof nlines); + if(minor(dev) >= nlines) { + fprintf(stderr, "Sub-device %d does not exist (only %d).\n", + minor(dev), nlines); + return(-1); + } + + (void)lseek(kmem, (off_t)addr, 0); + (void)read(kmem, (char *) &ubinfo, sizeof ubinfo); + (void)lseek(kmem, (off_t) &(ubinfo->ui_flags), 0); + (void)read(kmem, (char *) &flags, sizeof flags); + + tflags = 1<ui_flags), 0); + (void)write(kmem, (char *) &flags, sizeof flags); + switch(devtype) { + case DZ11: + if((addr = NLVALUE(DZSCAR)) == 0) { + fprintf(stderr, "No dzsoftCAR.\n"); + return(-1); + } + cflags = flags; + (void)lseek(kmem, (off_t) &(((char *)addr)[unit]), 0); + (void)write(kmem, (char *) &cflags, sizeof cflags); + break; + case DH11: + if((addr = NLVALUE(DHSCAR)) == 0) { + fprintf(stderr, "No dhsoftCAR.\n"); + return(-1); + } + sflags = flags; + (void)lseek(kmem, (off_t) &(((short *)addr)[unit]), 0); + (void)write(kmem, (char *) &sflags, sizeof sflags); + break; + case DMF: + if((addr = NLVALUE(DMFSCAR)) == 0) { + fprintf(stderr, "No dmfsoftCAR.\n"); + return(-1); + } + cflags = flags; + (void)lseek(kmem, (off_t) &(((char *)addr)[unit]), 0); + (void)write(kmem, (char *) &cflags, sizeof cflags); + break; + default: + fprintf(stderr, "Unknown device type\n"); + return(-1); + } + return(0); +} +#endif /* vax */ + +prefix(s1, s2) + register char *s1, *s2; +{ + register char c; + + while ((c = *s1++) == *s2++) + if (c == '\0') + return (1); + return (c == '\0'); +} +#else /* !DIALINOUT */ +main() +{ + fprintf(stderr,"acucntrl is not supported on this system\n"); +} +#endif /* !DIALINOUT */ diff --git a/usr.bin/uucp/uupoll/uupoll.8 b/usr.bin/uucp/uupoll/uupoll.8 new file mode 100644 index 00000000000..f6ee49b6c53 --- /dev/null +++ b/usr.bin/uucp/uupoll/uupoll.8 @@ -0,0 +1,111 @@ +.\" Copyright (c) 1986, 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. +.\" +.\" @(#)uupoll.8 8.1 (Berkeley) 6/6/93 +.\" +.Dd June 6, 1993 +.Dt UUPOLL 8 +.Os BSD 4.3 +.Sh NAME +.Nm uupoll +.Nd poll a remote +.Tn UUCP +site +.Sh SYNOPSIS +.Nm uupoll +.Op Fl g Ns Ar grade +.Op Fl n +.Ar system +.Sh DESCRIPTION +.Nm Uupoll +is used to force a poll of a remote system. It queues a null job for the +remote system and then invokes +.Xr uucico 8 . +.Pp +The following options are available: +.Bl -tag -width Fl +.It Fl g Ns Ar grade +Only send jobs of grade +.Ar grade +or higher on this call. +.It Fl n +Queue the null job, but do not invoke +.Xr uucico . +.El +.Pp +.Nm Uupoll +is usually run by +.Xr cron 5 +or by a user who wants to hurry a job along. A typical entry in +.Em crontab +could be: +.Bd -literal +0 0,8,16 * * * daemon /usr/bin/uupoll ihnp4 +0 4,12,20 * * * daemon /usr/bin/uupoll ucbvax +.Ed +.Pp +This will poll +.Em ihnp4 +at midnight, 0800, and 1600, and +.Em ucbvax +at 0400, noon, and 2000. +.Pp +If the local machine is already running +.Xr uucico +every +hour and has a limited number of outgoing modems, a more elegant approach +might be: +.Bd -literal +0 0,8,16 * * * daemon /usr/bin/uupoll -n ihnp4 +0 4,12,20 * * * daemon /usr/bin/uupoll -n ucbvax +5 * * * * daemon /usr/lib/uucp/uucico -r1 +.Ed +.Pp +This will queue null jobs for the remote sites at the top of hour; they +will be processed by +.Xr uucico +when it runs five minutes later. +.Sh FILES +.Bl -tag -width /usr/lib/uucp/UUCP -compact +.It Pa /usr/lib/uucp/UUCP +internal files/utilities +.It Pa /var/spool/uucp/ +Spool directory +.El +.Sh SEE ALSO +.Xr uucp 1 , +.Xr uux 1 , +.Xr uucico 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.3 . diff --git a/usr.bin/uucp/uupoll/uupoll.c b/usr.bin/uucp/uupoll/uupoll.c new file mode 100644 index 00000000000..5d5e662bcd2 --- /dev/null +++ b/usr.bin/uucp/uupoll/uupoll.c @@ -0,0 +1,129 @@ +/*- + * Copyright (c) 1986, 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) 1986, 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)uupoll.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +/* + * Poll named system(s). + * + * The poll occurs even if recent attempts have failed, + * but not if L.sys prohibits the call (e.g. wrong time of day). + * + * Original Author: Tom Truscott (rti!trt) + */ + +#include "uucp.h" + +int TransferSucceeded = 1; +struct timeb Now; + +main(argc, argv) +int argc; +char **argv; +{ + char wrkpre[MAXFULLNAME]; + char file[MAXFULLNAME]; + char grade = 'A'; + int nocall = 0; + int c; + char *sysname; + extern char *optarg; + extern int optind; + + if (argc < 2) { + fprintf(stderr, "usage: uupoll [-gX] [-n] system ...\n"); + cleanup(1); + } + + if (chdir(Spool) < 0) { + syslog(LOG_WARNING, "chdir(%s) failed: %m", Spool); + cleanup(1); + } + strcpy(Progname, "uupoll"); + uucpname(Myname); + + while ((c = getopt(argc, argv, "g:n")) != EOF) + switch(c) { + case 'g': + grade = *optarg; + break; + case 'n': + nocall++; + break; + case '?': + default: + fprintf(stderr, "unknown option %s\n", + argv[optind-1]); + } + + while(optind < argc) { + sysname = argv[optind++]; + if (strcmp(sysname, Myname) == SAME) { + fprintf(stderr, "This *is* %s!\n", Myname); + continue; + } + + if (versys(&sysname)) { + fprintf(stderr, "%s: unknown system.\n", sysname); + continue; + } + /* Remove any STST file that might stop the poll */ + sprintf(wrkpre, "%s/LCK..%.*s", LOCKDIR, MAXBASENAME, sysname); + if (access(wrkpre, 0) < 0) + rmstat(sysname); + sprintf(wrkpre, "%c.%.*s", CMDPRE, SYSNSIZE, sysname); + if (!iswrk(file, "chk", Spool, wrkpre)) { + sprintf(file, "%s/%c.%.*s%cPOLL", subdir(Spool, CMDPRE), + CMDPRE, SYSNSIZE, sysname, grade); + close(creat(file, 0666)); + } + /* Attempt the call */ + if (!nocall) + xuucico(sysname); + } + cleanup(0); +} + +cleanup(code) +int code; +{ + exit(code); +} diff --git a/usr.bin/uucp/uuq/Makefile b/usr.bin/uucp/uuq/Makefile new file mode 100644 index 00000000000..60dbe0ba0d8 --- /dev/null +++ b/usr.bin/uucp/uuq/Makefile @@ -0,0 +1,9 @@ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 + +PROG= uuq +CFLAGS+=-I${.CURDIR}/../includes +BINMODE=6555 +DPADD= ${LIBCOMPAT} +LDADD= ${LIBUU} -lcompat + +.include diff --git a/usr.bin/uucp/uuq/uuq.1 b/usr.bin/uucp/uuq/uuq.1 new file mode 100644 index 00000000000..783d4869c77 --- /dev/null +++ b/usr.bin/uucp/uuq/uuq.1 @@ -0,0 +1,126 @@ +.\" 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. +.\" +.\" @(#)uuq.1 8.1 (Berkeley) 6/6/93 +.\" +.Dd June 6, 1993 +.Dt UUQ 1 +.Os BSD 4.3 +.Sh NAME +.Nm uuq +.Nd examine or manipulate the uucp queue +.Sh SYNOPSIS +.Nm uuq +.Op Fl l +.Op Fl h +.Op Fl s Ns Ar system +.Op Fl u Ns Ar user +.Op Fl d Ns Ar jobno +.Op Fl r Ns Ar sdir +.Op Fl b Ns Ar baud +.Sh DESCRIPTION +.Nm Uuq +is used to examine (and possibly delete) entries in the uucp queue. +.Pp +When listing jobs, +.Nm uuq +uses a format reminiscent of +.Xr ls . +For the long format, +information for each job listed includes +job number, number of files to transfer, user who +spooled the job, number of bytes to send, type of command requested +(S for sending files, R for receiving files, X for remote uucp), +and file or command desired. +.Pp +Several options are available: +.Bl -tag -width Ar +.It Fl h +Print only the summary lines for each system. Summary lines give system +name, number of jobs for the system, and total number of bytes to send. +.It Fl l +Specifies a long format listing. The default is to list only the +job numbers sorted across the page. +.It Fl s Ns Ar system +Limit output to jobs for systems whose system names begin with +.Ar system . +.It Fl u Ns Ar user +Limit output to jobs for users whose login names begin with +.Ar user . +.It Fl d Ns Ar jobno +Delete job number +.Ar jobno +(as obtained from a previous +.Nm uuq +command) +from the uucp queue. +Only the +.Tn UUCP +Administrator is permitted to delete jobs. +.It Fl r Ns Ar sdir +Look for files in the spooling directory +.Ar sdir +instead of the default +directory. +.It Fl b Ns Ar baud +Use +.Ar baud +to compute the transfer time instead of the default +1200 baud. +.El +.Sh FILES +.Bl -tag -width /usr/spool/uucp/Dhostname./D.x -compact +.It Pa /usr/spool/uucp/ +Default spool directory +.It Pa /usr/spool/uucp/C./C.* +Control files +.It Pa /usr/spool/uucp/D Ns Em hostname ./D.* +Outgoing data files +.It Pa /usr/spool/uucp/X./X.* +Outgoing execution files +.El +.Sh SEE ALSO +.Xr uucp 1 , +.Xr uux 1 , +.Xr uulog 1 , +.Xr uusnap 8 +.Sh BUGS +No information is available on work requested by the remote machine. +.Pp +The user who requests a remote uucp command is unknown. +.Pp +.Dq Li uq \-l +can be horrendously slow. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.3 . diff --git a/usr.bin/uucp/uuq/uuq.c b/usr.bin/uucp/uuq/uuq.c new file mode 100644 index 00000000000..7abb25c42ab --- /dev/null +++ b/usr.bin/uucp/uuq/uuq.c @@ -0,0 +1,435 @@ +/*- + * 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[] = "@(#)uuq.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +/* + * uuq - looks at uucp queues + * + * Lou Salkind + * New York University + * + */ + +#include "uucp.h" +#include + +#ifdef NDIR +#include "libndir/ndir.h" +#else !NDIR +#include +#endif !NDIR +#include + +#define NOSYS (struct sys *)0 + +#define W_TYPE wrkvec[0] +#define W_FILE1 wrkvec[1] +#define W_FILE2 wrkvec[2] +#define W_USER wrkvec[3] +#define W_OPTNS wrkvec[4] +#define W_DFILE wrkvec[5] +#define W_MODE wrkvec[6] +#define WSUFSIZE 5 /* work file name suffix size */ + +struct sys { + char s_name[8]; + int s_njobs; + off_t s_bytes; + struct job *s_jobp; + struct sys *s_sysp; +}; + +struct job { + int j_files; + int j_flags; + char j_jobno[WSUFSIZE]; + char j_user[22]; + char j_fname[128]; + char j_grade; + off_t j_bytes; + time_t j_date; + struct job *j_jobp; +}; + +struct sys *syshead; +struct sys *getsys(); +int jcompare(); +char *sysname; +char *user; +char *rmjob; +int hflag; +int lflag; + +char *malloc(), *calloc(); +double atof(); +float baudrate = 2400.; +char Username[BUFSIZ]; +char Filename[BUFSIZ]; +int Maxulen = 0; +struct timeb Now; + +main(argc, argv) +int argc; +char **argv; +{ + register int i; + register struct sys *sp; + register struct job *jp; + struct job **sortjob; + int nsys; + extern char *optarg; + extern int optind; + + strcpy(Progname, "uuq"); + uucpname(Myname); + + while ((i = getopt(argc, argv, "r:S:s:u:d:b:hl")) != EOF) + switch (i) { + case 'r': + case 'S': + Spool = optarg; + break; + case 's': + sysname = optarg; + if (strlen(sysname) > SYSNSIZE) + sysname[SYSNSIZE] = '\0'; + break; + case 'u': + user = optarg; + break; + case 'd': + rmjob = optarg; + break; + case 'b': + baudrate = atof(optarg); + break; + case 'h': + hflag++; + break; + case 'l': + lflag++; + break; + default: + fprintf(stderr, + "usage: uuq [-l] [-h] [-ssystem] [-uuser] [-djobno] [-rspool] [-bbaudrate]\n"); + exit(0); + } + + subchdir(Spool); + baudrate *= 0.7; /* reduce speed because of protocol overhead */ + baudrate *= 7.5; /* convert to chars/minute (60/8) */ + gather(); + nsys = 0; + for (sp = syshead; sp; sp = sp->s_sysp) { + if (sp->s_njobs == 0) + continue; + if (!hflag && nsys++ > 0) + putchar('\n'); + printf("%s: %d %s", sp->s_name, + sp->s_njobs, sp->s_njobs > 1 ? "jobs" : "job"); + if (lflag) { + float minutes; + int hours; + /* The 80 * njobs is because of the uucp handshaking */ + minutes = (float)(sp->s_bytes + 80 * sp->s_njobs)/baudrate; + hours = minutes/60; + printf(", %ld bytes, ", sp->s_bytes); + if (minutes > 60){ + printf("%d hour%s, ",hours, + hours > 1 ? "s": ""); + minutes -= 60 * hours; + } + printf("%3.1f minutes (@ effective baudrate of %d)", + minutes,(int)(baudrate/6)); + } + putchar('\n'); + if (hflag) + continue; + /* sort them babies! */ + sortjob = (struct job **)calloc(sp->s_njobs, sizeof (struct job *)); + for (i=0, jp=sp->s_jobp; i < sp->s_njobs; i++, jp=jp->j_jobp) + sortjob[i] = jp; + qsort(sortjob, sp->s_njobs, sizeof (struct job *), jcompare); + for (i = 0; i < sp->s_njobs; i++) { + jp = sortjob[i]; + if (lflag) { + printf("%s %2d %-*s%7ld%5.1f %-12.12s %c %.*s\n", + jp->j_jobno, jp->j_files, Maxulen, jp->j_user, jp->j_bytes, jp->j_bytes/baudrate, + ctime(&jp->j_date) + 4, jp->j_flags, sizeof (jp->j_fname), jp->j_fname + ); + } else { + printf("%s", jp->j_jobno); + putchar((i+1)%10 ? '\t' : '\n'); + } + /* There's no need to keep the force poll if jobs > 1*/ + if (sp->s_njobs > 1 && strcmp("POLL", jp->j_jobno)==0) { + char pbuf[BUFSIZ]; + sprintf(pbuf,"%s/%c.%s%cPOLL", + subdir(Spool, CMDPRE), CMDPRE, + sp->s_name, jp->j_grade); + (void) unlink(pbuf); + } + } + if (!lflag && (sp->s_njobs%10)) + putchar('\n'); + } + exit(0); +} + +jcompare(j1, j2) +struct job **j1, **j2; +{ + int delta; + + delta = (*j1)->j_grade - (*j2)->j_grade; + if (delta) + return delta; + return(strcmp((*j1)->j_jobno,(*j2)->j_jobno)); +} + +/* + * Get all the command file names + */ +gather() +{ + struct direct *d; + DIR *df; + + /* + * Find all the spool files in the spooling directory + */ + if ((df = opendir(subdir(Spool, CMDPRE))) == NULL) { + fprintf(stderr, "can't examine spooling area\n"); + exit(1); + } + for (;;) { + if ((d = readdir(df)) == NULL) + break; + if (d->d_namlen <= 2 || d->d_name[0] != CMDPRE || + d->d_name[1] != '.') + continue; + if (analjob(d->d_name) < 0) { + fprintf(stderr, "out of memory\n"); + break; + } + } + closedir(df); +} + +/* + * analjob does the grunge work of verifying jobs + */ +#include +analjob(filename) +char *filename; +{ + struct job *jp; + struct sys *sp; + char sbuf[MAXNAMLEN+1], str[256], nbuf[256]; + char *jptr, *wrkvec[20]; + char grade; + FILE *fp, *df; + struct stat statb; + int files, gotname, i; + off_t bytes; + + strncpy(sbuf, filename, MAXNAMLEN); + sbuf[MAXNAMLEN] = '\0'; + jptr = sbuf + strlen(sbuf) - WSUFSIZE; + grade = *jptr; + *jptr++ = 0; + /* + * sbuf+2 now points to sysname name (null terminated) + * jptr now points to job number (null terminated) + */ + if (rmjob) { + if (strcmp(rmjob, jptr)) + return(0); + } else { + if ((sp = getsys(sbuf+2)) == NOSYS) + return(0); + if (!lflag) { + /* SHOULD USE A SMALLER STRUCTURE HERE */ + jp = (struct job *)malloc(sizeof(struct job)); + if (jp == (struct job *)0) + return(-1); + strcpy(jp->j_jobno, jptr); + jp->j_jobp = sp->s_jobp; + jp->j_grade = grade; + sp->s_jobp = jp; + sp->s_njobs++; + return(1); + } + } + if ((fp = fopen(subfile(filename), "r")) == NULL) { + perror(subfile(filename)); + return(0); + } + files = 0; + bytes = 0; + gotname = 0; + while (fgets(str, sizeof str, fp)) { + if (getargs(str, wrkvec, 20) <= 0) + continue; + if (rmjob) { + int myuid; + struct passwd *pw; + /* + * Make sure person who is removing data files is + * the person who created it or root. + */ + myuid = getuid(); + pw = getpwnam(W_USER); + if (myuid && (pw == NULL || myuid != pw->pw_uid)) { + fprintf(stderr, "Permission denied.\n"); + exit(1); + } + if (W_TYPE[0] == 'S' && !index(W_OPTNS, 'c')) { + unlink(subfile(W_DFILE)); + fprintf(stderr, "Removing data file %s\n", W_DFILE); + } + continue; + } + if (user && (W_TYPE[0] == 'X' || !prefix(user, W_USER))) { + fclose(fp); + return(0); + } + files++; + if (W_TYPE[0] == 'S') { + if (strcmp(W_DFILE, "D.0") && + stat(subfile(W_DFILE), &statb) >= 0) + bytes += statb.st_size; + else if (stat(subfile(W_FILE1), &statb) >= 0) + bytes += statb.st_size; + } + /* amusing heuristic */ +#define isXfile(s) (s[0]=='D' && s[strlen(s)-WSUFSIZE]=='X') + if (gotname == 0 && isXfile(W_FILE1)) { + if ((df = fopen(subfile(W_FILE1), "r")) == NULL) + continue; + while (fgets(nbuf, sizeof nbuf, df)) { + nbuf[strlen(nbuf) - 1] = '\0'; + if (nbuf[0] == 'C' && nbuf[1] == ' ') { + strcpy(Filename, nbuf+2); + gotname++; + } else if (nbuf[0] == 'R' && nbuf[1] == ' ') { + register char *p, *q, *r; + r = q = p = nbuf+2; + do { + if (*p == '!' || *p == '@'){ + r = q; + q = p+1; + } + } while (*p++); + + strcpy(Username, r); + W_USER = Username; + } + } + fclose(df); + } + } + fclose(fp); + if (rmjob) { + unlink(subfile(filename)); + fprintf(stderr, "Removing command file %s\n", filename); + exit(0); + } + if (files == 0) { + static char *wtype = "X"; + static char *wfile = "forced poll"; + if (strcmp("POLL", &filename[strlen(filename)-4])) { + fprintf(stderr, "%.14s: empty command file\n", filename); + return(0); + } + W_TYPE = wtype; + W_FILE1 = wfile; + } + jp = (struct job *)malloc(sizeof(struct job)); + if (jp == (struct job *)0) + return(-1); + strcpy(jp->j_jobno, jptr); + jp->j_files = files; + jp->j_bytes = bytes; + jp->j_grade = grade; + jp->j_flags = W_TYPE[0]; + strncpy(jp->j_user, W_TYPE[0]=='X' ? "---" : W_USER, 20 ); + jp->j_user[20] = '\0'; + i = strlen(jp->j_user); + if (i > Maxulen) + Maxulen = i; + /* SHOULD ADD ALL INFORMATION IN THE WHILE LOOP */ + if (gotname) + strncpy(jp->j_fname, Filename, sizeof jp->j_fname); + else + strncpy(jp->j_fname, W_FILE1, sizeof jp->j_fname); + stat(subfile(filename), &statb); + jp->j_date = statb.st_mtime; + jp->j_jobp = sp->s_jobp; + sp->s_jobp = jp; + sp->s_njobs++; + sp->s_bytes += jp->j_bytes; + return(1); +} + +struct sys * +getsys(s) +register char *s; +{ + register struct sys *sp; + + for (sp = syshead; sp; sp = sp->s_sysp) + if (strcmp(s, sp->s_name) == 0) + return(sp); + if (sysname && !prefix(sysname, s)) + return(NOSYS); + sp = (struct sys *)malloc(sizeof(struct sys)); + if (sp == NOSYS) + return(NOSYS); + strcpy(sp->s_name, s); + sp->s_njobs = 0; + sp->s_jobp = (struct job *)0; + sp->s_sysp = syshead; + sp->s_bytes = 0; + syshead = sp; + return(sp); +} diff --git a/usr.bin/uucp/uusend/Makefile b/usr.bin/uucp/uusend/Makefile new file mode 100644 index 00000000000..6c13fb4e6dd --- /dev/null +++ b/usr.bin/uucp/uusend/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 + +PROG= uusend +LINKS= ${BINDIR}/uusend ${BINDIR}/ruusend + +.include diff --git a/usr.bin/uucp/uusend/uusend.1 b/usr.bin/uucp/uusend/uusend.1 new file mode 100644 index 00000000000..93793071529 --- /dev/null +++ b/usr.bin/uucp/uusend/uusend.1 @@ -0,0 +1,96 @@ +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)uusend.1 8.3 (Berkeley) 2/16/94 +.\" +.Dd February 16, 1994 +.Dt UUSEND 1 +.Os BSD 4 +.Sh NAME +.Nm uusend +.Nd send a file to a remote host +.Sh SYNOPSIS +.Nm uusend +.Op Fl m Ar mode +.Ar sourcefile +.Ar sys1!sys2!..!remotefile +.Sh DESCRIPTION +.Nm Uusend +sends a file to a given location on a remote system. +The system need not be directly connected to the local +system, but a chain of +.Xr uucp 1 +links must to connect the two systems. +.Pp +Available option: +.Bl -tag -width Fl +.It Fl m Ar mode +The mode of the file on the remote +end is taken from the octal number given. +Otherwise, the mode of the input file will be used. +.El +.Pp +The sourcefile +can be +.Ql Fl , +meaning to use the standard input. +Both of these options are primarily intended for internal use of +.Nm uusend . +.Pp +The remotefile can include the +.Em ~userid +syntax. +.Sh DIAGNOSTICS +If anything goes wrong any further away than the first system down +the line, you will never hear about it. +.Sh SEE ALSO +.Xr uux 1 , +.Xr uucp 1 , +.Xr uuencode 1 +.Sh BUGS +This command should not exist, since +.Xr uucp +should handle it. +.Pp +All systems along the line must have the +.Nm uusend +command available and allow remote execution of it. +.Pp +Some uucp systems have a bug where binary files cannot be the +input to a +.Xr uux 1 +command. If this bug exists in any system along the line, +the file will show up severely munged. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.0 . diff --git a/usr.bin/uucp/uusend/uusend.c b/usr.bin/uucp/uusend/uusend.c new file mode 100644 index 00000000000..207108e93dc --- /dev/null +++ b/usr.bin/uucp/uusend/uusend.c @@ -0,0 +1,403 @@ +/*- + * 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[] = "@(#)uusend.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +/* + * uusend: primitive operation to allow uucp like copy of binary files + * but handle indirection over systems. + * + * usage: uusend [-r] [-m ooo] localfile sysname1!sysname2!...!destfile + * uusend [-r] [-m ooo] - sysname1!sysname2!...!destfile + * + * Author: Mark Horton, May 1980. + * + * "-r" switch added. Has same effect as "-r" in uux. 11/82 CCW + * + * Error recovery (a la uucp) added & ifdefs for ruusend (as in rmail). + * Checks for illegal access to /usr/lib/uucp. + * February 1983 Christopher Woodbury + * Fixed mode set[ug]id loophole. 4/8/83 CCW + * + * Add '-f' to make uusend syntax more similar to UUCP. "destname" + * can now be a directory. June 1983 CCW + */ + +#include +#include +#include +#include + +/* + * define RECOVER to permit requests like 'uusend file sys1!sys2!~uucp' + * (abbreviation for 'uusend file sys1!sys2!~uucp/file'). + * define DEBUG to keep log of uusend uusage. + * define RUUSEND if neighboring sites permit 'ruusend', + * which they certainly should to avoid security holes + */ +#define RECOVER +/*#define DEBUG "/usr/spool/uucp/uusend.log"/**/ + +FILE *in, *out; +FILE *dout; + +extern FILE *popen(); +extern char *index(), *strcpy(), *strcat(), *ctime(); + +#ifdef RUUSEND +int rsend; +#endif RUUSEND +int mode = -1; /* mode to chmod new file to */ +char *nextsys; /* next system in the chain */ +char dnbuf[200]; /* buffer for result of ~user/file */ +char cmdbuf[256]; /* buffer to build uux command in */ +char *rflg = ""; /* default value of rflg ccw -- 1 Nov '82 */ + +struct passwd *user; /* entry in /etc/passwd for ~user */ +struct passwd *getpwnam(); +struct stat stbuf; + +char *excl; /* location of first ! in destname */ +char *sl; /* location of first / in destname */ +char *sourcename; /* argv[1] */ +char *destname; /* argv[2] */ +char *UULIB = "/usr/lib/uucp"; /* UUCP lib directory */ + +#ifdef RECOVER +char *UUPUB = "/usr/spool/uucppublic/"; /* public UUCP directory */ +char *filename; /* file name from end of destname */ +char *getfname(); /* routine to get filename from destname */ +int fflg; +char f[100]; /* name of default output file */ +#else !RECOVER +char *f = ""; /* so we waste a little space */ +#endif !RECOVER + +main(argc, argv) +int argc; +char **argv; +{ + register int c; + long count; + extern char **environ; + +#ifdef DEBUG + long t; + umask(022); + dout = fopen(DEBUG, "a"); + if (dout == NULL) { + printf("Cannot append to %s\n", DEBUG); + exit(1); + } + freopen(DEBUG, "a", stdout); + fprintf(dout, "\nuusend run: "); + for (c=0; c 1 && argv[1][0] == '-' && argv[1][1]) { + switch(argv[1][1]) { + case 'm': + sscanf(argv[2], "%o", &mode); + mode &= 0777; /* fix set[ug]id loophole */ + argc--; argv++; + break; + case 'r': /* -r flag for uux */ + rflg = "-r "; + break; +#ifdef RECOVER + case 'f': + fflg++; + strcpy(f, argv[1]); + break; +#endif RECOVER + default: + fprintf(stderr, "Bad flag: %s\n", argv[1]); + break; + } + argc--; argv++; + } + + if (argc != 3) { + fprintf(stderr, "Usage: uusend [-m ooo] [-r] -/file sys!sys!..!rfile\n"); + exit(1); + } + + sourcename = argv[1]; + destname = argv[2]; + + if (sourcename[0] == '-') + in = stdin; + else { +#ifdef RUUSEND + if (rsend) { + fprintf(stderr, "illegal input\n"); + exit(2); + } +#endif RUUSEND + in = fopen(sourcename, "r"); + if (in == NULL) { + perror(argv[1]); + exit(2); + } + if (!fflg || f[2] == '\0') { + strcpy(f, "-f"); + strcat(f, getfname(sourcename)); + fflg++; + } + } + + excl = index(destname, '!'); + if (excl) { + /* + * destname is on a remote system. + */ + nextsys = destname; + *excl++ = 0; + destname = excl; + if (mode < 0) { + fstat(fileno(in), &stbuf); + mode = stbuf.st_mode & 0777; + } +#ifdef RUUSEND + sprintf(cmdbuf,"uux -gn -z %s- \"%s!ruusend %s -m %o - (%s)\"", +#else !RUUSEND + sprintf(cmdbuf, "uux -gn -z %s- \"%s!uusend %s -m %o - (%s)\"", +#endif !RUUSEND + rflg, nextsys, f, mode, destname); +#ifdef DEBUG + fprintf(dout, "remote: nextsys='%s', destname='%s', cmd='%s'\n", nextsys, destname, cmdbuf); +#endif DEBUG + out = popen(cmdbuf, "w"); + } else { + /* + * destname is local. + */ + if (destname[0] == '~') { +#ifdef DEBUG + fprintf(dout, "before ~: '%s'\n", destname); +fflush(dout); +#endif DEBUG + sl = index(destname, '/'); +#ifdef RECOVER + if (sl == NULL && !fflg) { + fprintf(stderr, "Illegal ~user\n"); + exit(3); + } + for (sl = destname; *sl != '\0'; sl++) + ; /* boy, is this a hack! */ +#else !RECOVER + if (sl == NULL) { + fprintf(stderr, "Illegal ~user\n"); + exit(3); + } + *sl++ = 0; +#endif !RECOVER + user = getpwnam(destname+1); + if (user == NULL) { + fprintf(stderr, "No such user as %s\n", + destname); +#ifdef RECOVER + if ((filename =getfname(sl)) == NULL && + !fflg) + exit(4); + strcpy(dnbuf, UUPUB); + if (fflg) + strcat(dnbuf, &f[2]); + else + strcat(dnbuf, filename); + } + else { + strcpy(dnbuf, user->pw_dir); + strcat(dnbuf, "/"); + strcat(dnbuf, sl); + } +#else !RECOVER + exit(4); + } + strcpy(dnbuf, user->pw_dir); + strcat(dnbuf, "/"); + strcat(dnbuf, sl); +#endif !RECOVER + destname = dnbuf; + } +#ifdef RECOVER + else + destname = strcpy(dnbuf, destname); +#endif !RECOVER + if(strncmp(UULIB, destname, strlen(UULIB)) == 0) { + fprintf(stderr, "illegal file: %s", destname); + exit(4); + } +#ifdef RECOVER + if (stat(destname, &stbuf) == 0 && + (stbuf.st_mode & S_IFMT) == S_IFDIR && + fflg) { + strcat(destname, "/"); + strcat(destname, &f[2]); + } +#endif RECOVER + out = fopen(destname, "w"); +#ifdef DEBUG + fprintf(dout, "local, file='%s'\n", destname); +#endif DEBUG + if (out == NULL) { + perror(destname); +#ifdef RECOVER + if (strncmp(destname,UUPUB,strlen(UUPUB)) == 0) + exit(5); /* forget it! */ + filename = getfname(destname); + if (destname == dnbuf) /* cmdbuf is scratch */ + filename = strcpy(cmdbuf, filename); + destname = strcpy(dnbuf, UUPUB); + if (user != NULL) { + strcat(destname, user->pw_name); + if (stat(destname, &stbuf) == -1) { + mkdir(destname, 0777); + } + strcat(destname, "/"); + } + if (fflg) + strcat(destname, &f[2]); + else + strcat(destname, filename); + if ((out = fopen(destname, "w")) == NULL) + exit(5); /* all for naught! */ +#else !RECOVER + exit(5); +#endif !RECOVER + } + if (mode > 0) + chmod(destname, mode); /* don't bother to check it */ + } + + /* + * Now, in any case, copy from in to out. + */ + + count = 0; + while ((c=getc(in)) != EOF) { + putc(c, out); + count++; + } +#ifdef DEBUG + fprintf(dout, "count %ld bytes\n", count); + fclose(dout); +#endif DEBUG + + fclose(in); + fclose(out); /* really should pclose in that case */ + exit(0); +} + +/* + * Return the ptr in sp at which the character c appears; + * NULL if not found. Included so I don't have to fight the + * index/strchr battle. + */ + +#define NULL 0 + +char * +index(sp, c) +register char *sp, c; +{ + do { + if (*sp == c) + return(sp); + } while (*sp++); + return(NULL); +} + +#ifdef RECOVER +char * +getfname(p) +register char *p; +{ + register char *s; + s = p; + while (*p != '\0') + p++; + if (p == s) + return (NULL); + for (;p != s; p--) + if (*p == '/') { + p++; + break; + } + return (p); +} + +#ifndef BSD4_2 +makedir(dirname, mode) +char *dirname; +int mode; +{ + register int pid; + int retcode, status; + switch ((pid = fork())) { + case -1: /* error */ + return (-1); + case 0: /* child */ + umask(0); + execl("/bin/mkdir", "mkdir", dirname, (char *)0); + exit(1); + /* NOTREACHED */ + default: /* parent */ + while ((retcode=wait(&status)) != pid && retcode != -1) + ; + if (retcode == -1) + return -1; + else { + chmod(dirname, mode); + return status; + } + } + /* NOTREACHED */ +} +#endif !BSD4_2 +#endif RECOVER diff --git a/usr.bin/uucp/uusnap/uusnap.8 b/usr.bin/uucp/uusnap/uusnap.8 new file mode 100644 index 00000000000..92f0e3307e3 --- /dev/null +++ b/usr.bin/uucp/uusnap/uusnap.8 @@ -0,0 +1,80 @@ +.\" 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. +.\" +.\" @(#)uusnap.8 8.1 (Berkeley) 6/6/93 +.\" +.Dd June 6, 1993 +.Dt UUSNAP 8 +.Os BSD 4.2 +.Sh NAME +.Nm uusnap +.Nd show snapshot of the +.Tn UUCP +system +.Sh SYNOPSIS +.Nm uusnap +.Sh DESCRIPTION +.Nm Uusnap +displays in tabular format a synopsis of the current +.Tn UUCP +situation. The format of each line is as follows: +.Bd -literal -offset indent -compact + +site N Cmds N Data N Xqts Message + +.Ed +Where "site" is the name of the site with work, "N" is a count of +each of the three possible types of work (command, data, or remote execute), +and "Message" is the current status message for that +site as found in the +.Tn STST +file. +.Pp +Included in "Message" may be the time left before +.Tn UUCP +can re-try the +call, and the count of the number of times that +.Tn UUCP +has tried +(unsuccessfully) to reach the site. +.Sh SEE ALSO +.Xr uucp 1 , +.Xr uux 1 , +.Xr uuq 1 , +.Xr uucico 8 +.Rs +.%T "UUCP Implementation Guide" +.Re +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/usr.bin/uucp/uusnap/uusnap.c b/usr.bin/uucp/uusnap/uusnap.c new file mode 100644 index 00000000000..565c52af72a --- /dev/null +++ b/usr.bin/uucp/uusnap/uusnap.c @@ -0,0 +1,348 @@ +/*- + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Rick Adams. Originally by RJKing WECo-MG6565 May 83. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE 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[] = "@(#)uusnap.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#include "uucp.h" +#include +#ifdef NDIR +#include "ndir.h" +#else +#include +#endif +#include + +#define NSYSTEM 300 /* max # of systems queued */ + +#define CMDSLEN 5 /* Length of trailer */ +#define DATALEN 5 /* Length of trailer */ +#define XEQTLEN 5 /* Length of trailer */ +#define NUMCTRS 3 /* # file types to count */ +#define CMDTYPE 0 /* Index into scnt.cntr */ +#define DATTYPE 1 /* Index into scnt.cntr */ +#define XEQTYPE 2 /* Index into scnt.cntr */ + +struct scnt { /* System count structure */ + char name[MAXBASENAME+1]; /* Name of system */ + short cntr[NUMCTRS]; /* Count */ + char stst[32]; /* STST Message */ + time_t locked; /* If LCK..sys present */ + int st_type; /* STST Type */ + int st_count; /* STST Count */ + time_t st_lastime; /* STST Last time tried */ + time_t st_retry; /* STST Secs to retry */ + }; + +int sndx; /* Number of systems */ +struct scnt sys[NSYSTEM]; /* Systems queued */ +int xqtisrunning = 0; + +main() +{ + register int i, j, nlen = 0; + time_t curtime, t; + + dodir(CMDSDIR, "C.", CMDSLEN, '\0', CMDTYPE); + dodir(DATADIR, "D.", DATALEN, '\0', DATTYPE); + dodir(XEQTDIR, "X.", XEQTLEN, 'X', XEQTYPE); + getstst(SPOOL); + time(&curtime); + for(i=0; i nlen) + nlen = j; + for(i=0; i1?"s":" "); + else + printf(" --- "); + if(sys[i].cntr[DATTYPE]) + printf("%3.d Data ", sys[i].cntr[DATTYPE]); + else + printf(" --- "); + if(sys[i].cntr[XEQTYPE]) + printf("%3.d Xqt%s ", sys[i].cntr[XEQTYPE], + sys[i].cntr[XEQTYPE]>1?"s":" "); + else + printf(" --- "); + if(*sys[i].stst == '\0' || sys[i].locked > sys[i].st_lastime) { + if(sys[i].locked) + printf("LOCKED\n"); + else + printf("\n"); + continue; + } + printf("%s ", sys[i].stst); + /* decide if STST info is worth pursuing */ + if (-t < ONEDAY*2 && (sys[i].st_count == 0 + || sys[i].st_type == SS_WRONGTIME + || (sys[i].st_type == SS_INPROGRESS && sys[i].locked))) { + printf("\n"); + continue; + } + t = (sys[i].st_lastime +sys[i].st_retry) - curtime; + if (-t < ONEDAY*2 && sys[i].st_type != SS_FAIL) + t = 0; + + if (sys[i].st_count > MAXRECALLS) + printf("at MAX RECALLS"); + else if (-t >= ONEDAY*2) + printf("%ld days ago", (long)-t/ONEDAY); + else if (t <= 0) + printf("Retry time reached"); + else if (t < 60) + printf("Retry time %ld sec%s", (long)(t%60), + (t%60)!=1? "s": ""); + else + printf("Retry time %ld min%s", (long)(t/60), + (t/60)!=1? "s": ""); + if(sys[i].st_count > 1) + printf(" Count: %d\n", sys[i].st_count); + else + printf("\n"); + } + if (xqtisrunning) + printf("\nUuxqt is running\n"); + exit(0); +} + +dodir(dnam, prfx, flen, fchr, type) +char *dnam, *prfx; +int flen; +char fchr; +int type; +{ + register struct direct *dentp; + register DIR *dirp; + register int i, fnamlen, plen; + char fnam[MAXNAMLEN+1]; + + plen = strlen(prfx); + if(chdir(dnam) < 0) { + perror(dnam); + exit(1); + } + if ((dirp = opendir(".")) == NULL) { + perror(dnam); + exit(1); + } + while((dentp = readdir(dirp)) != NULL) { + if(*dentp->d_name == '.') + continue; + if(strncmp(dentp->d_name, prfx, plen) != SAME) { + fprintf(stderr, "strange file (%s) in %s\n", + dentp->d_name, dnam); + continue; + } + strcpy(fnam, &dentp->d_name[plen]); + fnamlen = strlen(fnam); + if(flen > 0) { + fnamlen -= flen; + fnam[fnamlen] = '\0'; + fnamlen = MAXBASENAME; /* yes, after = '\0'*/ + } else { + for(; fnamlen>0; --fnamlen) { + if(fnam[fnamlen] == fchr) { + fnam[fnamlen] = '\0'; + break; + } + } + fnamlen = MAXBASENAME; + } + for(i=0; i= NSYSTEM) { + sndx = NSYSTEM-1; + fprintf(stderr,"Too many system names.\n"); + } + } + } + closedir(dirp); +} + +getstst(sdir) +char *sdir; +{ + register int i, csys; + register char *tp; + char fnam[MAXNAMLEN+1], buff[128]; + register struct direct *dentp; + register DIR *dirp; + register FILE *st; + struct stat stbuf; + long atol(); + + if (chdir(sdir) < 0) { + perror(sdir); + exit(1); + } + if ((dirp = opendir(LOCKDIR)) == NULL) { + perror(sdir); + exit(1); + } + while ((dentp = readdir(dirp)) != NULL) { + if (strcmp(&dentp->d_name[5], X_LOCK) == SAME) { + xqtisrunning++; + continue; + } + if(strncmp(dentp->d_name, "LCK..", 5) == SAME) { + if(strncmp(&dentp->d_name[5], "tty", 3) == SAME || + strncmp(&dentp->d_name[5], "cul", 3) == SAME) + continue; + strcpy(fnam, dentp->d_name); + for(csys=0; csysd_name == '.') + continue; + strcpy(fnam, dentp->d_name); + for(csys=0; csys. +--eric +.Ed +.Pp +.Nm Vacation +reads the first line from the standard input for a +.Ux +.Dq From +line to determine the sender. +.Xr Sendmail 8 +includes this +.Dq From +line automatically. +.Pp +Fatal errors, such as calling +.Nm vacation +with incorrect arguments, or with non-existent +.Ar login Ns Ar s , +are logged in the system log file, using +.Xr syslog 8 . +.Sh FILES +.Bl -tag -width "vacation.dirxxx" -compact +.It Pa ~/.vacation.db +database file +.It Pa ~/.vacation.msg +message to send +.El +.Sh SEE ALSO +.Xr sendmail 8 , +.Xr syslog 8 +.Sh HISTORY +The +.Nm vacation +command appeared in +.Bx 4.3 . diff --git a/usr.bin/vmstat/vmstat.c b/usr.bin/vmstat/vmstat.c new file mode 100644 index 00000000000..92c0dad2660 --- /dev/null +++ b/usr.bin/vmstat/vmstat.c @@ -0,0 +1,876 @@ +/* + * Copyright (c) 1980, 1986, 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, 1986, 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)vmstat.c 8.2 (Berkeley) 3/1/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 +#include +#include + +#define NEWVM /* XXX till old has been updated or purged */ +struct nlist namelist[] = { +#define X_CPTIME 0 + { "_cp_time" }, +#define X_DK_NDRIVE 1 + { "_dk_ndrive" }, +#define X_SUM 2 + { "_cnt" }, +#define X_BOOTTIME 3 + { "_boottime" }, +#define X_DKXFER 4 + { "_dk_xfer" }, +#define X_HZ 5 + { "_hz" }, +#define X_STATHZ 6 + { "_stathz" }, +#define X_NCHSTATS 7 + { "_nchstats" }, +#define X_INTRNAMES 8 + { "_intrnames" }, +#define X_EINTRNAMES 9 + { "_eintrnames" }, +#define X_INTRCNT 10 + { "_intrcnt" }, +#define X_EINTRCNT 11 + { "_eintrcnt" }, +#define X_KMEMSTAT 12 + { "_kmemstats" }, +#define X_KMEMBUCKETS 13 + { "_bucket" }, +#ifdef notdef +#define X_DEFICIT 14 + { "_deficit" }, +#define X_FORKSTAT 15 + { "_forkstat" }, +#define X_REC 16 + { "_rectime" }, +#define X_PGIN 17 + { "_pgintime" }, +#define X_XSTATS 18 + { "_xstats" }, +#define X_END 18 +#else +#define X_END 14 +#endif +#if defined(hp300) || defined(luna68k) +#define X_HPDINIT (X_END) + { "_hp_dinit" }, +#endif +#ifdef mips +#define X_SCSI_DINIT (X_END) + { "_scsi_dinit" }, +#endif +#ifdef tahoe +#define X_VBDINIT (X_END) + { "_vbdinit" }, +#define X_CKEYSTATS (X_END+1) + { "_ckeystats" }, +#define X_DKEYSTATS (X_END+2) + { "_dkeystats" }, +#endif +#ifdef vax +#define X_MBDINIT (X_END) + { "_mbdinit" }, +#define X_UBDINIT (X_END+1) + { "_ubdinit" }, +#endif + { "" }, +}; + +struct _disk { + long time[CPUSTATES]; + long *xfer; +} cur, last; + +struct vmmeter sum, osum; +char **dr_name; +int *dr_select, dk_ndrive, ndrives; + +int winlines = 20; + +kvm_t *kd; + +#define FORKSTAT 0x01 +#define INTRSTAT 0x02 +#define MEMSTAT 0x04 +#define SUMSTAT 0x08 +#define TIMESTAT 0x10 +#define VMSTAT 0x20 + +#include "names.c" /* disk names -- machine dependent */ + +void cpustats(), dkstats(), dointr(), domem(), dosum(); +void dovmstat(), kread(), usage(); +#ifdef notdef +void dotimes(), doforkst(); +#endif + +main(argc, argv) + register int argc; + register char **argv; +{ + extern int optind; + extern char *optarg; + register int c, todo; + u_int interval; + int reps; + char *memf, *nlistf; + char errbuf[_POSIX2_LINE_MAX]; + + memf = nlistf = NULL; + interval = reps = todo = 0; + while ((c = getopt(argc, argv, "c:fiM:mN:stw:")) != EOF) { + switch (c) { + case 'c': + reps = atoi(optarg); + break; +#ifndef notdef + case 'f': + todo |= FORKSTAT; + break; +#endif + case 'i': + todo |= INTRSTAT; + break; + case 'M': + memf = optarg; + break; + case 'm': + todo |= MEMSTAT; + break; + case 'N': + nlistf = optarg; + break; + case 's': + todo |= SUMSTAT; + break; +#ifndef notdef + case 't': + todo |= TIMESTAT; + break; +#endif + case 'w': + interval = atoi(optarg); + break; + case '?': + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (todo == 0) + todo = VMSTAT; + + /* + * Discard setgid privileges if not the running kernel so that bad + * guys can't print interesting stuff from kernel memory. + */ + if (nlistf != NULL || memf != NULL) + setgid(getgid()); + + kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf); + if (kd == 0) { + (void)fprintf(stderr, + "vmstat: kvm_openfiles: %s\n", errbuf); + exit(1); + } + + if ((c = kvm_nlist(kd, namelist)) != 0) { + if (c > 0) { + (void)fprintf(stderr, + "vmstat: undefined symbols:"); + for (c = 0; + c < sizeof(namelist)/sizeof(namelist[0]); c++) + if (namelist[c].n_type == 0) + fprintf(stderr, " %s", + namelist[c].n_name); + (void)fputc('\n', stderr); + } else + (void)fprintf(stderr, "vmstat: kvm_nlist: %s\n", + kvm_geterr(kd)); + exit(1); + } + + if (todo & VMSTAT) { + char **getdrivedata(); + struct winsize winsize; + + argv = getdrivedata(argv); + winsize.ws_row = 0; + (void) ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&winsize); + if (winsize.ws_row > 0) + winlines = winsize.ws_row; + + } + +#define BACKWARD_COMPATIBILITY +#ifdef BACKWARD_COMPATIBILITY + if (*argv) { + interval = atoi(*argv); + if (*++argv) + reps = atoi(*argv); + } +#endif + + if (interval) { + if (!reps) + reps = -1; + } else if (reps) + interval = 1; + +#ifdef notdef + if (todo & FORKSTAT) + doforkst(); +#endif + if (todo & MEMSTAT) + domem(); + if (todo & SUMSTAT) + dosum(); +#ifdef notdef + if (todo & TIMESTAT) + dotimes(); +#endif + if (todo & INTRSTAT) + dointr(); + if (todo & VMSTAT) + dovmstat(interval, reps); + exit(0); +} + +char ** +getdrivedata(argv) + char **argv; +{ + register int i; + register char **cp; + char buf[30]; + + kread(X_DK_NDRIVE, &dk_ndrive, sizeof(dk_ndrive)); + if (dk_ndrive <= 0) { + (void)fprintf(stderr, "vmstat: dk_ndrive %d\n", dk_ndrive); + exit(1); + } + dr_select = calloc((size_t)dk_ndrive, sizeof(int)); + dr_name = calloc((size_t)dk_ndrive, sizeof(char *)); + for (i = 0; i < dk_ndrive; i++) + dr_name[i] = NULL; + cur.xfer = calloc((size_t)dk_ndrive, sizeof(long)); + last.xfer = calloc((size_t)dk_ndrive, sizeof(long)); + if (!read_names()) + exit (1); + for (i = 0; i < dk_ndrive; i++) + if (dr_name[i] == NULL) { + (void)sprintf(buf, "??%d", i); + dr_name[i] = strdup(buf); + } + + /* + * Choose drives to be displayed. Priority goes to (in order) drives + * supplied as arguments, default drives. If everything isn't filled + * in and there are drives not taken care of, display the first few + * that fit. + */ +#define BACKWARD_COMPATIBILITY + for (ndrives = 0; *argv; ++argv) { +#ifdef BACKWARD_COMPATIBILITY + if (isdigit(**argv)) + break; +#endif + for (i = 0; i < dk_ndrive; i++) { + if (strcmp(dr_name[i], *argv)) + continue; + dr_select[i] = 1; + ++ndrives; + break; + } + } + for (i = 0; i < dk_ndrive && ndrives < 4; i++) { + if (dr_select[i]) + continue; + for (cp = defdrives; *cp; cp++) + if (strcmp(dr_name[i], *cp) == 0) { + dr_select[i] = 1; + ++ndrives; + break; + } + } + for (i = 0; i < dk_ndrive && ndrives < 4; i++) { + if (dr_select[i]) + continue; + dr_select[i] = 1; + ++ndrives; + } + return(argv); +} + +long +getuptime() +{ + static time_t now, boottime; + time_t uptime; + + if (boottime == 0) + kread(X_BOOTTIME, &boottime, sizeof(boottime)); + (void)time(&now); + uptime = now - boottime; + if (uptime <= 0 || uptime > 60*60*24*365*10) { + (void)fprintf(stderr, + "vmstat: time makes no sense; namelist must be wrong.\n"); + exit(1); + } + return(uptime); +} + +int hz, hdrcnt; + +void +dovmstat(interval, reps) + u_int interval; + int reps; +{ + struct vmtotal total; + time_t uptime, halfuptime; + void needhdr(); + int mib[2], size; + + uptime = getuptime(); + halfuptime = uptime / 2; + (void)signal(SIGCONT, needhdr); + + if (namelist[X_STATHZ].n_type != 0 && namelist[X_STATHZ].n_value != 0) + kread(X_STATHZ, &hz, sizeof(hz)); + if (!hz) + kread(X_HZ, &hz, sizeof(hz)); + + for (hdrcnt = 1;;) { + if (!--hdrcnt) + printhdr(); + kread(X_CPTIME, cur.time, sizeof(cur.time)); + kread(X_DKXFER, cur.xfer, sizeof(*cur.xfer) * dk_ndrive); + kread(X_SUM, &sum, sizeof(sum)); + size = sizeof(total); + mib[0] = CTL_VM; + mib[1] = VM_METER; + if (sysctl(mib, 2, &total, &size, NULL, 0) < 0) { + printf("Can't get kerninfo: %s\n", strerror(errno)); + bzero(&total, sizeof(total)); + } + (void)printf("%2d%2d%2d", + total.t_rq - 1, total.t_dw + total.t_pw, total.t_sw); +#define pgtok(a) ((a) * sum.v_page_size >> 10) +#define rate(x) (((x) + halfuptime) / uptime) /* round */ + (void)printf("%6ld%6ld ", + pgtok(total.t_avm), pgtok(total.t_free)); +#ifdef NEWVM + (void)printf("%4lu ", rate(sum.v_faults - osum.v_faults)); + (void)printf("%3lu ", + rate(sum.v_reactivated - osum.v_reactivated)); + (void)printf("%3lu ", rate(sum.v_pageins - osum.v_pageins)); + (void)printf("%3lu %3lu ", + rate(sum.v_pageouts - osum.v_pageouts), 0); +#else + (void)printf("%3lu %2lu ", + rate(sum.v_pgrec - (sum.v_xsfrec+sum.v_xifrec) - + (osum.v_pgrec - (osum.v_xsfrec+osum.v_xifrec))), + rate(sum.v_xsfrec + sum.v_xifrec - + osum.v_xsfrec - osum.v_xifrec)); + (void)printf("%3lu ", + rate(pgtok(sum.v_pgpgin - osum.v_pgpgin))); + (void)printf("%3lu %3lu ", + rate(pgtok(sum.v_pgpgout - osum.v_pgpgout)), + rate(pgtok(sum.v_dfree - osum.v_dfree))); + (void)printf("%3d ", pgtok(deficit)); +#endif + (void)printf("%3lu ", rate(sum.v_scan - osum.v_scan)); + dkstats(); + (void)printf("%4lu %4lu %3lu ", + rate(sum.v_intr - osum.v_intr), + rate(sum.v_syscall - osum.v_syscall), + rate(sum.v_swtch - osum.v_swtch)); + cpustats(); + (void)printf("\n"); + (void)fflush(stdout); + if (reps >= 0 && --reps <= 0) + break; + osum = sum; + uptime = interval; + /* + * We round upward to avoid losing low-frequency events + * (i.e., >= 1 per interval but < 1 per second). + */ + halfuptime = (uptime + 1) / 2; + (void)sleep(interval); + } +} + +printhdr() +{ + register int i; + + (void)printf(" procs memory page%*s", 20, ""); + if (ndrives > 1) + (void)printf("disks %*s faults cpu\n", + ndrives * 3 - 6, ""); + else + (void)printf("%*s faults cpu\n", ndrives * 3, ""); +#ifndef NEWVM + (void)printf(" r b w avm fre re at pi po fr de sr "); +#else + (void)printf(" r b w avm fre flt re pi po fr sr "); +#endif + for (i = 0; i < dk_ndrive; i++) + if (dr_select[i]) + (void)printf("%c%c ", dr_name[i][0], + dr_name[i][strlen(dr_name[i]) - 1]); + (void)printf(" in sy cs us sy id\n"); + hdrcnt = winlines - 2; +} + +/* + * Force a header to be prepended to the next output. + */ +void +needhdr() +{ + + hdrcnt = 1; +} + +#ifdef notdef +void +dotimes() +{ + u_int pgintime, rectime; + + kread(X_REC, &rectime, sizeof(rectime)); + kread(X_PGIN, &pgintime, sizeof(pgintime)); + kread(X_SUM, &sum, sizeof(sum)); + (void)printf("%u reclaims, %u total time (usec)\n", + sum.v_pgrec, rectime); + (void)printf("average: %u usec / reclaim\n", rectime / sum.v_pgrec); + (void)printf("\n"); + (void)printf("%u page ins, %u total time (msec)\n", + sum.v_pgin, pgintime / 10); + (void)printf("average: %8.1f msec / page in\n", + pgintime / (sum.v_pgin * 10.0)); +} +#endif + +pct(top, bot) + long top, bot; +{ + long ans; + + if (bot == 0) + return(0); + ans = (quad_t)top * 100 / bot; + return (ans); +} + +#define PCT(top, bot) pct((long)(top), (long)(bot)) + +#if defined(tahoe) +#include +#endif + +void +dosum() +{ + struct nchstats nchstats; +#ifndef NEWVM + struct xstats xstats; +#endif + long nchtotal; +#if defined(tahoe) + struct keystats keystats; +#endif + + kread(X_SUM, &sum, sizeof(sum)); + (void)printf("%9u cpu context switches\n", sum.v_swtch); + (void)printf("%9u device interrupts\n", sum.v_intr); + (void)printf("%9u software interrupts\n", sum.v_soft); +#ifdef vax + (void)printf("%9u pseudo-dma dz interrupts\n", sum.v_pdma); +#endif + (void)printf("%9u traps\n", sum.v_trap); + (void)printf("%9u system calls\n", sum.v_syscall); + (void)printf("%9u total faults taken\n", sum.v_faults); + (void)printf("%9u swap ins\n", sum.v_swpin); + (void)printf("%9u swap outs\n", sum.v_swpout); + (void)printf("%9u pages swapped in\n", sum.v_pswpin / CLSIZE); + (void)printf("%9u pages swapped out\n", sum.v_pswpout / CLSIZE); + (void)printf("%9u page ins\n", sum.v_pageins); + (void)printf("%9u page outs\n", sum.v_pageouts); + (void)printf("%9u pages paged in\n", sum.v_pgpgin); + (void)printf("%9u pages paged out\n", sum.v_pgpgout); + (void)printf("%9u pages reactivated\n", sum.v_reactivated); + (void)printf("%9u intransit blocking page faults\n", sum.v_intrans); + (void)printf("%9u zero fill pages created\n", sum.v_nzfod / CLSIZE); + (void)printf("%9u zero fill page faults\n", sum.v_zfod / CLSIZE); + (void)printf("%9u pages examined by the clock daemon\n", sum.v_scan); + (void)printf("%9u revolutions of the clock hand\n", sum.v_rev); +#ifdef NEWVM + (void)printf("%9u VM object cache lookups\n", sum.v_lookups); + (void)printf("%9u VM object hits\n", sum.v_hits); + (void)printf("%9u total VM faults taken\n", sum.v_vm_faults); + (void)printf("%9u copy-on-write faults\n", sum.v_cow_faults); + (void)printf("%9u pages freed by daemon\n", sum.v_dfree); + (void)printf("%9u pages freed by exiting processes\n", sum.v_pfree); + (void)printf("%9u pages free\n", sum.v_free_count); + (void)printf("%9u pages wired down\n", sum.v_wire_count); + (void)printf("%9u pages active\n", sum.v_active_count); + (void)printf("%9u pages inactive\n", sum.v_inactive_count); + (void)printf("%9u bytes per page\n", sum.v_page_size); + (void)printf("%9u target inactive pages\n", sum.v_inactive_target); + (void)printf("%9u target free pages\n", sum.v_free_target); + (void)printf("%9u minimum free pages\n", sum.v_free_min); +#else + (void)printf("%9u sequential process pages freed\n", sum.v_seqfree); + (void)printf("%9u total reclaims (%d%% fast)\n", sum.v_pgrec, + PCT(sum.v_fastpgrec, sum.v_pgrec)); + (void)printf("%9u reclaims from free list\n", sum.v_pgfrec); + (void)printf("%9u executable fill pages created\n", + sum.v_nexfod / CLSIZE); + (void)printf("%9u executable fill page faults\n", + sum.v_exfod / CLSIZE); + (void)printf("%9u swap text pages found in free list\n", + sum.v_xsfrec); + (void)printf("%9u inode text pages found in free list\n", + sum.v_xifrec); + (void)printf("%9u file fill pages created\n", sum.v_nvrfod / CLSIZE); + (void)printf("%9u file fill page faults\n", sum.v_vrfod / CLSIZE); + (void)printf("%9u pages freed by the clock daemon\n", + sum.v_dfree / CLSIZE); +#endif + kread(X_NCHSTATS, &nchstats, sizeof(nchstats)); + nchtotal = nchstats.ncs_goodhits + nchstats.ncs_neghits + + nchstats.ncs_badhits + nchstats.ncs_falsehits + + nchstats.ncs_miss + nchstats.ncs_long; + (void)printf("%9ld total name lookups\n", nchtotal); + (void)printf( + "%9s cache hits (%d%% pos + %d%% neg) system %d%% per-process\n", + "", PCT(nchstats.ncs_goodhits, nchtotal), + PCT(nchstats.ncs_neghits, nchtotal), + PCT(nchstats.ncs_pass2, nchtotal)); + (void)printf("%9s deletions %d%%, falsehits %d%%, toolong %d%%\n", "", + PCT(nchstats.ncs_badhits, nchtotal), + PCT(nchstats.ncs_falsehits, nchtotal), + PCT(nchstats.ncs_long, nchtotal)); +#ifndef NEWVM + kread(X_XSTATS, &xstats, sizeof(xstats)); + (void)printf("%9lu total calls to xalloc (cache hits %d%%)\n", + xstats.alloc, PCT(xstats.alloc_cachehit, xstats.alloc)); + (void)printf("%9s sticky %lu flushed %lu unused %lu\n", "", + xstats.alloc_inuse, xstats.alloc_cacheflush, xstats.alloc_unused); + (void)printf("%9lu total calls to xfree", xstats.free); + (void)printf(" (sticky %lu cached %lu swapped %lu)\n", + xstats.free_inuse, xstats.free_cache, xstats.free_cacheswap); +#endif +#if defined(tahoe) + kread(X_CKEYSTATS, &keystats, sizeof(keystats)); + (void)printf("%9d %s (free %d%% norefs %d%% taken %d%% shared %d%%)\n", + keystats.ks_allocs, "code cache keys allocated", + PCT(keystats.ks_allocfree, keystats.ks_allocs), + PCT(keystats.ks_norefs, keystats.ks_allocs), + PCT(keystats.ks_taken, keystats.ks_allocs), + PCT(keystats.ks_shared, keystats.ks_allocs)); + kread(X_DKEYSTATS, &keystats, sizeof(keystats)); + (void)printf("%9d %s (free %d%% norefs %d%% taken %d%% shared %d%%)\n", + keystats.ks_allocs, "data cache keys allocated", + PCT(keystats.ks_allocfree, keystats.ks_allocs), + PCT(keystats.ks_norefs, keystats.ks_allocs), + PCT(keystats.ks_taken, keystats.ks_allocs), + PCT(keystats.ks_shared, keystats.ks_allocs)); +#endif +} + +#ifdef notdef +void +doforkst() +{ + struct forkstat fks; + + kread(X_FORKSTAT, &fks, sizeof(struct forkstat)); + (void)printf("%d forks, %d pages, average %.2f\n", + fks.cntfork, fks.sizfork, (double)fks.sizfork / fks.cntfork); + (void)printf("%d vforks, %d pages, average %.2f\n", + fks.cntvfork, fks.sizvfork, (double)fks.sizvfork / fks.cntvfork); +} +#endif + +void +dkstats() +{ + register int dn, state; + double etime; + long tmp; + + for (dn = 0; dn < dk_ndrive; ++dn) { + tmp = cur.xfer[dn]; + cur.xfer[dn] -= last.xfer[dn]; + last.xfer[dn] = tmp; + } + etime = 0; + for (state = 0; state < CPUSTATES; ++state) { + tmp = cur.time[state]; + cur.time[state] -= last.time[state]; + last.time[state] = tmp; + etime += cur.time[state]; + } + if (etime == 0) + etime = 1; + etime /= hz; + for (dn = 0; dn < dk_ndrive; ++dn) { + if (!dr_select[dn]) + continue; + (void)printf("%2.0f ", cur.xfer[dn] / etime); + } +} + +void +cpustats() +{ + register int state; + double pct, total; + + total = 0; + for (state = 0; state < CPUSTATES; ++state) + total += cur.time[state]; + if (total) + pct = 100 / total; + else + pct = 0; + (void)printf("%2.0f ", (cur.time[CP_USER] + cur.time[CP_NICE]) * pct); + (void)printf("%2.0f ", (cur.time[CP_SYS] + cur.time[CP_INTR]) * pct); + (void)printf("%2.0f", cur.time[CP_IDLE] * pct); +} + +void +dointr() +{ + register long *intrcnt, inttotal, uptime; + register int nintr, inamlen; + register char *intrname; + + uptime = getuptime(); + nintr = namelist[X_EINTRCNT].n_value - namelist[X_INTRCNT].n_value; + inamlen = + namelist[X_EINTRNAMES].n_value - namelist[X_INTRNAMES].n_value; + intrcnt = malloc((size_t)nintr); + intrname = malloc((size_t)inamlen); + if (intrcnt == NULL || intrname == NULL) { + (void)fprintf(stderr, "vmstat: %s.\n", strerror(errno)); + exit(1); + } + kread(X_INTRCNT, intrcnt, (size_t)nintr); + kread(X_INTRNAMES, intrname, (size_t)inamlen); + (void)printf("interrupt total rate\n"); + inttotal = 0; + nintr /= sizeof(long); + while (--nintr >= 0) { + if (*intrcnt) + (void)printf("%-12s %8ld %8ld\n", intrname, + *intrcnt, *intrcnt / uptime); + intrname += strlen(intrname) + 1; + inttotal += *intrcnt++; + } + (void)printf("Total %8ld %8ld\n", inttotal, inttotal / uptime); +} + +/* + * These names are defined in . + */ +char *kmemnames[] = INITKMEMNAMES; + +void +domem() +{ + register struct kmembuckets *kp; + register struct kmemstats *ks; + register int i, j; + int len, size, first; + long totuse = 0, totfree = 0, totreq = 0; + char *name; + struct kmemstats kmemstats[M_LAST]; + struct kmembuckets buckets[MINBUCKET + 16]; + + kread(X_KMEMBUCKETS, buckets, sizeof(buckets)); + (void)printf("Memory statistics by bucket size\n"); + (void)printf( + " Size In Use Free Requests HighWater Couldfree\n"); + for (i = MINBUCKET, kp = &buckets[i]; i < MINBUCKET + 16; i++, kp++) { + if (kp->kb_calls == 0) + continue; + size = 1 << i; + (void)printf("%8d %8ld %6ld %10ld %7ld %10ld\n", size, + kp->kb_total - kp->kb_totalfree, + kp->kb_totalfree, kp->kb_calls, + kp->kb_highwat, kp->kb_couldfree); + totfree += size * kp->kb_totalfree; + } + + kread(X_KMEMSTAT, kmemstats, sizeof(kmemstats)); + (void)printf("\nMemory usage type by bucket size\n"); + (void)printf(" Size Type(s)\n"); + kp = &buckets[MINBUCKET]; + for (j = 1 << MINBUCKET; j < 1 << (MINBUCKET + 16); j <<= 1, kp++) { + if (kp->kb_calls == 0) + continue; + first = 1; + len = 8; + for (i = 0, ks = &kmemstats[0]; i < M_LAST; i++, ks++) { + if (ks->ks_calls == 0) + continue; + if ((ks->ks_size & j) == 0) + continue; + name = kmemnames[i] ? kmemnames[i] : "undefined"; + len += 2 + strlen(name); + if (first) + printf("%8d %s", j, name); + else + printf(","); + if (len >= 80) { + printf("\n\t "); + len = 10 + strlen(name); + } + if (!first) + printf(" %s", name); + first = 0; + } + printf("\n"); + } + + (void)printf( + "\nMemory statistics by type Type Kern\n"); + (void)printf( +" Type InUse MemUse HighUse Limit Requests Limit Limit Size(s)\n"); + for (i = 0, ks = &kmemstats[0]; i < M_LAST; i++, ks++) { + if (ks->ks_calls == 0) + continue; + (void)printf("%11s%6ld%6ldK%7ldK%6ldK%9ld%5u%6u", + kmemnames[i] ? kmemnames[i] : "undefined", + ks->ks_inuse, (ks->ks_memuse + 1023) / 1024, + (ks->ks_maxused + 1023) / 1024, + (ks->ks_limit + 1023) / 1024, ks->ks_calls, + ks->ks_limblocks, ks->ks_mapblocks); + first = 1; + for (j = 1 << MINBUCKET; j < 1 << (MINBUCKET + 16); j <<= 1) { + if ((ks->ks_size & j) == 0) + continue; + if (first) + printf(" %d", j); + else + printf(",%d", j); + first = 0; + } + printf("\n"); + totuse += ks->ks_memuse; + totreq += ks->ks_calls; + } + (void)printf("\nMemory Totals: In Use Free Requests\n"); + (void)printf(" %7ldK %6ldK %8ld\n", + (totuse + 1023) / 1024, (totfree + 1023) / 1024, totreq); +} + +/* + * kread reads something from the kernel, given its nlist index. + */ +void +kread(nlx, addr, size) + int nlx; + void *addr; + size_t size; +{ + char *sym; + + if (namelist[nlx].n_type == 0 || namelist[nlx].n_value == 0) { + sym = namelist[nlx].n_name; + if (*sym == '_') + ++sym; + (void)fprintf(stderr, + "vmstat: symbol %s not defined\n", sym); + exit(1); + } + if (kvm_read(kd, namelist[nlx].n_value, addr, size) != size) { + sym = namelist[nlx].n_name; + if (*sym == '_') + ++sym; + (void)fprintf(stderr, "vmstat: %s: %s\n", sym, kvm_geterr(kd)); + exit(1); + } +} + +void +usage() +{ + (void)fprintf(stderr, +#ifndef NEWVM + "usage: vmstat [-fimst] [-c count] [-M core] \ +[-N system] [-w wait] [disks]\n"); +#else + "usage: vmstat [-ims] [-c count] [-M core] \ +[-N system] [-w wait] [disks]\n"); +#endif + exit(1); +} diff --git a/usr.bin/w/w.c b/usr.bin/w/w.c new file mode 100644 index 00000000000..501938a9c58 --- /dev/null +++ b/usr.bin/w/w.c @@ -0,0 +1,428 @@ +/*- + * Copyright (c) 1980, 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1980, 1991, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)w.c 8.6 (Berkeley) 6/30/94"; +#endif /* not lint */ + +/* + * w - print system status (who and what) + * + * This program is similar to the systat command on Tenex/Tops 10/20 + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "extern.h" + +struct timeval boottime; +struct utmp utmp; +struct winsize ws; +kvm_t *kd; +time_t now; /* the current time of day */ +time_t uptime; /* time of last reboot & elapsed time since */ +int ttywidth; /* width of tty */ +int argwidth; /* width of tty */ +int header = 1; /* true if -h flag: don't print heading */ +int nflag; /* true if -n flag: don't convert addrs */ +int sortidle; /* sort bu idle time */ +char *sel_user; /* login of particular user selected */ +char domain[MAXHOSTNAMELEN]; + +/* + * One of these per active utmp entry. + */ +struct entry { + struct entry *next; + struct utmp utmp; + dev_t tdev; /* dev_t of terminal */ + time_t idle; /* idle time of terminal in seconds */ + struct kinfo_proc *kp; /* `most interesting' proc */ + char *args; /* arg list of interesting process */ +} *ep, *ehead = NULL, **nextp = &ehead; + +static void pr_header __P((time_t *, int)); +static struct stat + *ttystat __P((char *)); +static void usage __P((int)); + +char *fmt_argv __P((char **, char *, int)); /* ../../bin/ps/fmt.c */ + +int +main(argc, argv) + int argc; + char **argv; +{ + extern char *__progname; + struct kinfo_proc *kp; + struct hostent *hp; + struct stat *stp; + FILE *ut; + u_long l; + size_t arglen; + int ch, i, nentries, nusers, wcmd; + char *memf, *nlistf, *p, *vis_args, *x; + char buf[MAXHOSTNAMELEN], errbuf[256]; + + /* Are we w(1) or uptime(1)? */ + p = __progname; + if (*p == '-') + p++; + if (*p == 'u') { + wcmd = 0; + p = ""; + } else { + wcmd = 1; + p = "hiflM:N:nsuw"; + } + + memf = nlistf = NULL; + while ((ch = getopt(argc, argv, p)) != EOF) + switch (ch) { + case 'h': + header = 0; + break; + case 'i': + sortidle = 1; + break; + case 'M': + header = 0; + memf = optarg; + break; + case 'N': + nlistf = optarg; + break; + case 'n': + nflag = 1; + break; + case 'f': case 'l': case 's': case 'u': case 'w': + warnx("[-flsuw] no longer supported"); + /* FALLTHROUGH */ + case '?': + default: + usage(wcmd); + } + argc -= optind; + argv += optind; + + if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf)) == NULL) + errx(1, "%s", errbuf); + + (void)time(&now); + if ((ut = fopen(_PATH_UTMP, "r")) == NULL) + err(1, "%s", _PATH_UTMP); + + if (*argv) + sel_user = *argv; + + for (nusers = 0; fread(&utmp, sizeof(utmp), 1, ut);) { + if (utmp.ut_name[0] == '\0') + continue; + ++nusers; + if (wcmd == 0 || (sel_user && + strncmp(utmp.ut_name, sel_user, UT_NAMESIZE) != 0)) + continue; + if ((ep = calloc(1, sizeof(struct entry))) == NULL) + err(1, NULL); + *nextp = ep; + nextp = &(ep->next); + memmove(&(ep->utmp), &utmp, sizeof(struct utmp)); + if (!(stp = ttystat(ep->utmp.ut_line))) + continue; + ep->tdev = stp->st_rdev; +#ifdef CPU_CONSDEV + /* + * If this is the console device, attempt to ascertain + * the true console device dev_t. + */ + if (ep->tdev == 0) { + int mib[2]; + size_t size; + + mib[0] = CTL_MACHDEP; + mib[1] = CPU_CONSDEV; + size = sizeof(dev_t); + (void) sysctl(mib, 2, &ep->tdev, &size, NULL, 0); + } +#endif + if ((ep->idle = now - stp->st_atime) < 0) + ep->idle = 0; + } + (void)fclose(ut); + + if (header || wcmd == 0) { + pr_header(&now, nusers); + if (wcmd == 0) + exit (0); + } + +#define HEADER "USER TTY FROM LOGIN@ IDLE WHAT\n" +#define WUSED (sizeof (HEADER) - sizeof ("WHAT\n")) + (void)printf(HEADER); + + if ((kp = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nentries)) == NULL) + err(1, "%s", kvm_geterr(kd)); + for (i = 0; i < nentries; i++, kp++) { + struct proc *p = &kp->kp_proc; + struct eproc *e; + + if (p->p_stat == SIDL || p->p_stat == SZOMB) + continue; + e = &kp->kp_eproc; + for (ep = ehead; ep != NULL; ep = ep->next) { + if (ep->tdev == e->e_tdev && e->e_pgid == e->e_tpgid) { + /* + * Proc is in foreground of this terminal + */ + if (proc_compare(&ep->kp->kp_proc, p)) + ep->kp = kp; + break; + } + } + } + if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 && + ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) == -1 && + ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) || ws.ws_col == 0) + ttywidth = 79; + else + ttywidth = ws.ws_col - 1; + argwidth = ttywidth - WUSED; + if (argwidth < 4) + argwidth = 8; + for (ep = ehead; ep != NULL; ep = ep->next) { + if (ep->kp == NULL) { + ep->args = "-"; + continue; + } + ep->args = fmt_argv(kvm_getargv(kd, ep->kp, argwidth), + ep->kp->kp_proc.p_comm, MAXCOMLEN); + if (ep->args == NULL) + err(1, NULL); + } + /* sort by idle time */ + if (sortidle && ehead != NULL) { + struct entry *from = ehead, *save; + + ehead = NULL; + while (from != NULL) { + for (nextp = &ehead; + (*nextp) && from->idle >= (*nextp)->idle; + nextp = &(*nextp)->next) + continue; + save = from; + from = from->next; + save->next = *nextp; + *nextp = save; + } + } + + if (!nflag) + if (gethostname(domain, sizeof(domain) - 1) < 0 || + (p = strchr(domain, '.')) == 0) + domain[0] = '\0'; + else { + domain[sizeof(domain) - 1] = '\0'; + memmove(domain, p, strlen(p) + 1); + } + + if ((vis_args = malloc(argwidth * 4 + 1)) == NULL) + err(1, NULL); + for (ep = ehead; ep != NULL; ep = ep->next) { + p = *ep->utmp.ut_host ? ep->utmp.ut_host : "-"; + if ((x = strchr(p, ':')) != NULL) + *x++ = '\0'; + if (!nflag && isdigit(*p) && + (long)(l = inet_addr(p)) != -1 && + (hp = gethostbyaddr((char *)&l, sizeof(l), AF_INET))) { + if (domain[0] != '\0') { + p = hp->h_name; + p += strlen(hp->h_name); + p -= strlen(domain); + if (p > hp->h_name && strcmp(p, domain) == 0) + *p = '\0'; + } + p = hp->h_name; + } + if (x) { + (void)snprintf(buf, sizeof(buf), "%s:%.*s", p, + ep->utmp.ut_host + UT_HOSTSIZE - x, x); + p = buf; + } + (void)printf("%-*.*s %-2.2s %-*.*s ", + UT_NAMESIZE, UT_NAMESIZE, ep->utmp.ut_name, + strncmp(ep->utmp.ut_line, "tty", 3) ? + ep->utmp.ut_line : ep->utmp.ut_line + 3, + UT_HOSTSIZE, UT_HOSTSIZE, *p ? p : "-"); + pr_attime(&ep->utmp.ut_time, &now); + pr_idle(ep->idle); + if (ep->args != NULL) { + arglen = strlen(ep->args); + strvisx(vis_args, ep->args, + arglen > argwidth ? argwidth : arglen, + VIS_TAB | VIS_NL | VIS_NOSLASH); + } + (void)printf("%.*s\n", argwidth, ep->args); + } + exit(0); +} + +static void +pr_header(nowp, nusers) + time_t *nowp; + int nusers; +{ + double avenrun[3]; + time_t uptime; + int days, hrs, i, mins; + int mib[2]; + size_t size; + char buf[256]; + + /* + * Print time of day. + * + * SCCS forces the string manipulation below, as it replaces + * %, M, and % in a character string with the file name. + */ + (void)strftime(buf, sizeof(buf), + __CONCAT("%l:%","M%p"), localtime(nowp)); + (void)printf("%s ", buf); + + /* + * Print how long system has been up. + * (Found by looking getting "boottime" from the kernel) + */ + mib[0] = CTL_KERN; + mib[1] = KERN_BOOTTIME; + size = sizeof(boottime); + if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 && + boottime.tv_sec != 0) { + uptime = now - boottime.tv_sec; + uptime += 30; + days = uptime / SECSPERDAY; + uptime %= SECSPERDAY; + hrs = uptime / SECSPERHOUR; + uptime %= SECSPERHOUR; + mins = uptime / SECSPERMIN; + (void)printf(" up"); + if (days > 0) + (void)printf(" %d day%s,", days, days > 1 ? "s" : ""); + if (hrs > 0 && mins > 0) + (void)printf(" %2d:%02d,", hrs, mins); + else { + if (hrs > 0) + (void)printf(" %d hr%s,", + hrs, hrs > 1 ? "s" : ""); + if (mins > 0) + (void)printf(" %d min%s,", + mins, mins > 1 ? "s" : ""); + } + } + + /* Print number of users logged in to system */ + (void)printf(" %d user%s", nusers, nusers > 1 ? "s" : ""); + + /* + * Print 1, 5, and 15 minute load averages. + */ + if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) == -1) + (void)printf(", no load average information available\n"); + else { + (void)printf(", load averages:"); + for (i = 0; i < (sizeof(avenrun) / sizeof(avenrun[0])); i++) { + if (i > 0) + (void)printf(","); + (void)printf(" %.2f", avenrun[i]); + } + (void)printf("\n"); + } +} + +static struct stat * +ttystat(line) + char *line; +{ + static struct stat sb; + char ttybuf[MAXPATHLEN]; + + (void)snprintf(ttybuf, sizeof(ttybuf), "%s/%s", _PATH_DEV, line); + if (stat(ttybuf, &sb)) + return (NULL); + return (&sb); +} + +static void +usage(wcmd) + int wcmd; +{ + if (wcmd) + (void)fprintf(stderr, + "usage: w: [-hin] [-M core] [-N system] [user]\n"); + else + (void)fprintf(stderr, "uptime\n"); + exit (1); +} diff --git a/usr.bin/wc/wc.c b/usr.bin/wc/wc.c new file mode 100644 index 00000000000..d37f494f26c --- /dev/null +++ b/usr.bin/wc/wc.c @@ -0,0 +1,243 @@ +/* + * Copyright (c) 1980, 1987, 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, 1987, 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)wc.c 8.2 (Berkeley) 5/2/95"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +u_long tlinect, twordct, tcharct; +int doline, doword, dochar; + +void cnt __P((char *)); +void err __P((const char *, ...)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + register int ch; + int total; + + while ((ch = getopt(argc, argv, "lwc")) != EOF) + switch((char)ch) { + case 'l': + doline = 1; + break; + case 'w': + doword = 1; + break; + case 'c': + dochar = 1; + break; + case '?': + default: + usage(); + } + argv += optind; + argc -= optind; + + /* Wc's flags are on by default. */ + if (doline + doword + dochar == 0) + doline = doword = dochar = 1; + + total = 0; + if (!*argv) { + cnt(NULL); + (void)printf("\n"); + } + else do { + cnt(*argv); + (void)printf(" %s\n", *argv); + ++total; + } while(*++argv); + + if (total > 1) { + if (doline) + (void)printf(" %7ld", tlinect); + if (doword) + (void)printf(" %7ld", twordct); + if (dochar) + (void)printf(" %7ld", tcharct); + (void)printf(" total\n"); + } + exit(0); +} + +void +cnt(file) + char *file; +{ + register u_char *p; + register short gotsp; + register int ch, len; + register u_long linect, wordct, charct; + struct stat sb; + int fd; + u_char buf[MAXBSIZE]; + + fd = STDIN_FILENO; + linect = wordct = charct = 0; + if (file) { + if ((fd = open(file, O_RDONLY, 0)) < 0) + err("%s: %s", file, strerror(errno)); + if (doword) + goto word; + /* + * Line counting is split out because it's a lot faster to get + * lines than to get words, since the word count requires some + * logic. + */ + if (doline) { + while (len = read(fd, buf, MAXBSIZE)) { + if (len == -1) + err("%s: %s", file, strerror(errno)); + charct += len; + for (p = buf; len--; ++p) + if (*p == '\n') + ++linect; + } + tlinect += linect; + (void)printf(" %7lu", linect); + if (dochar) { + tcharct += charct; + (void)printf(" %7lu", charct); + } + (void)close(fd); + return; + } + /* + * If all we need is the number of characters and it's a + * regular or linked file, just stat the puppy. + */ + if (dochar) { + if (fstat(fd, &sb)) + err("%s: %s", file, strerror(errno)); + if (S_ISREG(sb.st_mode)) { + (void)printf(" %7qu", sb.st_size); + tcharct += sb.st_size; + (void)close(fd); + return; + } + } + } + + /* Do it the hard way... */ +word: for (gotsp = 1; len = read(fd, buf, MAXBSIZE);) { + if (len == -1) + err("%s: %s", file, strerror(errno)); + /* + * This loses in the presence of multi-byte characters. + * To do it right would require a function to return a + * character while knowing how many bytes it consumed. + */ + charct += len; + for (p = buf; len--;) { + ch = *p++; + if (ch == '\n') + ++linect; + if (isspace(ch)) + gotsp = 1; + else if (gotsp) { + gotsp = 0; + ++wordct; + } + } + } + if (doline) { + tlinect += linect; + (void)printf(" %7lu", linect); + } + if (doword) { + twordct += wordct; + (void)printf(" %7lu", wordct); + } + if (dochar) { + tcharct += charct; + (void)printf(" %7lu", charct); + } + (void)close(fd); +} + +void +usage() +{ + (void)fprintf(stderr, "usage: wc [-clw] [files]\n"); + exit(1); +} + +#if __STDC__ +#include +#else +#include +#endif + +void +#if __STDC__ +err(const char *fmt, ...) +#else +err(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)fprintf(stderr, "wc: "); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); + exit(1); + /* NOTREACHED */ +} diff --git a/usr.bin/whatis/Makefile b/usr.bin/whatis/Makefile new file mode 100644 index 00000000000..8655a7c9378 --- /dev/null +++ b/usr.bin/whatis/Makefile @@ -0,0 +1,7 @@ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 + +PROG= whatis +SRCS= whatis.c config.c +.PATH: ${.CURDIR}/../man + +.include diff --git a/usr.bin/whatis/whatis.1 b/usr.bin/whatis/whatis.1 new file mode 100644 index 00000000000..0f1b0a1b04d --- /dev/null +++ b/usr.bin/whatis/whatis.1 @@ -0,0 +1,105 @@ +.\" Copyright (c) 1989, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)whatis.1 8.1 (Berkeley) 6/6/93 +.\" +.Dd June 6, 1993 +.Dt WHATIS 1 +.Os BSD 4 +.Sh NAME +.Nm whatis +.Nd describe what a command is +.Sh SYNOPSIS +.Nm whatis +.Op Fl M Ar path +.Op Fl m Ar path +.Ar command Ar ... +.Sh DESCRIPTION +.Nm Whatis +looks up a given command and gives the header line from the manual page. +You can then use the +.Xr man 1 +command to get more information. +.Pp +The options are as follows: +.Bl -tag -width Fl +.It Fl M Ar path +Override the list of standard directories +.Nm whatis +searches for its database named +.Dq Pa whatis.db . +The supplied +.Ar path +must be a colon +.Dq \&: +separated list of directories. +This search path may also be set using the environment variable +.Ev MANPATH . +.It Fl m Ar path +Augment the list of standard directories +.Nm whatis +searches for its database named +.Dq Pa whatis.db . +The supplied +.Ar path +must be a colon +.Dq \&: +separated list of directories. +These directories will be searched before the standard directories +or the directories supplied with the +.Fl M +option or the +.Ev MANPATH +environment variable are searched. +.El +.Sh ENVIRONMENT +.Bl -tag -width MANPATH +.It Ev MANPATH +The standard search path used by +.Xr man 1 +may be overridden by specifying a path in the +.Ev MANPATH +environment variable. +.El +.Sh FILES +.Bl -tag -width whatis.db +.It Pa whatis.db +name of the whatis database +.El +.Sh SEE ALSO +.Xr apropos 1 , +.Xr man 1 , +.Xr whereis 1 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 3.0 . diff --git a/usr.bin/whatis/whatis.c b/usr.bin/whatis/whatis.c new file mode 100644 index 00000000000..904f0e47981 --- /dev/null +++ b/usr.bin/whatis/whatis.c @@ -0,0 +1,218 @@ +/* + * Copyright (c) 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1987, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)whatis.c 8.5 (Berkeley) 1/2/94"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "../man/config.h" +#include "../man/pathnames.h" + +#define MAXLINELEN 256 /* max line handled */ + +static int *found, foundman; + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; + extern int optind; + ENTRY *ep; + TAG *tp; + int ch, rv; + char *beg, *conffile, **p, *p_augment, *p_path; + + conffile = NULL; + p_augment = p_path = NULL; + while ((ch = getopt(argc, argv, "C:M:m:P:")) != EOF) + switch (ch) { + case 'C': + conffile = optarg; + break; + case 'M': + case 'P': /* backward compatible */ + p_path = optarg; + break; + case 'm': + p_augment = optarg; + break; + case '?': + default: + usage(); + } + argv += optind; + argc -= optind; + + if (argc < 1) + usage(); + + if ((found = malloc((u_int)argc * sizeof(int))) == NULL) + err(1, NULL); + memset(found, 0, argc * sizeof(int)); + + for (p = argv; *p; ++p) /* trim full paths */ + if (beg = rindex(*p, '/')) + *p = beg + 1; + + if (p_augment) + whatis(argv, p_augment, 1); + if (p_path || (p_path = getenv("MANPATH"))) + whatis(argv, p_path, 1); + else { + config(conffile); + ep = (tp = getlist("_whatdb")) == NULL ? + NULL : tp->list.tqh_first; + for (; ep != NULL; ep = ep->q.tqe_next) + whatis(argv, ep->s, 0); + } + + if (!foundman) { + fprintf(stderr, "whatis: no %s file found.\n", _PATH_WHATIS); + exit(1); + } + rv = 1; + for (p = argv; *p; ++p) + if (found[p - argv]) + rv = 0; + else + printf("%s: not found\n", *p); + exit(rv); +} + +whatis(argv, path, buildpath) + char **argv, *path; + int buildpath; +{ + register char *end, *name, **p; + char buf[MAXLINELEN + 1], wbuf[MAXLINELEN + 1]; + + for (name = path; name; name = end) { /* through name list */ + if (end = index(name, ':')) + *end++ = '\0'; + + if (buildpath) { + char hold[MAXPATHLEN + 1]; + + (void)sprintf(hold, "%s/%s", name, _PATH_WHATIS); + name = hold; + } + + if (!freopen(name, "r", stdin)) + continue; + + foundman = 1; + + /* for each file found */ + while (fgets(buf, sizeof(buf), stdin)) { + dashtrunc(buf, wbuf); + for (p = argv; *p; ++p) + if (match(wbuf, *p)) { + printf("%s", buf); + found[p - argv] = 1; + + /* only print line once */ + while (*++p) + if (match(wbuf, *p)) + found[p - argv] = 1; + break; + } + } + } +} + +/* + * match -- + * match a full word + */ +match(bp, str) + register char *bp, *str; +{ + register int len; + register char *start; + + if (!*str || !*bp) + return(0); + for (len = strlen(str);;) { + for (; *bp && !isdigit(*bp) && !isalpha(*bp); ++bp); + if (!*bp) + break; + for (start = bp++; + *bp && (*bp == '_' || isdigit(*bp) || isalpha(*bp)); ++bp); + if (bp - start == len && !strncasecmp(start, str, len)) + return(1); + } + return(0); +} + +/* + * dashtrunc -- + * truncate a string at " - " + */ +dashtrunc(from, to) + register char *from, *to; +{ + register int ch; + + for (; (ch = *from) && ch != '\n' && + (ch != ' ' || from[1] != '-' || from[2] != ' '); ++from) + *to++ = ch; + *to = '\0'; +} + +/* + * usage -- + * print usage message and die + */ +usage() +{ + (void)fprintf(stderr, + "usage: whatis [-C file] [-M path] [-m path] command ...\n"); + exit(1); +} diff --git a/usr.bin/whereis/whereis.1 b/usr.bin/whereis/whereis.1 new file mode 100644 index 00000000000..ee90db0ec69 --- /dev/null +++ b/usr.bin/whereis/whereis.1 @@ -0,0 +1,63 @@ +.\" 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. +.\" +.\" @(#)whereis.1 8.3 (Berkeley) 4/27/95 +.\" +.Dd April 27, 1995 +.Dt WHEREIS 1 +.Os BSD 3 +.Sh NAME +.Nm whereis +.Nd locate programs +.Sh SYNOPSIS +.Nm whereis +.Op Ar program ... +.Sh DESCRIPTION +The +.Nm whereis +utility checks the standard binary directories for the specified programs, +printing out the paths of any it finds. +.Pp +The path searched is the string returned by the +.Xr sysctl 8 +utility for the +.Dq user.cs_path +string. +.Sh SEE ALSO +.Xr sysctl 8 +.Sh COMPATIBILITY +The historic flags and arguments for the +.Nm whereis +utility are no longer available in this version. +.Sh HISTORY +The +.Nm whereis +command appeared in 3.0BSD. diff --git a/usr.bin/whereis/whereis.c b/usr.bin/whereis/whereis.c new file mode 100644 index 00000000000..4e39cda9387 --- /dev/null +++ b/usr.bin/whereis/whereis.c @@ -0,0 +1,120 @@ +/*- + * 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 copyright[] = +"@(#) Copyright (c) 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)whereis.c 8.3 (Berkeley) 5/4/95"; +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct stat sb; + size_t len; + int ch, sverrno, mib[2]; + char *p, *t, *std, path[MAXPATHLEN]; + + while ((ch = getopt(argc, argv, "")) != EOF) + switch (ch) { + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc == 0) + usage(); + + /* Retrieve the standard path. */ + mib[0] = CTL_USER; + mib[1] = USER_CS_PATH; + if (sysctl(mib, 2, NULL, &len, NULL, 0) == -1) + return (-1); + if (len == 0) + err(1, "user_cs_path: sysctl: zero length\n"); + if ((std = malloc(len)) == NULL) + err(1, NULL); + if (sysctl(mib, 2, std, &len, NULL, 0) == -1) { + sverrno = errno; + free(std); + errno = sverrno; + err(1, "sysctl: user_cs_path"); + } + + /* For each path, for each program... */ + for (; *argv; ++argv) + for (p = std;; *p++ = ':') { + t = p; + if ((p = strchr(p, ':')) != NULL) { + *p = '\0'; + if (t == p) + t = "."; + } else + if (strlen(t) == 0) + t = "."; + (void)snprintf(path, sizeof(path), "%s/%s", t, *argv); + if (!stat(path, &sb)) + (void)printf("%s\n", path); + if (p == NULL) + break; + } +} + +void +usage() +{ + + (void)fprintf(stderr, "usage: whereis program [...]\n"); + exit (1); +} diff --git a/usr.bin/whois/whois.1 b/usr.bin/whois/whois.1 new file mode 100644 index 00000000000..628a2a75ba1 --- /dev/null +++ b/usr.bin/whois/whois.1 @@ -0,0 +1,80 @@ +.\" Copyright (c) 1985, 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. +.\" +.\" @(#)whois.1 8.2 (Berkeley) 6/20/94 +.\" +.Dd June 20, 1994 +.Dt WHOIS 1 +.Os BSD 4.3 +.Sh NAME +.Nm whois +.Nd Internet user name directory service +.Sh SYNOPSIS +.Nm whois +.Op Fl h Ar hostname +.Ar name ... +.Sh DESCRIPTION +.Nm Whois +looks up records in the Network Information Center +.Pq Tn NIC +database. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl h +Use the specified host instead of the default NIC (whois.internic.net). +.El +.Pp +The operands specified to +.Nm whois +are concatenated together (separated by white-space) and presented to +the +.Nm whois +server. +.Pp +The default action, unless directed otherwise with a special +.Ar name , +is to do a very broad search, looking for matches to +.Ar name +in all types of records and most fields (name, nicknames, hostname, net +address, etc.) in the database. +For more information as to what +.Ar name +operands have special meaning, and how to guide the search, use +the special name +.Dq Ar help . +.Sh SEE ALSO +RFC 812: Nicname/Whois +.Sh HISTORY +The +.Nm whois +command appeared in +.Bx 4.3 . diff --git a/usr.bin/window/wwinit.c b/usr.bin/window/wwinit.c new file mode 100644 index 00000000000..1bf26f48997 --- /dev/null +++ b/usr.bin/window/wwinit.c @@ -0,0 +1,388 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Edward Wang at The University of California, 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. + */ + +#ifndef lint +static char sccsid[] = "@(#)wwinit.c 8.2 (Berkeley) 4/28/95"; +#endif /* not lint */ + +#include "ww.h" +#include "tt.h" +#include +#include +#include "char.h" + +wwinit() +{ + register i, j; + char *kp; + int s; + + wwdtablesize = 3; + wwhead.ww_forw = &wwhead; + wwhead.ww_back = &wwhead; + + s = sigblock(sigmask(SIGIO) | sigmask(SIGCHLD) | sigmask(SIGALRM) | + sigmask(SIGHUP) | sigmask(SIGTERM)); + if (signal(SIGIO, wwrint) == BADSIG || + signal(SIGCHLD, wwchild) == BADSIG || + signal(SIGHUP, wwquit) == BADSIG || + signal(SIGTERM, wwquit) == BADSIG || + signal(SIGPIPE, SIG_IGN) == BADSIG) { + wwerrno = WWE_SYS; + return -1; + } + + if (wwgettty(0, &wwoldtty) < 0) + return -1; + wwwintty = wwoldtty; +#ifdef OLD_TTY + wwwintty.ww_sgttyb.sg_flags &= ~XTABS; + wwnewtty.ww_sgttyb = wwoldtty.ww_sgttyb; + wwnewtty.ww_sgttyb.sg_erase = -1; + wwnewtty.ww_sgttyb.sg_kill = -1; + wwnewtty.ww_sgttyb.sg_flags |= CBREAK; + wwnewtty.ww_sgttyb.sg_flags &= ~(ECHO|CRMOD); + wwnewtty.ww_tchars.t_intrc = -1; + wwnewtty.ww_tchars.t_quitc = -1; + wwnewtty.ww_tchars.t_startc = -1; + wwnewtty.ww_tchars.t_stopc = -1; + wwnewtty.ww_tchars.t_eofc = -1; + wwnewtty.ww_tchars.t_brkc = -1; + wwnewtty.ww_ltchars.t_suspc = -1; + wwnewtty.ww_ltchars.t_dsuspc = -1; + wwnewtty.ww_ltchars.t_rprntc = -1; + wwnewtty.ww_ltchars.t_flushc = -1; + wwnewtty.ww_ltchars.t_werasc = -1; + wwnewtty.ww_ltchars.t_lnextc = -1; + wwnewtty.ww_lmode = wwoldtty.ww_lmode | LLITOUT; + wwnewtty.ww_ldisc = wwoldtty.ww_ldisc; +#else +#ifndef OXTABS +#define OXTABS XTABS +#endif +#ifndef _POSIX_VDISABLE +#define _POSIX_VDISABLE -1 +#endif + wwwintty.ww_termios.c_oflag &= ~OXTABS; + wwnewtty.ww_termios = wwoldtty.ww_termios; + wwnewtty.ww_termios.c_iflag &= + ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF | IMAXBEL); + wwnewtty.ww_termios.c_oflag = 0; + wwnewtty.ww_termios.c_cflag &= ~(CSIZE | PARENB); + wwnewtty.ww_termios.c_cflag |= CS8; + wwnewtty.ww_termios.c_lflag = 0; + for (i = 0; i < NCCS; i++) + wwnewtty.ww_termios.c_cc[i] = _POSIX_VDISABLE; + wwnewtty.ww_termios.c_cc[VMIN] = 0; + wwnewtty.ww_termios.c_cc[VTIME] = 0; +#endif + wwnewtty.ww_fflags = wwoldtty.ww_fflags | FASYNC; + if (wwsettty(0, &wwnewtty) < 0) + goto bad; + + if ((wwterm = getenv("TERM")) == 0) { + wwerrno = WWE_BADTERM; + goto bad; + } + if (tgetent(wwtermcap, wwterm) != 1) { + wwerrno = WWE_BADTERM; + goto bad; + } +#ifdef OLD_TTY + wwospeed = wwoldtty.ww_sgttyb.sg_ospeed; +#else + wwospeed = cfgetospeed(&wwoldtty.ww_termios); +#endif + switch (wwospeed) { + default: + case B0: + wwbaud = 0; + break; + case B50: + wwbaud = 50; + break; + case B75: + wwbaud = 75; + break; + case B110: + wwbaud = 110; + break; + case B134: + wwbaud = 134; + break; + case B150: + wwbaud = 150; + break; + case B200: + wwbaud = 200; + break; + case B300: + wwbaud = 300; + break; + case B600: + wwbaud = 600; + break; + case B1200: + wwbaud = 1200; + break; + case B1800: + wwbaud = 1800; + break; + case B2400: + wwbaud = 2400; + break; + case B4800: + wwbaud = 4800; + break; + case B9600: + wwbaud = 9600; + break; +#ifdef B19200 + case B19200: +#else + case EXTA: +#endif + wwbaud = 19200; + break; +#ifdef B38400 + case B38400: +#else + case EXTB: +#endif + wwbaud = 38400; + break; + } + + if (xxinit() < 0) + goto bad; + wwnrow = tt.tt_nrow; + wwncol = tt.tt_ncol; + wwavailmodes = tt.tt_availmodes; + wwwrap = tt.tt_wrap; + + if (wwavailmodes & WWM_REV) + wwcursormodes = WWM_REV | wwavailmodes & WWM_BLK; + else if (wwavailmodes & WWM_UL) + wwcursormodes = WWM_UL; + + if ((wwib = malloc((unsigned) 512)) == 0) + goto bad; + wwibe = wwib + 512; + wwibq = wwibp = wwib; + + if ((wwsmap = wwalloc(0, 0, wwnrow, wwncol, sizeof (char))) == 0) + goto bad; + for (i = 0; i < wwnrow; i++) + for (j = 0; j < wwncol; j++) + wwsmap[i][j] = WWX_NOBODY; + + wwos = (union ww_char **) + wwalloc(0, 0, wwnrow, wwncol, sizeof (union ww_char)); + if (wwos == 0) + goto bad; + /* wwos is cleared in wwstart1() */ + wwns = (union ww_char **) + wwalloc(0, 0, wwnrow, wwncol, sizeof (union ww_char)); + if (wwns == 0) + goto bad; + for (i = 0; i < wwnrow; i++) + for (j = 0; j < wwncol; j++) + wwns[i][j].c_w = ' '; + if (tt.tt_checkpoint) { + /* wwcs is also cleared in wwstart1() */ + wwcs = (union ww_char **) + wwalloc(0, 0, wwnrow, wwncol, sizeof (union ww_char)); + if (wwcs == 0) + goto bad; + } + + wwtouched = malloc((unsigned) wwnrow); + if (wwtouched == 0) { + wwerrno = WWE_NOMEM; + goto bad; + } + for (i = 0; i < wwnrow; i++) + wwtouched[i] = 0; + + wwupd = (struct ww_update *) malloc((unsigned) wwnrow * sizeof *wwupd); + if (wwupd == 0) { + wwerrno = WWE_NOMEM; + goto bad; + } + + wwindex[WWX_NOBODY] = &wwnobody; + wwnobody.ww_order = NWW; + + kp = wwwintermcap; + if (wwavailmodes & WWM_REV) + wwaddcap1(WWT_REV, &kp); + if (wwavailmodes & WWM_BLK) + wwaddcap1(WWT_BLK, &kp); + if (wwavailmodes & WWM_UL) + wwaddcap1(WWT_UL, &kp); + if (wwavailmodes & WWM_GRP) + wwaddcap1(WWT_GRP, &kp); + if (wwavailmodes & WWM_DIM) + wwaddcap1(WWT_DIM, &kp); + if (wwavailmodes & WWM_USR) + wwaddcap1(WWT_USR, &kp); + if (tt.tt_insline && tt.tt_delline || tt.tt_setscroll) + wwaddcap1(WWT_ALDL, &kp); + if (tt.tt_inschar) + wwaddcap1(WWT_IMEI, &kp); + if (tt.tt_insspace) + wwaddcap1(WWT_IC, &kp); + if (tt.tt_delchar) + wwaddcap1(WWT_DC, &kp); + wwaddcap("kb", &kp); + wwaddcap("ku", &kp); + wwaddcap("kd", &kp); + wwaddcap("kl", &kp); + wwaddcap("kr", &kp); + wwaddcap("kh", &kp); + if ((j = tgetnum("kn")) >= 0) { + char cap[32]; + + (void) sprintf(kp, "kn#%d:", j); + for (; *kp; kp++) + ; + for (i = 1; i <= j; i++) { + (void) sprintf(cap, "k%d", i); + wwaddcap(cap, &kp); + cap[0] = 'l'; + wwaddcap(cap, &kp); + } + } + /* + * It's ok to do this here even if setenv() is destructive + * since tt_init() has already made its own copy of it and + * wwterm now points to the copy. + */ + (void) setenv("TERM", WWT_TERM, 1); +#ifdef TERMINFO + if (wwterminfoinit() < 0) + goto bad; +#endif + + if (tt.tt_checkpoint) + if (signal(SIGALRM, wwalarm) == BADSIG) { + wwerrno = WWE_SYS; + goto bad; + } + /* catch typeahead before ASYNC was set */ + (void) kill(getpid(), SIGIO); + wwstart1(); + (void) sigsetmask(s); + return 0; +bad: + /* + * Don't bother to free storage. We're supposed + * to exit when wwinit fails anyway. + */ + (void) wwsettty(0, &wwoldtty); + (void) signal(SIGIO, SIG_DFL); + (void) sigsetmask(s); + return -1; +} + +wwaddcap(cap, kp) + register char *cap; + register char **kp; +{ + char tbuf[512]; + char *tp = tbuf; + register char *str, *p; + + if ((str = tgetstr(cap, &tp)) != 0) { + while (*(*kp)++ = *cap++) + ; + (*kp)[-1] = '='; + while (*str) { + for (p = unctrl(*str++); *(*kp)++ = *p++;) + ; + (*kp)--; + } + *(*kp)++ = ':'; + **kp = 0; + } +} + +wwaddcap1(cap, kp) + register char *cap; + register char **kp; +{ + while (*(*kp)++ = *cap++) + ; + (*kp)--; +} + +wwstart() +{ + register i; + + (void) wwsettty(0, &wwnewtty); + for (i = 0; i < wwnrow; i++) + wwtouched[i] = WWU_TOUCHED; + wwstart1(); +} + +wwstart1() +{ + register i, j; + + for (i = 0; i < wwnrow; i++) + for (j = 0; j < wwncol; j++) { + wwos[i][j].c_w = ' '; + if (tt.tt_checkpoint) + wwcs[i][j].c_w = ' '; + } + xxstart(); + if (tt.tt_checkpoint) + wwdocheckpoint = 1; +} + +/* + * Reset data structures and terminal from an unknown state. + * Restoring wwos has been taken care of elsewhere. + */ +wwreset() +{ + register i; + + xxreset(); + for (i = 0; i < wwnrow; i++) + wwtouched[i] = WWU_TOUCHED; +} diff --git a/usr.bin/window/wwopen.c b/usr.bin/window/wwopen.c new file mode 100644 index 00000000000..bddbb619c9c --- /dev/null +++ b/usr.bin/window/wwopen.c @@ -0,0 +1,190 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Edward Wang at The University of California, 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. + */ + +#ifndef lint +static char sccsid[] = "@(#)wwopen.c 8.2 (Berkeley) 4/28/95"; +#endif /* not lint */ + +#include "ww.h" +#include +#include +#include + +struct ww * +wwopen(flags, nrow, ncol, row, col, nline) +{ + register struct ww *w; + register i, j; + char m; + short nvis; + + w = (struct ww *)calloc(sizeof (struct ww), 1); + if (w == 0) { + wwerrno = WWE_NOMEM; + goto bad; + } + w->ww_pty = -1; + w->ww_socket = -1; + + for (i = 0; i < NWW && wwindex[i] != 0; i++) + ; + if (i >= NWW) { + wwerrno = WWE_TOOMANY; + goto bad; + } + w->ww_index = i; + + if (nline < nrow) + nline = nrow; + + w->ww_w.t = row; + w->ww_w.b = row + nrow; + w->ww_w.l = col; + w->ww_w.r = col + ncol; + w->ww_w.nr = nrow; + w->ww_w.nc = ncol; + + w->ww_b.t = row; + w->ww_b.b = row + nline; + w->ww_b.l = col; + w->ww_b.r = col + ncol; + w->ww_b.nr = nline; + w->ww_b.nc = ncol; + + w->ww_i.t = MAX(w->ww_w.t, 0); + w->ww_i.b = MIN(w->ww_w.b, wwnrow); + w->ww_i.l = MAX(w->ww_w.l, 0); + w->ww_i.r = MIN(w->ww_w.r, wwncol); + w->ww_i.nr = w->ww_i.b - w->ww_i.t; + w->ww_i.nc = w->ww_i.r - w->ww_i.l; + + w->ww_cur.r = w->ww_w.t; + w->ww_cur.c = w->ww_w.l; + + if (flags & WWO_PTY) { + if (wwgetpty(w) < 0) + goto bad; + w->ww_ispty = 1; + } else if (flags & WWO_SOCKET) { + int d[2]; + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, d) < 0) { + wwerrno = WWE_SYS; + goto bad; + } + (void) fcntl(d[0], F_SETFD, 1); + (void) fcntl(d[1], F_SETFD, 1); + w->ww_pty = d[0]; + w->ww_socket = d[1]; + } + if (flags & (WWO_PTY|WWO_SOCKET)) { + if ((w->ww_ob = malloc(512)) == 0) { + wwerrno = WWE_NOMEM; + goto bad; + } + w->ww_obe = w->ww_ob + 512; + w->ww_obp = w->ww_obq = w->ww_ob; + if (w->ww_pty >= wwdtablesize) + wwdtablesize = w->ww_pty + 1; + } + + w->ww_win = wwalloc(w->ww_w.t, w->ww_w.l, + w->ww_w.nr, w->ww_w.nc, sizeof (char)); + if (w->ww_win == 0) + goto bad; + m = 0; + if (flags & WWO_GLASS) + m |= WWM_GLS; + if (flags & WWO_REVERSE) + if (wwavailmodes & WWM_REV) + m |= WWM_REV; + else + flags &= ~WWO_REVERSE; + for (i = w->ww_w.t; i < w->ww_w.b; i++) + for (j = w->ww_w.l; j < w->ww_w.r; j++) + w->ww_win[i][j] = m; + + if (flags & WWO_FRAME) { + w->ww_fmap = wwalloc(w->ww_w.t, w->ww_w.l, + w->ww_w.nr, w->ww_w.nc, sizeof (char)); + if (w->ww_fmap == 0) + goto bad; + for (i = w->ww_w.t; i < w->ww_w.b; i++) + for (j = w->ww_w.l; j < w->ww_w.r; j++) + w->ww_fmap[i][j] = 0; + } + + w->ww_buf = (union ww_char **) + wwalloc(w->ww_b.t, w->ww_b.l, + w->ww_b.nr, w->ww_b.nc, sizeof (union ww_char)); + if (w->ww_buf == 0) + goto bad; + for (i = w->ww_b.t; i < w->ww_b.b; i++) + for (j = w->ww_b.l; j < w->ww_b.r; j++) + w->ww_buf[i][j].c_w = ' '; + + w->ww_nvis = (short *)malloc((unsigned) w->ww_w.nr * sizeof (short)); + if (w->ww_nvis == 0) { + wwerrno = WWE_NOMEM; + goto bad; + } + w->ww_nvis -= w->ww_w.t; + nvis = m ? 0 : w->ww_w.nc; + for (i = w->ww_w.t; i < w->ww_w.b; i++) + w->ww_nvis[i] = nvis; + + w->ww_state = WWS_INITIAL; + w->ww_oflags = flags; + return wwindex[w->ww_index] = w; +bad: + if (w != 0) { + if (w->ww_win != 0) + wwfree(w->ww_win, w->ww_w.t); + if (w->ww_fmap != 0) + wwfree(w->ww_fmap, w->ww_w.t); + if (w->ww_buf != 0) + wwfree((char **)w->ww_buf, w->ww_b.t); + if (w->ww_nvis != 0) + free((char *)(w->ww_nvis + w->ww_w.t)); + if (w->ww_ob != 0) + free(w->ww_ob); + if (w->ww_pty >= 0) + (void) close(w->ww_pty); + if (w->ww_socket >= 0) + (void) close(w->ww_socket); + free((char *)w); + } + return 0; +} diff --git a/usr.bin/write/write.c b/usr.bin/write/write.c new file mode 100644 index 00000000000..7ceedfba461 --- /dev/null +++ b/usr.bin/write/write.c @@ -0,0 +1,303 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jef Poskanzer and Craig Leres of the Lawrence Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE 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[] = "@(#)write.c 8.2 (Berkeley) 4/27/95"; +#endif /* not lint */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +extern int errno; + +main(argc, argv) + int argc; + char **argv; +{ + register char *cp; + time_t atime; + uid_t myuid; + int msgsok, myttyfd; + char tty[MAXPATHLEN], *mytty, *ttyname(); + void done(); + + /* check that sender has write enabled */ + if (isatty(fileno(stdin))) + myttyfd = fileno(stdin); + else if (isatty(fileno(stdout))) + myttyfd = fileno(stdout); + else if (isatty(fileno(stderr))) + myttyfd = fileno(stderr); + else + errx(1, "can't find your tty"); + if (!(mytty = ttyname(myttyfd))) + errx(1, "can't find your tty's name"); + if (cp = strrchr(mytty, '/')) + mytty = cp + 1; + if (term_chk(mytty, &msgsok, &atime, 1)) + exit(1); + if (!msgsok) + errx(1, "you have write permission turned off"); + + myuid = getuid(); + + /* check args */ + switch (argc) { + case 2: + search_utmp(argv[1], tty, mytty, myuid); + do_write(tty, mytty, myuid); + break; + case 3: + if (!strncmp(argv[2], "/dev/", 5)) + argv[2] += 5; + if (utmp_chk(argv[1], argv[2])) + errx(1, "%s is not logged in on %s", + argv[1], argv[2]); + if (term_chk(argv[2], &msgsok, &atime, 1)) + exit(1); + if (myuid && !msgsok) + errx(1, "%s has messages disabled on %s", + argv[1], argv[2]); + do_write(argv[2], mytty, myuid); + break; + default: + (void)fprintf(stderr, "usage: write user [tty]\n"); + exit(1); + } + done(); + /* NOTREACHED */ +} + +/* + * utmp_chk - checks that the given user is actually logged in on + * the given tty + */ +utmp_chk(user, tty) + char *user, *tty; +{ + struct utmp u; + int ufd; + + if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0) + return(0); /* ignore error, shouldn't happen anyway */ + + while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u)) + if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0 && + strncmp(tty, u.ut_line, sizeof(u.ut_line)) == 0) { + (void)close(ufd); + return(0); + } + + (void)close(ufd); + return(1); +} + +/* + * search_utmp - search utmp for the "best" terminal to write to + * + * Ignores terminals with messages disabled, and of the rest, returns + * the one with the most recent access time. Returns as value the number + * of the user's terminals with messages enabled, or -1 if the user is + * not logged in at all. + * + * Special case for writing to yourself - ignore the terminal you're + * writing from, unless that's the only terminal with messages enabled. + */ +search_utmp(user, tty, mytty, myuid) + char *user, *tty, *mytty; + uid_t myuid; +{ + struct utmp u; + time_t bestatime, atime; + int ufd, nloggedttys, nttys, msgsok, user_is_me; + char atty[UT_LINESIZE + 1]; + + if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0) + err(1, "%s", _PATH_UTMP); + + nloggedttys = nttys = 0; + bestatime = 0; + user_is_me = 0; + while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u)) + if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0) { + ++nloggedttys; + (void)strncpy(atty, u.ut_line, UT_LINESIZE); + atty[UT_LINESIZE] = '\0'; + if (term_chk(atty, &msgsok, &atime, 0)) + continue; /* bad term? skip */ + if (myuid && !msgsok) + continue; /* skip ttys with msgs off */ + if (strcmp(atty, mytty) == 0) { + user_is_me = 1; + continue; /* don't write to yourself */ + } + ++nttys; + if (atime > bestatime) { + bestatime = atime; + (void)strcpy(tty, atty); + } + } + + (void)close(ufd); + if (nloggedttys == 0) + errx(1, "%s is not logged in", user); + if (nttys == 0) { + if (user_is_me) { /* ok, so write to yourself! */ + (void)strcpy(tty, mytty); + return; + } + errx(1, "%s has messages disabled", user); + } else if (nttys > 1) + warnx("%s is logged in more than once; writing to %s", + user, tty); +} + +/* + * term_chk - check that a terminal exists, and get the message bit + * and the access time + */ +term_chk(tty, msgsokP, atimeP, showerror) + char *tty; + int *msgsokP, showerror; + time_t *atimeP; +{ + struct stat s; + char path[MAXPATHLEN]; + + (void)sprintf(path, "/dev/%s", tty); + if (stat(path, &s) < 0) { + if (showerror) + warn("%s", path); + return(1); + } + *msgsokP = (s.st_mode & (S_IWRITE >> 3)) != 0; /* group write bit */ + *atimeP = s.st_atime; + return(0); +} + +/* + * do_write - actually make the connection + */ +do_write(tty, mytty, myuid) + char *tty, *mytty; + uid_t myuid; +{ + register char *login, *nows; + register struct passwd *pwd; + time_t now, time(); + char *getlogin(), path[MAXPATHLEN], host[MAXHOSTNAMELEN], line[512]; + void done(); + + /* Determine our login name before the we reopen() stdout */ + if ((login = getlogin()) == NULL) + if (pwd = getpwuid(myuid)) + login = pwd->pw_name; + else + login = "???"; + + (void)sprintf(path, "/dev/%s", tty); + if ((freopen(path, "w", stdout)) == NULL) + err(1, "%s", path); + + (void)signal(SIGINT, done); + (void)signal(SIGHUP, done); + + /* print greeting */ + if (gethostname(host, sizeof(host)) < 0) + (void)strcpy(host, "???"); + now = time((time_t *)NULL); + nows = ctime(&now); + nows[16] = '\0'; + (void)printf("\r\n\007\007\007Message from %s@%s on %s at %s ...\r\n", + login, host, mytty, nows + 11); + + while (fgets(line, sizeof(line), stdin) != NULL) + wr_fputs(line); +} + +/* + * done - cleanup and exit + */ +void +done() +{ + (void)printf("EOF\r\n"); + exit(0); +} + +/* + * wr_fputs - like fputs(), but makes control characters visible and + * turns \n into \r\n + */ +wr_fputs(s) + register char *s; +{ + register char c; + +#define PUTC(c) if (putchar(c) == EOF) goto err; + + for (; *s != '\0'; ++s) { + c = toascii(*s); + if (c == '\n') { + PUTC('\r'); + PUTC('\n'); + } else if (!isprint(c) && !isspace(c) && c != '\007') { + PUTC('^'); + PUTC(c^0x40); /* DEL to ?, others to alpha */ + } else + PUTC(c); + } + return; + +err: err(1, NULL); +#undef PUTC +}