/*
 * This finger program is public domain, written by Mr A.N.Onymous at
 * Imperial College London Dept. of Computing.
 */

#ifndef lint
static char SccsId[] = "@(#)finger.c	1.3 (IC DoC) 1/28/86";
#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 -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.
 */

#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	<time.h>

#define UTMP_FILE	"/etc/utmp"

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

#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 */
#ifdef ICDOC
#ifdef	ERCC
#define		JCMB		'J'	/* J.C.M.B. */
#endif ERCC
#define		HUXLEY		'H'	/* huxley building */
#endif ICDOC
#define		CORY		'C'	/* cory hall office */
#define		EVANS		'E'	/* evans hall office */
#define		LINEBREAK	012	/* line feed */
#define		NULLSTR		""	/* the null string, opposed to NULL */
#define		SAMENAME	'&'	/* repeat login name in real name */
#define		TALKABLE	0222	/* tty is writeable if 222 mode */

struct  person  {			/* one for each person fingered */
	char		name[NMAX+1];	/* login name */
	char		tty[LMAX+1];	/* NULL terminated tty line */
	long		loginat;	/* time of login (possibly last) */
	long		idletime;	/* how long idle (if logged in) */
	short int	loggedin;	/* flag for being logged in */
	short int	writeable;	/* flag for tty being writeable */
	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 */
	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 );

#define HAVE_LASTLOG
#ifdef HAVE_LASTLOG
char		LASTLOG[]	= "/var/adm/lastlog";	/* last login info */
#endif
#ifdef UTMP_FILE
char		USERLOG[]	= UTMP_FILE;		/* who is logged in */
#else
char		USERLOG[]	= "/etc/utmp";		/* who is logged in */
#endif
char		outbuf[BUFSIZ];				/* output buffer */
char		*ctime();

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		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 */
	char			*PROJ = "/.project";	/* what project file */
	int			PLANLEN = strlen( PLAN );
	int			PROJLEN = strlen( PROJ );
	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 '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, "finger: Usage -- 'finger [-bfhilmpqsw] [login1 [login2 ...] ]'\n" );
				exit( 1 );
			}
		}
	    }
	}
	else  {
	    fngrlogin = 1;
	}
	if( unquick )  {
	    time( &tloc );
	}
	else  {
	    if( idle )  {
		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  )  {
#ifdef USER_PROCESS
		user.ut_type = EMPTY;
		while( user.ut_type != USER_PROCESS )
#else
		user.ut_name[0] = NULL;
		while( user.ut_name[0] == NULL )
#endif
		{
		    if( read( uf, (char *) &user, usize ) != usize )  {
			printf( "\nNo one logged on\n" );
			exit( 0 );
		    }
		}
#ifdef DEBUG
		printf("name = %s, line = %s\n", user.ut_name, user.ut_line);
#endif
		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;
		person1->loginat = user.ut_time;
		person1->pwd = NILPWD;
		person1->loggedin = 1;
		numnames++;
		p = person1;
		while( read( uf, (char *) &user, usize ) == usize )  {
#ifdef USER_PROCESS
		    if( user.ut_type != USER_PROCESS )  continue;
#else
		    if( user.ut_name[0] == NULL )  continue;
#endif
		    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;
		    p->loginat = user.ut_time;
		    p->pwd = NILPWD;
		    p->loggedin = 1;
		    numnames++;
		}
		p->link = NILPERS;
		close( uf );
	    }
	    else  {
		fprintf( stderr, "finger: error opening %s\n", USERLOG );
		exit( 2 );
	    }

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

	    if( unquick )  {
		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;
	    if( unquick )  {
		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 )  {
#ifdef USER_PROCESS
		    if( user.ut_type != USER_PROCESS )  continue;
#else
		    if( user.ut_name[0] == NULL )  continue;
#endif
		    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];
				}
				pend->tty[ NMAX ] = 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;
				    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 );
		    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( 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" );
		}
		printf( "\n" );
	    }
	}
	p = person1;
	do  {
	    if( unquick )  {
		if( unshort )  {
		    personprint( p );
		    if( p->pwd != NILPWD )  {
			if( hack )  {
			    s = malloc(strlen((p->pwd)->pw_dir) + PROJLEN + 1 );
			    strcpy(  s, (p->pwd)->pw_dir  );
			    strcat( s, PROJ );
			    if(  ( fp = fopen( s, "r") )  != NULL  )  {
				printf( "Project: " );
				while(  ( c = getc(fp) )  !=  EOF  )  {
				    if( c == LINEBREAK )  {
					break;
				    }
				    putc( c, stdout );
				}
				fclose( fp );
				printf( "\n" );
			    }
			}
			if( plan )  {
			    s = malloc( strlen( (p->pwd)->pw_dir ) + PLANLEN + 1 );
			    strcpy(  s, (p->pwd)->pw_dir  );
			    strcat( s, PLAN );
#ifndef MINT
			    if(  ( fp = fopen( s, "r") )  == NULL  )  {
#else
			    if(  ( fp = fopen( s, "rb") )  == NULL  )  {
#endif
				printf( "No Plan.\n" );
			    }
			    else  {
				printf( "Plan:\n" );
				while(  ( c = getc(fp) )  !=  EOF  )  {
				    putc( c, stdout );
				}
				fclose( fp );
			    }
			}
		    }
		    if( p->link != NILPERS )  {
			printf( "\n" );
		    }
		}
		else  {
		    shortprint( p );
		}
	    }
	    else  {
		quickprint( p );
	    }
	    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 on quick format giving just name, tty, login time
 *  and idle time if idle is set.
 */

quickprint( pers )

    struct  person		*pers;
{
	int			idleprinted;

	printf( "%-*.*s", NMAX, NMAX, pers->name );
	printf( "  " );
	if( pers->loggedin )  {
	    if( idle )  {
		findidle( pers );
		if( pers->writeable )  {
		    printf(  " %-*.*s %-16.16s", LMAX, LMAX, 
			pers->tty, ctime( &pers->loginat )  );
		}
		else  {
		    printf(  "*%-*.*s %-16.16s", LMAX, LMAX, 
			pers->tty, ctime( &pers->loginat )  );
		}
		printf( "   " );
		idleprinted = ltimeprint( &pers->idletime );
	    }
	    else  {
		printf(  " %-*.*s %-16.16s", LMAX, LMAX, 
		    pers->tty, ctime( &pers->loginat )  );
	    }
	}
	else  {
	    printf( "          Not Logged In" );
	}
	printf( "\n" );
}


/*  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( wide )  {
	    if(  strlen( pers->realname ) > 0  )  {
		printf( " %-20.20s", pers->realname );
	    }
	    else  {
		printf( "        ???          " );
	    }
	}
	if( pers->loggedin )  {
	    if( pers->writeable )  {
		printf( "  " );
	    }
	    else  {
		printf( " *" );
	    }
	}
	else  {
	    printf( "  " );
	}
	if(  strlen( pers->tty ) > 0  )  {
	  strcpy( buf, pers->tty );
	  if(  (buf[0] == 's')  &&  (buf[1] == 'x')  &&  (buf[2] == 't')  )  {
	     printf( "lx " );
	  }
	  else {
	    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( "%-2.2s ", buf );
	  }
	}
	else  {
	    printf( "   " );
	}
	strcpy(buf, ctime(&pers->loginat));
	if( pers->loggedin )  {
	    stimeprint( &pers->idletime );
	    offset = 7;
	    for( i = 4; i < 19; i++ )  {
		buf[i] = buf[i + offset];
	    }
	    printf( " %-9.9s ", buf );
	}
	else if (pers->loginat == 0)
	    printf(" < .  .  .  . >");
	else if (tloc - pers->loginat >= 180 * 24 * 60 * 60)
	    printf( " <%-6.6s, %-4.4s>", buf+4, buf+20 );
	else
	    printf(" <%-12.12s>", buf+4);
	len = strlen( pers->homephone );
	if(  dialup  &&  (len > 0)  )  {
	    if( len == 8 )  {
		printf( "             " );
	    }
	    else  {
		if( len == 12 )  {
		    printf( "         " );
		}
		else {
		    for( i = 1; i <= 21 - len; i++ )  {
			printf( " " );
		    }
		}
	    }
	    printf( "%s", pers->homephone );
	}
	else  {
	    if(  strlen( pers->office ) > 0  )  {
		printf( " %-11.11s", pers->office );
		if(  strlen( pers->officephone ) > 0  )  {
		    printf( " %8.8s", pers->officephone );
		}
		else  {
		    if( len == 8 )  {
			printf( " %8.8s", pers->homephone );
		    }
		}
	    }
	    else  {
		if(  strlen( pers->officephone ) > 0  )  {
		    printf( "             %8.8s", pers->officephone );
		}
		else  {
		    if( len == 8 )  {
			printf( "             %8.8s", pers->homephone );
		    }
		    else  {
			if( len == 12 )  {
			    printf( "         %12.12s", pers->homephone );
			}
		    }
		}
	    }
	}
	printf( "\n" );
}


/*  print out a person in long format giving all possible information.
 *  directory and shell are inhibited if unbrief is clear.
 */

personprint( pers )

    struct  person	*pers;
{
	struct  passwd		*pwdt = pers->pwd;
	int			idleprinted;

	if( pwdt == NILPWD )  {
	    printf( "Login name: %-10s", pers->name );
	    printf( "			" );
	    printf( "In real life: ???\n");
	    return;
	}
	printf( "Login name: %-10s", pwdt->pw_name );
	if( pers->loggedin )  {
	    if( pers->writeable )  {
		printf( "			" );
	    }
	    else  {
		printf( "	(messages off)	" );
	    }
	}
	else  {
	    printf( "			" );
	}
	if(  strlen( pers->realname ) > 0  )  {
	    printf( "In real life: %-s", pers->realname );
	}
	if(  strlen( pers->office ) > 0  )  {
	    printf( "\nOffice: %-.11s", pers->office );
	    if(  strlen( pers->officephone ) > 0  )  {
		printf( ", %s", pers->officephone );
		if(  strlen( pers->homephone ) > 0  )  {
		    printf( "		Home phone: %s", pers->homephone );
		}
		else  {
		    if(  strlen( pers->random ) > 0  )  {
			printf( "	%s", pers->random );
		    }
		}
	    }
	    else  {
		if(  strlen( pers->homephone ) > 0  )  {
		    printf("			Home phone: %s",pers->homephone);
		}
		if(  strlen( pers->random ) > 0  )  {
		    printf( "			%s", pers->random );
		}
	    }
	}
	else  {
	    if(  strlen( pers->officephone ) > 0  )  {
		printf( "\nPhone: %s", pers->officephone );
		if(  strlen( pers->homephone ) > 0  )  {
		    printf( "\n, %s", pers->homephone );
		    if(  strlen( pers->random ) > 0  )  {
			printf( ", %s", pers->random );
		    }
		}
		else  {
		    if(  strlen( pers->random ) > 0  )  {
			printf( "\n, %s", pers->random );
		    }
		}
	    }
	    else  {
		if(  strlen( pers->homephone ) > 0  )  {
		    printf( "\nPhone: %s", pers->homephone );
		    if(  strlen( pers->random ) > 0  )  {
			printf( ", %s", pers->random );
		    }
		}
		else  {
		    if(  strlen( pers->random ) > 0  )  {
			printf( "\n%s", pers->random );
		    }
		}
	    }
	}
	if( unbrief )  {
	    printf( "\n" );
	    printf( "Directory: %-25s", pwdt->pw_dir );
	    if(  strlen( pwdt->pw_shell ) > 0  )  {
		printf( "	Shell: %-s", pwdt->pw_shell );
	    }
	}
	if( pers->loggedin )  {
	    register char *ep = ctime( &pers->loginat );
	    printf("\nOn since %15.15s on %-*.*s	", &ep[4], LMAX, LMAX, pers->tty );
	    idleprinted = ltimeprint( &pers->idletime );
	    if( idleprinted )  {
		printf( " Idle Time" );
	    }
	}
	else if (pers->loginat == 0)
#ifdef HAVE_LASTLOG
	    printf("\nNever logged in.");
#else
	    printf("\nNot logged in.");
#endif
	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, LMAX, pers->tty);
	}
	else  {
	    register char *ep = ctime( &pers->loginat );
	    printf("\nLast login %16.16s on %.*s", ep, LMAX, pers->tty );
	}
	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:
#if defined(ICDOC) || defined(BT)
		fonebuf[ 0 ] = ' ';
		fonebuf[ 1 ] = 'x';
		for( i = 0; i <= 3; i++ )  {
		    fonebuf[ 2 + i ] = *s++;
		}
		fonebuf[ 6 ] = NULL;
#else
		fonebuf[ 0 ] = ' ';
		fonebuf[ 1 ] = 'x';
		fonebuf[ 2 ] = '2';
		fonebuf[ 3 ] = '-';
		for( i = 0; i <= 3; i++ )  {
		    fonebuf[ 4 + i ] = *s++;
		}
		fonebuf[ 8 ] = NULL;
#endif
		return( strsave( &fonebuf[0] ) );
		break;

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

            case  6:
	    case  7:
		for( i = 0; i <= 2; i++ )  {
		    fonebuf[ i ] = *s++;
		}
#if defined(ICDOC) || defined(BT)
		fonebuf[ 3 ] = ' ';		/* BT plc standard! */
#else
		fonebuf[ 3 ] = '-';
#endif
		for( i = 0; i <= 3; i++ )  {
		    fonebuf[ 4 + i ] = *s++;
		}
		fonebuf[ 8 ] = NULL;
		return( strsave( &fonebuf[0] ) );
		break;

	    case 10:
#ifdef BT
		for( i = 0; i <= 3; i++ )  {
		    fonebuf[ i ] = *s++;
		}
		fonebuf[ 4 ] = ' ';
		for( i = 0; i <= 5; i++ )  {
		    fonebuf[ 5 + i ] = *s++;
		}
		fonebuf[ 11 ] = NULL;
#else
		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;
#endif
		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;
#ifdef DEBUG
fprintf(stderr, "gecos = %s\n", gp);
#endif
	    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] );
#ifdef DEBUG
fprintf(stderr, "name = %s\n", pers->realname);
#endif
	    if( *gp++ == COMMA )  {			/* office, supposedly */
#ifdef DEBUG
fprintf(stderr, "office...");
#endif
		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] );
#ifdef DEBUG
fprintf(stderr, "got %s...", buffer);
#endif
#ifdef ICDOC
		if( buffer[ len - 1 ]  ==  HUXLEY )  {
#ifdef	ERCC
		    strcpy( &buffer[ len - 1 ], " JCMB" );
#else
		    strcpy( &buffer[ len - 1 ], " Huxley" );
#endif	ERCC
		    pers->office = malloc( len + 5 );
		    strcpy( pers->office, &buffer[0] );
		} else {
		if( buffer[ len - 1 ]  ==  CORY )  {
		    strcpy( &buffer[ len - 1 ], " Cory" );
		    pers->office = malloc( len + 5 );
		    strcpy( pers->office, &buffer[0] );
		}
		else  {
		    if( buffer[ len - 1 ] == EVANS )  {
			strcpy( &buffer[ len - 1 ], " Evans" );
			pers->office = malloc( len + 6 );
			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] );
			    }
			}
		    }
		}
/*}*/
#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->office = malloc( len + 1 );
		    strcpy( pers->office, &buffer[0] );
		}
#endif
#ifdef DEBUG
fprintf(stderr, "ended up with %s\n", pers->office);
#endif
		if( *gp++ == COMMA )  {	    /* office phone, theoretically */
#ifdef DEBUG
fprintf(stderr, "Processing %s\n", gp);
#endif
		    bp = &buffer[ 0 ];
		    alldigits = 1;
		    while(  (*gp != NULL)  &&  (*gp != COMMA)  )  {
			*bp = *gp++;
			alldigits = alldigits && ('0' <= *bp) && (*bp <= '9');
			bp++;
		    }
		    *bp = NULL;
#ifdef DEBUG
fprintf(stderr, "Got %s...", buffer);
#endif
		    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  {
#ifdef DEBUG
fprintf(stderr, "len == 4, so officephone\n");
#endif
				pers->officephone = phone( &buffer[0], len );
			}
		    }
		    else  {
			pers->random = malloc( len + 1 );
			strcpy( pers->random, &buffer[0] );
		    }
		    if( *gp++ == COMMA )  {		/* home phone?? */
#ifdef DEBUG
fprintf(stderr, "looking for home phone in %s\n", gp);
#endif
			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()
{
#ifdef HAVE_LASTLOG
	if(  ( lf = open(LASTLOG, 0) ) >= 0  )  {
	    llopenerr = 0;
	}
	else  {
	    fprintf( stderr, "finger: lastlog open error\n" );
	    llopenerr = 1;
	}
#else
	llopenerr = 1;
#endif
}


findwhen( pers )

    struct  person	*pers;
{
#ifdef HAVE_LASTLOG
	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;
	}
#else
	pers->tty[ 0 ] = NULL;
	pers->loginat = 0L;
#endif
}


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;
	    }
	    if(  (ttystatus.st_mode & TALKABLE) == TALKABLE  )  {
		pers->writeable = 1;
	    }
	    else  {
		pers->writeable = 0;
	    }
	}
	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( "%3.3d:", delta->tm_hour );
		}
		else  {
		    printf( "%1.1d:%02.2d", 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.
 */

ltimeprint( dt )

    long	*dt;
{
	struct  tm		*gmtime();
	struct  tm		*delta;
	int			printed = 1;

	delta = gmtime( dt );
	if( delta->tm_yday == 0 )  {
	    if( delta->tm_hour == 0 )  {
		if( delta->tm_min >= 10 )  {
		    printf( "%2d minutes", delta->tm_min );
		}
		else  {
		    if( delta->tm_min == 0 )  {
			if( delta->tm_sec > 10 )  {
			    printf( "%2d seconds", delta->tm_sec );
			}
			else  {
			    printed = 0;
			}
		    }
		    else  {
			if( delta->tm_min == 1 )  {
			    if( delta->tm_sec == 1 )  {
				printf( "%1d minute %1d second",
				    delta->tm_min, delta->tm_sec );
			    }
			    else  {
				printf( "%1d minute %d seconds",
				    delta->tm_min, delta->tm_sec );
			    }
			}
			else  {
			    if( delta->tm_sec == 1 )  {
				printf( "%1d minutes %1d second",
				    delta->tm_min, delta->tm_sec );
			    }
			    else  {
				printf( "%1d minutes %d seconds",
				    delta->tm_min, delta->tm_sec );
			    }
			}
		    }
		}
	    }
	    else  {
		if( delta->tm_hour >= 10 )  {
		    printf( "%2d hours", delta->tm_hour );
		}
		else  {
		    if( delta->tm_hour == 1 )  {
			if( delta->tm_min == 1 )  {
			    printf( "%1d hour %1d minute",
				delta->tm_hour, delta->tm_min );
			}
			else  {
			    printf( "%1d hour %2d minutes",
				delta->tm_hour, delta->tm_min );
			}
		    }
		    else  {
			if( delta->tm_min == 1 )  {
			    printf( "%1d hours %1d minute",
				delta->tm_hour, delta->tm_min );
			}
			else  {
			    printf( "%1d hours %2d minutes",
				delta->tm_hour, delta->tm_min );
			}
		    }
		}
	    }
	}
	else  {
		if( delta->tm_yday >= 10 )  {
		    printf( "%2d days", delta->tm_yday );
		}
		else  {
		    if( delta->tm_yday == 1 )  {
			if( delta->tm_hour == 1 )  {
			    printf( "%1d day %1d hour",
				delta->tm_yday, delta->tm_hour );
			}
			else  {
			    printf( "%1d day %2d hours",
				delta->tm_yday, delta->tm_hour );
			}
		    }
		    else  {
			if( delta->tm_hour == 1 )  {
			    printf( "%1d days %1d hour",
				delta->tm_yday, delta->tm_hour );
			}
			else  {
			    printf( "%1d days %2d hours",
				delta->tm_yday, delta->tm_hour );
			}
		    }
		}
	}
	return( printed );
}


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 );
}
