Print this page
10120 smatch indenting fixes for usr/src/cmd
Reviewed by: Gergő Doma <domag02@gmail.com>
Portions contributed by: Joyce McIntosh <joyce.mcintosh@nexenta.com>
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/cmd/cmd-inet/usr.bin/finger.c
+++ new/usr/src/cmd/cmd-inet/usr.bin/finger.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21
22 22 /*
23 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 24 * Use is subject to license terms.
25 25 */
26 26
27 27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
28 28 /* All Rights Reserved */
29 29
30 30 /*
↓ open down ↓ |
30 lines elided |
↑ open up ↑ |
31 31 * Copyright (c) 1982, 1986, 1988
32 32 * The Regents of the University of California
33 33 * All Rights Reserved
34 34 *
35 35 * Portions of this document are derived from
36 36 * software developed by the University of California, Berkeley, and its
37 37 * contributors.
38 38 */
39 39
40 40 /*
41 + * Copyright (c) 2018, Joyent, Inc.
42 + */
43 +
44 +/*
41 45 * This is a finger program. It prints out useful information about users
42 46 * by digging it up from various system files.
43 47 *
44 48 * There are three output formats, all of which give login name, teletype
45 49 * line number, and login time. The short output format is reminiscent
46 50 * of finger on ITS, and gives one line of information per user containing
47 51 * in addition to the minimum basic requirements (MBR), the user's full name,
48 52 * idle time and location.
49 53 * The quick style output is UNIX who-like, giving only name, teletype and
50 54 * login time. Finally, the long style output give the same information
51 55 * as the short (in more legible format), the home directory and shell
52 56 * of the user, and, if it exits, a copy of the file .plan in the users
53 57 * home directory. Finger may be called with or without a list of people
54 58 * to finger -- if no list is given, all the people currently logged in
55 59 * are fingered.
56 60 *
57 61 * The program is validly called by one of the following:
58 62 *
59 63 * finger {short form list of users}
60 64 * finger -l {long form list of users}
61 65 * finger -b {briefer long form list of users}
62 66 * finger -q {quick list of users}
63 67 * finger -i {quick list of users with idle times}
64 68 * finger -m {matches arguments against only username}
65 69 * finger -f {suppress header in non-long form}
66 70 * finger -p {suppress printing of .plan file}
67 71 * finger -h {suppress printing of .project file}
68 72 * finger -i {forces "idle" output format}
69 73 * finger namelist {long format list of specified users}
70 74 * finger -s namelist {short format list of specified users}
71 75 * finger -w namelist {narrow short format list of specified users}
72 76 *
73 77 * where 'namelist' is a list of users login names.
74 78 * The other options can all be given after one '-', or each can have its
75 79 * own '-'. The -f option disables the printing of headers for short and
76 80 * quick outputs. The -b option briefens long format outputs. The -p
77 81 * option turns off plans for long format outputs.
78 82 */
79 83
80 84 #include <sys/types.h>
81 85 #include <sys/stat.h>
82 86 #include <utmpx.h>
83 87 #include <sys/signal.h>
84 88 #include <pwd.h>
85 89 #include <stdio.h>
86 90 #include <lastlog.h>
87 91 #include <ctype.h>
88 92 #include <sys/time.h>
89 93 #include <time.h>
90 94 #include <sys/socket.h>
91 95 #include <netinet/in.h>
92 96 #include <netdb.h>
93 97 #include <locale.h>
94 98 #include <sys/select.h>
95 99 #include <stdlib.h>
96 100 #include <strings.h>
97 101 #include <fcntl.h>
98 102 #include <curses.h>
99 103 #include <unctrl.h>
100 104 #include <maillock.h>
101 105 #include <deflt.h>
102 106 #include <unistd.h>
103 107 #include <arpa/inet.h>
104 108 #include <macros.h>
105 109
106 110 static char gecos_ignore_c = '*'; /* ignore this in real name */
107 111 static char gecos_sep_c = ','; /* separator in pw_gecos field */
108 112 static char gecos_samename = '&'; /* repeat login name in real name */
109 113
110 114 #define TALKABLE 0220 /* tty is writable if this mode */
111 115
112 116 #define NMAX sizeof (((struct utmpx *)0)->ut_name)
113 117 #define LMAX sizeof (((struct utmpx *)0)->ut_line)
114 118 #define HMAX sizeof (((struct utmpx *)0)->ut_host)
115 119
116 120 struct person { /* one for each person fingered */
117 121 char *name; /* name */
118 122 char tty[LMAX+1]; /* null terminated tty line */
119 123 char host[HMAX+1]; /* null terminated remote host name */
120 124 char *ttyloc; /* location of tty line, if any */
121 125 time_t loginat; /* time of (last) login */
122 126 time_t idletime; /* how long idle (if logged in) */
123 127 char *realname; /* pointer to full name */
124 128 struct passwd *pwd; /* structure of /etc/passwd stuff */
125 129 char loggedin; /* person is logged in */
126 130 char writable; /* tty is writable */
127 131 char original; /* this is not a duplicate entry */
128 132 struct person *link; /* link to next person */
129 133 };
130 134
131 135 char LASTLOG[] = "/var/adm/lastlog"; /* last login info */
132 136 char PLAN[] = "/.plan"; /* what plan file is */
133 137 char PROJ[] = "/.project"; /* what project file */
134 138
135 139 int unbrief = 1; /* -b option default */
136 140 int header = 1; /* -f option default */
137 141 int hack = 1; /* -h option default */
138 142 int idle = 0; /* -i option default */
139 143 int large = 0; /* -l option default */
140 144 int match = 1; /* -m option default */
141 145 int plan = 1; /* -p option default */
142 146 int unquick = 1; /* -q option default */
143 147 int small = 0; /* -s option default */
144 148 int wide = 1; /* -w option default */
145 149
146 150 /*
147 151 * RFC 1288 says that system administrators should have the option of
148 152 * separately allowing ASCII characters less than 32 or greater than
149 153 * 126. The termpass variable keeps track of this.
150 154 */
151 155 char defaultfile[] = "/etc/default/finger";
152 156 char passvar[] = "PASS=";
153 157 int termpass = 0; /* default is ASCII only */
154 158 char *termopts[] = {
155 159 #define TERM_LOW 0
156 160 "low",
157 161 #define TERM_HIGH 1
158 162 "high",
159 163 (char *)NULL
160 164 };
161 165 #define TS_LOW (1 << TERM_LOW) /* print characters less than 32 */
162 166 #define TS_HIGH (1 << TERM_HIGH) /* print characters greater than 126 */
163 167
164 168
165 169 int unshort;
166 170 FILE *lf; /* LASTLOG file pointer */
167 171 struct person *person1; /* list of people */
168 172 size_t nperson; /* number of people */
169 173 time_t tloc; /* current time */
170 174
171 175 char usagestr[] = "Usage: "
172 176 "finger [-bfhilmpqsw] [name1 [name2 ...] ]\n";
173 177
174 178 int AlreadyPrinted(uid_t uid);
175 179 void AnyMail(char *name);
176 180 void catfile(char *s, mode_t mode, int trunc_at_nl);
177 181 void decode(struct person *pers);
178 182 void doall(void);
179 183 void donames(char **argv);
180 184 void findidle(struct person *pers);
181 185 void findwhen(struct person *pers);
182 186 void fwclose(void);
183 187 void fwopen(void);
184 188 void initscreening(void);
185 189 void ltimeprint(char *before, time_t *dt, char *after);
186 190 int matchcmp(char *gname, char *login, char *given);
187 191 int namecmp(char *name1, char *name2);
188 192 int netfinger(char *name);
189 193 void personprint(struct person *pers);
190 194 void print(void);
191 195 struct passwd *pwdcopy(const struct passwd *pfrom);
192 196 void quickprint(struct person *pers);
193 197 void shortprint(struct person *pers);
194 198 void stimeprint(time_t *dt);
195 199 void sort_by_username(void);
196 200
197 201
198 202 int
199 203 main(int argc, char **argv)
200 204 {
201 205 int c;
202 206
203 207 (void) setlocale(LC_ALL, "");
204 208 /* parse command line for (optional) arguments */
205 209 while ((c = getopt(argc, argv, "bfhilmpqsw")) != EOF)
206 210 switch (c) {
207 211 case 'b':
208 212 unbrief = 0;
209 213 break;
210 214 case 'f':
211 215 header = 0;
212 216 break;
213 217 case 'h':
214 218 hack = 0;
215 219 break;
216 220 case 'i':
217 221 idle = 1;
218 222 unquick = 0;
219 223 break;
220 224 case 'l':
221 225 large = 1;
222 226 break;
223 227 case 'm':
224 228 match = 0;
225 229 break;
226 230 case 'p':
227 231 plan = 0;
228 232 break;
229 233 case 'q':
230 234 unquick = 0;
231 235 break;
232 236 case 's':
233 237 small = 1;
234 238 break;
235 239 case 'w':
236 240 wide = 0;
237 241 break;
238 242 default:
239 243 (void) fprintf(stderr, usagestr);
240 244 exit(1);
241 245 }
242 246 if (unquick || idle)
243 247 tloc = time(NULL);
244 248
245 249 /* find out what filtering on .plan/.project files we should do */
246 250 initscreening();
247 251
248 252 /*
249 253 * optind == argc means no names given
250 254 */
251 255 if (optind == argc)
252 256 doall();
253 257 else
254 258 donames(&argv[optind]);
255 259
256 260 sort_by_username();
257 261
258 262 if (nperson > 0)
259 263 print();
260 264 return (0);
261 265 /* NOTREACHED */
262 266 }
263 267
264 268 void
265 269 doall(void)
266 270 {
267 271 struct person *p;
268 272 struct passwd *pw;
269 273 struct utmpx *u;
270 274 char name[NMAX + 1];
271 275
272 276 unshort = large;
273 277 setutxent();
274 278 if (unquick) {
275 279 setpwent();
276 280 fwopen();
277 281 }
278 282 while ((u = getutxent()) != NULL) {
279 283 if (u->ut_name[0] == 0 ||
280 284 nonuserx(*u) ||
281 285 u->ut_type != USER_PROCESS)
282 286 continue;
283 287 if (person1 == NULL)
284 288 p = person1 = malloc(sizeof (*p));
285 289 else {
286 290 p->link = malloc(sizeof (*p));
287 291 p = p->link;
288 292 }
289 293 bcopy(u->ut_name, name, NMAX);
290 294 name[NMAX] = 0;
291 295 bcopy(u->ut_line, p->tty, LMAX);
292 296 p->tty[LMAX] = 0;
293 297 bcopy(u->ut_host, p->host, HMAX);
294 298 p->host[HMAX] = 0;
295 299 p->loginat = u->ut_tv.tv_sec;
296 300 p->pwd = NULL;
297 301 p->loggedin = 1;
298 302 if (unquick && (pw = getpwnam(name))) {
299 303 p->pwd = pwdcopy(pw);
300 304 decode(p);
301 305 p->name = p->pwd->pw_name;
302 306 } else
303 307 p->name = strdup(name);
304 308 p->ttyloc = NULL;
305 309
306 310 nperson++;
307 311 }
308 312 if (unquick) {
309 313 fwclose();
310 314 endpwent();
311 315 }
312 316 endutxent();
313 317 if (nperson == 0) {
314 318 (void) printf("No one logged on\n");
315 319 return;
316 320 }
317 321 p->link = NULL;
318 322 }
319 323
320 324 void
321 325 donames(char **argv)
322 326 {
323 327 struct person *p;
324 328 struct passwd *pw;
325 329 struct utmpx *u;
326 330
327 331 /*
328 332 * get names from command line and check to see if they're
329 333 * logged in
330 334 */
331 335 unshort = !small;
332 336 for (; *argv != NULL; argv++) {
333 337 if (netfinger(*argv))
334 338 continue;
335 339 if (person1 == NULL)
336 340 p = person1 = malloc(sizeof (*p));
337 341 else {
338 342 p->link = malloc(sizeof (*p));
339 343 p = p->link;
340 344 }
341 345 p->name = *argv;
342 346 p->loggedin = 0;
343 347 p->original = 1;
344 348 p->pwd = NULL;
345 349
346 350 nperson++;
347 351 }
348 352 if (nperson == 0)
349 353 return;
350 354 p->link = NULL;
351 355 /*
352 356 * if we are doing it, read /etc/passwd for the useful info
353 357 */
354 358 if (unquick) {
355 359 setpwent();
356 360 if (!match) {
357 361 for (p = person1; p != NULL; p = p->link) {
358 362 if ((pw = getpwnam(p->name)) != NULL)
359 363 p->pwd = pwdcopy(pw);
360 364 }
361 365 } else {
362 366 while ((pw = getpwent()) != NULL) {
363 367 for (p = person1; p != NULL; p = p->link) {
364 368 if (!p->original)
365 369 continue;
366 370 if (strcmp(p->name, pw->pw_name) != 0 &&
367 371 !matchcmp(pw->pw_gecos, pw->pw_name,
368 372 p->name)) {
369 373 continue;
370 374 }
371 375 if (p->pwd == NULL) {
372 376 p->pwd = pwdcopy(pw);
373 377 } else {
374 378 struct person *new;
375 379 /*
376 380 * Handle multiple login names.
377 381 * Insert new "duplicate" entry
378 382 * behind.
379 383 */
380 384 new = malloc(sizeof (*new));
381 385 new->pwd = pwdcopy(pw);
382 386 new->name = p->name;
383 387 new->original = 1;
384 388 new->loggedin = 0;
385 389 new->ttyloc = NULL;
386 390 new->link = p->link;
387 391 p->original = 0;
388 392 p->link = new;
389 393 p = new;
390 394
391 395 nperson++;
392 396 }
393 397 }
394 398 }
395 399 }
396 400 endpwent();
397 401 }
398 402 /* Now get login information */
399 403 setutxent();
400 404 while ((u = getutxent()) != NULL) {
401 405 if (u->ut_name[0] == 0 || u->ut_type != USER_PROCESS)
402 406 continue;
403 407 for (p = person1; p != NULL; p = p->link) {
404 408 p->ttyloc = NULL;
405 409 if (p->loggedin == 2)
406 410 continue;
407 411 if (strncmp((p->pwd != NULL) ?
408 412 p->pwd->pw_name : p->name,
409 413 u->ut_name, NMAX) != 0)
410 414 continue;
411 415 if (p->loggedin == 0) {
412 416 bcopy(u->ut_line, p->tty, LMAX);
413 417 p->tty[LMAX] = 0;
414 418 bcopy(u->ut_host, p->host, HMAX);
415 419 p->host[HMAX] = 0;
416 420 p->loginat = u->ut_tv.tv_sec;
417 421 p->loggedin = 1;
418 422 } else { /* p->loggedin == 1 */
419 423 struct person *new;
420 424 new = malloc(sizeof (*new));
421 425 new->name = p->name;
422 426 bcopy(u->ut_line, new->tty, LMAX);
423 427 new->tty[LMAX] = 0;
424 428 bcopy(u->ut_host, new->host, HMAX);
425 429 new->host[HMAX] = 0;
426 430 new->loginat = u->ut_tv.tv_sec;
427 431 new->pwd = p->pwd;
428 432 new->loggedin = 1;
429 433 new->original = 0;
430 434 new->link = p->link;
431 435 p->loggedin = 2;
432 436 p->link = new;
433 437 p = new;
434 438
435 439 nperson++;
436 440 }
437 441 }
438 442 }
439 443 endutxent();
440 444 if (unquick) {
441 445 fwopen();
442 446 for (p = person1; p != NULL; p = p->link)
443 447 decode(p);
444 448 fwclose();
445 449 }
446 450 }
447 451
448 452 void
449 453 print(void)
450 454 {
451 455 struct person *p;
452 456 char *s;
453 457
454 458 /*
455 459 * print out what we got
456 460 */
457 461 if (header) {
458 462 if (unquick) {
459 463 if (!unshort) {
460 464 if (wide) {
461 465 (void) printf("Login "
462 466 "Name TTY "
463 467 "Idle When Where\n");
464 468 } else {
465 469 (void) printf("Login TTY Idle "
466 470 "When Where\n");
467 471 }
468 472 }
469 473 } else {
470 474 (void) printf("Login TTY When");
471 475 if (idle)
472 476 (void) printf(" Idle");
473 477 (void) putchar('\n');
474 478 }
475 479 }
476 480 for (p = person1; p != NULL; p = p->link) {
477 481 if (!unquick) {
478 482 quickprint(p);
479 483 continue;
480 484 }
481 485 if (!unshort) {
482 486 shortprint(p);
483 487 continue;
484 488 }
485 489 personprint(p);
486 490 if (p->pwd != NULL && !AlreadyPrinted(p->pwd->pw_uid)) {
487 491 AnyMail(p->pwd->pw_name);
488 492 if (hack) {
489 493 struct stat sbuf;
490 494
491 495 s = malloc(strlen(p->pwd->pw_dir) +
492 496 sizeof (PROJ));
493 497 if (s != NULL) {
494 498 (void) strcpy(s, p->pwd->pw_dir);
495 499 (void) strcat(s, PROJ);
496 500 if (stat(s, &sbuf) != -1 &&
497 501 (S_ISREG(sbuf.st_mode) ||
498 502 S_ISFIFO(sbuf.st_mode)) &&
499 503 (sbuf.st_mode & S_IROTH)) {
500 504 (void) printf("Project: ");
501 505 catfile(s, sbuf.st_mode, 1);
502 506 (void) putchar('\n');
503 507 }
504 508 free(s);
505 509 }
506 510 }
507 511 if (plan) {
508 512 struct stat sbuf;
509 513
510 514 s = malloc(strlen(p->pwd->pw_dir) +
511 515 sizeof (PLAN));
512 516 if (s != NULL) {
513 517 (void) strcpy(s, p->pwd->pw_dir);
514 518 (void) strcat(s, PLAN);
515 519 if (stat(s, &sbuf) == -1 ||
516 520 (!S_ISREG(sbuf.st_mode) &&
517 521 !S_ISFIFO(sbuf.st_mode)) ||
518 522 ((sbuf.st_mode & S_IROTH) == 0))
519 523 (void) printf("No Plan.\n");
520 524 else {
521 525 (void) printf("Plan:\n");
522 526 catfile(s, sbuf.st_mode, 0);
523 527 }
524 528 free(s);
525 529 }
526 530 }
527 531 }
528 532 if (p->link != NULL)
529 533 (void) putchar('\n');
530 534 }
531 535 }
532 536
533 537 /*
534 538 * Duplicate a pwd entry.
535 539 * Note: Only the useful things (what the program currently uses) are copied.
536 540 */
537 541 struct passwd *
538 542 pwdcopy(const struct passwd *pfrom)
539 543 {
540 544 struct passwd *pto;
541 545
542 546 pto = malloc(sizeof (*pto));
543 547 pto->pw_name = strdup(pfrom->pw_name);
544 548 pto->pw_uid = pfrom->pw_uid;
545 549 pto->pw_gecos = strdup(pfrom->pw_gecos);
546 550 pto->pw_dir = strdup(pfrom->pw_dir);
547 551 pto->pw_shell = strdup(pfrom->pw_shell);
548 552 return (pto);
549 553 }
550 554
551 555 /*
552 556 * print out information on quick format giving just name, tty, login time
553 557 * and idle time if idle is set.
554 558 */
555 559 void
556 560 quickprint(struct person *pers)
557 561 {
558 562 (void) printf("%-8.8s ", pers->name);
559 563 if (pers->loggedin) {
560 564 if (idle) {
561 565 findidle(pers);
562 566 (void) printf("%c%-12s %-16.16s",
563 567 pers->writable ? ' ' : '*',
564 568 pers->tty, ctime(&pers->loginat));
565 569 ltimeprint(" ", &pers->idletime, "");
566 570 } else {
567 571 (void) printf(" %-12s %-16.16s",
568 572 pers->tty, ctime(&pers->loginat));
569 573 }
570 574 (void) putchar('\n');
571 575 } else {
572 576 (void) printf(" Not Logged In\n");
573 577 }
574 578 }
575 579
576 580 /*
577 581 * print out information in short format, giving login name, full name,
578 582 * tty, idle time, login time, and host.
579 583 */
580 584 void
581 585 shortprint(struct person *pers)
582 586 {
583 587 char *p;
584 588
585 589 if (pers->pwd == NULL) {
586 590 (void) printf("%-15s ???\n", pers->name);
587 591 return;
588 592 }
589 593 (void) printf("%-8s", pers->pwd->pw_name);
590 594 if (wide) {
591 595 if (pers->realname != NULL) {
592 596 (void) printf(" %-20.20s", pers->realname);
593 597 } else {
594 598 (void) printf(" ??? ");
595 599 }
596 600 }
597 601 (void) putchar(' ');
598 602 if (pers->loggedin && !pers->writable) {
599 603 (void) putchar('*');
600 604 } else {
601 605 (void) putchar(' ');
602 606 }
603 607 if (*pers->tty) {
604 608 (void) printf("%-11.11s ", pers->tty);
605 609 } else {
606 610 (void) printf(" "); /* 12 spaces */
607 611 }
608 612 p = ctime(&pers->loginat);
609 613 if (pers->loggedin) {
610 614 stimeprint(&pers->idletime);
611 615 (void) printf(" %3.3s %-5.5s ", p, p + 11);
612 616 } else if (pers->loginat == 0) {
613 617 (void) printf(" < . . . . >");
614 618 } else if (tloc - pers->loginat >= 180 * 24 * 60 * 60) {
615 619 (void) printf(" <%-6.6s, %-4.4s>", p + 4, p + 20);
616 620 } else {
617 621 (void) printf(" <%-12.12s>", p + 4);
618 622 }
619 623 if (*pers->host) {
620 624 (void) printf(" %-20.20s", pers->host);
621 625 } else {
622 626 if (pers->ttyloc != NULL)
623 627 (void) printf(" %-20.20s", pers->ttyloc);
624 628 }
625 629 (void) putchar('\n');
626 630 }
627 631
628 632
629 633 /*
630 634 * print out a person in long format giving all possible information.
631 635 * directory and shell are inhibited if unbrief is clear.
632 636 */
633 637 void
634 638 personprint(struct person *pers)
635 639 {
636 640 if (pers->pwd == NULL) {
637 641 (void) printf("Login name: %-10s\t\t\tIn real life: ???\n",
638 642 pers->name);
639 643 return;
640 644 }
641 645 (void) printf("Login name: %-10s", pers->pwd->pw_name);
642 646 if (pers->loggedin && !pers->writable) {
643 647 (void) printf(" (messages off) ");
644 648 } else {
645 649 (void) printf(" ");
646 650 }
647 651 if (pers->realname != NULL) {
648 652 (void) printf("In real life: %s", pers->realname);
649 653 }
650 654 if (unbrief) {
651 655 (void) printf("\nDirectory: %-25s", pers->pwd->pw_dir);
652 656 if (*pers->pwd->pw_shell)
653 657 (void) printf("\tShell: %-s", pers->pwd->pw_shell);
654 658 }
655 659 if (pers->loggedin) {
656 660 char *ep = ctime(&pers->loginat);
657 661 if (*pers->host) {
658 662 (void) printf("\nOn since %15.15s on %s from %s",
659 663 &ep[4], pers->tty, pers->host);
660 664 ltimeprint("\n", &pers->idletime, " Idle Time");
661 665 } else {
662 666 (void) printf("\nOn since %15.15s on %-12s",
663 667 &ep[4], pers->tty);
664 668 ltimeprint("\n", &pers->idletime, " Idle Time");
665 669 }
666 670 } else if (pers->loginat == 0) {
667 671 (void) printf("\nNever logged in.");
668 672 } else if (tloc - pers->loginat > 180 * 24 * 60 * 60) {
669 673 char *ep = ctime(&pers->loginat);
670 674 (void) printf("\nLast login %10.10s, %4.4s on %s",
671 675 ep, ep+20, pers->tty);
672 676 if (*pers->host) {
673 677 (void) printf(" from %s", pers->host);
674 678 }
675 679 } else {
676 680 char *ep = ctime(&pers->loginat);
677 681 (void) printf("\nLast login %16.16s on %s", ep, pers->tty);
678 682 if (*pers->host) {
679 683 (void) printf(" from %s", pers->host);
680 684 }
681 685 }
682 686 (void) putchar('\n');
683 687 }
684 688
685 689
686 690 /*
687 691 * decode the information in the gecos field of /etc/passwd
688 692 */
689 693 void
690 694 decode(struct person *pers)
691 695 {
692 696 char buffer[256];
693 697 char *bp, *gp, *lp;
694 698
695 699 pers->realname = NULL;
696 700 if (pers->pwd == NULL)
697 701 return;
698 702 gp = pers->pwd->pw_gecos;
699 703 bp = buffer;
700 704
701 705 if (gecos_ignore_c != '\0' &&
702 706 *gp == gecos_ignore_c) {
703 707 gp++;
704 708 }
705 709 while (*gp != '\0' &&
706 710 *gp != gecos_sep_c) { /* name */
707 711 if (*gp == gecos_samename) {
708 712 lp = pers->pwd->pw_name;
709 713 if (islower(*lp))
710 714 *bp++ = toupper(*lp++);
711 715 while (*bp++ = *lp++)
712 716 ;
713 717 bp--;
714 718 gp++;
715 719 } else {
716 720 *bp++ = *gp++;
717 721 }
718 722 }
719 723 *bp++ = 0;
720 724 if (bp > (buffer + 1))
721 725 pers->realname = strdup(buffer);
722 726 if (pers->loggedin)
723 727 findidle(pers);
724 728 else
725 729 findwhen(pers);
726 730 }
727 731
728 732 /*
729 733 * find the last log in of a user by checking the LASTLOG file.
730 734 * the entry is indexed by the uid, so this can only be done if
731 735 * the uid is known (which it isn't in quick mode)
732 736 */
733 737 void
734 738 fwopen(void)
735 739 {
736 740 if ((lf = fopen(LASTLOG, "r")) == NULL)
737 741 (void) fprintf(stderr, "finger: %s open error\n", LASTLOG);
738 742 }
739 743
740 744 void
741 745 findwhen(struct person *pers)
742 746 {
743 747 struct lastlog ll;
744 748
745 749 if (lf != NULL) {
746 750 if (fseeko(lf, (off_t)pers->pwd->pw_uid * (off_t)sizeof (ll),
747 751 SEEK_SET) == 0) {
748 752 if (fread((char *)&ll, sizeof (ll), 1, lf) == 1) {
749 753 int l_max, h_max;
750 754
751 755 l_max = min(LMAX, sizeof (ll.ll_line));
752 756 h_max = min(HMAX, sizeof (ll.ll_host));
753 757
754 758 bcopy(ll.ll_line, pers->tty, l_max);
755 759 pers->tty[l_max] = '\0';
756 760 bcopy(ll.ll_host, pers->host, h_max);
757 761 pers->host[h_max] = '\0';
758 762 pers->loginat = ll.ll_time;
759 763 } else {
760 764 if (ferror(lf))
761 765 (void) fprintf(stderr,
762 766 "finger: %s read error\n", LASTLOG);
763 767 pers->tty[0] = 0;
764 768 pers->host[0] = 0;
765 769 pers->loginat = 0L;
766 770 }
767 771 } else {
768 772 (void) fprintf(stderr, "finger: %s fseeko error\n",
769 773 LASTLOG);
770 774 }
771 775 } else {
772 776 pers->tty[0] = 0;
773 777 pers->host[0] = 0;
774 778 pers->loginat = 0L;
775 779 }
776 780 }
777 781
778 782 void
779 783 fwclose(void)
780 784 {
781 785 if (lf != NULL)
782 786 (void) fclose(lf);
783 787 }
784 788
785 789 /*
786 790 * find the idle time of a user by doing a stat on /dev/tty??,
787 791 * where tty?? has been gotten from UTMPX_FILE, supposedly.
788 792 */
789 793 void
790 794 findidle(struct person *pers)
791 795 {
792 796 struct stat ttystatus;
793 797 struct stat inputdevstatus;
794 798 #define TTYLEN (sizeof ("/dev/") - 1)
795 799 static char buffer[TTYLEN + LMAX + 1] = "/dev/";
796 800 time_t t;
797 801 time_t lastinputtime;
798 802
799 803 (void) strcpy(buffer + TTYLEN, pers->tty);
800 804 buffer[TTYLEN+LMAX] = 0;
801 805 if (stat(buffer, &ttystatus) < 0) {
802 806 (void) fprintf(stderr, "finger: Can't stat %s\n", buffer);
803 807 exit(4);
804 808 }
805 809 lastinputtime = ttystatus.st_atime;
806 810 if (strcmp(pers->tty, "console") == 0) {
807 811 /*
808 812 * On the console, the user may be running a window system; if
809 813 * so, their activity will show up in the last-access times of
810 814 * "/dev/kbd" and "/dev/mouse", so take the minimum of the idle
811 815 * times on those two devices and "/dev/console" and treat that
812 816 * as the idle time.
813 817 */
814 818 if (stat("/dev/kbd", &inputdevstatus) == 0) {
815 819 if (lastinputtime < inputdevstatus.st_atime)
816 820 lastinputtime = inputdevstatus.st_atime;
817 821 }
818 822 if (stat("/dev/mouse", &inputdevstatus) == 0) {
819 823 if (lastinputtime < inputdevstatus.st_atime)
820 824 lastinputtime = inputdevstatus.st_atime;
821 825 }
822 826 }
823 827 t = time(NULL);
824 828 if (t < lastinputtime)
825 829 pers->idletime = (time_t)0;
826 830 else
827 831 pers->idletime = t - lastinputtime;
828 832 pers->writable = (ttystatus.st_mode & TALKABLE) == TALKABLE;
829 833 }
830 834
831 835 /*
832 836 * print idle time in short format; this program always prints 4 characters;
833 837 * if the idle time is zero, it prints 4 blanks.
834 838 */
835 839 void
836 840 stimeprint(time_t *dt)
837 841 {
838 842 struct tm *delta;
839 843
840 844 delta = gmtime(dt);
841 845 if (delta->tm_yday == 0)
842 846 if (delta->tm_hour == 0)
843 847 if (delta->tm_min == 0)
844 848 (void) printf(" ");
845 849 else
846 850 (void) printf(" %2d", delta->tm_min);
847 851 else
848 852 if (delta->tm_hour >= 10)
849 853 (void) printf("%3d:", delta->tm_hour);
850 854 else
851 855 (void) printf("%1d:%02d",
852 856 delta->tm_hour, delta->tm_min);
853 857 else
854 858 (void) printf("%3dd", delta->tm_yday);
855 859 }
856 860
857 861 /*
858 862 * print idle time in long format with care being taken not to pluralize
859 863 * 1 minutes or 1 hours or 1 days.
860 864 * print "prefix" first.
861 865 */
862 866 void
863 867 ltimeprint(char *before, time_t *dt, char *after)
864 868 {
865 869 struct tm *delta;
866 870
867 871 delta = gmtime(dt);
868 872 if (delta->tm_yday == 0 && delta->tm_hour == 0 && delta->tm_min == 0 &&
869 873 delta->tm_sec <= 10)
870 874 return;
871 875 (void) printf("%s", before);
872 876 if (delta->tm_yday >= 10)
873 877 (void) printf("%d days", delta->tm_yday);
874 878 else if (delta->tm_yday > 0)
875 879 (void) printf("%d day%s %d hour%s",
876 880 delta->tm_yday, delta->tm_yday == 1 ? "" : "s",
877 881 delta->tm_hour, delta->tm_hour == 1 ? "" : "s");
878 882 else
879 883 if (delta->tm_hour >= 10)
880 884 (void) printf("%d hours", delta->tm_hour);
881 885 else if (delta->tm_hour > 0)
882 886 (void) printf("%d hour%s %d minute%s",
883 887 delta->tm_hour, delta->tm_hour == 1 ? "" : "s",
884 888 delta->tm_min, delta->tm_min == 1 ? "" : "s");
885 889 else
886 890 if (delta->tm_min >= 10)
887 891 (void) printf("%2d minutes", delta->tm_min);
888 892 else if (delta->tm_min == 0)
889 893 (void) printf("%2d seconds", delta->tm_sec);
890 894 else
891 895 (void) printf("%d minute%s %d second%s",
892 896 delta->tm_min,
893 897 delta->tm_min == 1 ? "" : "s",
894 898 delta->tm_sec,
895 899 delta->tm_sec == 1 ? "" : "s");
896 900 (void) printf("%s", after);
897 901 }
898 902
899 903 /*
900 904 * The grammar of the pw_gecos field is sufficiently complex that the
901 905 * best way to parse it is by using an explicit finite-state machine,
902 906 * in which a table defines the rules of interpretation.
903 907 *
904 908 * Some special rules are necessary to handle the fact that names
905 909 * may contain certain punctuation characters. At this writing,
906 910 * the possible punctuation characters are '.', '-', and '_'.
907 911 *
908 912 * Other rules are needed to account for characters that require special
909 913 * processing when they appear in the pw_gecos field. At present, there
910 914 * are three such characters, with these default values and effects:
911 915 *
912 916 * gecos_ignore_c '*' This character is ignored.
913 917 * gecos_sep_c ',' Delimits displayed and nondisplayed contents.
914 918 * gecos_samename '&' Copies the login name into the output.
915 919 *
916 920 * As the program examines each successive character in the returned
917 921 * pw_gecos value, it fetches (from the table) the FSM rule applicable
918 922 * for that character in the current machine state, and thus determines
919 923 * the next state.
920 924 *
921 925 * The possible states are:
922 926 * S0 start
923 927 * S1 in a word
924 928 * S2 not in a word
925 929 * S3 copy login name into output
926 930 * S4 end of GECOS field
927 931 *
928 932 * Here follows a depiction of the state transitions.
929 933 *
930 934 *
931 935 * gecos_ignore_c OR isspace OR any other character
932 936 * +--+
933 937 * | |
934 938 * | V
935 939 * +-----+
936 940 * NULL OR | S0 | isalpha OR isdigit
937 941 * +---------------|start|------------------------+
938 942 * | gecos_sep_c +-----+ | isalpha OR isdigit
939 943 * | | | | +---------------------+
940 944 * | | | | | OR '.' '-' '_' |
941 945 * | | |isspace | | |
942 946 * | | +-------+ V V |
943 947 * | | | +-----------+ |
944 948 * | | | | S1 |<--+ |
945 949 * | | | | in a word | | isalpha OR |
946 950 * | | | +-----------+ | isdigit OR |
947 951 * | | | | | | | | '.' '-' '_' |
948 952 * | | +----- ---------------+ | | +-----+ |
949 953 * | | | | | | |
950 954 * | | | | gecos_ignore_c | | |
951 955 * | | | | isspace | | |
952 956 * | | | | ispunct/other | | |
953 957 * | | | | any other char | | |
954 958 * | | | | +---------------+ | |
955 959 * | | | | | |NULL OR gecos_sep_c |
956 960 * | | | | | +------------------+ |
957 961 * | gecos_samename| | V V | |
958 962 * | +-------------+ | +---------------+ | |
959 963 * | | | | S2 | isspace OR '.' '-' '_' | |
960 964 * | | gecos_samename | | not in a word |<---------------------+ | |
961 965 * | | +---------------+ +---------------+ OR gecos_ignore_c | | |
962 966 * | | | | ^ | | OR ispunct OR other | | |
963 967 * | | | | | | | | | |
964 968 * | | | gecos_samename | | | +-----------------------+ | |
965 969 * | | | +---------------------+ | | | |
966 970 * | | | | | | | |
967 971 * | | | | gecos_ignore_c| | NULL OR gecos_sep_c | |
968 972 * | | | | gecos_samename| +-----------------------+ | |
969 973 * | | | | ispunct/other | | | |
970 974 * | V V V isspace | | | |
971 975 * | +-----------------+ any other char| | | |
972 976 * | | S3 |---------------+ isalpha OR isdigit OR | | |
973 977 * | |insert login name|------------------------------------------ ----- ---+
974 978 * | +-----------------+ '.' '-' '_' | |
975 979 * | | NULL OR gecos_sep_c | |
976 980 * | +------------------------------------------+ | |
977 981 * | | | |
978 982 * | V V V
979 983 * | +------------+
980 984 * | NULL OR gecos_sep_c | S4 |
981 985 * +-------------------------------------------------------->|end of gecos|<--+
982 986 * +------------+ |
983 987 * | all |
984 988 * +-----+
985 989 *
986 990 *
987 991 * The transitions from the above diagram are summarized in
988 992 * the following table of target states, which is implemented
989 993 * in code as the gecos_fsm array.
990 994 *
991 995 * Input:
992 996 * +--gecos_ignore_c
993 997 * | +--gecos_sep_c
994 998 * | | +--gecos_samename
995 999 * | | | +--isalpha
996 1000 * | | | | +--isdigit
997 1001 * | | | | | +--isspace
998 1002 * | | | | | | +--punctuation possible in name
999 1003 * | | | | | | | +--other punctuation
1000 1004 * | | | | | | | | +--NULL character
1001 1005 * | | | | | | | | | +--any other character
1002 1006 * | | | | | | | | | |
1003 1007 * V V V V V V V V V V
1004 1008 * From: ---------------------------------------------------
1005 1009 * S0 | S0 | S4 | S3 | S1 | S1 | S0 | S1 | S2 | S4 | S0 |
1006 1010 * S1 | S2 | S4 | S3 | S1 | S1 | S2 | S1 | S2 | S4 | S2 |
1007 1011 * S2 | S2 | S4 | S3 | S1 | S1 | S2 | S2 | S2 | S4 | S2 |
1008 1012 * S3 | S2 | S4 | S2 | S1 | S1 | S2 | S1 | S2 | S4 | S2 |
1009 1013 * S4 | S4 | S4 | S4 | S4 | S4 | S4 | S4 | S4 | S4 | S4 |
1010 1014 *
1011 1015 */
1012 1016
1013 1017 /*
1014 1018 * Data types and structures for scanning the pw_gecos field.
1015 1019 */
1016 1020 typedef enum gecos_state {
1017 1021 S0, /* start */
1018 1022 S1, /* in a word */
1019 1023 S2, /* not in a word */
1020 1024 S3, /* copy login */
1021 1025 S4 /* end of gecos */
1022 1026 } gecos_state_t;
1023 1027
1024 1028 #define GFSM_ROWS 5
1025 1029 #define GFSM_COLS 10
1026 1030
1027 1031 gecos_state_t gecos_fsm[GFSM_ROWS][GFSM_COLS] = {
1028 1032 {S0, S4, S3, S1, S1, S0, S1, S2, S4, S0}, /* S0 */
1029 1033 {S2, S4, S3, S1, S1, S2, S1, S2, S4, S2}, /* S1 */
1030 1034 {S2, S4, S3, S1, S1, S2, S2, S2, S4, S2}, /* S2 */
1031 1035 {S2, S4, S2, S1, S1, S2, S1, S2, S4, S2}, /* S3 */
1032 1036 {S4, S4, S4, S4, S4, S4, S4, S4, S4, S4} /* S4 */
1033 1037 };
1034 1038
1035 1039 /*
1036 1040 * Scan the pw_gecos field according to defined state table;
1037 1041 * return the next state according the the rules.
1038 1042 */
1039 1043 gecos_state_t
1040 1044 gecos_scan_state(gecos_state_t instate, char ch)
1041 1045 {
1042 1046 if (ch == gecos_ignore_c) {
1043 1047 return (gecos_fsm[instate][0]);
1044 1048 } else if (ch == gecos_sep_c) {
1045 1049 return (gecos_fsm[instate][1]);
1046 1050 } else if (ch == gecos_samename) {
1047 1051 return (gecos_fsm[instate][2]);
1048 1052 } else if (isalpha(ch)) {
1049 1053 return (gecos_fsm[instate][3]);
1050 1054 } else if (isdigit(ch)) {
1051 1055 return (gecos_fsm[instate][4]);
1052 1056 } else if (isspace(ch)) {
1053 1057 return (gecos_fsm[instate][5]);
1054 1058 } else if (ch == '.' || ch == '-' || ch == '_') {
1055 1059 return (gecos_fsm[instate][6]);
1056 1060 } else if (ispunct(ch)) {
1057 1061 return (gecos_fsm[instate][7]);
1058 1062 } else if (ch == '\0') {
1059 1063 return (gecos_fsm[instate][8]);
1060 1064 }
1061 1065 return (gecos_fsm[instate][9]);
1062 1066 }
1063 1067
1064 1068
1065 1069 /*
1066 1070 * Compare the given argument, which is taken to be a username, with
1067 1071 * the login name and with strings in the the pw_gecos field.
1068 1072 */
1069 1073 int
1070 1074 matchcmp(char *gname, char *login, char *given)
1071 1075 {
1072 1076 char buffer[100];
1073 1077 char *bp, *lp, *gp;
1074 1078
1075 1079 gecos_state_t kstate = S0;
1076 1080 gecos_state_t kstate_next = S0;
1077 1081
1078 1082 if (*gname == '\0' && *given == '\0')
1079 1083 return (1);
1080 1084
1081 1085 bp = buffer;
1082 1086 gp = gname;
1083 1087
1084 1088 do {
1085 1089 kstate_next = gecos_scan_state(kstate, *gp);
1086 1090
1087 1091 switch (kstate_next) {
1088 1092
1089 1093 case S0:
1090 1094 gp++;
1091 1095 break;
1092 1096 case S1:
1093 1097 if (bp < buffer + sizeof (buffer)) {
1094 1098 *bp++ = *gp++;
1095 1099 }
1096 1100 break;
1097 1101 case S2:
1098 1102 if (kstate == S1 || kstate == S3) {
1099 1103 *bp++ = ' ';
1100 1104 }
1101 1105 gp++;
1102 1106 break;
1103 1107 case S3:
1104 1108 lp = login;
1105 1109 do {
1106 1110 *bp++ = *lp++;
1107 1111 } while (*bp != '\0' && bp < buffer + sizeof (buffer));
1108 1112 bp--;
1109 1113 break;
1110 1114 case S4:
1111 1115 *bp++ = '\0';
1112 1116 break;
1113 1117 default:
1114 1118 *bp++ = '\0';
1115 1119 break;
1116 1120 }
1117 1121 kstate = kstate_next;
1118 1122
1119 1123 } while ((bp < buffer + sizeof (buffer)) && kstate != S4);
1120 1124
1121 1125 gp = strtok(buffer, " ");
1122 1126
1123 1127 while (gp != NULL) {
1124 1128 if (namecmp(gp, given) > 0) {
1125 1129 return (1);
1126 1130 }
1127 1131 gp = strtok(NULL, " ");
1128 1132 }
1129 1133 return (0);
1130 1134 }
1131 1135
1132 1136 /*
1133 1137 * Perform the character-by-character comparison.
1134 1138 * It is intended that "finger foo" should match "foo2", but an argument
1135 1139 * consisting entirely of digits should not be matched too broadly.
1136 1140 * Also, we do not want "finger foo123" to match "Mr. Foo" in the gecos.
1137 1141 */
1138 1142 int
1139 1143 namecmp(char *name1, char *name2)
1140 1144 {
1141 1145 char c1, c2;
1142 1146 boolean_t alphaseen = B_FALSE;
1143 1147 boolean_t digitseen = B_FALSE;
1144 1148
1145 1149 for (;;) {
1146 1150 c1 = *name1++;
1147 1151 if (isalpha(c1))
1148 1152 alphaseen = B_TRUE;
1149 1153 if (isdigit(c1))
1150 1154 digitseen = B_TRUE;
1151 1155 if (isupper(c1))
1152 1156 c1 = tolower(c1);
1153 1157
1154 1158 c2 = *name2++;
1155 1159 if (isupper(c2))
1156 1160 c2 = tolower(c2);
1157 1161
1158 1162 if (c1 != c2)
1159 1163 break;
1160 1164 if (c1 == '\0')
1161 1165 return (1);
1162 1166 }
1163 1167 if (!c1) {
1164 1168 for (name2--; isdigit(*name2); name2++)
1165 1169 ;
1166 1170 if (*name2 == '\0' && digitseen) {
1167 1171 return (1);
1168 1172 }
1169 1173 } else if (!c2) {
1170 1174 for (name1--; isdigit(*name1); name1++)
1171 1175 ;
1172 1176 if (*name1 == '\0' && alphaseen) {
1173 1177 return (1);
1174 1178 }
1175 1179 }
1176 1180 return (0);
1177 1181 }
1178 1182
1179 1183
1180 1184 int
1181 1185 netfinger(char *name)
1182 1186 {
1183 1187 char *host;
1184 1188 struct hostent *hp;
1185 1189 struct sockaddr_in6 sin6;
1186 1190 struct in6_addr ipv6addr;
1187 1191 struct in_addr ipv4addr;
1188 1192 int s;
1189 1193 FILE *f;
1190 1194 int c;
1191 1195 int lastc;
1192 1196 char abuf[INET6_ADDRSTRLEN];
1193 1197 int error_num;
1194 1198
1195 1199 if (name == NULL)
1196 1200 return (0);
1197 1201 host = strrchr(name, '@');
1198 1202 if (host == NULL)
1199 1203 return (0);
1200 1204 *host++ = 0;
1201 1205
1202 1206 if ((hp = getipnodebyname(host, AF_INET6, AI_ALL | AI_ADDRCONFIG |
1203 1207 AI_V4MAPPED, &error_num)) == NULL) {
1204 1208 if (error_num == TRY_AGAIN) {
1205 1209 (void) fprintf(stderr,
1206 1210 "unknown host: %s (try again later)\n", host);
1207 1211 } else {
1208 1212 (void) fprintf(stderr, "unknown host: %s\n", host);
1209 1213 }
1210 1214 return (1);
1211 1215 }
1212 1216
1213 1217 /*
1214 1218 * If hp->h_name is a IPv4-mapped IPv6 literal, we'll convert it to
1215 1219 * IPv4 literal address.
1216 1220 */
1217 1221 if ((inet_pton(AF_INET6, hp->h_name, &ipv6addr) > 0) &&
1218 1222 IN6_IS_ADDR_V4MAPPED(&ipv6addr)) {
1219 1223 IN6_V4MAPPED_TO_INADDR(&ipv6addr, &ipv4addr);
1220 1224 (void) printf("[%s] ", inet_ntop(AF_INET, &ipv4addr, abuf,
1221 1225 sizeof (abuf)));
1222 1226 } else {
1223 1227 (void) printf("[%s] ", hp->h_name);
1224 1228 }
1225 1229 bzero(&sin6, sizeof (sin6));
1226 1230 sin6.sin6_family = hp->h_addrtype;
1227 1231 bcopy(hp->h_addr_list[0], (char *)&sin6.sin6_addr, hp->h_length);
1228 1232 sin6.sin6_port = htons(IPPORT_FINGER);
1229 1233 s = socket(sin6.sin6_family, SOCK_STREAM, 0);
1230 1234 if (s < 0) {
1231 1235 (void) fflush(stdout);
1232 1236 perror("socket");
1233 1237 freehostent(hp);
1234 1238 return (1);
1235 1239 }
1236 1240 while (connect(s, (struct sockaddr *)&sin6, sizeof (sin6)) < 0) {
1237 1241
1238 1242 if (hp && hp->h_addr_list[1]) {
1239 1243
1240 1244 hp->h_addr_list++;
1241 1245 bcopy(hp->h_addr_list[0],
1242 1246 (caddr_t)&sin6.sin6_addr, hp->h_length);
1243 1247 (void) close(s);
1244 1248 s = socket(sin6.sin6_family, SOCK_STREAM, 0);
1245 1249 if (s < 0) {
1246 1250 (void) fflush(stdout);
1247 1251 perror("socket");
1248 1252 freehostent(hp);
1249 1253 return (0);
1250 1254 }
1251 1255 continue;
1252 1256 }
1253 1257
1254 1258 (void) fflush(stdout);
1255 1259 perror("connect");
1256 1260 (void) close(s);
1257 1261 freehostent(hp);
1258 1262 return (1);
1259 1263 }
1260 1264 freehostent(hp);
1261 1265 hp = NULL;
1262 1266
1263 1267 (void) printf("\n");
1264 1268 if (large)
1265 1269 (void) write(s, "/W ", 3);
1266 1270 (void) write(s, name, strlen(name));
1267 1271 (void) write(s, "\r\n", 2);
1268 1272 f = fdopen(s, "r");
1269 1273
1270 1274 lastc = '\n';
1271 1275 while ((c = getc(f)) != EOF) {
1272 1276 /* map CRLF -> newline */
1273 1277 if ((lastc == '\r') && (c != '\n'))
1274 1278 /* print out saved CR */
1275 1279 (void) putchar('\r');
1276 1280 lastc = c;
1277 1281 if (c == '\r')
1278 1282 continue;
1279 1283 (void) putchar(c);
1280 1284 }
1281 1285
1282 1286 if (lastc != '\n')
1283 1287 (void) putchar('\n');
1284 1288 (void) fclose(f);
1285 1289 return (1);
1286 1290 }
1287 1291
1288 1292 /*
1289 1293 * AnyMail - takes a username (string pointer thereto), and
1290 1294 * prints on standard output whether there is any unread mail,
1291 1295 * and if so, how old it is. (JCM@Shasta 15 March 80)
1292 1296 */
1293 1297 void
1294 1298 AnyMail(char *name)
1295 1299 {
1296 1300 struct stat buf; /* space for file status buffer */
1297 1301 char *mbxdir = MAILDIR; /* string with path preamble */
1298 1302 char *mbxpath; /* space for entire pathname */
1299 1303
1300 1304 char *timestr;
1301 1305
1302 1306 mbxpath = malloc(strlen(name) + strlen(MAILDIR) + 1);
1303 1307 if (mbxpath == NULL)
1304 1308 return;
1305 1309
1306 1310 (void) strcpy(mbxpath, mbxdir); /* copy preamble into path name */
1307 1311 (void) strcat(mbxpath, name); /* concatenate user name to path */
1308 1312
1309 1313 if (stat(mbxpath, &buf) == -1 || buf.st_size == 0) {
1310 1314 /* Mailbox is empty or nonexistent */
1311 1315 (void) printf("No unread mail\n");
1312 1316 } else {
1313 1317 if (buf.st_mtime < buf.st_atime) {
1314 1318 /*
1315 1319 * No new mail since the last time the user read it.
1316 1320 */
1317 1321 (void) printf("Mail last read ");
1318 1322 (void) printf("%s", ctime(&buf.st_atime));
1319 1323 } else if (buf.st_mtime > buf.st_atime) {
1320 1324 /*
1321 1325 * New mail has definitely arrived since the last time
1322 1326 * mail was read. mtime is the time the most recent
1323 1327 * message arrived; atime is either the time the oldest
1324 1328 * unread message arrived, or the last time the mail
1325 1329 * was read.
1326 1330 */
1327 1331 (void) printf("New mail received ");
1328 1332 timestr = ctime(&buf.st_mtime); /* time last modified */
1329 1333 timestr[24] = '\0'; /* suppress newline (ugh) */
1330 1334 (void) printf("%s", timestr);
1331 1335 (void) printf(";\n unread since ");
1332 1336 (void) printf("%s", ctime(&buf.st_atime));
1333 1337 } else {
1334 1338 /*
1335 1339 * There is something in mailbox, but we can't really
1336 1340 * be sure whether it is mail held there by the user
1337 1341 * or a (single) new message that was placed in a newly
1338 1342 * recreated mailbox, so punt and call it "unread mail."
1339 1343 */
1340 1344 (void) printf("Unread mail since ");
1341 1345 (void) printf("%s", ctime(&buf.st_mtime));
1342 1346 }
1343 1347 }
1344 1348 free(mbxpath);
1345 1349 }
1346 1350
1347 1351 /*
1348 1352 * return true iff we've already printed project/plan for this uid;
1349 1353 * if not, enter this uid into table (so this function has a side-effect.)
1350 1354 */
1351 1355 #define PPMAX 4096 /* assume no more than 4096 logged-in users */
↓ open down ↓ |
1301 lines elided |
↑ open up ↑ |
1352 1356 uid_t PlanPrinted[PPMAX+1];
1353 1357 int PPIndex = 0; /* index of next unused table entry */
1354 1358
1355 1359 int
1356 1360 AlreadyPrinted(uid_t uid)
1357 1361 {
1358 1362 int i = 0;
1359 1363
1360 1364 while (i++ < PPIndex) {
1361 1365 if (PlanPrinted[i] == uid)
1362 - return (1);
1366 + return (1);
1363 1367 }
1364 1368 if (i < PPMAX) {
1365 1369 PlanPrinted[i] = uid;
1366 1370 PPIndex++;
1367 1371 }
1368 1372 return (0);
1369 1373 }
1370 1374
1371 1375 #define FIFOREADTIMEOUT (60) /* read timeout on select */
1372 1376 /* BEGIN CSTYLED */
1373 1377 #define PRINT_CHAR(c) \
1374 1378 ( \
1375 1379 ((termpass & TS_HIGH) && ((int)c) > 126) \
1376 1380 || \
1377 1381 (isascii((int)c) && \
1378 1382 (isprint((int)c) || isspace((int)c)) \
1379 1383 ) \
1380 1384 || \
1381 1385 ((termpass & TS_LOW) && ((int)c) < 32) \
1382 1386 )
1383 1387 /* END CSTYLED */
1384 1388
1385 1389
1386 1390 void
1387 1391 catfile(char *s, mode_t mode, int trunc_at_nl)
1388 1392 {
1389 1393 if (S_ISFIFO(mode)) {
1390 1394 int fd;
1391 1395
1392 1396 fd = open(s, O_RDONLY | O_NONBLOCK);
1393 1397 if (fd != -1) {
1394 1398 fd_set readfds, exceptfds;
1395 1399 struct timeval tv;
1396 1400
1397 1401 FD_ZERO(&readfds);
1398 1402 FD_ZERO(&exceptfds);
1399 1403 FD_SET(fd, &readfds);
1400 1404 FD_SET(fd, &exceptfds);
1401 1405
1402 1406 timerclear(&tv);
1403 1407 tv.tv_sec = FIFOREADTIMEOUT;
1404 1408
1405 1409 (void) fflush(stdout);
1406 1410 while (select(fd + 1, &readfds, (fd_set *) 0,
1407 1411 &exceptfds, &tv) != -1) {
1408 1412 unsigned char buf[BUFSIZ];
1409 1413 int nread;
1410 1414
1411 1415 nread = read(fd, buf, sizeof (buf));
1412 1416 if (nread > 0) {
1413 1417 unsigned char *p;
1414 1418
1415 1419 FD_SET(fd, &readfds);
1416 1420 FD_SET(fd, &exceptfds);
1417 1421 for (p = buf; p < buf + nread; p++) {
1418 1422 if (trunc_at_nl && *p == '\n')
1419 1423 goto out;
1420 1424 if (PRINT_CHAR(*p))
1421 1425 (void) putchar((int)*p);
1422 1426 else if (isascii(*p))
1423 1427 (void) fputs(unctrl(*p),
1424 1428 stdout);
1425 1429 }
1426 1430 } else
1427 1431 break;
1428 1432 }
1429 1433 out:
1430 1434 (void) close(fd);
1431 1435 }
1432 1436 } else {
1433 1437 int c;
1434 1438 FILE *fp;
1435 1439
1436 1440 fp = fopen(s, "r");
1437 1441 if (fp) {
1438 1442 while ((c = getc(fp)) != EOF) {
1439 1443 if (trunc_at_nl && c == '\n')
1440 1444 break;
1441 1445 if (PRINT_CHAR(c))
1442 1446 (void) putchar((int)c);
1443 1447 else
1444 1448 if (isascii(c))
1445 1449 (void) fputs(unctrl(c), stdout);
1446 1450 }
1447 1451 (void) fclose(fp);
1448 1452 }
1449 1453 }
1450 1454 }
1451 1455
1452 1456
1453 1457 void
1454 1458 initscreening(void)
1455 1459 {
1456 1460 char *options, *value;
1457 1461
1458 1462 if (defopen(defaultfile) == 0) {
1459 1463 char *cp;
1460 1464 int flags;
1461 1465
1462 1466 /*
1463 1467 * ignore case
1464 1468 */
1465 1469 flags = defcntl(DC_GETFLAGS, 0);
1466 1470 TURNOFF(flags, DC_CASE);
1467 1471 (void) defcntl(DC_SETFLAGS, flags);
1468 1472
1469 1473 if (cp = defread(passvar)) {
1470 1474 options = cp;
1471 1475 while (*options != '\0')
1472 1476 switch (getsubopt(&options, termopts, &value)) {
1473 1477 case TERM_LOW:
1474 1478 termpass |= TS_LOW;
1475 1479 break;
1476 1480 case TERM_HIGH:
1477 1481 termpass |= TS_HIGH;
1478 1482 break;
1479 1483 }
1480 1484 }
1481 1485 (void) defopen(NULL); /* close default file */
1482 1486 }
1483 1487 }
1484 1488
1485 1489 int
1486 1490 person_compare(const void *p1, const void *p2)
1487 1491 {
1488 1492 const struct person *pp1 = *(struct person **)p1;
1489 1493 const struct person *pp2 = *(struct person **)p2;
1490 1494 int r;
1491 1495
1492 1496 /*
1493 1497 * Sort by username.
1494 1498 */
1495 1499 r = strcmp(pp1->name, pp2->name);
1496 1500
1497 1501 if (r != 0)
1498 1502 return (r);
1499 1503
1500 1504 /*
1501 1505 * If usernames are the same, sort by idle time.
1502 1506 */
1503 1507 r = pp1->idletime - pp2->idletime;
1504 1508
1505 1509 return (r);
1506 1510 }
1507 1511
1508 1512 void
1509 1513 sort_by_username()
1510 1514 {
1511 1515 struct person **sortable, *loop;
1512 1516 size_t i;
1513 1517
1514 1518 sortable = malloc(sizeof (sortable[0]) * nperson);
1515 1519
1516 1520 if (sortable == NULL)
1517 1521 return;
1518 1522
1519 1523 for (i = 0, loop = person1; i < nperson; i++) {
1520 1524 struct person *next = loop->link;
1521 1525
1522 1526 sortable[i] = loop;
1523 1527 loop->link = NULL;
1524 1528
1525 1529 loop = next;
1526 1530 }
1527 1531
1528 1532 qsort(sortable, nperson, sizeof (sortable[0]), person_compare);
1529 1533
1530 1534 for (i = 1; i < nperson; i++)
1531 1535 sortable[i-1]->link = sortable[i];
1532 1536 person1 = sortable[0];
1533 1537
1534 1538 free(sortable);
1535 1539 }
↓ open down ↓ |
163 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX