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