v13i077: Utilities to monitor usage on system

Rich Salz rsalz at bbn.com
Wed Mar 2 10:02:02 AEST 1988


Submitted-by: Steven Grimm <koreth at ssyx.UCSC.EDU>
Posting-number: Volume 13, Issue 77
Archive-name: budpak

The "buddy system."

This is a collection of utilities for monitoring other users on the system.
It runs on 4.3BSD on a VAX, and 4.2BSD on a Sun 3/160 and an ISI Optimum V.
I have no idea whether it works (or even compiles) on SysV or not.

+New! Improved! Now 100% Artificial-+-+-----------------------------------+
|#   #  @@@  ****  &&&&& $$$$$ %   %| |Steven Grimm                       |
|#  #  @   @ *   * &       $   %   %+-+ ARPA: koreth at ssyx.ucsc.edu        |
|###   @   @ ****  &&&&    $   %%%%%| | UUCP: ...!ucbvax!ucscc!ssyx!koreth|
|#  #  @   @ * *   &       $   %   %+-+     ______________________________|
|#   #  @@@  *  ** &&&&&   $   %   %| |     |"Let's see what's out there."|
+-----with NutraSour(TM)!  No natural colors or preservatives!------------+


------------ (chop here) ------------
# This is a shell archive.  Remove anything before this line
# then unpack it by saving it in a file and typing "sh file"
# (Files unpacked will be owned by you and have default permissions).
# This archive contains the following files:
#	./MANIFEST
#	./Makefile
#	./aliases
#	./budpak.1
#	./buds.1
#	./buds.c
#	./cbuds.1
#	./cbuds.c
#	./mbuds.1
#	./mbuds.c
#	./monitor.1
#	./monitor.c
#	./rbuds.1
#	./rbuds.c
#	./wbuds.1
#	./wbuds.c
#
if `test ! -s ./MANIFEST`
then
echo "writing ./MANIFEST"
cat > ./MANIFEST << '\Rogue\Monster\'
This shar should contain the following files:

Makefile	- A makefile for BudPak.
budpak.1
buds.1
cbuds.1
mbuds.1
monitor.1
rbuds.1
wbuds.1		- Documentation for the BudPak utilities.  Use [nt]roff -man.
buds.c
cbuds.c
mbuds.c
monitor.c
rbuds.c
wbuds.c		- BudPak source code.
aliases		- Aliases for setting "single" and "horny" flags (see buds.6)

Read budpak.1 for an overview of the utilities provided.
\Rogue\Monster\
else
  echo "will not over write ./MANIFEST"
fi
if `test ! -s ./Makefile`
then
echo "writing ./Makefile"
cat > ./Makefile << '\Rogue\Monster\'
#
# BUDPAK 1.0
#
# Written by Jon Luini    (niteowl at ssyx.ucsc.edu)
#                          ...!uunet!ucbvax!ucscc!ssyx!niteowl
#        and Steven Grimm (koreth at ssyx.ucsc.edu)
#                          ...!uunet!ucbvax!ucscc!ssyx!koreth
#
CFLAGS=		-g
BINDIR= 	/usr/local
BINARIES=	buds cbuds mbuds monitor rbuds wbuds

BudPak: $(BINARIES)
buds :  buds.c
	cc $(CFLAGS)  buds.c -o  buds
cbuds: cbuds.c
	cc $(CFLAGS) cbuds.c -o cbuds
mbuds: mbuds.c
	cc $(CFLAGS) mbuds.c -o mbuds
monitor: monitor.c
	cc $(CFLAGS) monitor.c -o monitor
rbuds: rbuds.c
	cc $(CFLAGS) rbuds.c -o rbuds
wbuds: wbuds.c
	cc $(CFLAGS) wbuds.c -o wbuds
\Rogue\Monster\
else
  echo "will not over write ./Makefile"
fi
if `test ! -s ./aliases`
then
echo "writing ./aliases"
cat > ./aliases << '\Rogue\Monster\'
alias single chmod g+x \`tty\`
alias horny chmod o+x \`tty\`
\Rogue\Monster\
else
  echo "will not over write ./aliases"
fi
if `test ! -s ./budpak.1`
then
echo "writing ./budpak.1"
cat > ./budpak.1 << '\Rogue\Monster\'
.TH BUDPAK 1 BUDPAK
.UC 4
.SH NAME
buds, wbuds, mbuds, monitor, rbuds, cbuds \- The Buddy System
.SH SYNOPSIS
.B buds
[
.I options
]
.PP
.B wbuds
.PP
.B mbuds
[ user1 user2 user3 ... ]
.PP
.B monitor
[
.I options
] [ user1 user2 user3 ... ]
.PP
.B rbuds
.PP
.B cbuds
.SH DESCRIPTION
.I BudPak
is a collection of silly utilities to monitor the activities of other users on
the system.  It consists of six programs of varying uselessness:
.PP
.TP
.I buds
This is the program that started it all.  It was suggested by Mark Axelrod
(deckard at ucscb.ucsc.edu), who was displeased with the
.I whom
utility's inability to handle large alternate usernames (our
.I whom
reads in a file called
.B .whom
in the user's home directory, so that short real names can be substituted for
account names).
.I buds
and its friends allow tremendous (well, 37 characters) aliases.  The
.I buds
program itself simply lists the people who are online and in a file called
.B .buddy
in the your home directory.
.TP
.I wbuds
This command looks at your buddies and tells you what they're up to.
.TP
.I mbuds
.I mbuds
checks /usr/spool/mail and tells you which buddies have mail, and how much
of it they have.
.TP
.I monitor
This is perhaps the most interesting program of the bunch, which isn't saying
a whole lot.  It constantly monitors /etc/utmp and tells you when buddies log
on or off.
.TP
.I rbuds
.I rbuds
is a handy utility that allows several aliases to be kept for each buddy.
It randomly selects aliases from a file called
.B .rbuds
in your home directory.
.TP
.I cbuds
This is a silly hack that was necessary when we wrote
.I rbuds\fR.
It converts a
.B .buddy
file to a
.B .rbuds
file, so that you don't have to retype the whole thing.  If you use
.B .rbuds
from the start, you'll never need to use
.I cbuds.
.PP
.PP
The \fB.buddy\fR file in your home directory contains a list of account
names and a \fIbuddy alias\fR for each name.  A buddy alias can be up
to 37 characters, and is typically a user's real name, or a nickname or
some other information.  The format of the \fB.buddy\fR file is simply
<account> <space> <alias> <newline> for each buddy.  For instance:
.PP
.PP
  root Mister SuperUser
  zooker Donuts!  Feed me donuts!
  harris David Harris, accountant at large
.PP
.PP
Other utilities can affect your \fB.buddy\fR file; see rbuds(1) for
the most useful one.
.SH "SEE ALSO"
buds(1), wbuds(1), mbuds(1), monitor(1), rbuds(1), cbuds(1)
.SH AUTHORS
Jon Luini, niteowl at ssyx.ucsc.edu (\fIbuds\fR and the original \fImonitor\fR)
.PP
Steven Grimm, koreth at ssyx.ucsc.edu (everything else)
.SH BUGS
.I BudPak
is far too simple to have any bugs.

\Rogue\Monster\
else
  echo "will not over write ./budpak.1"
fi
if `test ! -s ./buds.1`
then
echo "writing ./buds.1"
cat > ./buds.1 << '\Rogue\Monster\'
.TH BUDS 1 BUDPAK
.UC 4
.SH NAME
buds \- list of other users
.SH SYNOPSIS
.B buds
[
.I options
]
.SH DESCRIPTION
.I buds
tells you who's online at the moment, and prints some statistics about
them.  Foremost is the \fIbuddy alias\fR, which is a long (up to 37 characters)
string containing a user's real name, nickname, or other information.
See budpak(1) for more information.  A typical line of \fIbuds\fR output
is:
.PP
.PP
Mister SuperUser      wb 13 hs p3 (root)  0:47 [ucbvax]
.PP
.PP
"Mister SuperUser" is the buddy alias for "root" (whose real username is
in the third to last column of the listing).  "w" and "b" indicates that
he's writable (see mesg(1)) and biffable (see biff(1)).  He has been idle
for 13 minutes.  "h" is the "horny" flag -- it indicates that root wants it,
and wants it bad.  "s" means that he's single ("h" without "s" can mean
trouble, so watch it!)  "p3" is the name of the tty that root is on; in this
example, it's rlogin port 3.  0:47 is the amount of time he's been
logged on.  The last field only appears when the user is logged in from a
remote host, and contains the name of the remote host (ucbvax, in this
example.)
.SH OPTIONS
.TP
.I \-w
Only lists writable users.
.TP
.I \-b
Only lists biffable users.
.TP
.I \-i
Only lists users who have been idle for one minute or more.
.TP
.I \-h
Only lists horny users.
.TP
.I \-s
Only lists single users.
.TP
.I \-d
Doesn't substitute buddy aliases for usernames.  This flag is actually pretty
useless, since real usernames are printed anyway.
.TP
.I \-a
List all users who are online, whether they're in \fB.buddy\fR or not.
.TP
.I \-S
Doesn't print the load average at the end of the buddy listing.  The load average
is only printed on systems which support the rwho(1C) service.
.SH AUTHOR
Jon Luini, niteowl at ssyx.ucsc.edu
.SH FILES
.TP
/etc/utmp
.TP
$HOME/.buddy \-
Buddy alias file.
.SH "SEE ALSO"
monitor(1), rbuds(1), budpak(1)

\Rogue\Monster\
else
  echo "will not over write ./buds.1"
fi
if `test ! -s ./buds.c`
then
echo "writing ./buds.c"
cat > ./buds.c << '\Rogue\Monster\'
/**
*** buds.c --> the ORIGINAL (completely rewritten) budpak utility
***   this (and all) versions 
***   by Jon Luini, niteowl at ssyx.ucsc.edu
***
***   A couple of modifications by Steven Grimm, koreth at ssyx.ucsc.edu
***
*** 1:32:11 A.M.  Saturday, November 14th 1987
**/

#include <stdio.h>
#include <utmp.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>

/*
** Comment out the following #define statement if your machine isn't listed
** in its own /etc/rwhod section.  The /etc/rwhod section is used for a fast
** load average estimate.
*/
#define RWHOD

/*
** The following define is a prefix that will be removed from the remote
** hostnames of people who are rlogged-in.  For instance, at UCSC we have
** machines called "ucsca", "ucscb", "ucscc", and so on.  Our prefix is
** "ucsc", so that the aforementioned machine names will show up as "a",
** "b", and "c", respectively, in the remote host part of the buddy listing.
** If you don't want a prefix, set it to something silly like "zzzzzzzzz"
** that will never occur.
*/
#define PREFIX      "ucsc"

#define TRUE             1
#define FALSE            0
#define UTMP   "/etc/utmp"	/* the location of utmp                       */
#define MAX_USERS       80	/* Maximum number of users possible           */

#define IDLE           002	/* is user idle for longer than 60 seconds?   */
#define RITABLE        020	/* Is each user's tty ritable?                */
#define BIFF          0100	/* User have biff set?                        */
#define SINGLE        0010	/* User have single set?                      */
#define HORNY         0001	/* User have HoRnY set?                       */
#define C_IDLE     0000001	/* list only if user is idle                  */
#define C_RITEABLE 0000010	/* list only if writeable                     */
#define C_BIFF     0000100	/* list only if biffable                      */
#define C_SINGLE   0001000	/* list only if user is single                */
#define C_HORNY    0010000	/* list only if HoRnY                         */
#define DONT_SUB   0100000	/* Don't substitute the login with the alias  */
#define LIST_ALL  01000000	/* List all users, not just buddies           */

struct BUD {
	char login[8];		/* login name           */
	char alias[50];		/* alias of login       */
	char line[8];		/* tty name             */
	char host[16];		/* host name, if remote */
	long time;		/* time on              */
	int  idle;		/* time idle            */
	int  buddy;		/* is the user a buddy? */
	unsigned long perm;	/* perm of the buds tty */
} buddy[MAX_USERS];

char *month[] = {
	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 
};

char *space(),			/* function for printing spaces            */
     *lentime(),		/* funtion to print time buddy has been on */
     hostname[10];		/* the hostname of this machine            */
unsigned long mode;		/* the mode of the command line params     */
int num_usrs = 0,		/* total number of users online            */
    num_buds = 0,		/* total number of buddies online          */
    compare(),			/* algorithm used for qsort                */
    shortf;			/* flag to tell buds to be in short mode   */

main(argc, argv)
int argc;
char **argv;
{
	int bflag = FALSE;

	while (*argv) {
		if (**argv == '-') {
			++*argv;
			while (**argv) {
				switch (**argv) {
					case 'w': mode |= C_RITEABLE; break;
					case 'b': mode |= C_BIFF; break;
					case 'i': mode |= C_IDLE; break;
					case 'h': mode |= C_HORNY; break;
					case 's': mode |= C_SINGLE; break;
					case 'd': mode |= DONT_SUB; break;
					case 'a': mode |= LIST_ALL; break;
					case 'S': shortf = TRUE; break;
					default : printf("'%c': bad flag\n",
							**argv); bflag = TRUE;
				}
				++*argv;
			}
		}
		++argv;
	}
 
	if (bflag) {    /** print usage screen **/
		puts("Usage: buds [-wbihsdaS]");
		puts("\tw \tlist only writeable users");
		puts("\tb \tlist only biffable users");
		puts("\ti \tlist only idle users");
		puts("\th \tlist only horny users");
		puts("\ts \tlist only single users");
		puts("\td \tdon't substitute buddy aliases");
		puts("\ta \tlist all users on");
#ifdef RWHOD
		puts("\tS \trun buds in Short mode.. dont print the load (faster)");
#endif
		exit(-1);
	}

	init();
	get_usrs();
	if (!(mode & DONT_SUB)) get_buds();
	qsort((char *) &buddy[0], num_usrs, sizeof(struct BUD), compare);
	print_out();
}

compare(name1, name2)        /** the compare function for qsort **/
struct BUD *name1, *name2; {
	return(strncmp(name1->alias, name2->alias, 40));
}

init() {      /** zero the buddy struct array **/
	register int i;

	for (i = 0; i < MAX_USERS; i++) 
		bzero(&buddy[i], sizeof(buddy[i]));
}

get_usrs() { /** get the user online in putthem in buddy struct array **/
	struct utmp butane;
	int fd, i = 0;

	if ((fd = open(UTMP, 0)) == -1) {
		perror("reading users");
		exit(-1);
	}

	while (read(fd, &butane, sizeof(struct utmp))) {
		if (butane.ut_name[0]) {
			strncpy(buddy[i].login, butane.ut_name, 8);
			strncpy(buddy[i].alias, butane.ut_name, 8);
			strncpy(buddy[i].line, butane.ut_line, 8);
			strcpy(buddy[i].host, butane.ut_host);
			buddy[i].time = butane.ut_time;
			status(&buddy[i]);
			i++;
		}
	}
	num_usrs = i;
	close(fd);
}

status(buddy)       /** get the ttystatus of each buddy **/
struct BUD *buddy;
{
	struct stat sbuf;
	char tty[14];
	long now;

	sprintf(tty, "/dev/%s", buddy->line);
	if (stat(tty, &sbuf) == -1) {
		perror("getting tty status");
		buddy->perm = 0;
		return(-1);
	}
	time(&now);
	buddy->perm = 0;
	if ((now - sbuf.st_atime) >= 60) {
		buddy->perm |= IDLE;
		buddy->idle = now - sbuf.st_atime;
	}
	if (sbuf.st_mode & RITABLE) buddy->perm |= RITABLE;
	if (sbuf.st_mode & BIFF) buddy->perm |= BIFF;
	if (sbuf.st_mode & HORNY) buddy->perm |= HORNY;
	if (sbuf.st_mode & SINGLE) buddy->perm |= SINGLE;
}

int get_buds() {  /** check the ~/.buddy for valid buddies **/
	FILE *fp;
	char budpath[100], login[8], alias[40];
	register int i;

	sprintf(budpath, "%s/.buddy", getenv("HOME"));
	if ((fp = fopen(budpath, "r")) == NULL) {
		perror("getting buddies");
		return(-1);
	}
	while (!feof(fp)) {
		fscanf(fp, "%s %[^\n]\n", login, alias);
		for (i = 0; i < num_usrs; i++) 
			if (!strcmp(buddy[i].login, login)) {
				strncpy(buddy[i].alias, alias, 40);
				buddy[i].buddy = TRUE;
			}
	}
	return(1);
}

print_out() {  /** print the final output to the screen **/
	struct tm *t;
	char idle[2], buf[14], host[20];
	register int i;
	long now;
	int valid[MAX_USERS], lav[2];

	num_buds = 0;
	gethostname(buf, 14);
	strncpy(hostname, buf, 5);
	hostname[5] = '\0';

	/** check to see whether buddy should be printed **/
	for (i = 0; i < num_usrs; i++) { 
		valid[i] = TRUE;
		if (mode & LIST_ALL)
			valid[i] = TRUE; else 
			if (!buddy[i].buddy && (!(mode & DONT_SUB))) 
				valid[i] = FALSE; 
			else valid[i] = TRUE;
		if (mode & C_RITEABLE) 
			if (!(buddy[i].perm & RITABLE)) valid[i] = FALSE;
		if (mode & C_BIFF) 
			if (!(buddy[i].perm & BIFF)) valid[i] = FALSE;
		if (mode & C_IDLE) 
			if (!(buddy[i].perm & IDLE)) valid[i] = FALSE;
		if (mode & C_HORNY)
			if (!(buddy[i].perm & HORNY)) valid[i] = FALSE;
		if (mode & C_SINGLE)
			if (!(buddy[i].perm & SINGLE)) valid[i] = FALSE;
		if (valid[i] && buddy[i].buddy) num_buds++;
	}

	time(&now);
	t = localtime(&now);

	if (!shortf)
		printf("%d buddies out of %d users on %s : %s %d at %d:%02d%s\n",
			num_buds, num_usrs, hostname, month[t->tm_mon], t->tm_mday,
			t->tm_hour > 12 ? t->tm_hour-12 : t->tm_hour == 0 ? 12 : t->tm_hour,
			t->tm_min, t->tm_hour > 11 ? "pm" : "am");

	puts("===================================================================");
	for (i = 0; i < num_usrs; i++) {
		if (buddy[i].line[strlen(buddy[i].line)-2] == 'p')
			sprintf(host, "[%s]", !strncmp(buddy[i].host, PREFIX, sizeof(PREFIX)-1) ?
		buddy[i].host+sizeof(PREFIX)-1 : buddy[i].host);
		else strcpy(host, "");

		if (valid[i]) {
			if (buddy[i].idle >= 60) sprintf(idle, "%02d", buddy[i].idle / 60);
			else strcpy(idle, "--");
			printf("%-40.40s %c%c %s %c%c %2.2s (%s)%s%s %s\n", 
				buddy[i].alias, 
				buddy[i].perm & RITABLE ? 'w' : '-',
				buddy[i].perm & BIFF    ? 'b' : '-',
				idle,
				buddy[i].perm & HORNY   ? 'h' : '-',
				buddy[i].perm & SINGLE  ? 's' : '-',
				strcmp(buddy[i].line, "console") ? &buddy[i].line[3] : "co",
				buddy[i].login, space(7-strlen(buddy[i].login)),
				lentime(now - buddy[i].time),
				host);
		}
	}
#ifdef RWHOD
	if (!shortf) {
		load(lav);
		printf("================== Load: %2.2f %2.2f %2.2f ===========================\n",
			lav[0]/100.0, lav[1]/100.0, lav[2]/100.0);
	} else
#endif	/* RWHOD */
		puts("===================================================================");
}

char *space(len)  /** returns a blank string of length 'len' **/
int len;
{
	register int i;
	static char tmp[100];

	strcpy(tmp, "");
	for (i = 0; i < len; i++) strcat(tmp, " ");
	return(tmp);
}

char *lentime (time)  /** returns the time on in readable format **/
long  time;
{
	 static char	buffer[32],		/* A buffer for formatted time	     */
			*s;			/* A pointer to the formatted time   */
	 int		minutes,		/* Number of minutes past the hour   */
			hours;			/* The number of hours past midnight */

	 hours = time / 3600;
	 minutes =  (time % 3600) / 60;
	 s = &buffer[31];
	 *s-- = '\0';
	 *s-- = minutes % 10 + '0';
	 *s-- = minutes / 10 + '0';
	 if (hours > 23)
	 {
		hours %= 24;
		*s-- = '?';
	 } 
	 else
		*s-- = ':';
	 *s-- = hours % 10 + '0';
	 *s =  (hours >= 10) ? hours / 10 + '0' : ' ';
	 return(s);
}

#ifdef RWHOD
#include <protocols/rwhod.h>

load(load_buf)
int *load_buf;
{
	struct whod wbuf;
	char whod_path[80];
	int fd;

	sprintf(whod_path, "/usr/spool/rwho/whod\.%s", hostname);
	if ((fd = open(whod_path, 0)) == -1) {
		perror("getting load");
		load_buf[0] = 0;
		load_buf[1] = 0;
		load_buf[2] = 0;
		return(-1);
	}
	read(fd, (char *) &wbuf, sizeof(struct whod));
	load_buf[0] = wbuf.wd_loadav[0];
	load_buf[1] = wbuf.wd_loadav[1];
	load_buf[2] = wbuf.wd_loadav[2];
	close(fd);
}

#endif	/* RWHOD */
\Rogue\Monster\
else
  echo "will not over write ./buds.c"
fi
if `test ! -s ./cbuds.1`
then
echo "writing ./cbuds.1"
cat > ./cbuds.1 << '\Rogue\Monster\'
.TH CBUDS 1 BUDPAK
.UC 4
.SH NAME
cbuds \- Convert a .buddy file to a .rbuds file.
.SH SYNOPSIS
.B cbuds
.SH DESCRIPTION
.I Cbuds
reads in a
.B .buddy
file (from the user's home directory) and creates a
.B .rbuds
file (also in the user's home directory).  See rbuds(1) for more information
about the format of the .rbuds file.
.SH AUTHOR
Steven Grimm, koreth at ssyx.ucsc.edu
.SH "SEE ALSO"
rbuds(1)
\Rogue\Monster\
else
  echo "will not over write ./cbuds.1"
fi
if `test ! -s ./cbuds.c`
then
echo "writing ./cbuds.c"
cat > ./cbuds.c << '\Rogue\Monster\'
#include <stdio.h>
#include <ctype.h>

char *getenv();

main()
{
	FILE *bud, *rbud;
	char f1[99], f2[99];

	strcpy(f1, getenv("HOME"));
	strcpy(f2, f1);
	strcat(f1, "/.buddy");
	strcat(f2, "/.rbuds");

	if ((bud = fopen(f1, "r")) == NULL)
	{
		printf("Couldn't open %s for read.\n", f1);
		exit(-1);
	}
	if (access(f2, 0) == 0)
	{
		char x;
		printf("%s already exists.  Overwrite? ", f2);
		fflush(stdout);
		x = getchar();
		if (toupper(x) != 'Y')
		{
			fclose(bud);
			exit(-1);
		}
	}
	if ((rbud = fopen(f2, "w+")) == NULL)
	{
		printf("Couldn't open %s for write.\n", f2);
		exit(-1);
	}

	while (! feof(bud))
	{
		char login[8], budname[99];
		fscanf(bud, "%s %[^\n]\n", login, budname);
		fprintf(rbud, "*%s\n%s\n", login, budname);
	}
	fclose(bud);
	fclose(rbud);
}

\Rogue\Monster\
else
  echo "will not over write ./cbuds.c"
fi
if `test ! -s ./mbuds.1`
then
echo "writing ./mbuds.1"
cat > ./mbuds.1 << '\Rogue\Monster\'
.TH MBUDS 1 BUDPAK
.UC 4
.SH NAME
mbuds \- Check buddies for mail
.SH SYNOPSIS
.B mbuds
[
.I user1
.I user2
.I user3
\&... ]
.SH DESCRIPTION
.I Mbuds
scans the file \fB.buddy\fR in the user's home directory (see budpak(1))
and checks the users
listed therein for mail.  If there are some users whose mailboxes (which
are assumed to be located in /usr/spool/mail) aren't empty, their names,
their aliases in the .buddy file, and the length of their mailbox files
are printed.
.PP
If login names are specified,
.I mbuds
scans those users' mailboxes and lists them (using the above format) whether
they have mail or not.
.SH AUTHOR
Steven Grimm, koreth at ssyx.ucsc.edu
.SH "SEE ALSO"
buds(1), wbuds(1), monitor(1), rbuds(1), budpak(1)
\Rogue\Monster\
else
  echo "will not over write ./mbuds.1"
fi
if `test ! -s ./mbuds.c`
then
echo "writing ./mbuds.c"
cat > ./mbuds.c << '\Rogue\Monster\'
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>

char *getlogin(), *getenv(), *malloc();

/*
** MBUDS - Check for buddies' mail
**
** Usage: mbuds [login1 login2 login3...]
**
** If no logins are specified, mbuds prints everyone in the user's .buddy
** file who has mail.  Otherwise, only the specified logins are selected
** from the .buddy file, and are printed whether they have mail or not.
*/

struct bud {
	char login[8];
	char name[40];
	struct bud *next;
};

struct bud *start;

/* Read in a .buddy file and make a linked list out of it. */
readbuds()
{
	struct bud *end;
	FILE *fp;
	char budfile[80];
	strcpy(budfile, getenv("HOME"));
	strcat(budfile, "/.buddy");
	if ((fp=fopen(budfile, "r"))==NULL)
	{
		printf("No .buddy file.\n");
		return 0;
	}
	start = end = (struct bud *)NULL;
	while (! feof(fp))
	{
		struct bud *newnode;
		newnode = (struct bud *)malloc(sizeof(struct bud));
		newnode->login[0] = 0;
		newnode->next = (struct bud *)NULL;
		fscanf(fp, "%s %39[^\n]\n", newnode->login, newnode->name);
		if (strlen(newnode->login))
		{
			if (end)
				end->next = newnode;
			else
				start = newnode;
			end = newnode;
		}
	}
	return 1;
}

/* Scan the linked list for entries that aren't in the command line arguments
   and chop them out. */
scanlist(argc, argv)
int argc;
char **argv;
{
	struct bud *now, *then, *temp;
	if (argc < 2)
		return;
	argv++;
	argc--;
	then = (struct bud *)NULL;
	now = start;
	while (now)
	{
		int i;
		for (i = 0; i < argc; ++i)
			if (! strcmp(argv[i], now->login))
				break;
		if (i >= argc)		/* Loop finished = name not found */
		{
			if (then)
			{
				temp = now;
				then->next = now->next;
				now = now->next;
				free(temp);
			}
			else
			{
				temp = now;
				start = now->next;
				now = start;
				free(temp);
			}
		}
		else
		{
			then = now;
			now = now->next;
			argv[i] = argv[argc];	/* Trim the list so it's */
			argc--;			/* faster to search */
		}
	}
}

checkmail(bud)
struct bud *bud;
{
	struct stat buf;
	if (stat(bud->login, &buf) < 0)
		return 0;
	return (int)buf.st_size;
}

main(argc, argv)
int argc;
char **argv;
{
	struct bud *now;
	int maillen;
	chdir("/usr/spool/mail");
	if (! readbuds())
		exit(-1);
	scanlist(argc, argv);
	now = start;
	while (now)
	{
		maillen = checkmail(now);
		if (argc > 1 || maillen)
		{
			printf("%7s %-40s %6d bytes\n", now->login,
				now->name, maillen);
		}
		now = now->next;
	}
}

\Rogue\Monster\
else
  echo "will not over write ./mbuds.c"
fi
if `test ! -s ./monitor.1`
then
echo "writing ./monitor.1"
cat > ./monitor.1 << '\Rogue\Monster\'
.TH MONITOR 1 BUDPAK
.UC 4
.SH NAME
monitor \- Notify about logins and logouts
.SH SYNOPSIS
.B monitor
[
.I options
] [ user1 user2 user3 ... ]
.SH DESCRIPTION
.I Monitor
is a utility that notifies the user when certain other people log on and off.
Pass the usernames of the people to monitor on the command line.  When one of
the monitored people logs on, a short message is printed containing his
username and alias in the user's
.B .buddy
file (see budpak(1)), if he has an alias there.  If the \-o option
(see below) is used, a short message is also printed when one of the
monitored users logs off.  The messages can be changed using the \-m option.
.SH OPTIONS
.TP
.I \-o
Users specified after the \-o option are monitored for both logins and
logouts.  Only logins are monitored for users specified before the \-o
option.
.TP
.I \-c
The \-c option prevents the program from printing its messages when "cbreak"
mode is on.  Cbreak is active in programs such as vi, which can easily be
messed up by messages appearing at random on the screen.  The messages are
printed as soon as cbreak mode is deactivated (e.g., when you leave vi).
.TP
.I \-n
The \-n option prevents \fImonitor\fR from printing users' real account
names if there are aliases in the \fB.buddy\fR file.
.TP
.I \-N
The \-N option prevents \fImonitor\fR from printing aliases from a \fB.buddy\fR
file.
.TP
.I \-q
The \-q (quiet) option suppresses \fImonitor\fR's startup message.
.TP
.I \-b
The \-b flag stops \fImonitor\fR from beeping when it prints messages.
.TP
.I \-t
The \-t flag causes \fImonitor\fR to print the name of the tty a monitoree
is on, as well as his name.
.TP
.I \-d
The \-d option causes \fImonitor\fR to terminate when any of the specified
users logs on.  This is useful if you're waiting for someone, and don't
want to have to search for \fImonitor\fR's PID to kill it.
.TP
.I \-m
The \-m option takes login names from a file called
.B .monitor
in the user's home directory, rather than from the command line.  The format of
the
.B .monitor
file is as follows:  The first line is a message that's printed out when one
of the users logs on (with a "%s" where the login name and/or buddy alias is
to be placed).  The second line is the logout message (again, with a "%s"
where the username should go).  The following lines are the login names that
the user wants to watch for logins only (i.e., logouts aren't printed).  Then,
optionally, a line containing only a pound sign ("#") character can be followed
by the login names of users to monitor for both logins and logouts.  Note that
it's possible to have the login/logout messages, then a pound sign and a list
of users; that will monitor both logins and logouts of everyone listed in the
.B .monitor
file.  A
.B .monitor
file might look like:
 
    Wow!  %s is here!
    Drat.  There goes %s.
    jones
    martin
    #
    root
    zooker
    harris
 
.SH AUTHOR
Steven Grimm, koreth at ssyx.ucsc.edu
.SH FILES
.TP
/etc/utmp
.TP
$HOME/.monitor \-
The file of login names to monitor, and the alert strings for logins and
logouts.
.TP
$HOME/.buddy \-
Buddy alias file.
.SH "SEE ALSO"
buds(1), rbuds(1), budpak(1)
.SH BUGS
.I Monitor
doesn't allow different login and logout messages for individual buddies.



\Rogue\Monster\
else
  echo "will not over write ./monitor.1"
fi
if `test ! -s ./monitor.c`
then
echo "writing ./monitor.c"
cat > ./monitor.c << '\Rogue\Monster\'
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <utmp.h>
#include <signal.h>
#include <sgtty.h>

/*
** MONITOR - Watch people log on and off
**
** Original program by	Jon Luini
**			niteowl at ssyx.ucsc.edu
** This version by	Steven Grimm
**			koreth at ssyx.ucsc.edu
**
** Operation:
**
**   Monitor scans the file /etc/utmp and checks each entry to see if its
** tty is in the binary tree of ttys belonging to buddies (actually, the
** tree is a structure with lots of information about the buddy).  If the
** tty is used by a buddy, Monitor compares the login names of the buddy and
** the person using the tty.  If they're not equal, the buddy who used to
** be on the tty has logged off and a message to that effect may be printed.
** The buddy is deleted from the tty tree and added to the second tree,
** the buddy tree.  If the tty isn't in the tty tree, the login name of the
** buddy is searched for in the second binary tree, which is sorted by login
** names.  If the name is found, the buddy is added to the tty tree and a
** logon message is printed out.
**   The /etc/utmp file isn't scanned if it hasn't been modified since the
** last time Monitor passed through it.
*/

#define UTMP	"/etc/utmp"
#define WAIT	15		/* Seconds between status checks */

char *ttyname(), *getenv(), *rindex();

struct buddy {
	char tty[8];		/* Tty name */
	char login[8];		/* Login name */
	char budname[40];	/* .buddy file name, if any */
	int  logoff;		/* Flag: Monitor logoffs? */
	struct buddy *lchild,	/* Binary search tree pointers */
		     *rchild,
		     *parent;
};

struct buddy *buddy, *tty;

char	logonmsg[80],		/* Gets printed when someone logs on */
	logoffmsg[80],		/* Gets printed when someone logs off */
	whichtty[8],		/* Which tty this was started on */
	login[8];		/* Name of the user who started this */

int	check_off = 0,		/* Flag: Check logoffs */
	die = 0,		/* Flag: Die when someone comes on */
	quiet = 0,		/* Flag: Start quietly */
	nobeep = 0,		/* Flag: Don't beep */
	noreal = 0,		/* Flag: Don't print account names */
	nofake = 0,		/* Flag: Don't print buddy aliases */
	dotty = 0,		/* Flag: Print a user's tty */
	cblock = 0;		/* Flag: Don't interrupt cbreak */

/* Search for a buddy.  Pass a 0 in "type" to search for a login name, or a
   1 to search for a tty. */
struct buddy *bst_search(tree, key, type)
struct buddy *tree;
char *key;
int type;
{
	int comp;
	while (tree)
	{
		if (type)
		{
			if (! (comp=strcmp(key, tree->tty)))
				return tree;
		}
		else
			if (! (comp=strcmp(key, tree->login)))
				return tree;
		if (comp < 0)
			tree = tree->lchild;
		else
			tree = tree->rchild;
	}
	return tree;
}

/* Add a node to a tree (the node can have children).  Pass the tree type,
   as in bst_search above. */
bst_add(tree, node, type)
struct buddy *tree, *node;
int type;
{
	int comp;
	if (! node)
		return 1;
	while (tree)
	{
		if (type)
		{
			if (! (comp=strcmp(node->tty, tree->tty)))
				return 0;
		}
		else
			if (! (comp=strcmp(node->login, tree->login)))
				return 0;
		if (comp < 0)
		{
			if (tree->lchild)
				tree = tree->lchild;
			else
			{
				tree->lchild = node;
				node->parent = tree;
				break;
			}
		}
		else
		{
			if (tree->rchild)
				tree = tree->rchild;
			else
			{
				tree->rchild = node;
				node->parent = tree;
				break;
			}
		}
	}
	if (tree)
		return 1;
	else
		return 0;		/* This should never happen */
}

/* Delete a node from a tree.  Returns the node that has taken the deleted
   node's place in the tree. */
struct buddy *bst_delete(node, type)
struct buddy *node;
int type;
{
	if (node->parent)
	{
		if (node == node->parent->lchild)
		{
			node->parent->lchild = node->rchild;
			if (node->rchild)
				node->rchild->parent = node->parent;
			if (node->lchild)
				bst_add(node->parent, node->lchild, type);
			return node->rchild;
		}
		else if (node == node->parent->rchild)
		{
			node->parent->rchild = node->lchild;
			if (node->lchild)
				node->lchild->parent = node->parent;
			if (node->rchild)
				bst_add(node->parent, node->rchild, type);
			return node->lchild;
		}
		else
			fprintf(stderr, "\r\nBinary tree error!\r\n");
	}
	else if (node->lchild)
	{
		node->lchild->parent = 0;
		if (node->rchild)
			bst_add(node->lchild, node->rchild, type);
		return node->lchild;
	}
	else if (node->rchild)
	{
		node->rchild->parent = 0;
		if (node->lchild)
			bst_add(node->rchild, node->lchild, type);
		return node->rchild;
	}
	else
		return (struct buddy *)0;
}

/* Handle one entry from the utmp file. */
doutmp(entry)
struct utmp *entry;
{
	struct buddy *bud, *newbud;
	if (tty)	/* Only check logoffs if there are buddies on */
	{
		bud = bst_search(tty, entry->ut_line, 1);
		if (bud)
		{
			if (strcmp(bud->login, entry->ut_name))
			{
				if (bud->logoff)
					domsg(logoffmsg, bud);
				newbud = bst_delete(bud);
				if (bud == tty)
					tty = newbud;
				free(bud);
			}
			return;
		}
	}
	if (buddy)
	{
		bud = bst_search(buddy, entry->ut_name, 0);
		if (bud)
		{
			newbud = (struct buddy *) malloc(sizeof(struct buddy));
			*newbud = *bud;
			strcpy(newbud->tty, entry->ut_line);
			newbud->parent =
			newbud->lchild =
			newbud->rchild = (struct buddy *) 0;
			if (tty)
				bst_add(tty, newbud, 1);
			else
				tty = newbud;
			domsg(logonmsg, newbud);
			if (die)
				exit(0);
		}
	}
}

/* Process the utmp file.  Returns 0 if the user has logged off. */
process()
{
	int	hand, status=1;
	struct utmp entry;
	hand = open(UTMP, 0);
	while (read(hand, &entry, sizeof(entry)) > 0)
	{
		if (! strcmp(entry.ut_line, whichtty))
			if (strcmp(entry.ut_name, login))
				status = 0;
		doutmp(&entry);
	}
	close(hand);
	return status;
}

/* Print the logoff message for a buddy. */
domsg(message, bud)
char *message;
struct buddy *bud;
{
	char budname[60], text[256];
	if ((! bud->budname[0]) || nofake)
		strcpy(budname, bud->login);
	else if (bud->budname[0] && !noreal)
		sprintf(budname, "%s (%s)", bud->budname, bud->login);
	else if (bud->budname[0])
		sprintf(budname, "%s", bud->budname);
	if (dotty)
	{
		strcat(budname, " on ");
		strcat(budname, bud->tty);
	}
	sprintf(text, message, budname);
	fprintf(stderr, "\r\n%s\r\n", text);
	if (! nobeep)
		putc(7, stderr);
}

/* Read in the .buddy file and assign aliases to the buddies in the buddy
   tree. */
readbuddy()
{
	char fname[256], login[8], budname[40];
	struct buddy *bud;
	FILE *fp;
	strcpy(fname, getenv("HOME"));
	strcat(fname, "/.buddy");
	if (!(fp = fopen(fname, "r")))
		return 1;
	while (! feof(fp))
	{
		fscanf(fp, "%s %[^\n]\n", login, budname);
		bud = bst_search(buddy, login, 0);
		if (bud)
			strcpy(bud->budname, budname);
	}
	fclose(fp);
}

/* Initialize the login and whichtty variables. */
initvars()
{
	char ttypath[256], *ri;
	strcpy(login, getenv("USER"));
	strcpy(ttypath, ttyname(2));
	ri = rindex(ttypath, '/');
	if (! ri)
		ri = ttypath;
	else
		++ri;
	strcpy(whichtty, ri);
	buddy = tty = (struct buddy *)0;
}

/* Add a user to the buddy tree. */
adduser(name)
char *name;
{
	struct buddy *newbud;
	newbud = (struct buddy *) malloc(sizeof(struct buddy));
	newbud->parent =
	newbud->lchild =
	newbud->rchild = (struct buddy *) 0;
	newbud->tty[0] = newbud->budname[0] = 0;
	newbud->logoff = check_off;
	strcpy(newbud->login, name);
	if (buddy)
		bst_add(buddy, newbud, 0);
	else
		buddy = newbud;
}

/* Read in a .monitor file. */
readmonitor()
{
	FILE *fp;
	char fname[256], user[80];
	strcpy(fname, getenv("HOME"));
	strcat(fname, "/.monitor");
	if (!(fp=fopen(fname, "r")))
	{
		printf("Monitor: Error opening %s.\n", fname);
		return 0;
	}
	fscanf(fp, "%[^\n]\n%[^\n]\n", logonmsg, logoffmsg);
	while (! feof(fp))
	{
		fscanf(fp, "%s\n", user);
		if (strcmp(user, "#"))
			adduser(user);
		else
			check_off = 1;
	}
	fclose(fp);
	return 1;
}

/* Check the last modify date of a file. */
long lastmod(file)
char *file;
{
	struct stat buf;
	stat(file, &buf);
	return buf.st_mtime;
}

/* Check the tty for cbreak mode. */
cbreak()
{
	struct sgttyb tty;
	gtty(0, &tty);
	return tty.sg_flags & CBREAK;
}

/* Main program. */
main(argc, argv)
int argc;
char **argv;
{
	long lastutmp, lu;
	initvars();
	strcpy(logonmsg, "%s has logged on.");
	strcpy(logoffmsg, "%s has logged off.");
	while (*++argv)
	{
		if (argv[0][0] == '-')
			switch (argv[0][1])
			{
				case 'o':
					check_off = 1;
					break;
				case 'm':
					readmonitor();
					break;
				case 'c':
					cblock = 1;
					break;
				case 'd':
					die = 1;
					break;
				case 'q':
					quiet = 1;
					break;
				case 'n':
					noreal = 1;
					break;
				case 'N':
					nofake = 1;
					break;
				case 'b':
					nobeep = 1;
					break;
				case 't':
					dotty = 1;
					break;
				default:
					printf("%c: unknown flag\n",
						argv[0][1]);
					break;
			}
		else
			adduser(argv[0]);
	}
	if (! quiet)
		printf("Monitor: running on %s.\n", whichtty);
	if (fork())		/* Shove myself in the background */
		exit(0);
	signal(SIGHUP, SIG_IGN);	/* Ignore some signals so that */
	signal(SIGINT, SIG_IGN);	/* w won't think we're the user's */
	signal(SIGQUIT, SIG_IGN);	/* current process. */
	if (! buddy)
	{
		printf("Monitor: No users selected (using default)\n");
		adduser("root");
		strcpy(buddy->budname, "God");
	}
	readbuddy();
	lastutmp = 0;
	while (1)
	{
		if ((!cblock)||(!cbreak()))
		{
			lu = lastmod(UTMP);
			if (lu != lastutmp)
			{
				lastutmp = lu;
				if (! process())
					exit(0);
			}
		}
		sleep(WAIT);
	}
}



\Rogue\Monster\
else
  echo "will not over write ./monitor.c"
fi
if `test ! -s ./rbuds.1`
then
echo "writing ./rbuds.1"
cat > ./rbuds.1 << '\Rogue\Monster\'
.TH RBUDS 1 BUDPAK
.UC 4
.SH NAME
rbuds \- Construct a random .buddy file
.SH SYNOPSIS
.B rbuds
[
.I \-b
]
.SH DESCRIPTION
.I Rbuds
allows users to keep lists of aliases for buddies on the system, and select
random names from those lists to put in a .buddy file.  The input to
.I rbuds
is the file
.B .rbuds
in the user's home directory, which contains data in the following format:
.PP
 *login1
 Buddy Alias 1 for login 1
 Buddy Alias 2 for login 1
 Buddy Alias 3 for login 1
 *login2
 Buddy Alias for login 2
 *login3
 Buddy Alias 1 for login 3
 Buddy Alias 2 for login 3
    .
    .
    .
.PP
Up to 128 aliases per buddy are allowed.  When
.I rbuds
is run, it reads in all the buddy aliases for a login name and selects a
random one to output to the
.B .buddy
file in the user's home directory.
.SH OPTIONS
.TP
.I \-b
The \-b option causes
.I rbuds
to put itself in the background as soon as it begins executing.  This is
primarily intended for users with large .rbuds files who want to put the
.I rbuds
command in their .login files without having to wait longer to log in.
.SH AUTHOR
Steven Grimm, koreth at ssyx.ucsc.edu
.SH FILES
$HOME/.buddy \- The output
.br
$HOME/.rbuds \- The input
.SH "SEE ALSO"
buds(1), mbuds(1), wbuds(1), monitor(1), budpak(1), cbuds(1)
.SH BUGS
Buddy aliases can't begin with asterisks.

\Rogue\Monster\
else
  echo "will not over write ./rbuds.1"
fi
if `test ! -s ./rbuds.c`
then
echo "writing ./rbuds.c"
cat > ./rbuds.c << '\Rogue\Monster\'
#include <stdio.h>

/*
** RBUDS - make a .buddy file from a .rbuds file.
**
** .rbuds is in the format
**
** *login1
** Buddy Alias 0
** *login2
** Buddy Alias 1
** Buddy Alias 2
** Buddy Alias 3
**
** Buddy aliases can't begin with asterisks.  Up to 128 choices per login
** name are allowed.
**
** If the -b option is used, rbuds puts itself in the background.
*/

char *getenv();

char oldlogin[8], curlogin[8];
char budnames[40][128];
FILE *rbuds, *buddy;

/* Read in the names for a person and return an index into budnames[]
   corresponding to the name that'll be used. */
getname()
{
	int i=0;
	budnames[i][0] = ' ';
	while (! feof(rbuds))
	{
		fscanf(rbuds, "%[^\n]\n", budnames[i]);
		if (budnames[i][0] == '*')
			break;
		i++;
	}
	if (budnames[i][0] == '*')
		strcpy(curlogin, budnames[i]+1);
	return (random() % i);
}

main(argc, argv)
char **argv;
{
	char fname[128], bname[128];
	if (argc == 2 && !strcmp(argv[1], "-b"))
		if (fork())
			exit(0);
	srandom(getpid());
	strcpy(fname, getenv("HOME"));
	strcpy(bname, fname);
	strcat(fname, "/.rbuds");
	strcat(bname, "/.buddy");
	if (! (rbuds = fopen(fname, "r")))
	{
		fprintf(stderr, "rbuds: No .rbuds file!\n");
		exit(-1);
	}
	if (! (buddy = fopen(bname, "w+")))
	{
		fprintf(stderr, "rbuds: Couldn't open .buddy!\n");
		exit(-1);
	}
	fscanf(rbuds, "*%s\n", curlogin);
	while (! feof(rbuds))
	{
		strcpy(oldlogin, curlogin);
		fprintf(buddy, "%s %s\n", oldlogin, budnames[getname()]);
	}
	fclose(buddy);
	fclose(rbuds);
}

\Rogue\Monster\
else
  echo "will not over write ./rbuds.c"
fi
if `test ! -s ./wbuds.1`
then
echo "writing ./wbuds.1"
cat > ./wbuds.1 << '\Rogue\Monster\'
.TH WBUDS 1 BUDPAK
.UC 4
.SH NAME
wbuds \- See what buddies are doing
.SH SYNOPSIS
.B wbuds
.SH DESCRIPTION
.I Wbuds
executes the "w" command, but only selects those users who are in the caller's
".buddy" file.  It replaces the statistics reported by "w" with the aliases
in the .buddy file.
.SH AUTHOR
Steven Grimm, koreth at ssyx.ucsc.edu
.SH FILES
/usr/ucb/w - the "w" command
.SH "SEE ALSO"
w(1), buds(1), budpak(1)
.SH BUGS
.I Wbuds
assumes that the output of /usr/ucb/w is in a specific format.
.PP
.I Wbuds
doesn't support the -s option to /usr/ucb/w, and can't restrict its
output to certain users.

\Rogue\Monster\
else
  echo "will not over write ./wbuds.1"
fi
if `test ! -s ./wbuds.c`
then
echo "writing ./wbuds.c"
cat > ./wbuds.c << '\Rogue\Monster\'
#include <stdio.h>

/*
** wbuds
**
** Tells you what everyone in your .buddy file is doing.
*/

#define W "/usr/ucb/w"

extern char *getenv();
extern FILE *popen();

main()
{
	FILE *buddy, *w;
	char budfile[128], budname[16], buddud[128];
	char buds[128][16], rbuds[128][60];
	int  numbuds=0, i;
	strcpy(budfile, getenv("HOME"));
	strcat(budfile, "/.buddy");
	if ((buddy = fopen(budfile, "r")) == NULL)
	{
		printf("No .buddy file!\n");
		exit(-1);
	}
	while (! feof(buddy))
	{
		budname[0] = 0;
		fscanf(buddy, "%s", budname);
		if (! budname[0])
			continue;
		fscanf(buddy, "%[^\n]", buddud);
		getc(buddy);
		buddud[38] = 0;
		while (buddud[0] == 10)		/* Strip tabs. */
			strcpy(buddud, buddud+1);
		strcpy(rbuds[numbuds], buddud);
		strcpy(buds[numbuds++], budname);
	}
	fclose(buddy);
	if ((w = popen(W, "r")) == NULL)
	{
		printf("%s isn't working.\n", W);
		exit(-1);
	}
	fscanf(w, "%[^\n]", buddud);	/* Get the load, etc. */
	getc(w);
	printf("%s\n", buddud);
	printf("%-8s%-39s%s\n", "Login", "Name", "What");
	fscanf(w, "%[^\n]", buddud);	/* Get the column headers. */
	getc(w);
	while (! feof(w))
	{
		buddud[0]=0;
		fscanf(w, "%[^\n]", buddud);
		buddud[8]=0;
		while (buddud[strlen(buddud)-1] == ' ')
			buddud[strlen(buddud)-1]=0;
		getc(w);
		for (i=0; i<numbuds; ++i)
			if (! strcmp(buddud, buds[i]))
				printf("%-7s%-40s%s\n", buddud, rbuds[i], &buddud[47]);
	}
	pclose(w);
}
\Rogue\Monster\
else
  echo "will not over write ./wbuds.c"
fi
echo "Finished archive 1 of 1"
# if you want to concatenate archives, remove anything after this line
exit


-- 
For comp.sources.unix stuff, mail to rsalz at uunet.uu.net.



More information about the Comp.sources.unix mailing list