strip spaces from ends of alias addresses so that blanks at end of line works
[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
a243bfdf 14static char sccsid[] = "@(#)finger.c 5.4 (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 */
74#define TALKABLE 0222 /* tty is writable if 222 mode */
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;
406 putchar(c);
407 }
408 fclose(fp);
409 putchar('\n');
2e61617d 410 }
f5ebcf9e 411 free(s);
2e61617d 412 }
f5ebcf9e
EW
413 if (plan) {
414 s = malloc(strlen(p->pwd->pw_dir) +
415 sizeof PLAN);
416 strcpy(s, p->pwd->pw_dir);
417 strcat(s, PLAN);
418 if ((fp = fopen(s, "r")) == 0)
419 printf("No Plan.\n");
420 else {
421 printf("Plan:\n");
422 while ((c = getc(fp)) != EOF)
423 putchar(c);
424 fclose(fp);
2e61617d 425 }
f5ebcf9e 426 free(s);
2e61617d 427 }
2e61617d 428 }
f5ebcf9e
EW
429 if (p->link != 0)
430 putchar('\n');
431 }
2e61617d
BJ
432}
433
f5ebcf9e
EW
434/*
435 * Duplicate a pwd entry.
436 * Note: Only the useful things (what the program currently uses) are copied.
2e61617d 437 */
f5ebcf9e
EW
438struct passwd *
439pwdcopy(pfrom)
440 register struct passwd *pfrom;
2e61617d 441{
f5ebcf9e
EW
442 register struct passwd *pto;
443
444 pto = (struct passwd *) malloc(sizeof *pto);
445#define savestr(s) strcpy(malloc(strlen(s) + 1), s)
446 pto->pw_name = savestr(pfrom->pw_name);
2e61617d 447 pto->pw_uid = pfrom->pw_uid;
f5ebcf9e
EW
448 pto->pw_gecos = savestr(pfrom->pw_gecos);
449 pto->pw_dir = savestr(pfrom->pw_dir);
450 pto->pw_shell = savestr(pfrom->pw_shell);
451#undef savestr
452 return pto;
2e61617d
BJ
453}
454
f5ebcf9e
EW
455/*
456 * print out information on quick format giving just name, tty, login time
457 * and idle time if idle is set.
2e61617d 458 */
f5ebcf9e
EW
459quickprint(pers)
460 register struct person *pers;
2e61617d 461{
f5ebcf9e
EW
462 printf("%-*.*s ", NMAX, NMAX, pers->name);
463 if (pers->loggedin) {
464 if (idle) {
465 findidle(pers);
466 printf("%c%-*s %-16.16s", pers->writable ? ' ' : '*',
467 LMAX, pers->tty, ctime(&pers->loginat));
468 ltimeprint(" ", &pers->idletime, "");
469 } else
470 printf(" %-*s %-16.16s", LMAX,
471 pers->tty, ctime(&pers->loginat));
472 putchar('\n');
473 } else
474 printf(" Not Logged In\n");
2e61617d
BJ
475}
476
f5ebcf9e
EW
477/*
478 * print out information in short format, giving login name, full name,
479 * tty, idle time, login time, office location and phone.
2e61617d 480 */
f5ebcf9e
EW
481shortprint(pers)
482 register struct person *pers;
2e61617d 483{
f5ebcf9e
EW
484 char *p;
485 char dialup;
2e61617d 486
f5ebcf9e
EW
487 if (pers->pwd == 0) {
488 printf("%-15s ???\n", pers->name);
489 return;
2e61617d 490 }
f5ebcf9e 491 printf("%-*s", NMAX, pers->pwd->pw_name);
2e61617d 492 dialup = 0;
f5ebcf9e
EW
493 if (wide) {
494 if (pers->realname)
495 printf(" %-20.20s", pers->realname);
496 else
497 printf(" ??? ");
2e61617d 498 }
f5ebcf9e
EW
499 putchar(' ');
500 if (pers->loggedin && !pers->writable)
501 putchar('*');
502 else
503 putchar(' ');
504 if (*pers->tty) {
505 if (pers->tty[0] == 't' && pers->tty[1] == 't' &&
506 pers->tty[2] == 'y') {
507 if (pers->tty[3] == 'd' && pers->loggedin)
508 dialup = 1;
509 printf("%-2.2s ", pers->tty + 3);
510 } else
511 printf("%-2.2s ", pers->tty);
512 } else
513 printf(" ");
514 p = ctime(&pers->loginat);
515 if (pers->loggedin) {
516 stimeprint(&pers->idletime);
517 printf(" %3.3s %-5.5s ", p, p + 11);
518 } else if (pers->loginat == 0)
519 printf(" < . . . . >");
27cee9bc 520 else if (tloc - pers->loginat >= 180 * 24 * 60 * 60)
f5ebcf9e 521 printf(" <%-6.6s, %-4.4s>", p + 4, p + 20);
27cee9bc 522 else
f5ebcf9e
EW
523 printf(" <%-12.12s>", p + 4);
524 if (dialup && pers->homephone)
525 printf(" %20s", pers->homephone);
526 else {
527 if (pers->office)
528 printf(" %-11.11s", pers->office);
529 else if (pers->officephone || pers->homephone)
530 printf(" ");
531 if (pers->officephone)
532 printf(" %s", pers->officephone);
533 else if (pers->homephone)
534 printf(" %s", pers->homephone);
2e61617d 535 }
f5ebcf9e 536 putchar('\n');
2e61617d
BJ
537}
538
f5ebcf9e
EW
539/*
540 * print out a person in long format giving all possible information.
541 * directory and shell are inhibited if unbrief is clear.
2e61617d 542 */
f5ebcf9e
EW
543personprint(pers)
544 register struct person *pers;
2e61617d 545{
f5ebcf9e
EW
546 if (pers->pwd == 0) {
547 printf("Login name: %-10s\t\t\tIn real life: ???\n",
548 pers->name);
549 return;
2e61617d 550 }
f5ebcf9e
EW
551 printf("Login name: %-10s", pers->pwd->pw_name);
552 if (pers->loggedin && !pers->writable)
553 printf(" (messages off) ");
554 else
555 printf(" ");
556 if (pers->realname)
557 printf("In real life: %s", pers->realname);
558 if (pers->office) {
559 printf("\nOffice: %-.11s", pers->office);
560 if (pers->officephone) {
561 printf(", %s", pers->officephone);
562 if (pers->homephone)
563 printf("\t\tHome phone: %s", pers->homephone);
564 else if (pers->random)
565 printf("\t\t%s", pers->random);
566 } else
567 if (pers->homephone)
568 printf("\t\t\tHome phone: %s", pers->homephone);
569 else if (pers->random)
570 printf("\t\t\t%s", pers->random);
571 } else if (pers->officephone) {
572 printf("\nPhone: %s", pers->officephone);
573 if (pers->homephone)
574 printf(", %s", pers->homephone);
575 if (pers->random)
576 printf(", %s", pers->random);
577 } else if (pers->homephone) {
578 printf("\nPhone: %s", pers->homephone);
579 if (pers->random)
580 printf(", %s", pers->random);
581 } else if (pers->random)
582 printf("\n%s", pers->random);
583 if (unbrief) {
584 printf("\nDirectory: %-25s", pers->pwd->pw_dir);
585 if (*pers->pwd->pw_shell)
586 printf("\tShell: %-s", pers->pwd->pw_shell);
2e61617d 587 }
f5ebcf9e
EW
588 if (pers->loggedin) {
589 register char *ep = ctime(&pers->loginat);
590 if (*pers->host) {
591 printf("\nOn since %15.15s on %s from %s",
592 &ep[4], pers->tty, pers->host);
593 ltimeprint("\n", &pers->idletime, " Idle Time");
594 } else {
595 printf("\nOn since %15.15s on %-*s",
596 &ep[4], LMAX, pers->tty);
597 ltimeprint("\t", &pers->idletime, " Idle Time");
2e61617d 598 }
f5ebcf9e
EW
599 } else if (pers->loginat == 0)
600 printf("\nNever logged in.");
27cee9bc 601 else if (tloc - pers->loginat > 180 * 24 * 60 * 60) {
f5ebcf9e
EW
602 register char *ep = ctime(&pers->loginat);
603 printf("\nLast login %10.10s, %4.4s on %s",
604 ep, ep+20, pers->tty);
605 if (*pers->host)
606 printf(" from %s", pers->host);
607 } else {
608 register char *ep = ctime(&pers->loginat);
609 printf("\nLast login %16.16s on %s", ep, pers->tty);
610 if (*pers->host)
611 printf(" from %s", pers->host);
27cee9bc 612 }
f5ebcf9e 613 putchar('\n');
2e61617d
BJ
614}
615
2e61617d
BJ
616/*
617 * very hacky section of code to format phone numbers. filled with
618 * magic constants like 4, 7 and 10.
619 */
f5ebcf9e
EW
620char *
621phone(s, len, alldigits)
622 register char *s;
623 int len;
624 char alldigits;
2e61617d 625{
f5ebcf9e
EW
626 char fonebuf[15];
627 register char *p = fonebuf;
628 register i;
629
630 if (!alldigits)
631 return (strcpy(malloc(len + 1), s));
632 switch (len) {
633 case 4:
634 *p++ = ' ';
635 *p++ = 'x';
636 *p++ = '2';
637 *p++ = '-';
638 for (i = 0; i < 4; i++)
639 *p++ = *s++;
2e61617d 640 break;
172389f0
JB
641 case 5:
642 *p++ = ' ';
643 *p++ = 'x';
644 *p++ = *s++;
645 *p++ = '-';
646 for (i = 0; i < 4; i++)
647 *p++ = *s++;
648 break;
f5ebcf9e
EW
649 case 7:
650 for (i = 0; i < 3; i++)
651 *p++ = *s++;
652 *p++ = '-';
653 for (i = 0; i < 4; i++)
654 *p++ = *s++;
2e61617d 655 break;
f5ebcf9e
EW
656 case 10:
657 for (i = 0; i < 3; i++)
658 *p++ = *s++;
659 *p++ = '-';
660 for (i = 0; i < 3; i++)
661 *p++ = *s++;
662 *p++ = '-';
663 for (i = 0; i < 4; i++)
664 *p++ = *s++;
2e61617d 665 break;
f5ebcf9e
EW
666 case 0:
667 return 0;
668 default:
669 return (strcpy(malloc(len + 1), s));
2e61617d 670 }
f5ebcf9e
EW
671 *p++ = 0;
672 return (strcpy(malloc(p - fonebuf), fonebuf));
2e61617d
BJ
673}
674
f5ebcf9e
EW
675/*
676 * decode the information in the gecos field of /etc/passwd
2e61617d 677 */
f5ebcf9e
EW
678decode(pers)
679 register struct person *pers;
2e61617d 680{
f5ebcf9e
EW
681 char buffer[256];
682 register char *bp, *gp, *lp;
683 int alldigits;
684 int hasspace;
685 int len;
686
687 pers->realname = 0;
688 pers->office = 0;
689 pers->officephone = 0;
690 pers->homephone = 0;
691 pers->random = 0;
692 if (pers->pwd == 0)
693 return;
694 gp = pers->pwd->pw_gecos;
695 bp = buffer;
696 if (*gp == ASTERISK)
2e61617d 697 gp++;
f5ebcf9e
EW
698 while (*gp && *gp != COMMA) /* name */
699 if (*gp == SAMENAME) {
700 lp = pers->pwd->pw_name;
701 if (islower(*lp))
702 *bp++ = toupper(*lp++);
703 while (*bp++ = *lp++)
704 ;
705 bp--;
706 gp++;
707 } else
708 *bp++ = *gp++;
709 *bp++ = 0;
710 if ((len = bp - buffer) > 1)
711 pers->realname = strcpy(malloc(len), buffer);
712 if (*gp == COMMA) { /* office */
713 gp++;
714 hasspace = 0;
715 bp = buffer;
716 while (*gp && *gp != COMMA) {
717 *bp = *gp++;
718 if (*bp == ' ')
719 hasspace = 1;
720 /* leave 5 for Cory and Evans expansion */
721 if (bp < buffer + sizeof buffer - 6)
722 bp++;
2e61617d 723 }
f5ebcf9e
EW
724 *bp = 0;
725 len = bp - buffer;
726 bp--; /* point to last character */
727 if (hasspace || len == 0)
728 len++;
729 else if (*bp == CORY) {
730 strcpy(bp, " Cory");
731 len += 5;
732 } else if (*bp == EVANS) {
733 strcpy(bp, " Evans");
734 len += 6;
735 } else
736 len++;
737 if (len > 1)
738 pers->office = strcpy(malloc(len), buffer);
739 }
740 if (*gp == COMMA) { /* office phone */
2e61617d 741 gp++;
f5ebcf9e 742 bp = buffer;
2e61617d 743 alldigits = 1;
f5ebcf9e
EW
744 while (*gp && *gp != COMMA) {
745 *bp = *gp++;
746 if (!isdigit(*bp))
747 alldigits = 0;
748 if (bp < buffer + sizeof buffer - 1)
749 bp++;
2e61617d 750 }
f5ebcf9e
EW
751 *bp = 0;
752 pers->officephone = phone(buffer, bp - buffer, alldigits);
753 }
754 if (*gp == COMMA) { /* home phone */
755 gp++;
756 bp = buffer;
757 alldigits = 1;
758 while (*gp && *gp != COMMA) {
2e61617d 759 *bp = *gp++;
f5ebcf9e
EW
760 if (!isdigit(*bp))
761 alldigits = 0;
762 if (bp < buffer + sizeof buffer - 1)
2e61617d 763 bp++;
2e61617d 764 }
f5ebcf9e
EW
765 *bp = 0;
766 pers->homephone = phone(buffer, bp - buffer, alldigits);
2e61617d 767 }
f5ebcf9e
EW
768 if (pers->loggedin)
769 findidle(pers);
770 else
771 findwhen(pers);
2e61617d
BJ
772}
773
f5ebcf9e
EW
774/*
775 * find the last log in of a user by checking the LASTLOG file.
776 * the entry is indexed by the uid, so this can only be done if
777 * the uid is known (which it isn't in quick mode)
2e61617d
BJ
778 */
779
780fwopen()
781{
f5ebcf9e
EW
782 if ((lf = open(LASTLOG, 0)) < 0)
783 fprintf(stderr, "finger: %s open error\n", LASTLOG);
2e61617d
BJ
784}
785
f5ebcf9e
EW
786findwhen(pers)
787 register struct person *pers;
2e61617d 788{
f5ebcf9e
EW
789 struct lastlog ll;
790 int i;
791
792 if (lf >= 0) {
793 lseek(lf, (long)pers->pwd->pw_uid * sizeof ll, 0);
794 if ((i = read(lf, (char *)&ll, sizeof ll)) == sizeof ll) {
795 bcopy(ll.ll_line, pers->tty, LMAX);
796 pers->tty[LMAX] = 0;
797 bcopy(ll.ll_host, pers->host, HMAX);
798 pers->host[HMAX] = 0;
799 pers->loginat = ll.ll_time;
800 } else {
801 if (i != 0)
802 fprintf(stderr, "finger: %s read error\n",
803 LASTLOG);
804 pers->tty[0] = 0;
805 pers->host[0] = 0;
806 pers->loginat = 0L;
807 }
808 } else {
809 pers->tty[0] = 0;
810 pers->host[0] = 0;
2e61617d 811 pers->loginat = 0L;
2e61617d
BJ
812 }
813}
814
2e61617d
BJ
815fwclose()
816{
f5ebcf9e
EW
817 if (lf >= 0)
818 close(lf);
2e61617d
BJ
819}
820
f5ebcf9e
EW
821/*
822 * find the idle time of a user by doing a stat on /dev/tty??,
823 * where tty?? has been gotten from USERLOG, supposedly.
2e61617d 824 */
f5ebcf9e
EW
825findidle(pers)
826 register struct person *pers;
2e61617d 827{
f5ebcf9e
EW
828 struct stat ttystatus;
829 static char buffer[20] = "/dev/";
830 long t;
831#define TTYLEN 5
832
833 strcpy(buffer + TTYLEN, pers->tty);
834 buffer[TTYLEN+LMAX] = 0;
835 if (stat(buffer, &ttystatus) < 0) {
836 fprintf(stderr, "finger: Can't stat %s\n", buffer);
837 exit(4);
2e61617d 838 }
f5ebcf9e
EW
839 time(&t);
840 if (t < ttystatus.st_atime)
841 pers->idletime = 0L;
842 else
843 pers->idletime = t - ttystatus.st_atime;
844 pers->writable = (ttystatus.st_mode & TALKABLE) == TALKABLE;
2e61617d
BJ
845}
846
f5ebcf9e
EW
847/*
848 * print idle time in short format; this program always prints 4 characters;
849 * if the idle time is zero, it prints 4 blanks.
2e61617d 850 */
f5ebcf9e
EW
851stimeprint(dt)
852 long *dt;
2e61617d 853{
f5ebcf9e
EW
854 register struct tm *delta;
855
856 delta = gmtime(dt);
857 if (delta->tm_yday == 0)
858 if (delta->tm_hour == 0)
859 if (delta->tm_min == 0)
860 printf(" ");
861 else
862 printf(" %2d", delta->tm_min);
863 else
864 if (delta->tm_hour >= 10)
865 printf("%3d:", delta->tm_hour);
866 else
867 printf("%1d:%02d",
868 delta->tm_hour, delta->tm_min);
869 else
870 printf("%3dd", delta->tm_yday);
2e61617d
BJ
871}
872
f5ebcf9e
EW
873/*
874 * print idle time in long format with care being taken not to pluralize
875 * 1 minutes or 1 hours or 1 days.
876 * print "prefix" first.
2e61617d 877 */
f5ebcf9e
EW
878ltimeprint(before, dt, after)
879 long *dt;
880 char *before, *after;
2e61617d 881{
f5ebcf9e 882 register struct tm *delta;
2e61617d 883
f5ebcf9e
EW
884 delta = gmtime(dt);
885 if (delta->tm_yday == 0 && delta->tm_hour == 0 && delta->tm_min == 0 &&
886 delta->tm_sec <= 10)
887 return (0);
888 printf("%s", before);
889 if (delta->tm_yday >= 10)
890 printf("%d days", delta->tm_yday);
891 else if (delta->tm_yday > 0)
892 printf("%d day%s %d hour%s",
893 delta->tm_yday, delta->tm_yday == 1 ? "" : "s",
894 delta->tm_hour, delta->tm_hour == 1 ? "" : "s");
895 else
896 if (delta->tm_hour >= 10)
897 printf("%d hours", delta->tm_hour);
898 else if (delta->tm_hour > 0)
899 printf("%d hour%s %d minute%s",
900 delta->tm_hour, delta->tm_hour == 1 ? "" : "s",
901 delta->tm_min, delta->tm_min == 1 ? "" : "s");
902 else
903 if (delta->tm_min >= 10)
904 printf("%2d minutes", delta->tm_min);
905 else if (delta->tm_min == 0)
906 printf("%2d seconds", delta->tm_sec);
907 else
908 printf("%d minute%s %d second%s",
909 delta->tm_min,
910 delta->tm_min == 1 ? "" : "s",
911 delta->tm_sec,
912 delta->tm_sec == 1 ? "" : "s");
913 printf("%s", after);
2e61617d
BJ
914}
915
f5ebcf9e
EW
916matchcmp(gname, login, given)
917 register char *gname;
918 char *login;
919 char *given;
2e61617d 920{
f5ebcf9e
EW
921 char buffer[100];
922 register char *bp, *lp;
923 register c;
924
925 if (*gname == ASTERISK)
926 gname++;
927 lp = 0;
928 bp = buffer;
929 for (;;)
930 switch (c = *gname++) {
931 case SAMENAME:
932 for (lp = login; bp < buffer + sizeof buffer
933 && (*bp++ = *lp++);)
934 ;
935 bp--;
936 break;
937 case ' ':
938 case COMMA:
939 case '\0':
940 *bp = 0;
941 if (namecmp(buffer, given))
942 return (1);
943 if (c == COMMA || c == 0)
944 return (0);
945 bp = buffer;
946 break;
947 default:
948 if (bp < buffer + sizeof buffer)
949 *bp++ = c;
2e61617d 950 }
f5ebcf9e 951 /*NOTREACHED*/
2e61617d
BJ
952}
953
f5ebcf9e
EW
954namecmp(name1, name2)
955 register char *name1, *name2;
2e61617d 956{
f5ebcf9e
EW
957 register c1, c2;
958
959 for (;;) {
960 c1 = *name1++;
961 if (islower(c1))
962 c1 = toupper(c1);
963 c2 = *name2++;
964 if (islower(c2))
965 c2 = toupper(c2);
966 if (c1 != c2)
967 break;
968 if (c1 == 0)
969 return (1);
2e61617d 970 }
f5ebcf9e
EW
971 if (!c1) {
972 for (name2--; isdigit(*name2); name2++)
973 ;
974 if (*name2 == 0)
975 return (1);
976 } else if (!c2) {
977 for (name1--; isdigit(*name1); name1++)
978 ;
979 if (*name2 == 0)
980 return (1);
2e61617d 981 }
f5ebcf9e 982 return (0);
2e61617d 983}
b474790b
SL
984
985netfinger(name)
f5ebcf9e 986 char *name;
b474790b
SL
987{
988 char *host;
989 char fname[100];
990 struct hostent *hp;
991 struct servent *sp;
f5ebcf9e 992 struct sockaddr_in sin;
b474790b
SL
993 int s;
994 char *rindex();
995 register FILE *f;
996 register int c;
997 register int lastc;
998
999 if (name == NULL)
f5ebcf9e 1000 return (0);
b474790b
SL
1001 host = rindex(name, '@');
1002 if (host == NULL)
f5ebcf9e 1003 return (0);
b474790b
SL
1004 *host++ = 0;
1005 hp = gethostbyname(host);
1006 if (hp == NULL) {
1007 static struct hostent def;
1008 static struct in_addr defaddr;
1009 static char namebuf[128];
1010 int inet_addr();
1011
1012 defaddr.s_addr = inet_addr(host);
1013 if (defaddr.s_addr == -1) {
1014 printf("unknown host: %s\n", host);
f5ebcf9e 1015 return (1);
b474790b
SL
1016 }
1017 strcpy(namebuf, host);
1018 def.h_name = namebuf;
1019 def.h_addr = (char *)&defaddr;
1020 def.h_length = sizeof (struct in_addr);
1021 def.h_addrtype = AF_INET;
1022 def.h_aliases = 0;
1023 hp = &def;
1024 }
1025 printf("[%s]", hp->h_name);
1026 sp = getservbyname("finger", "tcp");
1027 if (sp == 0) {
1028 printf("tcp/finger: unknown service\n");
f5ebcf9e 1029 return (1);
b474790b
SL
1030 }
1031 sin.sin_family = hp->h_addrtype;
1032 bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
1033 sin.sin_port = sp->s_port;
1034 s = socket(hp->h_addrtype, SOCK_STREAM, 0);
1035 if (s < 0) {
1036 fflush(stdout);
1037 perror("socket");
f5ebcf9e 1038 return (1);
b474790b
SL
1039 }
1040 if (connect(s, (char *)&sin, sizeof (sin)) < 0) {
1041 fflush(stdout);
1042 perror("connect");
1043 close(s);
f5ebcf9e 1044 return (1);
b474790b
SL
1045 }
1046 printf("\n");
1047 if (large) write(s, "/W ", 3);
1048 write(s, name, strlen(name));
1049 write(s, "\r\n", 2);
1050 f = fdopen(s, "r");
1051 while ((c = getc(f)) != EOF) {
1052 switch(c) {
1053 case 0210:
1054 case 0211:
1055 case 0212:
1056 case 0214:
1057 c -= 0200;
1058 break;
1059 case 0215:
1060 c = '\n';
1061 break;
1062 }
1063 putchar(lastc = c);
1064 }
1065 if (lastc != '\n')
1066 putchar('\n');
a243bfdf 1067 (void)fclose(f);
f5ebcf9e 1068 return (1);
b474790b 1069}