xref: /csrg-svn/usr.bin/finger/finger.c (revision 30958)
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*30958Sbostic static char sccsid[] = "@(#)finger.c	5.10 (Berkeley) 04/26/87";
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 */
7426869Smckusick #define TALKABLE	0220		/* tty is writable if 220 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;
40625589Sbloom 						if (isprint(c) || isspace(c))
40725589Sbloom 							putchar(c);
40825589Sbloom 						else
40925589Sbloom 							putchar(c ^ 100);
41018606Sedward 					}
41118606Sedward 					fclose(fp);
41218606Sedward 					putchar('\n');
4131014Sbill 				}
41418606Sedward 				free(s);
4151014Sbill 			}
41618606Sedward 			if (plan) {
41718606Sedward 				s = malloc(strlen(p->pwd->pw_dir) +
41818606Sedward 					sizeof PLAN);
41918606Sedward 				strcpy(s, p->pwd->pw_dir);
42018606Sedward 				strcat(s, PLAN);
42118606Sedward 				if ((fp = fopen(s, "r")) == 0)
42218606Sedward 					printf("No Plan.\n");
42318606Sedward 				else {
42418606Sedward 					printf("Plan:\n");
42518606Sedward 					while ((c = getc(fp)) != EOF)
42625589Sbloom 						if (isprint(c) || isspace(c))
42725589Sbloom 							putchar(c);
42825589Sbloom 						else
42925589Sbloom 							putchar(c ^ 100);
43018606Sedward 					fclose(fp);
4311014Sbill 				}
43218606Sedward 				free(s);
4331014Sbill 			}
4341014Sbill 		}
43518606Sedward 		if (p->link != 0)
43618606Sedward 			putchar('\n');
43718606Sedward 	}
4381014Sbill }
4391014Sbill 
44018606Sedward /*
44118606Sedward  * Duplicate a pwd entry.
44218606Sedward  * Note: Only the useful things (what the program currently uses) are copied.
4431014Sbill  */
44418606Sedward struct passwd *
44518606Sedward pwdcopy(pfrom)
44618606Sedward 	register struct passwd *pfrom;
44718606Sedward {
44818606Sedward 	register struct passwd *pto;
4491014Sbill 
45018606Sedward 	pto = (struct passwd *) malloc(sizeof *pto);
45118606Sedward #define savestr(s) strcpy(malloc(strlen(s) + 1), s)
45218606Sedward 	pto->pw_name = savestr(pfrom->pw_name);
4531014Sbill 	pto->pw_uid = pfrom->pw_uid;
45418606Sedward 	pto->pw_gecos = savestr(pfrom->pw_gecos);
45518606Sedward 	pto->pw_dir = savestr(pfrom->pw_dir);
45618606Sedward 	pto->pw_shell = savestr(pfrom->pw_shell);
45718606Sedward #undef savestr
45818606Sedward 	return pto;
4591014Sbill }
4601014Sbill 
46118606Sedward /*
46218606Sedward  * print out information on quick format giving just name, tty, login time
46318606Sedward  * and idle time if idle is set.
4641014Sbill  */
46518606Sedward quickprint(pers)
46618606Sedward 	register struct person *pers;
4671014Sbill {
46818606Sedward 	printf("%-*.*s  ", NMAX, NMAX, pers->name);
46918606Sedward 	if (pers->loggedin) {
47018606Sedward 		if (idle) {
47118606Sedward 			findidle(pers);
47218606Sedward 			printf("%c%-*s %-16.16s", pers->writable ? ' ' : '*',
47318606Sedward 				LMAX, pers->tty, ctime(&pers->loginat));
47418606Sedward 			ltimeprint("   ", &pers->idletime, "");
47518606Sedward 		} else
47618606Sedward 			printf(" %-*s %-16.16s", LMAX,
47718606Sedward 				pers->tty, ctime(&pers->loginat));
47818606Sedward 		putchar('\n');
47918606Sedward 	} else
48018606Sedward 		printf("          Not Logged In\n");
4811014Sbill }
4821014Sbill 
48318606Sedward /*
48418606Sedward  * print out information in short format, giving login name, full name,
48518606Sedward  * tty, idle time, login time, office location and phone.
4861014Sbill  */
48718606Sedward shortprint(pers)
48818606Sedward 	register struct person *pers;
4891014Sbill {
49018606Sedward 	char *p;
49118606Sedward 	char dialup;
4921014Sbill 
49318606Sedward 	if (pers->pwd == 0) {
49418606Sedward 		printf("%-15s       ???\n", pers->name);
49518606Sedward 		return;
4961014Sbill 	}
49718606Sedward 	printf("%-*s", NMAX, pers->pwd->pw_name);
4981014Sbill 	dialup = 0;
49918606Sedward 	if (wide) {
50018606Sedward 		if (pers->realname)
50118606Sedward 			printf(" %-20.20s", pers->realname);
50218606Sedward 		else
50318606Sedward 			printf("        ???          ");
5041014Sbill 	}
50518606Sedward 	putchar(' ');
50618606Sedward 	if (pers->loggedin && !pers->writable)
50718606Sedward 		putchar('*');
50818606Sedward 	else
50918606Sedward 		putchar(' ');
51018606Sedward 	if (*pers->tty) {
51118606Sedward 		if (pers->tty[0] == 't' && pers->tty[1] == 't' &&
51218606Sedward 		    pers->tty[2] == 'y') {
51318606Sedward 			if (pers->tty[3] == 'd' && pers->loggedin)
51418606Sedward 				dialup = 1;
51518606Sedward 			printf("%-2.2s ", pers->tty + 3);
51618606Sedward 		} else
51718606Sedward 			printf("%-2.2s ", pers->tty);
51818606Sedward 	} else
51918606Sedward 		printf("   ");
52018606Sedward 	p = ctime(&pers->loginat);
52118606Sedward 	if (pers->loggedin) {
52218606Sedward 		stimeprint(&pers->idletime);
52318606Sedward 		printf(" %3.3s %-5.5s ", p, p + 11);
52418606Sedward 	} else if (pers->loginat == 0)
52518606Sedward 		printf(" < .  .  .  . >");
5268842Smckusick 	else if (tloc - pers->loginat >= 180 * 24 * 60 * 60)
52718606Sedward 		printf(" <%-6.6s, %-4.4s>", p + 4, p + 20);
5288842Smckusick 	else
52918606Sedward 		printf(" <%-12.12s>", p + 4);
53018606Sedward 	if (dialup && pers->homephone)
53118606Sedward 		printf(" %20s", pers->homephone);
53218606Sedward 	else {
53318606Sedward 		if (pers->office)
53418606Sedward 			printf(" %-11.11s", pers->office);
53518606Sedward 		else if (pers->officephone || pers->homephone)
53618606Sedward 			printf("            ");
53718606Sedward 		if (pers->officephone)
53818606Sedward 			printf(" %s", pers->officephone);
53918606Sedward 		else if (pers->homephone)
54018606Sedward 			printf(" %s", pers->homephone);
5411014Sbill 	}
54218606Sedward 	putchar('\n');
5431014Sbill }
5441014Sbill 
54518606Sedward /*
54618606Sedward  * print out a person in long format giving all possible information.
54718606Sedward  * directory and shell are inhibited if unbrief is clear.
5481014Sbill  */
54918606Sedward personprint(pers)
55018606Sedward 	register struct person *pers;
5511014Sbill {
55218606Sedward 	if (pers->pwd == 0) {
55318606Sedward 		printf("Login name: %-10s\t\t\tIn real life: ???\n",
55418606Sedward 			pers->name);
55518606Sedward 		return;
5561014Sbill 	}
55718606Sedward 	printf("Login name: %-10s", pers->pwd->pw_name);
55818606Sedward 	if (pers->loggedin && !pers->writable)
55918606Sedward 		printf("	(messages off)	");
56018606Sedward 	else
56118606Sedward 		printf("			");
56218606Sedward 	if (pers->realname)
56318606Sedward 		printf("In real life: %s", pers->realname);
56418606Sedward 	if (pers->office) {
56518606Sedward 		printf("\nOffice: %-.11s", pers->office);
56618606Sedward 		if (pers->officephone) {
56718606Sedward 			printf(", %s", pers->officephone);
56818606Sedward 			if (pers->homephone)
56918606Sedward 				printf("\t\tHome phone: %s", pers->homephone);
57018606Sedward 			else if (pers->random)
57118606Sedward 				printf("\t\t%s", pers->random);
57218606Sedward 		} else
57318606Sedward 			if (pers->homephone)
57418606Sedward 				printf("\t\t\tHome phone: %s", pers->homephone);
57518606Sedward 			else if (pers->random)
57618606Sedward 				printf("\t\t\t%s", pers->random);
57718606Sedward 	} else if (pers->officephone) {
57818606Sedward 		printf("\nPhone: %s", pers->officephone);
57918606Sedward 		if (pers->homephone)
58018606Sedward 			printf(", %s", pers->homephone);
58118606Sedward 		if (pers->random)
58218606Sedward 			printf(", %s", pers->random);
58318606Sedward 	} else if (pers->homephone) {
58418606Sedward 		printf("\nPhone: %s", pers->homephone);
58518606Sedward 		if (pers->random)
58618606Sedward 			printf(", %s", pers->random);
58718606Sedward 	} else if (pers->random)
58818606Sedward 		printf("\n%s", pers->random);
58918606Sedward 	if (unbrief) {
59018606Sedward 		printf("\nDirectory: %-25s", pers->pwd->pw_dir);
59118606Sedward 		if (*pers->pwd->pw_shell)
59218606Sedward 			printf("\tShell: %-s", pers->pwd->pw_shell);
5931014Sbill 	}
59418606Sedward 	if (pers->loggedin) {
59518606Sedward 		register char *ep = ctime(&pers->loginat);
59618606Sedward 		if (*pers->host) {
59718606Sedward 			printf("\nOn since %15.15s on %s from %s",
59818606Sedward 				&ep[4], pers->tty, pers->host);
59918606Sedward 			ltimeprint("\n", &pers->idletime, " Idle Time");
60018606Sedward 		} else {
60118606Sedward 			printf("\nOn since %15.15s on %-*s",
60218606Sedward 				&ep[4], LMAX, pers->tty);
60318606Sedward 			ltimeprint("\t", &pers->idletime, " Idle Time");
6041014Sbill 		}
60518606Sedward 	} else if (pers->loginat == 0)
60618606Sedward 		printf("\nNever logged in.");
6078842Smckusick 	else if (tloc - pers->loginat > 180 * 24 * 60 * 60) {
60818606Sedward 		register char *ep = ctime(&pers->loginat);
60918606Sedward 		printf("\nLast login %10.10s, %4.4s on %s",
61018606Sedward 			ep, ep+20, pers->tty);
61118606Sedward 		if (*pers->host)
61218606Sedward 			printf(" from %s", pers->host);
61318606Sedward 	} else {
61418606Sedward 		register char *ep = ctime(&pers->loginat);
61518606Sedward 		printf("\nLast login %16.16s on %s", ep, pers->tty);
61618606Sedward 		if (*pers->host)
61718606Sedward 			printf(" from %s", pers->host);
6188842Smckusick 	}
61918606Sedward 	putchar('\n');
6201014Sbill }
6211014Sbill 
6221014Sbill /*
6231014Sbill  *  very hacky section of code to format phone numbers.  filled with
6241014Sbill  *  magic constants like 4, 7 and 10.
6251014Sbill  */
62618606Sedward char *
62718606Sedward phone(s, len, alldigits)
62818606Sedward 	register char *s;
62918606Sedward 	int len;
63018606Sedward 	char alldigits;
6311014Sbill {
63218606Sedward 	char fonebuf[15];
63318606Sedward 	register char *p = fonebuf;
63418606Sedward 	register i;
6351014Sbill 
63618606Sedward 	if (!alldigits)
63718606Sedward 		return (strcpy(malloc(len + 1), s));
63818606Sedward 	switch (len) {
63918606Sedward 	case 4:
64018606Sedward 		*p++ = ' ';
64118606Sedward 		*p++ = 'x';
64218606Sedward 		*p++ = '2';
64318606Sedward 		*p++ = '-';
64418606Sedward 		for (i = 0; i < 4; i++)
64518606Sedward 			*p++ = *s++;
6461014Sbill 		break;
64724463Sbloom 	case 5:
64824463Sbloom 		*p++ = ' ';
64924463Sbloom 		*p++ = 'x';
65024463Sbloom 		*p++ = *s++;
65124463Sbloom 		*p++ = '-';
65224463Sbloom 		for (i = 0; i < 4; i++)
65324463Sbloom 			*p++ = *s++;
65424463Sbloom 		break;
65518606Sedward 	case 7:
65618606Sedward 		for (i = 0; i < 3; i++)
65718606Sedward 			*p++ = *s++;
65818606Sedward 		*p++ = '-';
65918606Sedward 		for (i = 0; i < 4; i++)
66018606Sedward 			*p++ = *s++;
6611014Sbill 		break;
66218606Sedward 	case 10:
66318606Sedward 		for (i = 0; i < 3; i++)
66418606Sedward 			*p++ = *s++;
66518606Sedward 		*p++ = '-';
66618606Sedward 		for (i = 0; i < 3; i++)
66718606Sedward 			*p++ = *s++;
66818606Sedward 		*p++ = '-';
66918606Sedward 		for (i = 0; i < 4; i++)
67018606Sedward 			*p++ = *s++;
6711014Sbill 		break;
67218606Sedward 	case 0:
67318606Sedward 		return 0;
67418606Sedward 	default:
67518606Sedward 		return (strcpy(malloc(len + 1), s));
6761014Sbill 	}
67718606Sedward 	*p++ = 0;
67818606Sedward 	return (strcpy(malloc(p - fonebuf), fonebuf));
6791014Sbill }
6801014Sbill 
68118606Sedward /*
68218606Sedward  * decode the information in the gecos field of /etc/passwd
6831014Sbill  */
68418606Sedward decode(pers)
68518606Sedward 	register struct person *pers;
6861014Sbill {
68718606Sedward 	char buffer[256];
68818606Sedward 	register char *bp, *gp, *lp;
68918606Sedward 	int alldigits;
69018606Sedward 	int hasspace;
69118606Sedward 	int len;
6921014Sbill 
69318606Sedward 	pers->realname = 0;
69418606Sedward 	pers->office = 0;
69518606Sedward 	pers->officephone = 0;
69618606Sedward 	pers->homephone = 0;
69718606Sedward 	pers->random = 0;
69818606Sedward 	if (pers->pwd == 0)
69918606Sedward 		return;
70018606Sedward 	gp = pers->pwd->pw_gecos;
70118606Sedward 	bp = buffer;
70218606Sedward 	if (*gp == ASTERISK)
7031014Sbill 		gp++;
70418606Sedward 	while (*gp && *gp != COMMA)			/* name */
70518606Sedward 		if (*gp == SAMENAME) {
70618606Sedward 			lp = pers->pwd->pw_name;
70718606Sedward 			if (islower(*lp))
70818606Sedward 				*bp++ = toupper(*lp++);
70918606Sedward 			while (*bp++ = *lp++)
71018606Sedward 				;
71118606Sedward 			bp--;
71218606Sedward 			gp++;
71318606Sedward 		} else
71418606Sedward 			*bp++ = *gp++;
71518606Sedward 	*bp++ = 0;
71618606Sedward 	if ((len = bp - buffer) > 1)
71718606Sedward 		pers->realname = strcpy(malloc(len), buffer);
71818606Sedward 	if (*gp == COMMA) {				/* office */
71918606Sedward 		gp++;
72018606Sedward 		hasspace = 0;
72118606Sedward 		bp = buffer;
72218606Sedward 		while (*gp && *gp != COMMA) {
72318606Sedward 			*bp = *gp++;
72418606Sedward 			if (*bp == ' ')
72518606Sedward 				hasspace = 1;
72618606Sedward 			/* leave 5 for Cory and Evans expansion */
72718606Sedward 			if (bp < buffer + sizeof buffer - 6)
72818606Sedward 				bp++;
7291014Sbill 		}
73018606Sedward 		*bp = 0;
73118606Sedward 		len = bp - buffer;
73218606Sedward 		bp--;			/* point to last character */
73318606Sedward 		if (hasspace || len == 0)
73418606Sedward 			len++;
73518606Sedward 		else if (*bp == CORY) {
73618606Sedward 			strcpy(bp, " Cory");
73718606Sedward 			len += 5;
73818606Sedward 		} else if (*bp == EVANS) {
73918606Sedward 			strcpy(bp, " Evans");
74018606Sedward 			len += 6;
74118606Sedward 		} else
74218606Sedward 			len++;
74318606Sedward 		if (len > 1)
74418606Sedward 			pers->office = strcpy(malloc(len), buffer);
74518606Sedward 	}
74618606Sedward 	if (*gp == COMMA) {				/* office phone */
74718606Sedward 		gp++;
74818606Sedward 		bp = buffer;
74918606Sedward 		alldigits = 1;
75018606Sedward 		while (*gp && *gp != COMMA) {
75118606Sedward 			*bp = *gp++;
75218606Sedward 			if (!isdigit(*bp))
75318606Sedward 				alldigits = 0;
75418606Sedward 			if (bp < buffer + sizeof buffer - 1)
75518606Sedward 				bp++;
7561014Sbill 		}
75718606Sedward 		*bp = 0;
75818606Sedward 		pers->officephone = phone(buffer, bp - buffer, alldigits);
75918606Sedward 	}
76018606Sedward 	if (*gp == COMMA) {				/* home phone */
7611014Sbill 		gp++;
76218606Sedward 		bp = buffer;
7631014Sbill 		alldigits = 1;
76418606Sedward 		while (*gp && *gp != COMMA) {
7651014Sbill 			*bp = *gp++;
76618606Sedward 			if (!isdigit(*bp))
76718606Sedward 				alldigits = 0;
76818606Sedward 			if (bp < buffer + sizeof buffer - 1)
7691014Sbill 				bp++;
7701014Sbill 		}
77118606Sedward 		*bp = 0;
77218606Sedward 		pers->homephone = phone(buffer, bp - buffer, alldigits);
7731014Sbill 	}
77418606Sedward 	if (pers->loggedin)
77518606Sedward 		findidle(pers);
77618606Sedward 	else
77718606Sedward 		findwhen(pers);
7781014Sbill }
7791014Sbill 
78018606Sedward /*
78118606Sedward  * find the last log in of a user by checking the LASTLOG file.
78218606Sedward  * the entry is indexed by the uid, so this can only be done if
78318606Sedward  * the uid is known (which it isn't in quick mode)
7841014Sbill  */
7851014Sbill 
7861014Sbill fwopen()
7871014Sbill {
78818606Sedward 	if ((lf = open(LASTLOG, 0)) < 0)
78918606Sedward 		fprintf(stderr, "finger: %s open error\n", LASTLOG);
7901014Sbill }
7911014Sbill 
79218606Sedward findwhen(pers)
79318606Sedward 	register struct person *pers;
7941014Sbill {
79518606Sedward 	struct lastlog ll;
79618606Sedward 	int i;
7971014Sbill 
79818606Sedward 	if (lf >= 0) {
79918606Sedward 		lseek(lf, (long)pers->pwd->pw_uid * sizeof ll, 0);
80018606Sedward 		if ((i = read(lf, (char *)&ll, sizeof ll)) == sizeof ll) {
80118606Sedward 			bcopy(ll.ll_line, pers->tty, LMAX);
80218606Sedward 			pers->tty[LMAX] = 0;
80318606Sedward 			bcopy(ll.ll_host, pers->host, HMAX);
80418606Sedward 			pers->host[HMAX] = 0;
80518606Sedward 			pers->loginat = ll.ll_time;
80618606Sedward 		} else {
80718606Sedward 			if (i != 0)
80818606Sedward 				fprintf(stderr, "finger: %s read error\n",
80918606Sedward 					LASTLOG);
81018606Sedward 			pers->tty[0] = 0;
81118606Sedward 			pers->host[0] = 0;
81218606Sedward 			pers->loginat = 0L;
81318606Sedward 		}
81418606Sedward 	} else {
81518606Sedward 		pers->tty[0] = 0;
81618606Sedward 		pers->host[0] = 0;
8171014Sbill 		pers->loginat = 0L;
8181014Sbill 	}
8191014Sbill }
8201014Sbill 
8211014Sbill fwclose()
8221014Sbill {
82318606Sedward 	if (lf >= 0)
82418606Sedward 		close(lf);
8251014Sbill }
8261014Sbill 
82718606Sedward /*
82818606Sedward  * find the idle time of a user by doing a stat on /dev/tty??,
82918606Sedward  * where tty?? has been gotten from USERLOG, supposedly.
8301014Sbill  */
83118606Sedward findidle(pers)
83218606Sedward 	register struct person *pers;
8331014Sbill {
83418606Sedward 	struct stat ttystatus;
83518606Sedward 	static char buffer[20] = "/dev/";
83618606Sedward 	long t;
83718606Sedward #define TTYLEN 5
8381014Sbill 
83918606Sedward 	strcpy(buffer + TTYLEN, pers->tty);
84018606Sedward 	buffer[TTYLEN+LMAX] = 0;
84118606Sedward 	if (stat(buffer, &ttystatus) < 0) {
84218606Sedward 		fprintf(stderr, "finger: Can't stat %s\n", buffer);
84318606Sedward 		exit(4);
84418606Sedward 	}
84518606Sedward 	time(&t);
84618606Sedward 	if (t < ttystatus.st_atime)
8471014Sbill 		pers->idletime = 0L;
84818606Sedward 	else
84918606Sedward 		pers->idletime = t - ttystatus.st_atime;
85018606Sedward 	pers->writable = (ttystatus.st_mode & TALKABLE) == TALKABLE;
8511014Sbill }
8521014Sbill 
85318606Sedward /*
85418606Sedward  * print idle time in short format; this program always prints 4 characters;
85518606Sedward  * if the idle time is zero, it prints 4 blanks.
8561014Sbill  */
85718606Sedward stimeprint(dt)
85818606Sedward 	long *dt;
8591014Sbill {
86018606Sedward 	register struct tm *delta;
8611014Sbill 
86218606Sedward 	delta = gmtime(dt);
86318606Sedward 	if (delta->tm_yday == 0)
86418606Sedward 		if (delta->tm_hour == 0)
86518606Sedward 			if (delta->tm_min == 0)
86618606Sedward 				printf("    ");
86718606Sedward 			else
86818606Sedward 				printf("  %2d", delta->tm_min);
86918606Sedward 		else
87018606Sedward 			if (delta->tm_hour >= 10)
87118606Sedward 				printf("%3d:", delta->tm_hour);
87218606Sedward 			else
87318606Sedward 				printf("%1d:%02d",
87418606Sedward 					delta->tm_hour, delta->tm_min);
87518606Sedward 	else
87618606Sedward 		printf("%3dd", delta->tm_yday);
8771014Sbill }
8781014Sbill 
87918606Sedward /*
88018606Sedward  * print idle time in long format with care being taken not to pluralize
88118606Sedward  * 1 minutes or 1 hours or 1 days.
88218606Sedward  * print "prefix" first.
8831014Sbill  */
88418606Sedward ltimeprint(before, dt, after)
88518606Sedward 	long *dt;
88618606Sedward 	char *before, *after;
8871014Sbill {
88818606Sedward 	register struct tm *delta;
8891014Sbill 
89018606Sedward 	delta = gmtime(dt);
89118606Sedward 	if (delta->tm_yday == 0 && delta->tm_hour == 0 && delta->tm_min == 0 &&
89218606Sedward 	    delta->tm_sec <= 10)
89318606Sedward 		return (0);
89418606Sedward 	printf("%s", before);
89518606Sedward 	if (delta->tm_yday >= 10)
89618606Sedward 		printf("%d days", delta->tm_yday);
89718606Sedward 	else if (delta->tm_yday > 0)
89818606Sedward 		printf("%d day%s %d hour%s",
89918606Sedward 			delta->tm_yday, delta->tm_yday == 1 ? "" : "s",
90018606Sedward 			delta->tm_hour, delta->tm_hour == 1 ? "" : "s");
90118606Sedward 	else
90218606Sedward 		if (delta->tm_hour >= 10)
90318606Sedward 			printf("%d hours", delta->tm_hour);
90418606Sedward 		else if (delta->tm_hour > 0)
90518606Sedward 			printf("%d hour%s %d minute%s",
90618606Sedward 				delta->tm_hour, delta->tm_hour == 1 ? "" : "s",
90718606Sedward 				delta->tm_min, delta->tm_min == 1 ? "" : "s");
90818606Sedward 		else
90918606Sedward 			if (delta->tm_min >= 10)
91018606Sedward 				printf("%2d minutes", delta->tm_min);
91118606Sedward 			else if (delta->tm_min == 0)
91218606Sedward 				printf("%2d seconds", delta->tm_sec);
91318606Sedward 			else
91418606Sedward 				printf("%d minute%s %d second%s",
91518606Sedward 					delta->tm_min,
91618606Sedward 					delta->tm_min == 1 ? "" : "s",
91718606Sedward 					delta->tm_sec,
91818606Sedward 					delta->tm_sec == 1 ? "" : "s");
91918606Sedward 	printf("%s", after);
9201014Sbill }
9211014Sbill 
92218606Sedward matchcmp(gname, login, given)
92318606Sedward 	register char *gname;
92418606Sedward 	char *login;
92518606Sedward 	char *given;
9261014Sbill {
92718606Sedward 	char buffer[100];
92818606Sedward 	register char *bp, *lp;
92918606Sedward 	register c;
9301014Sbill 
93118606Sedward 	if (*gname == ASTERISK)
93218606Sedward 		gname++;
93318606Sedward 	lp = 0;
93418606Sedward 	bp = buffer;
93518606Sedward 	for (;;)
93618606Sedward 		switch (c = *gname++) {
93718606Sedward 		case SAMENAME:
93818606Sedward 			for (lp = login; bp < buffer + sizeof buffer
93918606Sedward 					 && (*bp++ = *lp++);)
94018606Sedward 				;
94118606Sedward 			bp--;
94218606Sedward 			break;
94318606Sedward 		case ' ':
94418606Sedward 		case COMMA:
94518606Sedward 		case '\0':
94618606Sedward 			*bp = 0;
94718606Sedward 			if (namecmp(buffer, given))
94818606Sedward 				return (1);
94918606Sedward 			if (c == COMMA || c == 0)
95018606Sedward 				return (0);
95118606Sedward 			bp = buffer;
95218606Sedward 			break;
95318606Sedward 		default:
95418606Sedward 			if (bp < buffer + sizeof buffer)
95518606Sedward 				*bp++ = c;
9561014Sbill 		}
95718606Sedward 	/*NOTREACHED*/
9581014Sbill }
9591014Sbill 
96018606Sedward namecmp(name1, name2)
96118606Sedward 	register char *name1, *name2;
9621014Sbill {
96318606Sedward 	register c1, c2;
9641014Sbill 
96518606Sedward 	for (;;) {
96618606Sedward 		c1 = *name1++;
96718606Sedward 		if (islower(c1))
96818606Sedward 			c1 = toupper(c1);
96918606Sedward 		c2 = *name2++;
97018606Sedward 		if (islower(c2))
97118606Sedward 			c2 = toupper(c2);
97218606Sedward 		if (c1 != c2)
97318606Sedward 			break;
97418606Sedward 		if (c1 == 0)
97518606Sedward 			return (1);
9761014Sbill 	}
97718606Sedward 	if (!c1) {
97818606Sedward 		for (name2--; isdigit(*name2); name2++)
97918606Sedward 			;
98018606Sedward 		if (*name2 == 0)
98118606Sedward 			return (1);
98218606Sedward 	} else if (!c2) {
98318606Sedward 		for (name1--; isdigit(*name1); name1++)
98418606Sedward 			;
98518606Sedward 		if (*name2 == 0)
98618606Sedward 			return (1);
9871014Sbill 	}
98818606Sedward 	return (0);
9891014Sbill }
9901014Sbill 
99116469Ssam netfinger(name)
99218606Sedward 	char *name;
99316469Ssam {
99416469Ssam 	char *host;
99516469Ssam 	char fname[100];
99616469Ssam 	struct hostent *hp;
99716469Ssam 	struct servent *sp;
99818606Sedward 	struct sockaddr_in sin;
99916469Ssam 	int s;
100016469Ssam 	char *rindex();
100116469Ssam 	register FILE *f;
100216469Ssam 	register int c;
100316469Ssam 	register int lastc;
100416469Ssam 
100516469Ssam 	if (name == NULL)
100618606Sedward 		return (0);
100716469Ssam 	host = rindex(name, '@');
100816469Ssam 	if (host == NULL)
100918606Sedward 		return (0);
101016469Ssam 	*host++ = 0;
101116469Ssam 	hp = gethostbyname(host);
101216469Ssam 	if (hp == NULL) {
101316469Ssam 		static struct hostent def;
101416469Ssam 		static struct in_addr defaddr;
101525240Sbloom 		static char *alist[1];
101616469Ssam 		static char namebuf[128];
101716469Ssam 		int inet_addr();
101816469Ssam 
101916469Ssam 		defaddr.s_addr = inet_addr(host);
102016469Ssam 		if (defaddr.s_addr == -1) {
102116469Ssam 			printf("unknown host: %s\n", host);
102218606Sedward 			return (1);
102316469Ssam 		}
102416469Ssam 		strcpy(namebuf, host);
102516469Ssam 		def.h_name = namebuf;
102625239Ssam 		def.h_addr_list = alist, def.h_addr = (char *)&defaddr;
102716469Ssam 		def.h_length = sizeof (struct in_addr);
102816469Ssam 		def.h_addrtype = AF_INET;
102916469Ssam 		def.h_aliases = 0;
103016469Ssam 		hp = &def;
103116469Ssam 	}
103216469Ssam 	sp = getservbyname("finger", "tcp");
103316469Ssam 	if (sp == 0) {
103416469Ssam 		printf("tcp/finger: unknown service\n");
103518606Sedward 		return (1);
103616469Ssam 	}
103716469Ssam 	sin.sin_family = hp->h_addrtype;
103816469Ssam 	bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
103916469Ssam 	sin.sin_port = sp->s_port;
104016469Ssam 	s = socket(hp->h_addrtype, SOCK_STREAM, 0);
104116469Ssam 	if (s < 0) {
104216469Ssam 		perror("socket");
104318606Sedward 		return (1);
104416469Ssam 	}
1045*30958Sbostic 	printf("[%s]\n", hp->h_name);
1046*30958Sbostic 	fflush(stdout);
104716469Ssam 	if (connect(s, (char *)&sin, sizeof (sin)) < 0) {
104816469Ssam 		perror("connect");
104916469Ssam 		close(s);
105018606Sedward 		return (1);
105116469Ssam 	}
105216469Ssam 	if (large) write(s, "/W ", 3);
105316469Ssam 	write(s, name, strlen(name));
105416469Ssam 	write(s, "\r\n", 2);
105516469Ssam 	f = fdopen(s, "r");
105616469Ssam 	while ((c = getc(f)) != EOF) {
105716469Ssam 		switch(c) {
105816469Ssam 		case 0210:
105916469Ssam 		case 0211:
106016469Ssam 		case 0212:
106116469Ssam 		case 0214:
106216469Ssam 			c -= 0200;
106316469Ssam 			break;
106416469Ssam 		case 0215:
106516469Ssam 			c = '\n';
106616469Ssam 			break;
106716469Ssam 		}
106825589Sbloom 		lastc = c;
106925589Sbloom 		if (isprint(c) || isspace(c))
107025589Sbloom 			putchar(c);
107125589Sbloom 		else
107225589Sbloom 			putchar(c ^ 100);
107316469Ssam 	}
107416469Ssam 	if (lastc != '\n')
107516469Ssam 		putchar('\n');
107625121Sbloom 	(void)fclose(f);
107718606Sedward 	return (1);
107816469Ssam }
1079