xref: /original-bsd/usr.bin/finger/finger.c (revision 7211505a)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 char copyright[] =
9 "@(#) Copyright (c) 1980 Regents of the University of California.\n\
10  All rights reserved.\n";
11 #endif not lint
12 
13 #ifndef lint
14 static char sccsid[] = "@(#)finger.c	5.10 (Berkeley) 04/26/87";
15 #endif not lint
16 
17 /*
18  * This is a finger program.  It prints out useful information about users
19  * by digging it up from various system files.  It is not very portable
20  * because the most useful parts of the information (the full user name,
21  * office, and phone numbers) are all stored in the VAX-unused gecos field
22  * of /etc/passwd, which, unfortunately, other UNIXes use for other things.
23  *
24  * There are three output formats, all of which give login name, teletype
25  * line number, and login time.  The short output format is reminiscent
26  * of finger on ITS, and gives one line of information per user containing
27  * in addition to the minimum basic requirements (MBR), the full name of
28  * the user, his idle time and office location and phone number.  The
29  * quick style output is UNIX who-like, giving only name, teletype and
30  * login time.  Finally, the long style output give the same information
31  * as the short (in more legible format), the home directory and shell
32  * of the user, and, if it exits, a copy of the file .plan in the users
33  * home directory.  Finger may be called with or without a list of people
34  * to finger -- if no list is given, all the people currently logged in
35  * are fingered.
36  *
37  * The program is validly called by one of the following:
38  *
39  *	finger			{short form list of users}
40  *	finger -l		{long form list of users}
41  *	finger -b		{briefer long form list of users}
42  *	finger -q		{quick list of users}
43  *	finger -i		{quick list of users with idle times}
44  *	finger namelist		{long format list of specified users}
45  *	finger -s namelist	{short format list of specified users}
46  *	finger -w namelist	{narrow short format list of specified users}
47  *
48  * where 'namelist' is a list of users login names.
49  * The other options can all be given after one '-', or each can have its
50  * own '-'.  The -f option disables the printing of headers for short and
51  * quick outputs.  The -b option briefens long format outputs.  The -p
52  * option turns off plans for long format outputs.
53  */
54 
55 #include <sys/types.h>
56 #include <sys/stat.h>
57 #include <utmp.h>
58 #include <sys/signal.h>
59 #include <pwd.h>
60 #include <stdio.h>
61 #include <lastlog.h>
62 #include <ctype.h>
63 #include <sys/time.h>
64 #include <sys/socket.h>
65 #include <netinet/in.h>
66 #include <netdb.h>
67 
68 #define ASTERISK	'*'		/* ignore this in real name */
69 #define COMMA		','		/* separator in pw_gecos field */
70 #define COMMAND		'-'		/* command line flag char */
71 #define CORY		'C'		/* cory hall office */
72 #define EVANS		'E'		/* evans hall office */
73 #define SAMENAME	'&'		/* repeat login name in real name */
74 #define TALKABLE	0220		/* tty is writable if 220 mode */
75 
76 struct utmp user;
77 #define NMAX sizeof(user.ut_name)
78 #define LMAX sizeof(user.ut_line)
79 #define HMAX sizeof(user.ut_host)
80 
81 struct person {			/* one for each person fingered */
82 	char *name;			/* name */
83 	char tty[LMAX+1];		/* null terminated tty line */
84 	char host[HMAX+1];		/* null terminated remote host name */
85 	long loginat;			/* time of (last) login */
86 	long idletime;			/* how long idle (if logged in) */
87 	char *realname;			/* pointer to full name */
88 	char *office;			/* pointer to office name */
89 	char *officephone;		/* pointer to office phone no. */
90 	char *homephone;		/* pointer to home phone no. */
91 	char *random;			/* for any random stuff in pw_gecos */
92 	struct passwd *pwd;		/* structure of /etc/passwd stuff */
93 	char loggedin;			/* person is logged in */
94 	char writable;			/* tty is writable */
95 	char original;			/* this is not a duplicate entry */
96 	struct person *link;		/* link to next person */
97 };
98 
99 char LASTLOG[] = "/usr/adm/lastlog";	/* last login info */
100 char USERLOG[] = "/etc/utmp";		/* who is logged in */
101 char PLAN[] = "/.plan";			/* what plan file is */
102 char PROJ[] = "/.project";		/* what project file */
103 
104 int unbrief = 1;			/* -b option default */
105 int header = 1;				/* -f option default */
106 int hack = 1;				/* -h option default */
107 int idle = 0;				/* -i option default */
108 int large = 0;				/* -l option default */
109 int match = 1;				/* -m option default */
110 int plan = 1;				/* -p option default */
111 int unquick = 1;			/* -q option default */
112 int small = 0;				/* -s option default */
113 int wide = 1;				/* -w option default */
114 
115 int unshort;
116 int lf;					/* LASTLOG file descriptor */
117 struct person *person1;			/* list of people */
118 long tloc;				/* current time */
119 
120 struct passwd *pwdcopy();
121 char *strcpy();
122 char *malloc();
123 char *ctime();
124 
125 main(argc, argv)
126 	int argc;
127 	register char **argv;
128 {
129 	FILE *fp;
130 	register char *s;
131 
132 	/* parse command line for (optional) arguments */
133 	while (*++argv && **argv == COMMAND)
134 		for (s = *argv + 1; *s; s++)
135 			switch (*s) {
136 			case 'b':
137 				unbrief = 0;
138 				break;
139 			case 'f':
140 				header = 0;
141 				break;
142 			case 'h':
143 				hack = 0;
144 				break;
145 			case 'i':
146 				idle = 1;
147 				unquick = 0;
148 				break;
149 			case 'l':
150 				large = 1;
151 				break;
152 			case 'm':
153 				match = 0;
154 				break;
155 			case 'p':
156 				plan = 0;
157 				break;
158 			case 'q':
159 				unquick = 0;
160 				break;
161 			case 's':
162 				small = 1;
163 				break;
164 			case 'w':
165 				wide = 0;
166 				break;
167 			default:
168 				fprintf(stderr, "Usage: finger [-bfhilmpqsw] [login1 [login2 ...] ]\n");
169 				exit(1);
170 			}
171 	if (unquick || idle)
172 		time(&tloc);
173 	/*
174 	 * *argv == 0 means no names given
175 	 */
176 	if (*argv == 0)
177 		doall();
178 	else
179 		donames(argv);
180 	if (person1)
181 		print();
182 	exit(0);
183 }
184 
185 doall()
186 {
187 	register struct person *p;
188 	register struct passwd *pw;
189 	int uf;
190 	char name[NMAX + 1];
191 
192 	unshort = large;
193 	if ((uf = open(USERLOG, 0)) < 0) {
194 		fprintf(stderr, "finger: error opening %s\n", USERLOG);
195 		exit(2);
196 	}
197 	if (unquick) {
198 		extern _pw_stayopen;
199 
200 		setpwent();
201 		_pw_stayopen = 1;
202 		fwopen();
203 	}
204 	while (read(uf, (char *)&user, sizeof user) == sizeof user) {
205 		if (user.ut_name[0] == 0)
206 			continue;
207 		if (person1 == 0)
208 			p = person1 = (struct person *) malloc(sizeof *p);
209 		else {
210 			p->link = (struct person *) malloc(sizeof *p);
211 			p = p->link;
212 		}
213 		bcopy(user.ut_name, name, NMAX);
214 		name[NMAX] = 0;
215 		bcopy(user.ut_line, p->tty, LMAX);
216 		p->tty[LMAX] = 0;
217 		bcopy(user.ut_host, p->host, HMAX);
218 		p->host[HMAX] = 0;
219 		p->loginat = user.ut_time;
220 		p->pwd = 0;
221 		p->loggedin = 1;
222 		if (unquick && (pw = getpwnam(name))) {
223 			p->pwd = pwdcopy(pw);
224 			decode(p);
225 			p->name = p->pwd->pw_name;
226 		} else
227 			p->name = strcpy(malloc(strlen(name) + 1), name);
228 	}
229 	if (unquick) {
230 		fwclose();
231 		endpwent();
232 	}
233 	close(uf);
234 	if (person1 == 0) {
235 		printf("No one logged on\n");
236 		return;
237 	}
238 	p->link = 0;
239 }
240 
241 donames(argv)
242 	char **argv;
243 {
244 	register struct person *p;
245 	register struct passwd *pw;
246 	int uf;
247 
248 	/*
249 	 * get names from command line and check to see if they're
250 	 * logged in
251 	 */
252 	unshort = !small;
253 	for (; *argv != 0; argv++) {
254 		if (netfinger(*argv))
255 			continue;
256 		if (person1 == 0)
257 			p = person1 = (struct person *) malloc(sizeof *p);
258 		else {
259 			p->link = (struct person *) malloc(sizeof *p);
260 			p = p->link;
261 		}
262 		p->name = *argv;
263 		p->loggedin = 0;
264 		p->original = 1;
265 		p->pwd = 0;
266 	}
267 	if (person1 == 0)
268 		return;
269 	p->link = 0;
270 	/*
271 	 * if we are doing it, read /etc/passwd for the useful info
272 	 */
273 	if (unquick) {
274 		setpwent();
275 		if (!match) {
276 			extern _pw_stayopen;
277 
278 			_pw_stayopen = 1;
279 			for (p = person1; p != 0; p = p->link)
280 				if (pw = getpwnam(p->name))
281 					p->pwd = pwdcopy(pw);
282 		} else while ((pw = getpwent()) != 0) {
283 			for (p = person1; p != 0; p = p->link) {
284 				if (!p->original)
285 					continue;
286 				if (strcmp(p->name, pw->pw_name) != 0 &&
287 				    !matchcmp(pw->pw_gecos, pw->pw_name, p->name))
288 					continue;
289 				if (p->pwd == 0)
290 					p->pwd = pwdcopy(pw);
291 				else {
292 					struct person *new;
293 					/*
294 					 * handle multiple login names, insert
295 					 * new "duplicate" entry behind
296 					 */
297 					new = (struct person *)
298 						malloc(sizeof *new);
299 					new->pwd = pwdcopy(pw);
300 					new->name = p->name;
301 					new->original = 1;
302 					new->loggedin = 0;
303 					new->link = p->link;
304 					p->original = 0;
305 					p->link = new;
306 					p = new;
307 				}
308 			}
309 		}
310 		endpwent();
311 	}
312 	/* Now get login information */
313 	if ((uf = open(USERLOG, 0)) < 0) {
314 		fprintf(stderr, "finger: error opening %s\n", USERLOG);
315 		exit(2);
316 	}
317 	while (read(uf, (char *)&user, sizeof user) == sizeof user) {
318 		if (*user.ut_name == 0)
319 			continue;
320 		for (p = person1; p != 0; p = p->link) {
321 			if (p->loggedin == 2)
322 				continue;
323 			if (strncmp(p->pwd ? p->pwd->pw_name : p->name,
324 				    user.ut_name, NMAX) != 0)
325 				continue;
326 			if (p->loggedin == 0) {
327 				bcopy(user.ut_line, p->tty, LMAX);
328 				p->tty[LMAX] = 0;
329 				bcopy(user.ut_host, p->host, HMAX);
330 				p->host[HMAX] = 0;
331 				p->loginat = user.ut_time;
332 				p->loggedin = 1;
333 			} else {	/* p->loggedin == 1 */
334 				struct person *new;
335 				new = (struct person *) malloc(sizeof *new);
336 				new->name = p->name;
337 				bcopy(user.ut_line, new->tty, LMAX);
338 				new->tty[LMAX] = 0;
339 				bcopy(user.ut_host, new->host, HMAX);
340 				new->host[HMAX] = 0;
341 				new->loginat = user.ut_time;
342 				new->pwd = p->pwd;
343 				new->loggedin = 1;
344 				new->original = 0;
345 				new->link = p->link;
346 				p->loggedin = 2;
347 				p->link = new;
348 				p = new;
349 			}
350 		}
351 	}
352 	close(uf);
353 	if (unquick) {
354 		fwopen();
355 		for (p = person1; p != 0; p = p->link)
356 			decode(p);
357 		fwclose();
358 	}
359 }
360 
361 print()
362 {
363 	register FILE *fp;
364 	register struct person *p;
365 	register char *s;
366 	register c;
367 
368 	/*
369 	 * print out what we got
370 	 */
371 	if (header) {
372 		if (unquick) {
373 			if (!unshort)
374 				if (wide)
375 					printf("Login       Name              TTY Idle    When            Office\n");
376 				else
377 					printf("Login    TTY Idle    When            Office\n");
378 		} else {
379 			printf("Login      TTY            When");
380 			if (idle)
381 				printf("             Idle");
382 			putchar('\n');
383 		}
384 	}
385 	for (p = person1; p != 0; p = p->link) {
386 		if (!unquick) {
387 			quickprint(p);
388 			continue;
389 		}
390 		if (!unshort) {
391 			shortprint(p);
392 			continue;
393 		}
394 		personprint(p);
395 		if (p->pwd != 0) {
396 			if (hack) {
397 				s = malloc(strlen(p->pwd->pw_dir) +
398 					sizeof PROJ);
399 				strcpy(s, p->pwd->pw_dir);
400 				strcat(s, PROJ);
401 				if ((fp = fopen(s, "r")) != 0) {
402 					printf("Project: ");
403 					while ((c = getc(fp)) != EOF) {
404 						if (c == '\n')
405 							break;
406 						if (isprint(c) || isspace(c))
407 							putchar(c);
408 						else
409 							putchar(c ^ 100);
410 					}
411 					fclose(fp);
412 					putchar('\n');
413 				}
414 				free(s);
415 			}
416 			if (plan) {
417 				s = malloc(strlen(p->pwd->pw_dir) +
418 					sizeof PLAN);
419 				strcpy(s, p->pwd->pw_dir);
420 				strcat(s, PLAN);
421 				if ((fp = fopen(s, "r")) == 0)
422 					printf("No Plan.\n");
423 				else {
424 					printf("Plan:\n");
425 					while ((c = getc(fp)) != EOF)
426 						if (isprint(c) || isspace(c))
427 							putchar(c);
428 						else
429 							putchar(c ^ 100);
430 					fclose(fp);
431 				}
432 				free(s);
433 			}
434 		}
435 		if (p->link != 0)
436 			putchar('\n');
437 	}
438 }
439 
440 /*
441  * Duplicate a pwd entry.
442  * Note: Only the useful things (what the program currently uses) are copied.
443  */
444 struct passwd *
445 pwdcopy(pfrom)
446 	register struct passwd *pfrom;
447 {
448 	register struct passwd *pto;
449 
450 	pto = (struct passwd *) malloc(sizeof *pto);
451 #define savestr(s) strcpy(malloc(strlen(s) + 1), s)
452 	pto->pw_name = savestr(pfrom->pw_name);
453 	pto->pw_uid = pfrom->pw_uid;
454 	pto->pw_gecos = savestr(pfrom->pw_gecos);
455 	pto->pw_dir = savestr(pfrom->pw_dir);
456 	pto->pw_shell = savestr(pfrom->pw_shell);
457 #undef savestr
458 	return pto;
459 }
460 
461 /*
462  * print out information on quick format giving just name, tty, login time
463  * and idle time if idle is set.
464  */
465 quickprint(pers)
466 	register struct person *pers;
467 {
468 	printf("%-*.*s  ", NMAX, NMAX, pers->name);
469 	if (pers->loggedin) {
470 		if (idle) {
471 			findidle(pers);
472 			printf("%c%-*s %-16.16s", pers->writable ? ' ' : '*',
473 				LMAX, pers->tty, ctime(&pers->loginat));
474 			ltimeprint("   ", &pers->idletime, "");
475 		} else
476 			printf(" %-*s %-16.16s", LMAX,
477 				pers->tty, ctime(&pers->loginat));
478 		putchar('\n');
479 	} else
480 		printf("          Not Logged In\n");
481 }
482 
483 /*
484  * print out information in short format, giving login name, full name,
485  * tty, idle time, login time, office location and phone.
486  */
487 shortprint(pers)
488 	register struct person *pers;
489 {
490 	char *p;
491 	char dialup;
492 
493 	if (pers->pwd == 0) {
494 		printf("%-15s       ???\n", pers->name);
495 		return;
496 	}
497 	printf("%-*s", NMAX, pers->pwd->pw_name);
498 	dialup = 0;
499 	if (wide) {
500 		if (pers->realname)
501 			printf(" %-20.20s", pers->realname);
502 		else
503 			printf("        ???          ");
504 	}
505 	putchar(' ');
506 	if (pers->loggedin && !pers->writable)
507 		putchar('*');
508 	else
509 		putchar(' ');
510 	if (*pers->tty) {
511 		if (pers->tty[0] == 't' && pers->tty[1] == 't' &&
512 		    pers->tty[2] == 'y') {
513 			if (pers->tty[3] == 'd' && pers->loggedin)
514 				dialup = 1;
515 			printf("%-2.2s ", pers->tty + 3);
516 		} else
517 			printf("%-2.2s ", pers->tty);
518 	} else
519 		printf("   ");
520 	p = ctime(&pers->loginat);
521 	if (pers->loggedin) {
522 		stimeprint(&pers->idletime);
523 		printf(" %3.3s %-5.5s ", p, p + 11);
524 	} else if (pers->loginat == 0)
525 		printf(" < .  .  .  . >");
526 	else if (tloc - pers->loginat >= 180 * 24 * 60 * 60)
527 		printf(" <%-6.6s, %-4.4s>", p + 4, p + 20);
528 	else
529 		printf(" <%-12.12s>", p + 4);
530 	if (dialup && pers->homephone)
531 		printf(" %20s", pers->homephone);
532 	else {
533 		if (pers->office)
534 			printf(" %-11.11s", pers->office);
535 		else if (pers->officephone || pers->homephone)
536 			printf("            ");
537 		if (pers->officephone)
538 			printf(" %s", pers->officephone);
539 		else if (pers->homephone)
540 			printf(" %s", pers->homephone);
541 	}
542 	putchar('\n');
543 }
544 
545 /*
546  * print out a person in long format giving all possible information.
547  * directory and shell are inhibited if unbrief is clear.
548  */
549 personprint(pers)
550 	register struct person *pers;
551 {
552 	if (pers->pwd == 0) {
553 		printf("Login name: %-10s\t\t\tIn real life: ???\n",
554 			pers->name);
555 		return;
556 	}
557 	printf("Login name: %-10s", pers->pwd->pw_name);
558 	if (pers->loggedin && !pers->writable)
559 		printf("	(messages off)	");
560 	else
561 		printf("			");
562 	if (pers->realname)
563 		printf("In real life: %s", pers->realname);
564 	if (pers->office) {
565 		printf("\nOffice: %-.11s", pers->office);
566 		if (pers->officephone) {
567 			printf(", %s", pers->officephone);
568 			if (pers->homephone)
569 				printf("\t\tHome phone: %s", pers->homephone);
570 			else if (pers->random)
571 				printf("\t\t%s", pers->random);
572 		} else
573 			if (pers->homephone)
574 				printf("\t\t\tHome phone: %s", pers->homephone);
575 			else if (pers->random)
576 				printf("\t\t\t%s", pers->random);
577 	} else if (pers->officephone) {
578 		printf("\nPhone: %s", pers->officephone);
579 		if (pers->homephone)
580 			printf(", %s", pers->homephone);
581 		if (pers->random)
582 			printf(", %s", pers->random);
583 	} else if (pers->homephone) {
584 		printf("\nPhone: %s", pers->homephone);
585 		if (pers->random)
586 			printf(", %s", pers->random);
587 	} else if (pers->random)
588 		printf("\n%s", pers->random);
589 	if (unbrief) {
590 		printf("\nDirectory: %-25s", pers->pwd->pw_dir);
591 		if (*pers->pwd->pw_shell)
592 			printf("\tShell: %-s", pers->pwd->pw_shell);
593 	}
594 	if (pers->loggedin) {
595 		register char *ep = ctime(&pers->loginat);
596 		if (*pers->host) {
597 			printf("\nOn since %15.15s on %s from %s",
598 				&ep[4], pers->tty, pers->host);
599 			ltimeprint("\n", &pers->idletime, " Idle Time");
600 		} else {
601 			printf("\nOn since %15.15s on %-*s",
602 				&ep[4], LMAX, pers->tty);
603 			ltimeprint("\t", &pers->idletime, " Idle Time");
604 		}
605 	} else if (pers->loginat == 0)
606 		printf("\nNever logged in.");
607 	else if (tloc - pers->loginat > 180 * 24 * 60 * 60) {
608 		register char *ep = ctime(&pers->loginat);
609 		printf("\nLast login %10.10s, %4.4s on %s",
610 			ep, ep+20, pers->tty);
611 		if (*pers->host)
612 			printf(" from %s", pers->host);
613 	} else {
614 		register char *ep = ctime(&pers->loginat);
615 		printf("\nLast login %16.16s on %s", ep, pers->tty);
616 		if (*pers->host)
617 			printf(" from %s", pers->host);
618 	}
619 	putchar('\n');
620 }
621 
622 /*
623  *  very hacky section of code to format phone numbers.  filled with
624  *  magic constants like 4, 7 and 10.
625  */
626 char *
627 phone(s, len, alldigits)
628 	register char *s;
629 	int len;
630 	char alldigits;
631 {
632 	char fonebuf[15];
633 	register char *p = fonebuf;
634 	register i;
635 
636 	if (!alldigits)
637 		return (strcpy(malloc(len + 1), s));
638 	switch (len) {
639 	case 4:
640 		*p++ = ' ';
641 		*p++ = 'x';
642 		*p++ = '2';
643 		*p++ = '-';
644 		for (i = 0; i < 4; i++)
645 			*p++ = *s++;
646 		break;
647 	case 5:
648 		*p++ = ' ';
649 		*p++ = 'x';
650 		*p++ = *s++;
651 		*p++ = '-';
652 		for (i = 0; i < 4; i++)
653 			*p++ = *s++;
654 		break;
655 	case 7:
656 		for (i = 0; i < 3; i++)
657 			*p++ = *s++;
658 		*p++ = '-';
659 		for (i = 0; i < 4; i++)
660 			*p++ = *s++;
661 		break;
662 	case 10:
663 		for (i = 0; i < 3; i++)
664 			*p++ = *s++;
665 		*p++ = '-';
666 		for (i = 0; i < 3; i++)
667 			*p++ = *s++;
668 		*p++ = '-';
669 		for (i = 0; i < 4; i++)
670 			*p++ = *s++;
671 		break;
672 	case 0:
673 		return 0;
674 	default:
675 		return (strcpy(malloc(len + 1), s));
676 	}
677 	*p++ = 0;
678 	return (strcpy(malloc(p - fonebuf), fonebuf));
679 }
680 
681 /*
682  * decode the information in the gecos field of /etc/passwd
683  */
684 decode(pers)
685 	register struct person *pers;
686 {
687 	char buffer[256];
688 	register char *bp, *gp, *lp;
689 	int alldigits;
690 	int hasspace;
691 	int len;
692 
693 	pers->realname = 0;
694 	pers->office = 0;
695 	pers->officephone = 0;
696 	pers->homephone = 0;
697 	pers->random = 0;
698 	if (pers->pwd == 0)
699 		return;
700 	gp = pers->pwd->pw_gecos;
701 	bp = buffer;
702 	if (*gp == ASTERISK)
703 		gp++;
704 	while (*gp && *gp != COMMA)			/* name */
705 		if (*gp == SAMENAME) {
706 			lp = pers->pwd->pw_name;
707 			if (islower(*lp))
708 				*bp++ = toupper(*lp++);
709 			while (*bp++ = *lp++)
710 				;
711 			bp--;
712 			gp++;
713 		} else
714 			*bp++ = *gp++;
715 	*bp++ = 0;
716 	if ((len = bp - buffer) > 1)
717 		pers->realname = strcpy(malloc(len), buffer);
718 	if (*gp == COMMA) {				/* office */
719 		gp++;
720 		hasspace = 0;
721 		bp = buffer;
722 		while (*gp && *gp != COMMA) {
723 			*bp = *gp++;
724 			if (*bp == ' ')
725 				hasspace = 1;
726 			/* leave 5 for Cory and Evans expansion */
727 			if (bp < buffer + sizeof buffer - 6)
728 				bp++;
729 		}
730 		*bp = 0;
731 		len = bp - buffer;
732 		bp--;			/* point to last character */
733 		if (hasspace || len == 0)
734 			len++;
735 		else if (*bp == CORY) {
736 			strcpy(bp, " Cory");
737 			len += 5;
738 		} else if (*bp == EVANS) {
739 			strcpy(bp, " Evans");
740 			len += 6;
741 		} else
742 			len++;
743 		if (len > 1)
744 			pers->office = strcpy(malloc(len), buffer);
745 	}
746 	if (*gp == COMMA) {				/* office phone */
747 		gp++;
748 		bp = buffer;
749 		alldigits = 1;
750 		while (*gp && *gp != COMMA) {
751 			*bp = *gp++;
752 			if (!isdigit(*bp))
753 				alldigits = 0;
754 			if (bp < buffer + sizeof buffer - 1)
755 				bp++;
756 		}
757 		*bp = 0;
758 		pers->officephone = phone(buffer, bp - buffer, alldigits);
759 	}
760 	if (*gp == COMMA) {				/* home phone */
761 		gp++;
762 		bp = buffer;
763 		alldigits = 1;
764 		while (*gp && *gp != COMMA) {
765 			*bp = *gp++;
766 			if (!isdigit(*bp))
767 				alldigits = 0;
768 			if (bp < buffer + sizeof buffer - 1)
769 				bp++;
770 		}
771 		*bp = 0;
772 		pers->homephone = phone(buffer, bp - buffer, alldigits);
773 	}
774 	if (pers->loggedin)
775 		findidle(pers);
776 	else
777 		findwhen(pers);
778 }
779 
780 /*
781  * find the last log in of a user by checking the LASTLOG file.
782  * the entry is indexed by the uid, so this can only be done if
783  * the uid is known (which it isn't in quick mode)
784  */
785 
786 fwopen()
787 {
788 	if ((lf = open(LASTLOG, 0)) < 0)
789 		fprintf(stderr, "finger: %s open error\n", LASTLOG);
790 }
791 
792 findwhen(pers)
793 	register struct person *pers;
794 {
795 	struct lastlog ll;
796 	int i;
797 
798 	if (lf >= 0) {
799 		lseek(lf, (long)pers->pwd->pw_uid * sizeof ll, 0);
800 		if ((i = read(lf, (char *)&ll, sizeof ll)) == sizeof ll) {
801 			bcopy(ll.ll_line, pers->tty, LMAX);
802 			pers->tty[LMAX] = 0;
803 			bcopy(ll.ll_host, pers->host, HMAX);
804 			pers->host[HMAX] = 0;
805 			pers->loginat = ll.ll_time;
806 		} else {
807 			if (i != 0)
808 				fprintf(stderr, "finger: %s read error\n",
809 					LASTLOG);
810 			pers->tty[0] = 0;
811 			pers->host[0] = 0;
812 			pers->loginat = 0L;
813 		}
814 	} else {
815 		pers->tty[0] = 0;
816 		pers->host[0] = 0;
817 		pers->loginat = 0L;
818 	}
819 }
820 
821 fwclose()
822 {
823 	if (lf >= 0)
824 		close(lf);
825 }
826 
827 /*
828  * find the idle time of a user by doing a stat on /dev/tty??,
829  * where tty?? has been gotten from USERLOG, supposedly.
830  */
831 findidle(pers)
832 	register struct person *pers;
833 {
834 	struct stat ttystatus;
835 	static char buffer[20] = "/dev/";
836 	long t;
837 #define TTYLEN 5
838 
839 	strcpy(buffer + TTYLEN, pers->tty);
840 	buffer[TTYLEN+LMAX] = 0;
841 	if (stat(buffer, &ttystatus) < 0) {
842 		fprintf(stderr, "finger: Can't stat %s\n", buffer);
843 		exit(4);
844 	}
845 	time(&t);
846 	if (t < ttystatus.st_atime)
847 		pers->idletime = 0L;
848 	else
849 		pers->idletime = t - ttystatus.st_atime;
850 	pers->writable = (ttystatus.st_mode & TALKABLE) == TALKABLE;
851 }
852 
853 /*
854  * print idle time in short format; this program always prints 4 characters;
855  * if the idle time is zero, it prints 4 blanks.
856  */
857 stimeprint(dt)
858 	long *dt;
859 {
860 	register struct tm *delta;
861 
862 	delta = gmtime(dt);
863 	if (delta->tm_yday == 0)
864 		if (delta->tm_hour == 0)
865 			if (delta->tm_min == 0)
866 				printf("    ");
867 			else
868 				printf("  %2d", delta->tm_min);
869 		else
870 			if (delta->tm_hour >= 10)
871 				printf("%3d:", delta->tm_hour);
872 			else
873 				printf("%1d:%02d",
874 					delta->tm_hour, delta->tm_min);
875 	else
876 		printf("%3dd", delta->tm_yday);
877 }
878 
879 /*
880  * print idle time in long format with care being taken not to pluralize
881  * 1 minutes or 1 hours or 1 days.
882  * print "prefix" first.
883  */
884 ltimeprint(before, dt, after)
885 	long *dt;
886 	char *before, *after;
887 {
888 	register struct tm *delta;
889 
890 	delta = gmtime(dt);
891 	if (delta->tm_yday == 0 && delta->tm_hour == 0 && delta->tm_min == 0 &&
892 	    delta->tm_sec <= 10)
893 		return (0);
894 	printf("%s", before);
895 	if (delta->tm_yday >= 10)
896 		printf("%d days", delta->tm_yday);
897 	else if (delta->tm_yday > 0)
898 		printf("%d day%s %d hour%s",
899 			delta->tm_yday, delta->tm_yday == 1 ? "" : "s",
900 			delta->tm_hour, delta->tm_hour == 1 ? "" : "s");
901 	else
902 		if (delta->tm_hour >= 10)
903 			printf("%d hours", delta->tm_hour);
904 		else if (delta->tm_hour > 0)
905 			printf("%d hour%s %d minute%s",
906 				delta->tm_hour, delta->tm_hour == 1 ? "" : "s",
907 				delta->tm_min, delta->tm_min == 1 ? "" : "s");
908 		else
909 			if (delta->tm_min >= 10)
910 				printf("%2d minutes", delta->tm_min);
911 			else if (delta->tm_min == 0)
912 				printf("%2d seconds", delta->tm_sec);
913 			else
914 				printf("%d minute%s %d second%s",
915 					delta->tm_min,
916 					delta->tm_min == 1 ? "" : "s",
917 					delta->tm_sec,
918 					delta->tm_sec == 1 ? "" : "s");
919 	printf("%s", after);
920 }
921 
922 matchcmp(gname, login, given)
923 	register char *gname;
924 	char *login;
925 	char *given;
926 {
927 	char buffer[100];
928 	register char *bp, *lp;
929 	register c;
930 
931 	if (*gname == ASTERISK)
932 		gname++;
933 	lp = 0;
934 	bp = buffer;
935 	for (;;)
936 		switch (c = *gname++) {
937 		case SAMENAME:
938 			for (lp = login; bp < buffer + sizeof buffer
939 					 && (*bp++ = *lp++);)
940 				;
941 			bp--;
942 			break;
943 		case ' ':
944 		case COMMA:
945 		case '\0':
946 			*bp = 0;
947 			if (namecmp(buffer, given))
948 				return (1);
949 			if (c == COMMA || c == 0)
950 				return (0);
951 			bp = buffer;
952 			break;
953 		default:
954 			if (bp < buffer + sizeof buffer)
955 				*bp++ = c;
956 		}
957 	/*NOTREACHED*/
958 }
959 
960 namecmp(name1, name2)
961 	register char *name1, *name2;
962 {
963 	register c1, c2;
964 
965 	for (;;) {
966 		c1 = *name1++;
967 		if (islower(c1))
968 			c1 = toupper(c1);
969 		c2 = *name2++;
970 		if (islower(c2))
971 			c2 = toupper(c2);
972 		if (c1 != c2)
973 			break;
974 		if (c1 == 0)
975 			return (1);
976 	}
977 	if (!c1) {
978 		for (name2--; isdigit(*name2); name2++)
979 			;
980 		if (*name2 == 0)
981 			return (1);
982 	} else if (!c2) {
983 		for (name1--; isdigit(*name1); name1++)
984 			;
985 		if (*name2 == 0)
986 			return (1);
987 	}
988 	return (0);
989 }
990 
991 netfinger(name)
992 	char *name;
993 {
994 	char *host;
995 	char fname[100];
996 	struct hostent *hp;
997 	struct servent *sp;
998 	struct sockaddr_in sin;
999 	int s;
1000 	char *rindex();
1001 	register FILE *f;
1002 	register int c;
1003 	register int lastc;
1004 
1005 	if (name == NULL)
1006 		return (0);
1007 	host = rindex(name, '@');
1008 	if (host == NULL)
1009 		return (0);
1010 	*host++ = 0;
1011 	hp = gethostbyname(host);
1012 	if (hp == NULL) {
1013 		static struct hostent def;
1014 		static struct in_addr defaddr;
1015 		static char *alist[1];
1016 		static char namebuf[128];
1017 		int inet_addr();
1018 
1019 		defaddr.s_addr = inet_addr(host);
1020 		if (defaddr.s_addr == -1) {
1021 			printf("unknown host: %s\n", host);
1022 			return (1);
1023 		}
1024 		strcpy(namebuf, host);
1025 		def.h_name = namebuf;
1026 		def.h_addr_list = alist, def.h_addr = (char *)&defaddr;
1027 		def.h_length = sizeof (struct in_addr);
1028 		def.h_addrtype = AF_INET;
1029 		def.h_aliases = 0;
1030 		hp = &def;
1031 	}
1032 	sp = getservbyname("finger", "tcp");
1033 	if (sp == 0) {
1034 		printf("tcp/finger: unknown service\n");
1035 		return (1);
1036 	}
1037 	sin.sin_family = hp->h_addrtype;
1038 	bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
1039 	sin.sin_port = sp->s_port;
1040 	s = socket(hp->h_addrtype, SOCK_STREAM, 0);
1041 	if (s < 0) {
1042 		perror("socket");
1043 		return (1);
1044 	}
1045 	printf("[%s]\n", hp->h_name);
1046 	fflush(stdout);
1047 	if (connect(s, (char *)&sin, sizeof (sin)) < 0) {
1048 		perror("connect");
1049 		close(s);
1050 		return (1);
1051 	}
1052 	if (large) write(s, "/W ", 3);
1053 	write(s, name, strlen(name));
1054 	write(s, "\r\n", 2);
1055 	f = fdopen(s, "r");
1056 	while ((c = getc(f)) != EOF) {
1057 		switch(c) {
1058 		case 0210:
1059 		case 0211:
1060 		case 0212:
1061 		case 0214:
1062 			c -= 0200;
1063 			break;
1064 		case 0215:
1065 			c = '\n';
1066 			break;
1067 		}
1068 		lastc = c;
1069 		if (isprint(c) || isspace(c))
1070 			putchar(c);
1071 		else
1072 			putchar(c ^ 100);
1073 	}
1074 	if (lastc != '\n')
1075 		putchar('\n');
1076 	(void)fclose(f);
1077 	return (1);
1078 }
1079