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