[comp.bugs.sys5] 4.3 finger diffs for A/UX

pst@anise.acc.com (Paul Traina) (07/06/89)

I was sick and tired of the stupid bugs in the Apple/Unisoft port
of finger(1) to SystemV,  so here is a shar file that contains the
diff of the 4.3-tahoe finger.c so that it will compile on a A/UX system.

It should work on any Uniplus based SystemV (and perhaps on some others
too).  finger(1) is untainted by AT&T lawyers and is available on uunet.
For those without access to finger.c,  I've also included my modified
finger.c in toto.

Previous bugs now fixed:
	finger used to report that a user was logged in,  even if they
	were not,  if they had ever logged in before.

	repeat by:
		login to two or different terminals & then logout
		do finger your-name@aux-machine
		finger incorrectly shows that you're there.

	cause:
		System V treats the utmp file differently.  We needed
		to check ut_type as well as ut_uid to see which ttys
		the user was logged in on.

	finger couldn't deal with last-login-time when user wasn't logged
	in.

	repeat by:
		make sure you're not logged in on aux-host
		do finger your-name@aux-machine from remote site
		it says you've never logged in.

	cause:
		System V doesn't have /usr/adm/lastlog.  It does have
		/etc/wtmp.  We now cruise through wtmp (backwards)
		looking for last occurance of a user's login & glom
		the information that way.  (Yes, its not as nice as
		lastlog, but at least it works).

As usual, this is "as-is".  Have fun (remember to compile with -DUSG).

				Happy Hacking,
					the PST

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  finger.diff finger.c
# Wrapped by pst@anise.acc.com on Wed Jul  5 17:27:40 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'finger.diff' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'finger.diff'\"
else
echo shar: Extracting \"'finger.diff'\" \(7315 characters\)
sed "s/^X//" >'finger.diff' <<'END_OF_FILE'
X*** finger.orig	Wed Jul  5 17:21:36 1989
X--- finger.c	Wed Jul  5 17:23:41 1989
X***************
X*** 58,64
X  #include <sys/signal.h>
X  #include <pwd.h>
X  #include <stdio.h>
X- #include <lastlog.h>
X  #include <ctype.h>
X  #include <sys/time.h>
X  #include <sys/socket.h>
X
X--- 58,63 -----
X  #include <sys/signal.h>
X  #include <pwd.h>
X  #include <stdio.h>
X  #include <ctype.h>
X  #ifdef	USG
X  #include <time.h>
X***************
X*** 60,65
X  #include <stdio.h>
X  #include <lastlog.h>
X  #include <ctype.h>
X  #include <sys/time.h>
X  #include <sys/socket.h>
X  #include <netinet/in.h>
X
X--- 59,68 -----
X  #include <pwd.h>
X  #include <stdio.h>
X  #include <ctype.h>
X+ #ifdef	USG
X+ #include <time.h>
X+ #else
X+ #include <lastlog.h>
X  #include <sys/time.h>
X  #endif
X  #include <sys/socket.h>
X***************
X*** 61,66
X  #include <lastlog.h>
X  #include <ctype.h>
X  #include <sys/time.h>
X  #include <sys/socket.h>
X  #include <netinet/in.h>
X  #include <netdb.h>
X
X--- 64,70 -----
X  #else
X  #include <lastlog.h>
X  #include <sys/time.h>
X+ #endif
X  #include <sys/socket.h>
X  #include <netinet/in.h>
X  #include <netdb.h>
X***************
X*** 96,101
X  	struct person *link;		/* link to next person */
X  };
X  
X  char LASTLOG[] = "/usr/adm/lastlog";	/* last login info */
X  char USERLOG[] = "/etc/utmp";		/* who is logged in */
X  char PLAN[] = "/.plan";			/* what plan file is */
X
X--- 100,108 -----
X  	struct person *link;		/* link to next person */
X  };
X  
X+ #ifdef	USG
X+ char LASTLOG[] = "/etc/wtmp";
X+ #else
X  char LASTLOG[] = "/usr/adm/lastlog";	/* last login info */
X  #endif
X  char USERLOG[] = "/etc/utmp";		/* who is logged in */
X***************
X*** 97,102
X  };
X  
X  char LASTLOG[] = "/usr/adm/lastlog";	/* last login info */
X  char USERLOG[] = "/etc/utmp";		/* who is logged in */
X  char PLAN[] = "/.plan";			/* what plan file is */
X  char PROJ[] = "/.project";		/* what project file */
X
X--- 104,110 -----
X  char LASTLOG[] = "/etc/wtmp";
X  #else
X  char LASTLOG[] = "/usr/adm/lastlog";	/* last login info */
X+ #endif
X  char USERLOG[] = "/etc/utmp";		/* who is logged in */
X  char PLAN[] = "/.plan";			/* what plan file is */
X  char PROJ[] = "/.project";		/* what project file */
X***************
X*** 195,200
X  		exit(2);
X  	}
X  	if (unquick) {
X  		extern _pw_stayopen;
X  
X  		setpwent();
X
X--- 203,209 -----
X  		exit(2);
X  	}
X  	if (unquick) {
X+ #ifndef	USG
X  		extern _pw_stayopen;
X  #endif
X  		setpwent();
X***************
X*** 196,202
X  	}
X  	if (unquick) {
X  		extern _pw_stayopen;
X! 
X  		setpwent();
X  		_pw_stayopen = 1;
X  		fwopen();
X
X--- 205,211 -----
X  	if (unquick) {
X  #ifndef	USG
X  		extern _pw_stayopen;
X! #endif
X  		setpwent();
X  #ifndef	USG
X  		_pw_stayopen = 1;
X***************
X*** 198,203
X  		extern _pw_stayopen;
X  
X  		setpwent();
X  		_pw_stayopen = 1;
X  		fwopen();
X  	}
X
X--- 207,213 -----
X  		extern _pw_stayopen;
X  #endif
X  		setpwent();
X+ #ifndef	USG
X  		_pw_stayopen = 1;
X  #endif
X  		fwopen();
X***************
X*** 199,204
X  
X  		setpwent();
X  		_pw_stayopen = 1;
X  		fwopen();
X  	}
X  	while (read(uf, (char *)&user, sizeof user) == sizeof user) {
X
X--- 209,215 -----
X  		setpwent();
X  #ifndef	USG
X  		_pw_stayopen = 1;
X+ #endif
X  		fwopen();
X  	}
X  	while (read(uf, (char *)&user, sizeof user) == sizeof user) {
X***************
X*** 202,207
X  		fwopen();
X  	}
X  	while (read(uf, (char *)&user, sizeof user) == sizeof user) {
X  		if (user.ut_name[0] == 0)
X  			continue;
X  		if (person1 == 0)
X
X--- 213,221 -----
X  		fwopen();
X  	}
X  	while (read(uf, (char *)&user, sizeof user) == sizeof user) {
X+ #ifdef	USG
X+ 		if (user.ut_name[0] == 0 || user.ut_type != USER_PROCESS)
X+ #else
X  		if (user.ut_name[0] == 0)
X  #endif
X  			continue;
X***************
X*** 203,208
X  	}
X  	while (read(uf, (char *)&user, sizeof user) == sizeof user) {
X  		if (user.ut_name[0] == 0)
X  			continue;
X  		if (person1 == 0)
X  			p = person1 = (struct person *) malloc(sizeof *p);
X
X--- 217,223 -----
X  		if (user.ut_name[0] == 0 || user.ut_type != USER_PROCESS)
X  #else
X  		if (user.ut_name[0] == 0)
X+ #endif
X  			continue;
X  		if (person1 == 0)
X  			p = person1 = (struct person *) malloc(sizeof *p);
X***************
X*** 273,278
X  	if (unquick) {
X  		setpwent();
X  		if (!match) {
X  			extern _pw_stayopen;
X  
X  			_pw_stayopen = 1;
X
X--- 288,294 -----
X  	if (unquick) {
X  		setpwent();
X  		if (!match) {
X+ #ifndef	USG
X  			extern _pw_stayopen;
X  
X  			_pw_stayopen = 1;
X***************
X*** 276,281
X  			extern _pw_stayopen;
X  
X  			_pw_stayopen = 1;
X  			for (p = person1; p != 0; p = p->link)
X  				if (pw = getpwnam(p->name))
X  					p->pwd = pwdcopy(pw);
X
X--- 292,298 -----
X  			extern _pw_stayopen;
X  
X  			_pw_stayopen = 1;
X+ #endif
X  			for (p = person1; p != 0; p = p->link)
X  				if (pw = getpwnam(p->name))
X  					p->pwd = pwdcopy(pw);
X***************
X*** 315,320
X  		exit(2);
X  	}
X  	while (read(uf, (char *)&user, sizeof user) == sizeof user) {
X  		if (*user.ut_name == 0)
X  			continue;
X  		for (p = person1; p != 0; p = p->link) {
X
X--- 332,340 -----
X  		exit(2);
X  	}
X  	while (read(uf, (char *)&user, sizeof user) == sizeof user) {
X+ #ifdef USG
X+ 		if (*user.ut_name == 0 || user.ut_type != USER_PROCESS)
X+ #else
X  		if (*user.ut_name == 0)
X  #endif
X  			continue;
X***************
X*** 316,321
X  	}
X  	while (read(uf, (char *)&user, sizeof user) == sizeof user) {
X  		if (*user.ut_name == 0)
X  			continue;
X  		for (p = person1; p != 0; p = p->link) {
X  			if (p->loggedin == 2)
X
X--- 336,342 -----
X  		if (*user.ut_name == 0 || user.ut_type != USER_PROCESS)
X  #else
X  		if (*user.ut_name == 0)
X+ #endif
X  			continue;
X  		for (p = person1; p != 0; p = p->link) {
X  			if (p->loggedin == 2)
X***************
X*** 792,797
X  findwhen(pers)
X  	register struct person *pers;
X  {
X  	struct lastlog ll;
X  	int i;
X  
X
X--- 813,821 -----
X  findwhen(pers)
X  	register struct person *pers;
X  {
X+ #ifdef	USG
X+ 	struct utmp    ut;
X+ #else
X  	struct lastlog ll;
X  #endif
X  	int i;
X***************
X*** 793,798
X  	register struct person *pers;
X  {
X  	struct lastlog ll;
X  	int i;
X  
X  	if (lf >= 0) {
X
X--- 817,823 -----
X  	struct utmp    ut;
X  #else
X  	struct lastlog ll;
X+ #endif
X  	int i;
X  
X  	if (lf >= 0) {
X***************
X*** 796,801
X  	int i;
X  
X  	if (lf >= 0) {
X  		lseek(lf, (long)pers->pwd->pw_uid * sizeof ll, 0);
X  		if ((i = read(lf, (char *)&ll, sizeof ll)) == sizeof ll) {
X  			bcopy(ll.ll_line, pers->tty, LMAX);
X
X--- 821,845 -----
X  	int i;
X  
X  	if (lf >= 0) {
X+ #ifdef USG
X+ 		lseek(lf, (long) (sizeof ut), 2);
X+ 		while (lseek(lf, (long) (-2 * sizeof ut), 1) >= 0) {
X+ 			read(lf, (char *)&ut, sizeof ut);
X+ 			if (ut.ut_type == USER_PROCESS &&
X+ 			    !strncmp(ut.ut_user, pers->name)) {
X+ 				bcopy(ut.ut_line, pers->tty,  LMAX);
X+ 				pers->tty[LMAX] = '\0';
X+ 				bcopy(ut.ut_host, pers->host, HMAX);
X+ 				pers->host[HMAX] = '\0';
X+ 				pers->loginat = ut.ut_time;
X+ 				return;
X+ 			} else 
X+ 				continue;
X+ 		}
X+ 		pers->tty[0] = 0;
X+ 		pers->host[0] = 0;
X+ 		pers->loginat = 0L;
X+ #else
X  		lseek(lf, (long)pers->pwd->pw_uid * sizeof ll, 0);
X  		if ((i = read(lf, (char *)&ll, sizeof ll)) == sizeof ll) {
X  			bcopy(ll.ll_line, pers->tty, LMAX);
X***************
X*** 811,816
X  			pers->host[0] = 0;
X  			pers->loginat = 0L;
X  		}
X  	} else {
X  		pers->tty[0] = 0;
X  		pers->host[0] = 0;
X
X--- 855,861 -----
X  			pers->host[0] = 0;
X  			pers->loginat = 0L;
X  		}
X+ #endif
X  	} else {
X  		pers->tty[0] = 0;
X  		pers->host[0] = 0;
END_OF_FILE
if test 7315 -ne `wc -c <'finger.diff'`; then
    echo shar: \"'finger.diff'\" unpacked with wrong size!
fi
# end of 'finger.diff'
fi
if test -f 'finger.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'finger.c'\"
else
echo shar: Extracting \"'finger.c'\" \(25816 characters\)
sed "s/^X//" >'finger.c' <<'END_OF_FILE'
X/*
X * Copyright (c) 1980 Regents of the University of California.
X * All rights reserved.  The Berkeley software License Agreement
X * specifies the terms and conditions for redistribution.
X */
X
X#ifndef lint
Xchar copyright[] =
X"@(#) Copyright (c) 1980 Regents of the University of California.\n\
X All rights reserved.\n";
X#endif not lint
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)finger.c	5.10 (Berkeley) 4/26/87";
X#endif not lint
X
X/*
X * This is a finger program.  It prints out useful information about users
X * by digging it up from various system files.  It is not very portable
X * because the most useful parts of the information (the full user name,
X * office, and phone numbers) are all stored in the VAX-unused gecos field
X * of /etc/passwd, which, unfortunately, other UNIXes use for other things.
X *
X * There are three output formats, all of which give login name, teletype
X * line number, and login time.  The short output format is reminiscent
X * of finger on ITS, and gives one line of information per user containing
X * in addition to the minimum basic requirements (MBR), the full name of
X * the user, his idle time and office location and phone number.  The
X * quick style output is UNIX who-like, giving only name, teletype and
X * login time.  Finally, the long style output give the same information
X * as the short (in more legible format), the home directory and shell
X * of the user, and, if it exits, a copy of the file .plan in the users
X * home directory.  Finger may be called with or without a list of people
X * to finger -- if no list is given, all the people currently logged in
X * are fingered.
X *
X * The program is validly called by one of the following:
X *
X *	finger			{short form list of users}
X *	finger -l		{long form list of users}
X *	finger -b		{briefer long form list of users}
X *	finger -q		{quick list of users}
X *	finger -i		{quick list of users with idle times}
X *	finger namelist		{long format list of specified users}
X *	finger -s namelist	{short format list of specified users}
X *	finger -w namelist	{narrow short format list of specified users}
X *
X * where 'namelist' is a list of users login names.
X * The other options can all be given after one '-', or each can have its
X * own '-'.  The -f option disables the printing of headers for short and
X * quick outputs.  The -b option briefens long format outputs.  The -p
X * option turns off plans for long format outputs.
X */
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <utmp.h>
X#include <sys/signal.h>
X#include <pwd.h>
X#include <stdio.h>
X#include <ctype.h>
X#ifdef	USG
X#include <time.h>
X#else
X#include <lastlog.h>
X#include <sys/time.h>
X#endif
X#include <sys/socket.h>
X#include <netinet/in.h>
X#include <netdb.h>
X
X#define ASTERISK	'*'		/* ignore this in real name */
X#define COMMA		','		/* separator in pw_gecos field */
X#define COMMAND		'-'		/* command line flag char */
X#define CORY		'C'		/* cory hall office */
X#define EVANS		'E'		/* evans hall office */
X#define SAMENAME	'&'		/* repeat login name in real name */
X#define TALKABLE	0220		/* tty is writable if 220 mode */
X
Xstruct utmp user;
X#define NMAX sizeof(user.ut_name)
X#define LMAX sizeof(user.ut_line)
X#define HMAX sizeof(user.ut_host)
X
Xstruct person {			/* one for each person fingered */
X	char *name;			/* name */
X	char tty[LMAX+1];		/* null terminated tty line */
X	char host[HMAX+1];		/* null terminated remote host name */
X	long loginat;			/* time of (last) login */
X	long idletime;			/* how long idle (if logged in) */
X	char *realname;			/* pointer to full name */
X	char *office;			/* pointer to office name */
X	char *officephone;		/* pointer to office phone no. */
X	char *homephone;		/* pointer to home phone no. */
X	char *random;			/* for any random stuff in pw_gecos */
X	struct passwd *pwd;		/* structure of /etc/passwd stuff */
X	char loggedin;			/* person is logged in */
X	char writable;			/* tty is writable */
X	char original;			/* this is not a duplicate entry */
X	struct person *link;		/* link to next person */
X};
X
X#ifdef	USG
Xchar LASTLOG[] = "/etc/wtmp";
X#else
Xchar LASTLOG[] = "/usr/adm/lastlog";	/* last login info */
X#endif
Xchar USERLOG[] = "/etc/utmp";		/* who is logged in */
Xchar PLAN[] = "/.plan";			/* what plan file is */
Xchar PROJ[] = "/.project";		/* what project file */
X	
Xint unbrief = 1;			/* -b option default */
Xint header = 1;				/* -f option default */
Xint hack = 1;				/* -h option default */
Xint idle = 0;				/* -i option default */
Xint large = 0;				/* -l option default */
Xint match = 1;				/* -m option default */
Xint plan = 1;				/* -p option default */
Xint unquick = 1;			/* -q option default */
Xint small = 0;				/* -s option default */
Xint wide = 1;				/* -w option default */
X
Xint unshort;
Xint lf;					/* LASTLOG file descriptor */
Xstruct person *person1;			/* list of people */
Xlong tloc;				/* current time */
X
Xstruct passwd *pwdcopy();
Xchar *strcpy();
Xchar *malloc();
Xchar *ctime();
X
Xmain(argc, argv)
X	int argc;
X	register char **argv;
X{
X	FILE *fp;
X	register char *s;
X
X	/* parse command line for (optional) arguments */
X	while (*++argv && **argv == COMMAND)
X		for (s = *argv + 1; *s; s++)
X			switch (*s) {
X			case 'b':
X				unbrief = 0;
X				break;
X			case 'f':
X				header = 0;
X				break;
X			case 'h':
X				hack = 0;
X				break;
X			case 'i':
X				idle = 1;
X				unquick = 0;
X				break;
X			case 'l':
X				large = 1;
X				break;
X			case 'm':
X				match = 0;
X				break;
X			case 'p':
X				plan = 0;
X				break;
X			case 'q':
X				unquick = 0;
X				break;
X			case 's':
X				small = 1;
X				break;
X			case 'w':
X				wide = 0;
X				break;
X			default:
X				fprintf(stderr, "Usage: finger [-bfhilmpqsw] [login1 [login2 ...] ]\n");
X				exit(1);
X			}
X	if (unquick || idle)
X		time(&tloc);
X	/*
X	 * *argv == 0 means no names given
X	 */
X	if (*argv == 0)
X		doall();
X	else
X		donames(argv);
X	if (person1)
X		print();
X	exit(0);
X}
X
Xdoall()
X{
X	register struct person *p;
X	register struct passwd *pw;
X	int uf;
X	char name[NMAX + 1];
X
X	unshort = large;
X	if ((uf = open(USERLOG, 0)) < 0) {
X		fprintf(stderr, "finger: error opening %s\n", USERLOG);
X		exit(2);
X	}
X	if (unquick) {
X#ifndef	USG
X		extern _pw_stayopen;
X#endif
X		setpwent();
X#ifndef	USG
X		_pw_stayopen = 1;
X#endif
X		fwopen();
X	}
X	while (read(uf, (char *)&user, sizeof user) == sizeof user) {
X#ifdef	USG
X		if (user.ut_name[0] == 0 || user.ut_type != USER_PROCESS)
X#else
X		if (user.ut_name[0] == 0)
X#endif
X			continue;
X		if (person1 == 0)
X			p = person1 = (struct person *) malloc(sizeof *p);
X		else {
X			p->link = (struct person *) malloc(sizeof *p);
X			p = p->link;
X		}
X		bcopy(user.ut_name, name, NMAX);
X		name[NMAX] = 0;
X		bcopy(user.ut_line, p->tty, LMAX);
X		p->tty[LMAX] = 0;
X		bcopy(user.ut_host, p->host, HMAX);
X		p->host[HMAX] = 0;
X		p->loginat = user.ut_time;
X		p->pwd = 0;
X		p->loggedin = 1;
X		if (unquick && (pw = getpwnam(name))) {
X			p->pwd = pwdcopy(pw);
X			decode(p);
X			p->name = p->pwd->pw_name;
X		} else
X			p->name = strcpy(malloc(strlen(name) + 1), name);
X	}
X	if (unquick) {
X		fwclose();
X		endpwent();
X	}
X	close(uf);
X	if (person1 == 0) {
X		printf("No one logged on\n");
X		return;
X	}
X	p->link = 0;
X}
X
Xdonames(argv)
X	char **argv;
X{
X	register struct person *p;
X	register struct passwd *pw;
X	int uf;
X
X	/*
X	 * get names from command line and check to see if they're
X	 * logged in
X	 */
X	unshort = !small;
X	for (; *argv != 0; argv++) {
X		if (netfinger(*argv))
X			continue;
X		if (person1 == 0)
X			p = person1 = (struct person *) malloc(sizeof *p);
X		else {
X			p->link = (struct person *) malloc(sizeof *p);
X			p = p->link;
X		}
X		p->name = *argv;
X		p->loggedin = 0;
X		p->original = 1;
X		p->pwd = 0;
X	}
X	if (person1 == 0)
X		return;
X	p->link = 0;
X	/*
X	 * if we are doing it, read /etc/passwd for the useful info
X	 */
X	if (unquick) {
X		setpwent();
X		if (!match) {
X#ifndef	USG
X			extern _pw_stayopen;
X
X			_pw_stayopen = 1;
X#endif
X			for (p = person1; p != 0; p = p->link)
X				if (pw = getpwnam(p->name))
X					p->pwd = pwdcopy(pw);
X		} else while ((pw = getpwent()) != 0) {
X			for (p = person1; p != 0; p = p->link) {
X				if (!p->original)
X					continue;
X				if (strcmp(p->name, pw->pw_name) != 0 &&
X				    !matchcmp(pw->pw_gecos, pw->pw_name, p->name))
X					continue;
X				if (p->pwd == 0)
X					p->pwd = pwdcopy(pw);
X				else {
X					struct person *new;
X					/*
X					 * handle multiple login names, insert
X					 * new "duplicate" entry behind
X					 */
X					new = (struct person *)
X						malloc(sizeof *new);
X					new->pwd = pwdcopy(pw);
X					new->name = p->name;
X					new->original = 1;
X					new->loggedin = 0;
X					new->link = p->link;
X					p->original = 0;
X					p->link = new;
X					p = new;
X				}
X			}
X		}
X		endpwent();
X	}
X	/* Now get login information */
X	if ((uf = open(USERLOG, 0)) < 0) {
X		fprintf(stderr, "finger: error opening %s\n", USERLOG);
X		exit(2);
X	}
X	while (read(uf, (char *)&user, sizeof user) == sizeof user) {
X#ifdef USG
X		if (*user.ut_name == 0 || user.ut_type != USER_PROCESS)
X#else
X		if (*user.ut_name == 0)
X#endif
X			continue;
X		for (p = person1; p != 0; p = p->link) {
X			if (p->loggedin == 2)
X				continue;
X			if (strncmp(p->pwd ? p->pwd->pw_name : p->name,
X				    user.ut_name, NMAX) != 0)
X				continue;
X			if (p->loggedin == 0) {
X				bcopy(user.ut_line, p->tty, LMAX);
X				p->tty[LMAX] = 0;
X				bcopy(user.ut_host, p->host, HMAX);
X				p->host[HMAX] = 0;
X				p->loginat = user.ut_time;
X				p->loggedin = 1;
X			} else {	/* p->loggedin == 1 */
X				struct person *new;
X				new = (struct person *) malloc(sizeof *new);
X				new->name = p->name;
X				bcopy(user.ut_line, new->tty, LMAX);
X				new->tty[LMAX] = 0;
X				bcopy(user.ut_host, new->host, HMAX);
X				new->host[HMAX] = 0;
X				new->loginat = user.ut_time;
X				new->pwd = p->pwd;
X				new->loggedin = 1;
X				new->original = 0;
X				new->link = p->link;
X				p->loggedin = 2;
X				p->link = new;
X				p = new;
X			}
X		}
X	}
X	close(uf);
X	if (unquick) {
X		fwopen();
X		for (p = person1; p != 0; p = p->link)
X			decode(p);
X		fwclose();
X	}
X}
X
Xprint()
X{
X	register FILE *fp;
X	register struct person *p;
X	register char *s;
X	register c;
X
X	/*
X	 * print out what we got
X	 */
X	if (header) {
X		if (unquick) {
X			if (!unshort)
X				if (wide)
X					printf("Login       Name              TTY Idle    When            Office\n");
X				else
X					printf("Login    TTY Idle    When            Office\n");
X		} else {
X			printf("Login      TTY            When");
X			if (idle)
X				printf("             Idle");
X			putchar('\n');
X		}
X	}
X	for (p = person1; p != 0; p = p->link) {
X		if (!unquick) {
X			quickprint(p);
X			continue;
X		}
X		if (!unshort) {
X			shortprint(p);
X			continue;
X		}
X		personprint(p);
X		if (p->pwd != 0) {
X			if (hack) {
X				s = malloc(strlen(p->pwd->pw_dir) +
X					sizeof PROJ);
X				strcpy(s, p->pwd->pw_dir);
X				strcat(s, PROJ);
X				if ((fp = fopen(s, "r")) != 0) {
X					printf("Project: ");
X					while ((c = getc(fp)) != EOF) {
X						if (c == '\n')
X							break;
X						if (isprint(c) || isspace(c))
X							putchar(c);
X						else
X							putchar(c ^ 100);
X					}
X					fclose(fp);
X					putchar('\n');
X				}
X				free(s);
X			}
X			if (plan) {
X				s = malloc(strlen(p->pwd->pw_dir) +
X					sizeof PLAN);
X				strcpy(s, p->pwd->pw_dir);
X				strcat(s, PLAN);
X				if ((fp = fopen(s, "r")) == 0)
X					printf("No Plan.\n");
X				else {
X					printf("Plan:\n");
X					while ((c = getc(fp)) != EOF)
X						if (isprint(c) || isspace(c))
X							putchar(c);
X						else
X							putchar(c ^ 100);
X					fclose(fp);
X				}
X				free(s);
X			}
X		}
X		if (p->link != 0)
X			putchar('\n');
X	}
X}
X
X/*
X * Duplicate a pwd entry.
X * Note: Only the useful things (what the program currently uses) are copied.
X */
Xstruct passwd *
Xpwdcopy(pfrom)
X	register struct passwd *pfrom;
X{
X	register struct passwd *pto;
X
X	pto = (struct passwd *) malloc(sizeof *pto);
X#define savestr(s) strcpy(malloc(strlen(s) + 1), s)
X	pto->pw_name = savestr(pfrom->pw_name);
X	pto->pw_uid = pfrom->pw_uid;
X	pto->pw_gecos = savestr(pfrom->pw_gecos);
X	pto->pw_dir = savestr(pfrom->pw_dir);
X	pto->pw_shell = savestr(pfrom->pw_shell);
X#undef savestr
X	return pto;
X}
X
X/*
X * print out information on quick format giving just name, tty, login time
X * and idle time if idle is set.
X */
Xquickprint(pers)
X	register struct person *pers;
X{
X	printf("%-*.*s  ", NMAX, NMAX, pers->name);
X	if (pers->loggedin) {
X		if (idle) {
X			findidle(pers);
X			printf("%c%-*s %-16.16s", pers->writable ? ' ' : '*',
X				LMAX, pers->tty, ctime(&pers->loginat));
X			ltimeprint("   ", &pers->idletime, "");
X		} else
X			printf(" %-*s %-16.16s", LMAX,
X				pers->tty, ctime(&pers->loginat));
X		putchar('\n');
X	} else
X		printf("          Not Logged In\n");
X}
X
X/*
X * print out information in short format, giving login name, full name,
X * tty, idle time, login time, office location and phone.
X */
Xshortprint(pers)
X	register struct person *pers;
X{
X	char *p;
X	char dialup;
X
X	if (pers->pwd == 0) {
X		printf("%-15s       ???\n", pers->name);
X		return;
X	}
X	printf("%-*s", NMAX, pers->pwd->pw_name);
X	dialup = 0;
X	if (wide) {
X		if (pers->realname)
X			printf(" %-20.20s", pers->realname);
X		else
X			printf("        ???          ");
X	}
X	putchar(' ');
X	if (pers->loggedin && !pers->writable)
X		putchar('*');
X	else
X		putchar(' ');
X	if (*pers->tty) {
X		if (pers->tty[0] == 't' && pers->tty[1] == 't' &&
X		    pers->tty[2] == 'y') {
X			if (pers->tty[3] == 'd' && pers->loggedin)
X				dialup = 1;
X			printf("%-2.2s ", pers->tty + 3);
X		} else
X			printf("%-2.2s ", pers->tty);
X	} else
X		printf("   ");
X	p = ctime(&pers->loginat);
X	if (pers->loggedin) {
X		stimeprint(&pers->idletime);
X		printf(" %3.3s %-5.5s ", p, p + 11);
X	} else if (pers->loginat == 0)
X		printf(" < .  .  .  . >");
X	else if (tloc - pers->loginat >= 180 * 24 * 60 * 60)
X		printf(" <%-6.6s, %-4.4s>", p + 4, p + 20);
X	else
X		printf(" <%-12.12s>", p + 4);
X	if (dialup && pers->homephone)
X		printf(" %20s", pers->homephone);
X	else {
X		if (pers->office)
X			printf(" %-11.11s", pers->office);
X		else if (pers->officephone || pers->homephone)
X			printf("            ");
X		if (pers->officephone)
X			printf(" %s", pers->officephone);
X		else if (pers->homephone)
X			printf(" %s", pers->homephone);
X	}
X	putchar('\n');
X}
X
X/*
X * print out a person in long format giving all possible information.
X * directory and shell are inhibited if unbrief is clear.
X */
Xpersonprint(pers)
X	register struct person *pers;
X{
X	if (pers->pwd == 0) {
X		printf("Login name: %-10s\t\t\tIn real life: ???\n",
X			pers->name);
X		return;
X	}
X	printf("Login name: %-10s", pers->pwd->pw_name);
X	if (pers->loggedin && !pers->writable)
X		printf("	(messages off)	");
X	else
X		printf("			");
X	if (pers->realname)
X		printf("In real life: %s", pers->realname);
X	if (pers->office) {
X		printf("\nOffice: %-.11s", pers->office);
X		if (pers->officephone) {
X			printf(", %s", pers->officephone);
X			if (pers->homephone)
X				printf("\t\tHome phone: %s", pers->homephone);
X			else if (pers->random)
X				printf("\t\t%s", pers->random);
X		} else
X			if (pers->homephone)
X				printf("\t\t\tHome phone: %s", pers->homephone);
X			else if (pers->random)
X				printf("\t\t\t%s", pers->random);
X	} else if (pers->officephone) {
X		printf("\nPhone: %s", pers->officephone);
X		if (pers->homephone)
X			printf(", %s", pers->homephone);
X		if (pers->random)
X			printf(", %s", pers->random);
X	} else if (pers->homephone) {
X		printf("\nPhone: %s", pers->homephone);
X		if (pers->random)
X			printf(", %s", pers->random);
X	} else if (pers->random)
X		printf("\n%s", pers->random);
X	if (unbrief) {
X		printf("\nDirectory: %-25s", pers->pwd->pw_dir);
X		if (*pers->pwd->pw_shell)
X			printf("\tShell: %-s", pers->pwd->pw_shell);
X	}
X	if (pers->loggedin) {
X		register char *ep = ctime(&pers->loginat);
X		if (*pers->host) {
X			printf("\nOn since %15.15s on %s from %s",
X				&ep[4], pers->tty, pers->host);
X			ltimeprint("\n", &pers->idletime, " Idle Time");
X		} else {
X			printf("\nOn since %15.15s on %-*s",
X				&ep[4], LMAX, pers->tty);
X			ltimeprint("\t", &pers->idletime, " Idle Time");
X		}
X	} else if (pers->loginat == 0)
X		printf("\nNever logged in.");
X	else if (tloc - pers->loginat > 180 * 24 * 60 * 60) {
X		register char *ep = ctime(&pers->loginat);
X		printf("\nLast login %10.10s, %4.4s on %s",
X			ep, ep+20, pers->tty);
X		if (*pers->host)
X			printf(" from %s", pers->host);
X	} else {
X		register char *ep = ctime(&pers->loginat);
X		printf("\nLast login %16.16s on %s", ep, pers->tty);
X		if (*pers->host)
X			printf(" from %s", pers->host);
X	}
X	putchar('\n');
X}
X
X/*
X *  very hacky section of code to format phone numbers.  filled with
X *  magic constants like 4, 7 and 10.
X */
Xchar *
Xphone(s, len, alldigits)
X	register char *s;
X	int len;
X	char alldigits;
X{
X	char fonebuf[15];
X	register char *p = fonebuf;
X	register i;
X
X	if (!alldigits)
X		return (strcpy(malloc(len + 1), s));
X	switch (len) {
X	case 4:
X		*p++ = ' ';
X		*p++ = 'x';
X		*p++ = '2';
X		*p++ = '-';
X		for (i = 0; i < 4; i++)
X			*p++ = *s++;
X		break;
X	case 5:
X		*p++ = ' ';
X		*p++ = 'x';
X		*p++ = *s++;
X		*p++ = '-';
X		for (i = 0; i < 4; i++)
X			*p++ = *s++;
X		break;
X	case 7:
X		for (i = 0; i < 3; i++)
X			*p++ = *s++;
X		*p++ = '-';
X		for (i = 0; i < 4; i++)
X			*p++ = *s++;
X		break;
X	case 10:
X		for (i = 0; i < 3; i++)
X			*p++ = *s++;
X		*p++ = '-';
X		for (i = 0; i < 3; i++)
X			*p++ = *s++;
X		*p++ = '-';
X		for (i = 0; i < 4; i++)
X			*p++ = *s++;
X		break;
X	case 0:
X		return 0;
X	default:
X		return (strcpy(malloc(len + 1), s));
X	}
X	*p++ = 0;
X	return (strcpy(malloc(p - fonebuf), fonebuf));
X}
X
X/*
X * decode the information in the gecos field of /etc/passwd
X */
Xdecode(pers)
X	register struct person *pers;
X{
X	char buffer[256];
X	register char *bp, *gp, *lp;
X	int alldigits;
X	int hasspace;
X	int len;
X
X	pers->realname = 0;
X	pers->office = 0;
X	pers->officephone = 0;
X	pers->homephone = 0;
X	pers->random = 0;
X	if (pers->pwd == 0)
X		return;
X	gp = pers->pwd->pw_gecos;
X	bp = buffer;
X	if (*gp == ASTERISK)
X		gp++;
X	while (*gp && *gp != COMMA)			/* name */
X		if (*gp == SAMENAME) {
X			lp = pers->pwd->pw_name;
X			if (islower(*lp))
X				*bp++ = toupper(*lp++);
X			while (*bp++ = *lp++)
X				;
X			bp--;
X			gp++;
X		} else
X			*bp++ = *gp++;
X	*bp++ = 0;
X	if ((len = bp - buffer) > 1)
X		pers->realname = strcpy(malloc(len), buffer);
X	if (*gp == COMMA) {				/* office */
X		gp++;
X		hasspace = 0;
X		bp = buffer;
X		while (*gp && *gp != COMMA) {
X			*bp = *gp++;
X			if (*bp == ' ')
X				hasspace = 1;
X			/* leave 5 for Cory and Evans expansion */
X			if (bp < buffer + sizeof buffer - 6)
X				bp++;
X		}
X		*bp = 0;
X		len = bp - buffer;
X		bp--;			/* point to last character */
X		if (hasspace || len == 0)
X			len++;
X		else if (*bp == CORY) {
X			strcpy(bp, " Cory");
X			len += 5;
X		} else if (*bp == EVANS) {
X			strcpy(bp, " Evans");
X			len += 6;
X		} else
X			len++;
X		if (len > 1)
X			pers->office = strcpy(malloc(len), buffer);
X	}
X	if (*gp == COMMA) {				/* office phone */
X		gp++;
X		bp = buffer;
X		alldigits = 1;
X		while (*gp && *gp != COMMA) {
X			*bp = *gp++;
X			if (!isdigit(*bp))
X				alldigits = 0;
X			if (bp < buffer + sizeof buffer - 1)
X				bp++;
X		}
X		*bp = 0;
X		pers->officephone = phone(buffer, bp - buffer, alldigits);
X	}
X	if (*gp == COMMA) {				/* home phone */
X		gp++;
X		bp = buffer;
X		alldigits = 1;
X		while (*gp && *gp != COMMA) {
X			*bp = *gp++;
X			if (!isdigit(*bp))
X				alldigits = 0;
X			if (bp < buffer + sizeof buffer - 1)
X				bp++;
X		}
X		*bp = 0;
X		pers->homephone = phone(buffer, bp - buffer, alldigits);
X	}
X	if (pers->loggedin)
X		findidle(pers);
X	else
X		findwhen(pers);
X}
X
X/*
X * find the last log in of a user by checking the LASTLOG file.
X * the entry is indexed by the uid, so this can only be done if
X * the uid is known (which it isn't in quick mode)
X */
X
Xfwopen()
X{
X	if ((lf = open(LASTLOG, 0)) < 0)
X		fprintf(stderr, "finger: %s open error\n", LASTLOG);
X}
X
Xfindwhen(pers)
X	register struct person *pers;
X{
X#ifdef	USG
X	struct utmp    ut;
X#else
X	struct lastlog ll;
X#endif
X	int i;
X
X	if (lf >= 0) {
X#ifdef USG
X		lseek(lf, (long) (sizeof ut), 2);
X		while (lseek(lf, (long) (-2 * sizeof ut), 1) >= 0) {
X			read(lf, (char *)&ut, sizeof ut);
X			if (ut.ut_type == USER_PROCESS &&
X			    !strncmp(ut.ut_user, pers->name)) {
X				bcopy(ut.ut_line, pers->tty,  LMAX);
X				pers->tty[LMAX] = '\0';
X				bcopy(ut.ut_host, pers->host, HMAX);
X				pers->host[HMAX] = '\0';
X				pers->loginat = ut.ut_time;
X				return;
X			} else 
X				continue;
X		}
X		pers->tty[0] = 0;
X		pers->host[0] = 0;
X		pers->loginat = 0L;
X#else
X		lseek(lf, (long)pers->pwd->pw_uid * sizeof ll, 0);
X		if ((i = read(lf, (char *)&ll, sizeof ll)) == sizeof ll) {
X			bcopy(ll.ll_line, pers->tty, LMAX);
X			pers->tty[LMAX] = 0;
X			bcopy(ll.ll_host, pers->host, HMAX);
X			pers->host[HMAX] = 0;
X			pers->loginat = ll.ll_time;
X		} else {
X			if (i != 0)
X				fprintf(stderr, "finger: %s read error\n",
X					LASTLOG);
X			pers->tty[0] = 0;
X			pers->host[0] = 0;
X			pers->loginat = 0L;
X		}
X#endif
X	} else {
X		pers->tty[0] = 0;
X		pers->host[0] = 0;
X		pers->loginat = 0L;
X	}
X}
X
Xfwclose()
X{
X	if (lf >= 0)
X		close(lf);
X}
X
X/*
X * find the idle time of a user by doing a stat on /dev/tty??,
X * where tty?? has been gotten from USERLOG, supposedly.
X */
Xfindidle(pers)
X	register struct person *pers;
X{
X	struct stat ttystatus;
X	static char buffer[20] = "/dev/";
X	long t;
X#define TTYLEN 5
X
X	strcpy(buffer + TTYLEN, pers->tty);
X	buffer[TTYLEN+LMAX] = 0;
X	if (stat(buffer, &ttystatus) < 0) {
X		fprintf(stderr, "finger: Can't stat %s\n", buffer);
X		exit(4);
X	}
X	time(&t);
X	if (t < ttystatus.st_atime)
X		pers->idletime = 0L;
X	else
X		pers->idletime = t - ttystatus.st_atime;
X	pers->writable = (ttystatus.st_mode & TALKABLE) == TALKABLE;
X}
X
X/*
X * print idle time in short format; this program always prints 4 characters;
X * if the idle time is zero, it prints 4 blanks.
X */
Xstimeprint(dt)
X	long *dt;
X{
X	register struct tm *delta;
X
X	delta = gmtime(dt);
X	if (delta->tm_yday == 0)
X		if (delta->tm_hour == 0)
X			if (delta->tm_min == 0)
X				printf("    ");
X			else
X				printf("  %2d", delta->tm_min);
X		else
X			if (delta->tm_hour >= 10)
X				printf("%3d:", delta->tm_hour);
X			else
X				printf("%1d:%02d",
X					delta->tm_hour, delta->tm_min);
X	else
X		printf("%3dd", delta->tm_yday);
X}
X
X/*
X * print idle time in long format with care being taken not to pluralize
X * 1 minutes or 1 hours or 1 days.
X * print "prefix" first.
X */
Xltimeprint(before, dt, after)
X	long *dt;
X	char *before, *after;
X{
X	register struct tm *delta;
X
X	delta = gmtime(dt);
X	if (delta->tm_yday == 0 && delta->tm_hour == 0 && delta->tm_min == 0 &&
X	    delta->tm_sec <= 10)
X		return (0);
X	printf("%s", before);
X	if (delta->tm_yday >= 10)
X		printf("%d days", delta->tm_yday);
X	else if (delta->tm_yday > 0)
X		printf("%d day%s %d hour%s",
X			delta->tm_yday, delta->tm_yday == 1 ? "" : "s",
X			delta->tm_hour, delta->tm_hour == 1 ? "" : "s");
X	else
X		if (delta->tm_hour >= 10)
X			printf("%d hours", delta->tm_hour);
X		else if (delta->tm_hour > 0)
X			printf("%d hour%s %d minute%s",
X				delta->tm_hour, delta->tm_hour == 1 ? "" : "s",
X				delta->tm_min, delta->tm_min == 1 ? "" : "s");
X		else
X			if (delta->tm_min >= 10)
X				printf("%2d minutes", delta->tm_min);
X			else if (delta->tm_min == 0)
X				printf("%2d seconds", delta->tm_sec);
X			else
X				printf("%d minute%s %d second%s",
X					delta->tm_min,
X					delta->tm_min == 1 ? "" : "s",
X					delta->tm_sec,
X					delta->tm_sec == 1 ? "" : "s");
X	printf("%s", after);
X}
X
Xmatchcmp(gname, login, given)
X	register char *gname;
X	char *login;
X	char *given;
X{
X	char buffer[100];
X	register char *bp, *lp;
X	register c;
X
X	if (*gname == ASTERISK)
X		gname++;
X	lp = 0;
X	bp = buffer;
X	for (;;)
X		switch (c = *gname++) {
X		case SAMENAME:
X			for (lp = login; bp < buffer + sizeof buffer
X					 && (*bp++ = *lp++);)
X				;
X			bp--;
X			break;
X		case ' ':
X		case COMMA:
X		case '\0':
X			*bp = 0;
X			if (namecmp(buffer, given))
X				return (1);
X			if (c == COMMA || c == 0)
X				return (0);
X			bp = buffer;
X			break;
X		default:
X			if (bp < buffer + sizeof buffer)
X				*bp++ = c;
X		}
X	/*NOTREACHED*/
X}
X
Xnamecmp(name1, name2)
X	register char *name1, *name2;
X{
X	register c1, c2;
X
X	for (;;) {
X		c1 = *name1++;
X		if (islower(c1))
X			c1 = toupper(c1);
X		c2 = *name2++;
X		if (islower(c2))
X			c2 = toupper(c2);
X		if (c1 != c2)
X			break;
X		if (c1 == 0)
X			return (1);
X	}
X	if (!c1) {
X		for (name2--; isdigit(*name2); name2++)
X			;
X		if (*name2 == 0)
X			return (1);
X	} else if (!c2) {
X		for (name1--; isdigit(*name1); name1++)
X			;
X		if (*name2 == 0)
X			return (1);
X	}
X	return (0);
X}
X
Xnetfinger(name)
X	char *name;
X{
X	char *host;
X	char fname[100];
X	struct hostent *hp;
X	struct servent *sp;
X	struct sockaddr_in sin;
X	int s;
X	char *rindex();
X	register FILE *f;
X	register int c;
X	register int lastc;
X
X	if (name == NULL)
X		return (0);
X	host = rindex(name, '@');
X	if (host == NULL)
X		return (0);
X	*host++ = 0;
X	hp = gethostbyname(host);
X	if (hp == NULL) {
X		static struct hostent def;
X		static struct in_addr defaddr;
X		static char *alist[1];
X		static char namebuf[128];
X		int inet_addr();
X
X		defaddr.s_addr = inet_addr(host);
X		if (defaddr.s_addr == -1) {
X			printf("unknown host: %s\n", host);
X			return (1);
X		}
X		strcpy(namebuf, host);
X		def.h_name = namebuf;
X		def.h_addr_list = alist, def.h_addr = (char *)&defaddr;
X		def.h_length = sizeof (struct in_addr);
X		def.h_addrtype = AF_INET;
X		def.h_aliases = 0;
X		hp = &def;
X	}
X	sp = getservbyname("finger", "tcp");
X	if (sp == 0) {
X		printf("tcp/finger: unknown service\n");
X		return (1);
X	}
X	sin.sin_family = hp->h_addrtype;
X	bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
X	sin.sin_port = sp->s_port;
X	s = socket(hp->h_addrtype, SOCK_STREAM, 0);
X	if (s < 0) {
X		perror("socket");
X		return (1);
X	}
X	printf("[%s]\n", hp->h_name);
X	fflush(stdout);
X	if (connect(s, (char *)&sin, sizeof (sin)) < 0) {
X		perror("connect");
X		close(s);
X		return (1);
X	}
X	if (large) write(s, "/W ", 3);
X	write(s, name, strlen(name));
X	write(s, "\r\n", 2);
X	f = fdopen(s, "r");
X	while ((c = getc(f)) != EOF) {
X		switch(c) {
X		case 0210:
X		case 0211:
X		case 0212:
X		case 0214:
X			c -= 0200;
X			break;
X		case 0215:
X			c = '\n';
X			break;
X		}
X		lastc = c;
X		if (isprint(c) || isspace(c))
X			putchar(c);
X		else
X			putchar(c ^ 100);
X	}
X	if (lastc != '\n')
X		putchar('\n');
X	(void)fclose(f);
X	return (1);
X}
END_OF_FILE
if test 25816 -ne `wc -c <'finger.c'`; then
    echo shar: \"'finger.c'\" unpacked with wrong size!
fi
# end of 'finger.c'
fi
echo shar: End of shell archive.
exit 0
-- 
"Do not meddle in the affairs of cats,
 for they are subtle and will piss on your computer."
				-- stolen from Brian Gollum