summaryrefslogtreecommitdiffstats
path: root/usr.bin
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin')
-rw-r--r--usr.bin/apropos/Makefile7
-rw-r--r--usr.bin/apropos/apropos.1120
-rw-r--r--usr.bin/apropos/apropos.c224
-rw-r--r--usr.bin/ar/ar.5.5146
-rw-r--r--usr.bin/ld/Makefile7
-rw-r--r--usr.bin/ld/cplus-dem.c970
-rw-r--r--usr.bin/ld/ld.c4718
-rw-r--r--usr.bin/ld/symseg.h358
-rw-r--r--usr.bin/m4/serv.c475
-rw-r--r--usr.bin/man/Makefile8
-rw-r--r--usr.bin/man/config.c176
-rw-r--r--usr.bin/man/config.h57
-rw-r--r--usr.bin/man/man.1188
-rw-r--r--usr.bin/man/man.c712
-rw-r--r--usr.bin/man/man.conf46
-rw-r--r--usr.bin/man/man.conf.5195
-rw-r--r--usr.bin/man/pathnames.h39
-rw-r--r--usr.bin/mklocale/Japanese158
-rw-r--r--usr.bin/passwd/kpasswd_proto.h54
-rw-r--r--usr.bin/passwd/krb_passwd.c319
-rw-r--r--usr.bin/patch/EXTERN.h15
-rw-r--r--usr.bin/patch/INTERN.h15
-rw-r--r--usr.bin/patch/Makefile6
-rw-r--r--usr.bin/patch/README79
-rw-r--r--usr.bin/patch/common.h138
-rw-r--r--usr.bin/patch/config.h16
-rw-r--r--usr.bin/patch/inp.c313
-rw-r--r--usr.bin/patch/inp.h18
-rw-r--r--usr.bin/patch/patch.1446
-rw-r--r--usr.bin/patch/patch.c800
-rw-r--r--usr.bin/patch/patchlevel.h1
-rw-r--r--usr.bin/patch/pch.c1108
-rw-r--r--usr.bin/patch/pch.h36
-rw-r--r--usr.bin/patch/util.c339
-rw-r--r--usr.bin/patch/util.h74
-rw-r--r--usr.bin/patch/version.c28
-rw-r--r--usr.bin/patch/version.h9
-rw-r--r--usr.bin/ranlib/ranlib.5.570
-rw-r--r--usr.bin/rlogin/des_rw.c203
-rw-r--r--usr.bin/sccs/Makefile5
-rw-r--r--usr.bin/sccs/PSD.doc/Makefile7
-rw-r--r--usr.bin/sccs/PSD.doc/sccs.me1609
-rw-r--r--usr.bin/sccs/PSD.doc/spell.ok77
-rw-r--r--usr.bin/sccs/pathnames.h51
-rw-r--r--usr.bin/sccs/sccs.1398
-rw-r--r--usr.bin/sccs/sccs.c1621
-rw-r--r--usr.bin/sort/sort.1310
-rw-r--r--usr.bin/tip/acu.c196
-rw-r--r--usr.bin/tip/aculib/biz22.c187
-rw-r--r--usr.bin/tip/aculib/biz31.c248
-rw-r--r--usr.bin/tip/aculib/courier.c380
-rw-r--r--usr.bin/tip/aculib/df.c132
-rw-r--r--usr.bin/tip/aculib/dn11.c142
-rw-r--r--usr.bin/tip/aculib/hayes.c305
-rw-r--r--usr.bin/tip/aculib/t3000.c408
-rw-r--r--usr.bin/tip/aculib/v3451.c214
-rw-r--r--usr.bin/tip/aculib/v831.c259
-rw-r--r--usr.bin/tip/aculib/ventel.c251
-rw-r--r--usr.bin/tip/acutab.c97
-rw-r--r--usr.bin/tip/cmds.c888
-rw-r--r--usr.bin/tip/cmdtab.c64
-rw-r--r--usr.bin/tip/cu.c132
-rw-r--r--usr.bin/tip/hunt.c93
-rw-r--r--usr.bin/tip/log.c86
-rw-r--r--usr.bin/tip/partab.c58
-rw-r--r--usr.bin/tip/remcap.c426
-rw-r--r--usr.bin/tip/remote.c226
-rw-r--r--usr.bin/tip/tip.1451
-rw-r--r--usr.bin/tip/value.c353
-rw-r--r--usr.bin/tip/vars.c112
-rw-r--r--usr.bin/uucp/acucntrl/acucntrl.c814
-rw-r--r--usr.bin/uucp/uupoll/uupoll.8111
-rw-r--r--usr.bin/uucp/uupoll/uupoll.c129
-rw-r--r--usr.bin/uucp/uuq/Makefile9
-rw-r--r--usr.bin/uucp/uuq/uuq.1126
-rw-r--r--usr.bin/uucp/uuq/uuq.c435
-rw-r--r--usr.bin/uucp/uusend/Makefile6
-rw-r--r--usr.bin/uucp/uusend/uusend.196
-rw-r--r--usr.bin/uucp/uusend/uusend.c403
-rw-r--r--usr.bin/uucp/uusnap/uusnap.880
-rw-r--r--usr.bin/uucp/uusnap/uusnap.c348
-rw-r--r--usr.bin/whatis/Makefile7
-rw-r--r--usr.bin/whatis/whatis.1105
-rw-r--r--usr.bin/whatis/whatis.c218
84 files changed, 25364 insertions, 0 deletions
diff --git a/usr.bin/apropos/Makefile b/usr.bin/apropos/Makefile
new file mode 100644
index 0000000..028a42c
--- /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 <bsd.prog.mk>
diff --git a/usr.bin/apropos/apropos.1 b/usr.bin/apropos/apropos.1
new file mode 100644
index 0000000..eb68f37
--- /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 0000000..112aada
--- /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 <sys/param.h>
+#include <sys/queue.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#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.5.5 b/usr.bin/ar/ar.5.5
new file mode 100644
index 0000000..2ab5b2d
--- /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 <ar.h>
+.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 "!<arch>\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/ld/Makefile b/usr.bin/ld/Makefile
new file mode 100644
index 0000000..834a5c9
--- /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 <bsd.prog.mk>
diff --git a/usr.bin/ld/cplus-dem.c b/usr.bin/ld/cplus-dem.c
new file mode 100644
index 0000000..b2e3050
--- /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 <stdio.h>
+#include <ctype.h>
+
+#ifdef USG
+#include <memory.h>
+#include <string.h>
+#else
+#include <strings.h>
+#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 0000000..f0b35f2
--- /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 <ar.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <fcntl.h>
+#include <a.out.h>
+#include <stab.h>
+#include <string.h>
+
+/* 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
+ * <whatever> 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 0000000..978d8be
--- /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/m4/serv.c b/usr.bin/m4/serv.c
new file mode 100644
index 0000000..54a2e59
--- /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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#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 0000000..728d038
--- /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 <bsd.prog.mk>
diff --git a/usr.bin/man/config.c b/usr.bin/man/config.c
new file mode 100644
index 0000000..42780fa
--- /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 <sys/types.h>
+#include <sys/queue.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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 0000000..52ea969
--- /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 0000000..081e204
--- /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 0000000..4f3b9e5
--- /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 <sys/param.h>
+#include <sys/queue.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fnmatch.h>
+#include <glob.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#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(&sectnewp->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(&sectnewp->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 0000000..9faad52
--- /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 0000000..353a302
--- /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.<any_digit>'' 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 <manual page> | 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 0000000..17284fe
--- /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 0000000..55eb155
--- /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/passwd/kpasswd_proto.h b/usr.bin/passwd/kpasswd_proto.h
new file mode 100644
index 0000000..465d4c7
--- /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 0000000..d4a0f15
--- /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 <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <netinet/in.h>
+#include <kerberosIV/des.h>
+#include <kerberosIV/krb.h>
+
+#include <err.h>
+#include <errno.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#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 0000000..0271074
--- /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 0000000..8bf16f5
--- /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 0000000..d6db9f7
--- /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 <bsd.prog.mk>
diff --git a/usr.bin/patch/README b/usr.bin/patch/README
new file mode 100644
index 0000000..017b1a0
--- /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 0000000..42d6883
--- /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 <stdio.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <signal.h>
+
+/* 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 0000000..9318fe7
--- /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 0000000..a3eeb90
--- /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 0000000..c6d2a91
--- /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 0000000..3e4a12e
--- /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
+<patchfile
+.SH DESCRIPTION
+.I Patch
+will take a patch file containing any of the three forms of difference
+listing produced by the
+.I diff
+program and apply those differences to an original file, producing a patched
+version.
+By default, the patched version is put in place of the original, with
+the original file backed up to the same name with the
+extension \*(L".orig\*(R", or as specified by the
+.B -b
+switch.
+You may also specify where you want the output to go with a
+.B -o
+switch.
+If
+.I patchfile
+is omitted, or is a hyphen, the patch will be read from standard input.
+.PP
+Upon startup, patch will attempt to determine the type of the diff listing,
+unless over-ruled by a
+.BR -c ,
+.BR -e ,
+or
+.B -n
+switch.
+Context diffs and normal diffs are applied by the
+.I patch
+program itself, while ed diffs are simply fed to the
+.I ed
+editor via a pipe.
+.PP
+.I Patch
+will try to skip any leading garbage, apply the diff,
+and then skip any trailing garbage.
+Thus you could feed an article or message containing a
+diff listing to
+.IR patch ,
+and it should work.
+If the entire diff is indented by a consistent amount,
+this will be taken into account.
+.PP
+With context diffs, and to a lesser extent with normal diffs,
+.I patch
+can detect when the line numbers mentioned in the patch are incorrect,
+and will attempt to find the correct place to apply each hunk of the patch.
+As a first guess, it takes the line number mentioned for the hunk, plus or
+minus any offset used in applying the previous hunk.
+If that is not the correct place,
+.I patch
+will scan both forwards and backwards for a set of lines matching the context
+given in the hunk.
+First
+.I patch
+looks for a place where all lines of the context match.
+If no such place is found, and it's a context diff, and the maximum fuzz factor
+is set to 1 or more, then another scan takes place ignoring the first and last
+line of context.
+If that fails, and the maximum fuzz factor is set to 2 or more,
+the first two and last two lines of context are ignored,
+and another scan is made.
+(The default maximum fuzz factor is 2.)
+If
+.I patch
+cannot find a place to install that hunk of the patch, it will put the
+hunk out to a reject file, which normally is the name of the output file
+plus \*(L".rej\*(R".
+(Note that the rejected hunk will come out in context diff form whether the
+input patch was a context diff or a normal diff.
+If the input was a normal diff, many of the contexts will simply be null.)
+The line numbers on the hunks in the reject file may be different than
+in the patch file: they reflect the approximate location patch thinks the
+failed hunks belong in the new file rather than the old one.
+.PP
+As each hunk is completed, you will be told whether the hunk succeeded or
+failed, and which line (in the new file)
+.I patch
+thought the hunk should go on.
+If this is different from the line number specified in the diff you will
+be told the offset.
+A single large offset MAY be an indication that a hunk was installed in the
+wrong place.
+You will also be told if a fuzz factor was used to make the match, in which
+case you should also be slightly suspicious.
+.PP
+If no original file is specified on the command line,
+.I patch
+will try to figure out from the leading garbage what the name of the file
+to edit is.
+In the header of a context diff, the filename is found from lines beginning
+with \*(L"***\*(R" or \*(L"---\*(R", with the shortest name of an existing
+file winning.
+Only context diffs have lines like that, but if there is an \*(L"Index:\*(R"
+line in the leading garbage,
+.I patch
+will try to use the filename from that line.
+The context diff header takes precedence over an Index line.
+If no filename can be intuited from the leading garbage, you will be asked
+for the name of the file to patch.
+.PP
+(If the original file cannot be found, but a suitable SCCS or RCS file is
+handy,
+.I patch
+will attempt to get or check out the file.)
+.PP
+Additionally, if the leading garbage contains a \*(L"Prereq: \*(R" line,
+.I patch
+will take the first word from the prerequisites line (normally a version
+number) and check the input file to see if that word can be found.
+If not,
+.I patch
+will ask for confirmation before proceeding.
+.PP
+The upshot of all this is that you should be able to say, while in a news
+interface, the following:
+.Sp
+ | patch -d /usr/src/local/blurfl
+.Sp
+and patch a file in the blurfl directory directly from the article containing
+the patch.
+.PP
+If the patch file contains more than one patch,
+.I patch
+will try to apply each of them as if they came from separate patch files.
+This means, among other things, that it is assumed that the name of the file
+to patch must be determined for each diff listing,
+and that the garbage before each diff listing will
+be examined for interesting things such as filenames and revision level, as
+mentioned previously.
+You can give switches (and another original file name) for the second and
+subsequent patches by separating the corresponding argument lists
+by a \*(L'+\*(R'.
+(The argument list for a second or subsequent patch may not specify a new
+patch file, however.)
+.PP
+.I Patch
+recognizes the following switches:
+.TP 5
+.B \-b
+causes the next argument to be interpreted as the backup extension, to be
+used in place of \*(L".orig\*(R".
+.TP 5
+.B \-c
+forces
+.I patch
+to interpret the patch file as a context diff.
+.TP 5
+.B \-d
+causes
+.I patch
+to interpret the next argument as a directory, and cd to it before doing
+anything else.
+.TP 5
+.B \-D
+causes
+.I patch
+to use the "#ifdef...#endif" construct to mark changes.
+The argument following will be used as the differentiating symbol.
+Note that, unlike the C compiler, there must be a space between the
+.B \-D
+and the argument.
+.TP 5
+.B \-e
+forces
+.I patch
+to interpret the patch file as an ed script.
+.TP 5
+.B \-f
+forces
+.I patch
+to assume that the user knows exactly what he or she is doing, and to not
+ask any questions.
+It does not suppress commentary, however.
+Use
+.B \-s
+for that.
+.TP 5
+.B \-F<number>
+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<number>
+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 + <patchfile
+.sp
+will ignore the first and second of three patches.
+.TP 5
+.B \-v
+causes
+.I patch
+to print out it's revision header and patch level.
+.TP 5
+.B \-x<number>
+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 0000000..0f91c5c
--- /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<MAXFILEC; i++)
+ filearg[i] = Nullch;
+ Mktemp(TMPOUTNAME);
+ Mktemp(TMPINNAME);
+ Mktemp(TMPREJNAME);
+ Mktemp(TMPPATNAME);
+
+ /* parse switches */
+ Argc = argc;
+ Argv = argv;
+ get_some_switches();
+
+ /* make sure we clean up /tmp in case of disaster */
+ set_signals();
+
+ for (
+ open_patch_file(filearg[1]);
+ there_is_another_patch();
+ reinitialize_almost_everything()
+ ) { /* for each patch in patch file */
+
+ if (outname == Nullch)
+ outname = savestr(filearg[0]);
+
+ /* initialize the patched file */
+ if (!skip_rest_of_patch)
+ init_output(TMPOUTNAME);
+
+ /* for ed script just up and do it and exit */
+ if (diff_type == ED_DIFF) {
+ do_ed_script();
+ continue;
+ }
+
+ /* initialize reject file */
+ init_reject(TMPREJNAME);
+
+ /* find out where all the lines are */
+ if (!skip_rest_of_patch)
+ scan_input(filearg[0]);
+
+ /* from here on, open no standard i/o files, because malloc */
+ /* might misfire and we can't catch it easily */
+
+ /* apply each hunk of patch */
+ hunk = 0;
+ failed = 0;
+ out_of_mem = FALSE;
+ while (another_hunk()) {
+ hunk++;
+ fuzz = Nulline;
+ mymaxfuzz = pch_context();
+ if (maxfuzz < mymaxfuzz)
+ mymaxfuzz = maxfuzz;
+ if (!skip_rest_of_patch) {
+ do {
+ where = locate_hunk(fuzz);
+ if (hunk == 1 && where == Nulline && !force) {
+ /* dwim for reversed patch? */
+ if (!pch_swap()) {
+ if (fuzz == Nulline)
+ say1("\
+Not enough memory to try swapped hunk! Assuming unswapped.\n");
+ continue;
+ }
+ reverse = !reverse;
+ where = locate_hunk(fuzz); /* try again */
+ if (where == Nulline) { /* didn't find it swapped */
+ if (!pch_swap()) /* put it back to normal */
+ fatal1("Lost hunk on alloc error!\n");
+ reverse = !reverse;
+ }
+ else if (noreverse) {
+ if (!pch_swap()) /* put it back to normal */
+ fatal1("Lost hunk on alloc error!\n");
+ reverse = !reverse;
+ say1("\
+Ignoring previously applied (or reversed) patch.\n");
+ skip_rest_of_patch = TRUE;
+ }
+ else {
+ ask3("\
+%seversed (or previously applied) patch detected! %s -R? [y] ",
+ reverse ? "R" : "Unr",
+ reverse ? "Assume" : "Ignore");
+ if (*buf == 'n') {
+ ask1("Apply anyway? [n] ");
+ if (*buf != 'y')
+ skip_rest_of_patch = TRUE;
+ where = Nulline;
+ reverse = !reverse;
+ if (!pch_swap()) /* put it back to normal */
+ fatal1("Lost hunk on alloc error!\n");
+ }
+ }
+ }
+ } while (!skip_rest_of_patch && where == Nulline &&
+ ++fuzz <= mymaxfuzz);
+
+ if (skip_rest_of_patch) { /* just got decided */
+ Fclose(ofp);
+ ofp = Nullfp;
+ }
+ }
+
+ newwhere = pch_newfirst() + last_offset;
+ if (skip_rest_of_patch) {
+ abort_hunk();
+ failed++;
+ if (verbose)
+ say3("Hunk #%d ignored at %ld.\n", hunk, newwhere);
+ }
+ else if (where == Nulline) {
+ abort_hunk();
+ failed++;
+ if (verbose)
+ say3("Hunk #%d failed at %ld.\n", hunk, newwhere);
+ }
+ else {
+ apply_hunk(where);
+ if (verbose) {
+ say3("Hunk #%d succeeded at %ld", hunk, newwhere);
+ if (fuzz)
+ say2(" with fuzz %ld", fuzz);
+ if (last_offset)
+ say3(" (offset %ld line%s)",
+ last_offset, last_offset==1L?"":"s");
+ say1(".\n");
+ }
+ }
+ }
+
+ if (out_of_mem && using_plan_a) {
+ Argc = Argc_last;
+ Argv = Argv_last;
+ say1("\n\nRan out of memory using Plan A--trying again...\n\n");
+ continue;
+ }
+
+ assert(hunk);
+
+ /* finish spewing out the new file */
+ if (!skip_rest_of_patch)
+ spew_output();
+
+ /* and put the output where desired */
+ ignore_signals();
+ if (!skip_rest_of_patch) {
+ if (move_file(TMPOUTNAME, outname) < 0) {
+ toutkeep = TRUE;
+ chmod(TMPOUTNAME, filemode);
+ }
+ else
+ chmod(outname, filemode);
+ }
+ Fclose(rejfp);
+ rejfp = Nullfp;
+ if (failed) {
+ if (!*rejname) {
+ Strcpy(rejname, outname);
+ Strcat(rejname, ".rej");
+ }
+ if (skip_rest_of_patch) {
+ say4("%d out of %d hunks ignored--saving rejects to %s\n",
+ failed, hunk, rejname);
+ }
+ else {
+ say4("%d out of %d hunks failed--saving rejects to %s\n",
+ failed, hunk, rejname);
+ }
+ if (move_file(TMPREJNAME, rejname) < 0)
+ trejkeep = TRUE;
+ }
+ set_signals();
+ }
+ my_exit(0);
+}
+
+/* Prepare to find the next patch to do in the patch file. */
+
+void
+reinitialize_almost_everything()
+{
+ re_patch();
+ re_input();
+
+ input_lines = 0;
+ last_frozen_line = 0;
+
+ filec = 0;
+ if (filearg[0] != Nullch && !out_of_mem) {
+ free(filearg[0]);
+ filearg[0] = Nullch;
+ }
+
+ if (outname != Nullch) {
+ free(outname);
+ outname = Nullch;
+ }
+
+ last_offset = 0;
+
+ diff_type = 0;
+
+ if (revision != Nullch) {
+ free(revision);
+ revision = Nullch;
+ }
+
+ reverse = FALSE;
+ skip_rest_of_patch = FALSE;
+
+ get_some_switches();
+
+ if (filec >= 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 0000000..618bca4
--- /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 0000000..8837212
--- /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 0000000..97a5b28
--- /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 0000000..5582d18
--- /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 0000000..9896c63
--- /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 0000000..17dfb81
--- /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 0000000..08fe68d
--- /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/ranlib/ranlib.5.5 b/usr.bin/ranlib/ranlib.5.5
new file mode 100644
index 0000000..e953c51
--- /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 <ranlib.h>
+.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 0000000..dbe47f0
--- /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 <sys/param.h>
+
+#include <kerberosIV/des.h>
+#include <kerberosIV/krb.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+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/sccs/Makefile b/usr.bin/sccs/Makefile
new file mode 100644
index 0000000..0ee9d1b
--- /dev/null
+++ b/usr.bin/sccs/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= sccs
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/sccs/PSD.doc/Makefile b/usr.bin/sccs/PSD.doc/Makefile
new file mode 100644
index 0000000..4e8ebba
--- /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 <bsd.doc.mk>
diff --git a/usr.bin/sccs/PSD.doc/sccs.me b/usr.bin/sccs/PSD.doc/sccs.me
new file mode 100644
index 0000000..16dc3fb
--- /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\&% <tab> %\&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 0000000..fb2fe24
--- /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 0000000..4da6874
--- /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 <paths.h>
+
+#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 0000000..7f4990c
--- /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 = <list of source files>
+.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 0000000..2dfd76d
--- /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 <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/dir.h>
+#include <signal.h>
+#include <sysexits.h>
+#include <errno.h>
+#include <pwd.h>
+#include <stdio.h>
+#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<dir> <dir> represents a directory to search
+** out of. It should be a full pathname
+** for general usage. E.g., if <dir> is
+** "/usr/src/sys", then a reference to the
+** file "dev/bio.c" becomes a reference to
+** "/usr/src/sys/dev/bio.c".
+** -p<path> prepends <path> 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<name> <name>"
+** 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/sort/sort.1 b/usr.bin/sort/sort.1
new file mode 100644
index 0000000..574efc3
--- /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/tip/acu.c b/usr.bin/tip/acu.c
new file mode 100644
index 0000000..f7bde99
--- /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 0000000..93c5e53
--- /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 0000000..412974d
--- /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 0000000..85f7b0d
--- /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 <stdio.h>
+
+#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 0000000..5f294f9
--- /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 0000000..152b376
--- /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(&lt)) != 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 0000000..a2196f4
--- /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 0000000..5e07359
--- /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 <stdio.h>
+
+#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 0000000..1623a58
--- /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 0000000..38aa230
--- /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 0000000..28b0d28
--- /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 <cr>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 0000000..112b43e
--- /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 0000000..63bfee2
--- /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: <take> 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: <put> 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 0000000..f6bcb60
--- /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 0000000..fae2b3c
--- /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 0000000..650ac26
--- /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 0000000..5da2c45
--- /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 0000000..1da4e23
--- /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 0000000..7a7e0c2
--- /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 <sys/types.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <stdlib.h>
+#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<tbuf) {
+ write(2, "Bad remcap entry\n", 18);
+ return (0);
+ }
+ p++;
+ /* p now points to beginning of last field */
+ if (p[0] != 't' || p[1] != 'c')
+ return (1);
+ strcpy(tcname, p+3);
+ q = tcname;
+ while (*q && *q != ':')
+ q++;
+ *q = 0;
+ if (++hopcount > 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 0000000..9b86066
--- /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 <stdio.h>
+#include <stdlib.h>
+
+#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 0000000..10b8a3e
--- /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 0000000..ce29a21
--- /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<<PUBLIC);
+}
+
+static int vaccess();
+
+/*VARARGS1*/
+vassign(p, v)
+ register value_t *p;
+ char *v;
+{
+
+ if (!vaccess(p->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<<PUBLIC))
+ return (1);
+ if (mode & (rw<<PRIVATE))
+ return (1);
+ return ((mode & (rw<<ROOT)) && getuid() == 0);
+}
+
+static value_t *
+vlookup(s)
+ register char *s;
+{
+ register value_t *p;
+
+ for (p = vtable; p->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 0000000..debe01b
--- /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)<<PUBLIC,
+ "be", (char *)TRUE },
+ { "baudrate", NUMBER|IREMOTE|INIT, (READ<<PUBLIC)|(WRITE<<ROOT),
+ "ba", (char *)&BR },
+ { "dialtimeout",NUMBER, (READ<<PUBLIC)|(WRITE<<ROOT),
+ "dial", (char *)60 },
+ { "eofread", STRING|IREMOTE|INIT, (READ|WRITE)<<PUBLIC,
+ "eofr", (char *)&IE },
+ { "eofwrite", STRING|IREMOTE|INIT, (READ|WRITE)<<PUBLIC,
+ "eofw", (char *)&OE },
+ { "eol", STRING|IREMOTE|INIT, (READ|WRITE)<<PUBLIC,
+ NOSTR, (char *)&EL },
+ { "escape", CHAR, (READ|WRITE)<<PUBLIC,
+ "es", (char *)'~' },
+ { "exceptions", STRING|INIT|IREMOTE, (READ|WRITE)<<PUBLIC,
+ "ex", (char *)&EX },
+ { "force", CHAR, (READ|WRITE)<<PUBLIC,
+ "fo", (char *)CTRL('p') },
+ { "framesize", NUMBER|IREMOTE|INIT, (READ|WRITE)<<PUBLIC,
+ "fr", (char *)&FS },
+ { "host", STRING|IREMOTE|INIT, READ<<PUBLIC,
+ "ho", (char *)&HO },
+ { "log", STRING|INIT, (READ|WRITE)<<ROOT,
+ NOSTR, _PATH_ACULOG },
+ { "phones", STRING|INIT|IREMOTE, READ<<PUBLIC,
+ NOSTR, (char *)&PH },
+ { "prompt", CHAR, (READ|WRITE)<<PUBLIC,
+ "pr", (char *)'\n' },
+ { "raise", BOOL, (READ|WRITE)<<PUBLIC,
+ "ra", (char *)FALSE },
+ { "raisechar", CHAR, (READ|WRITE)<<PUBLIC,
+ "rc", (char *)CTRL('a') },
+ { "record", STRING|INIT|IREMOTE, (READ|WRITE)<<PUBLIC,
+ "rec", (char *)&RE },
+ { "remote", STRING|INIT|IREMOTE, READ<<PUBLIC,
+ NOSTR, (char *)&RM },
+ { "script", BOOL, (READ|WRITE)<<PUBLIC,
+ "sc", (char *)FALSE },
+ { "tabexpand", BOOL, (READ|WRITE)<<PUBLIC,
+ "tab", (char *)FALSE },
+ { "verbose", BOOL, (READ|WRITE)<<PUBLIC,
+ "verb", (char *)TRUE },
+ { "SHELL", STRING|ENVIRON|INIT, (READ|WRITE)<<PUBLIC,
+ NULL, _PATH_BSHELL },
+ { "HOME", STRING|ENVIRON, (READ|WRITE)<<PUBLIC,
+ NOSTR, NOSTR },
+ { "echocheck", BOOL, (READ|WRITE)<<PUBLIC,
+ "ec", (char *)FALSE },
+ { "disconnect", STRING|IREMOTE|INIT, (READ|WRITE)<<PUBLIC,
+ "di", (char *)&DI },
+ { "tandem", BOOL, (READ|WRITE)<<PUBLIC,
+ "ta", (char *)TRUE },
+ { "linedelay", NUMBER|IREMOTE|INIT, (READ|WRITE)<<PUBLIC,
+ "ldelay", (char *)&DL },
+ { "chardelay", NUMBER|IREMOTE|INIT, (READ|WRITE)<<PUBLIC,
+ "cdelay", (char *)&CL },
+ { "etimeout", NUMBER|IREMOTE|INIT, (READ|WRITE)<<PUBLIC,
+ "et", (char *)&ET },
+ { "rawftp", BOOL, (READ|WRITE)<<PUBLIC,
+ "raw", (char *)FALSE },
+ { "halfduplex", BOOL, (READ|WRITE)<<PUBLIC,
+ "hdx", (char *)FALSE },
+ { "localecho", BOOL, (READ|WRITE)<<PUBLIC,
+ "le", (char *)FALSE },
+ { "parity", STRING|INIT|IREMOTE, (READ|WRITE)<<PUBLIC,
+ "par", (char *)&PA },
+ { NOSTR, NULL, NULL, NOSTR, NOSTR }
+};
diff --git a/usr.bin/uucp/acucntrl/acucntrl.c b/usr.bin/uucp/acucntrl/acucntrl.c
new file mode 100644
index 0000000..deba6ba
--- /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 <sys/buf.h>
+#include <signal.h>
+#include <sys/conf.h>
+#ifdef vax
+#ifdef BSD4_2
+#include <vaxuba/ubavar.h>
+#else
+#include <sys/ubavar.h>
+#endif
+#endif /* vax */
+#include <sys/stat.h>
+#include <nlist.h>
+#include <sgtty.h>
+#include <utmp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <sys/file.h>
+#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<NAMSIZ);
+
+ /* check to see if line is being used */
+ if( (etcutmp = open(_PATH_UTMP, 2)) < 0) {
+ fprintf(stderr, "On open %s open: %s\n",
+ _PATH_UTMP, sys_errlist[errno]);
+ exit(1);
+ }
+
+ (void)lseek(etcutmp, utmploc, 0);
+
+ i = read(etcutmp, (char *)&utmp, sizeof(struct utmp));
+
+ if(
+ i == sizeof(struct utmp) &&
+ utmp.ut_line[0] != '\0' &&
+ utmp.ut_name[0] != '\0' &&
+ (
+ !upcase(utmp.ut_name, NAMSIZ) ||
+ (
+ uid != 0 &&
+ strncmp(utmp.ut_name, Uname, NAMSIZ) != 0
+ )
+ )
+ ) {
+ fprintf(stderr, "%s in use by %s\n", device, utmp.ut_name);
+ exit(2);
+ }
+
+#ifndef sequent
+ /* Disable modem control */
+ if (setmodem(device, DISABLE) < 0) {
+ fprintf(stderr, "Unable to disable modem control\n");
+ exit(1);
+ }
+#endif !sequent
+
+ if (enable) {
+#ifdef sequent
+ if (setmodem(device, ENABLE) < 0) {
+ fprintf(stderr, "Cannot Enable modem control\n");
+ (void)setmodem(device, i);
+ exit(1);
+ }
+#endif sequent
+#ifndef sequent
+ if((devfile = open(device, 1)) < 0) {
+ fprintf(stderr, "On open of %s: %s\n",
+ device, sys_errlist[errno]);
+ (void)setmodem(device, resetmodem);
+ exit(1);
+ }
+ /* Try one last time to hang up */
+ if (ioctl(devfile, (int)TIOCCDTR, (char *)0) < 0)
+ fprintf(stderr, "On TIOCCDTR ioctl: %s\n",
+ sys_errlist[errno]);
+
+ if (ioctl(devfile, (int)TIOCNXCL, (char *)0) < 0)
+ fprintf(stderr,
+ "Cannot clear Exclusive Use on %s: %s\n",
+ device, sys_errlist[errno]);
+
+ if (ioctl(devfile, (int)TIOCHPCL, (char *)0) < 0)
+ fprintf(stderr,
+ "Cannot set hangup on close on %s: %s\n",
+ device, sys_errlist[errno]);
+
+#endif !sequent
+ i = resetmodem;
+
+#ifndef sequent
+ if (setmodem(device, ENABLE) < 0) {
+ fprintf(stderr, "Cannot Enable modem control\n");
+ (void)setmodem(device, i);
+ exit(1);
+ }
+#endif sequent
+ resetmodem=i;
+
+ if (settys(ENABLE)) {
+ fprintf(stderr, "%s already enabled\n", device);
+ } else {
+ pokeinit(device, Uname, enable);
+ }
+ post(device, "");
+
+ } else {
+#if defined(TIOCMGET) && defined(SENSECARRIER)
+ if (uid!=0) {
+ int linestat = 0;
+
+ /* check for presence of carrier */
+ sleep(2); /* need time after modem control turnoff */
+
+ if((devfile = open(device, 1)) < 0) {
+ fprintf(stderr, "On open of %s: %s\n",
+ device, sys_errlist[errno]);
+ (void)setmodem(device, resetmodem);
+ exit(1);
+ }
+
+ (void)ioctl(devfile, TIOCMGET, &linestat);
+
+ if (linestat&TIOCM_CAR) {
+ fprintf(stderr, "%s is in use (Carrier On)\n",
+ device);
+ (void)setmodem(device, resetmodem);
+ exit(2);
+ }
+ (void)close(devfile);
+ }
+#endif TIOCMGET
+ /* chown device */
+ if(chown(device, uid, gid) < 0)
+ fprintf(stderr, "Cannot chown %s: %s\n",
+ device, sys_errlist[errno]);
+
+
+ /* poke init */
+ if(settys(DISABLE)) {
+ fprintf(stderr, "%s already disabled\n", device);
+ } else {
+ pokeinit(device, Uname, enable);
+ }
+ post(device, Uname);
+#ifdef sequent
+ /* Disable modem control */
+ if (setmodem(device, DISABLE) < 0) {
+ fprintf(stderr, "Unable to disable modem control\n");
+ exit(1);
+ }
+#endif sequent
+ if((devfile = open(device, O_RDWR|O_NDELAY)) < 0) {
+ fprintf(stderr, "On %s open: %s\n",
+ device, sys_errlist[errno]);
+ } else {
+ if(ioctl(devfile, (int)TIOCSDTR, (char *)0) < 0)
+ fprintf(stderr,
+ "Cannot set DTR on %s: %s\n",
+ device, sys_errlist[errno]);
+ }
+ }
+
+ exit(0);
+}
+
+/* return true if no lower case */
+upcase(str, len)
+register char *str;
+register int len;
+{
+ for (; *str, --len >= 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<<line;
+ resetmodem = ((flags&tflags) == 0);
+ flags = enable ? (flags & ~tflags) : (flags | tflags);
+ (void)lseek(kmem, (off_t) &(ubinfo->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 0000000..f6ee49b
--- /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 0000000..5d5e662
--- /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 0000000..60dbe0b
--- /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 <bsd.prog.mk>
diff --git a/usr.bin/uucp/uuq/uuq.1 b/usr.bin/uucp/uuq/uuq.1
new file mode 100644
index 0000000..783d486
--- /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 0000000..7abb25c
--- /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 <stdio.h>
+
+#ifdef NDIR
+#include "libndir/ndir.h"
+#else !NDIR
+#include <sys/dir.h>
+#endif !NDIR
+#include <sys/stat.h>
+
+#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 <pwd.h>
+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 0000000..6c13fb4
--- /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 <bsd.prog.mk>
diff --git a/usr.bin/uucp/uusend/uusend.1 b/usr.bin/uucp/uusend/uusend.1
new file mode 100644
index 0000000..9379307
--- /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 0000000..207108e
--- /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 <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <pwd.h>
+
+/*
+ * 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<argc; c++)
+ fprintf(dout, "%s ", argv[c]);
+ time(&t);
+ fprintf(dout, "%s", ctime(&t));
+#endif DEBUG
+
+#ifdef RUUSEND
+ if(argv[0][0] == 'r')
+ rsend++;
+#endif RUUSEND
+ while (argc > 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 0000000..92f0e330
--- /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 0000000..565c52a
--- /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 <sys/stat.h>
+#ifdef NDIR
+#include "ndir.h"
+#else
+#include <sys/dir.h>
+#endif
+#include <ctype.h>
+
+#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<sndx; ++i)
+ if((j = strlen(sys[i].name)) > nlen)
+ nlen = j;
+ for(i=0; i<sndx; ++i) {
+ t = (sys[i].st_lastime +sys[i].st_retry) - curtime;
+
+ /* decide if STST text is worth printing */
+ if (-t < ONEDAY*2 && sys[i].st_type == SS_WRONGTIME) {
+ sys[i].stst[0] = '\0';
+ if (sys[i].cntr[0]+sys[i].cntr[1]+sys[i].cntr[2] == 0)
+ continue; /* ignore entire line */
+ }
+
+ printf("%-*.*s ", nlen, nlen, sys[i].name);
+ if(sys[i].cntr[CMDTYPE])
+ printf("%3.d Cmd%s ", sys[i].cntr[CMDTYPE],
+ sys[i].cntr[CMDTYPE]>1?"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<sndx; ++i) {
+ if(strncmp(fnam, sys[i].name, fnamlen) == SAME) {
+ ++sys[i].cntr[type];
+ break;
+ }
+ }
+ if(i == sndx) {
+ strcpy(sys[i].name, fnam);
+ ++sys[i].cntr[type];
+ if(++sndx >= 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; csys<sndx; ++csys) {
+ if(strncmp(&fnam[5], sys[csys].name, SYSNSIZE)
+ == SAME)
+ break;
+ }
+ strcpy(sys[csys].name, &fnam[5]);
+ if(csys == sndx) {
+ ++sndx;
+ }
+ if (stat(fnam, &stbuf) < 0)
+ sys[csys].locked = 1;
+ else
+ sys[csys].locked = stbuf.st_mtime;
+ continue;
+ }
+ }
+ closedir(dirp);
+ if (chdir("STST") < 0) {
+ perror("STST");
+ exit(1);
+ }
+ if ((dirp = opendir(".")) == NULL) {
+ perror("STST");
+ exit(1);
+ }
+ while ((dentp = readdir(dirp)) != NULL) {
+ if(*dentp->d_name == '.')
+ continue;
+ strcpy(fnam, dentp->d_name);
+ for(csys=0; csys<sndx; ++csys) {
+ if(strncmp(fnam, sys[csys].name, SYSNSIZE) == SAME)
+ break;
+ }
+ strcpy(sys[csys].name, fnam);
+ if(csys == sndx) {
+ ++sndx;
+ }
+ if((st = fopen(fnam, "r")) == NULL) {
+ sys[csys].stst[0] = '\0';
+ continue;
+ }
+ buff[0] = '\0';
+ fgets(buff, sizeof(buff), st);
+ fclose(st);
+ if(tp = rindex(buff, ' '))
+ *tp = '\0'; /* drop system name */
+ else
+ continue;
+ for(i=0, tp=buff; i<4; ++i, ++tp)
+ if((tp = index(tp, ' ')) == NULL)
+ break;
+ if(i != 4)
+ continue;
+ strncpy(sys[csys].stst, tp, sizeof(sys[csys].stst));
+ tp = buff;
+ sys[csys].st_type = atoi(tp);
+ tp = index(tp+1, ' ');
+ sys[csys].st_count = atoi(tp+1);
+ tp = index(tp+1, ' ');
+ sys[csys].st_lastime = atol(tp+1);
+ tp = index(tp+1, ' ');
+ sys[csys].st_retry = atol(tp+1);
+ }
+}
+/*
+ * Return the ptr in sp at which the character c appears;
+ * NULL if not found
+ */
+
+char *
+index(sp, c)
+register char *sp, c;
+{
+ do {
+ if (*sp == c)
+ return sp;
+ } while (*sp++);
+ return NULL;
+}
+
+/*
+ * Return the ptr in sp at which the character c last
+ * appears; NULL if not found
+*/
+
+char *
+rindex(sp, c)
+register char *sp, c;
+{
+ register char *r;
+
+ r = NULL;
+ do {
+ if (*sp == c)
+ r = sp;
+ } while (*sp++);
+ return r;
+}
diff --git a/usr.bin/whatis/Makefile b/usr.bin/whatis/Makefile
new file mode 100644
index 0000000..8655a7c
--- /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 <bsd.prog.mk>
diff --git a/usr.bin/whatis/whatis.1 b/usr.bin/whatis/whatis.1
new file mode 100644
index 0000000..0f1b0a1
--- /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 0000000..904f0e4
--- /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 <sys/param.h>
+#include <sys/queue.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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);
+}
OpenPOWER on IntegriCloud