xref: /original-bsd/usr.bin/finger/finger.c (revision 92d3de31)
1 static char *sccsid = "@(#)finger.c	4.3 (Berkeley) 12/13/82";
2 
3 /*  This is a finger program.  It prints out useful information about users
4  *  by digging it up from various system files.  It is not very portable
5  *  because the most useful parts of the information (the full user name,
6  *  office, and phone numbers) are all stored in the VAX-unused gecos field
7  *  of /etc/passwd, which, unfortunately, other UNIXes use for other things.
8  *
9  *  There are three output formats, all of which give login name, teletype
10  *  line number, and login time.  The short output format is reminiscent
11  *  of finger on ITS, and gives one line of information per user containing
12  *  in addition to the minimum basic requirements (MBR), the full name of
13  *  the user, his idle time and office location and phone number.  The
14  *  quick style output is UNIX who-like, giving only name, teletype and
15  *  login time.  Finally, the long style output give the same information
16  *  as the short (in more legible format), the home directory and shell
17  *  of the user, and, if it exits, a copy of the file .plan in the users
18  *  home directory.  Finger may be called with or without a list of people
19  *  to finger -- if no list is given, all the people currently logged in
20  *  are fingered.
21  *
22  *  The program is validly called by one of the following:
23  *
24  *	finger			{short form list of users}
25  *	finger -l		{long form list of users}
26  *	finger -b		{briefer long form list of users}
27  *	finger -q		{quick list of users}
28  *	finger -i		{quick list of users with idle times}
29  *	finger namelist		{long format list of specified users}
30  *	finger -s namelist	{short format list of specified users}
31  *	finger -w namelist	{narrow short format list of specified users}
32  *
33  *  where 'namelist' is a list of users login names.
34  *  The other options can all be given after one '-', or each can have its
35  *  own '-'.  The -f option disables the printing of headers for short and
36  *  quick outputs.  The -b option briefens long format outputs.  The -p
37  *  option turns off plans for long format outputs.
38  */
39 
40 #include	<sys/types.h>
41 #include	<sys/stat.h>
42 #include	<sgtty.h>
43 #include	<utmp.h>
44 #include	<signal.h>
45 #include	<pwd.h>
46 #include	<stdio.h>
47 #include	<sccs.h>
48 #include	<lastlog.h>
49 #include	<time.h>
50 
51 struct	utmp	utmp;	/* for sizeof */
52 #define NMAX sizeof(utmp.ut_name)
53 #define LMAX sizeof(utmp.ut_line)
54 
55 #define		ASTERISK	'*'	/* ignore this in real name */
56 #define		BLANK		' '	/* blank character (i.e. space) */
57 #define		CAPITALIZE	0137&	/* capitalize character macro */
58 #define		COMMA		','	/* separator in pw_gecos field */
59 #define		COMMAND		'-'	/* command line flag char */
60 #define		CORY		'C'	/* cory hall office */
61 #define		EVANS		'E'	/* evans hall office */
62 #define		LINEBREAK	012	/* line feed */
63 #define		NULLSTR		""	/* the null string, opposed to NULL */
64 #define		SAMENAME	'&'	/* repeat login name in real name */
65 #define		TALKABLE	0222	/* tty is writeable if 222 mode */
66 
67 struct  person  {			/* one for each person fingered */
68 	char		name[NMAX+1];	/* login name */
69 	char		tty[LMAX+1];	/* NULL terminated tty line */
70 	long		loginat;	/* time of login (possibly last) */
71 	long		idletime;	/* how long idle (if logged in) */
72 	short int	loggedin;	/* flag for being logged in */
73 	short int	writeable;	/* flag for tty being writeable */
74 	char		*realname;	/* pointer to full name */
75 	char		*office;	/* pointer to office name */
76 	char		*officephone;	/* pointer to office phone no. */
77 	char		*homephone;	/* pointer to home phone no. */
78 	char		*random;	/* for any random stuff in pw_gecos */
79 	struct  passwd	*pwd;		/* structure of /etc/passwd stuff */
80 	struct  person	*link;		/* link to next person */
81 };
82 
83 struct  passwd			*NILPWD = 0;
84 struct  person			*NILPERS = 0;
85 
86 int		persize		= sizeof( struct person );
87 int		pwdsize		= sizeof( struct passwd );
88 
89 char		LASTLOG[]	= "/usr/adm/lastlog";	/* last login info */
90 char		USERLOG[]	= "/etc/utmp";		/* who is logged in */
91 char		outbuf[BUFSIZ];				/* output buffer */
92 char		*ctime();
93 
94 int		unbrief		= 1;		/* -b option default */
95 int		header		= 1;		/* -f option default */
96 int		hack		= 1;		/* -h option default */
97 int		idle		= 0;		/* -i option default */
98 int		large		= 0;		/* -l option default */
99 int		match		= 1;		/* -m option default */
100 int		plan		= 1;		/* -p option default */
101 int		unquick		= 1;		/* -q option default */
102 int		small		= 0;		/* -s option default */
103 int		wide		= 1;		/* -w option default */
104 
105 int		lf;
106 int		llopenerr;
107 
108 long		tloc;				/* current time */
109 
110 
111 
112 main( argc, argv )
113 
114     int		argc;
115     char	*argv[];
116 
117 {
118 	FILE			*fp,  *fopen();		/* for plans */
119 	struct  passwd		*getpwent();		/* read /etc/passwd */
120 	struct  person		*person1,  *p,  *pend;	/* people */
121 	struct  passwd		*pw;			/* temporary */
122 	struct  utmp		user;			/*   ditto   */
123 	char			*malloc();
124 	char			*s,  *pn,  *ln;
125 	char			c;
126 	char			*PLAN = "/.plan";	/* what plan file is */
127 	char			*PROJ = "/.project";	/* what project file */
128 	int			PLANLEN = strlen( PLAN );
129 	int			PROJLEN = strlen( PROJ );
130 	int			numnames = 0;
131 	int			orgnumnames;
132 	int			uf;
133 	int			usize = sizeof user;
134 	int			unshort;
135 	int			i, j;
136 	int			fngrlogin;
137 
138 	setbuf( stdout, outbuf );			/* buffer output */
139 
140     /*  parse command line for (optional) arguments */
141 
142 	i = 1;
143 	if(  strcmp( *argv, "sh" )  )  {
144 	    fngrlogin = 0;
145 	    while( i++ < argc  &&  (*++argv)[0] == COMMAND )  {
146 		for( s = argv[0] + 1; *s != NULL; s++ )  {
147 			switch  (*s)  {
148 
149 			    case 'b':
150 				    unbrief = 0;
151 				    break;
152 
153 			    case 'f':
154 				    header = 0;
155 				    break;
156 
157 			    case 'h':
158 				    hack = 0;
159 				    break;
160 
161 			    case 'i':
162 				    idle = 1;
163 				    unquick = 0;
164 				    break;
165 
166 			    case 'l':
167 				    large = 1;
168 				    break;
169 
170 			    case 'm':
171 				    match = 0;
172 				    break;
173 
174 			    case 'p':
175 				    plan = 0;
176 				    break;
177 
178 			    case 'q':
179 				    unquick = 0;
180 				    break;
181 
182 			    case 's':
183 				    small = 1;
184 				    break;
185 
186 			    case 'w':
187 				    wide = 0;
188 				    break;
189 
190 			    default:
191 				fprintf( stderr, "finger: Usage -- 'finger [-bfhilmpqsw] [login1 [login2 ...] ]'\n" );
192 				exit( 1 );
193 			}
194 		}
195 	    }
196 	}
197 	else  {
198 	    fngrlogin = 1;
199 	}
200 	if( unquick )  {
201 	    time( &tloc );
202 	}
203 	else  {
204 	    if( idle )  {
205 		time( &tloc );
206 	    }
207 	}
208 
209     /*  i > argc means no login names given so get them by reading USERLOG */
210 
211 	if(  (i > argc)  ||  fngrlogin  )  {
212 	    unshort = large;
213 	    if(  ( uf = open(USERLOG, 0) ) >= 0  )  {
214 		user.ut_name[0] = NULL;
215 		while( user.ut_name[0] == NULL )  {
216 		    if( read( uf, (char *) &user, usize ) != usize )  {
217 			printf( "\nNo one logged on\n" );
218 			exit( 0 );
219 		    }
220 		}
221 		person1 = (struct person  *) malloc( persize );
222 		for( j = 0; j < NMAX; j++ )  {
223 		    person1->tty[j] = user.ut_line[j];
224 		    person1->name[j] = user.ut_name[j];
225 		}
226 		person1->name[NMAX] = NULL;
227 		person1->tty[NMAX] = NULL;
228 		person1->loginat = user.ut_time;
229 		person1->pwd = NILPWD;
230 		person1->loggedin = 1;
231 		numnames++;
232 		p = person1;
233 		while( read( uf, (char *) &user, usize ) == usize )  {
234 		    if( user.ut_name[0] == NULL )  continue;
235 		    p->link = (struct person  *) malloc( persize );
236 		    p = p->link;
237 		    for( j = 0; j < NMAX; j++ )  {
238 			p->tty[j] = user.ut_line[j];
239 			p->name[j] = user.ut_name[j];
240 		    }
241 		    p->name[NMAX] = NULL;
242 		    p->tty[NMAX] = NULL;
243 		    p->loginat = user.ut_time;
244 		    p->pwd = NILPWD;
245 		    p->loggedin = 1;
246 		    numnames++;
247 		}
248 		p->link = NILPERS;
249 		close( uf );
250 	    }
251 	    else  {
252 		fprintf( stderr, "finger: error opening %s\n", USERLOG );
253 		exit( 2 );
254 	    }
255 
256 		/*  if we are doing it, read /etc/passwd for the useful info */
257 
258 	    if( unquick )  {
259 		setpwent();
260 		fwopen();
261 		i = numnames;
262 		while(  ( (pw = getpwent()) != NILPWD )  &&  ( i > 0 )  )  {
263 		    p = person1;
264 		    do  {
265 			if( p->pwd == NILPWD )  {
266 			    if(  strcmp( p->name, pw->pw_name ) == 0  )  {
267 				p->pwd = (struct passwd  *) malloc( pwdsize );
268 				pwdcopy( p->pwd, pw );
269 				decode( p );
270 				i--;
271 			    }
272 			}
273 			p = p->link;
274 		    }  while( p != NILPERS );
275 		}
276 		fwclose();
277 		endpwent();
278 	    }
279 	}
280 
281     /* get names from command line and check to see if they're  logged in */
282 
283 	else  {
284 	    unshort = ( small == 1 ? 0 : 1 );
285 	    i++;
286 	    person1 = (struct person  *) malloc( persize );
287 	    strcpy(  person1->name, (argv++)[ 0 ]  );
288 	    person1->loggedin = 0;
289 	    person1->pwd = NILPWD;
290 	    numnames++;
291 	    p = person1;
292 	    while( i++ <= argc )  {
293 		p->link = (struct person  *) malloc( persize );
294 		p = p->link;
295 		strcpy(  p->name, (argv++)[ 0 ]  );
296 		p->loggedin = 0;
297 		p->pwd = NILPWD;
298 		numnames++;
299 	    }
300 	    p->link = NILPERS;
301 	    pend = p;
302 
303 		/*  if we are doing it, read /etc/passwd for the useful info */
304 
305 	    orgnumnames = numnames;
306 	    if( unquick )  {
307 		setpwent();
308 		while(  ( pw = getpwent() ) != NILPWD  )  {
309 		    p = person1;
310 		    i = 0;
311 		    do  {
312 			if( strcmp( p->name, pw->pw_name ) == 0    ||
313 			    matchcmp( pw->pw_gecos, pw->pw_name, p->name ) )  {
314 			    if( p->pwd == NILPWD )  {
315 				p->pwd = (struct passwd  *) malloc( pwdsize );
316 				pwdcopy( p->pwd, pw );
317 			    }
318 			    else  {	/* handle multiple logins -- append new
319 					   "duplicate" entry to end of list */
320 				pend->link = (struct person  *) malloc(persize);
321 				pend = pend->link;
322 				pend->link = NILPERS;
323 				strcpy( pend->name, p->name );
324 				pend->pwd = (struct passwd  *) malloc(pwdsize);
325 				pwdcopy( pend->pwd, pw );
326 				numnames++;
327 			    }
328 			}
329 			p = p->link;
330 		    }  while( ++i < orgnumnames );
331 		}
332 		endpwent();
333 	    }
334 
335 		/*  Now get login information */
336 
337 	    if(  ( uf = open(USERLOG, 0) ) >= 0  )  {
338 		while( read( uf, (char *) &user, usize ) == usize )  {
339 		    if( user.ut_name[0] == NULL )  continue;
340 		    p = person1;
341 		    do  {
342 			pw = p->pwd;
343 			if( pw == NILPWD )  {
344 			    i = ( strcmp( p->name, user.ut_name ) ? 0 : NMAX );
345 			}
346 			else  {
347 			    i = 0;
348 			    while(  (i < NMAX)  &&
349 				    ( pw->pw_name[i] == user.ut_name[i])  )  {
350 				if( pw->pw_name[i] == NULL )  {
351 				    i = NMAX;
352 				    break;
353 				}
354 				i++;
355 			    }
356 			}
357 			if( i == NMAX )  {
358 			    if( p->loggedin == 1 )  {
359 				pend->link = (struct person  *) malloc(persize);
360 				pend = pend->link;
361 				pend->link = NILPERS;
362 				strcpy( pend->name, p->name );
363 				for( j = 0; j < NMAX; j++ )  {
364 				    pend->tty[j] = user.ut_line[j];
365 				}
366 				pend->tty[ NMAX ] = NULL;
367 				pend->loginat = user.ut_time;
368 				pend->loggedin = 2;
369 				if(  pw == NILPWD  )  {
370 				    pend ->pwd = NILPWD;
371 				}
372 				else  {
373 				    pend->pwd = (struct passwd  *) malloc(pwdsize);
374 				    pwdcopy( pend->pwd, pw );
375 				}
376 				numnames++;
377 			    }
378 			    else  {
379 				if( p->loggedin != 2 )  {
380 				    for( j = 0; j < NMAX; j++ )  {
381 					p->tty[j] = user.ut_line[j];
382 				    }
383 				    p->tty[ NMAX ] = NULL;
384 				    p->loginat = user.ut_time;
385 				    p->loggedin = 1;
386 				}
387 			    }
388 			}
389 			p = p->link;
390 		    }  while( p != NILPERS );
391 		}
392 		fwopen();
393 		p = person1;
394 		while( p != NILPERS )  {
395 		    if( p->loggedin == 2 )  {
396 			p->loggedin = 1;
397 		    }
398 		    decode( p );
399 		    p = p->link;
400 		}
401 		fwclose();
402 		close( uf );
403 	    }
404 	    else  {
405 		fprintf( stderr, "finger: error opening %s\n", USERLOG );
406 		exit( 2 );
407 	    }
408 	}
409 
410     /* print out what we got */
411 
412 	if( header )  {
413 	    if( unquick )  {
414 		if( !unshort )  {
415 		    if( wide )  {
416 			printf(
417 "Login       Name              TTY Idle    When            Office\n" );
418 		    }
419 		    else  {
420 			printf(
421 "Login    TTY Idle    When            Office\n" );
422 		    }
423 		}
424 	    }
425 	    else  {
426 		printf( "Login      TTY            When" );
427 		if( idle )  {
428 		    printf( "             Idle" );
429 		}
430 		printf( "\n" );
431 	    }
432 	}
433 	p = person1;
434 	do  {
435 	    if( unquick )  {
436 		if( unshort )  {
437 		    personprint( p );
438 		    if( p->pwd != NILPWD )  {
439 			if( hack )  {
440 			    s = malloc(strlen((p->pwd)->pw_dir) + PROJLEN + 1 );
441 			    strcpy(  s, (p->pwd)->pw_dir  );
442 			    strcat( s, PROJ );
443 			    if(  ( fp = fopen( s, "r") )  != NULL  )  {
444 				printf( "Project: " );
445 				while(  ( c = getc(fp) )  !=  EOF  )  {
446 				    if( c == LINEBREAK )  {
447 					break;
448 				    }
449 				    putc( c, stdout );
450 				}
451 				fclose( fp );
452 				printf( "\n" );
453 			    }
454 			}
455 			if( plan )  {
456 			    s = malloc( strlen( (p->pwd)->pw_dir ) + PLANLEN + 1 );
457 			    strcpy(  s, (p->pwd)->pw_dir  );
458 			    strcat( s, PLAN );
459 			    if(  ( fp = fopen( s, "r") )  == NULL  )  {
460 				printf( "No Plan.\n" );
461 			    }
462 			    else  {
463 				printf( "Plan:\n" );
464 				while(  ( c = getc(fp) )  !=  EOF  )  {
465 				    putc( c, stdout );
466 				}
467 				fclose( fp );
468 			    }
469 			}
470 		    }
471 		    if( p->link != NILPERS )  {
472 			printf( "\n" );
473 		    }
474 		}
475 		else  {
476 		    shortprint( p );
477 		}
478 	    }
479 	    else  {
480 		quickprint( p );
481 	    }
482 	    p = p->link;
483 	}  while( p != NILPERS );
484 	exit(0);
485 }
486 
487 
488 /*  given a pointer to a pwd (pfrom) copy it to another one, allocating
489  *  space for all the stuff in it.  Note: Only the useful (what the
490  *  program currently uses) things are copied.
491  */
492 
493 pwdcopy( pto, pfrom )		/* copy relevant fields only */
494 
495     struct  passwd		*pto,  *pfrom;
496 {
497 	pto->pw_name = malloc(  strlen( pfrom->pw_name ) + 1  );
498 	strcpy( pto->pw_name, pfrom->pw_name );
499 	pto->pw_uid = pfrom->pw_uid;
500 	pto->pw_gecos = malloc(  strlen( pfrom->pw_gecos ) + 1  );
501 	strcpy( pto->pw_gecos, pfrom->pw_gecos );
502 	pto->pw_dir = malloc(  strlen( pfrom->pw_dir ) + 1  );
503 	strcpy( pto->pw_dir, pfrom->pw_dir );
504 	pto->pw_shell = malloc(  strlen( pfrom->pw_shell ) + 1  );
505 	strcpy( pto->pw_shell, pfrom->pw_shell );
506 }
507 
508 
509 /*  print out information on quick format giving just name, tty, login time
510  *  and idle time if idle is set.
511  */
512 
513 quickprint( pers )
514 
515     struct  person		*pers;
516 {
517 	int			idleprinted;
518 
519 	printf( "%-*.*s", NMAX, NMAX, pers->name );
520 	printf( "  " );
521 	if( pers->loggedin )  {
522 	    if( idle )  {
523 		findidle( pers );
524 		if( pers->writeable )  {
525 		    printf(  " %-*.*s %-16.16s", LMAX, LMAX,
526 			pers->tty, ctime( &pers->loginat )  );
527 		}
528 		else  {
529 		    printf(  "*%-*.*s %-16.16s", LMAX, LMAX,
530 			pers->tty, ctime( &pers->loginat )  );
531 		}
532 		printf( "   " );
533 		idleprinted = ltimeprint( &pers->idletime );
534 	    }
535 	    else  {
536 		printf(  " %-*.*s %-16.16s", LMAX, LMAX,
537 		    pers->tty, ctime( &pers->loginat )  );
538 	    }
539 	}
540 	else  {
541 	    printf( "          Not Logged In" );
542 	}
543 	printf( "\n" );
544 }
545 
546 
547 /*  print out information in short format, giving login name, full name,
548  *  tty, idle time, login time, office location and phone.
549  */
550 
551 shortprint( pers )
552 
553     struct  person	*pers;
554 
555 {
556 	struct  passwd		*pwdt = pers->pwd;
557 	char			buf[ 26 ];
558 	int			i,  len,  offset,  dialup;
559 
560 	if( pwdt == NILPWD )  {
561 	    printf( "%-*.*s", NMAX, NMAX,  pers->name );
562 	    printf( "       ???\n" );
563 	    return;
564 	}
565 	printf( "%-*.*s", NMAX, NMAX,  pwdt->pw_name );
566 	dialup = 0;
567 	if( wide )  {
568 	    if(  strlen( pers->realname ) > 0  )  {
569 		printf( " %-20.20s", pers->realname );
570 	    }
571 	    else  {
572 		printf( "        ???          " );
573 	    }
574 	}
575 	if( pers->loggedin )  {
576 	    if( pers->writeable )  {
577 		printf( "  " );
578 	    }
579 	    else  {
580 		printf( " *" );
581 	    }
582 	}
583 	else  {
584 	    printf( "  " );
585 	}
586 	if(  strlen( pers->tty ) > 0  )  {
587 	    strcpy( buf, pers->tty );
588 	    if(  (buf[0] == 't')  &&  (buf[1] == 't')  &&  (buf[2] == 'y')  )  {
589 		offset = 3;
590 		for( i = 0; i < 2; i++ )  {
591 		    buf[i] = buf[i + offset];
592 		}
593 	    }
594 	    if(  (buf[0] == 'd')  &&  pers->loggedin  )  {
595 		dialup = 1;
596 	    }
597 	    printf( "%-2.2s ", buf );
598 	}
599 	else  {
600 	    printf( "   " );
601 	}
602 	strcpy(buf, ctime(&pers->loginat));
603 	if( pers->loggedin )  {
604 	    stimeprint( &pers->idletime );
605 	    offset = 7;
606 	    for( i = 4; i < 19; i++ )  {
607 		buf[i] = buf[i + offset];
608 	    }
609 	    printf( " %-9.9s ", buf );
610 	}
611 	else if (pers->loginat == 0)
612 	    printf(" < .  .  .  . >");
613 	else if (tloc - pers->loginat >= 180 * 24 * 60 * 60)
614 	    printf( " <%-6.6s, %-4.4s>", buf+4, buf+20 );
615 	else
616 	    printf(" <%-12.12s>", buf+4);
617 	len = strlen( pers->homephone );
618 	if(  dialup  &&  (len > 0)  )  {
619 	    if( len == 8 )  {
620 		printf( "             " );
621 	    }
622 	    else  {
623 		if( len == 12 )  {
624 		    printf( "         " );
625 		}
626 		else {
627 		    for( i = 1; i <= 21 - len; i++ )  {
628 			printf( " " );
629 		    }
630 		}
631 	    }
632 	    printf( "%s", pers->homephone );
633 	}
634 	else  {
635 	    if(  strlen( pers->office ) > 0  )  {
636 		printf( " %-11.11s", pers->office );
637 		if(  strlen( pers->officephone ) > 0  )  {
638 		    printf( " %8.8s", pers->officephone );
639 		}
640 		else  {
641 		    if( len == 8 )  {
642 			printf( " %8.8s", pers->homephone );
643 		    }
644 		}
645 	    }
646 	    else  {
647 		if(  strlen( pers->officephone ) > 0  )  {
648 		    printf( "             %8.8s", pers->officephone );
649 		}
650 		else  {
651 		    if( len == 8 )  {
652 			printf( "             %8.8s", pers->homephone );
653 		    }
654 		    else  {
655 			if( len == 12 )  {
656 			    printf( "         %12.12s", pers->homephone );
657 			}
658 		    }
659 		}
660 	    }
661 	}
662 	printf( "\n" );
663 }
664 
665 
666 /*  print out a person in long format giving all possible information.
667  *  directory and shell are inhibited if unbrief is clear.
668  */
669 
670 personprint( pers )
671 
672     struct  person	*pers;
673 {
674 	struct  passwd		*pwdt = pers->pwd;
675 	int			idleprinted;
676 
677 	if( pwdt == NILPWD )  {
678 	    printf( "Login name: %-10s", pers->name );
679 	    printf( "			" );
680 	    printf( "In real life: ???\n");
681 	    return;
682 	}
683 	printf( "Login name: %-10s", pwdt->pw_name );
684 	if( pers->loggedin )  {
685 	    if( pers->writeable )  {
686 		printf( "			" );
687 	    }
688 	    else  {
689 		printf( "	(messages off)	" );
690 	    }
691 	}
692 	else  {
693 	    printf( "			" );
694 	}
695 	if(  strlen( pers->realname ) > 0  )  {
696 	    printf( "In real life: %-s", pers->realname );
697 	}
698 	if(  strlen( pers->office ) > 0  )  {
699 	    printf( "\nOffice: %-.11s", pers->office );
700 	    if(  strlen( pers->officephone ) > 0  )  {
701 		printf( ", %s", pers->officephone );
702 		if(  strlen( pers->homephone ) > 0  )  {
703 		    printf( "		Home phone: %s", pers->homephone );
704 		}
705 		else  {
706 		    if(  strlen( pers->random ) > 0  )  {
707 			printf( "	%s", pers->random );
708 		    }
709 		}
710 	    }
711 	    else  {
712 		if(  strlen( pers->homephone ) > 0  )  {
713 		    printf("			Home phone: %s",pers->homephone);
714 		}
715 		if(  strlen( pers->random ) > 0  )  {
716 		    printf( "			%s", pers->random );
717 		}
718 	    }
719 	}
720 	else  {
721 	    if(  strlen( pers->officephone ) > 0  )  {
722 		printf( "\nPhone: %s", pers->officephone );
723 		if(  strlen( pers->homephone ) > 0  )  {
724 		    printf( "\n, %s", pers->homephone );
725 		    if(  strlen( pers->random ) > 0  )  {
726 			printf( ", %s", pers->random );
727 		    }
728 		}
729 		else  {
730 		    if(  strlen( pers->random ) > 0  )  {
731 			printf( "\n, %s", pers->random );
732 		    }
733 		}
734 	    }
735 	    else  {
736 		if(  strlen( pers->homephone ) > 0  )  {
737 		    printf( "\nPhone: %s", pers->homephone );
738 		    if(  strlen( pers->random ) > 0  )  {
739 			printf( ", %s", pers->random );
740 		    }
741 		}
742 		else  {
743 		    if(  strlen( pers->random ) > 0  )  {
744 			printf( "\n%s", pers->random );
745 		    }
746 		}
747 	    }
748 	}
749 	if( unbrief )  {
750 	    printf( "\n" );
751 	    printf( "Directory: %-25s", pwdt->pw_dir );
752 	    if(  strlen( pwdt->pw_shell ) > 0  )  {
753 		printf( "	Shell: %-s", pwdt->pw_shell );
754 	    }
755 	}
756 	if( pers->loggedin )  {
757 	    register char *ep = ctime( &pers->loginat );
758 	    printf("\nOn since %15.15s on %-*.*s	", &ep[4], LMAX, LMAX, pers->tty );
759 	    idleprinted = ltimeprint( &pers->idletime );
760 	    if( idleprinted )  {
761 		printf( " Idle Time" );
762 	    }
763 	}
764 	else if (pers->loginat == 0)
765 	    printf("\nNever logged in.");
766 	else if (tloc - pers->loginat > 180 * 24 * 60 * 60) {
767 	    register char *ep = ctime( &pers->loginat );
768 	    printf("\nLast login %10.10s, %4.4s on %.*s", ep, ep+20, LMAX, pers->tty);
769 	}
770 	else  {
771 	    register char *ep = ctime( &pers->loginat );
772 	    printf("\nLast login %16.16s on %.*s", ep, LMAX, pers->tty );
773 	}
774 	printf( "\n" );
775 }
776 
777 
778 /*
779  *  very hacky section of code to format phone numbers.  filled with
780  *  magic constants like 4, 7 and 10.
781  */
782 
783 char  *phone( s, len )
784 
785     char		*s;
786     int			len;
787 {
788 	char		*strsave();
789 	char		fonebuf[ 15 ];
790 	int		i;
791 
792 	switch(  len  )  {
793 
794 	    case  4:
795 		fonebuf[ 0 ] = ' ';
796 		fonebuf[ 1 ] = 'x';
797 		fonebuf[ 2 ] = '2';
798 		fonebuf[ 3 ] = '-';
799 		for( i = 0; i <= 3; i++ )  {
800 		    fonebuf[ 4 + i ] = *s++;
801 		}
802 		fonebuf[ 8 ] = NULL;
803 		return( strsave( &fonebuf[0] ) );
804 		break;
805 
806 	    case  7:
807 		for( i = 0; i <= 2; i++ )  {
808 		    fonebuf[ i ] = *s++;
809 		}
810 		fonebuf[ 3 ] = '-';
811 		for( i = 0; i <= 3; i++ )  {
812 		    fonebuf[ 4 + i ] = *s++;
813 		}
814 		fonebuf[ 8 ] = NULL;
815 		return( strsave( &fonebuf[0] ) );
816 		break;
817 
818 	    case 10:
819 		for( i = 0; i <= 2; i++ )  {
820 		    fonebuf[ i ] = *s++;
821 		}
822 		fonebuf[ 3 ] = '-';
823 		for( i = 0; i <= 2; i++ )  {
824 		    fonebuf[ 4 + i ] = *s++;
825 		}
826 		fonebuf[ 7 ] = '-';
827 		for( i = 0; i <= 3; i++ )  {
828 		    fonebuf[ 8 + i ] = *s++;
829 		}
830 		fonebuf[ 12 ] = NULL;
831 		return( strsave( &fonebuf[0] ) );
832 		break;
833 
834 	    default:
835 		fprintf( stderr, "finger: error in phone numbering\n" );
836 		return( strsave(s) );
837 		break;
838 	}
839 }
840 
841 
842 /*  decode the information in the gecos field of /etc/passwd
843  *  another hacky section of code, but given the format the stuff is in...
844  */
845 
846 decode( pers )
847 
848     struct  person	*pers;
849 
850 {
851 	struct  passwd		*pwdt = pers->pwd;
852 	char			buffer[ 40 ],  *bp,  *gp,  *lp;
853 	char			*phone();
854 	int			alldigits;
855 	int			len;
856 	int			i;
857 
858 	pers->realname = NULLSTR;
859 	pers->office = NULLSTR;
860 	pers->officephone = NULLSTR;
861 	pers->homephone = NULLSTR;
862 	pers->random = NULLSTR;
863 	if(  pwdt != NILPWD )  {
864 	    gp = pwdt->pw_gecos;
865 	    bp = &buffer[ 0 ];
866 	    if( *gp == ASTERISK )  {
867 		gp++;
868 	    }
869 	    while(  (*gp != NULL)  &&  (*gp != COMMA)  )  {	/* name */
870 		if( *gp == SAMENAME )  {
871 		    lp = pwdt->pw_name;
872 		    *bp++ = CAPITALIZE(*lp++);
873 		    while( *lp != NULL )  {
874 			*bp++ = *lp++;
875 		    }
876 		}
877 		else  {
878 		    *bp++ = *gp;
879 		}
880 		gp++;
881 	    }
882 	    *bp = NULL;
883 	    pers->realname = malloc( strlen( &buffer[0] ) + 1 );
884 	    strcpy( pers->realname, &buffer[0] );
885 	    if( *gp++ == COMMA )  {			/* office, supposedly */
886 		alldigits = 1;
887 		bp = &buffer[ 0 ];
888 		while(  (*gp != NULL)  &&  (*gp != COMMA)  )  {
889 		    *bp = *gp++;
890 		    alldigits = alldigits && ('0' <= *bp) && (*bp <= '9');
891 		    bp++;
892 		}
893 		*bp = NULL;
894 		len = strlen( &buffer[0] );
895 		if( buffer[ len - 1 ]  ==  CORY )  {
896 		    strcpy( &buffer[ len - 1 ], " Cory" );
897 		    pers->office = malloc( len + 5 );
898 		    strcpy( pers->office, &buffer[0] );
899 		}
900 		else  {
901 		    if( buffer[ len - 1 ] == EVANS )  {
902 			strcpy( &buffer[ len - 1 ], " Evans" );
903 			pers->office = malloc( len + 6 );
904 			strcpy( pers->office, &buffer[0] );
905 		    }
906 		    else  {
907 			if( buffer[ len - 1 ] == 'L' )  {
908 			    strcpy( &buffer[ len - 1 ], " LBL" );
909 			    pers->office = malloc( len + 4 );
910 			    strcpy( pers->office, &buffer[0] );
911 			}
912 			else  {
913 			    if( alldigits )  {
914 				if( len == 4 )  {
915 				    pers->officephone = phone(&buffer[0], len);
916 				}
917 				else  {
918 				    if(  (len == 7) || (len == 10)  )  {
919 					pers->homephone = phone(&buffer[0],len);
920 				    }
921 				}
922 			    }
923 			    else  {
924 				pers->random = malloc( len + 1 );
925 				strcpy( pers->random, &buffer[0] );
926 			    }
927 			}
928 		    }
929 		}
930 		if( *gp++ == COMMA )  {	    /* office phone, theoretically */
931 		    bp = &buffer[ 0 ];
932 		    alldigits = 1;
933 		    while(  (*gp != NULL)  &&  (*gp != COMMA)  )  {
934 			*bp = *gp++;
935 			alldigits = alldigits && ('0' <= *bp) && (*bp <= '9');
936 			bp++;
937 		    }
938 		    *bp = NULL;
939 		    len = strlen( &buffer[0] );
940 		    if( alldigits )  {
941 			if(  len != 4  )  {
942 			    if(  (len == 7) || (len == 10)  )  {
943 				pers->homephone = phone( &buffer[0], len );
944 			    }
945 			    else  {
946 				pers->random = malloc( len + 1 );
947 				strcpy( pers->random, &buffer[0] );
948 			    }
949 			}
950 			else  {
951 				pers->officephone = phone( &buffer[0], len );
952 			}
953 		    }
954 		    else  {
955 			pers->random = malloc( len + 1 );
956 			strcpy( pers->random, &buffer[0] );
957 		    }
958 		    if( *gp++ == COMMA )  {		/* home phone?? */
959 			bp = &buffer[ 0 ];
960 			alldigits = 1;
961 			    while(  (*gp != NULL)  &&  (*gp != COMMA)  )  {
962 				*bp = *gp++;
963 				alldigits = alldigits && ('0' <= *bp) &&
964 							(*bp <= '9');
965 				bp++;
966 			    }
967 			*bp = NULL;
968 			len = strlen( &buffer[0] );
969 			if( alldigits  &&  ( (len == 7) || (len == 10) )  )  {
970 			    if( *pers->homephone != NULL )  {
971 				pers->officephone = pers->homephone;
972 			    }
973 			    pers->homephone = phone( &buffer[0], len );
974 			}
975 			else  {
976 			    pers->random = malloc( strlen( &buffer[0] ) + 1 );
977 			    strcpy( pers->random, &buffer[0] );
978 			}
979 		    }
980 		}
981 	    }
982 	    if( pers->loggedin == 0 )  {
983 		findwhen( pers );
984 	    }
985 	    else  {
986 		findidle( pers );
987 	    }
988 	}
989 }
990 
991 
992 /*  find the last log in of a user by checking the LASTLOG file.
993  *  the entry is indexed by the uid, so this can only be done if
994  *  the uid is known (which it isn't in quick mode)
995  */
996 
997 fwopen()
998 {
999 	if(  ( lf = open(LASTLOG, 0) ) >= 0  )  {
1000 	    llopenerr = 0;
1001 	}
1002 	else  {
1003 	    fprintf( stderr, "finger: lastlog open error\n" );
1004 	    llopenerr = 1;
1005 	}
1006 }
1007 
1008 
1009 findwhen( pers )
1010 
1011     struct  person	*pers;
1012 {
1013 	struct  passwd		*pwdt = pers->pwd;
1014 	struct  lastlog		ll;
1015 	int			llsize = sizeof ll;
1016 	int			i;
1017 
1018 	if( !llopenerr )  {
1019 	    lseek( lf, pwdt->pw_uid*llsize, 0 );
1020 	    if ((i = read( lf, (char *) &ll, llsize )) == llsize) {
1021 		    for( i = 0; i < LMAX; i++ )  {
1022 			pers->tty[ i ] = ll.ll_line[ i ];
1023 		    }
1024 		    pers->tty[ LMAX ] = NULL;
1025 		    pers->loginat = ll.ll_time;
1026 	    }
1027 	    else  {
1028 		if (i != 0)
1029 			fprintf(stderr, "finger: lastlog read error\n");
1030 		pers->tty[ 0 ] = NULL;
1031 		pers->loginat = 0L;
1032 	    }
1033 	}
1034 	else  {
1035 	    pers->tty[ 0 ] = NULL;
1036 	    pers->loginat = 0L;
1037 	}
1038 }
1039 
1040 
1041 fwclose()
1042 {
1043 	if( !llopenerr )  {
1044 	    close( lf );
1045 	}
1046 }
1047 
1048 
1049 /*  find the idle time of a user by doing a stat on /dev/histty,
1050  *  where histty has been gotten from USERLOG, supposedly.
1051  */
1052 
1053 findidle( pers )
1054 
1055     struct  person	*pers;
1056 {
1057 	struct  stat		ttystatus;
1058 	struct  passwd		*pwdt = pers->pwd;
1059 	char			buffer[ 20 ];
1060 	char			*TTY = "/dev/";
1061 	int			TTYLEN = strlen( TTY );
1062 	int			i;
1063 
1064 	strcpy( &buffer[0], TTY );
1065 	i = 0;
1066 	do  {
1067 	    buffer[ TTYLEN + i ] = pers->tty[ i ];
1068 	}  while( ++i <= LMAX );
1069 	if(  stat( &buffer[0], &ttystatus ) >= 0  )  {
1070 	    time( &tloc );
1071 	    if( tloc < ttystatus.st_atime )  {
1072 		pers->idletime = 0L;
1073 	    }
1074 	    else  {
1075 		pers->idletime = tloc - ttystatus.st_atime;
1076 	    }
1077 	    if(  (ttystatus.st_mode & TALKABLE) == TALKABLE  )  {
1078 		pers->writeable = 1;
1079 	    }
1080 	    else  {
1081 		pers->writeable = 0;
1082 	    }
1083 	}
1084 	else  {
1085 	    fprintf( stderr, "finger: error STATing %s\n", &buffer[0] );
1086 	    exit( 4 );
1087 	}
1088 }
1089 
1090 
1091 /*  print idle time in short format; this program always prints 4 characters;
1092  *  if the idle time is zero, it prints 4 blanks.
1093  */
1094 
1095 stimeprint( dt )
1096 
1097     long	*dt;
1098 {
1099 	struct  tm		*gmtime();
1100 	struct  tm		*delta;
1101 
1102 	delta = gmtime( dt );
1103 	if( delta->tm_yday == 0 )  {
1104 	    if( delta->tm_hour == 0 )  {
1105 		if( delta->tm_min >= 10 )  {
1106 		    printf( " %2.2d ", delta->tm_min );
1107 		}
1108 		else  {
1109 		    if( delta->tm_min == 0 )  {
1110 			printf( "    " );
1111 		    }
1112 		    else  {
1113 			printf( "  %1.1d ", delta->tm_min );
1114 		    }
1115 		}
1116 	    }
1117 	    else  {
1118 		if( delta->tm_hour >= 10 )  {
1119 		    printf( "%3.3d:", delta->tm_hour );
1120 		}
1121 		else  {
1122 		    printf( "%1.1d:%02.2d", delta->tm_hour, delta->tm_min );
1123 		}
1124 	    }
1125 	}
1126 	else  {
1127 	    printf( "%3dd", delta->tm_yday );
1128 	}
1129 }
1130 
1131 
1132 /*  print idle time in long format with care being taken not to pluralize
1133  *  1 minutes or 1 hours or 1 days.
1134  */
1135 
1136 ltimeprint( dt )
1137 
1138     long	*dt;
1139 {
1140 	struct  tm		*gmtime();
1141 	struct  tm		*delta;
1142 	int			printed = 1;
1143 
1144 	delta = gmtime( dt );
1145 	if( delta->tm_yday == 0 )  {
1146 	    if( delta->tm_hour == 0 )  {
1147 		if( delta->tm_min >= 10 )  {
1148 		    printf( "%2d minutes", delta->tm_min );
1149 		}
1150 		else  {
1151 		    if( delta->tm_min == 0 )  {
1152 			if( delta->tm_sec > 10 )  {
1153 			    printf( "%2d seconds", delta->tm_sec );
1154 			}
1155 			else  {
1156 			    printed = 0;
1157 			}
1158 		    }
1159 		    else  {
1160 			if( delta->tm_min == 1 )  {
1161 			    if( delta->tm_sec == 1 )  {
1162 				printf( "%1d minute %1d second",
1163 				    delta->tm_min, delta->tm_sec );
1164 			    }
1165 			    else  {
1166 				printf( "%1d minute %d seconds",
1167 				    delta->tm_min, delta->tm_sec );
1168 			    }
1169 			}
1170 			else  {
1171 			    if( delta->tm_sec == 1 )  {
1172 				printf( "%1d minutes %1d second",
1173 				    delta->tm_min, delta->tm_sec );
1174 			    }
1175 			    else  {
1176 				printf( "%1d minutes %d seconds",
1177 				    delta->tm_min, delta->tm_sec );
1178 			    }
1179 			}
1180 		    }
1181 		}
1182 	    }
1183 	    else  {
1184 		if( delta->tm_hour >= 10 )  {
1185 		    printf( "%2d hours", delta->tm_hour );
1186 		}
1187 		else  {
1188 		    if( delta->tm_hour == 1 )  {
1189 			if( delta->tm_min == 1 )  {
1190 			    printf( "%1d hour %1d minute",
1191 				delta->tm_hour, delta->tm_min );
1192 			}
1193 			else  {
1194 			    printf( "%1d hour %2d minutes",
1195 				delta->tm_hour, delta->tm_min );
1196 			}
1197 		    }
1198 		    else  {
1199 			if( delta->tm_min == 1 )  {
1200 			    printf( "%1d hours %1d minute",
1201 				delta->tm_hour, delta->tm_min );
1202 			}
1203 			else  {
1204 			    printf( "%1d hours %2d minutes",
1205 				delta->tm_hour, delta->tm_min );
1206 			}
1207 		    }
1208 		}
1209 	    }
1210 	}
1211 	else  {
1212 		if( delta->tm_yday >= 10 )  {
1213 		    printf( "%2d days", delta->tm_yday );
1214 		}
1215 		else  {
1216 		    if( delta->tm_yday == 1 )  {
1217 			if( delta->tm_hour == 1 )  {
1218 			    printf( "%1d day %1d hour",
1219 				delta->tm_yday, delta->tm_hour );
1220 			}
1221 			else  {
1222 			    printf( "%1d day %2d hours",
1223 				delta->tm_yday, delta->tm_hour );
1224 			}
1225 		    }
1226 		    else  {
1227 			if( delta->tm_hour == 1 )  {
1228 			    printf( "%1d days %1d hour",
1229 				delta->tm_yday, delta->tm_hour );
1230 			}
1231 			else  {
1232 			    printf( "%1d days %2d hours",
1233 				delta->tm_yday, delta->tm_hour );
1234 			}
1235 		    }
1236 		}
1237 	}
1238 	return( printed );
1239 }
1240 
1241 
1242 matchcmp( gname, login, given )
1243 
1244     char		*gname;
1245     char		*login;
1246     char		*given;
1247 {
1248 	char		buffer[ 20 ];
1249 	char		c;
1250 	int		flag,  i,  unfound;
1251 
1252 	if( !match )  {
1253 	    return( 0 );
1254 	}
1255 	else  {
1256 	    if(  namecmp( login, given )  )  {
1257 		return( 1 );
1258 	    }
1259 	    else if (*gname == '\0')
1260 		return (0);
1261 	    else  {
1262 		if( *gname == ASTERISK )  {
1263 		    gname++;
1264 		}
1265 		flag = 1;
1266 		i = 0;
1267 		unfound = 1;
1268 		while(  unfound  )  {
1269 		    if( flag )  {
1270 			c = *gname++;
1271 			if( c == SAMENAME )  {
1272 			    flag = 0;
1273 			    c = *login++;
1274 			}
1275 			else  {
1276 			    unfound = (*gname != COMMA)  &&  (*gname != NULL);
1277 			}
1278 		    }
1279 		    else {
1280 			c = *login++;
1281 			if( c == NULL )  {
1282 			    if(  (*gname == COMMA)  ||  (*gname == NULL)  )  {
1283 				break;
1284 			    }
1285 			    else  {
1286 				flag = 1;
1287 				continue;
1288 			    }
1289 			}
1290 		    }
1291 		    if( c == BLANK )  {
1292 			buffer[i++] = NULL;
1293 			if(  namecmp( buffer, given )  )  {
1294 			    return( 1 );
1295 			}
1296 			i = 0;
1297 			flag = 1;
1298 		    }
1299 		    else  {
1300 			buffer[ i++ ] = c;
1301 		    }
1302 		}
1303 		buffer[i++] = NULL;
1304 		if(  namecmp( buffer, given )  )  {
1305 		    return( 1 );
1306 		}
1307 		else  {
1308 		    return( 0 );
1309 		}
1310 	    }
1311 	}
1312 }
1313 
1314 
1315 namecmp( name1, name2 )
1316 
1317     char		*name1;
1318     char		*name2;
1319 {
1320 	char		c1,  c2;
1321 
1322 	c1 = *name1;
1323 	if( (('A' <= c1) && (c1 <= 'Z')) || (('a' <= c1) && (c1 <= 'z')) )  {
1324 	    c1 = CAPITALIZE( c1 );
1325 	}
1326 	c2 = *name2;
1327 	if( (('A' <= c2) && (c2 <= 'Z')) || (('a' <= c2) && (c2 <= 'z')) )  {
1328 	    c2 = CAPITALIZE( c2 );
1329 	}
1330 	while( c1 == c2 )  {
1331 	    if( c1 == NULL )  {
1332 		return( 1 );
1333 	    }
1334 	    c1 = *++name1;
1335 	    if( (('A'<=c1) && (c1<='Z')) || (('a'<=c1) && (c1<='z')) )  {
1336 		c1 = CAPITALIZE( c1 );
1337 	    }
1338 	    c2 = *++name2;
1339 	    if( (('A'<=c2) && (c2<='Z')) || (('a'<=c2) && (c2<='z')) )  {
1340 		c2 = CAPITALIZE( c2 );
1341 	    }
1342 	}
1343 	if( *name1 == NULL )  {
1344 	    while(  ('0' <= *name2)  &&  (*name2 <= '9')  )  {
1345 		name2++;
1346 	    }
1347 	    if( *name2 == NULL )  {
1348 		return( 1 );
1349 	    }
1350 	}
1351 	else  {
1352 	    if( *name2 == NULL )  {
1353 		while(  ('0' <= *name1)  &&  (*name1 <= '9')  )  {
1354 		    name1++;
1355 		}
1356 		if( *name1 == NULL )  {
1357 		    return( 1 );
1358 		}
1359 	    }
1360 	}
1361 	return( 0 );
1362 }
1363 
1364 
1365 char  *strsave( s )
1366 
1367     char		*s;
1368 {
1369 	char		*malloc();
1370 	char		*p;
1371 
1372 	p = malloc( strlen( s ) + 1 );
1373 	strcpy( p, s );
1374 }
1375