[comp.sys.hp] FINGER HP800's

spirit@uxe.cso.uiuc.edu (11/29/89)

Hello,

	Anybody have "finger" running on their HP9000/800 series?
I was going to set it up on ours, but I couldn't find a "fingerd"
for the /etc/inetd.conf entry.  Is this service NOT supported by
HP???

			ADVthanksANCE,
			John Kemp
			--------------------------------------
			University of Illinois
			Department of Atmospheric Sciences
			kemp@uiatma.atmos.uiuc.edu
			--------------------------------------

irf@kuling.UUCP (Bo Thide') (11/29/89)

In article <47500018@uxe.cso.uiuc.edu> spirit@uxe.cso.uiuc.edu writes:
>Hello,
>
>	Anybody have "finger" running on their HP9000/800 series?
>I was going to set it up on ours, but I couldn't find a "fingerd"
>for the /etc/inetd.conf entry.  Is this service NOT supported by
>HP???


We have "finger" running on all our HP9000/300 and 800 series workstations.
It is shipped with HP-UX.  Since "finger" is included in the manual I
don't see why it shouldn't be supported by HP.  Of course, there is still
problems with phone numbers having non-US format in the GCOS field but
that may perhaps be considered as a minor nuisance.


   ^   Bo Thide'--------------------------------------------------------------
  | |       Swedish Institute of Space Physics, S-755 91 Uppsala, Sweden
  |I|    [In Swedish: Institutet f|r RymdFysik, Uppsalaavdelningen (IRFU)]
  |R|  Phone: (+46) 18-403000.  Telex: 76036 (IRFUPP S).  Fax: (+46) 18-403100 
 /|F|\        INTERNET: bt@irfu.se       UUCP: ...!uunet!sunic!irfu!bt
 ~~U~~ -----------------------------------------------------------------sm5dfw

tml@hemuli.atk.vtt.fi (Tor Lillqvist) (11/30/89)

In article <1275@kuling.UUCP> irf@kuling.UUCP (Bo Thide') writes:
>We have "finger" running on all our HP9000/300 and 800 series workstations.
>It is shipped with HP-UX.  Since "finger" is included in the manual I
>don't see why it shouldn't be supported by HP.  Of course, there is still
>problems with phone numbers having non-US format in the GCOS field but
>that may perhaps be considered as a minor nuisance.

Yes, but the finger HP ships is a *local-host only* version.  The
program that usually is known as "finger" is able to contact finger
daemons on remote hosts.  In fact, that is what it usually is used
for.  After all, normally you see the users on your local machine each
day and can ask them face-to-face what (.)plans they have and what
(.)projects they are involved in.
-- 
Tor Lillqvist, VTT/ATK

kim@kannel.lut.fi (Kimmo Suominen) (11/30/89)

Here's a fingerd that works fine for me.  The port number for /etc/services is

	finger	79/tcp	# Networked finger

and an entry in /etc/inetd.conf might look like

	finger	stream tcp nowait guest /usr/local/lib/fingerd	fingerd

This is the code, enjoy.  Watch out for the signature.

--- CUT HERE --- CUT HERE --- CUT HERE --- CUT HERE --- CUT HERE ---

/*
 * Copyright (c) 1983 The Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by the University of California, Berkeley.  The name of the
 * University may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

#ifndef lint
char copyright[] =
"@(#) Copyright (c) 1983 The Regents of the University of California.\n\
 All rights reserved.\n";
#endif /* not lint */

#ifndef lint
static char sccsid[] = "@(#)fingerd.c    5.3 (Berkeley) 11/3/88";
#endif /* not lint */

/*
 * Finger server.
 */
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <ctype.h>

main(argc, argv)
    int argc;
    char *argv[];
{
    register char *sp;
    char line[512];
    struct sockaddr_in sin;
    int i, p[2], pid, status;
    FILE *fp;
    char *av[4];

    i = sizeof (sin);
    if (getpeername(0, &sin, &i) < 0)
        fatal(argv[0], "getpeername");
    if (fgets(line, sizeof(line), stdin) == NULL)
        exit(1);
    sp = line;
    av[0] = "finger";
    for (i = 1;;) {
        while (isspace(*sp))
            sp++;
        if (!*sp)
            break;
        if (*sp == '/' && (sp[1] == 'W' || sp[1] == 'w')) {
            sp += 2;
            av[i++] = "-l";
        }
        if (*sp && !isspace(*sp)) {
            av[i++] = sp;
            while (*sp && !isspace(*sp))
                sp++;
            *sp = '\0';
        }
    }
    av[i] = 0;
    if (pipe(p) < 0)
        fatal(argv[0], "pipe");
    if ((pid = fork()) == 0) {
        close(p[0]);
        if (p[1] != 1) {
            dup2(p[1], 1);
            close(p[1]);
        }
#ifdef hpux
        execv("/usr/local/bin/finger", av);
#else
        execv("/usr/ucb/finger", av);
#endif
        _exit(1);
    }
    if (pid == -1)
        fatal(argv[0], "fork");
    close(p[1]);
    if ((fp = fdopen(p[0], "r")) == NULL)
        fatal(argv[0], "fdopen");
    while ((i = getc(fp)) != EOF) {
        if (i == '\n')
            putchar('\r');
        putchar(i);
    }
    fclose(fp);
    while ((i = wait(&status)) != pid && i != -1)
        ;
    return(0);
}

fatal(prog, s)
    char *prog, *s;
{
    fprintf(stderr, "%s: ", prog);
    perror(s);
    exit(1);
}

--- CUT AGAIN --- CUT AGAIN --- CUT AGAIN --- CUT AGAIN --- CUT AGAIN ---

Regards

Kim
--
 ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
( Kimmo Suominen        ! Lappeenranta U of Technology ! kim@kannel.lut.fi )
( "That's what I think" ! Computing Centre  *  Finland ! Funet: KUULA::KIM )
 ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

saaf@joker.optics.rochester.edu (Lennart Saaf) (12/01/89)

In article <KIM.89Nov29150646@kannel.lut.fi> kim@kannel.lut.fi (Kimmo Suominen) writes:

> Here's a fingerd that works fine for me. 

Thank you! It works like a charm.  I had to make one small change on
our system because of the chunk of code below.

> #ifdef hpux
>          execv("/usr/local/bin/finger", av);
> #else
>          execv("/usr/ucb/finger", av);
> #endif

"hpux" was not defined in fingerd.c or any any of the include files
(as far as I could tell).  In addition, our finger is in /bin.  So I
replaced the above with:

   execv("/bin/finger", av);

With this done, I can now finger my HP from another machine.  But, of
course, the HP can't finger other machines.  Does anyone have a
finger.c (not fingerd.c) that has this capability?  Perhaps the finger
you have in /usr/local/bin, Kimmo?

Len
--
------------------------------------------------------------------------
| Len Saaf, The Institute of Optics, Univ. of Rochester, Rochester, NY |
| Internet: saaf@joker.optics.rochester.edu        Bitnet: SAAF@UOROPT |
------------------------------------------------------------------------

frank@tardis.ircc.ohio-state.EDU (Frank G. Fiamingo) (12/02/89)

Len Saaf writes:
>                                           But, of
>course, the HP can't finger other machines.  Does anyone have a
>finger.c (not fingerd.c) that has this capability? ...

Yes, I have one that I ftp'ed from hemuli.atk.vtt.fi.  They advertised
it as a port
of the BSD source to HP-UX.  And it is.  I found that it installed quite
easily on
an HP840, and I then made a few minor changes to have it check a user's
mailbox for
unread mail.  

I can e-mail you a copy if you'd like.

------------------------------------------
Frank Fiamingo, Instruction and Research Computer Center of the Ohio
State University
frank@hpuxa.ircc.ohio-state.edu
------------------------------------------

bd@hp-ses.SDE.HP.COM (Bob Desinger) (12/04/89)

> "hpux" was not defined in fingerd.c or any any of the include files
> (as far as I could tell).

It's not in any header file, but it is defined in /lib/cpp, the C
preprocessor.  So you get it "for free."  Another similar symbol
defined this way is hp9000s800.  On a Series 300 you get hp9000s300
defined.  This way you can get the right version:  code for any HP-UX
(both 800 and 300), just for the 800, or just for the 300.

Other vendors tend to define symbols for their machines too.
Sun uses "sun" and the VAX uses "vax".

-- bd

tml@hemuli.atk.vtt.fi (Tor Lillqvist) (12/04/89)

In article <KIM.89Dec4101011@kannel.lut.fi> kim@kannel.lut.fi (Kimmo Suominen) writes:
>it does show your last login CORRECTLY.  The one in hemuli shows actually your
>"first login".

Oops, I never noticed that...  Thanks. Here is a patch to your version
that fixes the followig feature: If you say "finger matti" and you
have several users with real first name Matti, you will see only one
of them.  Now it compares the login name when deciding which session
to show for a user.

*** finger.c	Mon Dec  4 14:54:01 1989
--- newfnger.c	Mon Dec  4 14:51:02 1989
***************
*** 442,457 ****
  			quickprint(p);
  			continue;
  		}
  		if (!unshort) {
  			shortprint(p);
  			continue;
  		}
- #define min(a,b) ((a) < (b) ? (a) : (b))
- 		for (q = person1, minidle = (((unsigned)~0)>>1); q != 0; q = q->link)
- 			if (!strcmp(q->name, p->name))
- 				minidle = min(minidle, q->idletime);
- 		if (p->idletime > minidle)
- 			continue;
  		personprint(p);
  		if (p->pwd != 0) {
  			if (hack) {
--- 442,461 ----
  			quickprint(p);
  			continue;
  		}
+ #define min(a,b) ((a) < (b) ? (a) : (b))
+ 	  	if (p->pwd) {
+ 			for (q = person1, minidle = (((unsigned)~0)>>1);
+ 			     q != 0; q = q->link)
+ 			      if (q->pwd != 0
+ 				  && !strcmp(q->pwd->pw_name, p->pwd->pw_name))
+ 				      minidle = min(minidle, q->idletime);
+ 			if (p->idletime > minidle)
+ 			    continue;
+ 		}
  		if (!unshort) {
  			shortprint(p);
  			continue;
  		}
  		personprint(p);
  		if (p->pwd != 0) {
  			if (hack) {
-- 
Tor Lillqvist, VTT/ATK

kim@kannel.lut.fi (Kimmo Suominen) (12/04/89)

I thought a few minutes later that you would propablu need this too.  They are
both originally from hemuli.atk.vtt.fi, but I've made some changes.

Use -DBSD43 when you compile this.  Add the resolver library if you want to
(if you have it).

Watch out for the signature and other comments in the end.

--- CUT HERE --- CUT HERE --- CUT HERE --- CUT HERE --- CUT HERE ---
/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
char copyright[] =
"@(#) Copyright (c) 1980 Regents of the University of California.\n\
 All rights reserved.\n";
#endif not lint

#ifndef lint
static char sccsid[] = "@(#)finger.c	5.8 (Berkeley) 3/13/86";
#endif not lint

/*
 * This is a finger program.  It prints out useful information about users
 * by digging it up from various system files.  It is not very portable
 * because the most useful parts of the information (the full user name,
 * office, and phone numbers) are all stored in the VAX-unused gecos field
 * of /etc/passwd, which, unfortunately, other UNIXes use for other things.
 *
 * There are three output formats, all of which give login name, teletype
 * line number, and login time.  The short output format is reminiscent
 * of finger on ITS, and gives one line of information per user containing
 * in addition to the minimum basic requirements (MBR), the full name of
 * the user, his idle time and office location and phone number.  The
 * quick style output is UNIX who-like, giving only name, teletype and
 * login time.  Finally, the long style output give the same information
 * as the short (in more legible format), the home directory and shell
 * of the user, and, if it exits, a copy of the file .plan in the users
 * home directory.  Finger may be called with or without a list of people
 * to finger -- if no list is given, all the people currently logged in
 * are fingered.
 *
 * The program is validly called by one of the following:
 *
 *	finger			{short form list of users}
 *	finger -l		{long form list of users}
 *	finger -b		{briefer long form list of users}
 *	finger -q		{quick list of users}
 *	finger -i		{quick list of users with idle times}
 *	finger namelist		{long format list of specified users}
 *	finger -s namelist	{short format list of specified users}
 *	finger -w namelist	{narrow short format list of specified users}
 *
 * where 'namelist' is a list of users login names.
 * The other options can all be given after one '-', or each can have its
 * own '-'.  The -f option disables the printing of headers for short and
 * quick outputs.  The -b option briefens long format outputs.  The -p
 * option turns off plans for long format outputs.
 */

/*
 * Change by pst@anise.acc.com (Paul Traina):  show only one instance
 * of a user on long displays.
 */

/*
 * Changed to read wtmp backwards by raussi@kannel.lut.fi (Heikki Raussi).
 */

/*
 * Added organization and cleaned up output.
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <utmp.h>
#include <sys/signal.h>
#include <pwd.h>
#include <stdio.h>
#include <string.h>
#if BSD
#include <lastlog.h>
#endif
#include <ctype.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#define bcopy(s,d,n) memcpy(d,s,n)
#define index(s,c) strchr(s,c)
#define rindex(s,c) strrchr(s,c)


#define ASTERISK	'*'		/* ignore this in real name */
#define COMMA		','		/* separator in pw_gecos field */
#define COMMAND		'-'		/* command line flag char */
#if UCB
#define CORY		'C'		/* cory hall office */
#define EVANS		'E'		/* evans hall office */
#endif
#define SAMENAME	'&'		/* repeat login name in real name */
#define TALKABLE	0220		/* tty is writable if 220 mode */

#define ORGANIZATION	"Lappeenranta University of Technology, Computing Centre, Finland\n\n"

struct utmp user;
#define NMAX sizeof(user.ut_name)
#define LMAX sizeof(user.ut_line)
#if BSD
#define HMAX sizeof(user.ut_host)
#endif

struct person {			/* one for each person fingered */
	char *name;			/* name */
	char tty[LMAX+1];		/* null terminated tty line */
#if BSD
	char host[HMAX+1];		/* null terminated remote host name */
#endif
	long loginat;			/* time of (last) login */
	long idletime;			/* how long idle (if logged in) */
	char *realname;			/* pointer to full name */
	char *office;			/* pointer to office name */
	char *officephone;		/* pointer to office phone no. */
	char *homephone;		/* pointer to home phone no. */
	char *random;			/* for any random stuff in pw_gecos */
	struct passwd *pwd;		/* structure of /etc/passwd stuff */
	char loggedin;			/* person is logged in */
	char writable;			/* tty is writable */
	char original;			/* this is not a duplicate entry */
	struct person *link;		/* link to next person */
};

#if BSD
char LASTLOG[] = "/usr/adm/lastlog";	/* last login info */
#endif
char USERLOG[] = "/etc/utmp";		/* who is logged in */
char PLAN[] = "/.plan";			/* what plan file is */
char PROJ[] = "/.project";		/* what project file */
	
int unbrief = 1;			/* -b option default */
int header = 1;				/* -f option default */
int hack = 1;				/* -h option default */
int idle = 0;				/* -i option default */
int large = 0;				/* -l option default */
int match = 1;				/* -m option default */
int plan = 1;				/* -p option default */
int unquick = 1;			/* -q option default */
int small = 0;				/* -s option default */
int wide = 1;				/* -w option default */

int unshort;
#if BSD
int lf;					/* LASTLOG file descriptor */
#endif
struct person *person1;			/* list of people */
long tloc;				/* current time */

struct passwd *pwdcopy();
char *strcpy();
char *malloc();
char *ctime();
int fdwt;                               /* hege */

main(argc, argv)
	int argc;
	register char **argv;
{
	FILE *fp;
	register char *s;

	/* parse command line for (optional) arguments */
	while (*++argv && **argv == COMMAND)
		for (s = *argv + 1; *s; s++)
			switch (*s) {
			case 'b':
				unbrief = 0;
				break;
			case 'f':
				header = 0;
				break;
			case 'h':
				hack = 0;
				break;
			case 'i':
				idle = 1;
				unquick = 0;
				break;
			case 'l':
				large = 1;
				break;
			case 'm':
				match = 0;
				break;
			case 'p':
				plan = 0;
				break;
			case 'q':
				unquick = 0;
				break;
			case 's':
				small = 1;
				break;
			case 'w':
				wide = 0;
				break;
			default:
				fprintf(stderr, "Usage: finger [-bfhilmpqsw] [login1 [login2 ...] ]\n");
				exit(1);
			}
	if (unquick || idle)
		time(&tloc);
	/*
	 * *argv == 0 means no names given
	 */
	if (*argv == 0)
		doall();
	else
		donames(argv);
	if (person1)
		print();
	exit(0);
}

doall()
{
	register struct person *p;
	register struct passwd *pw;
	int uf;
	char name[NMAX + 1];

	unshort = large;
	if ((uf = open(USERLOG, 0)) < 0) {
		fprintf(stderr, "finger: error opening %s\n", USERLOG);
		exit(2);
	}
	if (unquick) {
#if BSD
		extern _pw_stayopen;
#endif
		setpwent();
#if BSD
		_pw_stayopen = 1;
#endif
		fwopen();
	}
	while (read(uf, (char *)&user, sizeof user) == sizeof user) {
		if (user.ut_name[0] == 0)
			continue;
#if !BSD
		if (user.ut_type != USER_PROCESS)
			continue;
#endif
		if (person1 == 0)
			p = person1 = (struct person *) malloc(sizeof *p);
		else {
			p->link = (struct person *) malloc(sizeof *p);
			p = p->link;
		}
		bcopy(user.ut_name, name, NMAX);
		name[NMAX] = 0;
		bcopy(user.ut_line, p->tty, LMAX);
		p->tty[LMAX] = 0;
#if BSD
		bcopy(user.ut_host, p->host, HMAX);
		p->host[HMAX] = 0;
#endif
		p->loginat = user.ut_time;
		p->pwd = 0;
		p->loggedin = 1;
		if (unquick && (pw = getpwnam(name))) {
			p->pwd = pwdcopy(pw);
			decode(p);
			p->name = p->pwd->pw_name;
		} else
			p->name = strcpy(malloc(strlen(name) + 1), name);
	}
	if (unquick) {
		fwclose();
		endpwent();
	}
	close(uf);
	if (person1 == 0) {
		printf("No one logged on\n");
		return;
	}
	p->link = 0;
}

donames(argv)
	char **argv;
{
	register struct person *p;
	register struct passwd *pw;
	int uf;

	/*
	 * get names from command line and check to see if they're
	 * logged in
	 */
	unshort = !small;
	for (; *argv != 0; argv++) {
		if (netfinger(*argv))
			continue;
		if (person1 == 0)
			p = person1 = (struct person *) malloc(sizeof *p);
		else {
			p->link = (struct person *) malloc(sizeof *p);
			p = p->link;
		}
		p->name = *argv;
		p->loggedin = 0;
		p->original = 1;
		p->pwd = 0;
	}
	if (person1 == 0)
		return;
	p->link = 0;
	/*
	 * if we are doing it, read /etc/passwd for the useful info
	 */
	if (unquick) {
		setpwent();
		if (!match) {
#if BSD
			extern _pw_stayopen;

			_pw_stayopen = 1;
#endif
			for (p = person1; p != 0; p = p->link)
				if (pw = getpwnam(p->name))
					p->pwd = pwdcopy(pw);
		} else while ((pw = getpwent()) != 0) {
			for (p = person1; p != 0; p = p->link) {
				if (!p->original)
					continue;
				if (strcmp(p->name, pw->pw_name) != 0 &&
				    !matchcmp(pw->pw_gecos, pw->pw_name, p->name))
					continue;
				if (p->pwd == 0)
					p->pwd = pwdcopy(pw);
				else {
					struct person *new;
					/*
					 * handle multiple login names, insert
					 * new "duplicate" entry behind
					 */
					new = (struct person *)
						malloc(sizeof *new);
					new->pwd = pwdcopy(pw);
					new->name = p->name;
					new->original = 1;
					new->loggedin = 0;
					new->link = p->link;
					p->original = 0;
					p->link = new;
					p = new;
				}
			}
		}
		endpwent();
	}
	/* Now get login information */
	if ((uf = open(USERLOG, 0)) < 0) {
		fprintf(stderr, "finger: error opening %s\n", USERLOG);
		exit(2);
	}
	while (read(uf, (char *)&user, sizeof user) == sizeof user) {
		if (*user.ut_name == 0)
			continue;
#if !BSD
		if (user.ut_type != USER_PROCESS)
			continue;
#endif
		for (p = person1; p != 0; p = p->link) {
			if (p->loggedin == 2)
				continue;
			if (strncmp(p->pwd ? p->pwd->pw_name : p->name,
				    user.ut_name, NMAX) != 0)
				continue;
			if (p->loggedin == 0) {
				bcopy(user.ut_line, p->tty, LMAX);
				p->tty[LMAX] = 0;
#if BSD
				bcopy(user.ut_host, p->host, HMAX);
				p->host[HMAX] = 0;
#endif
				p->loginat = user.ut_time;
				p->loggedin = 1;
			} else {	/* p->loggedin == 1 */
				struct person *new;
				new = (struct person *) malloc(sizeof *new);
				new->name = p->name;
				bcopy(user.ut_line, new->tty, LMAX);
				new->tty[LMAX] = 0;
#if BSD
				bcopy(user.ut_host, new->host, HMAX);
				new->host[HMAX] = 0;
#endif
				new->loginat = user.ut_time;
				new->pwd = p->pwd;
				new->loggedin = 1;
				new->original = 0;
				new->link = p->link;
				p->loggedin = 2;
				p->link = new;
				p = new;
			}
		}
	}
	close(uf);
	if (unquick) {
		fwopen();
		for (p = person1; p != 0; p = p->link)
			decode(p);
		fwclose();
	}
}

print()
{
	register FILE *fp;
	register struct person *p, *q;
	register char *s;
	register c;
	register long minidle;

	/*
	 * print out what we got
	 */
	if (header) {
		printf(ORGANIZATION); /* kim */
		if (unquick) {
			if (!unshort)
				if (wide)
					printf("Login       Name               TTY Idle    When            Office\n");
				else
					printf("Login    TTY Idle    When            Office\n");
		} else {
			printf("Login      TTY            When");
			if (idle)
				printf("             Idle");
			putchar('\n');
		}
	}
	for (p = person1; p != 0; p = p->link) {
		if (!unquick) {
			quickprint(p);
			continue;
		}
		if (!unshort) {
			shortprint(p);
			continue;
		}
#define min(a,b) ((a) < (b) ? (a) : (b))
		for (q = person1, minidle = (((unsigned)~0)>>1); q != 0; q = q->link)
			if (!strcmp(q->name, p->name))
				minidle = min(minidle, q->idletime);
		if (p->idletime > minidle)
			continue;
		personprint(p);
		if (p->pwd != 0) {
			if (hack) {
				s = malloc(strlen(p->pwd->pw_dir) +
					sizeof PROJ);
				strcpy(s, p->pwd->pw_dir);
				strcat(s, PROJ);
				if ((fp = fopen(s, "r")) != 0) {
					printf("Project: ");
					while ((c = getc(fp)) != EOF) {
						if (c == '\n')
							break;
						if (isprint(c) || isspace(c))
							putchar(c);
						else
							putchar('?');
					}
					fclose(fp);
					putchar('\n');
				}
				free(s);
			}
			if (plan) {
				s = malloc(strlen(p->pwd->pw_dir) +
					sizeof PLAN);
				strcpy(s, p->pwd->pw_dir);
				strcat(s, PLAN);
				if ((fp = fopen(s, "r")) == 0)
					printf("No Plan.\n");
				else {
					printf("Plan:\n");
					while ((c = getc(fp)) != EOF)
						if (isprint(c) || isspace(c))
							putchar(c);
						else
							putchar('?');
					fclose(fp);
				}
				free(s);
			}
		}
		if (p->link != 0)
			putchar('\n');
	}
}

/*
 * Duplicate a pwd entry.
 * Note: Only the useful things (what the program currently uses) are copied.
 */
struct passwd *
pwdcopy(pfrom)
	register struct passwd *pfrom;
{
	register struct passwd *pto;

	pto = (struct passwd *) malloc(sizeof *pto);
#define savestr(s) strcpy(malloc(strlen(s) + 1), s)
	pto->pw_name = savestr(pfrom->pw_name);
	pto->pw_uid = pfrom->pw_uid;
	pto->pw_gecos = savestr(pfrom->pw_gecos);
	pto->pw_dir = savestr(pfrom->pw_dir);
	pto->pw_shell = savestr(pfrom->pw_shell);
#undef savestr
	return pto;
}

/*
 * print out information on quick format giving just name, tty, login time
 * and idle time if idle is set.
 */
quickprint(pers)
	register struct person *pers;
{
	printf("%-*.*s  ", NMAX, NMAX, pers->name);
	if (pers->loggedin) {
		if (idle) {
			findidle(pers);
			printf("%c%-*s %-16.16s", pers->writable ? ' ' : '*',
				LMAX, pers->tty, ctime(&pers->loginat));
			ltimeprint("   ", &pers->idletime, "");
		} else
			printf(" %-*s %-16.16s", LMAX,
				pers->tty, ctime(&pers->loginat));
		putchar('\n');
	} else
		printf("          Not Logged In\n");
}

/*
 * print out information in short format, giving login name, full name,
 * tty, idle time, login time, office location and phone.
 */
shortprint(pers)
	register struct person *pers;
{
	char *p;
	char dialup;

	if (pers->pwd == 0) {
		printf("%-15s       ???\n", pers->name);
		return;
	}
	printf("%-*s", NMAX, pers->pwd->pw_name);
	dialup = 0;
	if (wide) {
		if (pers->realname)
			printf(" %-20.20s", pers->realname);
		else
			printf("        ???          ");
	}
	putchar(' ');
	if (pers->loggedin && !pers->writable)
		putchar('*');
	else
		putchar(' ');
	if (*pers->tty) {
		if (pers->tty[0] == 't' && pers->tty[1] == 't' &&
		    pers->tty[2] == 'y') {
			if (pers->tty[3] == 'd' && pers->loggedin)
				dialup = 1;
			printf("%-3.3s ", pers->tty + 3);
		} else
			printf("%-3.3s ", pers->tty);
	} else
		printf("   ");
	p = ctime(&pers->loginat);
	if (pers->loggedin) {
		stimeprint(&pers->idletime);
		printf(" %3.3s %-5.5s ", p, p + 11);
	} else if (pers->loginat == 0)
		printf(" < .  .  .  . >");
	else if (tloc - pers->loginat >= 180 * 24 * 60 * 60)
		printf(" <%-6.6s, %-4.4s>", p + 4, p + 20);
	else
		printf(" <%-12.12s>", p + 4);
	if (dialup && pers->homephone)
		printf(" %20s", pers->homephone);
	else {
		if (pers->office)
			printf(" %-11.11s", pers->office);
		else if (pers->officephone || pers->homephone)
			printf("            ");
		if (pers->officephone)
			printf(" %s", pers->officephone);
		else if (pers->homephone)
			printf(" %s", pers->homephone);
	}
	putchar('\n');
}

/*
 * print out a person in long format giving all possible information.
 * directory and shell are inhibited if unbrief is clear.
 */
personprint(pers)
	register struct person *pers;
{
	if (pers->pwd == 0) {
		printf("Login name: %-10s\t\t\tIn real life: ???\n",
			pers->name);
		return;
	}
	printf("Login name: %-10s", pers->pwd->pw_name);
	if (pers->loggedin && !pers->writable)
		printf("\t(messages off)\t");
	else
		printf("\t\t\t");
	if (pers->realname)
		printf("In real life: %s", pers->realname);
	if (pers->office) {
		printf("\nOffice: %-.11s", pers->office);
		if (pers->officephone) {
			printf(", %s", pers->officephone);
			if (pers->homephone)
				printf("\t\t\tHome phone: %s", pers->homephone);
			else if (pers->random)
				printf("\t\t\t%s", pers->random);
		} else
			if (pers->homephone)
				printf("\t\t\t\tHome phone: %s", pers->homephone);
			else if (pers->random)
				printf("\t\t\t\t%s", pers->random);
	} else if (pers->officephone) {
		printf("\nPhone: %s", pers->officephone);
		if (pers->homephone)
			printf(", %s", pers->homephone);
		if (pers->random)
			printf(", %s", pers->random);
	} else if (pers->homephone) {
		printf("\nPhone: %s", pers->homephone);
		if (pers->random)
			printf(", %s", pers->random);
	} else if (pers->random)
		printf("\n%s", pers->random);
	if (unbrief) {
		printf("\nDirectory: %-25s", pers->pwd->pw_dir);
		if (*pers->pwd->pw_shell)
			printf("\tShell: %-s", pers->pwd->pw_shell);
	}
	if (pers->loggedin) {
		register char *ep = ctime(&pers->loginat);
#if BSD
		if (*pers->host) {
			printf("\nOn since %15.15s on %s from %s",
				&ep[4], pers->tty, pers->host);
			ltimeprint("\n", &pers->idletime, " Idle Time");
		} else 
#endif
		       {
			printf("\nOn since %15.15s on %-*s",
				&ep[4], LMAX, pers->tty);
			ltimeprint("", &pers->idletime, " Idle Time");
		}
	} else if (pers->loginat == 0)
		printf("\nNever logged in.");
	else if (tloc - pers->loginat > 180 * 24 * 60 * 60) {
		register char *ep = ctime(&pers->loginat);
		printf("\nLast login %10.10s, %4.4s on %s",
			ep, ep+20, pers->tty);
#if BSD
		if (*pers->host)
			printf(" from %s", pers->host);
#endif
	} else {
		register char *ep = ctime(&pers->loginat);
		printf("\nLast login %16.16s on %s", ep, pers->tty);
#if BSD
		if (*pers->host)
			printf(" from %s", pers->host);
#endif
	}
	putchar('\n');
}

/*
 *  very hacky section of code to format phone numbers.  filled with
 *  magic constants like 4, 7 and 10.
 */
char *
phone(s, len, alldigits)
	register char *s;
	int len;
	char alldigits;
{
	char fonebuf[15];
	register char *p = fonebuf;
	register i;

#if BSD
	if (!alldigits)
#endif
		return (strcpy(malloc(len + 1), s));
#if BSD
	switch (len) {
	case 4:
		*p++ = ' ';
		*p++ = 'x';
		*p++ = '2';
		*p++ = '-';
		for (i = 0; i < 4; i++)
			*p++ = *s++;
		break;
	case 5:
		*p++ = ' ';
		*p++ = 'x';
		*p++ = *s++;
		*p++ = '-';
		for (i = 0; i < 4; i++)
			*p++ = *s++;
		break;
	case 7:
		for (i = 0; i < 3; i++)
			*p++ = *s++;
		*p++ = '-';
		for (i = 0; i < 4; i++)
			*p++ = *s++;
		break;
	case 10:
		for (i = 0; i < 3; i++)
			*p++ = *s++;
		*p++ = '-';
		for (i = 0; i < 3; i++)
			*p++ = *s++;
		*p++ = '-';
		for (i = 0; i < 4; i++)
			*p++ = *s++;
		break;
	case 0:
		return 0;
	default:
		return (strcpy(malloc(len + 1), s));
	}
	*p++ = 0;
	return (strcpy(malloc(p - fonebuf), fonebuf));
#endif
}

/*
 * decode the information in the gecos field of /etc/passwd
 */
decode(pers)
	register struct person *pers;
{
	char buffer[256];
	register char *bp, *gp, *lp;
	int alldigits;
	int hasspace;
	int len;

	pers->realname = 0;
	pers->office = 0;
	pers->officephone = 0;
	pers->homephone = 0;
	pers->random = 0;
	if (pers->pwd == 0)
		return;
	gp = pers->pwd->pw_gecos;
	bp = buffer;
	if (*gp == ASTERISK)
		gp++;
	while (*gp && *gp != COMMA)			/* name */
		if (*gp == SAMENAME) {
			lp = pers->pwd->pw_name;
			if (islower(*lp))
				*bp++ = toupper(*lp++);
			while (*bp++ = *lp++)
				;
			bp--;
			gp++;
		} else
			*bp++ = *gp++;
	*bp++ = 0;
	if ((len = bp - buffer) > 1)
		pers->realname = strcpy(malloc(len), buffer);
	if (*gp == COMMA) {				/* office */
		gp++;
		hasspace = 0;
		bp = buffer;
		while (*gp && *gp != COMMA) {
			*bp = *gp++;
			if (*bp == ' ')
				hasspace = 1;
			/* leave 5 for Cory and Evans expansion */
			if (bp < buffer + sizeof buffer - 6)
				bp++;
		}
		*bp = 0;
		len = bp - buffer;
		bp--;			/* point to last character */
		if (hasspace || len == 0)
			len++;
#if UCB
		else if (*bp == CORY) {
			strcpy(bp, " Cory");
			len += 5;
		} else if (*bp == EVANS) {
			strcpy(bp, " Evans");
			len += 6;
		} 
#endif
		  else
			len++;
		if (len > 1)
			pers->office = strcpy(malloc(len), buffer);
	}
	if (*gp == COMMA) {				/* office phone */
		gp++;
		bp = buffer;
		alldigits = 1;
		while (*gp && *gp != COMMA) {
			*bp = *gp++;
			if (!isdigit(*bp))
				alldigits = 0;
			if (bp < buffer + sizeof buffer - 1)
				bp++;
		}
		*bp = 0;
		pers->officephone = phone(buffer, bp - buffer, alldigits);
	}
	if (*gp == COMMA) {				/* home phone */
		gp++;
		bp = buffer;
		alldigits = 1;
		while (*gp && *gp != COMMA) {
			*bp = *gp++;
			if (!isdigit(*bp))
				alldigits = 0;
			if (bp < buffer + sizeof buffer - 1)
				bp++;
		}
		*bp = 0;
		pers->homephone = phone(buffer, bp - buffer, alldigits);
	}
	if (pers->loggedin)
		findidle(pers);
	else
		findwhen(pers);
}

/*
 * find the last log in of a user by checking the LASTLOG file.
 * the entry is indexed by the uid, so this can only be done if
 * the uid is known (which it isn't in quick mode)
 */

fwopen()
{
#if BSD
	if ((lf = open(LASTLOG, 0)) < 0)
		fprintf(stderr, "finger: %s open error\n", LASTLOG);
#else
	fdwt=open(WTMP_FILE,0);    /* Hege */
#endif
}

findwhen(pers)
	register struct person *pers;
{
#if BSD
	struct lastlog ll;
	int i;

	if (lf >= 0) {
		lseek(lf, (long)pers->pwd->pw_uid * sizeof ll, 0);
		if ((i = read(lf, (char *)&ll, sizeof ll)) == sizeof ll) {
			bcopy(ll.ll_line, pers->tty, LMAX);
			pers->tty[LMAX] = 0;
			bcopy(ll.ll_host, pers->host, HMAX);
			pers->host[HMAX] = 0;
			pers->loginat = ll.ll_time;
		} else {
			if (i != 0)
				fprintf(stderr, "finger: %s read error\n",
					LASTLOG);
			pers->tty[0] = 0;
			pers->host[0] = 0;
			pers->loginat = 0L;
		}
	} else {
		pers->tty[0] = 0;
		pers->host[0] = 0;
		pers->loginat = 0L;
	}
#else   /* Hege */
	struct utmp *wtmp;
	int done=1,ok=0;
	
	wtmp=(struct utmp *)malloc(sizeof(struct utmp));
	if(lseek(fdwt,0l,2)==-1) done=0;
	while(done && (lseek(fdwt,(long)(sizeof(struct utmp)*-1),1)>0)) {
	  done=read(fdwt,wtmp,sizeof(struct utmp));
	  wtmp->ut_user[8]='\0'; 
	  if(!strcmp(pers->name,wtmp->ut_user) && wtmp->ut_type==USER_PROCESS){
            done=0;
	    ok=1;
	    }
	  if(lseek(fdwt,(long)(sizeof(struct utmp)*-1),1)<0) done=0;         
	}
	if (ok) {
		memcpy(pers->tty, wtmp->ut_line, LMAX);
		pers->tty[LMAX] = 0;
		pers->loginat = wtmp->ut_time;
	} else {
		pers->tty[0] = 0;
		pers->loginat = 0L;
	}
#endif
}

fwclose()
{
#if BSD
	if (lf >= 0)
		close(lf);
#else
	close(fdwt);    /* Hege */
#endif
}

/*
 * find the idle time of a user by doing a stat on /dev/tty??,
 * where tty?? has been gotten from USERLOG, supposedly.
 */
findidle(pers)
	register struct person *pers;
{
	struct stat ttystatus;
	static char buffer[20] = "/dev/";
	long t;
#define TTYLEN 5

	strcpy(buffer + TTYLEN, pers->tty);
	buffer[TTYLEN+LMAX] = 0;
	if (stat(buffer, &ttystatus) < 0) {
		fprintf(stderr, "finger: Can't stat %s\n", buffer);
		exit(4);
	}
	time(&t);
	if (t < ttystatus.st_atime)
		pers->idletime = 0L;
	else
		pers->idletime = t - ttystatus.st_atime;
	pers->writable = (ttystatus.st_mode & TALKABLE) == TALKABLE;
}

/*
 * print idle time in short format; this program always prints 4 characters;
 * if the idle time is zero, it prints 4 blanks.
 */
stimeprint(dt)
	long *dt;
{
	register struct tm *delta;

	delta = gmtime(dt);
	if (delta->tm_yday == 0)
		if (delta->tm_hour == 0)
			if (delta->tm_min == 0)
				printf("    ");
			else
				printf("  %2d", delta->tm_min);
		else
			if (delta->tm_hour >= 10)
				printf("%3d:", delta->tm_hour);
			else
				printf("%1d:%02d",
					delta->tm_hour, delta->tm_min);
	else
		printf("%3dd", delta->tm_yday);
}

/*
 * print idle time in long format with care being taken not to pluralize
 * 1 minutes or 1 hours or 1 days.
 * print "prefix" first.
 */
ltimeprint(before, dt, after)
	long *dt;
	char *before, *after;
{
	register struct tm *delta;

	delta = gmtime(dt);
	if (delta->tm_yday == 0 && delta->tm_hour == 0 && delta->tm_min == 0 &&
	    delta->tm_sec <= 10)
		return (0);
	printf("%s", before);
	if (delta->tm_yday >= 10)
		printf("%d days", delta->tm_yday);
	else if (delta->tm_yday > 0)
		printf("%d day%s %d hour%s",
			delta->tm_yday, delta->tm_yday == 1 ? "" : "s",
			delta->tm_hour, delta->tm_hour == 1 ? "" : "s");
	else
		if (delta->tm_hour >= 10)
			printf("%d hours", delta->tm_hour);
		else if (delta->tm_hour > 0)
			printf("%d hour%s %d minute%s",
				delta->tm_hour, delta->tm_hour == 1 ? "" : "s",
				delta->tm_min, delta->tm_min == 1 ? "" : "s");
		else
			if (delta->tm_min >= 10)
				printf("%2d minutes", delta->tm_min);
			else if (delta->tm_min == 0)
				printf("%2d seconds", delta->tm_sec);
			else
				printf("%d minute%s %d second%s",
					delta->tm_min,
					delta->tm_min == 1 ? "" : "s",
					delta->tm_sec,
					delta->tm_sec == 1 ? "" : "s");
	printf("%s", after);
}

matchcmp(gname, login, given)
	register char *gname;
	char *login;
	char *given;
{
	char buffer[100];
	register char *bp, *lp;
	register c;

	if (*gname == ASTERISK)
		gname++;
	lp = 0;
	bp = buffer;
	for (;;)
		switch (c = *gname++) {
		case SAMENAME:
			for (lp = login; bp < buffer + sizeof buffer
					 && (*bp++ = *lp++);)
				;
			bp--;
			break;
		case ' ':
		case COMMA:
		case '\0':
			*bp = 0;
			if (namecmp(buffer, given))
				return (1);
			if (c == COMMA || c == 0)
				return (0);
			bp = buffer;
			break;
		default:
			if (bp < buffer + sizeof buffer)
				*bp++ = c;
		}
	/*NOTREACHED*/
}

namecmp(name1, name2)
	register char *name1, *name2;
{
	register c1, c2;

	for (;;) {
		c1 = *name1++;
		if (islower(c1))
			c1 = toupper(c1);
		c2 = *name2++;
		if (islower(c2))
			c2 = toupper(c2);
		if (c1 != c2)
			break;
		if (c1 == 0)
			return (1);
	}
	if (!c1) {
		for (name2--; isdigit(*name2); name2++)
			;
		if (*name2 == 0)
			return (1);
	} else if (!c2) {
		for (name1--; isdigit(*name1); name1++)
			;
		if (*name2 == 0)
			return (1);
	}
	return (0);
}

netfinger(name)
	char *name;
{
	char *host;
	char fname[100];
	struct hostent *hp;
	struct servent *sp;
	struct sockaddr_in sin;
	int s;
	register FILE *f;
	register int c;
	register int lastc;

	if (name == NULL)
		return (0);
	host = rindex(name, '@');
	if (host == NULL)
		return (0);
	*host++ = 0;
	hp = gethostbyname(host);
	if (hp == NULL) {
		static struct hostent def;
		static struct in_addr defaddr;
#if BSD43
		static char *alist[1];
#endif
		static char namebuf[128];

		defaddr.s_addr = inet_addr(host);
		if (defaddr.s_addr == -1) {
			printf("unknown host: %s\n", host);
			return (1);
		}
		strcpy(namebuf, host);
		def.h_name = namebuf;
#if BSD43
		def.h_addr_list = alist;
#endif
		def.h_addr = (char *)&defaddr;
		def.h_length = sizeof (struct in_addr);
		def.h_addrtype = AF_INET;
		def.h_aliases = 0;
		hp = &def;
	}
	printf("[%s]", hp->h_name);
	sp = getservbyname("finger", "tcp");
	if (sp == 0) {
		printf("tcp/finger: unknown service\n");
		return (1);
	}
	sin.sin_family = hp->h_addrtype;
	bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
	sin.sin_port = sp->s_port;
	s = socket(hp->h_addrtype, SOCK_STREAM, 0);
	if (s < 0) {
		fflush(stdout);
		perror("socket");
		return (1);
	}
	if (connect(s, (char *)&sin, sizeof (sin)) < 0) {
		fflush(stdout);
		perror("connect");
		close(s);
		return (1);
	}
	printf("\n");
	if (large) write(s, "/W ", 3);
	write(s, name, strlen(name));
	write(s, "\r\n", 2);
	f = fdopen(s, "r");
	while ((c = getc(f)) != EOF) {
		switch(c) {
		case 0210:
		case 0211:
		case 0212:
		case 0214:
			c -= 0200;
			break;
		case 0215:
			c = '\n';
			break;
		}
		lastc = c;
		if (isprint(c) || isspace(c))
			putchar(c);
		else
			putchar('?');
	}
	if (lastc != '\n')
		putchar('\n');
	(void)fclose(f);
	return (1);
}
--- CUT AGAIN --- CUT AGAIN --- CUT AGAIN --- CUT AGAIN --- CUT AGAIN ---

This will get you along.  It doesn't check mail (yet, I'm working on it), but
it does show your last login CORRECTLY.  The one in hemuli shows actually your
"first login".  If there's interest, I'll post or e-mail the improved version
with mail checking.

Kim
--
 ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
( Kimmo Suominen        ! Lappeenranta U of Technology ! kim@kannel.lut.fi )
( "That's what I think" ! Computing Centre  *  Finland ! Funet: KUULA::KIM )
 ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

kim@kannel.lut.fi (Kimmo Suominen) (12/05/89)

Thanks, Tor.  I had forgot about that - I've delegated it to another person.
Now I'll try to come up with the version supporting mail checking real soon.

Kim
--
 ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
( Kimmo Suominen        ! Lappeenranta U of Technology ! kim@kannel.lut.fi )
( "That's what I think" ! Computing Centre  *  Finland ! Funet: KUULA::KIM )
 ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''