xref: /original-bsd/usr.bin/finger/finger.c (revision d25e1985)
1 static char *sccsid = "@(#)finger.c	4.1 (Berkeley) 10/01/80";
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  {
612 	    printf( " " );
613 	    offset = 4;
614 	    for( i = 0; i <22; i++ )  {
615 		buf[i] = buf[i + offset];
616 	    }
617 	    printf( "<%-12.12s>", buf );
618 	}
619 	len = strlen( pers->homephone );
620 	if(  dialup  &&  (len > 0)  )  {
621 	    if( len == 8 )  {
622 		printf( "             " );
623 	    }
624 	    else  {
625 		if( len == 12 )  {
626 		    printf( "         " );
627 		}
628 		else {
629 		    for( i = 1; i <= 21 - len; i++ )  {
630 			printf( " " );
631 		    }
632 		}
633 	    }
634 	    printf( "%s", pers->homephone );
635 	}
636 	else  {
637 	    if(  strlen( pers->office ) > 0  )  {
638 		printf( " %-11.11s", pers->office );
639 		if(  strlen( pers->officephone ) > 0  )  {
640 		    printf( " %8.8s", pers->officephone );
641 		}
642 		else  {
643 		    if( len == 8 )  {
644 			printf( " %8.8s", pers->homephone );
645 		    }
646 		}
647 	    }
648 	    else  {
649 		if(  strlen( pers->officephone ) > 0  )  {
650 		    printf( "             %8.8s", pers->officephone );
651 		}
652 		else  {
653 		    if( len == 8 )  {
654 			printf( "             %8.8s", pers->homephone );
655 		    }
656 		    else  {
657 			if( len == 12 )  {
658 			    printf( "         %12.12s", pers->homephone );
659 			}
660 		    }
661 		}
662 	    }
663 	}
664 	printf( "\n" );
665 }
666 
667 
668 /*  print out a person in long format giving all possible information.
669  *  directory and shell are inhibited if unbrief is clear.
670  */
671 
672 personprint( pers )
673 
674     struct  person	*pers;
675 {
676 	struct  passwd		*pwdt = pers->pwd;
677 	int			idleprinted;
678 
679 	if( pwdt == NILPWD )  {
680 	    printf( "Login name: %-10s", pers->name );
681 	    printf( "			" );
682 	    printf( "In real life: ???\n");
683 	    return;
684 	}
685 	printf( "Login name: %-10s", pwdt->pw_name );
686 	if( pers->loggedin )  {
687 	    if( pers->writeable )  {
688 		printf( "			" );
689 	    }
690 	    else  {
691 		printf( "	(messages off)	" );
692 	    }
693 	}
694 	else  {
695 	    printf( "			" );
696 	}
697 	if(  strlen( pers->realname ) > 0  )  {
698 	    printf( "In real life: %-s", pers->realname );
699 	}
700 	if(  strlen( pers->office ) > 0  )  {
701 	    printf( "\nOffice: %-.11s", pers->office );
702 	    if(  strlen( pers->officephone ) > 0  )  {
703 		printf( ", %s", pers->officephone );
704 		if(  strlen( pers->homephone ) > 0  )  {
705 		    printf( "		Home phone: %s", pers->homephone );
706 		}
707 		else  {
708 		    if(  strlen( pers->random ) > 0  )  {
709 			printf( "	%s", pers->random );
710 		    }
711 		}
712 	    }
713 	    else  {
714 		if(  strlen( pers->homephone ) > 0  )  {
715 		    printf("			Home phone: %s",pers->homephone);
716 		}
717 		if(  strlen( pers->random ) > 0  )  {
718 		    printf( "			%s", pers->random );
719 		}
720 	    }
721 	}
722 	else  {
723 	    if(  strlen( pers->officephone ) > 0  )  {
724 		printf( "\nPhone: %s", pers->officephone );
725 		if(  strlen( pers->homephone ) > 0  )  {
726 		    printf( "\n, %s", pers->homephone );
727 		    if(  strlen( pers->random ) > 0  )  {
728 			printf( ", %s", pers->random );
729 		    }
730 		}
731 		else  {
732 		    if(  strlen( pers->random ) > 0  )  {
733 			printf( "\n, %s", pers->random );
734 		    }
735 		}
736 	    }
737 	    else  {
738 		if(  strlen( pers->homephone ) > 0  )  {
739 		    printf( "\nPhone: %s", pers->homephone );
740 		    if(  strlen( pers->random ) > 0  )  {
741 			printf( ", %s", pers->random );
742 		    }
743 		}
744 		else  {
745 		    if(  strlen( pers->random ) > 0  )  {
746 			printf( "\n%s", pers->random );
747 		    }
748 		}
749 	    }
750 	}
751 	if( unbrief )  {
752 	    printf( "\n" );
753 	    printf( "Directory: %-25s", pwdt->pw_dir );
754 	    if(  strlen( pwdt->pw_shell ) > 0  )  {
755 		printf( "	Shell: %-s", pwdt->pw_shell );
756 	    }
757 	}
758 	if( pers->loggedin )  {
759 	    register char *ep = ctime( &pers->loginat );
760 	    printf("\nOn since %15.15s on %-*.*s	", &ep[4], LMAX, LMAX, pers->tty );
761 	    idleprinted = ltimeprint( &pers->idletime );
762 	    if( idleprinted )  {
763 		printf( " Idle Time" );
764 	    }
765 	}
766 	else  {
767 	    register char *ep = ctime( &pers->loginat );
768 	    printf("\nLast login %16.16s on %.*s", ep, LMAX, pers->tty );
769 	}
770 	printf( "\n" );
771 }
772 
773 
774 /*
775  *  very hacky section of code to format phone numbers.  filled with
776  *  magic constants like 4, 7 and 10.
777  */
778 
779 char  *phone( s, len )
780 
781     char		*s;
782     int			len;
783 {
784 	char		*strsave();
785 	char		fonebuf[ 15 ];
786 	int		i;
787 
788 	switch(  len  )  {
789 
790 	    case  4:
791 		fonebuf[ 0 ] = ' ';
792 		fonebuf[ 1 ] = 'x';
793 		fonebuf[ 2 ] = '2';
794 		fonebuf[ 3 ] = '-';
795 		for( i = 0; i <= 3; i++ )  {
796 		    fonebuf[ 4 + i ] = *s++;
797 		}
798 		fonebuf[ 8 ] = NULL;
799 		return( strsave( &fonebuf[0] ) );
800 		break;
801 
802 	    case  7:
803 		for( i = 0; i <= 2; i++ )  {
804 		    fonebuf[ i ] = *s++;
805 		}
806 		fonebuf[ 3 ] = '-';
807 		for( i = 0; i <= 3; i++ )  {
808 		    fonebuf[ 4 + i ] = *s++;
809 		}
810 		fonebuf[ 8 ] = NULL;
811 		return( strsave( &fonebuf[0] ) );
812 		break;
813 
814 	    case 10:
815 		for( i = 0; i <= 2; i++ )  {
816 		    fonebuf[ i ] = *s++;
817 		}
818 		fonebuf[ 3 ] = '-';
819 		for( i = 0; i <= 2; i++ )  {
820 		    fonebuf[ 4 + i ] = *s++;
821 		}
822 		fonebuf[ 7 ] = '-';
823 		for( i = 0; i <= 3; i++ )  {
824 		    fonebuf[ 8 + i ] = *s++;
825 		}
826 		fonebuf[ 12 ] = NULL;
827 		return( strsave( &fonebuf[0] ) );
828 		break;
829 
830 	    default:
831 		fprintf( stderr, "finger: error in phone numbering\n" );
832 		return( strsave(s) );
833 		break;
834 	}
835 }
836 
837 
838 /*  decode the information in the gecos field of /etc/passwd
839  *  another hacky section of code, but given the format the stuff is in...
840  */
841 
842 decode( pers )
843 
844     struct  person	*pers;
845 
846 {
847 	struct  passwd		*pwdt = pers->pwd;
848 	char			buffer[ 40 ],  *bp,  *gp,  *lp;
849 	char			*phone();
850 	int			alldigits;
851 	int			len;
852 	int			i;
853 
854 	pers->realname = NULLSTR;
855 	pers->office = NULLSTR;
856 	pers->officephone = NULLSTR;
857 	pers->homephone = NULLSTR;
858 	pers->random = NULLSTR;
859 	if(  pwdt != NILPWD )  {
860 	    gp = pwdt->pw_gecos;
861 	    bp = &buffer[ 0 ];
862 	    if( *gp == ASTERISK )  {
863 		gp++;
864 	    }
865 	    while(  (*gp != NULL)  &&  (*gp != COMMA)  )  {	/* name */
866 		if( *gp == SAMENAME )  {
867 		    lp = pwdt->pw_name;
868 		    *bp++ = CAPITALIZE(*lp++);
869 		    while( *lp != NULL )  {
870 			*bp++ = *lp++;
871 		    }
872 		}
873 		else  {
874 		    *bp++ = *gp;
875 		}
876 		gp++;
877 	    }
878 	    *bp = NULL;
879 	    pers->realname = malloc( strlen( &buffer[0] ) + 1 );
880 	    strcpy( pers->realname, &buffer[0] );
881 	    if( *gp++ == COMMA )  {			/* office, supposedly */
882 		alldigits = 1;
883 		bp = &buffer[ 0 ];
884 		while(  (*gp != NULL)  &&  (*gp != COMMA)  )  {
885 		    *bp = *gp++;
886 		    alldigits = alldigits && ('0' <= *bp) && (*bp <= '9');
887 		    bp++;
888 		}
889 		*bp = NULL;
890 		len = strlen( &buffer[0] );
891 		if( buffer[ len - 1 ]  ==  CORY )  {
892 		    strcpy( &buffer[ len - 1 ], " Cory" );
893 		    pers->office = malloc( len + 5 );
894 		    strcpy( pers->office, &buffer[0] );
895 		}
896 		else  {
897 		    if( buffer[ len - 1 ] == EVANS )  {
898 			strcpy( &buffer[ len - 1 ], " Evans" );
899 			pers->office = malloc( len + 6 );
900 			strcpy( pers->office, &buffer[0] );
901 		    }
902 		    else  {
903 			if( buffer[ len - 1 ] == 'L' )  {
904 			    strcpy( &buffer[ len - 1 ], " LBL" );
905 			    pers->office = malloc( len + 4 );
906 			    strcpy( pers->office, &buffer[0] );
907 			}
908 			else  {
909 			    if( alldigits )  {
910 				if( len == 4 )  {
911 				    pers->officephone = phone(&buffer[0], len);
912 				}
913 				else  {
914 				    if(  (len == 7) || (len == 10)  )  {
915 					pers->homephone = phone(&buffer[0],len);
916 				    }
917 				}
918 			    }
919 			    else  {
920 				pers->random = malloc( len + 1 );
921 				strcpy( pers->random, &buffer[0] );
922 			    }
923 			}
924 		    }
925 		}
926 		if( *gp++ == COMMA )  {	    /* office phone, theoretically */
927 		    bp = &buffer[ 0 ];
928 		    alldigits = 1;
929 		    while(  (*gp != NULL)  &&  (*gp != COMMA)  )  {
930 			*bp = *gp++;
931 			alldigits = alldigits && ('0' <= *bp) && (*bp <= '9');
932 			bp++;
933 		    }
934 		    *bp = NULL;
935 		    len = strlen( &buffer[0] );
936 		    if( alldigits )  {
937 			if(  len != 4  )  {
938 			    if(  (len == 7) || (len == 10)  )  {
939 				pers->homephone = phone( &buffer[0], len );
940 			    }
941 			    else  {
942 				pers->random = malloc( len + 1 );
943 				strcpy( pers->random, &buffer[0] );
944 			    }
945 			}
946 			else  {
947 				pers->officephone = phone( &buffer[0], len );
948 			}
949 		    }
950 		    else  {
951 			pers->random = malloc( len + 1 );
952 			strcpy( pers->random, &buffer[0] );
953 		    }
954 		    if( *gp++ == COMMA )  {		/* home phone?? */
955 			bp = &buffer[ 0 ];
956 			alldigits = 1;
957 			    while(  (*gp != NULL)  &&  (*gp != COMMA)  )  {
958 				*bp = *gp++;
959 				alldigits = alldigits && ('0' <= *bp) &&
960 							(*bp <= '9');
961 				bp++;
962 			    }
963 			*bp = NULL;
964 			len = strlen( &buffer[0] );
965 			if( alldigits  &&  ( (len == 7) || (len == 10) )  )  {
966 			    if( *pers->homephone != NULL )  {
967 				pers->officephone = pers->homephone;
968 			    }
969 			    pers->homephone = phone( &buffer[0], len );
970 			}
971 			else  {
972 			    pers->random = malloc( strlen( &buffer[0] ) + 1 );
973 			    strcpy( pers->random, &buffer[0] );
974 			}
975 		    }
976 		}
977 	    }
978 	    if( pers->loggedin == 0 )  {
979 		findwhen( pers );
980 	    }
981 	    else  {
982 		findidle( pers );
983 	    }
984 	}
985 }
986 
987 
988 /*  find the last log in of a user by checking the LASTLOG file.
989  *  the entry is indexed by the uid, so this can only be done if
990  *  the uid is known (which it isn't in quick mode)
991  */
992 
993 fwopen()
994 {
995 	if(  ( lf = open(LASTLOG, 0) ) >= 0  )  {
996 	    llopenerr = 0;
997 	}
998 	else  {
999 	    fprintf( stderr, "finger: lastlog open error\n" );
1000 	    llopenerr = 1;
1001 	}
1002 }
1003 
1004 
1005 findwhen( pers )
1006 
1007     struct  person	*pers;
1008 {
1009 	struct  passwd		*pwdt = pers->pwd;
1010 	struct  lastlog		ll;
1011 	int			llsize = sizeof ll;
1012 	int			i;
1013 
1014 	if( !llopenerr )  {
1015 	    lseek( lf, pwdt->pw_uid*llsize, 0 );
1016 	    if( read( lf, (char *) &ll, llsize ) == llsize )  {
1017 		    for( i = 0; i < LMAX; i++ )  {
1018 			pers->tty[ i ] = ll.ll_line[ i ];
1019 		    }
1020 		    pers->tty[ LMAX ] = NULL;
1021 		    pers->loginat = ll.ll_time;
1022 	    }
1023 	    else  {
1024 		fprintf( stderr, "finger: lastlog read error\n" );
1025 		pers->tty[ 0 ] = NULL;
1026 		pers->loginat = 0L;
1027 	    }
1028 	}
1029 	else  {
1030 	    pers->tty[ 0 ] = NULL;
1031 	    pers->loginat = 0L;
1032 	}
1033 }
1034 
1035 
1036 fwclose()
1037 {
1038 	if( !llopenerr )  {
1039 	    close( lf );
1040 	}
1041 }
1042 
1043 
1044 /*  find the idle time of a user by doing a stat on /dev/histty,
1045  *  where histty has been gotten from USERLOG, supposedly.
1046  */
1047 
1048 findidle( pers )
1049 
1050     struct  person	*pers;
1051 {
1052 	struct  stat		ttystatus;
1053 	struct  passwd		*pwdt = pers->pwd;
1054 	char			buffer[ 20 ];
1055 	char			*TTY = "/dev/";
1056 	int			TTYLEN = strlen( TTY );
1057 	int			i;
1058 
1059 	strcpy( &buffer[0], TTY );
1060 	i = 0;
1061 	do  {
1062 	    buffer[ TTYLEN + i ] = pers->tty[ i ];
1063 	}  while( ++i <= LMAX );
1064 	if(  stat( &buffer[0], &ttystatus ) >= 0  )  {
1065 	    time( &tloc );
1066 	    if( tloc < ttystatus.st_atime )  {
1067 		pers->idletime = 0L;
1068 	    }
1069 	    else  {
1070 		pers->idletime = tloc - ttystatus.st_atime;
1071 	    }
1072 	    if(  (ttystatus.st_mode & TALKABLE) == TALKABLE  )  {
1073 		pers->writeable = 1;
1074 	    }
1075 	    else  {
1076 		pers->writeable = 0;
1077 	    }
1078 	}
1079 	else  {
1080 	    fprintf( stderr, "finger: error STATing %s\n", &buffer[0] );
1081 	    exit( 4 );
1082 	}
1083 }
1084 
1085 
1086 /*  print idle time in short format; this program always prints 4 characters;
1087  *  if the idle time is zero, it prints 4 blanks.
1088  */
1089 
1090 stimeprint( dt )
1091 
1092     long	*dt;
1093 {
1094 	struct  tm		*gmtime();
1095 	struct  tm		*delta;
1096 
1097 	delta = gmtime( dt );
1098 	if( delta->tm_yday == 0 )  {
1099 	    if( delta->tm_hour == 0 )  {
1100 		if( delta->tm_min >= 10 )  {
1101 		    printf( " %2.2d ", delta->tm_min );
1102 		}
1103 		else  {
1104 		    if( delta->tm_min == 0 )  {
1105 			printf( "    " );
1106 		    }
1107 		    else  {
1108 			printf( "  %1.1d ", delta->tm_min );
1109 		    }
1110 		}
1111 	    }
1112 	    else  {
1113 		if( delta->tm_hour >= 10 )  {
1114 		    printf( "%3.3d:", delta->tm_hour );
1115 		}
1116 		else  {
1117 		    printf( "%1.1d:%02.2d", delta->tm_hour, delta->tm_min );
1118 		}
1119 	    }
1120 	}
1121 	else  {
1122 	    printf( "%3dd", delta->tm_yday );
1123 	}
1124 }
1125 
1126 
1127 /*  print idle time in long format with care being taken not to pluralize
1128  *  1 minutes or 1 hours or 1 days.
1129  */
1130 
1131 ltimeprint( dt )
1132 
1133     long	*dt;
1134 {
1135 	struct  tm		*gmtime();
1136 	struct  tm		*delta;
1137 	int			printed = 1;
1138 
1139 	delta = gmtime( dt );
1140 	if( delta->tm_yday == 0 )  {
1141 	    if( delta->tm_hour == 0 )  {
1142 		if( delta->tm_min >= 10 )  {
1143 		    printf( "%2d minutes", delta->tm_min );
1144 		}
1145 		else  {
1146 		    if( delta->tm_min == 0 )  {
1147 			if( delta->tm_sec > 10 )  {
1148 			    printf( "%2d seconds", delta->tm_sec );
1149 			}
1150 			else  {
1151 			    printed = 0;
1152 			}
1153 		    }
1154 		    else  {
1155 			if( delta->tm_min == 1 )  {
1156 			    if( delta->tm_sec == 1 )  {
1157 				printf( "%1d minute %1d second",
1158 				    delta->tm_min, delta->tm_sec );
1159 			    }
1160 			    else  {
1161 				printf( "%1d minute %d seconds",
1162 				    delta->tm_min, delta->tm_sec );
1163 			    }
1164 			}
1165 			else  {
1166 			    if( delta->tm_sec == 1 )  {
1167 				printf( "%1d minutes %1d second",
1168 				    delta->tm_min, delta->tm_sec );
1169 			    }
1170 			    else  {
1171 				printf( "%1d minutes %d seconds",
1172 				    delta->tm_min, delta->tm_sec );
1173 			    }
1174 			}
1175 		    }
1176 		}
1177 	    }
1178 	    else  {
1179 		if( delta->tm_hour >= 10 )  {
1180 		    printf( "%2d hours", delta->tm_hour );
1181 		}
1182 		else  {
1183 		    if( delta->tm_hour == 1 )  {
1184 			if( delta->tm_min == 1 )  {
1185 			    printf( "%1d hour %1d minute",
1186 				delta->tm_hour, delta->tm_min );
1187 			}
1188 			else  {
1189 			    printf( "%1d hour %2d minutes",
1190 				delta->tm_hour, delta->tm_min );
1191 			}
1192 		    }
1193 		    else  {
1194 			if( delta->tm_min == 1 )  {
1195 			    printf( "%1d hours %1d minute",
1196 				delta->tm_hour, delta->tm_min );
1197 			}
1198 			else  {
1199 			    printf( "%1d hours %2d minutes",
1200 				delta->tm_hour, delta->tm_min );
1201 			}
1202 		    }
1203 		}
1204 	    }
1205 	}
1206 	else  {
1207 		if( delta->tm_yday >= 10 )  {
1208 		    printf( "%2d days", delta->tm_yday );
1209 		}
1210 		else  {
1211 		    if( delta->tm_yday == 1 )  {
1212 			if( delta->tm_hour == 1 )  {
1213 			    printf( "%1d day %1d hour",
1214 				delta->tm_yday, delta->tm_hour );
1215 			}
1216 			else  {
1217 			    printf( "%1d day %2d hours",
1218 				delta->tm_yday, delta->tm_hour );
1219 			}
1220 		    }
1221 		    else  {
1222 			if( delta->tm_hour == 1 )  {
1223 			    printf( "%1d days %1d hour",
1224 				delta->tm_yday, delta->tm_hour );
1225 			}
1226 			else  {
1227 			    printf( "%1d days %2d hours",
1228 				delta->tm_yday, delta->tm_hour );
1229 			}
1230 		    }
1231 		}
1232 	}
1233 	return( printed );
1234 }
1235 
1236 
1237 matchcmp( gname, login, given )
1238 
1239     char		*gname;
1240     char		*login;
1241     char		*given;
1242 {
1243 	char		buffer[ 20 ];
1244 	char		c;
1245 	int		flag,  i,  unfound;
1246 
1247 	if( !match )  {
1248 	    return( 0 );
1249 	}
1250 	else  {
1251 	    if(  namecmp( login, given )  )  {
1252 		return( 1 );
1253 	    }
1254 	    else  {
1255 		if( *gname == ASTERISK )  {
1256 		    gname++;
1257 		}
1258 		flag = 1;
1259 		i = 0;
1260 		unfound = 1;
1261 		while(  unfound  )  {
1262 		    if( flag )  {
1263 			c = *gname++;
1264 			if( c == SAMENAME )  {
1265 			    flag = 0;
1266 			    c = *login++;
1267 			}
1268 			else  {
1269 			    unfound = (*gname != COMMA)  &&  (*gname != NULL);
1270 			}
1271 		    }
1272 		    else {
1273 			c = *login++;
1274 			if( c == NULL )  {
1275 			    if(  (*gname == COMMA)  ||  (*gname == NULL)  )  {
1276 				break;
1277 			    }
1278 			    else  {
1279 				flag = 1;
1280 				continue;
1281 			    }
1282 			}
1283 		    }
1284 		    if( c == BLANK )  {
1285 			buffer[i++] = NULL;
1286 			if(  namecmp( buffer, given )  )  {
1287 			    return( 1 );
1288 			}
1289 			i = 0;
1290 			flag = 1;
1291 		    }
1292 		    else  {
1293 			buffer[ i++ ] = c;
1294 		    }
1295 		}
1296 		buffer[i++] = NULL;
1297 		if(  namecmp( buffer, given )  )  {
1298 		    return( 1 );
1299 		}
1300 		else  {
1301 		    return( 0 );
1302 		}
1303 	    }
1304 	}
1305 }
1306 
1307 
1308 namecmp( name1, name2 )
1309 
1310     char		*name1;
1311     char		*name2;
1312 {
1313 	char		c1,  c2;
1314 
1315 	c1 = *name1;
1316 	if( (('A' <= c1) && (c1 <= 'Z')) || (('a' <= c1) && (c1 <= 'z')) )  {
1317 	    c1 = CAPITALIZE( c1 );
1318 	}
1319 	c2 = *name2;
1320 	if( (('A' <= c2) && (c2 <= 'Z')) || (('a' <= c2) && (c2 <= 'z')) )  {
1321 	    c2 = CAPITALIZE( c2 );
1322 	}
1323 	while( c1 == c2 )  {
1324 	    if( c1 == NULL )  {
1325 		return( 1 );
1326 	    }
1327 	    c1 = *++name1;
1328 	    if( (('A'<=c1) && (c1<='Z')) || (('a'<=c1) && (c1<='z')) )  {
1329 		c1 = CAPITALIZE( c1 );
1330 	    }
1331 	    c2 = *++name2;
1332 	    if( (('A'<=c2) && (c2<='Z')) || (('a'<=c2) && (c2<='z')) )  {
1333 		c2 = CAPITALIZE( c2 );
1334 	    }
1335 	}
1336 	if( *name1 == NULL )  {
1337 	    while(  ('0' <= *name2)  &&  (*name2 <= '9')  )  {
1338 		name2++;
1339 	    }
1340 	    if( *name2 == NULL )  {
1341 		return( 1 );
1342 	    }
1343 	}
1344 	else  {
1345 	    if( *name2 == NULL )  {
1346 		while(  ('0' <= *name1)  &&  (*name1 <= '9')  )  {
1347 		    name1++;
1348 		}
1349 		if( *name1 == NULL )  {
1350 		    return( 1 );
1351 		}
1352 	    }
1353 	}
1354 	return( 0 );
1355 }
1356 
1357 
1358 char  *strsave( s )
1359 
1360     char		*s;
1361 {
1362 	char		*malloc();
1363 	char		*p;
1364 
1365 	p = malloc( strlen( s ) + 1 );
1366 	strcpy( p, s );
1367 }
1368