xref: /original-bsd/usr.bin/finger/finger.c (revision 9c06e5c6)
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.6 (Berkeley) 10/21/85";
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	0222		/* tty is writable if 222 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 						putchar(c);
407 					}
408 					fclose(fp);
409 					putchar('\n');
410 				}
411 				free(s);
412 			}
413 			if (plan) {
414 				s = malloc(strlen(p->pwd->pw_dir) +
415 					sizeof PLAN);
416 				strcpy(s, p->pwd->pw_dir);
417 				strcat(s, PLAN);
418 				if ((fp = fopen(s, "r")) == 0)
419 					printf("No Plan.\n");
420 				else {
421 					printf("Plan:\n");
422 					while ((c = getc(fp)) != EOF)
423 						putchar(c);
424 					fclose(fp);
425 				}
426 				free(s);
427 			}
428 		}
429 		if (p->link != 0)
430 			putchar('\n');
431 	}
432 }
433 
434 /*
435  * Duplicate a pwd entry.
436  * Note: Only the useful things (what the program currently uses) are copied.
437  */
438 struct passwd *
439 pwdcopy(pfrom)
440 	register struct passwd *pfrom;
441 {
442 	register struct passwd *pto;
443 
444 	pto = (struct passwd *) malloc(sizeof *pto);
445 #define savestr(s) strcpy(malloc(strlen(s) + 1), s)
446 	pto->pw_name = savestr(pfrom->pw_name);
447 	pto->pw_uid = pfrom->pw_uid;
448 	pto->pw_gecos = savestr(pfrom->pw_gecos);
449 	pto->pw_dir = savestr(pfrom->pw_dir);
450 	pto->pw_shell = savestr(pfrom->pw_shell);
451 #undef savestr
452 	return pto;
453 }
454 
455 /*
456  * print out information on quick format giving just name, tty, login time
457  * and idle time if idle is set.
458  */
459 quickprint(pers)
460 	register struct person *pers;
461 {
462 	printf("%-*.*s  ", NMAX, NMAX, pers->name);
463 	if (pers->loggedin) {
464 		if (idle) {
465 			findidle(pers);
466 			printf("%c%-*s %-16.16s", pers->writable ? ' ' : '*',
467 				LMAX, pers->tty, ctime(&pers->loginat));
468 			ltimeprint("   ", &pers->idletime, "");
469 		} else
470 			printf(" %-*s %-16.16s", LMAX,
471 				pers->tty, ctime(&pers->loginat));
472 		putchar('\n');
473 	} else
474 		printf("          Not Logged In\n");
475 }
476 
477 /*
478  * print out information in short format, giving login name, full name,
479  * tty, idle time, login time, office location and phone.
480  */
481 shortprint(pers)
482 	register struct person *pers;
483 {
484 	char *p;
485 	char dialup;
486 
487 	if (pers->pwd == 0) {
488 		printf("%-15s       ???\n", pers->name);
489 		return;
490 	}
491 	printf("%-*s", NMAX, pers->pwd->pw_name);
492 	dialup = 0;
493 	if (wide) {
494 		if (pers->realname)
495 			printf(" %-20.20s", pers->realname);
496 		else
497 			printf("        ???          ");
498 	}
499 	putchar(' ');
500 	if (pers->loggedin && !pers->writable)
501 		putchar('*');
502 	else
503 		putchar(' ');
504 	if (*pers->tty) {
505 		if (pers->tty[0] == 't' && pers->tty[1] == 't' &&
506 		    pers->tty[2] == 'y') {
507 			if (pers->tty[3] == 'd' && pers->loggedin)
508 				dialup = 1;
509 			printf("%-2.2s ", pers->tty + 3);
510 		} else
511 			printf("%-2.2s ", pers->tty);
512 	} else
513 		printf("   ");
514 	p = ctime(&pers->loginat);
515 	if (pers->loggedin) {
516 		stimeprint(&pers->idletime);
517 		printf(" %3.3s %-5.5s ", p, p + 11);
518 	} else if (pers->loginat == 0)
519 		printf(" < .  .  .  . >");
520 	else if (tloc - pers->loginat >= 180 * 24 * 60 * 60)
521 		printf(" <%-6.6s, %-4.4s>", p + 4, p + 20);
522 	else
523 		printf(" <%-12.12s>", p + 4);
524 	if (dialup && pers->homephone)
525 		printf(" %20s", pers->homephone);
526 	else {
527 		if (pers->office)
528 			printf(" %-11.11s", pers->office);
529 		else if (pers->officephone || pers->homephone)
530 			printf("            ");
531 		if (pers->officephone)
532 			printf(" %s", pers->officephone);
533 		else if (pers->homephone)
534 			printf(" %s", pers->homephone);
535 	}
536 	putchar('\n');
537 }
538 
539 /*
540  * print out a person in long format giving all possible information.
541  * directory and shell are inhibited if unbrief is clear.
542  */
543 personprint(pers)
544 	register struct person *pers;
545 {
546 	if (pers->pwd == 0) {
547 		printf("Login name: %-10s\t\t\tIn real life: ???\n",
548 			pers->name);
549 		return;
550 	}
551 	printf("Login name: %-10s", pers->pwd->pw_name);
552 	if (pers->loggedin && !pers->writable)
553 		printf("	(messages off)	");
554 	else
555 		printf("			");
556 	if (pers->realname)
557 		printf("In real life: %s", pers->realname);
558 	if (pers->office) {
559 		printf("\nOffice: %-.11s", pers->office);
560 		if (pers->officephone) {
561 			printf(", %s", pers->officephone);
562 			if (pers->homephone)
563 				printf("\t\tHome phone: %s", pers->homephone);
564 			else if (pers->random)
565 				printf("\t\t%s", pers->random);
566 		} else
567 			if (pers->homephone)
568 				printf("\t\t\tHome phone: %s", pers->homephone);
569 			else if (pers->random)
570 				printf("\t\t\t%s", pers->random);
571 	} else if (pers->officephone) {
572 		printf("\nPhone: %s", pers->officephone);
573 		if (pers->homephone)
574 			printf(", %s", pers->homephone);
575 		if (pers->random)
576 			printf(", %s", pers->random);
577 	} else if (pers->homephone) {
578 		printf("\nPhone: %s", pers->homephone);
579 		if (pers->random)
580 			printf(", %s", pers->random);
581 	} else if (pers->random)
582 		printf("\n%s", pers->random);
583 	if (unbrief) {
584 		printf("\nDirectory: %-25s", pers->pwd->pw_dir);
585 		if (*pers->pwd->pw_shell)
586 			printf("\tShell: %-s", pers->pwd->pw_shell);
587 	}
588 	if (pers->loggedin) {
589 		register char *ep = ctime(&pers->loginat);
590 		if (*pers->host) {
591 			printf("\nOn since %15.15s on %s from %s",
592 				&ep[4], pers->tty, pers->host);
593 			ltimeprint("\n", &pers->idletime, " Idle Time");
594 		} else {
595 			printf("\nOn since %15.15s on %-*s",
596 				&ep[4], LMAX, pers->tty);
597 			ltimeprint("\t", &pers->idletime, " Idle Time");
598 		}
599 	} else if (pers->loginat == 0)
600 		printf("\nNever logged in.");
601 	else if (tloc - pers->loginat > 180 * 24 * 60 * 60) {
602 		register char *ep = ctime(&pers->loginat);
603 		printf("\nLast login %10.10s, %4.4s on %s",
604 			ep, ep+20, pers->tty);
605 		if (*pers->host)
606 			printf(" from %s", pers->host);
607 	} else {
608 		register char *ep = ctime(&pers->loginat);
609 		printf("\nLast login %16.16s on %s", ep, pers->tty);
610 		if (*pers->host)
611 			printf(" from %s", pers->host);
612 	}
613 	putchar('\n');
614 }
615 
616 /*
617  *  very hacky section of code to format phone numbers.  filled with
618  *  magic constants like 4, 7 and 10.
619  */
620 char *
621 phone(s, len, alldigits)
622 	register char *s;
623 	int len;
624 	char alldigits;
625 {
626 	char fonebuf[15];
627 	register char *p = fonebuf;
628 	register i;
629 
630 	if (!alldigits)
631 		return (strcpy(malloc(len + 1), s));
632 	switch (len) {
633 	case 4:
634 		*p++ = ' ';
635 		*p++ = 'x';
636 		*p++ = '2';
637 		*p++ = '-';
638 		for (i = 0; i < 4; i++)
639 			*p++ = *s++;
640 		break;
641 	case 5:
642 		*p++ = ' ';
643 		*p++ = 'x';
644 		*p++ = *s++;
645 		*p++ = '-';
646 		for (i = 0; i < 4; i++)
647 			*p++ = *s++;
648 		break;
649 	case 7:
650 		for (i = 0; i < 3; i++)
651 			*p++ = *s++;
652 		*p++ = '-';
653 		for (i = 0; i < 4; i++)
654 			*p++ = *s++;
655 		break;
656 	case 10:
657 		for (i = 0; i < 3; i++)
658 			*p++ = *s++;
659 		*p++ = '-';
660 		for (i = 0; i < 3; i++)
661 			*p++ = *s++;
662 		*p++ = '-';
663 		for (i = 0; i < 4; i++)
664 			*p++ = *s++;
665 		break;
666 	case 0:
667 		return 0;
668 	default:
669 		return (strcpy(malloc(len + 1), s));
670 	}
671 	*p++ = 0;
672 	return (strcpy(malloc(p - fonebuf), fonebuf));
673 }
674 
675 /*
676  * decode the information in the gecos field of /etc/passwd
677  */
678 decode(pers)
679 	register struct person *pers;
680 {
681 	char buffer[256];
682 	register char *bp, *gp, *lp;
683 	int alldigits;
684 	int hasspace;
685 	int len;
686 
687 	pers->realname = 0;
688 	pers->office = 0;
689 	pers->officephone = 0;
690 	pers->homephone = 0;
691 	pers->random = 0;
692 	if (pers->pwd == 0)
693 		return;
694 	gp = pers->pwd->pw_gecos;
695 	bp = buffer;
696 	if (*gp == ASTERISK)
697 		gp++;
698 	while (*gp && *gp != COMMA)			/* name */
699 		if (*gp == SAMENAME) {
700 			lp = pers->pwd->pw_name;
701 			if (islower(*lp))
702 				*bp++ = toupper(*lp++);
703 			while (*bp++ = *lp++)
704 				;
705 			bp--;
706 			gp++;
707 		} else
708 			*bp++ = *gp++;
709 	*bp++ = 0;
710 	if ((len = bp - buffer) > 1)
711 		pers->realname = strcpy(malloc(len), buffer);
712 	if (*gp == COMMA) {				/* office */
713 		gp++;
714 		hasspace = 0;
715 		bp = buffer;
716 		while (*gp && *gp != COMMA) {
717 			*bp = *gp++;
718 			if (*bp == ' ')
719 				hasspace = 1;
720 			/* leave 5 for Cory and Evans expansion */
721 			if (bp < buffer + sizeof buffer - 6)
722 				bp++;
723 		}
724 		*bp = 0;
725 		len = bp - buffer;
726 		bp--;			/* point to last character */
727 		if (hasspace || len == 0)
728 			len++;
729 		else if (*bp == CORY) {
730 			strcpy(bp, " Cory");
731 			len += 5;
732 		} else if (*bp == EVANS) {
733 			strcpy(bp, " Evans");
734 			len += 6;
735 		} else
736 			len++;
737 		if (len > 1)
738 			pers->office = strcpy(malloc(len), buffer);
739 	}
740 	if (*gp == COMMA) {				/* office phone */
741 		gp++;
742 		bp = buffer;
743 		alldigits = 1;
744 		while (*gp && *gp != COMMA) {
745 			*bp = *gp++;
746 			if (!isdigit(*bp))
747 				alldigits = 0;
748 			if (bp < buffer + sizeof buffer - 1)
749 				bp++;
750 		}
751 		*bp = 0;
752 		pers->officephone = phone(buffer, bp - buffer, alldigits);
753 	}
754 	if (*gp == COMMA) {				/* home phone */
755 		gp++;
756 		bp = buffer;
757 		alldigits = 1;
758 		while (*gp && *gp != COMMA) {
759 			*bp = *gp++;
760 			if (!isdigit(*bp))
761 				alldigits = 0;
762 			if (bp < buffer + sizeof buffer - 1)
763 				bp++;
764 		}
765 		*bp = 0;
766 		pers->homephone = phone(buffer, bp - buffer, alldigits);
767 	}
768 	if (pers->loggedin)
769 		findidle(pers);
770 	else
771 		findwhen(pers);
772 }
773 
774 /*
775  * find the last log in of a user by checking the LASTLOG file.
776  * the entry is indexed by the uid, so this can only be done if
777  * the uid is known (which it isn't in quick mode)
778  */
779 
780 fwopen()
781 {
782 	if ((lf = open(LASTLOG, 0)) < 0)
783 		fprintf(stderr, "finger: %s open error\n", LASTLOG);
784 }
785 
786 findwhen(pers)
787 	register struct person *pers;
788 {
789 	struct lastlog ll;
790 	int i;
791 
792 	if (lf >= 0) {
793 		lseek(lf, (long)pers->pwd->pw_uid * sizeof ll, 0);
794 		if ((i = read(lf, (char *)&ll, sizeof ll)) == sizeof ll) {
795 			bcopy(ll.ll_line, pers->tty, LMAX);
796 			pers->tty[LMAX] = 0;
797 			bcopy(ll.ll_host, pers->host, HMAX);
798 			pers->host[HMAX] = 0;
799 			pers->loginat = ll.ll_time;
800 		} else {
801 			if (i != 0)
802 				fprintf(stderr, "finger: %s read error\n",
803 					LASTLOG);
804 			pers->tty[0] = 0;
805 			pers->host[0] = 0;
806 			pers->loginat = 0L;
807 		}
808 	} else {
809 		pers->tty[0] = 0;
810 		pers->host[0] = 0;
811 		pers->loginat = 0L;
812 	}
813 }
814 
815 fwclose()
816 {
817 	if (lf >= 0)
818 		close(lf);
819 }
820 
821 /*
822  * find the idle time of a user by doing a stat on /dev/tty??,
823  * where tty?? has been gotten from USERLOG, supposedly.
824  */
825 findidle(pers)
826 	register struct person *pers;
827 {
828 	struct stat ttystatus;
829 	static char buffer[20] = "/dev/";
830 	long t;
831 #define TTYLEN 5
832 
833 	strcpy(buffer + TTYLEN, pers->tty);
834 	buffer[TTYLEN+LMAX] = 0;
835 	if (stat(buffer, &ttystatus) < 0) {
836 		fprintf(stderr, "finger: Can't stat %s\n", buffer);
837 		exit(4);
838 	}
839 	time(&t);
840 	if (t < ttystatus.st_atime)
841 		pers->idletime = 0L;
842 	else
843 		pers->idletime = t - ttystatus.st_atime;
844 	pers->writable = (ttystatus.st_mode & TALKABLE) == TALKABLE;
845 }
846 
847 /*
848  * print idle time in short format; this program always prints 4 characters;
849  * if the idle time is zero, it prints 4 blanks.
850  */
851 stimeprint(dt)
852 	long *dt;
853 {
854 	register struct tm *delta;
855 
856 	delta = gmtime(dt);
857 	if (delta->tm_yday == 0)
858 		if (delta->tm_hour == 0)
859 			if (delta->tm_min == 0)
860 				printf("    ");
861 			else
862 				printf("  %2d", delta->tm_min);
863 		else
864 			if (delta->tm_hour >= 10)
865 				printf("%3d:", delta->tm_hour);
866 			else
867 				printf("%1d:%02d",
868 					delta->tm_hour, delta->tm_min);
869 	else
870 		printf("%3dd", delta->tm_yday);
871 }
872 
873 /*
874  * print idle time in long format with care being taken not to pluralize
875  * 1 minutes or 1 hours or 1 days.
876  * print "prefix" first.
877  */
878 ltimeprint(before, dt, after)
879 	long *dt;
880 	char *before, *after;
881 {
882 	register struct tm *delta;
883 
884 	delta = gmtime(dt);
885 	if (delta->tm_yday == 0 && delta->tm_hour == 0 && delta->tm_min == 0 &&
886 	    delta->tm_sec <= 10)
887 		return (0);
888 	printf("%s", before);
889 	if (delta->tm_yday >= 10)
890 		printf("%d days", delta->tm_yday);
891 	else if (delta->tm_yday > 0)
892 		printf("%d day%s %d hour%s",
893 			delta->tm_yday, delta->tm_yday == 1 ? "" : "s",
894 			delta->tm_hour, delta->tm_hour == 1 ? "" : "s");
895 	else
896 		if (delta->tm_hour >= 10)
897 			printf("%d hours", delta->tm_hour);
898 		else if (delta->tm_hour > 0)
899 			printf("%d hour%s %d minute%s",
900 				delta->tm_hour, delta->tm_hour == 1 ? "" : "s",
901 				delta->tm_min, delta->tm_min == 1 ? "" : "s");
902 		else
903 			if (delta->tm_min >= 10)
904 				printf("%2d minutes", delta->tm_min);
905 			else if (delta->tm_min == 0)
906 				printf("%2d seconds", delta->tm_sec);
907 			else
908 				printf("%d minute%s %d second%s",
909 					delta->tm_min,
910 					delta->tm_min == 1 ? "" : "s",
911 					delta->tm_sec,
912 					delta->tm_sec == 1 ? "" : "s");
913 	printf("%s", after);
914 }
915 
916 matchcmp(gname, login, given)
917 	register char *gname;
918 	char *login;
919 	char *given;
920 {
921 	char buffer[100];
922 	register char *bp, *lp;
923 	register c;
924 
925 	if (*gname == ASTERISK)
926 		gname++;
927 	lp = 0;
928 	bp = buffer;
929 	for (;;)
930 		switch (c = *gname++) {
931 		case SAMENAME:
932 			for (lp = login; bp < buffer + sizeof buffer
933 					 && (*bp++ = *lp++);)
934 				;
935 			bp--;
936 			break;
937 		case ' ':
938 		case COMMA:
939 		case '\0':
940 			*bp = 0;
941 			if (namecmp(buffer, given))
942 				return (1);
943 			if (c == COMMA || c == 0)
944 				return (0);
945 			bp = buffer;
946 			break;
947 		default:
948 			if (bp < buffer + sizeof buffer)
949 				*bp++ = c;
950 		}
951 	/*NOTREACHED*/
952 }
953 
954 namecmp(name1, name2)
955 	register char *name1, *name2;
956 {
957 	register c1, c2;
958 
959 	for (;;) {
960 		c1 = *name1++;
961 		if (islower(c1))
962 			c1 = toupper(c1);
963 		c2 = *name2++;
964 		if (islower(c2))
965 			c2 = toupper(c2);
966 		if (c1 != c2)
967 			break;
968 		if (c1 == 0)
969 			return (1);
970 	}
971 	if (!c1) {
972 		for (name2--; isdigit(*name2); name2++)
973 			;
974 		if (*name2 == 0)
975 			return (1);
976 	} else if (!c2) {
977 		for (name1--; isdigit(*name1); name1++)
978 			;
979 		if (*name2 == 0)
980 			return (1);
981 	}
982 	return (0);
983 }
984 
985 netfinger(name)
986 	char *name;
987 {
988 	char *host;
989 	char fname[100];
990 	struct hostent *hp;
991 	struct servent *sp;
992 	struct sockaddr_in sin;
993 	int s;
994 	char *rindex();
995 	register FILE *f;
996 	register int c;
997 	register int lastc;
998 
999 	if (name == NULL)
1000 		return (0);
1001 	host = rindex(name, '@');
1002 	if (host == NULL)
1003 		return (0);
1004 	*host++ = 0;
1005 	hp = gethostbyname(host);
1006 	if (hp == NULL) {
1007 		static struct hostent def;
1008 		static struct in_addr defaddr;
1009 		static char *alist[1];
1010 		static char namebuf[128];
1011 		int inet_addr();
1012 
1013 		defaddr.s_addr = inet_addr(host);
1014 		if (defaddr.s_addr == -1) {
1015 			printf("unknown host: %s\n", host);
1016 			return (1);
1017 		}
1018 		strcpy(namebuf, host);
1019 		def.h_name = namebuf;
1020 		def.h_addr_list = alist, def.h_addr = (char *)&defaddr;
1021 		def.h_length = sizeof (struct in_addr);
1022 		def.h_addrtype = AF_INET;
1023 		def.h_aliases = 0;
1024 		hp = &def;
1025 	}
1026 	printf("[%s]", hp->h_name);
1027 	sp = getservbyname("finger", "tcp");
1028 	if (sp == 0) {
1029 		printf("tcp/finger: unknown service\n");
1030 		return (1);
1031 	}
1032 	sin.sin_family = hp->h_addrtype;
1033 	bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
1034 	sin.sin_port = sp->s_port;
1035 	s = socket(hp->h_addrtype, SOCK_STREAM, 0);
1036 	if (s < 0) {
1037 		fflush(stdout);
1038 		perror("socket");
1039 		return (1);
1040 	}
1041 	if (connect(s, (char *)&sin, sizeof (sin)) < 0) {
1042 		fflush(stdout);
1043 		perror("connect");
1044 		close(s);
1045 		return (1);
1046 	}
1047 	printf("\n");
1048 	if (large) write(s, "/W ", 3);
1049 	write(s, name, strlen(name));
1050 	write(s, "\r\n", 2);
1051 	f = fdopen(s, "r");
1052 	while ((c = getc(f)) != EOF) {
1053 		switch(c) {
1054 		case 0210:
1055 		case 0211:
1056 		case 0212:
1057 		case 0214:
1058 			c -= 0200;
1059 			break;
1060 		case 0215:
1061 			c = '\n';
1062 			break;
1063 		}
1064 		putchar(lastc = c);
1065 	}
1066 	if (lastc != '\n')
1067 		putchar('\n');
1068 	(void)fclose(f);
1069 	return (1);
1070 }
1071