xref: /csrg-svn/usr.bin/finger/finger.c (revision 25121)
121554Sdist /*
221554Sdist  * Copyright (c) 1980 Regents of the University of California.
321554Sdist  * All rights reserved.  The Berkeley software License Agreement
421554Sdist  * specifies the terms and conditions for redistribution.
521554Sdist  */
621554Sdist 
713619Ssam #ifndef lint
821554Sdist char copyright[] =
921554Sdist "@(#) Copyright (c) 1980 Regents of the University of California.\n\
1021554Sdist  All rights reserved.\n";
1121554Sdist #endif not lint
121014Sbill 
1321554Sdist #ifndef lint
14*25121Sbloom static char sccsid[] = "@(#)finger.c	5.4 (Berkeley) 10/09/85";
1521554Sdist #endif not lint
1621554Sdist 
1718606Sedward /*
1818606Sedward  * This is a finger program.  It prints out useful information about users
1918606Sedward  * by digging it up from various system files.  It is not very portable
2018606Sedward  * because the most useful parts of the information (the full user name,
2118606Sedward  * office, and phone numbers) are all stored in the VAX-unused gecos field
2218606Sedward  * of /etc/passwd, which, unfortunately, other UNIXes use for other things.
231014Sbill  *
2418606Sedward  * There are three output formats, all of which give login name, teletype
2518606Sedward  * line number, and login time.  The short output format is reminiscent
2618606Sedward  * of finger on ITS, and gives one line of information per user containing
2718606Sedward  * in addition to the minimum basic requirements (MBR), the full name of
2818606Sedward  * the user, his idle time and office location and phone number.  The
2918606Sedward  * quick style output is UNIX who-like, giving only name, teletype and
3018606Sedward  * login time.  Finally, the long style output give the same information
3118606Sedward  * as the short (in more legible format), the home directory and shell
3218606Sedward  * of the user, and, if it exits, a copy of the file .plan in the users
3318606Sedward  * home directory.  Finger may be called with or without a list of people
3418606Sedward  * to finger -- if no list is given, all the people currently logged in
3518606Sedward  * are fingered.
361014Sbill  *
3718606Sedward  * The program is validly called by one of the following:
381014Sbill  *
391014Sbill  *	finger			{short form list of users}
401014Sbill  *	finger -l		{long form list of users}
411014Sbill  *	finger -b		{briefer long form list of users}
421014Sbill  *	finger -q		{quick list of users}
431014Sbill  *	finger -i		{quick list of users with idle times}
441014Sbill  *	finger namelist		{long format list of specified users}
451014Sbill  *	finger -s namelist	{short format list of specified users}
461014Sbill  *	finger -w namelist	{narrow short format list of specified users}
471014Sbill  *
4818606Sedward  * where 'namelist' is a list of users login names.
4918606Sedward  * The other options can all be given after one '-', or each can have its
5018606Sedward  * own '-'.  The -f option disables the printing of headers for short and
5118606Sedward  * quick outputs.  The -b option briefens long format outputs.  The -p
5218606Sedward  * option turns off plans for long format outputs.
531014Sbill  */
541014Sbill 
5518606Sedward #include <sys/types.h>
5618606Sedward #include <sys/stat.h>
5718606Sedward #include <utmp.h>
5818606Sedward #include <sys/signal.h>
5918606Sedward #include <pwd.h>
6018606Sedward #include <stdio.h>
6118606Sedward #include <lastlog.h>
6218606Sedward #include <ctype.h>
6318606Sedward #include <sys/time.h>
6418606Sedward #include <sys/socket.h>
6518606Sedward #include <netinet/in.h>
6618606Sedward #include <netdb.h>
671014Sbill 
6818606Sedward #define ASTERISK	'*'		/* ignore this in real name */
6918606Sedward #define COMMA		','		/* separator in pw_gecos field */
7018606Sedward #define COMMAND		'-'		/* command line flag char */
7118606Sedward #define CORY		'C'		/* cory hall office */
7218606Sedward #define EVANS		'E'		/* evans hall office */
7318606Sedward #define SAMENAME	'&'		/* repeat login name in real name */
7418606Sedward #define TALKABLE	0222		/* tty is writable if 222 mode */
751014Sbill 
7618606Sedward struct utmp user;
7718606Sedward #define NMAX sizeof(user.ut_name)
7818606Sedward #define LMAX sizeof(user.ut_line)
7918606Sedward #define HMAX sizeof(user.ut_host)
801014Sbill 
8118606Sedward struct person {			/* one for each person fingered */
8218606Sedward 	char *name;			/* name */
8318606Sedward 	char tty[LMAX+1];		/* null terminated tty line */
8418606Sedward 	char host[HMAX+1];		/* null terminated remote host name */
8518606Sedward 	long loginat;			/* time of (last) login */
8618606Sedward 	long idletime;			/* how long idle (if logged in) */
8718606Sedward 	char *realname;			/* pointer to full name */
8818606Sedward 	char *office;			/* pointer to office name */
8918606Sedward 	char *officephone;		/* pointer to office phone no. */
9018606Sedward 	char *homephone;		/* pointer to home phone no. */
9118606Sedward 	char *random;			/* for any random stuff in pw_gecos */
9218606Sedward 	struct passwd *pwd;		/* structure of /etc/passwd stuff */
9318606Sedward 	char loggedin;			/* person is logged in */
9418606Sedward 	char writable;			/* tty is writable */
9518606Sedward 	char original;			/* this is not a duplicate entry */
9618606Sedward 	struct person *link;		/* link to next person */
971014Sbill };
981014Sbill 
9918606Sedward char LASTLOG[] = "/usr/adm/lastlog";	/* last login info */
10018606Sedward char USERLOG[] = "/etc/utmp";		/* who is logged in */
10118606Sedward char PLAN[] = "/.plan";			/* what plan file is */
10218606Sedward char PROJ[] = "/.project";		/* what project file */
10318606Sedward 
10418606Sedward int unbrief = 1;			/* -b option default */
10518606Sedward int header = 1;				/* -f option default */
10618606Sedward int hack = 1;				/* -h option default */
10718606Sedward int idle = 0;				/* -i option default */
10818606Sedward int large = 0;				/* -l option default */
10918606Sedward int match = 1;				/* -m option default */
11018606Sedward int plan = 1;				/* -p option default */
11118606Sedward int unquick = 1;			/* -q option default */
11218606Sedward int small = 0;				/* -s option default */
11318606Sedward int wide = 1;				/* -w option default */
1141014Sbill 
11518606Sedward int unshort;
11618606Sedward int lf;					/* LASTLOG file descriptor */
11718606Sedward struct person *person1;			/* list of people */
11818606Sedward long tloc;				/* current time */
1191014Sbill 
12018606Sedward struct passwd *pwdcopy();
12118606Sedward char *strcpy();
12218606Sedward char *malloc();
12318606Sedward char *ctime();
1241014Sbill 
12518606Sedward main(argc, argv)
12618606Sedward 	int argc;
12718606Sedward 	register char **argv;
12818606Sedward {
12918606Sedward 	FILE *fp;
13018606Sedward 	register char *s;
1311014Sbill 
13218606Sedward 	/* parse command line for (optional) arguments */
13318606Sedward 	while (*++argv && **argv == COMMAND)
13418606Sedward 		for (s = *argv + 1; *s; s++)
13518606Sedward 			switch (*s) {
13618606Sedward 			case 'b':
13718606Sedward 				unbrief = 0;
13818606Sedward 				break;
13918606Sedward 			case 'f':
14018606Sedward 				header = 0;
14118606Sedward 				break;
14218606Sedward 			case 'h':
14318606Sedward 				hack = 0;
14418606Sedward 				break;
14518606Sedward 			case 'i':
14618606Sedward 				idle = 1;
14718606Sedward 				unquick = 0;
14818606Sedward 				break;
14918606Sedward 			case 'l':
15018606Sedward 				large = 1;
15118606Sedward 				break;
15218606Sedward 			case 'm':
15318606Sedward 				match = 0;
15418606Sedward 				break;
15518606Sedward 			case 'p':
15618606Sedward 				plan = 0;
15718606Sedward 				break;
15818606Sedward 			case 'q':
15918606Sedward 				unquick = 0;
16018606Sedward 				break;
16118606Sedward 			case 's':
16218606Sedward 				small = 1;
16318606Sedward 				break;
16418606Sedward 			case 'w':
16518606Sedward 				wide = 0;
16618606Sedward 				break;
16718606Sedward 			default:
16818606Sedward 				fprintf(stderr, "Usage: finger [-bfhilmpqsw] [login1 [login2 ...] ]\n");
16918606Sedward 				exit(1);
17018606Sedward 			}
17118606Sedward 	if (unquick || idle)
17218606Sedward 		time(&tloc);
17318606Sedward 	/*
17418606Sedward 	 * *argv == 0 means no names given
17518606Sedward 	 */
17618606Sedward 	if (*argv == 0)
17718606Sedward 		doall();
17818606Sedward 	else
17918606Sedward 		donames(argv);
18024461Sedward 	if (person1)
18124461Sedward 		print();
18218606Sedward 	exit(0);
18318606Sedward }
1841014Sbill 
18518606Sedward doall()
1861014Sbill {
18718606Sedward 	register struct person *p;
18818606Sedward 	register struct passwd *pw;
18918606Sedward 	int uf;
19018606Sedward 	char name[NMAX + 1];
1911014Sbill 
19218606Sedward 	unshort = large;
19318606Sedward 	if ((uf = open(USERLOG, 0)) < 0) {
19418606Sedward 		fprintf(stderr, "finger: error opening %s\n", USERLOG);
19518606Sedward 		exit(2);
1961014Sbill 	}
19718606Sedward 	if (unquick) {
19818606Sedward 		extern _pw_stayopen;
1991014Sbill 
2001014Sbill 		setpwent();
20118606Sedward 		_pw_stayopen = 1;
2021014Sbill 		fwopen();
20318606Sedward 	}
20418606Sedward 	while (read(uf, (char *)&user, sizeof user) == sizeof user) {
20518606Sedward 		if (user.ut_name[0] == 0)
20618606Sedward 			continue;
20718606Sedward 		if (person1 == 0)
20818606Sedward 			p = person1 = (struct person *) malloc(sizeof *p);
20918606Sedward 		else {
21018606Sedward 			p->link = (struct person *) malloc(sizeof *p);
2111014Sbill 			p = p->link;
2121014Sbill 		}
21318606Sedward 		bcopy(user.ut_name, name, NMAX);
21418606Sedward 		name[NMAX] = 0;
21518606Sedward 		bcopy(user.ut_line, p->tty, LMAX);
21618606Sedward 		p->tty[LMAX] = 0;
21718606Sedward 		bcopy(user.ut_host, p->host, HMAX);
21818606Sedward 		p->host[HMAX] = 0;
21918606Sedward 		p->loginat = user.ut_time;
22018606Sedward 		p->pwd = 0;
22118606Sedward 		p->loggedin = 1;
22218606Sedward 		if (unquick && (pw = getpwnam(name))) {
22318606Sedward 			p->pwd = pwdcopy(pw);
22418606Sedward 			decode(p);
22518606Sedward 			p->name = p->pwd->pw_name;
22618606Sedward 		} else
22718606Sedward 			p->name = strcpy(malloc(strlen(name) + 1), name);
22818606Sedward 	}
22918606Sedward 	if (unquick) {
2301014Sbill 		fwclose();
2311014Sbill 		endpwent();
2321014Sbill 	}
23318606Sedward 	close(uf);
23418606Sedward 	if (person1 == 0) {
23518606Sedward 		printf("No one logged on\n");
23624461Sedward 		return;
23718606Sedward 	}
23818606Sedward 	p->link = 0;
23918606Sedward }
2401014Sbill 
24118606Sedward donames(argv)
24218606Sedward 	char **argv;
24318606Sedward {
24418606Sedward 	register struct person *p;
24518606Sedward 	register struct passwd *pw;
24618606Sedward 	int uf;
2471014Sbill 
24818606Sedward 	/*
24918606Sedward 	 * get names from command line and check to see if they're
25018606Sedward 	 * logged in
25118606Sedward 	 */
25218606Sedward 	unshort = !small;
25318606Sedward 	for (; *argv != 0; argv++) {
25418606Sedward 		if (netfinger(*argv))
25518606Sedward 			continue;
25618606Sedward 		if (person1 == 0)
25718606Sedward 			p = person1 = (struct person *) malloc(sizeof *p);
25818606Sedward 		else {
25918606Sedward 			p->link = (struct person *) malloc(sizeof *p);
26018606Sedward 			p = p->link;
26116469Ssam 		}
26218606Sedward 		p->name = *argv;
2631014Sbill 		p->loggedin = 0;
26418606Sedward 		p->original = 1;
26518606Sedward 		p->pwd = 0;
26618606Sedward 	}
26724461Sedward 	if (person1 == 0)
26824461Sedward 		return;
26918606Sedward 	p->link = 0;
27018606Sedward 	/*
27118606Sedward 	 * if we are doing it, read /etc/passwd for the useful info
27218606Sedward 	 */
27318606Sedward 	if (unquick) {
27418606Sedward 		setpwent();
27518606Sedward 		if (!match) {
27618606Sedward 			extern _pw_stayopen;
2771014Sbill 
27818606Sedward 			_pw_stayopen = 1;
27918606Sedward 			for (p = person1; p != 0; p = p->link)
28018606Sedward 				if (pw = getpwnam(p->name))
28118606Sedward 					p->pwd = pwdcopy(pw);
28218606Sedward 		} else while ((pw = getpwent()) != 0) {
28318606Sedward 			for (p = person1; p != 0; p = p->link) {
28418606Sedward 				if (!p->original)
28518606Sedward 					continue;
28618606Sedward 				if (strcmp(p->name, pw->pw_name) != 0 &&
28718606Sedward 				    !matchcmp(pw->pw_gecos, pw->pw_name, p->name))
28818606Sedward 					continue;
28918606Sedward 				if (p->pwd == 0)
29018606Sedward 					p->pwd = pwdcopy(pw);
29118606Sedward 				else {
29218606Sedward 					struct person *new;
29318606Sedward 					/*
29418606Sedward 					 * handle multiple login names, insert
29518606Sedward 					 * new "duplicate" entry behind
29618606Sedward 					 */
29718606Sedward 					new = (struct person *)
29818606Sedward 						malloc(sizeof *new);
29918606Sedward 					new->pwd = pwdcopy(pw);
30018606Sedward 					new->name = p->name;
30118606Sedward 					new->original = 1;
30218606Sedward 					new->loggedin = 0;
30318606Sedward 					new->link = p->link;
30418606Sedward 					p->original = 0;
30518606Sedward 					p->link = new;
30618606Sedward 					p = new;
30718606Sedward 				}
3081014Sbill 			}
3091014Sbill 		}
3101014Sbill 		endpwent();
31118606Sedward 	}
31218606Sedward 	/* Now get login information */
31318606Sedward 	if ((uf = open(USERLOG, 0)) < 0) {
31418606Sedward 		fprintf(stderr, "finger: error opening %s\n", USERLOG);
31518606Sedward 		exit(2);
31618606Sedward 	}
31718606Sedward 	while (read(uf, (char *)&user, sizeof user) == sizeof user) {
31818606Sedward 		if (*user.ut_name == 0)
31918606Sedward 			continue;
32018606Sedward 		for (p = person1; p != 0; p = p->link) {
32118606Sedward 			if (p->loggedin == 2)
32218606Sedward 				continue;
32318606Sedward 			if (strncmp(p->pwd ? p->pwd->pw_name : p->name,
32418606Sedward 				    user.ut_name, NMAX) != 0)
32518606Sedward 				continue;
32618606Sedward 			if (p->loggedin == 0) {
32718606Sedward 				bcopy(user.ut_line, p->tty, LMAX);
32818606Sedward 				p->tty[LMAX] = 0;
32918606Sedward 				bcopy(user.ut_host, p->host, HMAX);
33018606Sedward 				p->host[HMAX] = 0;
33118606Sedward 				p->loginat = user.ut_time;
33218606Sedward 				p->loggedin = 1;
33318606Sedward 			} else {	/* p->loggedin == 1 */
33418606Sedward 				struct person *new;
33518606Sedward 				new = (struct person *) malloc(sizeof *new);
33618606Sedward 				new->name = p->name;
33718606Sedward 				bcopy(user.ut_line, new->tty, LMAX);
33818606Sedward 				new->tty[LMAX] = 0;
33918606Sedward 				bcopy(user.ut_host, new->host, HMAX);
34018606Sedward 				new->host[HMAX] = 0;
34118606Sedward 				new->loginat = user.ut_time;
34218606Sedward 				new->pwd = p->pwd;
34318606Sedward 				new->loggedin = 1;
34418606Sedward 				new->original = 0;
34518606Sedward 				new->link = p->link;
34618606Sedward 				p->loggedin = 2;
34718606Sedward 				p->link = new;
34818606Sedward 				p = new;
3491014Sbill 			}
3501014Sbill 		}
35118606Sedward 	}
35218606Sedward 	close(uf);
35318606Sedward 	if (unquick) {
3541014Sbill 		fwopen();
35518606Sedward 		for (p = person1; p != 0; p = p->link)
35618606Sedward 			decode(p);
3571014Sbill 		fwclose();
3581014Sbill 	}
35918606Sedward }
3601014Sbill 
36118606Sedward print()
36218606Sedward {
36318606Sedward 	register FILE *fp;
36418606Sedward 	register struct person *p;
36518606Sedward 	register char *s;
36618606Sedward 	register c;
3671014Sbill 
36818606Sedward 	/*
36918606Sedward 	 * print out what we got
37018606Sedward 	 */
37118606Sedward 	if (header) {
37218606Sedward 		if (unquick) {
37318606Sedward 			if (!unshort)
37418606Sedward 				if (wide)
37518606Sedward 					printf("Login       Name              TTY Idle    When            Office\n");
37618606Sedward 				else
37718606Sedward 					printf("Login    TTY Idle    When            Office\n");
37818606Sedward 		} else {
37918606Sedward 			printf("Login      TTY            When");
38018606Sedward 			if (idle)
38118606Sedward 				printf("             Idle");
38218606Sedward 			putchar('\n');
3831014Sbill 		}
38418606Sedward 	}
38518606Sedward 	for (p = person1; p != 0; p = p->link) {
38618606Sedward 		if (!unquick) {
38718606Sedward 			quickprint(p);
38818606Sedward 			continue;
3891014Sbill 		}
39018606Sedward 		if (!unshort) {
39118606Sedward 			shortprint(p);
39218606Sedward 			continue;
39318606Sedward 		}
39418606Sedward 		personprint(p);
39518606Sedward 		if (p->pwd != 0) {
39618606Sedward 			if (hack) {
39718606Sedward 				s = malloc(strlen(p->pwd->pw_dir) +
39818606Sedward 					sizeof PROJ);
39918606Sedward 				strcpy(s, p->pwd->pw_dir);
40018606Sedward 				strcat(s, PROJ);
40118606Sedward 				if ((fp = fopen(s, "r")) != 0) {
40218606Sedward 					printf("Project: ");
40318606Sedward 					while ((c = getc(fp)) != EOF) {
40418606Sedward 						if (c == '\n')
40518606Sedward 							break;
40618606Sedward 						putchar(c);
40718606Sedward 					}
40818606Sedward 					fclose(fp);
40918606Sedward 					putchar('\n');
4101014Sbill 				}
41118606Sedward 				free(s);
4121014Sbill 			}
41318606Sedward 			if (plan) {
41418606Sedward 				s = malloc(strlen(p->pwd->pw_dir) +
41518606Sedward 					sizeof PLAN);
41618606Sedward 				strcpy(s, p->pwd->pw_dir);
41718606Sedward 				strcat(s, PLAN);
41818606Sedward 				if ((fp = fopen(s, "r")) == 0)
41918606Sedward 					printf("No Plan.\n");
42018606Sedward 				else {
42118606Sedward 					printf("Plan:\n");
42218606Sedward 					while ((c = getc(fp)) != EOF)
42318606Sedward 						putchar(c);
42418606Sedward 					fclose(fp);
4251014Sbill 				}
42618606Sedward 				free(s);
4271014Sbill 			}
4281014Sbill 		}
42918606Sedward 		if (p->link != 0)
43018606Sedward 			putchar('\n');
43118606Sedward 	}
4321014Sbill }
4331014Sbill 
43418606Sedward /*
43518606Sedward  * Duplicate a pwd entry.
43618606Sedward  * Note: Only the useful things (what the program currently uses) are copied.
4371014Sbill  */
43818606Sedward struct passwd *
43918606Sedward pwdcopy(pfrom)
44018606Sedward 	register struct passwd *pfrom;
44118606Sedward {
44218606Sedward 	register struct passwd *pto;
4431014Sbill 
44418606Sedward 	pto = (struct passwd *) malloc(sizeof *pto);
44518606Sedward #define savestr(s) strcpy(malloc(strlen(s) + 1), s)
44618606Sedward 	pto->pw_name = savestr(pfrom->pw_name);
4471014Sbill 	pto->pw_uid = pfrom->pw_uid;
44818606Sedward 	pto->pw_gecos = savestr(pfrom->pw_gecos);
44918606Sedward 	pto->pw_dir = savestr(pfrom->pw_dir);
45018606Sedward 	pto->pw_shell = savestr(pfrom->pw_shell);
45118606Sedward #undef savestr
45218606Sedward 	return pto;
4531014Sbill }
4541014Sbill 
45518606Sedward /*
45618606Sedward  * print out information on quick format giving just name, tty, login time
45718606Sedward  * and idle time if idle is set.
4581014Sbill  */
45918606Sedward quickprint(pers)
46018606Sedward 	register struct person *pers;
4611014Sbill {
46218606Sedward 	printf("%-*.*s  ", NMAX, NMAX, pers->name);
46318606Sedward 	if (pers->loggedin) {
46418606Sedward 		if (idle) {
46518606Sedward 			findidle(pers);
46618606Sedward 			printf("%c%-*s %-16.16s", pers->writable ? ' ' : '*',
46718606Sedward 				LMAX, pers->tty, ctime(&pers->loginat));
46818606Sedward 			ltimeprint("   ", &pers->idletime, "");
46918606Sedward 		} else
47018606Sedward 			printf(" %-*s %-16.16s", LMAX,
47118606Sedward 				pers->tty, ctime(&pers->loginat));
47218606Sedward 		putchar('\n');
47318606Sedward 	} else
47418606Sedward 		printf("          Not Logged In\n");
4751014Sbill }
4761014Sbill 
47718606Sedward /*
47818606Sedward  * print out information in short format, giving login name, full name,
47918606Sedward  * tty, idle time, login time, office location and phone.
4801014Sbill  */
48118606Sedward shortprint(pers)
48218606Sedward 	register struct person *pers;
4831014Sbill {
48418606Sedward 	char *p;
48518606Sedward 	char dialup;
4861014Sbill 
48718606Sedward 	if (pers->pwd == 0) {
48818606Sedward 		printf("%-15s       ???\n", pers->name);
48918606Sedward 		return;
4901014Sbill 	}
49118606Sedward 	printf("%-*s", NMAX, pers->pwd->pw_name);
4921014Sbill 	dialup = 0;
49318606Sedward 	if (wide) {
49418606Sedward 		if (pers->realname)
49518606Sedward 			printf(" %-20.20s", pers->realname);
49618606Sedward 		else
49718606Sedward 			printf("        ???          ");
4981014Sbill 	}
49918606Sedward 	putchar(' ');
50018606Sedward 	if (pers->loggedin && !pers->writable)
50118606Sedward 		putchar('*');
50218606Sedward 	else
50318606Sedward 		putchar(' ');
50418606Sedward 	if (*pers->tty) {
50518606Sedward 		if (pers->tty[0] == 't' && pers->tty[1] == 't' &&
50618606Sedward 		    pers->tty[2] == 'y') {
50718606Sedward 			if (pers->tty[3] == 'd' && pers->loggedin)
50818606Sedward 				dialup = 1;
50918606Sedward 			printf("%-2.2s ", pers->tty + 3);
51018606Sedward 		} else
51118606Sedward 			printf("%-2.2s ", pers->tty);
51218606Sedward 	} else
51318606Sedward 		printf("   ");
51418606Sedward 	p = ctime(&pers->loginat);
51518606Sedward 	if (pers->loggedin) {
51618606Sedward 		stimeprint(&pers->idletime);
51718606Sedward 		printf(" %3.3s %-5.5s ", p, p + 11);
51818606Sedward 	} else if (pers->loginat == 0)
51918606Sedward 		printf(" < .  .  .  . >");
5208842Smckusick 	else if (tloc - pers->loginat >= 180 * 24 * 60 * 60)
52118606Sedward 		printf(" <%-6.6s, %-4.4s>", p + 4, p + 20);
5228842Smckusick 	else
52318606Sedward 		printf(" <%-12.12s>", p + 4);
52418606Sedward 	if (dialup && pers->homephone)
52518606Sedward 		printf(" %20s", pers->homephone);
52618606Sedward 	else {
52718606Sedward 		if (pers->office)
52818606Sedward 			printf(" %-11.11s", pers->office);
52918606Sedward 		else if (pers->officephone || pers->homephone)
53018606Sedward 			printf("            ");
53118606Sedward 		if (pers->officephone)
53218606Sedward 			printf(" %s", pers->officephone);
53318606Sedward 		else if (pers->homephone)
53418606Sedward 			printf(" %s", pers->homephone);
5351014Sbill 	}
53618606Sedward 	putchar('\n');
5371014Sbill }
5381014Sbill 
53918606Sedward /*
54018606Sedward  * print out a person in long format giving all possible information.
54118606Sedward  * directory and shell are inhibited if unbrief is clear.
5421014Sbill  */
54318606Sedward personprint(pers)
54418606Sedward 	register struct person *pers;
5451014Sbill {
54618606Sedward 	if (pers->pwd == 0) {
54718606Sedward 		printf("Login name: %-10s\t\t\tIn real life: ???\n",
54818606Sedward 			pers->name);
54918606Sedward 		return;
5501014Sbill 	}
55118606Sedward 	printf("Login name: %-10s", pers->pwd->pw_name);
55218606Sedward 	if (pers->loggedin && !pers->writable)
55318606Sedward 		printf("	(messages off)	");
55418606Sedward 	else
55518606Sedward 		printf("			");
55618606Sedward 	if (pers->realname)
55718606Sedward 		printf("In real life: %s", pers->realname);
55818606Sedward 	if (pers->office) {
55918606Sedward 		printf("\nOffice: %-.11s", pers->office);
56018606Sedward 		if (pers->officephone) {
56118606Sedward 			printf(", %s", pers->officephone);
56218606Sedward 			if (pers->homephone)
56318606Sedward 				printf("\t\tHome phone: %s", pers->homephone);
56418606Sedward 			else if (pers->random)
56518606Sedward 				printf("\t\t%s", pers->random);
56618606Sedward 		} else
56718606Sedward 			if (pers->homephone)
56818606Sedward 				printf("\t\t\tHome phone: %s", pers->homephone);
56918606Sedward 			else if (pers->random)
57018606Sedward 				printf("\t\t\t%s", pers->random);
57118606Sedward 	} else if (pers->officephone) {
57218606Sedward 		printf("\nPhone: %s", pers->officephone);
57318606Sedward 		if (pers->homephone)
57418606Sedward 			printf(", %s", pers->homephone);
57518606Sedward 		if (pers->random)
57618606Sedward 			printf(", %s", pers->random);
57718606Sedward 	} else if (pers->homephone) {
57818606Sedward 		printf("\nPhone: %s", pers->homephone);
57918606Sedward 		if (pers->random)
58018606Sedward 			printf(", %s", pers->random);
58118606Sedward 	} else if (pers->random)
58218606Sedward 		printf("\n%s", pers->random);
58318606Sedward 	if (unbrief) {
58418606Sedward 		printf("\nDirectory: %-25s", pers->pwd->pw_dir);
58518606Sedward 		if (*pers->pwd->pw_shell)
58618606Sedward 			printf("\tShell: %-s", pers->pwd->pw_shell);
5871014Sbill 	}
58818606Sedward 	if (pers->loggedin) {
58918606Sedward 		register char *ep = ctime(&pers->loginat);
59018606Sedward 		if (*pers->host) {
59118606Sedward 			printf("\nOn since %15.15s on %s from %s",
59218606Sedward 				&ep[4], pers->tty, pers->host);
59318606Sedward 			ltimeprint("\n", &pers->idletime, " Idle Time");
59418606Sedward 		} else {
59518606Sedward 			printf("\nOn since %15.15s on %-*s",
59618606Sedward 				&ep[4], LMAX, pers->tty);
59718606Sedward 			ltimeprint("\t", &pers->idletime, " Idle Time");
5981014Sbill 		}
59918606Sedward 	} else if (pers->loginat == 0)
60018606Sedward 		printf("\nNever logged in.");
6018842Smckusick 	else if (tloc - pers->loginat > 180 * 24 * 60 * 60) {
60218606Sedward 		register char *ep = ctime(&pers->loginat);
60318606Sedward 		printf("\nLast login %10.10s, %4.4s on %s",
60418606Sedward 			ep, ep+20, pers->tty);
60518606Sedward 		if (*pers->host)
60618606Sedward 			printf(" from %s", pers->host);
60718606Sedward 	} else {
60818606Sedward 		register char *ep = ctime(&pers->loginat);
60918606Sedward 		printf("\nLast login %16.16s on %s", ep, pers->tty);
61018606Sedward 		if (*pers->host)
61118606Sedward 			printf(" from %s", pers->host);
6128842Smckusick 	}
61318606Sedward 	putchar('\n');
6141014Sbill }
6151014Sbill 
6161014Sbill /*
6171014Sbill  *  very hacky section of code to format phone numbers.  filled with
6181014Sbill  *  magic constants like 4, 7 and 10.
6191014Sbill  */
62018606Sedward char *
62118606Sedward phone(s, len, alldigits)
62218606Sedward 	register char *s;
62318606Sedward 	int len;
62418606Sedward 	char alldigits;
6251014Sbill {
62618606Sedward 	char fonebuf[15];
62718606Sedward 	register char *p = fonebuf;
62818606Sedward 	register i;
6291014Sbill 
63018606Sedward 	if (!alldigits)
63118606Sedward 		return (strcpy(malloc(len + 1), s));
63218606Sedward 	switch (len) {
63318606Sedward 	case 4:
63418606Sedward 		*p++ = ' ';
63518606Sedward 		*p++ = 'x';
63618606Sedward 		*p++ = '2';
63718606Sedward 		*p++ = '-';
63818606Sedward 		for (i = 0; i < 4; i++)
63918606Sedward 			*p++ = *s++;
6401014Sbill 		break;
64124463Sbloom 	case 5:
64224463Sbloom 		*p++ = ' ';
64324463Sbloom 		*p++ = 'x';
64424463Sbloom 		*p++ = *s++;
64524463Sbloom 		*p++ = '-';
64624463Sbloom 		for (i = 0; i < 4; i++)
64724463Sbloom 			*p++ = *s++;
64824463Sbloom 		break;
64918606Sedward 	case 7:
65018606Sedward 		for (i = 0; i < 3; i++)
65118606Sedward 			*p++ = *s++;
65218606Sedward 		*p++ = '-';
65318606Sedward 		for (i = 0; i < 4; i++)
65418606Sedward 			*p++ = *s++;
6551014Sbill 		break;
65618606Sedward 	case 10:
65718606Sedward 		for (i = 0; i < 3; i++)
65818606Sedward 			*p++ = *s++;
65918606Sedward 		*p++ = '-';
66018606Sedward 		for (i = 0; i < 3; i++)
66118606Sedward 			*p++ = *s++;
66218606Sedward 		*p++ = '-';
66318606Sedward 		for (i = 0; i < 4; i++)
66418606Sedward 			*p++ = *s++;
6651014Sbill 		break;
66618606Sedward 	case 0:
66718606Sedward 		return 0;
66818606Sedward 	default:
66918606Sedward 		return (strcpy(malloc(len + 1), s));
6701014Sbill 	}
67118606Sedward 	*p++ = 0;
67218606Sedward 	return (strcpy(malloc(p - fonebuf), fonebuf));
6731014Sbill }
6741014Sbill 
67518606Sedward /*
67618606Sedward  * decode the information in the gecos field of /etc/passwd
6771014Sbill  */
67818606Sedward decode(pers)
67918606Sedward 	register struct person *pers;
6801014Sbill {
68118606Sedward 	char buffer[256];
68218606Sedward 	register char *bp, *gp, *lp;
68318606Sedward 	int alldigits;
68418606Sedward 	int hasspace;
68518606Sedward 	int len;
6861014Sbill 
68718606Sedward 	pers->realname = 0;
68818606Sedward 	pers->office = 0;
68918606Sedward 	pers->officephone = 0;
69018606Sedward 	pers->homephone = 0;
69118606Sedward 	pers->random = 0;
69218606Sedward 	if (pers->pwd == 0)
69318606Sedward 		return;
69418606Sedward 	gp = pers->pwd->pw_gecos;
69518606Sedward 	bp = buffer;
69618606Sedward 	if (*gp == ASTERISK)
6971014Sbill 		gp++;
69818606Sedward 	while (*gp && *gp != COMMA)			/* name */
69918606Sedward 		if (*gp == SAMENAME) {
70018606Sedward 			lp = pers->pwd->pw_name;
70118606Sedward 			if (islower(*lp))
70218606Sedward 				*bp++ = toupper(*lp++);
70318606Sedward 			while (*bp++ = *lp++)
70418606Sedward 				;
70518606Sedward 			bp--;
70618606Sedward 			gp++;
70718606Sedward 		} else
70818606Sedward 			*bp++ = *gp++;
70918606Sedward 	*bp++ = 0;
71018606Sedward 	if ((len = bp - buffer) > 1)
71118606Sedward 		pers->realname = strcpy(malloc(len), buffer);
71218606Sedward 	if (*gp == COMMA) {				/* office */
71318606Sedward 		gp++;
71418606Sedward 		hasspace = 0;
71518606Sedward 		bp = buffer;
71618606Sedward 		while (*gp && *gp != COMMA) {
71718606Sedward 			*bp = *gp++;
71818606Sedward 			if (*bp == ' ')
71918606Sedward 				hasspace = 1;
72018606Sedward 			/* leave 5 for Cory and Evans expansion */
72118606Sedward 			if (bp < buffer + sizeof buffer - 6)
72218606Sedward 				bp++;
7231014Sbill 		}
72418606Sedward 		*bp = 0;
72518606Sedward 		len = bp - buffer;
72618606Sedward 		bp--;			/* point to last character */
72718606Sedward 		if (hasspace || len == 0)
72818606Sedward 			len++;
72918606Sedward 		else if (*bp == CORY) {
73018606Sedward 			strcpy(bp, " Cory");
73118606Sedward 			len += 5;
73218606Sedward 		} else if (*bp == EVANS) {
73318606Sedward 			strcpy(bp, " Evans");
73418606Sedward 			len += 6;
73518606Sedward 		} else
73618606Sedward 			len++;
73718606Sedward 		if (len > 1)
73818606Sedward 			pers->office = strcpy(malloc(len), buffer);
73918606Sedward 	}
74018606Sedward 	if (*gp == COMMA) {				/* office phone */
74118606Sedward 		gp++;
74218606Sedward 		bp = buffer;
74318606Sedward 		alldigits = 1;
74418606Sedward 		while (*gp && *gp != COMMA) {
74518606Sedward 			*bp = *gp++;
74618606Sedward 			if (!isdigit(*bp))
74718606Sedward 				alldigits = 0;
74818606Sedward 			if (bp < buffer + sizeof buffer - 1)
74918606Sedward 				bp++;
7501014Sbill 		}
75118606Sedward 		*bp = 0;
75218606Sedward 		pers->officephone = phone(buffer, bp - buffer, alldigits);
75318606Sedward 	}
75418606Sedward 	if (*gp == COMMA) {				/* home phone */
7551014Sbill 		gp++;
75618606Sedward 		bp = buffer;
7571014Sbill 		alldigits = 1;
75818606Sedward 		while (*gp && *gp != COMMA) {
7591014Sbill 			*bp = *gp++;
76018606Sedward 			if (!isdigit(*bp))
76118606Sedward 				alldigits = 0;
76218606Sedward 			if (bp < buffer + sizeof buffer - 1)
7631014Sbill 				bp++;
7641014Sbill 		}
76518606Sedward 		*bp = 0;
76618606Sedward 		pers->homephone = phone(buffer, bp - buffer, alldigits);
7671014Sbill 	}
76818606Sedward 	if (pers->loggedin)
76918606Sedward 		findidle(pers);
77018606Sedward 	else
77118606Sedward 		findwhen(pers);
7721014Sbill }
7731014Sbill 
77418606Sedward /*
77518606Sedward  * find the last log in of a user by checking the LASTLOG file.
77618606Sedward  * the entry is indexed by the uid, so this can only be done if
77718606Sedward  * the uid is known (which it isn't in quick mode)
7781014Sbill  */
7791014Sbill 
7801014Sbill fwopen()
7811014Sbill {
78218606Sedward 	if ((lf = open(LASTLOG, 0)) < 0)
78318606Sedward 		fprintf(stderr, "finger: %s open error\n", LASTLOG);
7841014Sbill }
7851014Sbill 
78618606Sedward findwhen(pers)
78718606Sedward 	register struct person *pers;
7881014Sbill {
78918606Sedward 	struct lastlog ll;
79018606Sedward 	int i;
7911014Sbill 
79218606Sedward 	if (lf >= 0) {
79318606Sedward 		lseek(lf, (long)pers->pwd->pw_uid * sizeof ll, 0);
79418606Sedward 		if ((i = read(lf, (char *)&ll, sizeof ll)) == sizeof ll) {
79518606Sedward 			bcopy(ll.ll_line, pers->tty, LMAX);
79618606Sedward 			pers->tty[LMAX] = 0;
79718606Sedward 			bcopy(ll.ll_host, pers->host, HMAX);
79818606Sedward 			pers->host[HMAX] = 0;
79918606Sedward 			pers->loginat = ll.ll_time;
80018606Sedward 		} else {
80118606Sedward 			if (i != 0)
80218606Sedward 				fprintf(stderr, "finger: %s read error\n",
80318606Sedward 					LASTLOG);
80418606Sedward 			pers->tty[0] = 0;
80518606Sedward 			pers->host[0] = 0;
80618606Sedward 			pers->loginat = 0L;
80718606Sedward 		}
80818606Sedward 	} else {
80918606Sedward 		pers->tty[0] = 0;
81018606Sedward 		pers->host[0] = 0;
8111014Sbill 		pers->loginat = 0L;
8121014Sbill 	}
8131014Sbill }
8141014Sbill 
8151014Sbill fwclose()
8161014Sbill {
81718606Sedward 	if (lf >= 0)
81818606Sedward 		close(lf);
8191014Sbill }
8201014Sbill 
82118606Sedward /*
82218606Sedward  * find the idle time of a user by doing a stat on /dev/tty??,
82318606Sedward  * where tty?? has been gotten from USERLOG, supposedly.
8241014Sbill  */
82518606Sedward findidle(pers)
82618606Sedward 	register struct person *pers;
8271014Sbill {
82818606Sedward 	struct stat ttystatus;
82918606Sedward 	static char buffer[20] = "/dev/";
83018606Sedward 	long t;
83118606Sedward #define TTYLEN 5
8321014Sbill 
83318606Sedward 	strcpy(buffer + TTYLEN, pers->tty);
83418606Sedward 	buffer[TTYLEN+LMAX] = 0;
83518606Sedward 	if (stat(buffer, &ttystatus) < 0) {
83618606Sedward 		fprintf(stderr, "finger: Can't stat %s\n", buffer);
83718606Sedward 		exit(4);
83818606Sedward 	}
83918606Sedward 	time(&t);
84018606Sedward 	if (t < ttystatus.st_atime)
8411014Sbill 		pers->idletime = 0L;
84218606Sedward 	else
84318606Sedward 		pers->idletime = t - ttystatus.st_atime;
84418606Sedward 	pers->writable = (ttystatus.st_mode & TALKABLE) == TALKABLE;
8451014Sbill }
8461014Sbill 
84718606Sedward /*
84818606Sedward  * print idle time in short format; this program always prints 4 characters;
84918606Sedward  * if the idle time is zero, it prints 4 blanks.
8501014Sbill  */
85118606Sedward stimeprint(dt)
85218606Sedward 	long *dt;
8531014Sbill {
85418606Sedward 	register struct tm *delta;
8551014Sbill 
85618606Sedward 	delta = gmtime(dt);
85718606Sedward 	if (delta->tm_yday == 0)
85818606Sedward 		if (delta->tm_hour == 0)
85918606Sedward 			if (delta->tm_min == 0)
86018606Sedward 				printf("    ");
86118606Sedward 			else
86218606Sedward 				printf("  %2d", delta->tm_min);
86318606Sedward 		else
86418606Sedward 			if (delta->tm_hour >= 10)
86518606Sedward 				printf("%3d:", delta->tm_hour);
86618606Sedward 			else
86718606Sedward 				printf("%1d:%02d",
86818606Sedward 					delta->tm_hour, delta->tm_min);
86918606Sedward 	else
87018606Sedward 		printf("%3dd", delta->tm_yday);
8711014Sbill }
8721014Sbill 
87318606Sedward /*
87418606Sedward  * print idle time in long format with care being taken not to pluralize
87518606Sedward  * 1 minutes or 1 hours or 1 days.
87618606Sedward  * print "prefix" first.
8771014Sbill  */
87818606Sedward ltimeprint(before, dt, after)
87918606Sedward 	long *dt;
88018606Sedward 	char *before, *after;
8811014Sbill {
88218606Sedward 	register struct tm *delta;
8831014Sbill 
88418606Sedward 	delta = gmtime(dt);
88518606Sedward 	if (delta->tm_yday == 0 && delta->tm_hour == 0 && delta->tm_min == 0 &&
88618606Sedward 	    delta->tm_sec <= 10)
88718606Sedward 		return (0);
88818606Sedward 	printf("%s", before);
88918606Sedward 	if (delta->tm_yday >= 10)
89018606Sedward 		printf("%d days", delta->tm_yday);
89118606Sedward 	else if (delta->tm_yday > 0)
89218606Sedward 		printf("%d day%s %d hour%s",
89318606Sedward 			delta->tm_yday, delta->tm_yday == 1 ? "" : "s",
89418606Sedward 			delta->tm_hour, delta->tm_hour == 1 ? "" : "s");
89518606Sedward 	else
89618606Sedward 		if (delta->tm_hour >= 10)
89718606Sedward 			printf("%d hours", delta->tm_hour);
89818606Sedward 		else if (delta->tm_hour > 0)
89918606Sedward 			printf("%d hour%s %d minute%s",
90018606Sedward 				delta->tm_hour, delta->tm_hour == 1 ? "" : "s",
90118606Sedward 				delta->tm_min, delta->tm_min == 1 ? "" : "s");
90218606Sedward 		else
90318606Sedward 			if (delta->tm_min >= 10)
90418606Sedward 				printf("%2d minutes", delta->tm_min);
90518606Sedward 			else if (delta->tm_min == 0)
90618606Sedward 				printf("%2d seconds", delta->tm_sec);
90718606Sedward 			else
90818606Sedward 				printf("%d minute%s %d second%s",
90918606Sedward 					delta->tm_min,
91018606Sedward 					delta->tm_min == 1 ? "" : "s",
91118606Sedward 					delta->tm_sec,
91218606Sedward 					delta->tm_sec == 1 ? "" : "s");
91318606Sedward 	printf("%s", after);
9141014Sbill }
9151014Sbill 
91618606Sedward matchcmp(gname, login, given)
91718606Sedward 	register char *gname;
91818606Sedward 	char *login;
91918606Sedward 	char *given;
9201014Sbill {
92118606Sedward 	char buffer[100];
92218606Sedward 	register char *bp, *lp;
92318606Sedward 	register c;
9241014Sbill 
92518606Sedward 	if (*gname == ASTERISK)
92618606Sedward 		gname++;
92718606Sedward 	lp = 0;
92818606Sedward 	bp = buffer;
92918606Sedward 	for (;;)
93018606Sedward 		switch (c = *gname++) {
93118606Sedward 		case SAMENAME:
93218606Sedward 			for (lp = login; bp < buffer + sizeof buffer
93318606Sedward 					 && (*bp++ = *lp++);)
93418606Sedward 				;
93518606Sedward 			bp--;
93618606Sedward 			break;
93718606Sedward 		case ' ':
93818606Sedward 		case COMMA:
93918606Sedward 		case '\0':
94018606Sedward 			*bp = 0;
94118606Sedward 			if (namecmp(buffer, given))
94218606Sedward 				return (1);
94318606Sedward 			if (c == COMMA || c == 0)
94418606Sedward 				return (0);
94518606Sedward 			bp = buffer;
94618606Sedward 			break;
94718606Sedward 		default:
94818606Sedward 			if (bp < buffer + sizeof buffer)
94918606Sedward 				*bp++ = c;
9501014Sbill 		}
95118606Sedward 	/*NOTREACHED*/
9521014Sbill }
9531014Sbill 
95418606Sedward namecmp(name1, name2)
95518606Sedward 	register char *name1, *name2;
9561014Sbill {
95718606Sedward 	register c1, c2;
9581014Sbill 
95918606Sedward 	for (;;) {
96018606Sedward 		c1 = *name1++;
96118606Sedward 		if (islower(c1))
96218606Sedward 			c1 = toupper(c1);
96318606Sedward 		c2 = *name2++;
96418606Sedward 		if (islower(c2))
96518606Sedward 			c2 = toupper(c2);
96618606Sedward 		if (c1 != c2)
96718606Sedward 			break;
96818606Sedward 		if (c1 == 0)
96918606Sedward 			return (1);
9701014Sbill 	}
97118606Sedward 	if (!c1) {
97218606Sedward 		for (name2--; isdigit(*name2); name2++)
97318606Sedward 			;
97418606Sedward 		if (*name2 == 0)
97518606Sedward 			return (1);
97618606Sedward 	} else if (!c2) {
97718606Sedward 		for (name1--; isdigit(*name1); name1++)
97818606Sedward 			;
97918606Sedward 		if (*name2 == 0)
98018606Sedward 			return (1);
9811014Sbill 	}
98218606Sedward 	return (0);
9831014Sbill }
9841014Sbill 
98516469Ssam netfinger(name)
98618606Sedward 	char *name;
98716469Ssam {
98816469Ssam 	char *host;
98916469Ssam 	char fname[100];
99016469Ssam 	struct hostent *hp;
99116469Ssam 	struct servent *sp;
99218606Sedward 	struct sockaddr_in sin;
99316469Ssam 	int s;
99416469Ssam 	char *rindex();
99516469Ssam 	register FILE *f;
99616469Ssam 	register int c;
99716469Ssam 	register int lastc;
99816469Ssam 
99916469Ssam 	if (name == NULL)
100018606Sedward 		return (0);
100116469Ssam 	host = rindex(name, '@');
100216469Ssam 	if (host == NULL)
100318606Sedward 		return (0);
100416469Ssam 	*host++ = 0;
100516469Ssam 	hp = gethostbyname(host);
100616469Ssam 	if (hp == NULL) {
100716469Ssam 		static struct hostent def;
100816469Ssam 		static struct in_addr defaddr;
100916469Ssam 		static char namebuf[128];
101016469Ssam 		int inet_addr();
101116469Ssam 
101216469Ssam 		defaddr.s_addr = inet_addr(host);
101316469Ssam 		if (defaddr.s_addr == -1) {
101416469Ssam 			printf("unknown host: %s\n", host);
101518606Sedward 			return (1);
101616469Ssam 		}
101716469Ssam 		strcpy(namebuf, host);
101816469Ssam 		def.h_name = namebuf;
101916469Ssam 		def.h_addr = (char *)&defaddr;
102016469Ssam 		def.h_length = sizeof (struct in_addr);
102116469Ssam 		def.h_addrtype = AF_INET;
102216469Ssam 		def.h_aliases = 0;
102316469Ssam 		hp = &def;
102416469Ssam 	}
102516469Ssam 	printf("[%s]", hp->h_name);
102616469Ssam 	sp = getservbyname("finger", "tcp");
102716469Ssam 	if (sp == 0) {
102816469Ssam 		printf("tcp/finger: unknown service\n");
102918606Sedward 		return (1);
103016469Ssam 	}
103116469Ssam 	sin.sin_family = hp->h_addrtype;
103216469Ssam 	bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
103316469Ssam 	sin.sin_port = sp->s_port;
103416469Ssam 	s = socket(hp->h_addrtype, SOCK_STREAM, 0);
103516469Ssam 	if (s < 0) {
103616469Ssam 		fflush(stdout);
103716469Ssam 		perror("socket");
103818606Sedward 		return (1);
103916469Ssam 	}
104016469Ssam 	if (connect(s, (char *)&sin, sizeof (sin)) < 0) {
104116469Ssam 		fflush(stdout);
104216469Ssam 		perror("connect");
104316469Ssam 		close(s);
104418606Sedward 		return (1);
104516469Ssam 	}
104616469Ssam 	printf("\n");
104716469Ssam 	if (large) write(s, "/W ", 3);
104816469Ssam 	write(s, name, strlen(name));
104916469Ssam 	write(s, "\r\n", 2);
105016469Ssam 	f = fdopen(s, "r");
105116469Ssam 	while ((c = getc(f)) != EOF) {
105216469Ssam 		switch(c) {
105316469Ssam 		case 0210:
105416469Ssam 		case 0211:
105516469Ssam 		case 0212:
105616469Ssam 		case 0214:
105716469Ssam 			c -= 0200;
105816469Ssam 			break;
105916469Ssam 		case 0215:
106016469Ssam 			c = '\n';
106116469Ssam 			break;
106216469Ssam 		}
106316469Ssam 		putchar(lastc = c);
106416469Ssam 	}
106516469Ssam 	if (lastc != '\n')
106616469Ssam 		putchar('\n');
1067*25121Sbloom 	(void)fclose(f);
106818606Sedward 	return (1);
106916469Ssam }
1070