#ifndef lint
static char *sccsid = "@(#)finger.c	4.5 (Berkeley) 9/16/83";
#endif

/*  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 -q		{quick list of users}
 *	finger namelist		{long format list of specified users}
 *	finger -s namelist	{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.
 */

#include	<sys/types.h>
#include	<sys/stat.h>
#include	<sgtty.h>
#include	<utmp.h>
#include	<signal.h>
#include	<pwd.h>
#include	<stdio.h>
#include	<lastlog.h>
#include	<sys/time.h>

struct	utmp	utmp;	/* for sizeof */
#define NMAX sizeof(utmp.ut_name)
#define LMAX sizeof(utmp.ut_line)
#define HMAX sizeof(utmp.ut_host)

#define		ASTERISK	'*'	/* ignore this in real name */
#define		BLANK		' '	/* blank character (i.e. space) */
#define		CAPITALIZE	0137&	/* capitalize character macro */
#define		COMMA		','	/* separator in pw_gecos field */
#define		COMMAND		'-'	/* command line flag char */
#define		TECHSQ		'T'	/* tech square office */
#define		MIT		'M'	/* MIT office */
#define		LINEBREAK	012	/* line feed */
#define		NULLSTR		""	/* the null string, opposed to NULL */
#define		SAMENAME	'&'	/* repeat login name in real name */

struct  person  {			/* one for each person fingered */
	char		name[NMAX+1];	/* login name */
	char		tty[LMAX+1];	/* NULL terminated tty line */
	char		host[HMAX+1];	/* remote host name */
	long		loginat;	/* time of login (possibly last) */
	long		idletime;	/* how long idle (if logged in) */
	short int	loggedin;	/* flag for being 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 */
	char		*ttyloc;	/* pointer to console location */
	struct  passwd	*pwd;		/* structure of /etc/passwd stuff */
	struct  person	*link;		/* link to next person */
};

struct  passwd			*NILPWD = 0;
struct  person			*NILPERS = 0;

int		persize		= sizeof( struct person );
int		pwdsize		= sizeof( struct passwd );

char		LASTLOG[]	= "/usr/adm/lastlog";	/* last login info */
char		USERLOG[]	= "/etc/utmp";		/* who is logged in */
char		outbuf[BUFSIZ];				/* output buffer */
char		*ctime();

int		header		= 1;		/* -f option default */
int		hack		= 1;		/* -h option default */
int		large		= 0;		/* -l option default */
int		match		= 1;		/* -m option default */
int		small		= 0;		/* -s option default */

int		lf;
int		llopenerr;

long		tloc;				/* current time */


main( argc, argv )

    int		argc;
    char	*argv[];

{
	FILE			*fp,  *fopen();		/* for plans */
	struct  passwd		*getpwent();		/* read /etc/passwd */
	struct  person		*person1,  *p,  *pend;	/* people */
	struct  passwd		*pw;			/* temporary */
	struct  utmp		user;			/*   ditto   */
	char			*malloc();
	char			*s,  *pn,  *ln;
	char			c;
	char			*PLAN = "/.plan";	/* what plan file is */
	int			PLANLEN = strlen( PLAN );
	int			numnames = 0;
	int			orgnumnames;
	int			uf;
	int			usize = sizeof user;
	int			unshort;
	int			i, j;
	int			fngrlogin;

	setbuf( stdout, outbuf );			/* buffer output */

    /*  parse command line for (optional) arguments */

	i = 1;
	if(  strcmp( *argv, "sh" )  )  {
	    fngrlogin = 0;
	    while( i++ < argc  &&  (*++argv)[0] == COMMAND )  {
		for( s = argv[0] + 1; *s != NULL; s++ )  {
			switch  (*s)  {

			    case 'f':
				    header = 0;
				    break;

			    case 'h':
				    hack = 0;
				    break;

			    case 'l':
				    large = 1;
				    break;

			    case 'm':
				    match = 0;
				    break;

			    case 's':
				    small = 1;
				    break;

			    default:
				fprintf( stderr, "finger: Usage -- 'finger [-fhlms] [login1 [login2 ...] ]'\n" );
				exit( 1 );
			}
		}
	    }
	}
	else  {
	    fngrlogin = 1;
	}
	time( &tloc );

    /*  i > argc means no login names given so get them by reading USERLOG */

	if(  (i > argc)  ||  fngrlogin  )  {
	    unshort = large;
	    if(  ( uf = open(USERLOG, 0) ) >= 0  )  {
		user.ut_name[0] = NULL;
		while( user.ut_name[0] == NULL )  {
		    if( read( uf, (char *) &user, usize ) != usize )  {
			printf( "\nNo one logged on\n" );
			exit( 0 );
		    }
		}
		person1 = (struct person  *) malloc( persize );
		for( j = 0; j < NMAX; j++ )  {
		    person1->tty[j] = user.ut_line[j];
		    person1->name[j] = user.ut_name[j];
		}
		person1->name[NMAX] = NULL;
		person1->tty[NMAX] = NULL;
		if( user.ut_host[0] )  {
		    for( j = 0; j < HMAX; j++ )  {
			person1->host[j] = user.ut_host[j];
		    }
		    person1->host[HMAX] = NULL;
		}
		person1->loginat = user.ut_time;
		person1->pwd = NILPWD;
		person1->loggedin = 1;
		gtloc( person1 );
		numnames++;
		p = person1;
		while( read( uf, (char *) &user, usize ) == usize )  {
		    if( user.ut_name[0] == NULL )  continue;
		    p->link = (struct person  *) malloc( persize );
		    p = p->link;
		    for( j = 0; j < NMAX; j++ )  {
			p->tty[j] = user.ut_line[j];
			p->name[j] = user.ut_name[j];
		    }
		    p->name[NMAX] = NULL;
		    p->tty[NMAX] = NULL;
		    if( user.ut_host[0] )  {
			for( j = 0; j < HMAX; j++ )  {
			    p->host[j] = user.ut_host[j];
			}
			p->host[HMAX] = NULL;
		    }
		    p->loginat = user.ut_time;
		    p->pwd = NILPWD;
		    p->loggedin = 1;
		    gtloc( p );
		    numnames++;
		}
		p->link = NILPERS;
		close( uf );
	    }
	    else  {
		fprintf( stderr, "finger: error opening %s\n", USERLOG );
		exit( 2 );
	    }

		/*  read /etc/passwd for the useful info */

	    setpwent();
	    fwopen();
	    i = numnames;
	    while(  ( (pw = getpwent()) != NILPWD )  &&  ( i > 0 )  )  {
		p = person1;
		do  {
		    if( p->pwd == NILPWD )  {
			if(  strcmp( p->name, pw->pw_name ) == 0  )  {
			    p->pwd = (struct passwd  *) malloc( pwdsize );
			    pwdcopy( p->pwd, pw );
			    decode( p );
			    i--;
			}
		    }
		    p = p->link;
		}  while( p != NILPERS );
	    }
	    fwclose();
	    endpwent();
	}

    /* get names from command line and check to see if they're  logged in */

	else  {
	    unshort = ( small == 1 ? 0 : 1 );
	    i++;
	    person1 = (struct person  *) malloc( persize );
	    strcpy(  person1->name, (argv++)[ 0 ]  );
	    person1->loggedin = 0;
	    person1->pwd = NILPWD;
	    numnames++;
	    p = person1;
	    while( i++ <= argc )  {
		p->link = (struct person  *) malloc( persize );
		p = p->link;
		strcpy(  p->name, (argv++)[ 0 ]  );
		p->loggedin = 0;
		p->pwd = NILPWD;
		numnames++;
	    }
	    p->link = NILPERS;
	    pend = p;

		/*  if we are doing it, read /etc/passwd for the useful info */

	    orgnumnames = numnames;
	    setpwent();
	    while(  ( pw = getpwent() ) != NILPWD  )  {
		p = person1;
		i = 0;
		do  {
		    if( strcmp( p->name, pw->pw_name ) == 0    ||
			matchcmp( pw->pw_gecos, pw->pw_name, p->name ) )  {
			if( p->pwd == NILPWD )  {
			    p->pwd = (struct passwd  *) malloc( pwdsize );
			    pwdcopy( p->pwd, pw );
			}
			else  {	/* handle multiple logins -- append new
				   "duplicate" entry to end of list */
			    pend->link = (struct person  *) malloc(persize);
			    pend = pend->link;
			    pend->link = NILPERS;
			    strcpy( pend->name, p->name );
			    pend->pwd = (struct passwd  *) malloc(pwdsize);
			    pwdcopy( pend->pwd, pw );
			    numnames++;
			}
		    }
		    p = p->link;
		}  while( ++i < orgnumnames );
	    }
	    endpwent();

		/*  Now get login information */

	    if(  ( uf = open(USERLOG, 0) ) >= 0  )  {
		while( read( uf, (char *) &user, usize ) == usize )  {
		    if( user.ut_name[0] == NULL )  continue;
		    p = person1;
		    do  {
			pw = p->pwd;
			if( pw == NILPWD )  {
			    i = ( strcmp( p->name, user.ut_name ) ? 0 : NMAX );
			}
			else  {
			    i = 0;
			    while(  (i < NMAX)  &&
				    ( pw->pw_name[i] == user.ut_name[i])  )  {
				if( pw->pw_name[i] == NULL )  {
				    i = NMAX;
				    break;
				}
				i++;
			    }
			}
			if( i == NMAX )  {
			    if( p->loggedin == 1 )  {
				pend->link = (struct person  *) malloc(persize);
				pend = pend->link;
				pend->link = NILPERS;
				strcpy( pend->name, p->name );
				for( j = 0; j < NMAX; j++ )  {
				    pend->tty[j] = user.ut_line[j];
				}
				if( user.ut_host[0] )  {
				    for( j = 0; j < HMAX; j++ )  {
					pend->host[j] = user.ut_host[j];
				    }
				    pend->host[ HMAX ] = NULL;
				}
				pend->loginat = user.ut_time;
				pend->loggedin = 2;
				if(  pw == NILPWD  )  {
				    pend ->pwd = NILPWD;
				}
				else  {
				    pend->pwd = (struct passwd  *) malloc(pwdsize);
				    pwdcopy( pend->pwd, pw );
				}
				numnames++;
			    }
			    else  {
				if( p->loggedin != 2 )  {
				    for( j = 0; j < NMAX; j++ )  {
					p->tty[j] = user.ut_line[j];
				    }
				    p->tty[ NMAX ] = NULL;
				    if( user.ut_host[0] )  {
					for( j = 0; j < HMAX; j++ )  {
					    p->host[j] = user.ut_host[j];
					}
					p->tty[ NMAX ] = NULL;
				    }
				    p->loginat = user.ut_time;
				    p->loggedin = 1;
				}
			    }
			}
			p = p->link;
		    }  while( p != NILPERS );
		}
		fwopen();
		p = person1;
		while( p != NILPERS )  {
		    if( p->loggedin == 2 )  {
			p->loggedin = 1;
		    }
		    decode( p );
		    if( p->loggedin)	gtloc( p );
		    p = p->link;
		}
		fwclose();
		close( uf );
	    }
	    else  {
		fprintf( stderr, "finger: error opening %s\n", USERLOG );
		exit( 2 );
	    }
	}

    /* print out what we got */

	if( header )  {
	    if( !unshort )  {
		printf(
"-User-   --Full name--       Idle TTY -Console location-\n" );
	    }
	}
	p = person1;
	do  {
	    shortprint( p );
	    if( unshort )  {
		personprint( p );
		if( p->pwd != NILPWD )  {
		    if( hack )  {
		    }
		    s = malloc( strlen( (p->pwd)->pw_dir ) + PLANLEN + 1 );
		    strcpy(  s, (p->pwd)->pw_dir  );
		    strcat( s, PLAN );
		    if(  ( fp = fopen( s, "r") )  == NULL  )  {
			printf( "No Plan.\n" );
		    }
		    else  {
			printf( "Plan:\n" );
			while(  ( c = getc(fp) )  !=  EOF  )  {
			    putc( c, stdout );
			}
			fclose( fp );
		    }
		}
		if( p->link != NILPERS )  {
		    printf( "\n" );
		}
	    }
	    p = p->link;
	}  while( p != NILPERS );
	exit(0);
}



/*  given a pointer to a pwd (pfrom) copy it to another one, allocating
 *  space for all the stuff in it.  Note: Only the useful (what the
 *  program currently uses) things are copied.
 */

pwdcopy( pto, pfrom )		/* copy relevant fields only */

    struct  passwd		*pto,  *pfrom;
{
	pto->pw_name = malloc(  strlen( pfrom->pw_name ) + 1  );
	strcpy( pto->pw_name, pfrom->pw_name );
	pto->pw_uid = pfrom->pw_uid;
	pto->pw_gecos = malloc(  strlen( pfrom->pw_gecos ) + 1  );
	strcpy( pto->pw_gecos, pfrom->pw_gecos );
	pto->pw_dir = malloc(  strlen( pfrom->pw_dir ) + 1  );
	strcpy( pto->pw_dir, pfrom->pw_dir );
	pto->pw_shell = malloc(  strlen( pfrom->pw_shell ) + 1  );
	strcpy( pto->pw_shell, pfrom->pw_shell );
}

/*  print out information in short format, giving login name, full name,
 *  tty, idle time, login time, office location and phone.
 */

shortprint( pers )

    struct  person	*pers;

{
	struct  passwd		*pwdt = pers->pwd;
	char			buf[ 26 ];
	int			i,  len,  offset,  dialup;

	if( pwdt == NILPWD )  {
	    printf( "%-*.*s", NMAX, NMAX,  pers->name );
	    printf( "       ???\n" );
	    return;
	}
	printf( "%-*.*s", NMAX, NMAX,  pwdt->pw_name );
	dialup = 0;
	if(  strlen( pers->realname ) > 0  )  {
	    printf( " %-19.19s", pers->realname );
	    if( strlen( pers->realname ) >= 20 )  {
		putc( '!', stdout );
	    }
	    else  {
		putc( ' ', stdout );
	    }
	}
	else  {
	    printf( "        ???          " );
	}
	if( pers->loggedin )  {
	    stimeprint( &pers->idletime );
	    if(  strlen( pers->tty ) > 0  )  {
		strcpy( buf, pers->tty );
		    if(  (buf[0] == 't')  &&  (buf[1] == 't')  &&  (buf[2] == 'y')  )  {
		    offset = 3;
		    for( i = 0; i < 2; i++ )  {
			buf[i] = buf[i + offset];
		    }
		}
		if(  (buf[0] == 'd')  &&  pers->loggedin  )  {
		    dialup = 1;
		}
		printf( " %3.2s ", buf );
	    }
	    else  {
		printf( "   " );
	    }
	    if( pers->host[0] )  {
		printf( "Net site %.*s", HMAX, pers->host );
	    }
	    else if( strlen( pers->ttyloc ) > 0 )  {
		printf( "%s", pers->ttyloc);
	    }
	    else  {
		printf( "Net virtual terminal" );
	    }
	}
	else if (pers->loginat == 0)
	    printf(" Last login or logout unknown");
	else
	    printf(" Login %-24.24s", ctime(&pers->loginat) );
	printf( "\n" );
}

/*  print out a person in long format giving all possible information.
 */

personprint( pers )

    struct  person	*pers;
{
	FILE			*fp,	*fopen();	/* for projects */
	struct  passwd		*pwdt = pers->pwd;
	char			*s;
	char			c;
	char			*FRWD = "/.forward";	/* what mail alias */
	char			*PROJ = "/.project";	/* what project file */
	int			FRWDLEN = strlen( FRWD );
	int			PROJLEN = strlen( PROJ );
	int			idleprinted;

	if( pwdt == NILPWD )  {
	    printf( "Login name: %-10s", pers->name );
	    printf( "			" );
	    printf( "In real life: ???\n");
	    return;
	}
	printf( "   " );
	s = malloc(strlen((pers->pwd)->pw_dir) + FRWDLEN + 1 );
	strcpy(  s, (pers->pwd)->pw_dir  );
	strcat( s, FRWD );
	if(  ( fp = fopen( s, "r") )  != NULL  )  {
	    putc( '[', stdout );
	    while(  ( c = getc(fp) )  !=  EOF  )  {
		if( c == LINEBREAK )  {
		    break;
		}
		putc( c, stdout );
	    }
	    fclose( fp );
	    printf( "] " );
	}
	printf( "<%s>", pwdt->pw_dir );
	s = malloc(strlen((pers->pwd)->pw_dir) + PROJLEN + 1 );
	strcpy(  s, (pers->pwd)->pw_dir  );
	strcat( s, PROJ );
	if(  ( fp = fopen( s, "r") )  != NULL  )  {
	    putc( ' ', stdout );
	    while(  ( c = getc(fp) )  !=  EOF  )  {
		if( c == LINEBREAK )  {
		    break;
		}
		putc( c, stdout );
	    }
	    fclose( fp );
	}
	if(  strlen( pers->office ) > 0  )  {
	    printf( "\n   Work %s", pers->office );
	    if(  strlen( pers->officephone ) > 0  )  {
		printf( "; %s", pers->officephone );
	    }
	}
	else  {
	    if(  strlen( pers->officephone ) > 0  )  {
		printf( "\n   Work phone %s", pers->officephone );
	    }
	}
	if(  strlen( pers->homephone ) > 0  )  {
	    printf( "\n   Home phone %s", pers->homephone );
	}
	if(  strlen( pers->random ) > 0  )  {
	    printf( "\n%s", pers->random );
	}
	printf( "\n" );
}

/*
 *  very hacky section of code to format phone numbers.  filled with
 *  magic constants like 4, 7 and 10.
 */

char  *phone( s, len )

    char		*s;
    int			len;
{
	char		*strsave();
	char		fonebuf[ 15 ];
	int		i;

	switch(  len  )  {

	    case  4:
		fonebuf[ 0 ] = ' ';
		fonebuf[ 1 ] = 'x';
		fonebuf[ 2 ] = '3';
		fonebuf[ 3 ] = '-';
		for( i = 0; i <= 3; i++ )  {
		    fonebuf[ 4 + i ] = *s++;
		}
		fonebuf[ 8 ] = NULL;
		return( strsave( &fonebuf[0] ) );
		break;

	    case  7:
		for( i = 0; i <= 2; i++ )  {
		    fonebuf[ i ] = *s++;
		}
		fonebuf[ 3 ] = '-';
		for( i = 0; i <= 3; i++ )  {
		    fonebuf[ 4 + i ] = *s++;
		}
		fonebuf[ 8 ] = NULL;
		return( strsave( &fonebuf[0] ) );
		break;

	    case 10:
		for( i = 0; i <= 2; i++ )  {
		    fonebuf[ i ] = *s++;
		}
		fonebuf[ 3 ] = '-';
		for( i = 0; i <= 2; i++ )  {
		    fonebuf[ 4 + i ] = *s++;
		}
		fonebuf[ 7 ] = '-';
		for( i = 0; i <= 3; i++ )  {
		    fonebuf[ 8 + i ] = *s++;
		}
		fonebuf[ 12 ] = NULL;
		return( strsave( &fonebuf[0] ) );
		break;

	    default:
		fprintf( stderr, "finger: error in phone numbering\n" );
		return( strsave(s) );
		break;
	}
}

/*  decode the information in the gecos field of /etc/passwd
 *  another hacky section of code, but given the format the stuff is in...
 */

decode( pers )

    struct  person	*pers;

{
	struct  passwd		*pwdt = pers->pwd;
	char			buffer[ 256 ],  *bp,  *gp,  *lp;
	char			*phone();
	int			alldigits;
	int			len;
	int			i;

	pers->realname = NULLSTR;
	pers->office = NULLSTR;
	pers->officephone = NULLSTR;
	pers->homephone = NULLSTR;
	pers->random = NULLSTR;
	if(  pwdt != NILPWD )  {
	    gp = pwdt->pw_gecos;
	    bp = &buffer[ 0 ];
	    if( *gp == ASTERISK )  {
		gp++;
	    }
	    while(  (*gp != NULL)  &&  (*gp != COMMA)  )  {	/* name */
		if( *gp == SAMENAME )  {
		    lp = pwdt->pw_name;
		    *bp++ = CAPITALIZE(*lp++);
		    while( *lp != NULL )  {
			*bp++ = *lp++;
		    }
		}
		else  {
		    *bp++ = *gp;
		}
		gp++;
	    }
	    *bp = NULL;
	    pers->realname = malloc( strlen( &buffer[0] ) + 1 );
	    strcpy( pers->realname, &buffer[0] );
	    if( *gp++ == COMMA )  {			/* office, supposedly */
		alldigits = 1;
		bp = &buffer[ 0 ];
		while(  (*gp != NULL)  &&  (*gp != COMMA)  )  {
		    *bp = *gp++;
		    alldigits = alldigits && ('0' <= *bp) && (*bp <= '9');
		    bp++;
		}
		*bp = NULL;
		len = strlen( &buffer[0] );
		if( buffer[ len - 1 ]  ==  TECHSQ )  {
		    strcpy( &buffer[ len - 1 ], " Tech Sq." );
		    pers->office = malloc( len + 9 );
		    strcpy( pers->office, &buffer[0] );
		}
		else  {
		    if( buffer[ len - 1 ] == MIT )  {
			strcpy( &buffer[ len - 1 ], " MIT" );
			pers->office = malloc( len + 4 );
			strcpy( pers->office, &buffer[0] );
		    }
		    else  {
			if( buffer[ len - 1 ] == 'L' )  {
			    strcpy( &buffer[ len - 1 ], " LBL" );
			    pers->office = malloc( len + 4 );
			    strcpy( pers->office, &buffer[0] );
			}
			else  {
			    if( alldigits )  {
				if( len == 4 )  {
				    pers->officephone = phone(&buffer[0], len);
				}
				else  {
				    if(  (len == 7) || (len == 10)  )  {
					pers->homephone = phone(&buffer[0],len);
				    }
				}
			    }
			    else  {
				pers->random = malloc( len + 1 );
				strcpy( pers->random, &buffer[0] );
			    }
			}
		    }
		}
		if( *gp++ == COMMA )  {	    /* office phone, theoretically */
		    bp = &buffer[ 0 ];
		    alldigits = 1;
		    while(  (*gp != NULL)  &&  (*gp != COMMA)  )  {
			*bp = *gp++;
			alldigits = alldigits && ('0' <= *bp) && (*bp <= '9');
			bp++;
		    }
		    *bp = NULL;
		    len = strlen( &buffer[0] );
		    if( alldigits )  {
			if(  len != 4  )  {
			    if(  (len == 7) || (len == 10)  )  {
				pers->homephone = phone( &buffer[0], len );
			    }
			    else  {
				pers->random = malloc( len + 1 );
				strcpy( pers->random, &buffer[0] );
			    }
			}
			else  {
				pers->officephone = phone( &buffer[0], len );
			}
		    }
		    else  {
			pers->random = malloc( len + 1 );
			strcpy( pers->random, &buffer[0] );
		    }
		    if( *gp++ == COMMA )  {		/* home phone?? */
			bp = &buffer[ 0 ];
			alldigits = 1;
			    while(  (*gp != NULL)  &&  (*gp != COMMA)  )  {
				*bp = *gp++;
				alldigits = alldigits && ('0' <= *bp) &&
							(*bp <= '9');
				bp++;
			    }
			*bp = NULL;
			len = strlen( &buffer[0] );
			if( alldigits  &&  ( (len == 7) || (len == 10) )  )  {
			    if( *pers->homephone != NULL )  {
				pers->officephone = pers->homephone;
			    }
			    pers->homephone = phone( &buffer[0], len );
			}
			else  {
			    pers->random = malloc( strlen( &buffer[0] ) + 1 );
			    strcpy( pers->random, &buffer[0] );
			}
		    }
		}
	    }
	    if( pers->loggedin == 0 )  {
		findwhen( pers );
	    }
	    else  {
		findidle( 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(  ( lf = open(LASTLOG, 0) ) >= 0  )  {
	    llopenerr = 0;
	}
	else  {
	    fprintf( stderr, "finger: lastlog open error\n" );
	    llopenerr = 1;
	}
}


findwhen( pers )

    struct  person	*pers;
{
	struct  passwd		*pwdt = pers->pwd;
	struct  lastlog		ll;
	int			llsize = sizeof ll;
	int			i;

	if( !llopenerr )  {
	    lseek( lf, pwdt->pw_uid*llsize, 0 );
	    if ((i = read( lf, (char *) &ll, llsize )) == llsize) {
		    for( i = 0; i < LMAX; i++ )  {
			pers->tty[ i ] = ll.ll_line[ i ];
		    }
		    pers->tty[ LMAX ] = NULL;
		    pers->loginat = ll.ll_time;
	    }
	    else  {
		if (i != 0)
			fprintf(stderr, "finger: lastlog read error\n");
		pers->tty[ 0 ] = NULL;
		pers->loginat = 0L;
	    }
	}
	else  {
	    pers->tty[ 0 ] = NULL;
	    pers->loginat = 0L;
	}
}


fwclose()
{
	if( !llopenerr )  {
	    close( lf );
	}
}

/*  find the idle time of a user by doing a stat on /dev/histty,
 *  where histty has been gotten from USERLOG, supposedly.
 */

findidle( pers )

    struct  person	*pers;
{
	struct  stat		ttystatus;
	struct  passwd		*pwdt = pers->pwd;
	char			buffer[ 20 ];
	char			*TTY = "/dev/";
	int			TTYLEN = strlen( TTY );
	int			i;

	strcpy( &buffer[0], TTY );
	i = 0;
	do  {
	    buffer[ TTYLEN + i ] = pers->tty[ i ];
	}  while( ++i <= LMAX );
	if(  stat( &buffer[0], &ttystatus ) >= 0  )  {
	    time( &tloc );
	    if( tloc < ttystatus.st_atime )  {
		pers->idletime = 0L;
	    }
	    else  {
		pers->idletime = tloc - ttystatus.st_atime;
	    }
	}
	else  {
	    fprintf( stderr, "finger: error STATing %s\n", &buffer[0] );
	    exit( 4 );
	}
}

/*  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;
{
	struct  tm		*gmtime();
	struct  tm		*delta;

	delta = gmtime( dt );
	if( delta->tm_yday == 0 )  {
	    if( delta->tm_hour == 0 )  {
		if( delta->tm_min >= 10 )  {
		    printf( "  %2.2d", delta->tm_min );
		}
		else  {
		    if( delta->tm_min == 0 )  {
			printf( "    " );
		    }
		    else  {
			printf( "   %1.1d", delta->tm_min );
		    }
		}
	    }
	    else  {
		if( delta->tm_hour >= 10 )  {
		    printf( " %2.2d:", delta->tm_hour );
		}
		else  {
		    printf( "%1.1d:%02.2d", delta->tm_hour, delta->tm_min );
		}
	    }
	}
	else  {
	    if( delta->tm_yday >= 100 )  {
		printf( "%3.3dd", delta->tm_yday );
	    }
	    else if( delta->tm_yday >= 10 )  {
		printf( "%2.2d d", delta->tm_yday );
	    }
	    else  {
		printf( "%1.1dday", delta->tm_yday );
	    }
	}
}


matchcmp( gname, login, given )

    char		*gname;
    char		*login;
    char		*given;
{
	char		buffer[ 20 ];
	char		c;
	int		flag,  i,  unfound;

	if( !match )  {
	    return( 0 );
	}
	else  {
	    if(  namecmp( login, given )  )  {
		return( 1 );
	    }
	    else if (*gname == '\0')
		return (0);
	    else  {
		if( *gname == ASTERISK )  {
		    gname++;
		}
		flag = 1;
		i = 0;
		unfound = 1;
		while(  unfound  )  {
		    if( flag )  {
			c = *gname++;
			if( c == SAMENAME )  {
			    flag = 0;
			    c = *login++;
			}
			else  {
			    unfound = (*gname != COMMA)  &&  (*gname != NULL);
			}
		    }
		    else {
			c = *login++;
			if( c == NULL )  {
			    if(  (*gname == COMMA)  ||  (*gname == NULL)  )  {
				break;
			    }
			    else  {
				flag = 1;
				continue;
			    }
			}
		    }
		    if( c == BLANK )  {
			buffer[i++] = NULL;
			if(  namecmp( buffer, given )  )  {
			    return( 1 );
			}
			i = 0;
			flag = 1;
		    }
		    else  {
			buffer[ i++ ] = c;
		    }
		}
		buffer[i++] = NULL;
		if(  namecmp( buffer, given )  )  {
		    return( 1 );
		}
		else  {
		    return( 0 );
		}
	    }
	}
}

namecmp( name1, name2 )

    char		*name1;
    char		*name2;
{
	char		c1,  c2;

	c1 = *name1;
	if( (('A' <= c1) && (c1 <= 'Z')) || (('a' <= c1) && (c1 <= 'z')) )  {
	    c1 = CAPITALIZE( c1 );
	}
	c2 = *name2;
	if( (('A' <= c2) && (c2 <= 'Z')) || (('a' <= c2) && (c2 <= 'z')) )  {
	    c2 = CAPITALIZE( c2 );
	}
	while( c1 == c2 )  {
	    if( c1 == NULL )  {
		return( 1 );
	    }
	    c1 = *++name1;
	    if( (('A'<=c1) && (c1<='Z')) || (('a'<=c1) && (c1<='z')) )  {
		c1 = CAPITALIZE( c1 );
	    }
	    c2 = *++name2;
	    if( (('A'<=c2) && (c2<='Z')) || (('a'<=c2) && (c2<='z')) )  {
		c2 = CAPITALIZE( c2 );
	    }
	}
	if( *name1 == NULL )  {
	    while(  ('0' <= *name2)  &&  (*name2 <= '9')  )  {
		name2++;
	    }
	    if( *name2 == NULL )  {
		return( 1 );
	    }
	}
	else  {
	    if( *name2 == NULL )  {
		while(  ('0' <= *name1)  &&  (*name1 <= '9')  )  {
		    name1++;
		}
		if( *name1 == NULL )  {
		    return( 1 );
		}
	    }
	}
	return( 0 );
}

char  *strsave( s )

    char		*s;
{
	char		*malloc();
	char		*p;

	p = malloc( strlen( s ) + 1 );
	strcpy( p, s );
}
/*	Given a tty name, return a pointer to a string describing
 *	that tty's location. LOCFILE is searched, with possible
 *	`@' indirections.
 */

#define	LOCFILE	"/u/turists/trafton/finger/new/finger/ttyloc"
#define LOCLEN	80

gtloc (pers)

    struct  person	*pers;
{
	FILE			*f;
	char			*location;
	static char		ebuf[LOCLEN];
	char			s[80], type, tname[LOCLEN];
	char			*malloc();

	pers->ttyloc = NULLSTR;
	location = malloc( LOCLEN );
	if( f = fopen ( LOCFILE, "r" ) ) 
	{
	    while( fgets( s, sizeof s, f) )  {
		sscanf (s, "%[^@=]%c%[^\n]", tname, &type, location);
		if( !strcmp (tname, pers->tty) )  {
		    break;
		}
 	    }
	    if (feof (f) )  {
		fclose (f);
		return( 0 );
	    }
	    fclose (f);
	    switch  (type)  {

		case '@':
			if( f = fopen( location, "r" ) )  {
			    if( fscanf( f, "%[^\n]", location) )  {
				fclose (f);
				break;
			    }
			}
			return( 0 );
			
		case '=':
			break;
			
		default:	/* Can't really happen */
			return( 0 );
	    }
	    pers->ttyloc = malloc( strlen( location) );
	    strcpy( pers->ttyloc, location);
	    return( 1 );
	}
	else  {
	    return( 0 );
	}
}
