diff options
Diffstat (limited to 'usr.bin')
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(§newp->list, ep, q); + continue; + } + e_subp = (subp = getlist("_subdir")) == NULL ? + NULL : subp->list.tqh_first; + for (; e_subp != NULL; e_subp = e_subp->q.tqe_next) { + (void)snprintf(buf, sizeof(buf), "%s%s{/%s,}", + e_sectp->s, e_subp->s, machine); + if ((ep = malloc(sizeof(ENTRY))) == NULL || + (ep->s = strdup(buf)) == NULL) + err(1, NULL); + TAILQ_INSERT_TAIL(§newp->list, ep, q); + } + } + sectnewp->s = section->s; + defp = sectnewp; + TAILQ_REMOVE(&head, section, q); + } + + /* + * 5: Search for the files. Set up an interrupt handler, so the + * temporary files go away. + */ + (void)signal(SIGINT, onsig); + (void)signal(SIGHUP, onsig); + + memset(&pg, 0, sizeof(pg)); + for (found = 0; *argv; ++argv) + if (manual(*argv, defp, &pg)) + found = 1; + + /* 6: If nothing found, we're done. */ + if (!found) { + (void)cleanup(); + exit (1); + } + + /* 7: If it's simple, display it fast. */ + if (f_cat) { + for (ap = pg.gl_pathv; *ap != NULL; ++ap) { + if (**ap == '\0') + continue; + cat(*ap); + } + exit (cleanup()); + } + if (f_how) { + for (ap = pg.gl_pathv; *ap != NULL; ++ap) { + if (**ap == '\0') + continue; + how(*ap); + } + exit(cleanup()); + } + if (f_where) { + for (ap = pg.gl_pathv; *ap != NULL; ++ap) { + if (**ap == '\0') + continue; + (void)printf("%s\n", *ap); + } + exit(cleanup()); + } + + /* + * 8: We display things in a single command; build a list of things + * to display. + */ + for (ap = pg.gl_pathv, len = strlen(pager) + 1; *ap != NULL; ++ap) { + if (**ap == '\0') + continue; + len += strlen(*ap) + 1; + } + if ((cmd = malloc(len)) == NULL) { + warn(NULL); + (void)cleanup(); + exit(1); + } + p = cmd; + len = strlen(pager); + memmove(p, pager, len); + p += len; + *p++ = ' '; + for (ap = pg.gl_pathv; *ap != NULL; ++ap) { + if (**ap == '\0') + continue; + len = strlen(*ap); + memmove(p, *ap, len); + p += len; + *p++ = ' '; + } + *p = '\0'; + + /* Use system(3) in case someone's pager is "pager arg1 arg2". */ + (void)system(cmd); + + exit(cleanup()); +} + +/* + * manual -- + * Search the manuals for the pages. + */ +static int +manual(page, tag, pg) + char *page; + TAG *tag; + glob_t *pg; +{ + ENTRY *ep, *e_sufp, *e_tag; + TAG *missp, *sufp; + int anyfound, cnt, found; + char *p, buf[128]; + + anyfound = 0; + buf[0] = '*'; + + /* For each element in the list... */ + e_tag = tag == NULL ? NULL : tag->list.tqh_first; + for (; e_tag != NULL; e_tag = e_tag->q.tqe_next) { + (void)snprintf(buf, sizeof(buf), "%s/%s.*", e_tag->s, page); + if (glob(buf, + GLOB_APPEND | GLOB_BRACE | GLOB_NOSORT | GLOB_QUOTE, + NULL, pg)) { + warn("globbing"); + (void)cleanup(); + exit(1); + } + if (pg->gl_matchc == 0) + continue; + + /* Find out if it's really a man page. */ + for (cnt = pg->gl_pathc - pg->gl_matchc; + cnt < pg->gl_pathc; ++cnt) { + + /* + * Try the _suffix key words first. + * + * XXX + * Older versions of man.conf didn't have the suffix + * key words, it was assumed that everything was a .0. + * We just test for .0 first, it's fast and probably + * going to hit. + */ + (void)snprintf(buf, sizeof(buf), "*/%s.0", page); + if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) + goto next; + + e_sufp = (sufp = getlist("_suffix")) == NULL ? + NULL : sufp->list.tqh_first; + for (found = 0; + e_sufp != NULL; e_sufp = e_sufp->q.tqe_next) { + (void)snprintf(buf, + sizeof(buf), "*/%s%s", page, e_sufp->s); + if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) { + found = 1; + break; + } + } + if (found) + goto next; + + /* Try the _build key words next. */ + e_sufp = (sufp = getlist("_build")) == NULL ? + NULL : sufp->list.tqh_first; + for (found = 0; + e_sufp != NULL; e_sufp = e_sufp->q.tqe_next) { + for (p = e_sufp->s; + *p != '\0' && !isspace(*p); ++p); + if (*p == '\0') + continue; + *p = '\0'; + (void)snprintf(buf, + sizeof(buf), "*/%s%s", page, e_sufp->s); + if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) { + if (!f_where) + build_page(p + 1, + &pg->gl_pathv[cnt]); + *p = ' '; + found = 1; + break; + } + *p = ' '; + } + if (found) { +next: anyfound = 1; + if (!f_all) { + /* Delete any other matches. */ + while (++cnt< pg->gl_pathc) + pg->gl_pathv[cnt] = ""; + break; + } + continue; + } + + /* It's not a man page, forget about it. */ + pg->gl_pathv[cnt] = ""; + } + + if (anyfound && !f_all) + break; + } + + /* If not found, enter onto the missing list. */ + if (!anyfound) { + if ((missp = getlist("_missing")) == NULL) + missp = addlist("_missing"); + if ((ep = malloc(sizeof(ENTRY))) == NULL || + (ep->s = strdup(page)) == NULL) { + warn(NULL); + (void)cleanup(); + exit(1); + } + TAILQ_INSERT_TAIL(&missp->list, ep, q); + } + return (anyfound); +} + +/* + * build_page -- + * Build a man page for display. + */ +static void +build_page(fmt, pathp) + char *fmt, **pathp; +{ + static int warned; + ENTRY *ep; + TAG *intmpp; + int fd; + char buf[MAXPATHLEN], cmd[MAXPATHLEN], tpath[sizeof(_PATH_TMP)]; + + /* Let the user know this may take awhile. */ + if (!warned) { + warned = 1; + warnx("Formatting manual page..."); + } + + /* Add a remove-when-done list. */ + if ((intmpp = getlist("_intmp")) == NULL) + intmpp = addlist("_intmp"); + + /* Move to the printf(3) format string. */ + for (; *fmt && isspace(*fmt); ++fmt); + + /* + * Get a temporary file and build a version of the file + * to display. Replace the old file name with the new one. + */ + (void)strcpy(tpath, _PATH_TMP); + if ((fd = mkstemp(tpath)) == -1) { + warn("%s", tpath); + (void)cleanup(); + exit(1); + } + (void)snprintf(buf, sizeof(buf), "%s > %s", fmt, tpath); + (void)snprintf(cmd, sizeof(cmd), buf, *pathp); + (void)system(cmd); + (void)close(fd); + if ((*pathp = strdup(tpath)) == NULL) { + warn(NULL); + (void)cleanup(); + exit(1); + } + + /* Link the built file into the remove-when-done list. */ + if ((ep = malloc(sizeof(ENTRY))) == NULL) { + warn(NULL); + (void)cleanup(); + exit(1); + } + ep->s = *pathp; + TAILQ_INSERT_TAIL(&intmpp->list, ep, q); +} + +/* + * how -- + * display how information + */ +static void +how(fname) + char *fname; +{ + FILE *fp; + + int lcnt, print; + char *p, buf[256]; + + if (!(fp = fopen(fname, "r"))) { + warn("%s", fname); + (void)cleanup(); + exit (1); + } +#define S1 "SYNOPSIS" +#define S2 "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS" +#define D1 "DESCRIPTION" +#define D2 "D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN" + for (lcnt = print = 0; fgets(buf, sizeof(buf), fp);) { + if (!strncmp(buf, S1, sizeof(S1) - 1) || + !strncmp(buf, S2, sizeof(S2) - 1)) { + print = 1; + continue; + } else if (!strncmp(buf, D1, sizeof(D1) - 1) || + !strncmp(buf, D2, sizeof(D2) - 1)) + return; + if (!print) + continue; + if (*buf == '\n') + ++lcnt; + else { + for(; lcnt; --lcnt) + (void)putchar('\n'); + for (p = buf; isspace(*p); ++p); + (void)fputs(p, stdout); + } + } + (void)fclose(fp); +} + +/* + * cat -- + * cat out the file + */ +static void +cat(fname) + char *fname; +{ + int fd, n; + char buf[2048]; + + if ((fd = open(fname, O_RDONLY, 0)) < 0) { + warn("%s", fname); + (void)cleanup(); + exit(1); + } + while ((n = read(fd, buf, sizeof(buf))) > 0) + if (write(STDOUT_FILENO, buf, n) != n) { + warn("write"); + (void)cleanup(); + exit (1); + } + if (n == -1) { + warn("read"); + (void)cleanup(); + exit(1); + } + (void)close(fd); +} + +/* + * check_pager -- + * check the user supplied page information + */ +static char * +check_pager(name) + char *name; +{ + char *p, *save; + + /* + * if the user uses "more", we make it "more -s"; watch out for + * PAGER = "mypager /usr/ucb/more" + */ + for (p = name; *p && !isspace(*p); ++p); + for (; p > name && *p != '/'; --p); + if (p != name) + ++p; + + /* make sure it's "more", not "morex" */ + if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))){ + save = name; + /* allocate space to add the "-s" */ + if (!(name = + malloc((u_int)(strlen(save) + sizeof("-s") + 1)))) + err(1, NULL); + (void)sprintf(name, "%s %s", save, "-s"); + } + return(name); +} + +/* + * jump -- + * strip out flag argument and jump + */ +static void +jump(argv, flag, name) + char **argv, *flag, *name; +{ + char **arg; + + argv[0] = name; + for (arg = argv + 1; *arg; ++arg) + if (!strcmp(*arg, flag)) + break; + for (; *arg; ++arg) + arg[0] = arg[1]; + execvp(name, argv); + (void)fprintf(stderr, "%s: Command not found.\n", name); + exit(1); +} + +/* + * onsig -- + * If signaled, delete the temporary files. + */ +static void +onsig(signo) + int signo; +{ + (void)cleanup(); + + (void)signal(signo, SIG_DFL); + (void)kill(getpid(), signo); + + /* NOTREACHED */ + exit (1); +} + +/* + * cleanup -- + * Clean up temporary files, show any error messages. + */ +static int +cleanup() +{ + TAG *intmpp, *missp; + ENTRY *ep; + int rval; + + rval = 0; + ep = (missp = getlist("_missing")) == NULL ? + NULL : missp->list.tqh_first; + if (ep != NULL) + for (; ep != NULL; ep = ep->q.tqe_next) { + warnx("no entry for %s in the manual.", ep->s); + rval = 1; + } + + ep = (intmpp = getlist("_intmp")) == NULL ? + NULL : intmpp->list.tqh_first; + for (; ep != NULL; ep = ep->q.tqe_next) + (void)unlink(ep->s); + return (rval); +} + +/* + * usage -- + * print usage message and die + */ +static void +usage() +{ + (void)fprintf(stderr, + "usage: man [-achw] [-C file] [-M path] [-m path] [section] title ...\n"); + exit(1); +} diff --git a/usr.bin/man/man.conf b/usr.bin/man/man.conf new file mode 100644 index 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(<)) != 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); +} |