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