xref: /csrg-svn/usr.bin/finger/finger.c (revision 21554)
1*21554Sdist /*
2*21554Sdist  * Copyright (c) 1980 Regents of the University of California.
3*21554Sdist  * All rights reserved.  The Berkeley software License Agreement
4*21554Sdist  * specifies the terms and conditions for redistribution.
5*21554Sdist  */
6*21554Sdist 
713619Ssam #ifndef lint
8*21554Sdist char copyright[] =
9*21554Sdist "@(#) Copyright (c) 1980 Regents of the University of California.\n\
10*21554Sdist  All rights reserved.\n";
11*21554Sdist #endif not lint
121014Sbill 
13*21554Sdist #ifndef lint
14*21554Sdist static char sccsid[] = "@(#)finger.c	5.1 (Berkeley) 05/31/85";
15*21554Sdist #endif not lint
16*21554Sdist 
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);
18018606Sedward 	print();
18118606Sedward 	exit(0);
18218606Sedward }
1831014Sbill 
18418606Sedward doall()
1851014Sbill {
18618606Sedward 	register struct person *p;
18718606Sedward 	register struct passwd *pw;
18818606Sedward 	int uf;
18918606Sedward 	char name[NMAX + 1];
1901014Sbill 
19118606Sedward 	unshort = large;
19218606Sedward 	if ((uf = open(USERLOG, 0)) < 0) {
19318606Sedward 		fprintf(stderr, "finger: error opening %s\n", USERLOG);
19418606Sedward 		exit(2);
1951014Sbill 	}
19618606Sedward 	if (unquick) {
19718606Sedward 		extern _pw_stayopen;
1981014Sbill 
1991014Sbill 		setpwent();
20018606Sedward 		_pw_stayopen = 1;
2011014Sbill 		fwopen();
20218606Sedward 	}
20318606Sedward 	while (read(uf, (char *)&user, sizeof user) == sizeof user) {
20418606Sedward 		if (user.ut_name[0] == 0)
20518606Sedward 			continue;
20618606Sedward 		if (person1 == 0)
20718606Sedward 			p = person1 = (struct person *) malloc(sizeof *p);
20818606Sedward 		else {
20918606Sedward 			p->link = (struct person *) malloc(sizeof *p);
2101014Sbill 			p = p->link;
2111014Sbill 		}
21218606Sedward 		bcopy(user.ut_name, name, NMAX);
21318606Sedward 		name[NMAX] = 0;
21418606Sedward 		bcopy(user.ut_line, p->tty, LMAX);
21518606Sedward 		p->tty[LMAX] = 0;
21618606Sedward 		bcopy(user.ut_host, p->host, HMAX);
21718606Sedward 		p->host[HMAX] = 0;
21818606Sedward 		p->loginat = user.ut_time;
21918606Sedward 		p->pwd = 0;
22018606Sedward 		p->loggedin = 1;
22118606Sedward 		if (unquick && (pw = getpwnam(name))) {
22218606Sedward 			p->pwd = pwdcopy(pw);
22318606Sedward 			decode(p);
22418606Sedward 			p->name = p->pwd->pw_name;
22518606Sedward 		} else
22618606Sedward 			p->name = strcpy(malloc(strlen(name) + 1), name);
22718606Sedward 	}
22818606Sedward 	if (unquick) {
2291014Sbill 		fwclose();
2301014Sbill 		endpwent();
2311014Sbill 	}
23218606Sedward 	close(uf);
23318606Sedward 	if (person1 == 0) {
23418606Sedward 		printf("No one logged on\n");
23518606Sedward 		exit(0);
23618606Sedward 	}
23718606Sedward 	p->link = 0;
23818606Sedward }
2391014Sbill 
24018606Sedward donames(argv)
24118606Sedward 	char **argv;
24218606Sedward {
24318606Sedward 	register struct person *p;
24418606Sedward 	register struct passwd *pw;
24518606Sedward 	int uf;
2461014Sbill 
24718606Sedward 	/*
24818606Sedward 	 * get names from command line and check to see if they're
24918606Sedward 	 * logged in
25018606Sedward 	 */
25118606Sedward 	unshort = !small;
25218606Sedward 	for (; *argv != 0; argv++) {
25318606Sedward 		if (netfinger(*argv))
25418606Sedward 			continue;
25518606Sedward 		if (person1 == 0)
25618606Sedward 			p = person1 = (struct person *) malloc(sizeof *p);
25718606Sedward 		else {
25818606Sedward 			p->link = (struct person *) malloc(sizeof *p);
25918606Sedward 			p = p->link;
26016469Ssam 		}
26118606Sedward 		p->name = *argv;
2621014Sbill 		p->loggedin = 0;
26318606Sedward 		p->original = 1;
26418606Sedward 		p->pwd = 0;
26518606Sedward 	}
26618606Sedward 	p->link = 0;
26718606Sedward 	/*
26818606Sedward 	 * if we are doing it, read /etc/passwd for the useful info
26918606Sedward 	 */
27018606Sedward 	if (unquick) {
27118606Sedward 		setpwent();
27218606Sedward 		if (!match) {
27318606Sedward 			extern _pw_stayopen;
2741014Sbill 
27518606Sedward 			_pw_stayopen = 1;
27618606Sedward 			for (p = person1; p != 0; p = p->link)
27718606Sedward 				if (pw = getpwnam(p->name))
27818606Sedward 					p->pwd = pwdcopy(pw);
27918606Sedward 		} else while ((pw = getpwent()) != 0) {
28018606Sedward 			for (p = person1; p != 0; p = p->link) {
28118606Sedward 				if (!p->original)
28218606Sedward 					continue;
28318606Sedward 				if (strcmp(p->name, pw->pw_name) != 0 &&
28418606Sedward 				    !matchcmp(pw->pw_gecos, pw->pw_name, p->name))
28518606Sedward 					continue;
28618606Sedward 				if (p->pwd == 0)
28718606Sedward 					p->pwd = pwdcopy(pw);
28818606Sedward 				else {
28918606Sedward 					struct person *new;
29018606Sedward 					/*
29118606Sedward 					 * handle multiple login names, insert
29218606Sedward 					 * new "duplicate" entry behind
29318606Sedward 					 */
29418606Sedward 					new = (struct person *)
29518606Sedward 						malloc(sizeof *new);
29618606Sedward 					new->pwd = pwdcopy(pw);
29718606Sedward 					new->name = p->name;
29818606Sedward 					new->original = 1;
29918606Sedward 					new->loggedin = 0;
30018606Sedward 					new->link = p->link;
30118606Sedward 					p->original = 0;
30218606Sedward 					p->link = new;
30318606Sedward 					p = new;
30418606Sedward 				}
3051014Sbill 			}
3061014Sbill 		}
3071014Sbill 		endpwent();
30818606Sedward 	}
30918606Sedward 	/* Now get login information */
31018606Sedward 	if ((uf = open(USERLOG, 0)) < 0) {
31118606Sedward 		fprintf(stderr, "finger: error opening %s\n", USERLOG);
31218606Sedward 		exit(2);
31318606Sedward 	}
31418606Sedward 	while (read(uf, (char *)&user, sizeof user) == sizeof user) {
31518606Sedward 		if (*user.ut_name == 0)
31618606Sedward 			continue;
31718606Sedward 		for (p = person1; p != 0; p = p->link) {
31818606Sedward 			if (p->loggedin == 2)
31918606Sedward 				continue;
32018606Sedward 			if (strncmp(p->pwd ? p->pwd->pw_name : p->name,
32118606Sedward 				    user.ut_name, NMAX) != 0)
32218606Sedward 				continue;
32318606Sedward 			if (p->loggedin == 0) {
32418606Sedward 				bcopy(user.ut_line, p->tty, LMAX);
32518606Sedward 				p->tty[LMAX] = 0;
32618606Sedward 				bcopy(user.ut_host, p->host, HMAX);
32718606Sedward 				p->host[HMAX] = 0;
32818606Sedward 				p->loginat = user.ut_time;
32918606Sedward 				p->loggedin = 1;
33018606Sedward 			} else {	/* p->loggedin == 1 */
33118606Sedward 				struct person *new;
33218606Sedward 				new = (struct person *) malloc(sizeof *new);
33318606Sedward 				new->name = p->name;
33418606Sedward 				bcopy(user.ut_line, new->tty, LMAX);
33518606Sedward 				new->tty[LMAX] = 0;
33618606Sedward 				bcopy(user.ut_host, new->host, HMAX);
33718606Sedward 				new->host[HMAX] = 0;
33818606Sedward 				new->loginat = user.ut_time;
33918606Sedward 				new->pwd = p->pwd;
34018606Sedward 				new->loggedin = 1;
34118606Sedward 				new->original = 0;
34218606Sedward 				new->link = p->link;
34318606Sedward 				p->loggedin = 2;
34418606Sedward 				p->link = new;
34518606Sedward 				p = new;
3461014Sbill 			}
3471014Sbill 		}
34818606Sedward 	}
34918606Sedward 	close(uf);
35018606Sedward 	if (unquick) {
3511014Sbill 		fwopen();
35218606Sedward 		for (p = person1; p != 0; p = p->link)
35318606Sedward 			decode(p);
3541014Sbill 		fwclose();
3551014Sbill 	}
35618606Sedward }
3571014Sbill 
35818606Sedward print()
35918606Sedward {
36018606Sedward 	register FILE *fp;
36118606Sedward 	register struct person *p;
36218606Sedward 	register char *s;
36318606Sedward 	register c;
3641014Sbill 
36518606Sedward 	/*
36618606Sedward 	 * print out what we got
36718606Sedward 	 */
36818606Sedward 	if (header) {
36918606Sedward 		if (unquick) {
37018606Sedward 			if (!unshort)
37118606Sedward 				if (wide)
37218606Sedward 					printf("Login       Name              TTY Idle    When            Office\n");
37318606Sedward 				else
37418606Sedward 					printf("Login    TTY Idle    When            Office\n");
37518606Sedward 		} else {
37618606Sedward 			printf("Login      TTY            When");
37718606Sedward 			if (idle)
37818606Sedward 				printf("             Idle");
37918606Sedward 			putchar('\n');
3801014Sbill 		}
38118606Sedward 	}
38218606Sedward 	for (p = person1; p != 0; p = p->link) {
38318606Sedward 		if (!unquick) {
38418606Sedward 			quickprint(p);
38518606Sedward 			continue;
3861014Sbill 		}
38718606Sedward 		if (!unshort) {
38818606Sedward 			shortprint(p);
38918606Sedward 			continue;
39018606Sedward 		}
39118606Sedward 		personprint(p);
39218606Sedward 		if (p->pwd != 0) {
39318606Sedward 			if (hack) {
39418606Sedward 				s = malloc(strlen(p->pwd->pw_dir) +
39518606Sedward 					sizeof PROJ);
39618606Sedward 				strcpy(s, p->pwd->pw_dir);
39718606Sedward 				strcat(s, PROJ);
39818606Sedward 				if ((fp = fopen(s, "r")) != 0) {
39918606Sedward 					printf("Project: ");
40018606Sedward 					while ((c = getc(fp)) != EOF) {
40118606Sedward 						if (c == '\n')
40218606Sedward 							break;
40318606Sedward 						putchar(c);
40418606Sedward 					}
40518606Sedward 					fclose(fp);
40618606Sedward 					putchar('\n');
4071014Sbill 				}
40818606Sedward 				free(s);
4091014Sbill 			}
41018606Sedward 			if (plan) {
41118606Sedward 				s = malloc(strlen(p->pwd->pw_dir) +
41218606Sedward 					sizeof PLAN);
41318606Sedward 				strcpy(s, p->pwd->pw_dir);
41418606Sedward 				strcat(s, PLAN);
41518606Sedward 				if ((fp = fopen(s, "r")) == 0)
41618606Sedward 					printf("No Plan.\n");
41718606Sedward 				else {
41818606Sedward 					printf("Plan:\n");
41918606Sedward 					while ((c = getc(fp)) != EOF)
42018606Sedward 						putchar(c);
42118606Sedward 					fclose(fp);
4221014Sbill 				}
42318606Sedward 				free(s);
4241014Sbill 			}
4251014Sbill 		}
42618606Sedward 		if (p->link != 0)
42718606Sedward 			putchar('\n');
42818606Sedward 	}
4291014Sbill }
4301014Sbill 
43118606Sedward /*
43218606Sedward  * Duplicate a pwd entry.
43318606Sedward  * Note: Only the useful things (what the program currently uses) are copied.
4341014Sbill  */
43518606Sedward struct passwd *
43618606Sedward pwdcopy(pfrom)
43718606Sedward 	register struct passwd *pfrom;
43818606Sedward {
43918606Sedward 	register struct passwd *pto;
4401014Sbill 
44118606Sedward 	pto = (struct passwd *) malloc(sizeof *pto);
44218606Sedward #define savestr(s) strcpy(malloc(strlen(s) + 1), s)
44318606Sedward 	pto->pw_name = savestr(pfrom->pw_name);
4441014Sbill 	pto->pw_uid = pfrom->pw_uid;
44518606Sedward 	pto->pw_gecos = savestr(pfrom->pw_gecos);
44618606Sedward 	pto->pw_dir = savestr(pfrom->pw_dir);
44718606Sedward 	pto->pw_shell = savestr(pfrom->pw_shell);
44818606Sedward #undef savestr
44918606Sedward 	return pto;
4501014Sbill }
4511014Sbill 
45218606Sedward /*
45318606Sedward  * print out information on quick format giving just name, tty, login time
45418606Sedward  * and idle time if idle is set.
4551014Sbill  */
45618606Sedward quickprint(pers)
45718606Sedward 	register struct person *pers;
4581014Sbill {
45918606Sedward 	printf("%-*.*s  ", NMAX, NMAX, pers->name);
46018606Sedward 	if (pers->loggedin) {
46118606Sedward 		if (idle) {
46218606Sedward 			findidle(pers);
46318606Sedward 			printf("%c%-*s %-16.16s", pers->writable ? ' ' : '*',
46418606Sedward 				LMAX, pers->tty, ctime(&pers->loginat));
46518606Sedward 			ltimeprint("   ", &pers->idletime, "");
46618606Sedward 		} else
46718606Sedward 			printf(" %-*s %-16.16s", LMAX,
46818606Sedward 				pers->tty, ctime(&pers->loginat));
46918606Sedward 		putchar('\n');
47018606Sedward 	} else
47118606Sedward 		printf("          Not Logged In\n");
4721014Sbill }
4731014Sbill 
47418606Sedward /*
47518606Sedward  * print out information in short format, giving login name, full name,
47618606Sedward  * tty, idle time, login time, office location and phone.
4771014Sbill  */
47818606Sedward shortprint(pers)
47918606Sedward 	register struct person *pers;
4801014Sbill {
48118606Sedward 	char *p;
48218606Sedward 	char dialup;
4831014Sbill 
48418606Sedward 	if (pers->pwd == 0) {
48518606Sedward 		printf("%-15s       ???\n", pers->name);
48618606Sedward 		return;
4871014Sbill 	}
48818606Sedward 	printf("%-*s", NMAX, pers->pwd->pw_name);
4891014Sbill 	dialup = 0;
49018606Sedward 	if (wide) {
49118606Sedward 		if (pers->realname)
49218606Sedward 			printf(" %-20.20s", pers->realname);
49318606Sedward 		else
49418606Sedward 			printf("        ???          ");
4951014Sbill 	}
49618606Sedward 	putchar(' ');
49718606Sedward 	if (pers->loggedin && !pers->writable)
49818606Sedward 		putchar('*');
49918606Sedward 	else
50018606Sedward 		putchar(' ');
50118606Sedward 	if (*pers->tty) {
50218606Sedward 		if (pers->tty[0] == 't' && pers->tty[1] == 't' &&
50318606Sedward 		    pers->tty[2] == 'y') {
50418606Sedward 			if (pers->tty[3] == 'd' && pers->loggedin)
50518606Sedward 				dialup = 1;
50618606Sedward 			printf("%-2.2s ", pers->tty + 3);
50718606Sedward 		} else
50818606Sedward 			printf("%-2.2s ", pers->tty);
50918606Sedward 	} else
51018606Sedward 		printf("   ");
51118606Sedward 	p = ctime(&pers->loginat);
51218606Sedward 	if (pers->loggedin) {
51318606Sedward 		stimeprint(&pers->idletime);
51418606Sedward 		printf(" %3.3s %-5.5s ", p, p + 11);
51518606Sedward 	} else if (pers->loginat == 0)
51618606Sedward 		printf(" < .  .  .  . >");
5178842Smckusick 	else if (tloc - pers->loginat >= 180 * 24 * 60 * 60)
51818606Sedward 		printf(" <%-6.6s, %-4.4s>", p + 4, p + 20);
5198842Smckusick 	else
52018606Sedward 		printf(" <%-12.12s>", p + 4);
52118606Sedward 	if (dialup && pers->homephone)
52218606Sedward 		printf(" %20s", pers->homephone);
52318606Sedward 	else {
52418606Sedward 		if (pers->office)
52518606Sedward 			printf(" %-11.11s", pers->office);
52618606Sedward 		else if (pers->officephone || pers->homephone)
52718606Sedward 			printf("            ");
52818606Sedward 		if (pers->officephone)
52918606Sedward 			printf(" %s", pers->officephone);
53018606Sedward 		else if (pers->homephone)
53118606Sedward 			printf(" %s", pers->homephone);
5321014Sbill 	}
53318606Sedward 	putchar('\n');
5341014Sbill }
5351014Sbill 
53618606Sedward /*
53718606Sedward  * print out a person in long format giving all possible information.
53818606Sedward  * directory and shell are inhibited if unbrief is clear.
5391014Sbill  */
54018606Sedward personprint(pers)
54118606Sedward 	register struct person *pers;
5421014Sbill {
54318606Sedward 	if (pers->pwd == 0) {
54418606Sedward 		printf("Login name: %-10s\t\t\tIn real life: ???\n",
54518606Sedward 			pers->name);
54618606Sedward 		return;
5471014Sbill 	}
54818606Sedward 	printf("Login name: %-10s", pers->pwd->pw_name);
54918606Sedward 	if (pers->loggedin && !pers->writable)
55018606Sedward 		printf("	(messages off)	");
55118606Sedward 	else
55218606Sedward 		printf("			");
55318606Sedward 	if (pers->realname)
55418606Sedward 		printf("In real life: %s", pers->realname);
55518606Sedward 	if (pers->office) {
55618606Sedward 		printf("\nOffice: %-.11s", pers->office);
55718606Sedward 		if (pers->officephone) {
55818606Sedward 			printf(", %s", pers->officephone);
55918606Sedward 			if (pers->homephone)
56018606Sedward 				printf("\t\tHome phone: %s", pers->homephone);
56118606Sedward 			else if (pers->random)
56218606Sedward 				printf("\t\t%s", pers->random);
56318606Sedward 		} else
56418606Sedward 			if (pers->homephone)
56518606Sedward 				printf("\t\t\tHome phone: %s", pers->homephone);
56618606Sedward 			else if (pers->random)
56718606Sedward 				printf("\t\t\t%s", pers->random);
56818606Sedward 	} else if (pers->officephone) {
56918606Sedward 		printf("\nPhone: %s", pers->officephone);
57018606Sedward 		if (pers->homephone)
57118606Sedward 			printf(", %s", pers->homephone);
57218606Sedward 		if (pers->random)
57318606Sedward 			printf(", %s", pers->random);
57418606Sedward 	} else if (pers->homephone) {
57518606Sedward 		printf("\nPhone: %s", pers->homephone);
57618606Sedward 		if (pers->random)
57718606Sedward 			printf(", %s", pers->random);
57818606Sedward 	} else if (pers->random)
57918606Sedward 		printf("\n%s", pers->random);
58018606Sedward 	if (unbrief) {
58118606Sedward 		printf("\nDirectory: %-25s", pers->pwd->pw_dir);
58218606Sedward 		if (*pers->pwd->pw_shell)
58318606Sedward 			printf("\tShell: %-s", pers->pwd->pw_shell);
5841014Sbill 	}
58518606Sedward 	if (pers->loggedin) {
58618606Sedward 		register char *ep = ctime(&pers->loginat);
58718606Sedward 		if (*pers->host) {
58818606Sedward 			printf("\nOn since %15.15s on %s from %s",
58918606Sedward 				&ep[4], pers->tty, pers->host);
59018606Sedward 			ltimeprint("\n", &pers->idletime, " Idle Time");
59118606Sedward 		} else {
59218606Sedward 			printf("\nOn since %15.15s on %-*s",
59318606Sedward 				&ep[4], LMAX, pers->tty);
59418606Sedward 			ltimeprint("\t", &pers->idletime, " Idle Time");
5951014Sbill 		}
59618606Sedward 	} else if (pers->loginat == 0)
59718606Sedward 		printf("\nNever logged in.");
5988842Smckusick 	else if (tloc - pers->loginat > 180 * 24 * 60 * 60) {
59918606Sedward 		register char *ep = ctime(&pers->loginat);
60018606Sedward 		printf("\nLast login %10.10s, %4.4s on %s",
60118606Sedward 			ep, ep+20, pers->tty);
60218606Sedward 		if (*pers->host)
60318606Sedward 			printf(" from %s", pers->host);
60418606Sedward 	} else {
60518606Sedward 		register char *ep = ctime(&pers->loginat);
60618606Sedward 		printf("\nLast login %16.16s on %s", ep, pers->tty);
60718606Sedward 		if (*pers->host)
60818606Sedward 			printf(" from %s", pers->host);
6098842Smckusick 	}
61018606Sedward 	putchar('\n');
6111014Sbill }
6121014Sbill 
6131014Sbill /*
6141014Sbill  *  very hacky section of code to format phone numbers.  filled with
6151014Sbill  *  magic constants like 4, 7 and 10.
6161014Sbill  */
61718606Sedward char *
61818606Sedward phone(s, len, alldigits)
61918606Sedward 	register char *s;
62018606Sedward 	int len;
62118606Sedward 	char alldigits;
6221014Sbill {
62318606Sedward 	char fonebuf[15];
62418606Sedward 	register char *p = fonebuf;
62518606Sedward 	register i;
6261014Sbill 
62718606Sedward 	if (!alldigits)
62818606Sedward 		return (strcpy(malloc(len + 1), s));
62918606Sedward 	switch (len) {
63018606Sedward 	case 4:
63118606Sedward 		*p++ = ' ';
63218606Sedward 		*p++ = 'x';
63318606Sedward 		*p++ = '2';
63418606Sedward 		*p++ = '-';
63518606Sedward 		for (i = 0; i < 4; i++)
63618606Sedward 			*p++ = *s++;
6371014Sbill 		break;
63818606Sedward 	case 7:
63918606Sedward 		for (i = 0; i < 3; i++)
64018606Sedward 			*p++ = *s++;
64118606Sedward 		*p++ = '-';
64218606Sedward 		for (i = 0; i < 4; i++)
64318606Sedward 			*p++ = *s++;
6441014Sbill 		break;
64518606Sedward 	case 10:
64618606Sedward 		for (i = 0; i < 3; i++)
64718606Sedward 			*p++ = *s++;
64818606Sedward 		*p++ = '-';
64918606Sedward 		for (i = 0; i < 3; i++)
65018606Sedward 			*p++ = *s++;
65118606Sedward 		*p++ = '-';
65218606Sedward 		for (i = 0; i < 4; i++)
65318606Sedward 			*p++ = *s++;
6541014Sbill 		break;
65518606Sedward 	case 0:
65618606Sedward 		return 0;
65718606Sedward 	default:
65818606Sedward 		return (strcpy(malloc(len + 1), s));
6591014Sbill 	}
66018606Sedward 	*p++ = 0;
66118606Sedward 	return (strcpy(malloc(p - fonebuf), fonebuf));
6621014Sbill }
6631014Sbill 
66418606Sedward /*
66518606Sedward  * decode the information in the gecos field of /etc/passwd
6661014Sbill  */
66718606Sedward decode(pers)
66818606Sedward 	register struct person *pers;
6691014Sbill {
67018606Sedward 	char buffer[256];
67118606Sedward 	register char *bp, *gp, *lp;
67218606Sedward 	int alldigits;
67318606Sedward 	int hasspace;
67418606Sedward 	int len;
6751014Sbill 
67618606Sedward 	pers->realname = 0;
67718606Sedward 	pers->office = 0;
67818606Sedward 	pers->officephone = 0;
67918606Sedward 	pers->homephone = 0;
68018606Sedward 	pers->random = 0;
68118606Sedward 	if (pers->pwd == 0)
68218606Sedward 		return;
68318606Sedward 	gp = pers->pwd->pw_gecos;
68418606Sedward 	bp = buffer;
68518606Sedward 	if (*gp == ASTERISK)
6861014Sbill 		gp++;
68718606Sedward 	while (*gp && *gp != COMMA)			/* name */
68818606Sedward 		if (*gp == SAMENAME) {
68918606Sedward 			lp = pers->pwd->pw_name;
69018606Sedward 			if (islower(*lp))
69118606Sedward 				*bp++ = toupper(*lp++);
69218606Sedward 			while (*bp++ = *lp++)
69318606Sedward 				;
69418606Sedward 			bp--;
69518606Sedward 			gp++;
69618606Sedward 		} else
69718606Sedward 			*bp++ = *gp++;
69818606Sedward 	*bp++ = 0;
69918606Sedward 	if ((len = bp - buffer) > 1)
70018606Sedward 		pers->realname = strcpy(malloc(len), buffer);
70118606Sedward 	if (*gp == COMMA) {				/* office */
70218606Sedward 		gp++;
70318606Sedward 		hasspace = 0;
70418606Sedward 		bp = buffer;
70518606Sedward 		while (*gp && *gp != COMMA) {
70618606Sedward 			*bp = *gp++;
70718606Sedward 			if (*bp == ' ')
70818606Sedward 				hasspace = 1;
70918606Sedward 			/* leave 5 for Cory and Evans expansion */
71018606Sedward 			if (bp < buffer + sizeof buffer - 6)
71118606Sedward 				bp++;
7121014Sbill 		}
71318606Sedward 		*bp = 0;
71418606Sedward 		len = bp - buffer;
71518606Sedward 		bp--;			/* point to last character */
71618606Sedward 		if (hasspace || len == 0)
71718606Sedward 			len++;
71818606Sedward 		else if (*bp == CORY) {
71918606Sedward 			strcpy(bp, " Cory");
72018606Sedward 			len += 5;
72118606Sedward 		} else if (*bp == EVANS) {
72218606Sedward 			strcpy(bp, " Evans");
72318606Sedward 			len += 6;
72418606Sedward 		} else
72518606Sedward 			len++;
72618606Sedward 		if (len > 1)
72718606Sedward 			pers->office = strcpy(malloc(len), buffer);
72818606Sedward 	}
72918606Sedward 	if (*gp == COMMA) {				/* office phone */
73018606Sedward 		gp++;
73118606Sedward 		bp = buffer;
73218606Sedward 		alldigits = 1;
73318606Sedward 		while (*gp && *gp != COMMA) {
73418606Sedward 			*bp = *gp++;
73518606Sedward 			if (!isdigit(*bp))
73618606Sedward 				alldigits = 0;
73718606Sedward 			if (bp < buffer + sizeof buffer - 1)
73818606Sedward 				bp++;
7391014Sbill 		}
74018606Sedward 		*bp = 0;
74118606Sedward 		pers->officephone = phone(buffer, bp - buffer, alldigits);
74218606Sedward 	}
74318606Sedward 	if (*gp == COMMA) {				/* home phone */
7441014Sbill 		gp++;
74518606Sedward 		bp = buffer;
7461014Sbill 		alldigits = 1;
74718606Sedward 		while (*gp && *gp != COMMA) {
7481014Sbill 			*bp = *gp++;
74918606Sedward 			if (!isdigit(*bp))
75018606Sedward 				alldigits = 0;
75118606Sedward 			if (bp < buffer + sizeof buffer - 1)
7521014Sbill 				bp++;
7531014Sbill 		}
75418606Sedward 		*bp = 0;
75518606Sedward 		pers->homephone = phone(buffer, bp - buffer, alldigits);
7561014Sbill 	}
75718606Sedward 	if (pers->loggedin)
75818606Sedward 		findidle(pers);
75918606Sedward 	else
76018606Sedward 		findwhen(pers);
7611014Sbill }
7621014Sbill 
76318606Sedward /*
76418606Sedward  * find the last log in of a user by checking the LASTLOG file.
76518606Sedward  * the entry is indexed by the uid, so this can only be done if
76618606Sedward  * the uid is known (which it isn't in quick mode)
7671014Sbill  */
7681014Sbill 
7691014Sbill fwopen()
7701014Sbill {
77118606Sedward 	if ((lf = open(LASTLOG, 0)) < 0)
77218606Sedward 		fprintf(stderr, "finger: %s open error\n", LASTLOG);
7731014Sbill }
7741014Sbill 
77518606Sedward findwhen(pers)
77618606Sedward 	register struct person *pers;
7771014Sbill {
77818606Sedward 	struct lastlog ll;
77918606Sedward 	int i;
7801014Sbill 
78118606Sedward 	if (lf >= 0) {
78218606Sedward 		lseek(lf, (long)pers->pwd->pw_uid * sizeof ll, 0);
78318606Sedward 		if ((i = read(lf, (char *)&ll, sizeof ll)) == sizeof ll) {
78418606Sedward 			bcopy(ll.ll_line, pers->tty, LMAX);
78518606Sedward 			pers->tty[LMAX] = 0;
78618606Sedward 			bcopy(ll.ll_host, pers->host, HMAX);
78718606Sedward 			pers->host[HMAX] = 0;
78818606Sedward 			pers->loginat = ll.ll_time;
78918606Sedward 		} else {
79018606Sedward 			if (i != 0)
79118606Sedward 				fprintf(stderr, "finger: %s read error\n",
79218606Sedward 					LASTLOG);
79318606Sedward 			pers->tty[0] = 0;
79418606Sedward 			pers->host[0] = 0;
79518606Sedward 			pers->loginat = 0L;
79618606Sedward 		}
79718606Sedward 	} else {
79818606Sedward 		pers->tty[0] = 0;
79918606Sedward 		pers->host[0] = 0;
8001014Sbill 		pers->loginat = 0L;
8011014Sbill 	}
8021014Sbill }
8031014Sbill 
8041014Sbill fwclose()
8051014Sbill {
80618606Sedward 	if (lf >= 0)
80718606Sedward 		close(lf);
8081014Sbill }
8091014Sbill 
81018606Sedward /*
81118606Sedward  * find the idle time of a user by doing a stat on /dev/tty??,
81218606Sedward  * where tty?? has been gotten from USERLOG, supposedly.
8131014Sbill  */
81418606Sedward findidle(pers)
81518606Sedward 	register struct person *pers;
8161014Sbill {
81718606Sedward 	struct stat ttystatus;
81818606Sedward 	static char buffer[20] = "/dev/";
81918606Sedward 	long t;
82018606Sedward #define TTYLEN 5
8211014Sbill 
82218606Sedward 	strcpy(buffer + TTYLEN, pers->tty);
82318606Sedward 	buffer[TTYLEN+LMAX] = 0;
82418606Sedward 	if (stat(buffer, &ttystatus) < 0) {
82518606Sedward 		fprintf(stderr, "finger: Can't stat %s\n", buffer);
82618606Sedward 		exit(4);
82718606Sedward 	}
82818606Sedward 	time(&t);
82918606Sedward 	if (t < ttystatus.st_atime)
8301014Sbill 		pers->idletime = 0L;
83118606Sedward 	else
83218606Sedward 		pers->idletime = t - ttystatus.st_atime;
83318606Sedward 	pers->writable = (ttystatus.st_mode & TALKABLE) == TALKABLE;
8341014Sbill }
8351014Sbill 
83618606Sedward /*
83718606Sedward  * print idle time in short format; this program always prints 4 characters;
83818606Sedward  * if the idle time is zero, it prints 4 blanks.
8391014Sbill  */
84018606Sedward stimeprint(dt)
84118606Sedward 	long *dt;
8421014Sbill {
84318606Sedward 	register struct tm *delta;
8441014Sbill 
84518606Sedward 	delta = gmtime(dt);
84618606Sedward 	if (delta->tm_yday == 0)
84718606Sedward 		if (delta->tm_hour == 0)
84818606Sedward 			if (delta->tm_min == 0)
84918606Sedward 				printf("    ");
85018606Sedward 			else
85118606Sedward 				printf("  %2d", delta->tm_min);
85218606Sedward 		else
85318606Sedward 			if (delta->tm_hour >= 10)
85418606Sedward 				printf("%3d:", delta->tm_hour);
85518606Sedward 			else
85618606Sedward 				printf("%1d:%02d",
85718606Sedward 					delta->tm_hour, delta->tm_min);
85818606Sedward 	else
85918606Sedward 		printf("%3dd", delta->tm_yday);
8601014Sbill }
8611014Sbill 
86218606Sedward /*
86318606Sedward  * print idle time in long format with care being taken not to pluralize
86418606Sedward  * 1 minutes or 1 hours or 1 days.
86518606Sedward  * print "prefix" first.
8661014Sbill  */
86718606Sedward ltimeprint(before, dt, after)
86818606Sedward 	long *dt;
86918606Sedward 	char *before, *after;
8701014Sbill {
87118606Sedward 	register struct tm *delta;
8721014Sbill 
87318606Sedward 	delta = gmtime(dt);
87418606Sedward 	if (delta->tm_yday == 0 && delta->tm_hour == 0 && delta->tm_min == 0 &&
87518606Sedward 	    delta->tm_sec <= 10)
87618606Sedward 		return (0);
87718606Sedward 	printf("%s", before);
87818606Sedward 	if (delta->tm_yday >= 10)
87918606Sedward 		printf("%d days", delta->tm_yday);
88018606Sedward 	else if (delta->tm_yday > 0)
88118606Sedward 		printf("%d day%s %d hour%s",
88218606Sedward 			delta->tm_yday, delta->tm_yday == 1 ? "" : "s",
88318606Sedward 			delta->tm_hour, delta->tm_hour == 1 ? "" : "s");
88418606Sedward 	else
88518606Sedward 		if (delta->tm_hour >= 10)
88618606Sedward 			printf("%d hours", delta->tm_hour);
88718606Sedward 		else if (delta->tm_hour > 0)
88818606Sedward 			printf("%d hour%s %d minute%s",
88918606Sedward 				delta->tm_hour, delta->tm_hour == 1 ? "" : "s",
89018606Sedward 				delta->tm_min, delta->tm_min == 1 ? "" : "s");
89118606Sedward 		else
89218606Sedward 			if (delta->tm_min >= 10)
89318606Sedward 				printf("%2d minutes", delta->tm_min);
89418606Sedward 			else if (delta->tm_min == 0)
89518606Sedward 				printf("%2d seconds", delta->tm_sec);
89618606Sedward 			else
89718606Sedward 				printf("%d minute%s %d second%s",
89818606Sedward 					delta->tm_min,
89918606Sedward 					delta->tm_min == 1 ? "" : "s",
90018606Sedward 					delta->tm_sec,
90118606Sedward 					delta->tm_sec == 1 ? "" : "s");
90218606Sedward 	printf("%s", after);
9031014Sbill }
9041014Sbill 
90518606Sedward matchcmp(gname, login, given)
90618606Sedward 	register char *gname;
90718606Sedward 	char *login;
90818606Sedward 	char *given;
9091014Sbill {
91018606Sedward 	char buffer[100];
91118606Sedward 	register char *bp, *lp;
91218606Sedward 	register c;
9131014Sbill 
91418606Sedward 	if (*gname == ASTERISK)
91518606Sedward 		gname++;
91618606Sedward 	lp = 0;
91718606Sedward 	bp = buffer;
91818606Sedward 	for (;;)
91918606Sedward 		switch (c = *gname++) {
92018606Sedward 		case SAMENAME:
92118606Sedward 			for (lp = login; bp < buffer + sizeof buffer
92218606Sedward 					 && (*bp++ = *lp++);)
92318606Sedward 				;
92418606Sedward 			bp--;
92518606Sedward 			break;
92618606Sedward 		case ' ':
92718606Sedward 		case COMMA:
92818606Sedward 		case '\0':
92918606Sedward 			*bp = 0;
93018606Sedward 			if (namecmp(buffer, given))
93118606Sedward 				return (1);
93218606Sedward 			if (c == COMMA || c == 0)
93318606Sedward 				return (0);
93418606Sedward 			bp = buffer;
93518606Sedward 			break;
93618606Sedward 		default:
93718606Sedward 			if (bp < buffer + sizeof buffer)
93818606Sedward 				*bp++ = c;
9391014Sbill 		}
94018606Sedward 	/*NOTREACHED*/
9411014Sbill }
9421014Sbill 
94318606Sedward namecmp(name1, name2)
94418606Sedward 	register char *name1, *name2;
9451014Sbill {
94618606Sedward 	register c1, c2;
9471014Sbill 
94818606Sedward 	for (;;) {
94918606Sedward 		c1 = *name1++;
95018606Sedward 		if (islower(c1))
95118606Sedward 			c1 = toupper(c1);
95218606Sedward 		c2 = *name2++;
95318606Sedward 		if (islower(c2))
95418606Sedward 			c2 = toupper(c2);
95518606Sedward 		if (c1 != c2)
95618606Sedward 			break;
95718606Sedward 		if (c1 == 0)
95818606Sedward 			return (1);
9591014Sbill 	}
96018606Sedward 	if (!c1) {
96118606Sedward 		for (name2--; isdigit(*name2); name2++)
96218606Sedward 			;
96318606Sedward 		if (*name2 == 0)
96418606Sedward 			return (1);
96518606Sedward 	} else if (!c2) {
96618606Sedward 		for (name1--; isdigit(*name1); name1++)
96718606Sedward 			;
96818606Sedward 		if (*name2 == 0)
96918606Sedward 			return (1);
9701014Sbill 	}
97118606Sedward 	return (0);
9721014Sbill }
9731014Sbill 
97416469Ssam netfinger(name)
97518606Sedward 	char *name;
97616469Ssam {
97716469Ssam 	char *host;
97816469Ssam 	char fname[100];
97916469Ssam 	struct hostent *hp;
98016469Ssam 	struct servent *sp;
98118606Sedward 	struct sockaddr_in sin;
98216469Ssam 	int s;
98316469Ssam 	char *rindex();
98416469Ssam 	register FILE *f;
98516469Ssam 	register int c;
98616469Ssam 	register int lastc;
98716469Ssam 
98816469Ssam 	if (name == NULL)
98918606Sedward 		return (0);
99016469Ssam 	host = rindex(name, '@');
99116469Ssam 	if (host == NULL)
99218606Sedward 		return (0);
99316469Ssam 	*host++ = 0;
99416469Ssam 	hp = gethostbyname(host);
99516469Ssam 	if (hp == NULL) {
99616469Ssam 		static struct hostent def;
99716469Ssam 		static struct in_addr defaddr;
99816469Ssam 		static char namebuf[128];
99916469Ssam 		int inet_addr();
100016469Ssam 
100116469Ssam 		defaddr.s_addr = inet_addr(host);
100216469Ssam 		if (defaddr.s_addr == -1) {
100316469Ssam 			printf("unknown host: %s\n", host);
100418606Sedward 			return (1);
100516469Ssam 		}
100616469Ssam 		strcpy(namebuf, host);
100716469Ssam 		def.h_name = namebuf;
100816469Ssam 		def.h_addr = (char *)&defaddr;
100916469Ssam 		def.h_length = sizeof (struct in_addr);
101016469Ssam 		def.h_addrtype = AF_INET;
101116469Ssam 		def.h_aliases = 0;
101216469Ssam 		hp = &def;
101316469Ssam 	}
101416469Ssam 	printf("[%s]", hp->h_name);
101516469Ssam 	sp = getservbyname("finger", "tcp");
101616469Ssam 	if (sp == 0) {
101716469Ssam 		printf("tcp/finger: unknown service\n");
101818606Sedward 		return (1);
101916469Ssam 	}
102016469Ssam 	sin.sin_family = hp->h_addrtype;
102116469Ssam 	bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
102216469Ssam 	sin.sin_port = sp->s_port;
102316469Ssam 	s = socket(hp->h_addrtype, SOCK_STREAM, 0);
102416469Ssam 	if (s < 0) {
102516469Ssam 		fflush(stdout);
102616469Ssam 		perror("socket");
102718606Sedward 		return (1);
102816469Ssam 	}
102916469Ssam 	if (connect(s, (char *)&sin, sizeof (sin)) < 0) {
103016469Ssam 		fflush(stdout);
103116469Ssam 		perror("connect");
103216469Ssam 		close(s);
103318606Sedward 		return (1);
103416469Ssam 	}
103516469Ssam 	printf("\n");
103616469Ssam 	if (large) write(s, "/W ", 3);
103716469Ssam 	write(s, name, strlen(name));
103816469Ssam 	write(s, "\r\n", 2);
103916469Ssam 	f = fdopen(s, "r");
104016469Ssam 	while ((c = getc(f)) != EOF) {
104116469Ssam 		switch(c) {
104216469Ssam 		case 0210:
104316469Ssam 		case 0211:
104416469Ssam 		case 0212:
104516469Ssam 		case 0214:
104616469Ssam 			c -= 0200;
104716469Ssam 			break;
104816469Ssam 		case 0215:
104916469Ssam 			c = '\n';
105016469Ssam 			break;
105116469Ssam 		}
105216469Ssam 		putchar(lastc = c);
105316469Ssam 	}
105416469Ssam 	if (lastc != '\n')
105516469Ssam 		putchar('\n');
105618606Sedward 	return (1);
105716469Ssam }
1058